From 4e2a5258a5b16e670fa46eb3b0fa677bf456cf9b Mon Sep 17 00:00:00 2001 From: kntran1 <61509560+kntran1@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:40:39 -0500 Subject: [PATCH] Initial commit Initial commit. --- CMakeLists.txt | 57 + Kconfig | 13 + VERSION | 5 + .../cyber_nrf52840/Kconfig.cyber_nrf52840 | 2 + boards/MAPS/cyber_nrf52840/Kconfig.defconfig | 6 + boards/MAPS/cyber_nrf52840/board.cmake | 9 + boards/MAPS/cyber_nrf52840/board.yml | 5 + .../cyber_nrf52840-pinctrl.dtsi | 123 + boards/MAPS/cyber_nrf52840/cyber_nrf52840.dts | 243 + boards/MAPS/cyber_nrf52840/cyber_nrf52840.yml | 11 + .../cyber_nrf52840/cyber_nrf52840_defconfig | 12 + boards/MAPS/cyber_nrf52840/pre_dt_board.cmake | 2 + bootloader/mcuboot/.gitignore | 30 + bootloader/mcuboot/.gitmodules | 21 + bootloader/mcuboot/.mbedignore | 20 + bootloader/mcuboot/.travis.yml-disabled | 81 + bootloader/mcuboot/CODE_OF_CONDUCT.md | 134 + bootloader/mcuboot/Cargo.lock | 542 + bootloader/mcuboot/Cargo.toml | 14 + bootloader/mcuboot/LICENSE | 206 + bootloader/mcuboot/NOTICE | 11 + bootloader/mcuboot/README.md | 85 + .../include/boot_serial/boot_serial.h | 59 + .../boot_serial/boot_serial_encryption.h | 32 + bootloader/mcuboot/boot/boot_serial/pkg.yml | 38 + .../boot/boot_serial/src/boot_serial.c | 1498 ++ .../boot_serial/src/boot_serial_encryption.c | 308 + .../boot/boot_serial/src/boot_serial_priv.h | 106 + .../mcuboot/boot/boot_serial/src/zcbor_bulk.c | 69 + .../mcuboot/boot/boot_serial/src/zcbor_bulk.h | 116 + .../mcuboot/boot/boot_serial/syscfg.yml | 96 + .../mcuboot/boot/boot_serial/test/pkg.yml | 35 + .../boot/boot_serial/test/src/boot_test.c | 85 + .../boot/boot_serial/test/src/boot_test.h | 50 + .../src/testcases/boot_serial_empty_img_msg.c | 35 + .../src/testcases/boot_serial_empty_msg.c | 37 + .../test/src/testcases/boot_serial_img_msg.c | 70 + .../test/src/testcases/boot_serial_setup.c | 24 + .../boot_serial_upload_bigger_image.c | 117 + .../mcuboot/boot/boot_serial/test/syscfg.yml | 23 + .../mcuboot/boot/bootutil/CMakeLists.txt | 35 + .../boot/bootutil/include/bootutil/bench.h | 68 + .../bootutil/include/bootutil/boot_hooks.h | 181 + .../include/bootutil/boot_public_hooks.h | 52 + .../bootutil/include/bootutil/boot_record.h | 89 + .../bootutil/include/bootutil/boot_status.h | 188 + .../boot/bootutil/include/bootutil/bootutil.h | 104 + .../bootutil/include/bootutil/bootutil_log.h | 59 + .../include/bootutil/bootutil_public.h | 316 + .../bootutil/include/bootutil/bootutil_test.h | 35 + .../boot/bootutil/include/bootutil/caps.h | 67 + .../include/bootutil/crypto/aes_ctr.h | 158 + .../bootutil/include/bootutil/crypto/aes_kw.h | 142 + .../bootutil/include/bootutil/crypto/common.h | 29 + .../include/bootutil/crypto/ecdh_p256.h | 154 + .../include/bootutil/crypto/ecdh_x25519.h | 57 + .../bootutil/include/bootutil/crypto/ecdsa.h | 672 + .../include/bootutil/crypto/hmac_sha256.h | 134 + .../bootutil/include/bootutil/crypto/rsa.h | 356 + .../bootutil/include/bootutil/crypto/sha.h | 250 + .../boot/bootutil/include/bootutil/enc_key.h | 83 + .../include/bootutil/enc_key_public.h | 66 + .../bootutil/fault_injection_hardening.h | 371 + .../fault_injection_hardening_delay_rng.h | 30 + .../boot/bootutil/include/bootutil/ignore.h | 70 + .../boot/bootutil/include/bootutil/image.h | 228 + .../include/bootutil/mcuboot_status.h | 30 + .../boot/bootutil/include/bootutil/ramload.h | 35 + .../bootutil/include/bootutil/security_cnt.h | 70 + .../boot/bootutil/include/bootutil/sign_key.h | 71 + bootloader/mcuboot/boot/bootutil/pkg.yml | 65 + .../mcuboot/boot/bootutil/src/boot_record.c | 343 + .../mcuboot/boot/bootutil/src/bootutil_misc.c | 357 + .../mcuboot/boot/bootutil/src/bootutil_misc.h | 52 + .../mcuboot/boot/bootutil/src/bootutil_priv.h | 486 + .../boot/bootutil/src/bootutil_public.c | 762 + bootloader/mcuboot/boot/bootutil/src/caps.c | 95 + .../mcuboot/boot/bootutil/src/ed25519_psa.c | 122 + .../mcuboot/boot/bootutil/src/encrypted.c | 755 + .../mcuboot/boot/bootutil/src/encrypted_psa.c | 454 + .../bootutil/src/fault_injection_hardening.c | 78 + ...lt_injection_hardening_delay_rng_mbedtls.c | 47 + .../mcuboot/boot/bootutil/src/image_ecdsa.c | 95 + .../mcuboot/boot/bootutil/src/image_ed25519.c | 160 + .../mcuboot/boot/bootutil/src/image_rsa.c | 288 + .../boot/bootutil/src/image_validate.c | 920 + bootloader/mcuboot/boot/bootutil/src/loader.c | 3974 +++ .../bootutil/src/loader_legacy_child_parent.c | 3896 +++ .../mcuboot/boot/bootutil/src/swap_misc.c | 234 + .../mcuboot/boot/bootutil/src/swap_move.c | 608 + .../mcuboot/boot/bootutil/src/swap_nsib.c | 70 + .../mcuboot/boot/bootutil/src/swap_priv.h | 117 + .../mcuboot/boot/bootutil/src/swap_scratch.c | 992 + bootloader/mcuboot/boot/bootutil/src/tlv.c | 154 + .../boot/bootutil/zephyr/CMakeLists.txt | 46 + bootloader/mcuboot/boot/cypress/.gitignore | 41 + .../boot/cypress/BlinkyApp/BlinkyApp.mk | 132 + .../BlinkyApp/BlinkyApp_CM4_Debug.launch | 62 + .../mcuboot/boot/cypress/BlinkyApp/Readme.md | 165 + .../mcuboot/boot/cypress/BlinkyApp/libs.mk | 60 + .../BlinkyApp/linker/BlinkyApp_template.ld | 425 + .../mcuboot/boot/cypress/BlinkyApp/main.c | 136 + .../mcuboot/boot/cypress/BlinkyApp/main.h | 25 + .../boot/cypress/MCUBootApp/ExternalMemory.md | 100 + .../boot/cypress/MCUBootApp/MCUBootApp.ld | 418 + .../boot/cypress/MCUBootApp/MCUBootApp.mk | 113 + .../MCUBootApp/MCUBootApp_CM0P_Debug.launch | 62 + .../mcuboot/boot/cypress/MCUBootApp/README.md | 221 + .../config/mcuboot_config/mcuboot_assert.h | 22 + .../config/mcuboot_config/mcuboot_config.h | 166 + .../config/mcuboot_config/mcuboot_logging.h | 99 + .../config/mcuboot_crypto_acc_config.h | 54 + .../MCUBootApp/config/mcuboot_crypto_config.h | 3604 +++ .../boot/cypress/MCUBootApp/cy_security_cnt.c | 44 + .../cypress/MCUBootApp/cy_serial_flash_prog.c | 100 + .../mcuboot/boot/cypress/MCUBootApp/keys.c | 169 + .../mcuboot/boot/cypress/MCUBootApp/libs.mk | 95 + .../mcuboot/boot/cypress/MCUBootApp/main.c | 181 + .../mcuboot/boot/cypress/MCUBootApp/os/os.h | 18 + .../boot/cypress/MCUBootApp/os/os_heap.h | 37 + .../boot/cypress/MCUBootApp/os/os_malloc.h | 42 + .../cypress/MCUBootApp/sysflash/sysflash.h | 81 + bootloader/mcuboot/boot/cypress/Makefile | 212 + bootloader/mcuboot/boot/cypress/README.md | 84 + .../mcuboot/boot/cypress/common_libs.mk | 89 + .../boot/cypress/cy_flash_pal/cy_flash_map.c | 547 + .../boot/cypress/cy_flash_pal/cy_smif_psoc6.c | 143 + .../cy_flash_pal/flash_qspi/flash_qspi.c | 494 + .../cy_flash_pal/flash_qspi/flash_qspi.h | 70 + .../cy_flash_pal/include/cy_smif_psoc6.h | 64 + .../flash_map_backend/flash_map_backend.h | 195 + bootloader/mcuboot/boot/cypress/host.mk | 48 + .../cypress/keys/cypress-test-ec-p256.pem | 5 + .../cypress/keys/cypress-test-ec-p256.pub | 16 + .../libs/retarget_io_pdl/cy_retarget_io_pdl.c | 270 + .../libs/retarget_io_pdl/cy_retarget_io_pdl.h | 62 + .../boot/cypress/libs/watchdog/watchdog.c | 183 + .../boot/cypress/libs/watchdog/watchdog.h | 90 + bootloader/mcuboot/boot/cypress/platforms.mk | 78 + .../CM0P/GCC_ARM/cy8c6xxa_cm0plus.ld | 418 + .../CM0P/GCC_ARM/startup_psoc6_02_cm0plus.S | 372 + .../CM4/GCC_ARM/cy8c6xxa_cm4_dual.ld | 436 + .../CM4/GCC_ARM/startup_psoc6_02_cm4.S | 697 + .../mcuboot/boot/cypress/platforms/cycfg.c | 33 + .../mcuboot/boot/cypress/platforms/cycfg.h | 45 + .../boot/cypress/platforms/cycfg_clocks.c | 47 + .../boot/cypress/platforms/cycfg_clocks.h | 54 + .../cypress/platforms/cycfg_peripherals.c | 73 + .../cypress/platforms/cycfg_peripherals.h | 57 + .../boot/cypress/platforms/cycfg_pins.c | 89 + .../boot/cypress/platforms/cycfg_pins.h | 113 + .../boot/cypress/platforms/cycfg_routing.c | 31 + .../boot/cypress/platforms/cycfg_routing.h | 46 + .../boot/cypress/platforms/cycfg_system.c | 537 + .../boot/cypress/platforms/cycfg_system.h | 86 + .../retarget_io_pdl/cy_retarget_io_pdl.c | 270 + .../retarget_io_pdl/cy_retarget_io_pdl.h | 62 + bootloader/mcuboot/boot/cypress/toolchains.mk | 107 + .../mcuboot/boot/espressif/CMakeLists.txt | 409 + .../boot/espressif/ci_configs/multi-boot.conf | 10 + .../espressif/ci_configs/multi-image.conf | 9 + .../ci_configs/secureboot-sign-ec256.conf | 22 + .../ci_configs/secureboot-sign-ed25519.conf | 22 + .../ci_configs/secureboot-sign-rsa2048.conf | 23 + .../ci_configs/secureboot-sign-rsa3072.conf | 23 + .../espressif/ci_configs/serialrecovery.conf | 10 + .../mcuboot/boot/espressif/hal/CMakeLists.txt | 213 + .../espressif/hal/include/app_cpu_start.h | 11 + .../espressif/hal/include/bootloader_wdt.h | 8 + .../espressif/hal/include/esp32/esp32.cmake | 32 + .../espressif/hal/include/esp32/sdkconfig.h | 31 + .../hal/include/esp32c2/esp32c2.cmake | 29 + .../espressif/hal/include/esp32c2/sdkconfig.h | 30 + .../hal/include/esp32c3/esp32c3.cmake | 20 + .../espressif/hal/include/esp32c3/sdkconfig.h | 29 + .../hal/include/esp32c6/esp32c6.cmake | 34 + .../espressif/hal/include/esp32c6/sdkconfig.h | 30 + .../hal/include/esp32h2/esp32h2.cmake | 34 + .../espressif/hal/include/esp32h2/sdkconfig.h | 31 + .../hal/include/esp32s2/esp32s2.cmake | 22 + .../espressif/hal/include/esp32s2/sdkconfig.h | 29 + .../hal/include/esp32s3/esp32s3.cmake | 25 + .../espressif/hal/include/esp32s3/sdkconfig.h | 27 + .../boot/espressif/hal/include/esp_log.h | 29 + .../espressif/hal/include/esp_mcuboot_image.h | 26 + .../include/mcuboot_config/mcuboot_assert.h | 19 + .../include/mcuboot_config/mcuboot_config.h | 193 + .../include/mcuboot_config/mcuboot_logging.h | 79 + .../boot/espressif/hal/include/soc_log.h | 15 + .../espressif/hal/src/bootloader_banner.c | 15 + .../boot/espressif/hal/src/bootloader_wdt.c | 17 + .../espressif/hal/src/esp32/app_cpu_start.c | 38 + .../hal/src/esp32/console_uart_custom.c | 24 + .../hal/src/esp32c2/console_uart_custom.c | 21 + .../hal/src/esp32c3/console_uart_custom.c | 22 + .../hal/src/esp32c6/console_uart_custom.c | 23 + .../hal/src/esp32h2/console_uart_custom.c | 22 + .../espressif/hal/src/esp32s3/app_cpu_start.c | 42 + .../boot/espressif/hal/src/flash_encrypt.c | 482 + .../boot/espressif/hal/src/secure_boot.c | 266 + .../include/crypto_config/ec256.cmake | 29 + .../include/crypto_config/ed25519.cmake | 31 + .../crypto_config/mbedtls_custom_config.h | 3265 +++ .../espressif/include/crypto_config/rsa.cmake | 28 + .../boot/espressif/include/esp_loader.h | 14 + .../flash_map_backend/flash_map_backend.h | 93 + .../mcuboot/boot/espressif/include/os/os.h | 0 .../boot/espressif/include/os/os_malloc.h | 11 + .../include/serial_adapter/serial_adapter.h | 29 + .../espressif/include/sysflash/sysflash.h | 50 + bootloader/mcuboot/boot/espressif/keys.c | 56 + bootloader/mcuboot/boot/espressif/main.c | 298 + bootloader/mcuboot/boot/espressif/os.c | 29 + .../port/esp32/bootloader-multi.conf | 34 + .../boot/espressif/port/esp32/bootloader.conf | 101 + .../espressif/port/esp32/ld/bootloader.ld | 144 + .../espressif/port/esp32/serial_adapter.c | 179 + .../espressif/port/esp32c2/bootloader.conf | 87 + .../espressif/port/esp32c2/ld/bootloader.ld | 181 + .../espressif/port/esp32c2/serial_adapter.c | 190 + .../espressif/port/esp32c3/bootloader.conf | 88 + .../espressif/port/esp32c3/ld/bootloader.ld | 147 + .../espressif/port/esp32c3/serial_adapter.c | 226 + .../espressif/port/esp32c6/bootloader.conf | 88 + .../espressif/port/esp32c6/ld/bootloader.ld | 177 + .../espressif/port/esp32c6/serial_adapter.c | 226 + .../espressif/port/esp32h2/bootloader.conf | 88 + .../espressif/port/esp32h2/ld/bootloader.ld | 179 + .../espressif/port/esp32h2/serial_adapter.c | 226 + .../espressif/port/esp32s2/bootloader.conf | 84 + .../espressif/port/esp32s2/ld/bootloader.ld | 147 + .../espressif/port/esp32s2/serial_adapter.c | 191 + .../port/esp32s3/bootloader-multi.conf | 38 + .../espressif/port/esp32s3/bootloader.conf | 108 + .../espressif/port/esp32s3/ld/bootloader.ld | 147 + .../espressif/port/esp32s3/serial_adapter.c | 228 + .../mcuboot/boot/espressif/port/esp_loader.c | 105 + .../mcuboot/boot/espressif/port/esp_mcuboot.c | 420 + .../espressif/tools/toolchain-esp32.cmake | 12 + .../espressif/tools/toolchain-esp32c2.cmake | 13 + .../espressif/tools/toolchain-esp32c3.cmake | 9 + .../espressif/tools/toolchain-esp32c6.cmake | 13 + .../espressif/tools/toolchain-esp32h2.cmake | 13 + .../espressif/tools/toolchain-esp32s2.cmake | 10 + .../espressif/tools/toolchain-esp32s3.cmake | 16 + .../mcuboot/boot/espressif/tools/utils.cmake | 31 + bootloader/mcuboot/boot/mbed/CMakeLists.txt | 61 + bootloader/mcuboot/boot/mbed/app_enc_keys.c | 71 + .../flash_map_backend/flash_map_backend.h | 177 + .../include/flash_map_backend/secondary_bd.h | 35 + .../include/mcuboot_config/mcuboot_assert.h | 20 + .../include/mcuboot_config/mcuboot_config.h | 95 + .../include/mcuboot_config/mcuboot_logging.h | 80 + .../mcuboot/boot/mbed/include/os/os_malloc.h | 1 + .../boot/mbed/include/sysflash/sysflash.h | 14 + .../boot/mbed/include/utils/DataShare.cpp | 97 + .../boot/mbed/include/utils/DataShare.h | 92 + bootloader/mcuboot/boot/mbed/mbed_lib.json | 194 + bootloader/mcuboot/boot/mbed/mcuboot_main.cpp | 93 + .../boot/mbed/src/flash_map_backend.cpp | 247 + .../mcuboot/boot/mbed/src/secondary_bd.cpp | 40 + bootloader/mcuboot/boot/mynewt/README.md | 6 + .../boot_uart/include/boot_uart/boot_uart.h | 28 + .../mcuboot/boot/mynewt/boot_uart/pkg.yml | 32 + .../boot/mynewt/boot_uart/src/boot_uart.c | 178 + .../mcuboot/boot/mynewt/boot_uart/syscfg.yml | 24 + .../flash_map_backend/flash_map_backend.h | 86 + .../boot/mynewt/flash_map_backend/pkg.yml | 14 + .../src/flash_map_extended.c | 83 + .../include/mcuboot_config/mcuboot_config.h | 157 + .../include/mcuboot_config/mcuboot_logging.h | 87 + .../boot/mynewt/mcuboot_config/pkg.yml | 23 + .../boot/mynewt/mcuboot_config/syscfg.yml | 159 + bootloader/mcuboot/boot/mynewt/pkg.yml | 51 + bootloader/mcuboot/boot/mynewt/src/main.c | 278 + .../mcuboot/boot/mynewt/src/single_loader.c | 136 + bootloader/mcuboot/boot/mynewt/syscfg.yml | 47 + .../flash_map_backend/flash_map_backend.h | 449 + .../include/mcuboot_config/mcuboot_config.h | 212 + .../include/mcuboot_config/mcuboot_logging.h | 48 + .../mcuboot/boot/nuttx/include/os/os_malloc.h | 29 + .../boot/nuttx/include/sysflash/sysflash.h | 39 + .../boot/nuttx/include/watchdog/watchdog.h | 65 + bootloader/mcuboot/boot/nuttx/main.c | 137 + .../src/flash_map_backend/flash_map_backend.c | 844 + .../boot/nuttx/src/watchdog/watchdog.c | 130 + .../boot/zcbor/add_zcbor_copy_version.sh | 24 + .../mcuboot/boot/zcbor/include/zcbor_common.h | 514 + .../mcuboot/boot/zcbor/include/zcbor_decode.h | 452 + .../mcuboot/boot/zcbor/include/zcbor_encode.h | 245 + .../mcuboot/boot/zcbor/include/zcbor_print.h | 170 + .../mcuboot/boot/zcbor/include/zcbor_tags.h | 99 + bootloader/mcuboot/boot/zcbor/pkg.yml | 27 + .../mcuboot/boot/zcbor/src/zcbor_common.c | 442 + .../mcuboot/boot/zcbor/src/zcbor_decode.c | 1572 ++ .../mcuboot/boot/zcbor/src/zcbor_encode.c | 614 + bootloader/mcuboot/boot/zephyr/CMakeLists.txt | 635 + bootloader/mcuboot/boot/zephyr/Kconfig | 1040 + .../boot/zephyr/Kconfig.firmware_loader | 47 + .../boot/zephyr/Kconfig.serial_recovery | 212 + bootloader/mcuboot/boot/zephyr/VERSION | 5 + bootloader/mcuboot/boot/zephyr/app.overlay | 5 + bootloader/mcuboot/boot/zephyr/arm_cleanup.c | 55 + .../boards/actinius_icarus_bee_nrf9160.conf | 10 + .../boards/actinius_icarus_nrf9160.conf | 10 + .../actinius_icarus_som_dk_nrf9160.conf | 10 + .../boards/actinius_icarus_som_nrf9160.conf | 10 + .../boards/bl5340_dvk_nrf5340_cpuapp.conf | 6 + .../boards/circuitdojo_feather_nrf9160.conf | 13 + .../boot/zephyr/boards/conexio_stratus.conf | 15 + .../boards/disco_l475_iot1_stm32l475xx.conf | 2 + .../boot/zephyr/boards/flash_sim_driver.conf | 2 + .../boot/zephyr/boards/frdm_k64f_mk64f12.conf | 1 + .../boards/frdm_mcxn947_mcxn947_cpu0.conf | 5 + .../frdm_mcxn947_mcxn947_cpu0_qspi.conf | 5 + .../frdm_mcxn947_mcxn947_cpu0_qspi.overlay | 12 + .../boards/lpcxpresso55s06_lpc55s06.conf | 6 + .../boards/lpcxpresso55s16_lpc55s16.conf | 6 + .../boards/lpcxpresso55s28_lpc55s28.conf | 6 + .../boards/lpcxpresso55s36_lpc55s36.conf | 6 + .../boards/lpcxpresso55s69_lpc55s69_cpu0.conf | 6 + .../boards/mimxrt1010_evk_mimxrt1011.conf | 4 + .../boards/mimxrt1015_evk_mimxrt1015.conf | 4 + .../boards/mimxrt1020_evk_mimxrt1021.conf | 4 + .../boards/mimxrt1024_evk_mimxrt1024.conf | 4 + .../boards/mimxrt1040_evk_mimxrt1042.conf | 4 + .../boards/mimxrt1050_evk_mimxrt1052.conf | 5 + .../boards/mimxrt1060_evk_mimxrt1062.conf | 5 + .../boards/mimxrt1060_evk_mimxrt1062_cm7.conf | 7 + .../boards/mimxrt1060_evkb_mimxrt1062.conf | 5 + .../boot/zephyr/boards/mimxrt1062_fmurt6.conf | 5 + .../boards/mimxrt1064_evk_mimxrt1064.conf | 4 + .../boards/mimxrt1160_evk_mimxrt1166_cm7.conf | 5 + .../boards/mimxrt1170_evk_mimxrt1176_cm7.conf | 6 + .../boards/mimxrt595_evk_mimxrt595s_cm33.conf | 3 + .../boards/mimxrt685_evk_mimxrt685s_cm33.conf | 4 + .../boot/zephyr/boards/nrf51dk_nrf51822.conf | 5 + .../boot/zephyr/boards/nrf52840_big.overlay | 36 + .../boards/nrf52840_single_slot.overlay | 26 + .../nrf52840dk_hooks_sample_overlay.conf | 6 + .../zephyr/boards/nrf52840dk_nrf52840.conf | 2 + .../zephyr/boards/nrf52840dk_nrf52840.overlay | 3 + .../zephyr/boards/nrf52840dk_qspi_nor.conf | 3 + .../nrf52840dk_qspi_nor_secondary.overlay | 35 + .../nrf52840dk_qspi_secondary_boot.conf | 2 + .../boot/zephyr/boards/nrf52840dk_ram.overlay | 54 + .../boards/nrf52840dk_ram_multi.overlay | 62 + .../boards/nrf52840dongle_nrf52840.conf | 26 + .../boards/nrf52_minimal_footprint.conf | 61 + .../nrf5340dk_nrf5340_cpuapp_minimal.conf | 13 + .../boards/nrf54l15dk_nrf54l15_cpuapp.conf | 13 + .../nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf | 15 + ...f54l15dk_nrf54l15_cpuapp_ext_flash.overlay | 47 + .../boards/nrf54l15pdk_nrf54l15_cpuapp.conf | 13 + ...nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf | 15 + ...54l15pdk_nrf54l15_cpuapp_ext_flash.overlay | 47 + .../boards/nrf9161dk_nrf9161_0_7_0.conf | 1 + .../zephyr/boards/odroid_go_esp32_procpu.conf | 4 + .../boards/pinnacle_100_dvk_nrf52840.conf | 6 + .../zephyr/boards/rd_rw612_bga_rw612.conf | 4 + .../boards/sparkfun_thing_plus_nrf9160.conf | 13 + .../boot/zephyr/boards/thingy52_nrf52832.conf | 1 + .../boards/thingy53_nrf5340_cpuapp.conf | 74 + .../boot/zephyr/boards/thingy91_nrf52840.conf | 34 + .../boot/zephyr/boards/thingy91_nrf9160.conf | 13 + .../boards/thingy91x_nrf5340_cpuapp.conf | 63 + .../boot/zephyr/boards/thingy91x_nrf9151.conf | 21 + .../zephyr/boards/thingy91x_nrf9151.overlay | 4 + .../boards/tlsr9518adk80d_tlsr9518.conf | 4 + .../boards/vmu_rt1170_mimxrt1176_cm7.conf | 5 + .../boot_serial_extension_zephyr_basic.c | 71 + .../boot/zephyr/boot_serial_extensions.c | 39 + .../mcuboot/boot/zephyr/decompression.c | 1278 + .../mcuboot/boot/zephyr/external_crypto.conf | 20 + .../mcuboot/boot/zephyr/firmware_loader.c | 196 + .../mcuboot/boot/zephyr/flash_map_extended.c | 174 + .../mcuboot/boot/zephyr/flash_map_legacy.c | 104 + bootloader/mcuboot/boot/zephyr/hooks_sample.c | 95 + .../mcuboot/boot/zephyr/include/arm_cleanup.h | 23 + .../zephyr/include/boot_serial/boot_serial.ld | 9 + .../boot_serial/boot_serial_extensions.h | 41 + .../include/compression/decompression.h | 104 + .../mcuboot/boot/zephyr/include/config-asn1.h | 44 + .../mcuboot/boot/zephyr/include/config-ec.h | 94 + .../boot/zephyr/include/config-ed25519.h | 76 + .../mcuboot/boot/zephyr/include/config-kw.h | 66 + .../boot/zephyr/include/config-rsa-kw.h | 80 + .../mcuboot/boot/zephyr/include/config-rsa.h | 83 + .../flash_map_backend/flash_map_backend.h | 113 + .../mcuboot/boot/zephyr/include/hal/hal_bsp.h | 79 + .../boot/zephyr/include/hal/hal_flash.h | 43 + .../mcuboot/boot/zephyr/include/io/io.h | 83 + .../boot/zephyr/include/mcuboot-mbedtls-cfg.h | 36 + .../include/mcuboot_config/mcuboot_config.h | 424 + .../include/mcuboot_config/mcuboot_logging.h | 30 + .../mcuboot/boot/zephyr/include/nrf_cleanup.h | 24 + .../mcuboot/boot/zephyr/include/os/os.h | 0 .../mcuboot/boot/zephyr/include/os/os_heap.h | 38 + .../boot/zephyr/include/os/os_malloc.h | 42 + .../boot/zephyr/include/platform-bench.h | 40 + .../include/serial_adapter/serial_adapter.h | 32 + .../zephyr/include/sysflash/pm_sysflash.h | 87 + .../pm_sysflash_legacy_child_parent.h | 100 + .../boot/zephyr/include/sysflash/sysflash.h | 80 + .../mcuboot/boot/zephyr/include/target.h | 52 + bootloader/mcuboot/boot/zephyr/io.c | 211 + .../mcuboot/boot/zephyr/kernel/banner.c | 46 + bootloader/mcuboot/boot/zephyr/keys.c | 88 + bootloader/mcuboot/boot/zephyr/main.c | 811 + .../nrf52840dk_nrf52840_cc310_ecdsa.conf | 2 + bootloader/mcuboot/boot/zephyr/nrf_cleanup.c | 122 + bootloader/mcuboot/boot/zephyr/os.c | 63 + bootloader/mcuboot/boot/zephyr/pm.yml | 90 + bootloader/mcuboot/boot/zephyr/prj.conf | 45 + .../mcuboot/boot/zephyr/prj_minimal.conf | 40 + bootloader/mcuboot/boot/zephyr/ram_load.conf | 6 + bootloader/mcuboot/boot/zephyr/sample.yaml | 59 + .../mcuboot/boot/zephyr/serial_adapter.c | 250 + .../mcuboot/boot/zephyr/serial_recovery.conf | 3 + bootloader/mcuboot/boot/zephyr/shared_data.c | 124 + .../mcuboot/boot/zephyr/single_loader.c | 136 + .../mcuboot/boot/zephyr/single_slot.conf | 1 + .../boot/zephyr/socs/esp32_procpu.conf | 19 + .../mcuboot/boot/zephyr/socs/esp32c2.conf | 20 + .../mcuboot/boot/zephyr/socs/esp32c3.conf | 20 + .../mcuboot/boot/zephyr/socs/esp32c6.conf | 20 + .../mcuboot/boot/zephyr/socs/esp32s2.conf | 19 + .../boot/zephyr/socs/esp32s3_procpu.conf | 19 + .../boot/zephyr/sysbuild/CMakeLists.txt | 43 + .../mcuboot/boot/zephyr/targets/arduino_101.h | 37 + .../mcuboot/boot/zephyr/usb_cdc_acm.overlay | 5 + .../boot/zephyr/usb_cdc_acm_log_recovery.conf | 11 + bootloader/mcuboot/ci/check-signed-off-by.sh | 81 + bootloader/mcuboot/ci/compare_versions.py | 65 + bootloader/mcuboot/ci/espressif_install.sh | 23 + bootloader/mcuboot/ci/espressif_run.sh | 60 + bootloader/mcuboot/ci/fih-tests_install.sh | 34 + bootloader/mcuboot/ci/fih-tests_run.sh | 58 + bootloader/mcuboot/ci/fih-tests_version.sh | 1 + .../ci/fih_test_docker/damage_image.py | 211 + .../fih_test_docker/docker-build/Dockerfile | 45 + .../ci/fih_test_docker/docker-build/build.sh | 29 + .../ci/fih_test_docker/execute_test.sh | 68 + .../ci/fih_test_docker/fi_make_manifest.sh | 41 + .../ci/fih_test_docker/fi_tester_gdb.sh | 204 + .../fih_test_docker/generate_test_report.py | 47 + .../mcuboot/ci/fih_test_docker/paths.sh | 10 + .../mcuboot/ci/fih_test_docker/run_fi_test.sh | 88 + .../mcuboot/ci/fih_test_docker/utils.py | 63 + .../ci/fih_test_docker/validate_output.py | 39 + bootloader/mcuboot/ci/get_features.py | 43 + bootloader/mcuboot/ci/imgtool_install.sh | 23 + bootloader/mcuboot/ci/imgtool_run.sh | 53 + bootloader/mcuboot/ci/mynewt_install.sh | 83 + .../mcuboot/ci/mynewt_keys/enc_kw/pkg.yml | 22 + .../mcuboot/ci/mynewt_keys/enc_kw/src/keys.c | 30 + .../mcuboot/ci/mynewt_keys/enc_rsa/pkg.yml | 25 + .../mcuboot/ci/mynewt_keys/enc_rsa/src/keys.c | 128 + bootloader/mcuboot/ci/mynewt_run.sh | 33 + .../mcuboot/ci/mynewt_targets/basic/pkg.yml | 27 + .../ci/mynewt_targets/basic/syscfg.yml | 45 + .../ci/mynewt_targets/basic/target.yml | 22 + .../ci/mynewt_targets/bootserial/pkg.yml | 27 + .../ci/mynewt_targets/bootserial/syscfg.yml | 24 + .../ci/mynewt_targets/bootserial/target.yml | 22 + .../mcuboot/ci/mynewt_targets/ecdsa/pkg.yml | 27 + .../ci/mynewt_targets/ecdsa/syscfg.yml | 24 + .../ci/mynewt_targets/ecdsa/target.yml | 23 + .../ci/mynewt_targets/ecdsa_kw/pkg.yml | 28 + .../ci/mynewt_targets/ecdsa_kw/syscfg.yml | 25 + .../ci/mynewt_targets/ecdsa_kw/target.yml | 23 + .../mcuboot/ci/mynewt_targets/rsa/pkg.yml | 27 + .../mcuboot/ci/mynewt_targets/rsa/syscfg.yml | 27 + .../mcuboot/ci/mynewt_targets/rsa/target.yml | 23 + .../mcuboot/ci/mynewt_targets/rsa_kw/pkg.yml | 28 + .../ci/mynewt_targets/rsa_kw/syscfg.yml | 30 + .../ci/mynewt_targets/rsa_kw/target.yml | 23 + .../mynewt_targets/rsa_overwriteonly/pkg.yml | 27 + .../rsa_overwriteonly/syscfg.yml | 28 + .../rsa_overwriteonly/target.yml | 23 + .../ci/mynewt_targets/rsa_rsaoaep/pkg.yml | 27 + .../ci/mynewt_targets/rsa_rsaoaep/syscfg.yml | 29 + .../ci/mynewt_targets/rsa_rsaoaep/target.yml | 23 + .../rsa_rsaoaep_bootstrap/pkg.yml | 27 + .../rsa_rsaoaep_bootstrap/syscfg.yml | 30 + .../rsa_rsaoaep_bootstrap/target.yml | 23 + .../ci/mynewt_targets/swap_move/pkg.yml | 27 + .../ci/mynewt_targets/swap_move/syscfg.yml | 46 + .../ci/mynewt_targets/swap_move/target.yml | 22 + bootloader/mcuboot/ci/requirements.txt | 1 + bootloader/mcuboot/ci/sim_install.sh | 19 + bootloader/mcuboot/ci/sim_run.sh | 65 + bootloader/mcuboot/docs/.gitignore | 2 + bootloader/mcuboot/docs/CNAME | 1 + bootloader/mcuboot/docs/Gemfile | 26 + bootloader/mcuboot/docs/Gemfile.lock | 266 + bootloader/mcuboot/docs/PORTING.md | 195 + bootloader/mcuboot/docs/SECURITY.md | 56 + bootloader/mcuboot/docs/SubmittingPatches.md | 89 + bootloader/mcuboot/docs/_config.yml | 3 + bootloader/mcuboot/docs/design.md | 1512 ++ bootloader/mcuboot/docs/ecdsa.md | 90 + bootloader/mcuboot/docs/encrypted_images.md | 175 + bootloader/mcuboot/docs/imgtool.md | 135 + bootloader/mcuboot/docs/index.md | 93 + bootloader/mcuboot/docs/readme-espressif.md | 1253 + bootloader/mcuboot/docs/readme-mbed.md | 41 + bootloader/mcuboot/docs/readme-mynewt.md | 52 + bootloader/mcuboot/docs/readme-nuttx.md | 52 + bootloader/mcuboot/docs/readme-riot.md | 47 + bootloader/mcuboot/docs/readme-zephyr.md | 256 + .../mcuboot/docs/release-notes.d/00readme.md | 29 + .../release-notes.d/bootutil-enc-hw-keys.md | 2 + .../bootutil-image-verification.md | 4 + .../encrypted-scratch-partition.md | 3 + .../docs/release-notes.d/fix-nordic.md | 2 + .../fix-ram-load-zephyr-address.md | 2 + .../fix-zephyr-sysbuild-name.md | 2 + .../release-notes.d/imgtool_sanity_test.md | 2 + .../release-notes.d/max-app-size-changes.md | 4 + .../serial-recovery-slot-info.md | 1 + .../zephyr-auto-max-sectors.md | 5 + .../release-notes.d/zephyr-compression.md | 7 + bootloader/mcuboot/docs/release-notes.md | 553 + bootloader/mcuboot/docs/release.md | 133 + bootloader/mcuboot/docs/serial_recovery.md | 128 + bootloader/mcuboot/docs/signed_images.md | 100 + bootloader/mcuboot/docs/testplan-mynewt.md | 162 + bootloader/mcuboot/docs/testplan-zephyr.md | 71 + bootloader/mcuboot/enc-aes128kw.b64 | 1 + bootloader/mcuboot/enc-aes256kw.b64 | 1 + bootloader/mcuboot/enc-ec256-priv.pem | 5 + bootloader/mcuboot/enc-ec256-pub.pem | 4 + bootloader/mcuboot/enc-rsa2048-priv.pem | 27 + bootloader/mcuboot/enc-rsa2048-pub.pem | 9 + bootloader/mcuboot/enc-x25519-priv.pem | 3 + bootloader/mcuboot/enc-x25519-pub.pem | 3 + bootloader/mcuboot/ext/fiat/LICENSE | 22 + bootloader/mcuboot/ext/fiat/METADATA | 13 + bootloader/mcuboot/ext/fiat/README.chromium | 10 + bootloader/mcuboot/ext/fiat/README.md | 47 + bootloader/mcuboot/ext/fiat/pkg.yml | 24 + bootloader/mcuboot/ext/fiat/src/curve25519.c | 1314 + bootloader/mcuboot/ext/fiat/src/curve25519.h | 884 + .../mcuboot/ext/fiat/src/curve25519_tables.h | 107 + bootloader/mcuboot/ext/mbedtls-asn1/README | 3 + .../mcuboot/ext/mbedtls-asn1/include/common.h | 56 + .../ext/mbedtls-asn1/include/mbedtls/asn1.h | 604 + .../ext/mbedtls-asn1/include/mbedtls/bignum.h | 1021 + .../mbedtls-asn1/include/mbedtls/build_info.h | 83 + .../include/mbedtls/check_config.h | 838 + .../ext/mbedtls-asn1/include/mbedtls/ecdsa.h | 506 + .../ext/mbedtls-asn1/include/mbedtls/ecp.h | 1286 + .../ext/mbedtls-asn1/include/mbedtls/error.h | 210 + .../include/mbedtls/mbedtls_config.h | 96 + .../ext/mbedtls-asn1/include/mbedtls/md.h | 446 + .../ext/mbedtls-asn1/include/mbedtls/oid.h | 641 + .../ext/mbedtls-asn1/include/mbedtls/pk.h | 890 + .../mbedtls-asn1/include/mbedtls/platform.h | 411 + .../include/mbedtls/platform_util.h | 122 + .../include/mbedtls/private_access.h | 32 + .../ext/mbedtls-asn1/include/mbedtls/rsa.h | 1119 + .../mbedtls-asn1/include/mbedtls/threading.h | 116 + .../mbedtls-asn1/include/mbedtls/version.h | 90 + bootloader/mcuboot/ext/mbedtls-asn1/pkg.yml | 24 + .../mcuboot/ext/mbedtls-asn1/src/asn1parse.c | 481 + .../ext/mbedtls-asn1/src/platform_util.c | 133 + bootloader/mcuboot/ext/nrf/README.md | 18 + bootloader/mcuboot/ext/nrf/cc310_glue.c | 73 + bootloader/mcuboot/ext/nrf/cc310_glue.h | 77 + .../lib/include/tinycrypt/sha512.h | 129 + .../mcuboot/ext/tinycrypt-sha512/lib/pkg.yml | 30 + .../ext/tinycrypt-sha512/lib/source/sha512.c | 234 + bootloader/mcuboot/ext/tinycrypt/.gitignore | 5 + bootloader/mcuboot/ext/tinycrypt/AUTHORS | 15 + bootloader/mcuboot/ext/tinycrypt/LICENSE | 61 + bootloader/mcuboot/ext/tinycrypt/Makefile | 21 + bootloader/mcuboot/ext/tinycrypt/README | 71 + bootloader/mcuboot/ext/tinycrypt/VERSION | 1 + bootloader/mcuboot/ext/tinycrypt/config.mk | 35 + .../ext/tinycrypt/documentation/tinycrypt.rst | 352 + bootloader/mcuboot/ext/tinycrypt/lib/Makefile | 39 + .../ext/tinycrypt/lib/include/tinycrypt/aes.h | 130 + .../lib/include/tinycrypt/cbc_mode.h | 151 + .../lib/include/tinycrypt/ccm_mode.h | 211 + .../lib/include/tinycrypt/cmac_mode.h | 194 + .../lib/include/tinycrypt/constants.h | 61 + .../lib/include/tinycrypt/ctr_mode.h | 110 + .../lib/include/tinycrypt/ctr_prng.h | 166 + .../ext/tinycrypt/lib/include/tinycrypt/ecc.h | 545 + .../tinycrypt/lib/include/tinycrypt/ecc_dh.h | 131 + .../tinycrypt/lib/include/tinycrypt/ecc_dsa.h | 139 + .../include/tinycrypt/ecc_platform_specific.h | 81 + .../tinycrypt/lib/include/tinycrypt/hmac.h | 139 + .../lib/include/tinycrypt/hmac_prng.h | 164 + .../tinycrypt/lib/include/tinycrypt/sha256.h | 129 + .../tinycrypt/lib/include/tinycrypt/utils.h | 95 + bootloader/mcuboot/ext/tinycrypt/lib/pkg.yml | 30 + .../ext/tinycrypt/lib/source/aes_decrypt.c | 164 + .../ext/tinycrypt/lib/source/aes_encrypt.c | 191 + .../ext/tinycrypt/lib/source/cbc_mode.c | 114 + .../ext/tinycrypt/lib/source/ccm_mode.c | 266 + .../ext/tinycrypt/lib/source/cmac_mode.c | 254 + .../ext/tinycrypt/lib/source/ctr_mode.c | 91 + .../ext/tinycrypt/lib/source/ctr_prng.c | 283 + .../mcuboot/ext/tinycrypt/lib/source/ecc.c | 942 + .../mcuboot/ext/tinycrypt/lib/source/ecc_dh.c | 200 + .../ext/tinycrypt/lib/source/ecc_dsa.c | 295 + .../lib/source/ecc_platform_specific.c | 105 + .../mcuboot/ext/tinycrypt/lib/source/hmac.c | 148 + .../ext/tinycrypt/lib/source/hmac_prng.c | 212 + .../mcuboot/ext/tinycrypt/lib/source/sha256.c | 217 + .../mcuboot/ext/tinycrypt/lib/source/utils.c | 74 + .../mcuboot/ext/tinycrypt/tests/Makefile | 67 + .../tinycrypt/tests/include/test_ecc_utils.h | 100 + .../ext/tinycrypt/tests/include/test_utils.h | 125 + .../tinycrypt/tests/pseudo-random-data.bin | Bin 0 -> 524288 bytes .../mcuboot/ext/tinycrypt/tests/test_aes.c | 2078 ++ .../ext/tinycrypt/tests/test_cbc_mode.c | 177 + .../ext/tinycrypt/tests/test_ccm_mode.c | 545 + .../ext/tinycrypt/tests/test_cmac_mode.c | 314 + .../ext/tinycrypt/tests/test_ctr_mode.c | 150 + .../ext/tinycrypt/tests/test_ctr_prng.c | 565 + .../mcuboot/ext/tinycrypt/tests/test_ecc_dh.c | 521 + .../ext/tinycrypt/tests/test_ecc_dsa.c | 669 + .../ext/tinycrypt/tests/test_ecc_utils.c | 271 + .../mcuboot/ext/tinycrypt/tests/test_hmac.c | 362 + .../ext/tinycrypt/tests/test_hmac_prng.c | 135 + .../mcuboot/ext/tinycrypt/tests/test_sha256.c | 511 + bootloader/mcuboot/go.mod | 3 + bootloader/mcuboot/project.yml | 31 + bootloader/mcuboot/ptest/.gitignore | 2 + bootloader/mcuboot/ptest/Cargo.lock | 556 + bootloader/mcuboot/ptest/Cargo.toml | 18 + bootloader/mcuboot/ptest/src/main.rs | 374 + bootloader/mcuboot/repository.yml | 48 + bootloader/mcuboot/root-ec-p256-pkcs8.pem | 5 + bootloader/mcuboot/root-ec-p256.pem | 5 + bootloader/mcuboot/root-ec-p384-pkcs8.pem | 6 + bootloader/mcuboot/root-ec-p384.pem | 6 + bootloader/mcuboot/root-ed25519.pem | 3 + bootloader/mcuboot/root-rsa-2048.pem | 27 + bootloader/mcuboot/root-rsa-3072.pem | 39 + .../mcuboot_config/mcuboot_config.template.h | 198 + bootloader/mcuboot/samples/zephyr/.gitignore | 2 + bootloader/mcuboot/samples/zephyr/Makefile | 295 + bootloader/mcuboot/samples/zephyr/README.md | 24 + .../mcuboot/samples/zephyr/bad-keys/README.md | 6 + .../samples/zephyr/bad-keys/bad-ec-p256.pem | 5 + .../samples/zephyr/bad-keys/bad-rsa-2048.pem | 27 + .../mcuboot/samples/zephyr/build-boot.sh | 22 + .../mcuboot/samples/zephyr/build-hello.sh | 22 + .../samples/zephyr/hello-world/CMakeLists.txt | 26 + .../samples/zephyr/hello-world/README.rst | 6 + .../zephyr/hello-world/boards/.gitignore | 1 + .../zephyr/hello-world/boards/README.rst | 2 + .../samples/zephyr/hello-world/prj.conf | 12 + .../samples/zephyr/hello-world/sample.yaml | 9 + .../samples/zephyr/hello-world/src/Makefile | 1 + .../samples/zephyr/hello-world/src/main.c | 14 + .../samples/zephyr/mcutests/mcutests.go | 281 + .../samples/zephyr/overlay-ecdsa-p256.conf | 4 + .../mcuboot/samples/zephyr/overlay-rsa.conf | 3 + .../overlay-skip-primary-slot-validate.conf | 4 + .../samples/zephyr/overlay-upgrade-only.conf | 2 + .../mcuboot/samples/zephyr/run-tests.go | 290 + .../mcuboot/samples/zephyr/run-tests.sh | 4 + .../mcuboot/samples/zephyr/test-compile.go | 156 + bootloader/mcuboot/scripts/assemble.py | 148 + bootloader/mcuboot/scripts/flash.sh | 20 + bootloader/mcuboot/scripts/gdb-boot.sh | 29 + bootloader/mcuboot/scripts/imgtool.nix | 32 + bootloader/mcuboot/scripts/imgtool.py | 22 + .../mcuboot/scripts/imgtool/__init__.py | 17 + .../mcuboot/scripts/imgtool/boot_record.py | 52 + .../mcuboot/scripts/imgtool/dumpinfo.py | 328 + bootloader/mcuboot/scripts/imgtool/image.py | 880 + .../mcuboot/scripts/imgtool/keys/__init__.py | 105 + .../mcuboot/scripts/imgtool/keys/ecdsa.py | 289 + .../scripts/imgtool/keys/ecdsa_test.py | 120 + .../mcuboot/scripts/imgtool/keys/ed25519.py | 111 + .../scripts/imgtool/keys/ed25519_test.py | 124 + .../mcuboot/scripts/imgtool/keys/general.py | 115 + .../scripts/imgtool/keys/privatebytes.py | 16 + .../mcuboot/scripts/imgtool/keys/rsa.py | 173 + .../mcuboot/scripts/imgtool/keys/rsa_test.py | 136 + .../mcuboot/scripts/imgtool/keys/x25519.py | 119 + bootloader/mcuboot/scripts/imgtool/main.py | 618 + bootloader/mcuboot/scripts/imgtool/version.py | 56 + bootloader/mcuboot/scripts/jgdb.sh | 8 + bootloader/mcuboot/scripts/jl.sh | 7 + bootloader/mcuboot/scripts/mcubin.bt | 135 + bootloader/mcuboot/scripts/requirements.txt | 7 + bootloader/mcuboot/scripts/setup.py | 33 + bootloader/mcuboot/scripts/tests/conftest.py | 31 + .../mcuboot/scripts/tests/test_commands.py | 112 + bootloader/mcuboot/scripts/tests/test_keys.py | 253 + bootloader/mcuboot/sim/.gitignore | 2 + bootloader/mcuboot/sim/Cargo.toml | 54 + bootloader/mcuboot/sim/README.rst | 61 + bootloader/mcuboot/sim/mcuboot-sys/.gitignore | 1 + bootloader/mcuboot/sim/mcuboot-sys/Cargo.toml | 104 + bootloader/mcuboot/sim/mcuboot-sys/build.rs | 532 + .../sim/mcuboot-sys/csupport/bootsim.h | 26 + .../csupport/config-add-psa-crypto.h | 47 + .../sim/mcuboot-sys/csupport/config-asn1.h | 44 + .../sim/mcuboot-sys/csupport/config-ec-psa.h | 35 + .../sim/mcuboot-sys/csupport/config-ec.h | 95 + .../sim/mcuboot-sys/csupport/config-ed25519.h | 77 + .../sim/mcuboot-sys/csupport/config-kw.h | 65 + .../sim/mcuboot-sys/csupport/config-rsa-kw.h | 84 + .../sim/mcuboot-sys/csupport/config-rsa.h | 85 + .../sim/mcuboot-sys/csupport/devicetree.h | 20 + .../flash_map_backend/flash_map_backend.h | 38 + .../mcuboot/sim/mcuboot-sys/csupport/keys.c | 330 + .../csupport/mcuboot_config/mcuboot_assert.h | 28 + .../csupport/mcuboot_config/mcuboot_config.h | 28 + .../csupport/mcuboot_config/mcuboot_logging.h | 107 + .../sim/mcuboot-sys/csupport/os/os_heap.h | 10 + .../sim/mcuboot-sys/csupport/os/os_malloc.h | 10 + .../csupport/psa_crypto_init_stub.c | 22 + .../mcuboot/sim/mcuboot-sys/csupport/run.c | 543 + .../sim/mcuboot-sys/csupport/security_cnt.c | 43 + .../mcuboot-sys/csupport/storage/flash_map.h | 171 + .../mcuboot-sys/csupport/sysflash/sysflash.h | 46 + bootloader/mcuboot/sim/mcuboot-sys/src/api.rs | 390 + .../mcuboot/sim/mcuboot-sys/src/area.rs | 210 + bootloader/mcuboot/sim/mcuboot-sys/src/c.rs | 226 + bootloader/mcuboot/sim/mcuboot-sys/src/lib.rs | 52 + bootloader/mcuboot/sim/simflash/.gitignore | 1 + bootloader/mcuboot/sim/simflash/Cargo.toml | 10 + bootloader/mcuboot/sim/simflash/src/lib.rs | 377 + bootloader/mcuboot/sim/simflash/src/pdump.rs | 80 + bootloader/mcuboot/sim/src/caps.rs | 62 + bootloader/mcuboot/sim/src/depends.rs | 187 + .../mcuboot/sim/src/ecdsa_pub_key-rs.txt | 32 + .../mcuboot/sim/src/ed25519_pub_key-rs.txt | 8 + bootloader/mcuboot/sim/src/image.rs | 2332 ++ bootloader/mcuboot/sim/src/lib.rs | 231 + bootloader/mcuboot/sim/src/main.rs | 10 + .../mcuboot/sim/src/rsa3072_pub_key-rs.txt | 53 + bootloader/mcuboot/sim/src/rsa_pub_key-rs.txt | 37 + bootloader/mcuboot/sim/src/testlog.rs | 23 + bootloader/mcuboot/sim/src/tlv.rs | 841 + bootloader/mcuboot/sim/src/utils.rs | 11 + bootloader/mcuboot/sim/tests/core.rs | 201 + bootloader/mcuboot/testplan/mynewt/Makefile | 91 + .../testplan/mynewt/apps/blinky/pkg.yml | 35 + .../testplan/mynewt/apps/blinky/src/main.c | 78 + .../testplan/mynewt/apps/blinky/syscfg.yml | 8 + .../testplan/mynewt/apps/slinky/pkg.yml | 39 + .../testplan/mynewt/apps/slinky/src/main.c | 296 + .../mynewt/apps/slinky/src/random_data.c | 21354 ++++++++++++++++ .../testplan/mynewt/apps/slinky/syscfg.yml | 34 + bootloader/mcuboot/testplan/mynewt/key_ec.pem | 5 + .../mcuboot/testplan/mynewt/key_ec256.pem | 5 + .../mcuboot/testplan/mynewt/key_ec256_2.pem | 5 + .../mcuboot/testplan/mynewt/key_ec_2.pem | 5 + .../mcuboot/testplan/mynewt/key_rsa.pem | 27 + .../mcuboot/testplan/mynewt/key_rsa_2.pem | 27 + .../testplan/mynewt/keys/ec256/pkg.yml | 3 + .../testplan/mynewt/keys/ec256/src/keys.c | 19 + .../mcuboot/testplan/mynewt/keys/pkg.yml | 9 + .../mcuboot/testplan/mynewt/keys/rsa/pkg.yml | 3 + .../testplan/mynewt/keys/rsa/src/keys.c | 34 + .../mcuboot/testplan/mynewt/project.yml | 36 + bootloader/mcuboot/zephyr/module.yml | 6 + docs/BLE.xlsx | Bin 0 -> 9852 bytes docs/IDE_setup_and_program.docx | Bin 0 -> 360512 bytes docs/interface.xlsx | Bin 0 -> 9630 bytes docs/shotData/diagrams.vsdx | Bin 0 -> 23001 bytes docs/shotData/format.xlsx | Bin 0 -> 15730 bytes docs/shotData/shotDataExamples.md | 19 + docs/shotData/shotDataFormat.docx | Bin 0 -> 80942 bytes docs/shot_capture_flow_dia.vsdx | Bin 0 -> 27830 bytes docs/thread_analysis.xlsx | Bin 0 -> 9472 bytes keys/cyber_pk.pem | 5 + keys/readme.txt | 13 + keys/test_pk.pem | 5 + prj.conf | 192 + releases/20251216_v1_0_0.zip | Bin 0 -> 354619 bytes requirements/SamplePaperScoreSheets.docx | Bin 0 -> 41655 bytes requirements/goals_and_use_cases.md | 150 + requirements/tbd.md | 94 + src/app/app.c | 537 + src/app/app.h | 93 + src/app/app_cli.c | 109 + src/app/app_cli.h | 50 + src/app/ble.c | 2265 ++ src/app/ble.h | 187 + src/app/bubble.c | 261 + src/app/bubble.h | 65 + src/app/event.c | 984 + src/app/event.h | 92 + src/app/fiber.c | 261 + src/app/fiber.h | 65 + src/app/fsm.c | 2237 ++ src/app/fsm.h | 139 + src/app/light_sensor.c | 210 + src/app/light_sensor.h | 52 + src/app/log.c | 192 + src/app/log.h | 59 + src/app/motion.c | 664 + src/app/motion.h | 120 + src/app/pin.c | 261 + src/app/pin.h | 65 + src/app/scope_ring.c | 1887 ++ src/app/scope_ring.h | 162 + src/app/shot_capture.c | 1001 + src/app/shot_capture.h | 146 + src/app/shot_storage.c | 835 + src/app/shot_storage.h | 176 + src/app/uuid.h | 181 + src/app/uuid_base.h | 35 + src/bsp/als.c | 634 + src/bsp/als.h | 105 + src/bsp/battery.c | 416 + src/bsp/battery.h | 54 + src/bsp/button.c | 274 + src/bsp/button.h | 56 + src/bsp/imu.c | 3606 +++ src/bsp/imu.h | 202 + src/bsp/imu_reg.h | 2211 ++ src/bsp/led.c | 159 + src/bsp/led.h | 59 + src/bsp/led_ring.c | 501 + src/bsp/led_ring.h | 60 + src/bsp/mcu.c | 365 + src/bsp/mcu.h | 53 + src/bsp/nvm_ext.c | 736 + src/bsp/nvm_ext.h | 73 + src/bsp/power.c | 161 + src/bsp/power.h | 48 + src/bsp/rtc.c | 150 + src/bsp/rtc.h | 50 + src/bsp/wdt.c | 175 + src/bsp/wdt.h | 49 + src/lib/cli.c | 333 + src/lib/cli.h | 57 + src/lib/crc.c | 127 + src/lib/crc.h | 57 + src/lib/debug.c | 37 + src/lib/debug.h | 102 + src/lib/errno.c | 83 + src/lib/errno.h | 83 + src/lib/fs.c | 1196 + src/lib/fs.h | 94 + src/lib/gamma.c | 64 + src/lib/gamma.h | 39 + src/lib/print.c | 179 + src/lib/print.h | 53 + src/lib/rgb.c | 93 + src/lib/rgb.h | 63 + src/lib/virtual_led_ring.c | 284 + src/lib/virtual_led_ring.h | 97 + src/main.c | 29 + src/wrappers/adc.c | 185 + src/wrappers/adc.h | 104 + src/wrappers/gpio - Copy.c | 437 + src/wrappers/gpio - Copy.h | 100 + src/wrappers/gpio.c | 523 + src/wrappers/gpio.h | 98 + src/wrappers/i2c.c | 468 + src/wrappers/i2c.h | 67 + src/wrappers/pwm.c | 173 + src/wrappers/pwm.h | 65 + src/wrappers/spi.c | 179 + src/wrappers/spi.h | 47 + src/wrappers/uart.c | 184 + src/wrappers/uart.h | 60 + sysbuild.conf | 7 + sysbuild/mcuboot.conf | 12 + 872 files changed, 165227 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Kconfig create mode 100644 VERSION create mode 100644 boards/MAPS/cyber_nrf52840/Kconfig.cyber_nrf52840 create mode 100644 boards/MAPS/cyber_nrf52840/Kconfig.defconfig create mode 100644 boards/MAPS/cyber_nrf52840/board.cmake create mode 100644 boards/MAPS/cyber_nrf52840/board.yml create mode 100644 boards/MAPS/cyber_nrf52840/cyber_nrf52840-pinctrl.dtsi create mode 100644 boards/MAPS/cyber_nrf52840/cyber_nrf52840.dts create mode 100644 boards/MAPS/cyber_nrf52840/cyber_nrf52840.yml create mode 100644 boards/MAPS/cyber_nrf52840/cyber_nrf52840_defconfig create mode 100644 boards/MAPS/cyber_nrf52840/pre_dt_board.cmake create mode 100644 bootloader/mcuboot/.gitignore create mode 100644 bootloader/mcuboot/.gitmodules create mode 100644 bootloader/mcuboot/.mbedignore create mode 100644 bootloader/mcuboot/.travis.yml-disabled create mode 100644 bootloader/mcuboot/CODE_OF_CONDUCT.md create mode 100644 bootloader/mcuboot/Cargo.lock create mode 100644 bootloader/mcuboot/Cargo.toml create mode 100644 bootloader/mcuboot/LICENSE create mode 100644 bootloader/mcuboot/NOTICE create mode 100644 bootloader/mcuboot/README.md create mode 100644 bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial.h create mode 100644 bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial_encryption.h create mode 100644 bootloader/mcuboot/boot/boot_serial/pkg.yml create mode 100644 bootloader/mcuboot/boot/boot_serial/src/boot_serial.c create mode 100644 bootloader/mcuboot/boot/boot_serial/src/boot_serial_encryption.c create mode 100644 bootloader/mcuboot/boot/boot_serial/src/boot_serial_priv.h create mode 100644 bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.c create mode 100644 bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.h create mode 100644 bootloader/mcuboot/boot/boot_serial/syscfg.yml create mode 100644 bootloader/mcuboot/boot/boot_serial/test/pkg.yml create mode 100644 bootloader/mcuboot/boot/boot_serial/test/src/boot_test.c create mode 100644 bootloader/mcuboot/boot/boot_serial/test/src/boot_test.h create mode 100644 bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_img_msg.c create mode 100644 bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_msg.c create mode 100644 bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_img_msg.c create mode 100644 bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_setup.c create mode 100644 bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c create mode 100644 bootloader/mcuboot/boot/boot_serial/test/syscfg.yml create mode 100644 bootloader/mcuboot/boot/bootutil/CMakeLists.txt create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/bench.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/boot_hooks.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/boot_public_hooks.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/boot_record.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/boot_status.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_log.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_public.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_test.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/caps.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_ctr.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_kw.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/common.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_p256.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_x25519.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdsa.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/hmac_sha256.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/rsa.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/sha.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key_public.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening_delay_rng.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/ignore.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/image.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/mcuboot_status.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/ramload.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/security_cnt.h create mode 100644 bootloader/mcuboot/boot/bootutil/include/bootutil/sign_key.h create mode 100644 bootloader/mcuboot/boot/bootutil/pkg.yml create mode 100644 bootloader/mcuboot/boot/bootutil/src/boot_record.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/bootutil_misc.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/bootutil_misc.h create mode 100644 bootloader/mcuboot/boot/bootutil/src/bootutil_priv.h create mode 100644 bootloader/mcuboot/boot/bootutil/src/bootutil_public.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/caps.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/ed25519_psa.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/encrypted.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/encrypted_psa.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening_delay_rng_mbedtls.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/image_ecdsa.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/image_ed25519.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/image_rsa.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/image_validate.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/loader.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/loader_legacy_child_parent.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/swap_misc.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/swap_move.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/swap_nsib.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/swap_priv.h create mode 100644 bootloader/mcuboot/boot/bootutil/src/swap_scratch.c create mode 100644 bootloader/mcuboot/boot/bootutil/src/tlv.c create mode 100644 bootloader/mcuboot/boot/bootutil/zephyr/CMakeLists.txt create mode 100644 bootloader/mcuboot/boot/cypress/.gitignore create mode 100644 bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp.mk create mode 100644 bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp_CM4_Debug.launch create mode 100644 bootloader/mcuboot/boot/cypress/BlinkyApp/Readme.md create mode 100644 bootloader/mcuboot/boot/cypress/BlinkyApp/libs.mk create mode 100644 bootloader/mcuboot/boot/cypress/BlinkyApp/linker/BlinkyApp_template.ld create mode 100644 bootloader/mcuboot/boot/cypress/BlinkyApp/main.c create mode 100644 bootloader/mcuboot/boot/cypress/BlinkyApp/main.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/ExternalMemory.md create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.ld create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.mk create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp_CM0P_Debug.launch create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/README.md create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_assert.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_config.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_logging.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_acc_config.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_config.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/cy_security_cnt.c create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/cy_serial_flash_prog.c create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/keys.c create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/libs.mk create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/main.c create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/os/os.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_heap.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_malloc.h create mode 100644 bootloader/mcuboot/boot/cypress/MCUBootApp/sysflash/sysflash.h create mode 100644 bootloader/mcuboot/boot/cypress/Makefile create mode 100644 bootloader/mcuboot/boot/cypress/README.md create mode 100644 bootloader/mcuboot/boot/cypress/common_libs.mk create mode 100644 bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_flash_map.c create mode 100644 bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_smif_psoc6.c create mode 100644 bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.c create mode 100644 bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.h create mode 100644 bootloader/mcuboot/boot/cypress/cy_flash_pal/include/cy_smif_psoc6.h create mode 100644 bootloader/mcuboot/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h create mode 100644 bootloader/mcuboot/boot/cypress/host.mk create mode 100644 bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pem create mode 100644 bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pub create mode 100644 bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.c create mode 100644 bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.h create mode 100644 bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.c create mode 100644 bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.h create mode 100644 bootloader/mcuboot/boot/cypress/platforms.mk create mode 100644 bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/cy8c6xxa_cm0plus.ld create mode 100644 bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/startup_psoc6_02_cm0plus.S create mode 100644 bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/cy8c6xxa_cm4_dual.ld create mode 100644 bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/startup_psoc6_02_cm4.S create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg.c create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg.h create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.c create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.h create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.c create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.h create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.c create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.h create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.c create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.h create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_system.c create mode 100644 bootloader/mcuboot/boot/cypress/platforms/cycfg_system.h create mode 100644 bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.c create mode 100644 bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.h create mode 100644 bootloader/mcuboot/boot/cypress/toolchains.mk create mode 100644 bootloader/mcuboot/boot/espressif/CMakeLists.txt create mode 100644 bootloader/mcuboot/boot/espressif/ci_configs/multi-boot.conf create mode 100644 bootloader/mcuboot/boot/espressif/ci_configs/multi-image.conf create mode 100644 bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ec256.conf create mode 100644 bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ed25519.conf create mode 100644 bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa2048.conf create mode 100644 bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa3072.conf create mode 100644 bootloader/mcuboot/boot/espressif/ci_configs/serialrecovery.conf create mode 100644 bootloader/mcuboot/boot/espressif/hal/CMakeLists.txt create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/app_cpu_start.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/bootloader_wdt.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32/esp32.cmake create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32/sdkconfig.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32c2/esp32c2.cmake create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32c2/sdkconfig.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32c3/esp32c3.cmake create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32c3/sdkconfig.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32c6/esp32c6.cmake create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32c6/sdkconfig.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32h2/esp32h2.cmake create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32h2/sdkconfig.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32s2/esp32s2.cmake create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32s2/sdkconfig.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32s3/esp32s3.cmake create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp32s3/sdkconfig.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp_log.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/esp_mcuboot_image.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_assert.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_logging.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/include/soc_log.h create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/bootloader_banner.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/bootloader_wdt.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/esp32/app_cpu_start.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/esp32/console_uart_custom.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/esp32c2/console_uart_custom.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/esp32c3/console_uart_custom.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/esp32c6/console_uart_custom.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/esp32h2/console_uart_custom.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/esp32s3/app_cpu_start.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/flash_encrypt.c create mode 100644 bootloader/mcuboot/boot/espressif/hal/src/secure_boot.c create mode 100644 bootloader/mcuboot/boot/espressif/include/crypto_config/ec256.cmake create mode 100644 bootloader/mcuboot/boot/espressif/include/crypto_config/ed25519.cmake create mode 100644 bootloader/mcuboot/boot/espressif/include/crypto_config/mbedtls_custom_config.h create mode 100644 bootloader/mcuboot/boot/espressif/include/crypto_config/rsa.cmake create mode 100644 bootloader/mcuboot/boot/espressif/include/esp_loader.h create mode 100644 bootloader/mcuboot/boot/espressif/include/flash_map_backend/flash_map_backend.h create mode 100644 bootloader/mcuboot/boot/espressif/include/os/os.h create mode 100644 bootloader/mcuboot/boot/espressif/include/os/os_malloc.h create mode 100644 bootloader/mcuboot/boot/espressif/include/serial_adapter/serial_adapter.h create mode 100644 bootloader/mcuboot/boot/espressif/include/sysflash/sysflash.h create mode 100644 bootloader/mcuboot/boot/espressif/keys.c create mode 100644 bootloader/mcuboot/boot/espressif/main.c create mode 100644 bootloader/mcuboot/boot/espressif/os.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32/bootloader-multi.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32/bootloader.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32/ld/bootloader.ld create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c2/bootloader.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c2/ld/bootloader.ld create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c2/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c3/bootloader.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c3/ld/bootloader.ld create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c3/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c6/bootloader.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c6/ld/bootloader.ld create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32c6/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32h2/bootloader.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32h2/ld/bootloader.ld create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32h2/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32s2/bootloader.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32s2/ld/bootloader.ld create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32s2/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader-multi.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader.conf create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32s3/ld/bootloader.ld create mode 100644 bootloader/mcuboot/boot/espressif/port/esp32s3/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp_loader.c create mode 100644 bootloader/mcuboot/boot/espressif/port/esp_mcuboot.c create mode 100644 bootloader/mcuboot/boot/espressif/tools/toolchain-esp32.cmake create mode 100644 bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c2.cmake create mode 100644 bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c3.cmake create mode 100644 bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c6.cmake create mode 100644 bootloader/mcuboot/boot/espressif/tools/toolchain-esp32h2.cmake create mode 100644 bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s2.cmake create mode 100644 bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s3.cmake create mode 100644 bootloader/mcuboot/boot/espressif/tools/utils.cmake create mode 100644 bootloader/mcuboot/boot/mbed/CMakeLists.txt create mode 100644 bootloader/mcuboot/boot/mbed/app_enc_keys.c create mode 100644 bootloader/mcuboot/boot/mbed/include/flash_map_backend/flash_map_backend.h create mode 100644 bootloader/mcuboot/boot/mbed/include/flash_map_backend/secondary_bd.h create mode 100644 bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_assert.h create mode 100644 bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_config.h create mode 100644 bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_logging.h create mode 100644 bootloader/mcuboot/boot/mbed/include/os/os_malloc.h create mode 100644 bootloader/mcuboot/boot/mbed/include/sysflash/sysflash.h create mode 100644 bootloader/mcuboot/boot/mbed/include/utils/DataShare.cpp create mode 100644 bootloader/mcuboot/boot/mbed/include/utils/DataShare.h create mode 100644 bootloader/mcuboot/boot/mbed/mbed_lib.json create mode 100644 bootloader/mcuboot/boot/mbed/mcuboot_main.cpp create mode 100644 bootloader/mcuboot/boot/mbed/src/flash_map_backend.cpp create mode 100644 bootloader/mcuboot/boot/mbed/src/secondary_bd.cpp create mode 100644 bootloader/mcuboot/boot/mynewt/README.md create mode 100644 bootloader/mcuboot/boot/mynewt/boot_uart/include/boot_uart/boot_uart.h create mode 100644 bootloader/mcuboot/boot/mynewt/boot_uart/pkg.yml create mode 100644 bootloader/mcuboot/boot/mynewt/boot_uart/src/boot_uart.c create mode 100644 bootloader/mcuboot/boot/mynewt/boot_uart/syscfg.yml create mode 100644 bootloader/mcuboot/boot/mynewt/flash_map_backend/include/flash_map_backend/flash_map_backend.h create mode 100644 bootloader/mcuboot/boot/mynewt/flash_map_backend/pkg.yml create mode 100644 bootloader/mcuboot/boot/mynewt/flash_map_backend/src/flash_map_extended.c create mode 100644 bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h create mode 100644 bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_logging.h create mode 100644 bootloader/mcuboot/boot/mynewt/mcuboot_config/pkg.yml create mode 100644 bootloader/mcuboot/boot/mynewt/mcuboot_config/syscfg.yml create mode 100644 bootloader/mcuboot/boot/mynewt/pkg.yml create mode 100644 bootloader/mcuboot/boot/mynewt/src/main.c create mode 100644 bootloader/mcuboot/boot/mynewt/src/single_loader.c create mode 100644 bootloader/mcuboot/boot/mynewt/syscfg.yml create mode 100644 bootloader/mcuboot/boot/nuttx/include/flash_map_backend/flash_map_backend.h create mode 100644 bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_config.h create mode 100644 bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_logging.h create mode 100644 bootloader/mcuboot/boot/nuttx/include/os/os_malloc.h create mode 100644 bootloader/mcuboot/boot/nuttx/include/sysflash/sysflash.h create mode 100644 bootloader/mcuboot/boot/nuttx/include/watchdog/watchdog.h create mode 100644 bootloader/mcuboot/boot/nuttx/main.c create mode 100644 bootloader/mcuboot/boot/nuttx/src/flash_map_backend/flash_map_backend.c create mode 100644 bootloader/mcuboot/boot/nuttx/src/watchdog/watchdog.c create mode 100644 bootloader/mcuboot/boot/zcbor/add_zcbor_copy_version.sh create mode 100644 bootloader/mcuboot/boot/zcbor/include/zcbor_common.h create mode 100644 bootloader/mcuboot/boot/zcbor/include/zcbor_decode.h create mode 100644 bootloader/mcuboot/boot/zcbor/include/zcbor_encode.h create mode 100644 bootloader/mcuboot/boot/zcbor/include/zcbor_print.h create mode 100644 bootloader/mcuboot/boot/zcbor/include/zcbor_tags.h create mode 100644 bootloader/mcuboot/boot/zcbor/pkg.yml create mode 100644 bootloader/mcuboot/boot/zcbor/src/zcbor_common.c create mode 100644 bootloader/mcuboot/boot/zcbor/src/zcbor_decode.c create mode 100644 bootloader/mcuboot/boot/zcbor/src/zcbor_encode.c create mode 100644 bootloader/mcuboot/boot/zephyr/CMakeLists.txt create mode 100644 bootloader/mcuboot/boot/zephyr/Kconfig create mode 100644 bootloader/mcuboot/boot/zephyr/Kconfig.firmware_loader create mode 100644 bootloader/mcuboot/boot/zephyr/Kconfig.serial_recovery create mode 100644 bootloader/mcuboot/boot/zephyr/VERSION create mode 100644 bootloader/mcuboot/boot/zephyr/app.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/arm_cleanup.c create mode 100644 bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_bee_nrf9160.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_nrf9160.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_dk_nrf9160.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_nrf9160.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/bl5340_dvk_nrf5340_cpuapp.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/circuitdojo_feather_nrf9160.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/conexio_stratus.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/disco_l475_iot1_stm32l475xx.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/flash_sim_driver.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/frdm_k64f_mk64f12.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s06_lpc55s06.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s16_lpc55s16.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s28_lpc55s28.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s36_lpc55s36.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s69_lpc55s69_cpu0.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1010_evk_mimxrt1011.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1015_evk_mimxrt1015.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1020_evk_mimxrt1021.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1024_evk_mimxrt1024.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1040_evk_mimxrt1042.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1050_evk_mimxrt1052.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062_cm7.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evkb_mimxrt1062.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1062_fmurt6.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1064_evk_mimxrt1064.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1160_evk_mimxrt1166_cm7.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt1170_evk_mimxrt1176_cm7.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt595_evk_mimxrt595s_cm33.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/mimxrt685_evk_mimxrt685s_cm33.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf51dk_nrf51822.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840_big.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840_single_slot.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_hooks_sample_overlay.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor_secondary.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram_multi.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52840dongle_nrf52840.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf52_minimal_footprint.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf5340dk_nrf5340_cpuapp_minimal.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/nrf9161dk_nrf9161_0_7_0.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/odroid_go_esp32_procpu.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/pinnacle_100_dvk_nrf52840.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/rd_rw612_bga_rw612.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/sparkfun_thing_plus_nrf9160.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/thingy52_nrf52832.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf52840.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf9160.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/boards/tlsr9518adk80d_tlsr9518.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boards/vmu_rt1170_mimxrt1176_cm7.conf create mode 100644 bootloader/mcuboot/boot/zephyr/boot_serial_extension_zephyr_basic.c create mode 100644 bootloader/mcuboot/boot/zephyr/boot_serial_extensions.c create mode 100644 bootloader/mcuboot/boot/zephyr/decompression.c create mode 100644 bootloader/mcuboot/boot/zephyr/external_crypto.conf create mode 100644 bootloader/mcuboot/boot/zephyr/firmware_loader.c create mode 100644 bootloader/mcuboot/boot/zephyr/flash_map_extended.c create mode 100644 bootloader/mcuboot/boot/zephyr/flash_map_legacy.c create mode 100644 bootloader/mcuboot/boot/zephyr/hooks_sample.c create mode 100644 bootloader/mcuboot/boot/zephyr/include/arm_cleanup.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial.ld create mode 100644 bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial_extensions.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/compression/decompression.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/config-asn1.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/config-ec.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/config-ed25519.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/config-kw.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/config-rsa-kw.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/config-rsa.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/flash_map_backend/flash_map_backend.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/hal/hal_bsp.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/hal/hal_flash.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/io/io.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/mcuboot-mbedtls-cfg.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_config.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_logging.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/nrf_cleanup.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/os/os.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/os/os_heap.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/os/os_malloc.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/platform-bench.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/serial_adapter/serial_adapter.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash_legacy_child_parent.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/sysflash/sysflash.h create mode 100644 bootloader/mcuboot/boot/zephyr/include/target.h create mode 100644 bootloader/mcuboot/boot/zephyr/io.c create mode 100644 bootloader/mcuboot/boot/zephyr/kernel/banner.c create mode 100644 bootloader/mcuboot/boot/zephyr/keys.c create mode 100644 bootloader/mcuboot/boot/zephyr/main.c create mode 100644 bootloader/mcuboot/boot/zephyr/nrf52840dk_nrf52840_cc310_ecdsa.conf create mode 100644 bootloader/mcuboot/boot/zephyr/nrf_cleanup.c create mode 100644 bootloader/mcuboot/boot/zephyr/os.c create mode 100644 bootloader/mcuboot/boot/zephyr/pm.yml create mode 100644 bootloader/mcuboot/boot/zephyr/prj.conf create mode 100644 bootloader/mcuboot/boot/zephyr/prj_minimal.conf create mode 100644 bootloader/mcuboot/boot/zephyr/ram_load.conf create mode 100644 bootloader/mcuboot/boot/zephyr/sample.yaml create mode 100644 bootloader/mcuboot/boot/zephyr/serial_adapter.c create mode 100644 bootloader/mcuboot/boot/zephyr/serial_recovery.conf create mode 100644 bootloader/mcuboot/boot/zephyr/shared_data.c create mode 100644 bootloader/mcuboot/boot/zephyr/single_loader.c create mode 100644 bootloader/mcuboot/boot/zephyr/single_slot.conf create mode 100644 bootloader/mcuboot/boot/zephyr/socs/esp32_procpu.conf create mode 100644 bootloader/mcuboot/boot/zephyr/socs/esp32c2.conf create mode 100644 bootloader/mcuboot/boot/zephyr/socs/esp32c3.conf create mode 100644 bootloader/mcuboot/boot/zephyr/socs/esp32c6.conf create mode 100644 bootloader/mcuboot/boot/zephyr/socs/esp32s2.conf create mode 100644 bootloader/mcuboot/boot/zephyr/socs/esp32s3_procpu.conf create mode 100644 bootloader/mcuboot/boot/zephyr/sysbuild/CMakeLists.txt create mode 100644 bootloader/mcuboot/boot/zephyr/targets/arduino_101.h create mode 100644 bootloader/mcuboot/boot/zephyr/usb_cdc_acm.overlay create mode 100644 bootloader/mcuboot/boot/zephyr/usb_cdc_acm_log_recovery.conf create mode 100644 bootloader/mcuboot/ci/check-signed-off-by.sh create mode 100644 bootloader/mcuboot/ci/compare_versions.py create mode 100644 bootloader/mcuboot/ci/espressif_install.sh create mode 100644 bootloader/mcuboot/ci/espressif_run.sh create mode 100644 bootloader/mcuboot/ci/fih-tests_install.sh create mode 100644 bootloader/mcuboot/ci/fih-tests_run.sh create mode 100644 bootloader/mcuboot/ci/fih-tests_version.sh create mode 100644 bootloader/mcuboot/ci/fih_test_docker/damage_image.py create mode 100644 bootloader/mcuboot/ci/fih_test_docker/docker-build/Dockerfile create mode 100644 bootloader/mcuboot/ci/fih_test_docker/docker-build/build.sh create mode 100644 bootloader/mcuboot/ci/fih_test_docker/execute_test.sh create mode 100644 bootloader/mcuboot/ci/fih_test_docker/fi_make_manifest.sh create mode 100644 bootloader/mcuboot/ci/fih_test_docker/fi_tester_gdb.sh create mode 100644 bootloader/mcuboot/ci/fih_test_docker/generate_test_report.py create mode 100644 bootloader/mcuboot/ci/fih_test_docker/paths.sh create mode 100644 bootloader/mcuboot/ci/fih_test_docker/run_fi_test.sh create mode 100644 bootloader/mcuboot/ci/fih_test_docker/utils.py create mode 100644 bootloader/mcuboot/ci/fih_test_docker/validate_output.py create mode 100644 bootloader/mcuboot/ci/get_features.py create mode 100644 bootloader/mcuboot/ci/imgtool_install.sh create mode 100644 bootloader/mcuboot/ci/imgtool_run.sh create mode 100644 bootloader/mcuboot/ci/mynewt_install.sh create mode 100644 bootloader/mcuboot/ci/mynewt_keys/enc_kw/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_keys/enc_kw/src/keys.c create mode 100644 bootloader/mcuboot/ci/mynewt_keys/enc_rsa/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_keys/enc_rsa/src/keys.c create mode 100644 bootloader/mcuboot/ci/mynewt_run.sh create mode 100644 bootloader/mcuboot/ci/mynewt_targets/basic/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/basic/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/basic/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/bootserial/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/bootserial/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/bootserial/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/ecdsa/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/ecdsa/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/ecdsa/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_kw/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_kw/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_kw/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/target.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/swap_move/pkg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/swap_move/syscfg.yml create mode 100644 bootloader/mcuboot/ci/mynewt_targets/swap_move/target.yml create mode 100644 bootloader/mcuboot/ci/requirements.txt create mode 100644 bootloader/mcuboot/ci/sim_install.sh create mode 100644 bootloader/mcuboot/ci/sim_run.sh create mode 100644 bootloader/mcuboot/docs/.gitignore create mode 100644 bootloader/mcuboot/docs/CNAME create mode 100644 bootloader/mcuboot/docs/Gemfile create mode 100644 bootloader/mcuboot/docs/Gemfile.lock create mode 100644 bootloader/mcuboot/docs/PORTING.md create mode 100644 bootloader/mcuboot/docs/SECURITY.md create mode 100644 bootloader/mcuboot/docs/SubmittingPatches.md create mode 100644 bootloader/mcuboot/docs/_config.yml create mode 100644 bootloader/mcuboot/docs/design.md create mode 100644 bootloader/mcuboot/docs/ecdsa.md create mode 100644 bootloader/mcuboot/docs/encrypted_images.md create mode 100644 bootloader/mcuboot/docs/imgtool.md create mode 100644 bootloader/mcuboot/docs/index.md create mode 100644 bootloader/mcuboot/docs/readme-espressif.md create mode 100644 bootloader/mcuboot/docs/readme-mbed.md create mode 100644 bootloader/mcuboot/docs/readme-mynewt.md create mode 100644 bootloader/mcuboot/docs/readme-nuttx.md create mode 100644 bootloader/mcuboot/docs/readme-riot.md create mode 100644 bootloader/mcuboot/docs/readme-zephyr.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/00readme.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/bootutil-enc-hw-keys.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/bootutil-image-verification.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/encrypted-scratch-partition.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/fix-nordic.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/fix-ram-load-zephyr-address.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/fix-zephyr-sysbuild-name.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/imgtool_sanity_test.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/max-app-size-changes.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/serial-recovery-slot-info.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/zephyr-auto-max-sectors.md create mode 100644 bootloader/mcuboot/docs/release-notes.d/zephyr-compression.md create mode 100644 bootloader/mcuboot/docs/release-notes.md create mode 100644 bootloader/mcuboot/docs/release.md create mode 100644 bootloader/mcuboot/docs/serial_recovery.md create mode 100644 bootloader/mcuboot/docs/signed_images.md create mode 100644 bootloader/mcuboot/docs/testplan-mynewt.md create mode 100644 bootloader/mcuboot/docs/testplan-zephyr.md create mode 100644 bootloader/mcuboot/enc-aes128kw.b64 create mode 100644 bootloader/mcuboot/enc-aes256kw.b64 create mode 100644 bootloader/mcuboot/enc-ec256-priv.pem create mode 100644 bootloader/mcuboot/enc-ec256-pub.pem create mode 100644 bootloader/mcuboot/enc-rsa2048-priv.pem create mode 100644 bootloader/mcuboot/enc-rsa2048-pub.pem create mode 100644 bootloader/mcuboot/enc-x25519-priv.pem create mode 100644 bootloader/mcuboot/enc-x25519-pub.pem create mode 100644 bootloader/mcuboot/ext/fiat/LICENSE create mode 100644 bootloader/mcuboot/ext/fiat/METADATA create mode 100644 bootloader/mcuboot/ext/fiat/README.chromium create mode 100644 bootloader/mcuboot/ext/fiat/README.md create mode 100644 bootloader/mcuboot/ext/fiat/pkg.yml create mode 100644 bootloader/mcuboot/ext/fiat/src/curve25519.c create mode 100644 bootloader/mcuboot/ext/fiat/src/curve25519.h create mode 100644 bootloader/mcuboot/ext/fiat/src/curve25519_tables.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/README create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/common.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/asn1.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/bignum.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/build_info.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/check_config.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecdsa.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecp.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/error.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/mbedtls_config.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/md.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/oid.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/pk.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform_util.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/private_access.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/rsa.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/threading.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/version.h create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/pkg.yml create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/src/asn1parse.c create mode 100644 bootloader/mcuboot/ext/mbedtls-asn1/src/platform_util.c create mode 100644 bootloader/mcuboot/ext/nrf/README.md create mode 100644 bootloader/mcuboot/ext/nrf/cc310_glue.c create mode 100644 bootloader/mcuboot/ext/nrf/cc310_glue.h create mode 100644 bootloader/mcuboot/ext/tinycrypt-sha512/lib/include/tinycrypt/sha512.h create mode 100644 bootloader/mcuboot/ext/tinycrypt-sha512/lib/pkg.yml create mode 100644 bootloader/mcuboot/ext/tinycrypt-sha512/lib/source/sha512.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/.gitignore create mode 100644 bootloader/mcuboot/ext/tinycrypt/AUTHORS create mode 100644 bootloader/mcuboot/ext/tinycrypt/LICENSE create mode 100644 bootloader/mcuboot/ext/tinycrypt/Makefile create mode 100644 bootloader/mcuboot/ext/tinycrypt/README create mode 100644 bootloader/mcuboot/ext/tinycrypt/VERSION create mode 100644 bootloader/mcuboot/ext/tinycrypt/config.mk create mode 100644 bootloader/mcuboot/ext/tinycrypt/documentation/tinycrypt.rst create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/Makefile create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/aes.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cbc_mode.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ccm_mode.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cmac_mode.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/constants.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_mode.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_prng.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dh.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dsa.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac_prng.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/sha256.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/utils.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/pkg.yml create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/aes_decrypt.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/aes_encrypt.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/cbc_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/ccm_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/cmac_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_prng.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/ecc.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dh.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dsa.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_platform_specific.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/hmac.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/hmac_prng.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/sha256.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/lib/source/utils.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/Makefile create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/include/test_ecc_utils.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/include/test_utils.h create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/pseudo-random-data.bin create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_aes.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_cbc_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_ccm_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_cmac_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_mode.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_prng.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_dh.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_dsa.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_utils.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_hmac.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_hmac_prng.c create mode 100644 bootloader/mcuboot/ext/tinycrypt/tests/test_sha256.c create mode 100644 bootloader/mcuboot/go.mod create mode 100644 bootloader/mcuboot/project.yml create mode 100644 bootloader/mcuboot/ptest/.gitignore create mode 100644 bootloader/mcuboot/ptest/Cargo.lock create mode 100644 bootloader/mcuboot/ptest/Cargo.toml create mode 100644 bootloader/mcuboot/ptest/src/main.rs create mode 100644 bootloader/mcuboot/repository.yml create mode 100644 bootloader/mcuboot/root-ec-p256-pkcs8.pem create mode 100644 bootloader/mcuboot/root-ec-p256.pem create mode 100644 bootloader/mcuboot/root-ec-p384-pkcs8.pem create mode 100644 bootloader/mcuboot/root-ec-p384.pem create mode 100644 bootloader/mcuboot/root-ed25519.pem create mode 100644 bootloader/mcuboot/root-rsa-2048.pem create mode 100644 bootloader/mcuboot/root-rsa-3072.pem create mode 100644 bootloader/mcuboot/samples/mcuboot_config/mcuboot_config.template.h create mode 100644 bootloader/mcuboot/samples/zephyr/.gitignore create mode 100644 bootloader/mcuboot/samples/zephyr/Makefile create mode 100644 bootloader/mcuboot/samples/zephyr/README.md create mode 100644 bootloader/mcuboot/samples/zephyr/bad-keys/README.md create mode 100644 bootloader/mcuboot/samples/zephyr/bad-keys/bad-ec-p256.pem create mode 100644 bootloader/mcuboot/samples/zephyr/bad-keys/bad-rsa-2048.pem create mode 100644 bootloader/mcuboot/samples/zephyr/build-boot.sh create mode 100644 bootloader/mcuboot/samples/zephyr/build-hello.sh create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/CMakeLists.txt create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/README.rst create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/boards/.gitignore create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/boards/README.rst create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/prj.conf create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/sample.yaml create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/src/Makefile create mode 100644 bootloader/mcuboot/samples/zephyr/hello-world/src/main.c create mode 100644 bootloader/mcuboot/samples/zephyr/mcutests/mcutests.go create mode 100644 bootloader/mcuboot/samples/zephyr/overlay-ecdsa-p256.conf create mode 100644 bootloader/mcuboot/samples/zephyr/overlay-rsa.conf create mode 100644 bootloader/mcuboot/samples/zephyr/overlay-skip-primary-slot-validate.conf create mode 100644 bootloader/mcuboot/samples/zephyr/overlay-upgrade-only.conf create mode 100644 bootloader/mcuboot/samples/zephyr/run-tests.go create mode 100644 bootloader/mcuboot/samples/zephyr/run-tests.sh create mode 100644 bootloader/mcuboot/samples/zephyr/test-compile.go create mode 100644 bootloader/mcuboot/scripts/assemble.py create mode 100644 bootloader/mcuboot/scripts/flash.sh create mode 100644 bootloader/mcuboot/scripts/gdb-boot.sh create mode 100644 bootloader/mcuboot/scripts/imgtool.nix create mode 100644 bootloader/mcuboot/scripts/imgtool.py create mode 100644 bootloader/mcuboot/scripts/imgtool/__init__.py create mode 100644 bootloader/mcuboot/scripts/imgtool/boot_record.py create mode 100644 bootloader/mcuboot/scripts/imgtool/dumpinfo.py create mode 100644 bootloader/mcuboot/scripts/imgtool/image.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/__init__.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/ecdsa.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/ecdsa_test.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/ed25519.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/ed25519_test.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/general.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/privatebytes.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/rsa.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/rsa_test.py create mode 100644 bootloader/mcuboot/scripts/imgtool/keys/x25519.py create mode 100644 bootloader/mcuboot/scripts/imgtool/main.py create mode 100644 bootloader/mcuboot/scripts/imgtool/version.py create mode 100644 bootloader/mcuboot/scripts/jgdb.sh create mode 100644 bootloader/mcuboot/scripts/jl.sh create mode 100644 bootloader/mcuboot/scripts/mcubin.bt create mode 100644 bootloader/mcuboot/scripts/requirements.txt create mode 100644 bootloader/mcuboot/scripts/setup.py create mode 100644 bootloader/mcuboot/scripts/tests/conftest.py create mode 100644 bootloader/mcuboot/scripts/tests/test_commands.py create mode 100644 bootloader/mcuboot/scripts/tests/test_keys.py create mode 100644 bootloader/mcuboot/sim/.gitignore create mode 100644 bootloader/mcuboot/sim/Cargo.toml create mode 100644 bootloader/mcuboot/sim/README.rst create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/.gitignore create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/Cargo.toml create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/build.rs create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/bootsim.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-add-psa-crypto.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-asn1.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec-psa.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ed25519.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-kw.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa-kw.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/devicetree.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/flash_map_backend/flash_map_backend.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/keys.c create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_assert.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_config.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_logging.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_heap.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_malloc.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/psa_crypto_init_stub.c create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/run.c create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/security_cnt.c create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/storage/flash_map.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/csupport/sysflash/sysflash.h create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/src/api.rs create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/src/area.rs create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/src/c.rs create mode 100644 bootloader/mcuboot/sim/mcuboot-sys/src/lib.rs create mode 100644 bootloader/mcuboot/sim/simflash/.gitignore create mode 100644 bootloader/mcuboot/sim/simflash/Cargo.toml create mode 100644 bootloader/mcuboot/sim/simflash/src/lib.rs create mode 100644 bootloader/mcuboot/sim/simflash/src/pdump.rs create mode 100644 bootloader/mcuboot/sim/src/caps.rs create mode 100644 bootloader/mcuboot/sim/src/depends.rs create mode 100644 bootloader/mcuboot/sim/src/ecdsa_pub_key-rs.txt create mode 100644 bootloader/mcuboot/sim/src/ed25519_pub_key-rs.txt create mode 100644 bootloader/mcuboot/sim/src/image.rs create mode 100644 bootloader/mcuboot/sim/src/lib.rs create mode 100644 bootloader/mcuboot/sim/src/main.rs create mode 100644 bootloader/mcuboot/sim/src/rsa3072_pub_key-rs.txt create mode 100644 bootloader/mcuboot/sim/src/rsa_pub_key-rs.txt create mode 100644 bootloader/mcuboot/sim/src/testlog.rs create mode 100644 bootloader/mcuboot/sim/src/tlv.rs create mode 100644 bootloader/mcuboot/sim/src/utils.rs create mode 100644 bootloader/mcuboot/sim/tests/core.rs create mode 100644 bootloader/mcuboot/testplan/mynewt/Makefile create mode 100644 bootloader/mcuboot/testplan/mynewt/apps/blinky/pkg.yml create mode 100644 bootloader/mcuboot/testplan/mynewt/apps/blinky/src/main.c create mode 100644 bootloader/mcuboot/testplan/mynewt/apps/blinky/syscfg.yml create mode 100644 bootloader/mcuboot/testplan/mynewt/apps/slinky/pkg.yml create mode 100644 bootloader/mcuboot/testplan/mynewt/apps/slinky/src/main.c create mode 100644 bootloader/mcuboot/testplan/mynewt/apps/slinky/src/random_data.c create mode 100644 bootloader/mcuboot/testplan/mynewt/apps/slinky/syscfg.yml create mode 100644 bootloader/mcuboot/testplan/mynewt/key_ec.pem create mode 100644 bootloader/mcuboot/testplan/mynewt/key_ec256.pem create mode 100644 bootloader/mcuboot/testplan/mynewt/key_ec256_2.pem create mode 100644 bootloader/mcuboot/testplan/mynewt/key_ec_2.pem create mode 100644 bootloader/mcuboot/testplan/mynewt/key_rsa.pem create mode 100644 bootloader/mcuboot/testplan/mynewt/key_rsa_2.pem create mode 100644 bootloader/mcuboot/testplan/mynewt/keys/ec256/pkg.yml create mode 100644 bootloader/mcuboot/testplan/mynewt/keys/ec256/src/keys.c create mode 100644 bootloader/mcuboot/testplan/mynewt/keys/pkg.yml create mode 100644 bootloader/mcuboot/testplan/mynewt/keys/rsa/pkg.yml create mode 100644 bootloader/mcuboot/testplan/mynewt/keys/rsa/src/keys.c create mode 100644 bootloader/mcuboot/testplan/mynewt/project.yml create mode 100644 bootloader/mcuboot/zephyr/module.yml create mode 100644 docs/BLE.xlsx create mode 100644 docs/IDE_setup_and_program.docx create mode 100644 docs/interface.xlsx create mode 100644 docs/shotData/diagrams.vsdx create mode 100644 docs/shotData/format.xlsx create mode 100644 docs/shotData/shotDataExamples.md create mode 100644 docs/shotData/shotDataFormat.docx create mode 100644 docs/shot_capture_flow_dia.vsdx create mode 100644 docs/thread_analysis.xlsx create mode 100644 keys/cyber_pk.pem create mode 100644 keys/readme.txt create mode 100644 keys/test_pk.pem create mode 100644 prj.conf create mode 100644 releases/20251216_v1_0_0.zip create mode 100644 requirements/SamplePaperScoreSheets.docx create mode 100644 requirements/goals_and_use_cases.md create mode 100644 requirements/tbd.md create mode 100644 src/app/app.c create mode 100644 src/app/app.h create mode 100644 src/app/app_cli.c create mode 100644 src/app/app_cli.h create mode 100644 src/app/ble.c create mode 100644 src/app/ble.h create mode 100644 src/app/bubble.c create mode 100644 src/app/bubble.h create mode 100644 src/app/event.c create mode 100644 src/app/event.h create mode 100644 src/app/fiber.c create mode 100644 src/app/fiber.h create mode 100644 src/app/fsm.c create mode 100644 src/app/fsm.h create mode 100644 src/app/light_sensor.c create mode 100644 src/app/light_sensor.h create mode 100644 src/app/log.c create mode 100644 src/app/log.h create mode 100644 src/app/motion.c create mode 100644 src/app/motion.h create mode 100644 src/app/pin.c create mode 100644 src/app/pin.h create mode 100644 src/app/scope_ring.c create mode 100644 src/app/scope_ring.h create mode 100644 src/app/shot_capture.c create mode 100644 src/app/shot_capture.h create mode 100644 src/app/shot_storage.c create mode 100644 src/app/shot_storage.h create mode 100644 src/app/uuid.h create mode 100644 src/app/uuid_base.h create mode 100644 src/bsp/als.c create mode 100644 src/bsp/als.h create mode 100644 src/bsp/battery.c create mode 100644 src/bsp/battery.h create mode 100644 src/bsp/button.c create mode 100644 src/bsp/button.h create mode 100644 src/bsp/imu.c create mode 100644 src/bsp/imu.h create mode 100644 src/bsp/imu_reg.h create mode 100644 src/bsp/led.c create mode 100644 src/bsp/led.h create mode 100644 src/bsp/led_ring.c create mode 100644 src/bsp/led_ring.h create mode 100644 src/bsp/mcu.c create mode 100644 src/bsp/mcu.h create mode 100644 src/bsp/nvm_ext.c create mode 100644 src/bsp/nvm_ext.h create mode 100644 src/bsp/power.c create mode 100644 src/bsp/power.h create mode 100644 src/bsp/rtc.c create mode 100644 src/bsp/rtc.h create mode 100644 src/bsp/wdt.c create mode 100644 src/bsp/wdt.h create mode 100644 src/lib/cli.c create mode 100644 src/lib/cli.h create mode 100644 src/lib/crc.c create mode 100644 src/lib/crc.h create mode 100644 src/lib/debug.c create mode 100644 src/lib/debug.h create mode 100644 src/lib/errno.c create mode 100644 src/lib/errno.h create mode 100644 src/lib/fs.c create mode 100644 src/lib/fs.h create mode 100644 src/lib/gamma.c create mode 100644 src/lib/gamma.h create mode 100644 src/lib/print.c create mode 100644 src/lib/print.h create mode 100644 src/lib/rgb.c create mode 100644 src/lib/rgb.h create mode 100644 src/lib/virtual_led_ring.c create mode 100644 src/lib/virtual_led_ring.h create mode 100644 src/main.c create mode 100644 src/wrappers/adc.c create mode 100644 src/wrappers/adc.h create mode 100644 src/wrappers/gpio - Copy.c create mode 100644 src/wrappers/gpio - Copy.h create mode 100644 src/wrappers/gpio.c create mode 100644 src/wrappers/gpio.h create mode 100644 src/wrappers/i2c.c create mode 100644 src/wrappers/i2c.h create mode 100644 src/wrappers/pwm.c create mode 100644 src/wrappers/pwm.h create mode 100644 src/wrappers/spi.c create mode 100644 src/wrappers/spi.h create mode 100644 src/wrappers/uart.c create mode 100644 src/wrappers/uart.h create mode 100644 sysbuild.conf create mode 100644 sysbuild/mcuboot.conf diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c1b098d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(cyber_scope) + +target_sources(app PRIVATE src/main.c) + +# APP +target_sources(app PRIVATE src/main.c) +target_sources(app PRIVATE src/app/app_cli.c) +target_sources(app PRIVATE src/app/app.c) +target_sources(app PRIVATE src/app/ble.c) +target_sources(app PRIVATE src/app/bubble.c) +target_sources(app PRIVATE src/app/fiber.c) +target_sources(app PRIVATE src/app/fsm.c) +target_sources(app PRIVATE src/app/event.c) +target_sources(app PRIVATE src/app/light_sensor.c) +target_sources(app PRIVATE src/app/motion.c) +target_sources(app PRIVATE src/app/pin.c) +target_sources(app PRIVATE src/app/scope_ring.c) +target_sources(app PRIVATE src/app/shot_storage.c) +target_sources(app PRIVATE src/app/shot_capture.c) + +# BSP +target_sources(app PRIVATE src/bsp/als.c) +target_sources(app PRIVATE src/bsp/battery.c) +target_sources(app PRIVATE src/bsp/button.c) +target_sources(app PRIVATE src/bsp/imu.h) +target_sources(app PRIVATE src/bsp/imu.c) +target_sources(app PRIVATE src/bsp/led_ring.c) +target_sources(app PRIVATE src/bsp/led.c) +target_sources(app PRIVATE src/bsp/mcu.c) +target_sources(app PRIVATE src/bsp/nvm_ext.c) +target_sources(app PRIVATE src/bsp/power.c) +target_sources(app PRIVATE src/bsp/rtc.c) +target_sources(app PRIVATE src/bsp/wdt.c) + +# LIB +target_sources(app PRIVATE src/lib/cli.c) +target_sources(app PRIVATE src/lib/crc.c) +if(CONFIG_DBG_STATS) + target_sources(app PRIVATE src/lib/debug.c) +endif() +target_sources(app PRIVATE src/lib/errno.c) +target_sources(app PRIVATE src/lib/fs.c) +target_sources(app PRIVATE src/lib/gamma.c) +target_sources(app PRIVATE src/lib/print.c) +target_sources(app PRIVATE src/lib/rgb.c) +target_sources(app PRIVATE src/lib/virtual_led_ring.c) + +# WRAPPERS +target_sources(app PRIVATE src/wrappers/adc.c) +target_sources(app PRIVATE src/wrappers/gpio.c) +target_sources(app PRIVATE src/wrappers/i2c.c) +target_sources(app PRIVATE src/wrappers/pwm.c) +target_sources(app PRIVATE src/wrappers/spi.c) +target_sources(app PRIVATE src/wrappers/uart.c) diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..8ddcaa8 --- /dev/null +++ b/Kconfig @@ -0,0 +1,13 @@ +# Copyright (C) Morgan Advanced Programmable Systems, Inc. + +mainmenu "Cyber Scope Application" + +config DBG_STATS + bool "Enable debug statistics" + default n + help + Enable debug statistics variables for development and debugging. + When enabled, counters for I2C transfers, FIFO reads, shot captures, + etc. are tracked. When disabled, no code or RAM is used. + +source "Kconfig.zephyr" diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..9d8c17b --- /dev/null +++ b/VERSION @@ -0,0 +1,5 @@ +VERSION_MAJOR = 0 +VERSION_MINOR = 0 +PATCHLEVEL = 0 +VERSION_TWEAK = 0 +EXTRAVERSION = 0 \ No newline at end of file diff --git a/boards/MAPS/cyber_nrf52840/Kconfig.cyber_nrf52840 b/boards/MAPS/cyber_nrf52840/Kconfig.cyber_nrf52840 new file mode 100644 index 0000000..927aceb --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/Kconfig.cyber_nrf52840 @@ -0,0 +1,2 @@ +config BOARD_CYBER_NRF52840 + select SOC_NRF52840_QIAA diff --git a/boards/MAPS/cyber_nrf52840/Kconfig.defconfig b/boards/MAPS/cyber_nrf52840/Kconfig.defconfig new file mode 100644 index 0000000..c60e44b --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/Kconfig.defconfig @@ -0,0 +1,6 @@ +if BOARD_CYBER_NRF52840 + +config BT_CTLR + default BT + +endif # BOARD_CYBER_NRF52840 \ No newline at end of file diff --git a/boards/MAPS/cyber_nrf52840/board.cmake b/boards/MAPS/cyber_nrf52840/board.cmake new file mode 100644 index 0000000..4a36350 --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/board.cmake @@ -0,0 +1,9 @@ +board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000") +board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000") + +set(OPENOCD_NRF5_SUBFAMILY "nrf52") +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) +include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake) \ No newline at end of file diff --git a/boards/MAPS/cyber_nrf52840/board.yml b/boards/MAPS/cyber_nrf52840/board.yml new file mode 100644 index 0000000..4540f01 --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/board.yml @@ -0,0 +1,5 @@ +board: + name: cyber_nrf52840 + vendor: MAPS + socs: + - name: nrf52840 \ No newline at end of file diff --git a/boards/MAPS/cyber_nrf52840/cyber_nrf52840-pinctrl.dtsi b/boards/MAPS/cyber_nrf52840/cyber_nrf52840-pinctrl.dtsi new file mode 100644 index 0000000..55335a4 --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/cyber_nrf52840-pinctrl.dtsi @@ -0,0 +1,123 @@ +&pinctrl { + i2c0_default: i2c0_default { + group1 { + psels = , + ; + }; + }; + + i2c0_sleep: i2c0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + i2c1_default: i2c1_default { + group1 { + psels = , + ; + }; + }; + + i2c1_sleep: i2c1_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + i2s0_default: i2s0_default { + group1 { + /* Using i2s SDOUT to drive LED ring. SCK and LRCK not needed but Zephyr driver will not + * drive SDOUT without them. Workaround : Burn up 2 unused GPIO for SCK and LRCK. + */ + psels = , + , + ; + }; + }; + + i2s0_sleep: i2s0_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + pwm0_default: pwm0_default { + group1 { + psels = , + , + ; + }; + }; + + pwm0_sleep: pwm0_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + qspi_default: qspi_default { + group1 { + psels = , + , + , + , + , + ; + }; + }; + + qspi_sleep: qspi_sleep { + group1 { + psels = , + , + , + , + , + ; + low-power-enable; + }; + }; + + spi2_default: spi2_default { + group1 { + psels = , + , + ; + }; + }; + + spi2_sleep: spi2_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + uart0_default: uart0_default { + group1 { + psels = , + ; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; diff --git a/boards/MAPS/cyber_nrf52840/cyber_nrf52840.dts b/boards/MAPS/cyber_nrf52840/cyber_nrf52840.dts new file mode 100644 index 0000000..c0094bf --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/cyber_nrf52840.dts @@ -0,0 +1,243 @@ +/dts-v1/; +#include +#include "cyber_nrf52840-pinctrl.dtsi" +/* For use with WS2812 LEDs. */ +#include + +/ { + model = "MAPS - Cyber NRF52840 Board"; + compatible = "MAPS,cyber-nrf52840"; + + chosen { + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_partition; + zephyr,console = &uart0; + }; + + zephyr,user { + io-channels = <&adc 0>; + }; + + aliases { + mcuboot-led = &mcuboot_led; + left-button = &left_button; + center-button = ¢er_button; + right-button = &right_button; + pwr-5v-enable = &pwr_5v_enable; + led-ring-pwr = &led_ring_pwr; + led-ring-level-shift-enable = &led_ring_level_shift_enable; + led-ring-din = &led_ring_din; + battery-check-enable = &battery_check_enable; + imu-int = &imu_int; + + + bubble-led = &bubble_led; + pin-led = &pin_led; + fiber-led = &fiber_led; + + led-ring = &led_ring; + + watchdog0 = &wdt0; + + // spi-flash0 = &mx25r64; + // bootloader-led0 = &bubble_led; + // mcuboot-button0 = ¢er_button; + // mcuboot-led0 = &bubble_led; + }; + + // GPIO enable assumes true is whatever the logic level is set to. Set everything to active high here so we can control in code. + gpios { + compatible = "gpio-keys"; + mcuboot_led: mcuboot_led { + gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + }; + left_button: left_button { + gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; + }; + center_button: center_button { + gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + }; + right_button: right_button { + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; + }; + pwr_5v_enable: pwr_5v_enable { + gpios = <&gpio0 12 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + }; + led_ring_pwr: led_ring_pwr { + gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + }; + led_ring_level_shift_enable: led_ring_level_shift_enable { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + }; + led_ring_din: led_ring_din { + gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>; + }; + battery_check_enable: battery_check_enable { + gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; + }; + imu_int: imu_int { + gpios = <&gpio0 11 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + }; + charger_status: charger_status { + gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; + }; + + }; + + pwmleds: pwmleds { + compatible = "pwm-leds"; + + bubble_led: bubble_led { + pwms = <&pwm0 0 PWM_USEC(4000) PWM_POLARITY_NORMAL>; + }; + + pin_led: pin_led { + pwms = <&pwm0 1 PWM_USEC(4000) PWM_POLARITY_NORMAL>; + }; + + fiber_led: fiber_led { + pwms = <&pwm0 2 PWM_USEC(4000) PWM_POLARITY_NORMAL>; + }; + }; +}; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 DT_SIZE_K(48)>; + }; + + slot0_partition: partition@c000 { + label = "image-0"; + reg = <0x0000c000 DT_SIZE_K(472)>; + }; + + slot1_partition: partition@82000 { + label = "image-1"; + reg = <0x00082000 DT_SIZE_K(472)>; + }; + + storage_partition: partition@f8000 { + label = "storage"; + reg = <0x000f8000 DT_SIZE_K(32)>; + }; + }; +}; + +&uicr { + /* Pins P0.09 and P0.10 are defaulted to be used with NFC, need to configure for standard GPIO + * https://devzone.nordicsemi.com/f/nordic-q-a/35505/nrf52-enabling-gpio-on-nfc-pins + */ + nfct-pins-as-gpios; +}; + +&adc { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; /* P0.02 */ + zephyr,resolution = <12>; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpiote { + status = "okay"; +}; + +&i2c0 { + compatible = "nordic,nrf-twi"; + status = "okay"; + clock-frequency = ; + pinctrl-0 = <&i2c0_default>; + pinctrl-1 = <&i2c0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&i2c1 { + compatible = "nordic,nrf-twim"; + status = "okay"; + clock-frequency = ; + pinctrl-0 = <&i2c1_default>; + pinctrl-1 = <&i2c1_sleep>; + pinctrl-names = "default", "sleep"; +}; + +i2s_led: &i2s0 { + status = "okay"; + pinctrl-0 = <&i2s0_default>; + pinctrl-1 = <&i2s0_sleep>; + pinctrl-names = "default", "sleep"; + + led_ring: ws2812 { + compatible = "worldsemi,ws2812-i2s"; + + i2s-dev = < &i2s_led >; + chain-length = <24>; + color-mapping = ; + // out-active-low; + reset-delay = <120>; + }; +}; + +&spi2 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi2_default>; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; + cs-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; +}; + +&uart0 { + status = "okay"; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; + current-speed = <115200>; +}; + +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&qspi { + status = "disabled"; + pinctrl-0 = <&qspi_default>; + pinctrl-1 = <&qspi_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&rtc2 { + status = "okay"; + clock-frequency = <32768>; + prescaler = <4095>; /* +1 for prescaler */ +}; + +&wdt0 { + status = "okay"; +}; diff --git a/boards/MAPS/cyber_nrf52840/cyber_nrf52840.yml b/boards/MAPS/cyber_nrf52840/cyber_nrf52840.yml new file mode 100644 index 0000000..2fa0bfe --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/cyber_nrf52840.yml @@ -0,0 +1,11 @@ +identifier: cyber_nrf52840/nrf52840 +name: Custom Board auto generated by nRF Connect for VS Code +vendor: MAPS +type: mcu +arch: arm +ram: 256 +flash: 1024 +toolchain: + - zephyr +supported: + - gpio \ No newline at end of file diff --git a/boards/MAPS/cyber_nrf52840/cyber_nrf52840_defconfig b/boards/MAPS/cyber_nrf52840/cyber_nrf52840_defconfig new file mode 100644 index 0000000..5170518 --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/cyber_nrf52840_defconfig @@ -0,0 +1,12 @@ +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable hardware stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable uart driver +CONFIG_SERIAL=y + +# Enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/boards/MAPS/cyber_nrf52840/pre_dt_board.cmake b/boards/MAPS/cyber_nrf52840/pre_dt_board.cmake new file mode 100644 index 0000000..519d784 --- /dev/null +++ b/boards/MAPS/cyber_nrf52840/pre_dt_board.cmake @@ -0,0 +1,2 @@ +# Suppress "unique_unit_address_if_enabled" to handle some overlaps +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/bootloader/mcuboot/.gitignore b/bootloader/mcuboot/.gitignore new file mode 100644 index 0000000..7986688 --- /dev/null +++ b/bootloader/mcuboot/.gitignore @@ -0,0 +1,30 @@ +outdir/ +.*.swp +target.sh +*.pyc +tags +rusty-tags.* + +# mynewt +/repos/ +/project.state +/bin/ +/targets/ +**/build/**/* + +#Eclipse project files +.cproject +.project + +# Compiled python modules. +*.pyc + +# Setuptools distribution folder. +/scripts/dist/ + +# Python egg metadata, regenerated from source files by setuptools. +/scripts/*.egg-info +/scripts/*.egg + +# The target directory from Rust development +/target/ diff --git a/bootloader/mcuboot/.gitmodules b/bootloader/mcuboot/.gitmodules new file mode 100644 index 0000000..37919b0 --- /dev/null +++ b/bootloader/mcuboot/.gitmodules @@ -0,0 +1,21 @@ +[submodule "sim/mbedtls"] + path = ext/mbedtls + url = https://github.com/ARMmbed/mbedtls +[submodule "boot/cypress/libs/mtb-pdl-cat1"] + path = boot/cypress/libs/mtb-pdl-cat1 + url = https://github.com/cypresssemiconductorco/mtb-pdl-cat1.git +[submodule "boot/cypress/libs/pdl/psoc6pdl"] + path = boot/cypress/libs/pdl/psoc6pdl + url = https://github.com/cypresssemiconductorco/psoc6pdl.git +[submodule "boot/cypress/libs/retarget-io"] + path = boot/cypress/libs/retarget-io + url = https://github.com/cypresssemiconductorco/retarget-io.git +[submodule "boot/cypress/libs/core-lib"] + path = boot/cypress/libs/core-lib + url = https://github.com/cypresssemiconductorco/core-lib.git +[submodule "boot/cypress/libs/psoc6hal"] + path = boot/cypress/libs/psoc6hal + url = https://github.com/cypresssemiconductorco/psoc6hal.git +[submodule "boot/cypress/libs/cy-mbedtls-acceleration"] + path = boot/cypress/libs/cy-mbedtls-acceleration + url = https://github.com/cypresssemiconductorco/cy-mbedtls-acceleration.git diff --git a/bootloader/mcuboot/.mbedignore b/bootloader/mcuboot/.mbedignore new file mode 100644 index 0000000..5dd4ea2 --- /dev/null +++ b/bootloader/mcuboot/.mbedignore @@ -0,0 +1,20 @@ +boot/boot_serial/* +boot/mynewt/* +boot/zephyr/* +boot/cypress/* +boot/espressif/* +boot/nuttx/* +ci/* +docs/* +ptest/* +samples/* +scripts/* +sim/* +testplan/* +ext/fiat/* +ext/mbedtls/* +ext/mbedtls-asn1/* +ext/nrf/* +ext/tinycrypt/tests/* +ext/tinycrypt/* +ext/tinycrypt-sha512/* diff --git a/bootloader/mcuboot/.travis.yml-disabled b/bootloader/mcuboot/.travis.yml-disabled new file mode 100644 index 0000000..bc89c48 --- /dev/null +++ b/bootloader/mcuboot/.travis.yml-disabled @@ -0,0 +1,81 @@ +# Travis configuration. Run FI hardening tests. + +language: minimal + +services: + - docker + +matrix: + include: + - os: linux + language: minimal + env: BUILD_TYPE=RELEASE SKIP_SIZE=2,4,6,8,10 TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=RELEASE SKIP_SIZE=2,4,6,8,10 FIH_LEVEL=LOW TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=RELEASE SKIP_SIZE=2,4,6,8,10 FIH_LEVEL=MEDIUM TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 FIH_LEVEL=LOW TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 FIH_LEVEL=MEDIUM TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=8,10 TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=8,10 FIH_LEVEL=LOW TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + - os: linux + language: minimal + env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=8,10 FIH_LEVEL=MEDIUM TEST=fih-tests DAMAGE_TYPE=SIGNATURE + + ## Corrupt image hash is not tested as it is in the unprotected TLV section + ## and is easy to calculate a valid hash for a changed image + #- os: linux + # language: minimal + # env: BUILD_TYPE=MINSIZEREL SKIP_SIZE=2,4,6 TEST=fih-tests DAMAGE_TYPE=IMAGE_HASH + + ## Max profile is not tested as it requires HW entropy source which is not + ## present in the QEMU system being used for the tests. + #- os: linux + # language: minimal + # env: FIH_LEVEL=MAX TEST=fih-tests + +before_install: + - | + if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + ./ci/check-signed-off-by.sh + if [ $? -ne 0 ]; then + exit 1 + fi + fi + +install: + - ./ci/${TEST}_install.sh + +script: + - ./ci/${TEST}_run.sh + +cache: + directories: + - docker + +notifications: + slack: + rooms: + - secure: "Tg9ZvJfb6e4QSEsxUvwW2MIqbuNRxD4nAOkgs8eopj/fRtqN6Zk06NVSeMmYcZunDFJXUSzYANBsF98OtaaUlm4WVt2T0ZFBJZrOYfIv18/zXCjYa04sDxur57F1ZYTYKyRpdUkfzPd/rGE4jKLQNcia+r/BTQbJkcZbXeg5/6cUeMP1so8/o0oMhSQP+GH0fLbyLzx3VPE8zu6+j2fCFC7R3idxtfO9VtsKlubfi3HgPgXTs+DEAAA8aoOku2esjFSFXTtdUFGz90YzyShZvtMcRg3Grp9+PZAsJwWk4eKSonKCO0DScfPUlMZbJcV7VsmyTxYKLLsGRZae6ZBH+XLfx5XeqgtgCor3FYx2dUXYfV9y8VvERDdossB0gZ/V2OUGePctDefiORytV6dMa7X3AfSdosgs/tjG4kbf+PMaVULzwF6dd3CdsxdClSl68UQPWA6wC2TEyo1EQea8jgZU6heLustZaQZhBkFkr/6j75XeGBjPw2fgVRkgg/OnTOYmo7N8181wOU+xORIEO1BtYmgRpc0cgpm4H9457diSHG1D2SoNy4tiQPCW2enN00QNGK5pZSL/FGA/ReUcALgAW0QcOljjU2bUSGPxo/VAi5ZyljWgVAGQ5qHJ4jgdfPf7VJUzCVH64G1S5+0gZPpO6vvvPdZtqeXrfBZMOBw=" + on_success: always diff --git a/bootloader/mcuboot/CODE_OF_CONDUCT.md b/bootloader/mcuboot/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..51bb693 --- /dev/null +++ b/bootloader/mcuboot/CODE_OF_CONDUCT.md @@ -0,0 +1,134 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +mcuboot@groups.io. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/bootloader/mcuboot/Cargo.lock b/bootloader/mcuboot/Cargo.lock new file mode 100644 index 0000000..9dff624 --- /dev/null +++ b/bootloader/mcuboot/Cargo.lock @@ -0,0 +1,542 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "ctr", + "opaque-debug", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bootsim" +version = "0.1.0" +dependencies = [ + "aes", + "base64", + "byteorder", + "cipher", + "docopt", + "env_logger", + "libc", + "log", + "mcuboot-sys", + "pem", + "rand", + "ring", + "serde", + "serde_derive", + "simflash", + "typenum", + "untrusted 0.9.0", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "docopt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" +dependencies = [ + "lazy_static", + "regex", + "serde", + "strsim", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "js-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mcuboot-sys" +version = "0.1.0" +dependencies = [ + "cc", + "libc", + "log", + "simflash", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pem" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947" +dependencies = [ + "base64", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "simflash" +version = "0.1.0" +dependencies = [ + "log", + "rand", + "thiserror", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" + +[[package]] +name = "web-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/bootloader/mcuboot/Cargo.toml b/bootloader/mcuboot/Cargo.toml new file mode 100644 index 0000000..185777f --- /dev/null +++ b/bootloader/mcuboot/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = ["sim"] +exclude = ["ptest"] +resolver = "2" + +# The simulator runs very slowly without optimization. A value of 1 +# compiles in about half the time, but runs about 5-6 times slower. 2 +# and 3 are hardly different in either compile time or performance. +# Use 2 in case that makes the code slightly more debuggable. +[profile.test] +opt-level = 2 + +[profile.dev] +opt-level = 2 diff --git a/bootloader/mcuboot/LICENSE b/bootloader/mcuboot/LICENSE new file mode 100644 index 0000000..d89a331 --- /dev/null +++ b/bootloader/mcuboot/LICENSE @@ -0,0 +1,206 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +This product bundles tinycrypt, which is available under the "3-clause BSD" +license. For details, and bundled files see: + * ext/tinycrypt/LICENSE + * ext/tinycrypt diff --git a/bootloader/mcuboot/NOTICE b/bootloader/mcuboot/NOTICE new file mode 100644 index 0000000..ce203c1 --- /dev/null +++ b/bootloader/mcuboot/NOTICE @@ -0,0 +1,11 @@ +Apache Mynewt +Copyright 2015-2017 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at +Runtime Inc, copyright 2015. + +Portions of this software were developed at +Arm Limited, copyright 2019-2021. diff --git a/bootloader/mcuboot/README.md b/bootloader/mcuboot/README.md new file mode 100644 index 0000000..8473fc4 --- /dev/null +++ b/bootloader/mcuboot/README.md @@ -0,0 +1,85 @@ +# [MCUboot](http://mcuboot.com/) + +[![Package on PyPI](https://img.shields.io/pypi/v/imgtool.svg)][pypi] +[![Coverity Scan Build Status](https://scan.coverity.com/projects/12307/badge.svg)][coverity] +[![Build Status (Sim)](https://github.com/mcu-tools/mcuboot/workflows/Sim/badge.svg)][sim] +[![Build Status (Mynewt)](https://github.com/mcu-tools/mcuboot/workflows/Mynewt/badge.svg)][mynewt] +[![Build Status (Espressif)](https://github.com/mcu-tools/mcuboot/workflows/Espressif/badge.svg)][espressif] +[![Publishing Status (imgtool)](https://github.com/mcu-tools/mcuboot/workflows/imgtool/badge.svg)][imgtool] +[![Build Status (Travis CI)](https://img.shields.io/travis/mcu-tools/mcuboot/main.svg?label=travis-ci)][travis] +[![Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)][license] + +[pypi]: https://pypi.org/project/imgtool/ +[coverity]: https://scan.coverity.com/projects/mcuboot +[sim]: https://github.com/mcu-tools/mcuboot/actions?query=workflow:Sim +[mynewt]: https://github.com/mcu-tools/mcuboot/actions?query=workflow:Mynewt +[espressif]: https://github.com/mcu-tools/mcuboot/actions?query=workflow:Espressif +[imgtool]: https://github.com/mcu-tools/mcuboot/actions?query=workflow:imgtool +[travis]: https://travis-ci.org/mcu-tools/mcuboot +[license]: https://github.com/mcu-tools/mcuboot/blob/main/LICENSE + +This is MCUboot version 2.1.0 + +MCUboot is a secure bootloader for 32-bits microcontrollers. It defines a +common infrastructure for the bootloader and the system flash layout on +microcontroller systems, and provides a secure bootloader that enables easy +software upgrade. + +MCUboot is not dependent on any specific operating system and hardware and +relies on hardware porting layers from the operating system it works with. +Currently, MCUboot works with the following operating systems and SoCs: +- [Zephyr](https://www.zephyrproject.org/) +- [Apache Mynewt](https://mynewt.apache.org/) +- [Apache NuttX](https://nuttx.apache.org/) +- [RIOT](https://www.riot-os.org/) +- [Mbed OS](https://os.mbed.com/) +- [Espressif](https://www.espressif.com/) +- [Cypress/Infineon](https://www.cypress.com/) + +RIOT is supported only as a boot target. We will accept any new +port contributed by the community once it is good enough. + +## MCUboot How-tos + +See the following pages for instructions on using MCUboot with different +operating systems and SoCs: +- [Zephyr](docs/readme-zephyr.md) +- [Apache Mynewt](docs/readme-mynewt.md) +- [Apache NuttX](docs/readme-nuttx.md) +- [RIOT](docs/readme-riot.md) +- [Mbed OS](docs/readme-mbed.md) +- [Espressif](docs/readme-espressif.md) +- [Cypress/Infineon](boot/cypress/README.md) + +There are also instructions for the [Simulator](sim/README.rst). + +## Roadmap + +The issues being planned and worked on are tracked using GitHub issues. To +give your input, visit [MCUboot GitHub +Issues](https://github.com/mcu-tools/mcuboot/issues). + +## Source files + +You can find additional documentation on the bootloader in the source files. +For more information, use the following links: +- [boot/bootutil](https://github.com/mcu-tools/mcuboot/tree/main/boot/bootutil) - The core of the bootloader itself. +- [boot/boot\_serial](https://github.com/mcu-tools/mcuboot/tree/main/boot/boot_serial) - Support for serial upgrade within the bootloader itself. +- [boot/zephyr](https://github.com/mcu-tools/mcuboot/tree/main/boot/zephyr) - Port of the bootloader to Zephyr. +- [boot/mynewt](https://github.com/mcu-tools/mcuboot/tree/main/boot/mynewt) - Bootloader application for Apache Mynewt. +- [boot/nuttx](https://github.com/mcu-tools/mcuboot/tree/main/boot/nuttx) - Bootloader application and port of MCUboot interfaces for Apache NuttX. +- [boot/mbed](https://github.com/mcu-tools/mcuboot/tree/main/boot/mbed) - Port of the bootloader to Mbed OS. +- [boot/espressif](https://github.com/mcu-tools/mcuboot/tree/main/boot/espressif) - Bootloader application and MCUboot port for Espressif SoCs. +- [boot/cypress](https://github.com/mcu-tools/mcuboot/tree/main/boot/cypress) - Bootloader application and MCUboot port for Cypress/Infineon SoCs. +- [imgtool](https://github.com/mcu-tools/mcuboot/tree/main/scripts/imgtool.py) - A tool to securely sign firmware images for booting by MCUboot. +- [sim](https://github.com/mcu-tools/mcuboot/tree/main/sim) - A bootloader simulator for testing and regression. + +## Joining the project + +Developers are welcome! + +Use the following links to join or see more about the project: + +* [Our developer mailing list](https://groups.io/g/MCUBoot) +* [Our Discord channel](https://discord.com/channels/1106321706588577904/1106322802308550716)
+ Get [your invite](https://discord.com/invite/5PpXhvda5p) diff --git a/bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial.h b/bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial.h new file mode 100644 index 0000000..8c56fe7 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial.h @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __BOOT_SERIAL_H__ +#define __BOOT_SERIAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Function pointers to read/write data from uart. + * read returns the number of bytes read, str points to buffer to fill, + * cnt is the number of bytes to fill within buffer, *newline will be + * set if newline is the last character. + * write takes as it's arguments pointer to data to write, and the count + * of bytes. + */ +struct boot_uart_funcs { + int (*read)(char *str, int cnt, int *newline); + void (*write)(const char *ptr, int cnt); +}; + +/** + * Start processing newtmgr commands for uploading image0 over serial. + * Assumes serial port is open and waits for download command. + */ +void boot_serial_start(const struct boot_uart_funcs *f); + +/** + * Start processing newtmgr commands for uploading image0 over serial. + * Assumes serial port is open and waits for download command. + * This function will return if there is no mcumgr command received within + * the given timeout. If a command is received within this timeout, the + * function is similar to boot_serial_start + */ +void boot_serial_check_start(const struct boot_uart_funcs *f, int timeout_in_ms); + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOT_SERIAL_H__ */ diff --git a/bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial_encryption.h b/bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial_encryption.h new file mode 100644 index 0000000..b7cf9ff --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/include/boot_serial/boot_serial_encryption.h @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Nordic Semiconductor ASA + */ + +#ifndef H_BOOT_SERIAL_ENCRYPTION_ +#define H_BOOT_SERIAL_ENCRYPTION_ +#include "bootutil/fault_injection_hardening.h" + +/** + * Validate hash of a primary boot image doing on the fly decryption as well + * + * @param[in] fa_p flash area pointer + * @param[in] hdr boot image header pointer + * @param[in] buf buffer which is used for validating data + * @param[in] buf_size size of input buffer + * + * @return FIH_SUCCESS on success, error code otherwise + */ +fih_ret +boot_image_validate_encrypted(const struct flash_area *fa_p, + struct image_header *hdr, uint8_t *buf, + uint16_t buf_size); + +/** + * Handle an encrypted firmware in the main flash. + * This will decrypt the image inplace + */ +int boot_handle_enc_fw(const struct flash_area *flash_area); + +#endif diff --git a/bootloader/mcuboot/boot/boot_serial/pkg.yml b/bootloader/mcuboot/boot/boot_serial/pkg.yml new file mode 100644 index 0000000..aaaf703 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/pkg.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: boot/boot_serial +pkg.description: The boot_serial library is used when downloading image over serial port. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - boot + - bootloader + +pkg.deps: + - "@apache-mynewt-core/hw/hal" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/encoding/base64" + - "@mcuboot/boot/mynewt/flash_map_backend" + - "@mcuboot/boot/mynewt/boot_uart" + - "@mcuboot/boot/zcbor" + - "@apache-mynewt-core/util/crc" + +pkg.req_apis: + - bootloader diff --git a/bootloader/mcuboot/boot/boot_serial/src/boot_serial.c b/bootloader/mcuboot/boot/boot_serial/src/boot_serial.c new file mode 100644 index 0000000..3891793 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/src/boot_serial.c @@ -0,0 +1,1498 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "sysflash/sysflash.h" + +#include "bootutil/bootutil_log.h" + +#ifdef __ZEPHYR__ +#include +#include +#include +#include +#include +#include +#include +#include +#elif __ESPRESSIF__ +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif /* __ZEPHYR__ */ + +#include +#include +#include "zcbor_bulk.h" + +#include +#include +#include + +#include +#include + +#include "boot_serial/boot_serial.h" +#include "boot_serial_priv.h" +#include "mcuboot_config/mcuboot_config.h" +#include "../src/bootutil_priv.h" + +#ifdef MCUBOOT_ENC_IMAGES +#include "boot_serial/boot_serial_encryption.h" +#endif + +#include "bootutil/boot_hooks.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE ZCBOR_ARRAY_SIZE +#endif + +#if defined(MCUBOOT_SHA512) + #define IMAGE_HASH_SIZE (64) + #define IMAGE_SHA_TLV IMAGE_TLV_SHA512 +#elif defined(MCUBOOT_SIGN_EC384) + #define IMAGE_HASH_SIZE (48) + #define IMAGE_SHA_TLV IMAGE_TLV_SHA384 +#else + #define IMAGE_HASH_SIZE (32) + #define IMAGE_SHA_TLV IMAGE_TLV_SHA256 +#endif + +#ifndef MCUBOOT_SERIAL_MAX_RECEIVE_SIZE +#define MCUBOOT_SERIAL_MAX_RECEIVE_SIZE 512 +#endif + +#ifdef MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE +#define BOOT_SERIAL_IMAGE_STATE_SIZE_MAX 48 +#else +#define BOOT_SERIAL_IMAGE_STATE_SIZE_MAX 0 +#endif +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH +#define BOOT_SERIAL_HASH_SIZE_MAX (IMAGE_HASH_SIZE + 4) +#else +#define BOOT_SERIAL_HASH_SIZE_MAX 0 +#endif +#ifdef MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO +#define BOOT_SERIAL_SLOT_INFO_SIZE_MAX 164 +#else +#define BOOT_SERIAL_SLOT_INFO_SIZE_MAX 0 +#endif + +#if (128 + BOOT_SERIAL_IMAGE_STATE_SIZE_MAX + BOOT_SERIAL_HASH_SIZE_MAX) > \ + BOOT_SERIAL_SLOT_INFO_SIZE_MAX +#define BOOT_SERIAL_MAX_MESSAGE_SIZE (128 + BOOT_SERIAL_IMAGE_STATE_SIZE_MAX + \ + BOOT_SERIAL_HASH_SIZE_MAX) +#else +#define BOOT_SERIAL_MAX_MESSAGE_SIZE BOOT_SERIAL_SLOT_INFO_SIZE_MAX +#endif + +#define BOOT_SERIAL_OUT_MAX (BOOT_SERIAL_MAX_MESSAGE_SIZE * BOOT_IMAGE_NUMBER) + +#define BOOT_SERIAL_FRAME_MTU 124 /* 127 - pkt start (2 bytes) and stop (1 byte) */ + +/* Number of estimated CBOR elements for responses */ +#define CBOR_ENTRIES_SLOT_INFO_IMAGE_MAP 4 +#define CBOR_ENTRIES_SLOT_INFO_SLOTS_MAP 3 + +#ifdef __ZEPHYR__ +/* base64 lib encodes data to null-terminated string */ +#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1) + +#define CRC16_INITIAL_CRC 0 /* what to seed crc16 with */ +#define CRC_CITT_POLYMINAL 0x1021 + +#define ntohs(x) sys_be16_to_cpu(x) +#define htons(x) sys_cpu_to_be16(x) +#elif __ESPRESSIF__ +#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1) +#define CRC16_INITIAL_CRC 0 /* what to seed crc16 with */ + +#define ntohs(x) be16toh(x) +#define htons(x) htobe16(x) + +#define base64_decode mbedtls_base64_decode +#define base64_encode mbedtls_base64_encode +#endif + +#if (BOOT_IMAGE_NUMBER > 1) +#define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x)) +#else +#define IMAGES_ITER(x) +#endif + +static char in_buf[MCUBOOT_SERIAL_MAX_RECEIVE_SIZE + 1]; +static char dec_buf[MCUBOOT_SERIAL_MAX_RECEIVE_SIZE + 1]; +const struct boot_uart_funcs *boot_uf; +static struct nmgr_hdr *bs_hdr; +static bool bs_entry; + +static char bs_obuf[BOOT_SERIAL_OUT_MAX]; + +static void boot_serial_output(void); + +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH +static int boot_serial_get_hash(const struct image_header *hdr, + const struct flash_area *fap, uint8_t *hash); +#endif + +static zcbor_state_t cbor_state[2]; + +void reset_cbor_state(void) +{ + zcbor_new_encode_state(cbor_state, 2, (uint8_t *)bs_obuf, + sizeof(bs_obuf), 0); +} + +/** + * Function that processes MGMT_GROUP_ID_PERUSER mcumgr group and may be + * used to process any groups that have not been processed by generic boot + * serial implementation. + * + * @param[in] hdr -- the decoded header of mcumgr message; + * @param[in] buffer -- buffer with first mcumgr message; + * @param[in] len -- length of of data in buffer; + * @param[out] *cs -- object with encoded response. + * + * @return 0 on success; non-0 error code otherwise. + */ +extern int bs_peruser_system_specific(const struct nmgr_hdr *hdr, + const char *buffer, + int len, zcbor_state_t *cs); + +#define zcbor_tstr_put_lit_cast(state, string) \ + zcbor_tstr_encode_ptr(state, (char *)string, sizeof(string) - 1) + +#ifndef MCUBOOT_USE_SNPRINTF +/* + * Convert version into string without use of snprintf(). + */ +static int +u32toa(char *tgt, uint32_t val) +{ + char *dst; + uint32_t d = 1; + uint32_t dgt; + int n = 0; + + dst = tgt; + while (val / d >= 10) { + d *= 10; + } + while (d) { + dgt = val / d; + val %= d; + d /= 10; + if (n || dgt > 0 || d == 0) { + *dst++ = dgt + '0'; + ++n; + } + } + *dst = '\0'; + + return dst - tgt; +} + +/* + * dst has to be able to fit "255.255.65535.4294967295" (25 characters). + */ +static void +bs_list_img_ver(char *dst, int maxlen, struct image_version *ver) +{ + int off; + + off = u32toa(dst, ver->iv_major); + dst[off++] = '.'; + off += u32toa(dst + off, ver->iv_minor); + dst[off++] = '.'; + off += u32toa(dst + off, ver->iv_revision); + + if (ver->iv_build_num != 0) { + dst[off++] = '.'; + off += u32toa(dst + off, ver->iv_build_num); + } +} +#else +/* + * dst has to be able to fit "255.255.65535.4294967295" (25 characters). + */ +static void +bs_list_img_ver(char *dst, int maxlen, struct image_version *ver) +{ + int len; + + len = snprintf(dst, maxlen, "%hu.%hu.%hu", (uint16_t)ver->iv_major, + (uint16_t)ver->iv_minor, ver->iv_revision); + + if (ver->iv_build_num != 0 && len > 0 && len < maxlen) { + snprintf(&dst[len], (maxlen - len), ".%u", ver->iv_build_num); + } +} +#endif /* !MCUBOOT_USE_SNPRINTF */ + +/* + * List images. + */ +static void +bs_list(char *buf, int len) +{ + struct image_header hdr; + uint32_t slot, area_id; + const struct flash_area *fap; + uint8_t image_index; +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH + uint8_t hash[IMAGE_HASH_SIZE]; +#endif + + zcbor_map_start_encode(cbor_state, 1); + zcbor_tstr_put_lit_cast(cbor_state, "images"); + zcbor_list_start_encode(cbor_state, 5); + image_index = 0; + IMAGES_ITER(image_index) { +#ifdef MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE + int swap_status = boot_swap_type_multi(image_index); +#endif + + for (slot = 0; slot < MCUBOOT_IMAGE_NUMBER; slot++) { + FIH_DECLARE(fih_rc, FIH_FAILURE); + uint8_t tmpbuf[64]; + +#ifdef MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE + bool active = false; + bool confirmed = false; + bool pending = false; + bool permanent = false; +#endif + + area_id = flash_area_id_from_multi_image_slot(image_index, slot); + if (flash_area_open(area_id, &fap)) { + continue; + } + + int rc = BOOT_HOOK_CALL(boot_read_image_header_hook, + BOOT_HOOK_REGULAR, image_index, slot, &hdr); + if (rc == BOOT_HOOK_REGULAR) + { + flash_area_read(fap, 0, &hdr, sizeof(hdr)); + } + + if (hdr.ih_magic == IMAGE_MAGIC) + { + BOOT_HOOK_CALL_FIH(boot_image_check_hook, + FIH_BOOT_HOOK_REGULAR, + fih_rc, image_index, slot); + if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) + { +#if defined(MCUBOOT_ENC_IMAGES) +#if !defined(MCUBOOT_SINGLE_APPLICATION_SLOT) + if (IS_ENCRYPTED(&hdr) && MUST_DECRYPT(fap, image_index, &hdr)) { + FIH_CALL(boot_image_validate_encrypted, fih_rc, fap, + &hdr, tmpbuf, sizeof(tmpbuf)); + } else { +#endif + if (IS_ENCRYPTED(&hdr)) { + /* + * There is an image present which has an encrypted flag set but is + * not encrypted, therefore remove the flag from the header and run a + * normal image validation on it. + */ + hdr.ih_flags &= ~ENCRYPTIONFLAGS; + } +#endif + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr, + fap, tmpbuf, sizeof(tmpbuf), NULL, 0, NULL); +#if defined(MCUBOOT_ENC_IMAGES) && !defined(MCUBOOT_SINGLE_APPLICATION_SLOT) + } +#endif + } + } + + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + flash_area_close(fap); + continue; + } + +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH + /* Retrieve hash of image for identification */ + rc = boot_serial_get_hash(&hdr, fap, hash); +#endif + + flash_area_close(fap); + zcbor_map_start_encode(cbor_state, 20); + +#if (BOOT_IMAGE_NUMBER > 1) + zcbor_tstr_put_lit_cast(cbor_state, "image"); + zcbor_uint32_put(cbor_state, image_index); +#endif + +#ifdef MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE + if (swap_status == BOOT_SWAP_TYPE_NONE) { + if (slot == BOOT_PRIMARY_SLOT) { + confirmed = true; + active = true; + } + } else if (swap_status == BOOT_SWAP_TYPE_TEST) { + if (slot == BOOT_PRIMARY_SLOT) { + confirmed = true; + } else { + pending = true; + } + } else if (swap_status == BOOT_SWAP_TYPE_PERM) { + if (slot == BOOT_PRIMARY_SLOT) { + confirmed = true; + } else { + pending = true; + permanent = true; + } + } else if (swap_status == BOOT_SWAP_TYPE_REVERT) { + if (slot == BOOT_PRIMARY_SLOT) { + active = true; + } else { + confirmed = true; + } + } + + if (!(hdr.ih_flags & IMAGE_F_NON_BOOTABLE)) { + zcbor_tstr_put_lit_cast(cbor_state, "bootable"); + zcbor_bool_put(cbor_state, true); + } + + if (confirmed) { + zcbor_tstr_put_lit_cast(cbor_state, "confirmed"); + zcbor_bool_put(cbor_state, true); + } + + if (active) { + zcbor_tstr_put_lit_cast(cbor_state, "active"); + zcbor_bool_put(cbor_state, true); + } + + if (pending) { + zcbor_tstr_put_lit_cast(cbor_state, "pending"); + zcbor_bool_put(cbor_state, true); + } + + if (permanent) { + zcbor_tstr_put_lit_cast(cbor_state, "permanent"); + zcbor_bool_put(cbor_state, true); + } +#endif + + zcbor_tstr_put_lit_cast(cbor_state, "slot"); + zcbor_uint32_put(cbor_state, slot); + +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH + if (rc == 0) { + zcbor_tstr_put_lit_cast(cbor_state, "hash"); + zcbor_bstr_encode_ptr(cbor_state, hash, sizeof(hash)); + } +#endif + + zcbor_tstr_put_lit_cast(cbor_state, "version"); + + bs_list_img_ver((char *)tmpbuf, sizeof(tmpbuf), &hdr.ih_ver); + + zcbor_tstr_encode_ptr(cbor_state, (char *)tmpbuf, strlen((char *)tmpbuf)); + zcbor_map_end_encode(cbor_state, 20); + } + } + zcbor_list_end_encode(cbor_state, 5); + zcbor_map_end_encode(cbor_state, 1); + boot_serial_output(); +} + +#ifdef MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE +/* + * Set image state. + */ +static void +bs_set(char *buf, int len) +{ + /* + * Expected data format. + * { + * "confirm": + * "hash": + * } + */ + uint8_t image_index = 0; + size_t decoded = 0; + uint8_t hash[IMAGE_HASH_SIZE]; + bool confirm; + struct zcbor_string img_hash; + bool ok; + int rc; + +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH + bool found = false; +#endif + + zcbor_state_t zsd[4]; + zcbor_new_state(zsd, sizeof(zsd) / sizeof(zcbor_state_t), (uint8_t *)buf, len, 1, NULL, 0); + + struct zcbor_map_decode_key_val image_set_state_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("confirm", zcbor_bool_decode, &confirm), +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH + ZCBOR_MAP_DECODE_KEY_DECODER("hash", zcbor_bstr_decode, &img_hash), +#endif + }; + + ok = zcbor_map_decode_bulk(zsd, image_set_state_decode, ARRAY_SIZE(image_set_state_decode), + &decoded) == 0; + + if (!ok) { + rc = MGMT_ERR_EINVAL; + goto out; + } + +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH + if ((img_hash.len != sizeof(hash) && img_hash.len != 0) || + (img_hash.len == 0 && BOOT_IMAGE_NUMBER > 1)) { + /* Hash is required and was not provided or is invalid size */ + rc = MGMT_ERR_EINVAL; + goto out; + } + + if (img_hash.len != 0) { + for (image_index = 0; image_index < BOOT_IMAGE_NUMBER; ++image_index) { + struct image_header hdr; + uint32_t area_id; + const struct flash_area *fap; + uint8_t tmpbuf[64]; + + area_id = flash_area_id_from_multi_image_slot(image_index, 1); + if (flash_area_open(area_id, &fap)) { + BOOT_LOG_ERR("Failed to open flash area ID %d", area_id); + continue; + } + + rc = BOOT_HOOK_CALL(boot_read_image_header_hook, + BOOT_HOOK_REGULAR, image_index, 1, &hdr); + if (rc == BOOT_HOOK_REGULAR) + { + flash_area_read(fap, 0, &hdr, sizeof(hdr)); + } + + if (hdr.ih_magic == IMAGE_MAGIC) + { + FIH_DECLARE(fih_rc, FIH_FAILURE); + + BOOT_HOOK_CALL_FIH(boot_image_check_hook, + FIH_BOOT_HOOK_REGULAR, + fih_rc, image_index, 1); + if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) + { +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(&hdr)) { + FIH_CALL(boot_image_validate_encrypted, fih_rc, fap, + &hdr, tmpbuf, sizeof(tmpbuf)); + } else { +#endif + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr, + fap, tmpbuf, sizeof(tmpbuf), NULL, 0, NULL); +#ifdef MCUBOOT_ENC_IMAGES + } +#endif + } + + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + continue; + } + } + + /* Retrieve hash of image for identification */ + rc = boot_serial_get_hash(&hdr, fap, hash); + flash_area_close(fap); + + if (rc == 0 && memcmp(hash, img_hash.value, sizeof(hash)) == 0) { + /* Hash matches, set this slot for test or confirmation */ + found = true; + break; + } + } + + if (!found) { + /* Image was not found with specified hash */ + BOOT_LOG_ERR("Did not find image with specified hash"); + rc = MGMT_ERR_ENOENT; + goto out; + } + } +#endif + + rc = boot_set_pending_multi(image_index, confirm); + +out: + if (rc == 0) { + /* Success - return updated list of images */ + bs_list(buf, len); + } else { + /* Error code, only return the error */ + zcbor_map_start_encode(cbor_state, 10); + zcbor_tstr_put_lit_cast(cbor_state, "rc"); + zcbor_int32_put(cbor_state, rc); + zcbor_map_end_encode(cbor_state, 10); + + boot_serial_output(); + } +} +#endif + +/* + * Send rc code only. + */ +static void +bs_rc_rsp(int rc_code) +{ + zcbor_map_start_encode(cbor_state, 10); + zcbor_tstr_put_lit_cast(cbor_state, "rc"); + zcbor_int32_put(cbor_state, rc_code); + zcbor_map_end_encode(cbor_state, 10); + boot_serial_output(); +} + +static void +bs_list_set(uint8_t op, char *buf, int len) +{ + if (op == NMGR_OP_READ) { + bs_list(buf, len); + } else { +#ifdef MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE + bs_set(buf, len); +#else + bs_rc_rsp(MGMT_ERR_ENOTSUP); +#endif + } +} + +#ifdef MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO +static void +bs_slot_info(uint8_t op, char *buf, int len) +{ + uint32_t slot, area_id; + const struct flash_area *fap; + uint8_t image_index = 0; + int rc; + bool ok = true; + const struct image_max_size *image_max_sizes; + + if (op != NMGR_OP_READ) { + bs_rc_rsp(MGMT_ERR_ENOTSUP); + } + + image_max_sizes = boot_get_max_app_size(); + + zcbor_map_start_encode(cbor_state, 1); + zcbor_tstr_put_lit_cast(cbor_state, "images"); + zcbor_list_start_encode(cbor_state, MCUBOOT_IMAGE_NUMBER); + + IMAGES_ITER(image_index) { + for (slot = 0; slot < MCUBOOT_IMAGE_NUMBER; slot++) { + if (slot == 0) { + ok = zcbor_map_start_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_IMAGE_MAP) && + zcbor_tstr_put_lit(cbor_state, "image") && + zcbor_uint32_put(cbor_state, (uint32_t)image_index) && + zcbor_tstr_put_lit(cbor_state, "slots") && + zcbor_list_start_encode(cbor_state, MCUBOOT_IMAGE_NUMBER); + + if (!ok) { + goto finish; + } + } + + ok = zcbor_map_start_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_SLOTS_MAP) && + zcbor_tstr_put_lit(cbor_state, "slot") && + zcbor_uint32_put(cbor_state, slot); + + if (!ok) { + goto finish; + } + + area_id = flash_area_id_from_multi_image_slot(image_index, slot); + rc = flash_area_open(area_id, &fap); + + if (rc) { + ok = zcbor_tstr_put_lit(cbor_state, "rc") && + zcbor_int32_put(cbor_state, rc); + } else { + if (sizeof(fap->fa_size) == sizeof(uint64_t)) { + ok = zcbor_tstr_put_lit(cbor_state, "size") && + zcbor_uint64_put(cbor_state, fap->fa_size); + } else { + ok = zcbor_tstr_put_lit(cbor_state, "size") && + zcbor_uint32_put(cbor_state, fap->fa_size); + } + + if (!ok) { + flash_area_close(fap); + goto finish; + } + + /* + * Check if we support uploading to this slot and if so, return the + * image ID + */ +#if defined(MCUBOOT_SINGLE_APPLICATION_SLOT) + ok = zcbor_tstr_put_lit(cbor_state, "upload_image_id") && + zcbor_uint32_put(cbor_state, (image_index + 1)); +#elif defined(MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD) + ok = zcbor_tstr_put_lit(cbor_state, "upload_image_id") && + zcbor_uint32_put(cbor_state, (image_index * 2 + slot + 1)); +#else + if (slot == 1) { + ok = zcbor_tstr_put_lit(cbor_state, "upload_image_id") && + zcbor_uint32_put(cbor_state, (image_index * 2 + 1)); + } +#endif + + flash_area_close(fap); + + if (!ok) { + goto finish; + } + + ok = zcbor_map_end_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_SLOTS_MAP); + + if (!ok) { + goto finish; + } + + if (slot == (MCUBOOT_IMAGE_NUMBER - 1)) { + ok = zcbor_list_end_encode(cbor_state, MCUBOOT_IMAGE_NUMBER); + + if (!ok) { + goto finish; + } + + if (image_max_sizes[image_index].calculated == true) { + ok = zcbor_tstr_put_lit(cbor_state, "max_image_size") && + zcbor_uint32_put(cbor_state, + image_max_sizes[image_index].max_size); + + if (!ok) { + goto finish; + } + } + + ok = zcbor_map_end_encode(cbor_state, CBOR_ENTRIES_SLOT_INFO_IMAGE_MAP); + + } + } + + if (!ok) { + goto finish; + } + } + } + + ok = zcbor_list_end_encode(cbor_state, MCUBOOT_IMAGE_NUMBER) && + zcbor_map_end_encode(cbor_state, 1); + +finish: + if (!ok) { + reset_cbor_state(); + bs_rc_rsp(MGMT_ERR_ENOMEM); + } + + boot_serial_output(); +} +#endif + +#ifdef MCUBOOT_ERASE_PROGRESSIVELY +/** Erases range of flash, aligned to sector size + * + * Function will erase all sectors withing [start, end] range; it does not check + * the @p start for alignment, and it will use @p end to find boundaries of las + * sector to erase. Function returns offset of the first byte past the last + * erased sector, so basically offset of next sector to be erased if needed. + * The function is intended to be called iteratively with previously returned + * offset as @p start. + * + * @param start starting offset, aligned to sector offset; + * @param end ending offset, maybe anywhere within sector; + * + * @retval On success: offset of the first byte past last erased sector; + * On failure: -EINVAL. + */ +static off_t erase_range(const struct flash_area *fap, off_t start, off_t end) +{ + struct flash_sector sect; + size_t size; + int rc; + + if (end >= flash_area_get_size(fap)) { + return -EINVAL; + } + + if (end < start) { + return start; + } + + if (flash_area_get_sector(fap, end, §)) { + return -EINVAL; + } + + size = flash_sector_get_off(§) + flash_sector_get_size(§) - start; + BOOT_LOG_DBG("Erasing range 0x%jx:0x%jx", (intmax_t)start, + (intmax_t)(start + size - 1)); + + rc = flash_area_erase(fap, start, size); + if (rc != 0) { + BOOT_LOG_ERR("Error %d while erasing range", rc); + return -EINVAL; + } + + return start + size; +} +#endif + +/* + * Image upload request. + */ +static void +bs_upload(char *buf, int len) +{ + static size_t img_size; /* Total image size, held for duration of upload */ + static uint32_t curr_off; /* Expected current offset */ + const uint8_t *img_chunk = NULL; /* Pointer to buffer with received image chunk */ + size_t img_chunk_len = 0; /* Length of received image chunk */ + size_t img_chunk_off = SIZE_MAX; /* Offset of image chunk within image */ + uint8_t rem_bytes; /* Reminder bytes after aligning chunk write to + * to flash alignment */ + uint32_t img_num_tmp = UINT_MAX; /* Temp variable for image number */ + static uint32_t img_num = 0; + size_t img_size_tmp = SIZE_MAX; /* Temp variable for image size */ + const struct flash_area *fap = NULL; + int rc; + struct zcbor_string img_chunk_data; + size_t decoded = 0; + bool ok; +#ifdef MCUBOOT_ERASE_PROGRESSIVELY + static off_t not_yet_erased = 0; /* Offset of next byte to erase; writes to flash + * are done in consecutive manner and erases are done + * to allow currently received chunk to be written; + * this state variable holds information where last + * erase has stopped to let us know whether erase + * is needed to be able to write current chunk. + */ + static struct flash_sector status_sector; +#endif + + zcbor_state_t zsd[4]; + zcbor_new_state(zsd, sizeof(zsd) / sizeof(zcbor_state_t), (uint8_t *)buf, len, 1, NULL, 0); + + struct zcbor_map_decode_key_val image_upload_decode[] = { + ZCBOR_MAP_DECODE_KEY_DECODER("image", zcbor_uint32_decode, &img_num_tmp), + ZCBOR_MAP_DECODE_KEY_DECODER("data", zcbor_bstr_decode, &img_chunk_data), + ZCBOR_MAP_DECODE_KEY_DECODER("len", zcbor_size_decode, &img_size_tmp), + ZCBOR_MAP_DECODE_KEY_DECODER("off", zcbor_size_decode, &img_chunk_off), + }; + + ok = zcbor_map_decode_bulk(zsd, image_upload_decode, ARRAY_SIZE(image_upload_decode), + &decoded) == 0; + + if (!ok) { + goto out_invalid_data; + } + + img_chunk = img_chunk_data.value; + img_chunk_len = img_chunk_data.len; + + /* + * Expected data format. + * { + * "image": + * "data": + * "len": + * "off": + * } + */ + + if (img_chunk_off == SIZE_MAX || img_chunk == NULL) { + /* + * Offset must be set in every block. + */ + goto out_invalid_data; + } + + /* Use image number only from packet with offset == 0. */ + if (img_chunk_off == 0) { + if (img_num_tmp != UINT_MAX) { + img_num = img_num_tmp; + } else { + img_num = 0; + } + } + +#if !defined(MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD) + rc = flash_area_open(flash_area_id_from_multi_image_slot(img_num, 0), &fap); +#else + rc = flash_area_open(flash_area_id_from_direct_image(img_num), &fap); +#endif + if (rc) { + rc = MGMT_ERR_EINVAL; + goto out; + } + + if (img_chunk_off == 0) { + /* Receiving chunk with 0 offset resets the upload state; this basically + * means that upload has started from beginning. + */ + const size_t area_size = flash_area_get_size(fap); + + curr_off = 0; +#ifdef MCUBOOT_ERASE_PROGRESSIVELY + /* Get trailer sector information; this is done early because inability to get + * that sector information means that upload will not work anyway. + * TODO: This is single occurrence issue, it should get detected during tests + * and fixed otherwise you are deploying broken mcuboot. + */ + if (flash_area_get_sector(fap, boot_status_off(fap), &status_sector)) { + rc = MGMT_ERR_EUNKNOWN; + BOOT_LOG_ERR("Unable to determine flash sector of the image trailer"); + goto out; + } +#endif + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) + /* We are using swap state at end of flash area to store validation + * result. Make sure the user cannot write it from an image to skip validation. + */ + if (img_size_tmp > (area_size - BOOT_MAGIC_SZ)) { + goto out_invalid_data; + } +#else + if (img_size_tmp > area_size) { + goto out_invalid_data; + } + +#endif + +#ifndef MCUBOOT_ERASE_PROGRESSIVELY + /* Non-progressive erase erases entire image slot when first chunk of + * an image is received. + */ + rc = flash_area_erase(fap, 0, area_size); + if (rc) { + goto out_invalid_data; + } +#else + not_yet_erased = 0; +#endif + + img_size = img_size_tmp; + } else if (img_chunk_off != curr_off) { + /* If received chunk offset does not match expected one jump, pretend + * success and jump to out; out will respond to client with success + * and request the expected offset, held by curr_off. + */ + rc = 0; + goto out; + } else if (curr_off + img_chunk_len > img_size) { + rc = MGMT_ERR_EINVAL; + goto out; + } + +#ifdef MCUBOOT_ERASE_PROGRESSIVELY + /* Progressive erase will erase enough flash, aligned to sector size, + * as needed for the current chunk to be written. + */ + not_yet_erased = erase_range(fap, not_yet_erased, + curr_off + img_chunk_len - 1); + + if (not_yet_erased < 0) { + rc = MGMT_ERR_EINVAL; + goto out; + } +#endif + + /* Writes are aligned to flash write alignment, so may drop a few bytes + * from the end of the buffer; we will request these bytes again with + * new buffer by responding with request for offset after the last aligned + * write. + */ + rem_bytes = img_chunk_len % flash_area_align(fap); + img_chunk_len -= rem_bytes; + + if (curr_off + img_chunk_len + rem_bytes < img_size) { + rem_bytes = 0; + } + + BOOT_LOG_DBG("Writing at 0x%x until 0x%x", curr_off, curr_off + (uint32_t)img_chunk_len); + /* Write flash aligned chunk, note that img_chunk_len now holds aligned length */ +#if defined(MCUBOOT_SERIAL_UNALIGNED_BUFFER_SIZE) && MCUBOOT_SERIAL_UNALIGNED_BUFFER_SIZE > 0 + if (flash_area_align(fap) > 1 && + (((size_t)img_chunk) & (flash_area_align(fap) - 1)) != 0) { + /* Buffer address incompatible with write address, use buffer to write */ + uint8_t write_size = MCUBOOT_SERIAL_UNALIGNED_BUFFER_SIZE; + uint8_t wbs_aligned[MCUBOOT_SERIAL_UNALIGNED_BUFFER_SIZE]; + + while (img_chunk_len >= flash_area_align(fap)) { + if (write_size > img_chunk_len) { + write_size = img_chunk_len; + } + + memset(wbs_aligned, flash_area_erased_val(fap), sizeof(wbs_aligned)); + memcpy(wbs_aligned, img_chunk, write_size); + + rc = flash_area_write(fap, curr_off, wbs_aligned, write_size); + + if (rc != 0) { + goto out; + } + + curr_off += write_size; + img_chunk += write_size; + img_chunk_len -= write_size; + } + } else { + rc = flash_area_write(fap, curr_off, img_chunk, img_chunk_len); + } +#else + rc = flash_area_write(fap, curr_off, img_chunk, img_chunk_len); +#endif + + if (rc == 0 && rem_bytes) { + /* Non-zero rem_bytes means that last chunk needs alignment; the aligned + * part, in the img_chunk_len - rem_bytes count bytes, has already been + * written by the above write, so we are left with the rem_bytes. + */ + uint8_t wbs_aligned[BOOT_MAX_ALIGN]; + + memset(wbs_aligned, flash_area_erased_val(fap), sizeof(wbs_aligned)); + memcpy(wbs_aligned, img_chunk + img_chunk_len, rem_bytes); + + rc = flash_area_write(fap, curr_off + img_chunk_len, wbs_aligned, + flash_area_align(fap)); + } + + if (rc == 0) { + curr_off += img_chunk_len + rem_bytes; + if (curr_off == img_size) { +#ifdef MCUBOOT_ERASE_PROGRESSIVELY + /* Assure that sector for image trailer was erased. */ + /* Check whether it was erased during previous upload. */ + off_t start = flash_sector_get_off(&status_sector); + + if (erase_range(fap, start, start) < 0) { + rc = MGMT_ERR_EUNKNOWN; + goto out; + } +#endif + rc = BOOT_HOOK_CALL(boot_serial_uploaded_hook, 0, img_num, fap, + img_size); + if (rc) { + BOOT_LOG_ERR("Error %d post upload hook", rc); + goto out; + } + } + } else { + out_invalid_data: + rc = MGMT_ERR_EINVAL; + } + +out: + BOOT_LOG_DBG("RX: 0x%x", rc); + zcbor_map_start_encode(cbor_state, 10); + zcbor_tstr_put_lit_cast(cbor_state, "rc"); + zcbor_int32_put(cbor_state, rc); + if (rc == 0) { + zcbor_tstr_put_lit_cast(cbor_state, "off"); + zcbor_uint32_put(cbor_state, curr_off); + } + zcbor_map_end_encode(cbor_state, 10); + + boot_serial_output(); + +#ifdef MCUBOOT_ENC_IMAGES + /* Check if this upload was for the primary slot */ +#if !defined(MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD) + if (flash_area_id_from_multi_image_slot(img_num, 0) == FLASH_AREA_IMAGE_PRIMARY(0)) +#else + if (flash_area_id_from_direct_image(img_num) == FLASH_AREA_IMAGE_PRIMARY(0)) +#endif + { + if (curr_off == img_size) { + /* Last sector received, now start a decryption on the image if it is encrypted */ + rc = boot_handle_enc_fw(fap); + } + } +#endif + + flash_area_close(fap); +} + +#ifdef MCUBOOT_BOOT_MGMT_ECHO +static void +bs_echo(char *buf, int len) +{ + struct zcbor_string value = { 0 }; + struct zcbor_string key; + bool ok; + uint32_t rc = MGMT_ERR_EINVAL; + + zcbor_state_t zsd[4]; + zcbor_new_state(zsd, sizeof(zsd) / sizeof(zcbor_state_t), (uint8_t *)buf, len, 1, NULL, 0); + + if (!zcbor_map_start_decode(zsd)) { + goto out; + } + + do { + ok = zcbor_tstr_decode(zsd, &key); + + if (ok) { + if (key.len == 1 && *key.value == 'd') { + ok = zcbor_tstr_decode(zsd, &value); + break; + } + + ok = zcbor_any_skip(zsd, NULL); + } + } while (ok); + + if (!ok || !zcbor_map_end_decode(zsd)) { + goto out; + } + + zcbor_map_start_encode(cbor_state, 10); + zcbor_tstr_put_lit(cbor_state, "r"); + if (zcbor_tstr_encode(cbor_state, &value) && zcbor_map_end_encode(cbor_state, 10)) { + boot_serial_output(); + return; + } else { + rc = MGMT_ERR_ENOMEM; + } + +out: + reset_cbor_state(); + bs_rc_rsp(rc); +} +#endif + +/* + * Reset, and (presumably) boot to newly uploaded image. Flush console + * before restarting. + */ +static void +bs_reset(char *buf, int len) +{ + int rc = BOOT_HOOK_CALL(boot_reset_request_hook, 0, false); + if (rc == BOOT_RESET_REQUEST_HOOK_BUSY) { + rc = MGMT_ERR_EBUSY; + } else { + /* Currently whatever else is returned it is just converted + * to 0/no error. Boot serial starts accepting "force" parameter + * in command this needs to change. + */ + rc = 0; + } + bs_rc_rsp(rc); + + if (rc == 0) { +#ifdef __ZEPHYR__ +#ifdef CONFIG_MULTITHREADING + k_sleep(K_MSEC(250)); +#else + k_busy_wait(250000); +#endif + sys_reboot(SYS_REBOOT_COLD); +#elif __ESPRESSIF__ + esp_rom_delay_us(250000); + bootloader_reset(); +#else + os_cputime_delay_usecs(250000); + hal_system_reset(); +#endif + } +} + +/* + * Parse incoming line of input from console. + * Expect newtmgr protocol with serial transport. + */ +void +boot_serial_input(char *buf, int len) +{ + struct nmgr_hdr *hdr; + + hdr = (struct nmgr_hdr *)buf; + if (len < sizeof(*hdr) || + (hdr->nh_op != NMGR_OP_READ && hdr->nh_op != NMGR_OP_WRITE) || + (ntohs(hdr->nh_len) < len - sizeof(*hdr))) { + return; + } + bs_hdr = hdr; + hdr->nh_group = ntohs(hdr->nh_group); + + buf += sizeof(*hdr); + len -= sizeof(*hdr); + + reset_cbor_state(); + + /* + * Limited support for commands. + */ + if (hdr->nh_group == MGMT_GROUP_ID_IMAGE) { + switch (hdr->nh_id) { + case IMGMGR_NMGR_ID_STATE: + bs_list_set(hdr->nh_op, buf, len); + break; + case IMGMGR_NMGR_ID_UPLOAD: + bs_upload(buf, len); + break; +#ifdef MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO + case IMGMGR_NMGR_ID_SLOT_INFO: + bs_slot_info(hdr->nh_op, buf, len); + break; +#endif + default: + bs_rc_rsp(MGMT_ERR_ENOTSUP); + break; + } + } else if (hdr->nh_group == MGMT_GROUP_ID_DEFAULT) { + switch (hdr->nh_id) { +#ifdef MCUBOOT_BOOT_MGMT_ECHO + case NMGR_ID_ECHO: + bs_echo(buf, len); + break; +#endif + case NMGR_ID_CONS_ECHO_CTRL: + bs_rc_rsp(0); + break; + case NMGR_ID_RESET: + bs_reset(buf, len); + break; + default: + bs_rc_rsp(MGMT_ERR_ENOTSUP); + break; + } + } else if (MCUBOOT_PERUSER_MGMT_GROUP_ENABLED == 1) { + if (bs_peruser_system_specific(hdr, buf, len, cbor_state) == 0) { + boot_serial_output(); + } + } else { + bs_rc_rsp(MGMT_ERR_ENOTSUP); + } +#ifdef MCUBOOT_SERIAL_WAIT_FOR_DFU + bs_entry = true; +#endif +} + +static void +boot_serial_output(void) +{ + char *data; + int len, out; + uint16_t crc; + uint16_t totlen; + char pkt_cont[2] = { SHELL_NLIP_DATA_START1, SHELL_NLIP_DATA_START2 }; + char pkt_start[2] = { SHELL_NLIP_PKT_START1, SHELL_NLIP_PKT_START2 }; + char buf[BOOT_SERIAL_OUT_MAX + sizeof(*bs_hdr) + sizeof(crc) + sizeof(totlen)]; + char encoded_buf[BASE64_ENCODE_SIZE(sizeof(buf))]; + + data = bs_obuf; + len = (uintptr_t)cbor_state->payload_mut - (uintptr_t)bs_obuf; + + bs_hdr->nh_op++; + bs_hdr->nh_flags = 0; + bs_hdr->nh_len = htons(len); + bs_hdr->nh_group = htons(bs_hdr->nh_group); + +#ifdef __ZEPHYR__ + crc = crc16_itu_t(CRC16_INITIAL_CRC, (uint8_t *)bs_hdr, sizeof(*bs_hdr)); + crc = crc16_itu_t(crc, data, len); +#elif __ESPRESSIF__ + /* For ESP32 it was used the CRC API in rom/crc.h */ + crc = ~esp_crc16_be(~CRC16_INITIAL_CRC, (uint8_t *)bs_hdr, sizeof(*bs_hdr)); + crc = ~esp_crc16_be(~crc, (uint8_t *)data, len); +#else + crc = crc16_ccitt(CRC16_INITIAL_CRC, bs_hdr, sizeof(*bs_hdr)); + crc = crc16_ccitt(crc, data, len); +#endif + crc = htons(crc); + + totlen = len + sizeof(*bs_hdr) + sizeof(crc); + totlen = htons(totlen); + + memcpy(buf, &totlen, sizeof(totlen)); + totlen = sizeof(totlen); + memcpy(&buf[totlen], bs_hdr, sizeof(*bs_hdr)); + totlen += sizeof(*bs_hdr); + memcpy(&buf[totlen], data, len); + totlen += len; + memcpy(&buf[totlen], &crc, sizeof(crc)); + totlen += sizeof(crc); +#ifdef __ZEPHYR__ + size_t enc_len; + base64_encode(encoded_buf, sizeof(encoded_buf), &enc_len, buf, totlen); + totlen = enc_len; +#elif __ESPRESSIF__ + size_t enc_len; + base64_encode((unsigned char *)encoded_buf, sizeof(encoded_buf), &enc_len, (unsigned char *)buf, totlen); + totlen = enc_len; +#else + totlen = base64_encode(buf, totlen, encoded_buf, 1); +#endif + + out = 0; + while (out < totlen) { + if (out == 0) { + boot_uf->write(pkt_start, sizeof(pkt_start)); + } else { + boot_uf->write(pkt_cont, sizeof(pkt_cont)); + } + + len = MIN(BOOT_SERIAL_FRAME_MTU, totlen - out); + boot_uf->write(&encoded_buf[out], len); + + out += len; + + boot_uf->write("\n", 1); + } + + BOOT_LOG_DBG("TX"); +} + +/* + * Returns 1 if full packet has been received. + */ +static int +boot_serial_in_dec(char *in, int inlen, char *out, int *out_off, int maxout) +{ + size_t rc; + uint16_t crc; + uint16_t len; + +#ifdef __ZEPHYR__ + int err; + err = base64_decode( &out[*out_off], maxout - *out_off, &rc, in, inlen - 2); + if (err) { + return -1; + } +#elif __ESPRESSIF__ + int err; + err = base64_decode((unsigned char *)&out[*out_off], maxout - *out_off, &rc, (unsigned char *)in, inlen); + if (err) { + return -1; + } +#else + if (*out_off + base64_decode_len(in) >= maxout) { + return -1; + } + rc = base64_decode(in, &out[*out_off]); + if (rc < 0) { + return -1; + } +#endif + + *out_off += rc; + if (*out_off <= sizeof(uint16_t)) { + return 0; + } + + len = ntohs(*(uint16_t *)out); + if (len != *out_off - sizeof(uint16_t)) { + return 0; + } + + out += sizeof(uint16_t); +#ifdef __ZEPHYR__ + crc = crc16_itu_t(CRC16_INITIAL_CRC, out, len); +#elif __ESPRESSIF__ + crc = ~esp_crc16_be(~CRC16_INITIAL_CRC, (uint8_t *)out, len); +#else + crc = crc16_ccitt(CRC16_INITIAL_CRC, out, len); +#endif + if (crc || len <= sizeof(crc)) { + return 0; + } + *out_off -= sizeof(crc); + out[*out_off] = '\0'; + + return 1; +} + +/* + * Task which waits reading console, expecting to get image over + * serial port. + */ +static void +boot_serial_read_console(const struct boot_uart_funcs *f,int timeout_in_ms) +{ + int rc; + int off; + int dec_off = 0; + int full_line; + int max_input; + int elapsed_in_ms = 0; + +#ifndef MCUBOOT_SERIAL_WAIT_FOR_DFU + bool allow_idle = true; +#endif + + boot_uf = f; + max_input = sizeof(in_buf); + + off = 0; + while (timeout_in_ms > 0 || bs_entry) { + /* + * Don't enter CPU idle state here if timeout based serial recovery is + * used as otherwise the boot process hangs forever, waiting for input + * from serial console (if single-thread mode is used). + */ +#ifndef MCUBOOT_SERIAL_WAIT_FOR_DFU + if (allow_idle == true) { + MCUBOOT_CPU_IDLE(); + allow_idle = false; + } +#endif + MCUBOOT_WATCHDOG_FEED(); +#ifdef MCUBOOT_SERIAL_WAIT_FOR_DFU + uint32_t start = k_uptime_get_32(); +#endif + rc = f->read(in_buf + off, sizeof(in_buf) - off, &full_line); + if (rc <= 0 && !full_line) { +#ifndef MCUBOOT_SERIAL_WAIT_FOR_DFU + allow_idle = true; +#endif + goto check_timeout; + } + off += rc; + if (!full_line) { + if (off == max_input) { + /* + * Full line, no newline yet. Reset the input buffer. + */ + off = 0; + } + goto check_timeout; + } + if (in_buf[0] == SHELL_NLIP_PKT_START1 && + in_buf[1] == SHELL_NLIP_PKT_START2) { + dec_off = 0; + rc = boot_serial_in_dec(&in_buf[2], off - 2, dec_buf, &dec_off, max_input); + } else if (in_buf[0] == SHELL_NLIP_DATA_START1 && + in_buf[1] == SHELL_NLIP_DATA_START2) { + rc = boot_serial_in_dec(&in_buf[2], off - 2, dec_buf, &dec_off, max_input); + } + + /* serve errors: out of decode memory, or bad encoding */ + if (rc == 1) { + boot_serial_input(&dec_buf[2], dec_off - 2); + } + off = 0; +check_timeout: + /* Subtract elapsed time */ +#ifdef MCUBOOT_SERIAL_WAIT_FOR_DFU + elapsed_in_ms = (k_uptime_get_32() - start); +#endif + timeout_in_ms -= elapsed_in_ms; + } +} + +/* + * Task which waits reading console, expecting to get image over + * serial port. + */ +void +boot_serial_start(const struct boot_uart_funcs *f) +{ + bs_entry = true; + boot_serial_read_console(f,0); +} + +#ifdef MCUBOOT_SERIAL_WAIT_FOR_DFU +/* + * Task which waits reading console for a certain amount of timeout. + * If within this timeout no mcumgr command is received, the function is + * returning, else the serial boot is never exited + */ +void +boot_serial_check_start(const struct boot_uart_funcs *f, int timeout_in_ms) +{ + bs_entry = false; + boot_serial_read_console(f,timeout_in_ms); +} +#endif + +#ifdef MCUBOOT_SERIAL_IMG_GRP_HASH +/* Function to find the hash of an image, returns 0 on success. */ +static int boot_serial_get_hash(const struct image_header *hdr, + const struct flash_area *fap, uint8_t *hash) +{ + struct image_tlv_iter it; + uint32_t offset; + uint16_t len; + uint16_t type; + int rc; + + /* Manifest data is concatenated to the end of the image. + * It is encoded in TLV format. + */ + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); + if (rc) { + return -1; + } + + /* Traverse through the TLV area to find the image hash TLV. */ + while (true) { + rc = bootutil_tlv_iter_next(&it, &offset, &len, &type); + if (rc < 0) { + return -1; + } else if (rc > 0) { + break; + } + + if (type == IMAGE_SHA_TLV) { + /* Get the image's hash value from the manifest section. */ + if (len != IMAGE_HASH_SIZE) { + return -1; + } + + rc = flash_area_read(fap, offset, hash, len); + if (rc) { + return -1; + } + + return 0; + } + } + + return -1; +} +#endif diff --git a/bootloader/mcuboot/boot/boot_serial/src/boot_serial_encryption.c b/bootloader/mcuboot/boot/boot_serial/src/boot_serial_encryption.c new file mode 100644 index 0000000..c4bd7d8 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/src/boot_serial_encryption.c @@ -0,0 +1,308 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020-2023 Nordic Semiconductor ASA + * Copyright (c) 2020 Arm Limited + */ + +#include +#include "bootutil/image.h" +#include <../src/bootutil_priv.h> +#include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/enc_key.h" + +#include "mcuboot_config/mcuboot_config.h" + +#ifdef MCUBOOT_ENC_IMAGES + +BOOT_LOG_MODULE_DECLARE(serial_encryption); + +fih_ret +boot_image_validate_encrypted(const struct flash_area *fa_p, + struct image_header *hdr, uint8_t *buf, + uint16_t buf_size) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + + struct boot_loader_state boot_data; + struct boot_loader_state *state = &boot_data; + struct boot_status _bs; + struct boot_status *bs = &_bs; + uint8_t image_index; + int rc; + + memset(&boot_data, 0, sizeof(struct boot_loader_state)); + image_index = BOOT_CURR_IMG(state); + if(IS_ENCRYPTED(hdr)) { + rc = boot_enc_load(BOOT_CURR_ENC(state), 1, hdr, fa_p, bs); + if (rc < 0) { + FIH_RET(fih_rc); + } + rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs); + if (rc < 0) { + FIH_RET(fih_rc); + } + } + FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), image_index, + hdr, fa_p, buf, buf_size, NULL, 0, NULL); + + FIH_RET(fih_rc); +} + +/* + * Compute the total size of the given image. Includes the size of + * the TLVs. + */ +static int +read_image_size(const struct flash_area *fa_p, + struct image_header *hdr, + uint32_t *size) +{ + struct image_tlv_info info; + uint32_t off; + uint32_t protect_tlv_size; + int rc; + + off = BOOT_TLV_OFF(hdr); + + if (flash_area_read(fa_p, off, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + protect_tlv_size = hdr->ih_protect_tlv_size; + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (protect_tlv_size != info.it_tlv_tot) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (flash_area_read(fa_p, off + info.it_tlv_tot, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + } else if (protect_tlv_size != 0) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + protect_tlv_size + info.it_tlv_tot; + rc = 0; + +done: + return rc; +} + +/** + * reads, decrypts in RAM & write back the decrypted image in the same region + * This function is NOT power failsafe since the image is decrypted in the RAM + * buffer. + * + * @param flash_area The ID of the source flash area. + * @param off_src The offset within the flash area to + * copy from. + * @param sz The number of bytes to copy. should match erase sector + * + * @return 0 on success; nonzero on failure. + */ +static int +decrypt_region_inplace(struct boot_loader_state *state, + const struct flash_area *fap, + struct image_header *hdr, + uint32_t off, uint32_t sz) +{ + uint32_t bytes_copied; + int chunk_sz; + int rc; + uint32_t tlv_off; + size_t blk_off; + uint16_t idx; + uint32_t blk_sz; + int slot = flash_area_id_to_multi_image_slot(BOOT_CURR_IMG(state), + flash_area_get_id(fap)); + uint8_t buf[sz] __attribute__((aligned)); + assert(sz <= sizeof buf); + assert(slot >= 0); + + bytes_copied = 0; + while (bytes_copied < sz) { + if (sz - bytes_copied > sizeof buf) { + chunk_sz = sizeof buf; + } else { + chunk_sz = sz - bytes_copied; + } + + rc = flash_area_read(fap, off + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + + if (IS_ENCRYPTED(hdr)) { + blk_sz = chunk_sz; + idx = 0; + if (off + bytes_copied < hdr->ih_hdr_size) { + /* do not decrypt header */ + if (hdr->ih_hdr_size > (off + bytes_copied + chunk_sz)) { + /* all bytes in header, skip decryption */ + blk_sz = 0; + } + else { + blk_sz = off + bytes_copied + chunk_sz - hdr->ih_hdr_size; + } + + blk_off = 0; + idx = hdr->ih_hdr_size; + } else { + blk_off = ((off + bytes_copied) - hdr->ih_hdr_size) & 0xf; + } + tlv_off = BOOT_TLV_OFF(hdr); + if (off + bytes_copied + chunk_sz > tlv_off) { + /* do not decrypt TLVs */ + if (off + bytes_copied >= tlv_off) { + blk_sz = 0; + } else { + blk_sz = tlv_off - (off + bytes_copied); + } + } + boot_enc_decrypt(BOOT_CURR_ENC(state), slot, + (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } + rc = flash_area_erase(fap, off + bytes_copied, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + rc = flash_area_write(fap, off + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + + bytes_copied += chunk_sz; + + MCUBOOT_WATCHDOG_FEED(); + } + + return 0; +} + +/** + * Check if a image was encrypted into the first slot, and decrypt it + * in place. this operation is not power failsafe. + * + * The operation is done by checking the last flash sector, and using it as a + * temporarely scratch partition. The + * + * @param[in] fa_p flash area pointer + * @param[in] hdr boot image header pointer + * + * @return FIH_SUCCESS on success, error code otherwise + */ +inline static fih_ret +decrypt_image_inplace(const struct flash_area *fa_p, + struct image_header *hdr) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + int rc; + struct boot_loader_state boot_data; + struct boot_loader_state *state = &boot_data; + struct boot_status _bs; + struct boot_status *bs = &_bs; + size_t size; + size_t sect_size; + size_t sect_count; + size_t sect; + struct flash_sector sector; + + memset(&boot_data, 0, sizeof(struct boot_loader_state)); + memset(&_bs, 0, sizeof(struct boot_status)); + + /* Get size from last sector to know page/sector erase size */ + rc = flash_area_get_sector(fa_p, boot_status_off(fa_p), §or); + + + if(IS_ENCRYPTED(hdr)) { +#if 0 //Skip this step?, the image will just not boot if it's not decrypted properly + static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + /* First check if the encrypted image is a good image before decrypting */ + FIH_CALL(boot_image_validate_encrypted,fih_rc,fa_p,&_hdr,tmpbuf,BOOT_TMPBUF_SZ); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(fih_rc); + } +#endif + memset(&boot_data, 0, sizeof(struct boot_loader_state)); + /* Load the encryption keys into cache */ + rc = boot_enc_load(BOOT_CURR_ENC(state), 0, hdr, fa_p, bs); + if (rc < 0) { + FIH_RET(fih_rc); + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) { + FIH_RET(fih_rc); + } + } + else + { + /* Expected encrypted image! */ + FIH_RET(fih_rc); + } + + uint32_t src_size = 0; + rc = read_image_size(fa_p,hdr, &src_size); + if (rc != 0) { + FIH_RET(fih_rc); + } + + /* TODO: This assumes every sector has an equal size, should instead use + * flash_area_get_sectors() to get the size of each sector and iterate + * over it. + */ + sect_size = sector.fs_size; + sect_count = fa_p->fa_size / sect_size; + for (sect = 0, size = 0; size < src_size && sect < sect_count; sect++) { + rc = decrypt_region_inplace(state, fa_p,hdr, size, sect_size); + if (rc != 0) { + FIH_RET(fih_rc); + } + size += sect_size; + } + + fih_rc = FIH_SUCCESS; + FIH_RET(fih_rc); +} + +int +boot_handle_enc_fw(const struct flash_area *flash_area) +{ + int rc = -1; + struct image_header _hdr = { 0 }; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + rc = boot_image_load_header(flash_area, &_hdr); + if (rc != 0) { + goto out; + } + + if (IS_ENCRYPTED(&_hdr)) { + //encrypted, we need to decrypt in place + FIH_CALL(decrypt_image_inplace,fih_rc,flash_area,&_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + rc = -1; + goto out; + } + } + else + { + rc = 0; + } + +out: + return rc; +} + +#endif diff --git a/bootloader/mcuboot/boot/boot_serial/src/boot_serial_priv.h b/bootloader/mcuboot/boot/boot_serial/src/boot_serial_priv.h new file mode 100644 index 0000000..1801da1 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/src/boot_serial_priv.h @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __BOOTUTIL_SERIAL_PRIV_H__ +#define __BOOTUTIL_SERIAL_PRIV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * From shell.h + */ +#define SHELL_NLIP_PKT_START1 6 +#define SHELL_NLIP_PKT_START2 9 + +#define SHELL_NLIP_DATA_START1 4 +#define SHELL_NLIP_DATA_START2 20 + +/* + * From newtmgr.h + */ +#define MGMT_ERR_OK 0 +#define MGMT_ERR_EUNKNOWN 1 +#define MGMT_ERR_ENOMEM 2 +#define MGMT_ERR_EINVAL 3 +#define MGMT_ERR_ENOENT 5 +#define MGMT_ERR_ENOTSUP 8 +#define MGMT_ERR_EBUSY 10 + +#define NMGR_OP_READ 0 +#define NMGR_OP_WRITE 2 + +#define MGMT_GROUP_ID_DEFAULT 0 +#define MGMT_GROUP_ID_IMAGE 1 +#define MGMT_GROUP_ID_PERUSER 64 + +#define NMGR_ID_ECHO 0 +#define NMGR_ID_CONS_ECHO_CTRL 1 +#define NMGR_ID_RESET 5 + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +struct nmgr_hdr { +#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + uint8_t _res1:3; + uint8_t nh_version:2; + uint8_t nh_op:3; /* NMGR_OP_XXX */ +#else + uint8_t nh_op:3; /* NMGR_OP_XXX */ + uint8_t nh_version:2; + uint8_t _res1:3; +#endif + uint8_t nh_flags; + uint16_t nh_len; /* length of the payload */ + uint16_t nh_group; /* NMGR_GROUP_XXX */ + uint8_t nh_seq; /* sequence number */ + uint8_t nh_id; /* message ID within group */ +} __packed; + +/* + * From imgmgr.h + */ +#define IMGMGR_NMGR_ID_STATE 0 +#define IMGMGR_NMGR_ID_UPLOAD 1 +#define IMGMGR_NMGR_ID_SLOT_INFO 6 + +void boot_serial_input(char *buf, int len); +extern const struct boot_uart_funcs *boot_uf; + +/** + * @brief Selects direct image to upload according to the "image" + * parameter of the mcumgr update frame. + * + * @param[in] image_id the value of the "image" parameter of the + * mcumgr update frame to be translated. + * + * @return flash area ID for the image if defined; + * -EINVAL when flash area for given image number has not been + * defined. + */ +extern int flash_area_id_from_direct_image(int image_id); + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_SERIAL_PRIV_H__ */ diff --git a/bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.c b/bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.c new file mode 100644 index 0000000..35ae0bd --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "zcbor_bulk.h" + +int zcbor_map_decode_bulk(zcbor_state_t *zsd, struct zcbor_map_decode_key_val *map, + size_t map_size, size_t *matched) +{ + bool ok; + struct zcbor_map_decode_key_val *dptr = map; + + if (!zcbor_map_start_decode(zsd)) { + return -EBADMSG; + } + + *matched = 0; + ok = true; + + do { + struct zcbor_string key; + bool found = false; + size_t map_count = 0; + + ok = zcbor_tstr_decode(zsd, &key); + + while (ok && map_count < map_size) { + if (dptr >= (map + map_size)) { + dptr = map; + } + + if (key.len == dptr->key.len && + memcmp(key.value, dptr->key.value, key.len) == 0) { + + if (dptr->found) { + return -EADDRINUSE; + } + if (!dptr->decoder(zsd, dptr->value_ptr)) { + /* Failure to decode value matched to key + * means that either decoder has been + * incorrectly assigned or SMP payload + * is broken anyway. + */ + return -ENOMSG; + } + + dptr->found = true; + found = true; + ++dptr; + ++(*matched); + break; + } + + ++dptr; + ++map_count; + } + + if (!found && ok) { + ok = zcbor_any_skip(zsd, NULL); + } + } while (ok); + + return zcbor_map_end_decode(zsd) ? 0 : -EBADMSG; +} diff --git a/bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.h b/bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.h new file mode 100644 index 0000000..3b7c42f --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/src/zcbor_bulk.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef H_ZCBOR_BULK_PRIV_ +#define H_ZCBOR_BULK_PRIV_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __ZEPHYR__ +#include +#include +#else +#include "zcbor_common.h" +#include "zcbor_decode.h" +#endif + +/** @cond INTERNAL_HIDDEN */ + +struct zcbor_map_decode_key_val { + struct zcbor_string key; /* Map key string */ + zcbor_decoder_t *decoder; /* Key corresponding decoder */ + void *value_ptr; + bool found; +}; + +/** @brief Define single key-decoder mapping + * + * The macro creates a single zcbor_map_decode_key_val type object. + * + * @param k key is "" enclosed string representing key; + * @param dec decoder function; this should be zcbor_decoder_t + * type function from zcbor or a user provided implementation + * compatible with the type. + * @param vp non-NULL pointer for result of decoding; should correspond + * to type served by decoder function for the mapping. + */ +#define ZCBOR_MAP_DECODE_KEY_DECODER(k, dec, vp) \ + { \ + { \ + .value = (uint8_t *)k, \ + .len = sizeof(k) - 1, \ + }, \ + .decoder = (zcbor_decoder_t *)dec, \ + .value_ptr = vp, \ + } + +/** @brief Define single key-value decode mapping + * + * ZCBOR_MAP_DECODE_KEY_DECODER should be used instead of this macro as, + * this macro does not allow keys with whitespaces embeeded, which CBOR + * does allow. + * + * The macro creates a single zcbor_map_decode_key_val type object. + * + * @param k key; the @p k will be stringified so should be given + * without ""; + * @param dec decoder function; this should be zcbor_decoder_t + * type function from zcbor or a user provided implementation + * compatible with the type. + * @param vp non-NULL pointer for result of decoding; should correspond + * to type served by decoder function for the mapping. + */ +#define ZCBOR_MAP_DECODE_KEY_VAL(k, dec, vp) \ + ZCBOR_MAP_DECODE_KEY_DECODER(STRINGIFY(k), dec, vp) + +/** @brief Decodes single level map according to a provided key-decode map. + * + * The function takes @p map of key to decoder array defined as: + * + * struct zcbor_map_decode_key_val map[] = { + * ZCBOR_MAP_DECODE_KEY_DECODER("key0", decode_fun0, val_ptr0), + * ZCBOR_MAP_DECODE_KEY_DECODER("key1", decode_fun1, val_ptr1), + * ... + * }; + * + * where "key?" is string representing key; the decode_fun? is + * zcbor_decoder_t compatible function, either from zcbor or defined by + * user; val_ptr? are pointers to variables where decoder function for + * a given key will place a decoded value - they have to agree in type + * with decoder function. + * + * Failure to decode any of values will cause the function to return + * negative error, and leave the map open: map is broken anyway or key-decoder + * mapping is broken, and we can not really decode the map. + * + * Note that the function opens map by itself and will fail if map + * is already opened. + * + * @param zsd zcbor decoder state; + * @param map key-decoder mapping list; + * @param map_size size of maps, both maps have to have the same size; + * @param matched pointer to the counter of matched keys, zeroed upon + * successful map entry and incremented only for successful + * decoded fields. + * @return 0 when the whole map has been parsed, there have been + * no decoding errors, and map has been closed successfully; + * -ENOMSG when given decoder function failed to decode + * value; + * -EADDRINUSE when key appears twice within map, map is then + * parsed up to they key that has appeared twice; + * -EBADMSG when failed to close map. + */ +int zcbor_map_decode_bulk(zcbor_state_t *zsd, struct zcbor_map_decode_key_val *map, + size_t map_size, size_t *matched); + +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* H_ZCBOR_BULK_PRIV_ */ diff --git a/bootloader/mcuboot/boot/boot_serial/syscfg.yml b/bootloader/mcuboot/boot/boot_serial/syscfg.yml new file mode 100644 index 0000000..e479bd5 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/syscfg.yml @@ -0,0 +1,96 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BOOT_SERIAL_DETECT_PIN: + description: > + Start the serial boot loader if this pin is asserted at boot time. + value: '-1' + restrictions: + - '(BOOT_SERIAL_DETECT_PIN != -1) || + (BOOT_SERIAL_DETECT_TIMEOUT != 0) || + (BOOT_SERIAL_NVREG_INDEX != -1)' + + BOOT_SERIAL_DETECT_PIN_CFG: + description: > + GPIO configuration for the serial boot loader detect pin. + value: 'HAL_GPIO_PULL_UP' + + BOOT_SERIAL_DETECT_PIN_VAL: + description: > + The value the detect pin must be set to for the serial boot loader + to start. + value: 0 + + BOOT_SERIAL_DETECT_TIMEOUT: + description: > + The duration, in milliseconds, to listen on the UART for the + management string (BOOT_SERIAL_DETECT_STRING). If the management + string is detected during this period, the serial boot loader is + started. If the period expires without the management string being + received, the boot loader runs in the normal (non-serial) mode. + Specify 0 to disable listening on the UART for the management + string. + value: 0 + restrictions: + - '(BOOT_SERIAL_DETECT_PIN != -1) || + (BOOT_SERIAL_DETECT_TIMEOUT != 0) || + (BOOT_SERIAL_NVREG_INDEX != -1)' + + BOOT_SERIAL_DETECT_STRING: + description: > + The string to listen for on the UART. If this management string is + detected during the timeout period, the serial boot loader is + started. If the period expires without this string being received, + the boot loader runs in the normal (non-serial) mode. This setting + has no effect if BOOT_SERIAL_DETECT_TIMEOUT is set to 0. + value: '"nmgr"' + + BOOT_SERIAL_REPORT_PIN: + description: > + The GPIO to toggle while the serial boot loader is running. Set to + -1 to disable reporting. + value: 'LED_BLINK_PIN' + + BOOT_SERIAL_REPORT_FREQ: + description: > + The toggle rate, in Hz, of the serial boot loader report pin. + value: 4 + + BOOT_SERIAL_NVREG_MAGIC: + description: > + Magic number, to be saved in a retained (reset-surviving) register. + If the value in the register matches, the serial bootloader will + load. Value must not be 0. + value: 0xB7 + restrictions: + - '(BOOT_SERIAL_NVREG_MAGIC != 0)' + + BOOT_SERIAL_NVREG_INDEX: + description: > + Index of retained register to use (using hal_nvreg_read) for reading + magic value. + value: -1 + restrictions: + - '(BOOT_SERIAL_DETECT_PIN != -1) || + (BOOT_SERIAL_DETECT_TIMEOUT != 0) || + (BOOT_SERIAL_NVREG_INDEX != -1)' + + BOOT_SERIAL_MGMT_ECHO: + description: If enabled, support for the mcumgr echo command is being added. + value: 0 diff --git a/bootloader/mcuboot/boot/boot_serial/test/pkg.yml b/bootloader/mcuboot/boot/boot_serial/test/pkg.yml new file mode 100644 index 0000000..5f55728 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/pkg.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +pkg.name: boot/boot_serial/test +pkg.type: unittest +pkg.description: "Boot serial unit tests." +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@mcuboot/boot/boot_serial" + - "@mcuboot/boot/bootutil" + - "@apache-mynewt-core/sys/log/stub" + - "@apache-mynewt-core/test/testutil" + +pkg.deps.SELFTEST: + - "@apache-mynewt-core/sys/console/stub" + +pkg.cflags: + - '-DMCUBOOT_MYNEWT=1' diff --git a/bootloader/mcuboot/boot/boot_serial/test/src/boot_test.c b/bootloader/mcuboot/boot/boot_serial/test/src/boot_test.c new file mode 100644 index 0000000..e8deb7e --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/src/boot_test.c @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include +#include +#include +#include +#include +#include +#include "syscfg/syscfg.h" +#include "sysflash/sysflash.h" +#include "os/endian.h" +#include "base64/base64.h" +#include "crc/crc16.h" +#include "testutil/testutil.h" +#include "hal/hal_flash.h" +#include "flash_map_backend/flash_map_backend.h" + +#include "boot_serial/boot_serial.h" +#include "boot_serial_priv.h" + +TEST_CASE_DECL(boot_serial_setup) +TEST_CASE_DECL(boot_serial_empty_msg) +TEST_CASE_DECL(boot_serial_empty_img_msg) +TEST_CASE_DECL(boot_serial_img_msg) +TEST_CASE_DECL(boot_serial_upload_bigger_image) + +static void +test_uart_write(const char *str, int len) +{ +} + +static const struct boot_uart_funcs test_uart = { + .write = test_uart_write +}; + +void +tx_msg(void *src, int len) +{ + boot_serial_input(src, len); +} + +TEST_SUITE(boot_serial_suite) +{ + boot_serial_setup(); + boot_serial_empty_msg(); + boot_serial_empty_img_msg(); + boot_serial_img_msg(); + boot_serial_upload_bigger_image(); +} + +int +boot_serial_test(void) +{ + boot_uf = &test_uart; + boot_serial_suite(); + return tu_any_failed; +} + +#if MYNEWT_VAL(SELFTEST) +int +main(void) +{ + sysinit(); + + boot_serial_test(); + + return tu_any_failed; +} +#endif diff --git a/bootloader/mcuboot/boot/boot_serial/test/src/boot_test.h b/bootloader/mcuboot/boot/boot_serial/test/src/boot_test.h new file mode 100644 index 0000000..2139d9f --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/src/boot_test.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef _BOOT_TEST_H +#define _BOOT_TEST_H + +#include +#include +#include +#include +#include +#include +#include "syscfg/syscfg.h" +#include "sysflash/sysflash.h" +#include "os/endian.h" +#include "base64/base64.h" +#include "crc/crc16.h" +#include "testutil/testutil.h" +#include "hal/hal_flash.h" +#include "flash_map_backend/flash_map_backend.h" +#include "bootutil/bootutil.h" + +#include "boot_serial_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void tx_msg(void *src, int len); + +#ifdef __cplusplus +} +#endif + +#endif /* _BOOT_TEST_H */ diff --git a/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_img_msg.c b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_img_msg.c new file mode 100644 index 0000000..a5e7174 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_img_msg.c @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "boot_test.h" + +TEST_CASE(boot_serial_empty_img_msg) +{ + char buf[sizeof(struct nmgr_hdr) + 32]; + struct nmgr_hdr *hdr; + + hdr = (struct nmgr_hdr *)buf; + memset(hdr, 0, sizeof(*hdr)); + hdr->nh_op = NMGR_OP_WRITE; + hdr->nh_group = htons(MGMT_GROUP_ID_IMAGE); + hdr->nh_id = IMGMGR_NMGR_ID_UPLOAD; + hdr->nh_len = htons(2); + strcpy((char *)(hdr + 1), "{}"); + + tx_msg(buf, sizeof(*hdr) + 2); +} diff --git a/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_msg.c b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_msg.c new file mode 100644 index 0000000..12b6457 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_empty_msg.c @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "boot_test.h" + +TEST_CASE(boot_serial_empty_msg) +{ + char buf[4]; + struct nmgr_hdr hdr; + + tx_msg(buf, 0); + + strcpy(buf, "--"); + tx_msg(buf, 2); + + memset(&hdr, 0, sizeof(hdr)); + tx_msg(&hdr, sizeof(hdr)); + + hdr.nh_op = NMGR_OP_WRITE; + + tx_msg(&hdr, sizeof(hdr)); +} diff --git a/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_img_msg.c b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_img_msg.c new file mode 100644 index 0000000..7aa9590 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_img_msg.c @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "boot_test.h" + +TEST_CASE(boot_serial_img_msg) +{ + char img[16]; + char enc_img[BASE64_ENCODE_SIZE(sizeof(img)) + 1]; + char buf[sizeof(struct nmgr_hdr) + sizeof(enc_img) + 32]; + int len; + int rc; + struct nmgr_hdr *hdr; + const struct flash_area *fap; + + /* 00000000 a3 64 64 61 74 61 58 10 |.ddataX.| + * 00000008 a5 a5 a5 a5 a5 a5 a5 a5 |........| + * 00000010 a5 a5 a5 a5 a5 a5 a5 a5 |........| + * 00000018 63 6c 65 6e 1a 00 01 14 |clen....| + * 00000020 e8 63 6f 66 66 00 |.coff.| + */ + static const uint8_t payload[] = { + 0xa3, 0x64, 0x64, 0x61, 0x74, 0x61, 0x58, 0x10, + /* 16 bytes of image data starts here. */ + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0x63, 0x6c, 0x65, 0x6e, 0x1a, 0x00, 0x01, 0x14, + 0xe8, 0x63, 0x6f, 0x66, 0x66, 0x00, + }; + + memset(img, 0xa5, sizeof(img)); + + hdr = (struct nmgr_hdr *)buf; + memset(hdr, 0, sizeof(*hdr)); + hdr->nh_op = NMGR_OP_WRITE; + hdr->nh_group = htons(MGMT_GROUP_ID_IMAGE); + hdr->nh_id = IMGMGR_NMGR_ID_UPLOAD; + + memcpy(hdr + 1, payload, sizeof payload); + hdr->nh_len = htons(sizeof payload); + + len = sizeof(*hdr) + sizeof payload; + tx_msg(buf, len); + + /* + * Validate contents inside the primary slot + */ + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &fap); + assert(rc == 0); + + rc = flash_area_read(fap, 0, enc_img, sizeof(img)); + assert(rc == 0); + assert(!memcmp(enc_img, img, sizeof(img))); +} diff --git a/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_setup.c b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_setup.c new file mode 100644 index 0000000..791e845 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_setup.c @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "boot_test.h" + +TEST_CASE(boot_serial_setup) +{ + +} diff --git a/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c new file mode 100644 index 0000000..d8b3185 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/src/testcases/boot_serial_upload_bigger_image.c @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "boot_test.h" +#include "zcbor_common.h" + +TEST_CASE(boot_serial_upload_bigger_image) +{ + char img[256]; + char enc_img[64]; + char buf[sizeof(struct nmgr_hdr) + 128]; + int len; + int off; + int rc; + struct nmgr_hdr *hdr; + const struct flash_area *fap; + int i; + + const int payload_off = sizeof *hdr; + const int img_data_off = payload_off + 8; + + /* 00000000 a3 64 64 61 74 61 58 20 |.ddataX.| + * 00000008 00 00 00 00 00 00 00 00 |........| + * 00000010 00 00 00 00 00 00 00 00 |........| + * 00000018 00 00 00 00 00 00 00 00 |........| + * 00000020 00 00 00 00 00 00 00 00 |........| + * 00000028 63 6c 65 6e 1a 00 01 14 |clen....| + * 00000030 e8 63 6f 66 66 00 |.coff.| + */ + static const uint8_t payload_first[] = { + 0xa3, 0x64, 0x64, 0x61, 0x74, 0x61, 0x58, 0x20, + /* 32 bytes of image data starts here. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x6c, 0x65, 0x6e, 0x1a, 0x00, 0x01, 0x14, + 0xe8, 0x63, 0x6f, 0x66, 0x66, 0x00, + }; + + /* 00000000 a3 64 64 61 74 61 58 20 |.ddataX.| + * 00000008 00 00 00 00 00 00 00 00 |........| + * 00000010 00 00 00 00 00 00 00 00 |........| + * 00000018 00 00 00 00 00 00 00 00 |........| + * 00000020 00 00 00 00 00 00 00 00 |........| + * 00000028 63 6f 66 66 00 00 |coff..| + */ + static const uint8_t payload_next[] = { + 0xa2, 0x64, 0x64, 0x61, 0x74, 0x61, 0x58, 0x20, + /* 32 bytes of image data starts here. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x6f, 0x66, 0x66, + /* 2 bytes of offset value starts here. */ + 0x00, 0x00 + }; + + for (i = 0; i < sizeof(img); i++) { + img[i] = i; + } + + for (off = 0; off < sizeof(img); off += 32) { + hdr = (struct nmgr_hdr *)buf; + memset(hdr, 0, sizeof(*hdr)); + hdr->nh_op = NMGR_OP_WRITE; + hdr->nh_group = htons(MGMT_GROUP_ID_IMAGE); + hdr->nh_id = IMGMGR_NMGR_ID_UPLOAD; + + if (off) { + memcpy(buf + payload_off, payload_next, sizeof payload_next); + len = sizeof payload_next; + buf[payload_off + len - 2] = ZCBOR_VALUE_IS_1_BYTE; + buf[payload_off + len - 1] = off; + } else { + memcpy(buf + payload_off, payload_first, sizeof payload_first); + len = sizeof payload_first; + } + memcpy(buf + img_data_off, img + off, 32); + hdr->nh_len = htons(len); + + len = sizeof(*hdr) + len; + + tx_msg(buf, len); + } + + /* + * Validate contents inside the primary slot + */ + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &fap); + assert(rc == 0); + + for (off = 0; off < sizeof(img); off += sizeof(enc_img)) { + rc = flash_area_read(fap, off, enc_img, sizeof(enc_img)); + assert(rc == 0); + assert(!memcmp(enc_img, &img[off], sizeof(enc_img))); + } +} diff --git a/bootloader/mcuboot/boot/boot_serial/test/syscfg.yml b/bootloader/mcuboot/boot/boot_serial/test/syscfg.yml new file mode 100644 index 0000000..ff841d6 --- /dev/null +++ b/bootloader/mcuboot/boot/boot_serial/test/syscfg.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Package: boot/boot_serial/test + +syscfg.vals: + # This is here to work around the $notnull syscfg restriction. + BOOT_SERIAL_DETECT_PIN: 0 diff --git a/bootloader/mcuboot/boot/bootutil/CMakeLists.txt b/bootloader/mcuboot/boot/bootutil/CMakeLists.txt new file mode 100644 index 0000000..9097706 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/CMakeLists.txt @@ -0,0 +1,35 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2020-2023, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +#------------------------------------------------------------------------------ + +add_library(bootutil STATIC) + +target_include_directories(bootutil + PUBLIC + include + PRIVATE + src +) + +target_sources(bootutil + PRIVATE + src/boot_record.c + src/bootutil_misc.c + src/bootutil_public.c + src/caps.c + src/encrypted.c + src/fault_injection_hardening.c + src/fault_injection_hardening_delay_rng_mbedtls.c + src/image_ecdsa.c + src/image_ed25519.c + src/image_rsa.c + src/image_validate.c + src/loader.c + src/swap_misc.c + src/swap_move.c + src/swap_scratch.c + src/tlv.c +) diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/bench.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/bench.h new file mode 100644 index 0000000..1e7195c --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/bench.h @@ -0,0 +1,68 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2019 Linaro Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_BOOTUTIL_BENCH_H__ +#define H_BOOTUTIL_BENCH_H__ + +#include "ignore.h" + +#ifdef MCUBOOT_USE_BENCH + +/* The platform-specific benchmark code should define a + * `bench_state_t` type that holds the information needed for the + * benchmark. This is generally something small, such as an integer + * holding the state. This should also define plat_bench_start and + * plat_bench_end, which likely have to be macros so that log messages + * come from the right place in the code. */ +#include + +/* + * These are simple barrier-type benchmarks. If a platform has + * benchmarks that are enabled, calling `boot_bench_start()` before a + * block of code and `boot_bench_stop()` after that block of code will + * present this information in some manner (usually through logging). + * The details of what is measured and how it is printed are specific + * to the platform and the implementation. A pointer to the + * platform-specific state should be passed in. + */ +#define boot_bench_start(_state) do { \ + plat_bench_start(_state); \ +} while (0) + +#define boot_bench_stop(_state) do { \ + plat_bench_stop(_state); \ +} while (0) + +#else /* not MCUBOOT_USE_BENCH */ + +/* The type needs to take space. As long as it remains unused, the C + * compiler should eliminate this value entirely. */ +typedef int bench_state_t; + +/* Without benchmarking enabled, these are just empty. */ +#define boot_bench_start(_state) do { \ + IGNORE(_state); \ +} while(0) + +#define boot_bench_stop(_state) do { \ + IGNORE(_state); \ +} while(0) + +#endif /* not MCUBOOT_USE_BENCH */ + +#endif /* not H_BOOTUTIL_BENCH_H__ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_hooks.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_hooks.h new file mode 100644 index 0000000..6d4a34e --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_hooks.h @@ -0,0 +1,181 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file + * @brief Hooks definition implementation API + * + * This file contains API interface definition for hooks which can be + * implemented to overide or to amend some of MCUboot's native routines. + */ + +#ifndef H_BOOTUTIL_HOOKS +#define H_BOOTUTIL_HOOKS + +#ifdef MCUBOOT_IMAGE_ACCESS_HOOKS + +#define BOOT_HOOK_CALL(f, ret_default, ...) f(__VA_ARGS__) + +#define BOOT_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ + do { \ + FIH_CALL(f, fih_rc, __VA_ARGS__); \ + } while(0); + +#else + +#define BOOT_HOOK_CALL(f, ret_default, ...) ret_default + +#define BOOT_HOOK_CALL_FIH(f, fih_ret_default, fih_rc, ...) \ + do { \ + fih_rc = fih_ret_default; \ + } while(0); + +#endif + +/** Hook for provide image header data. + * + * This Hook may be used to overide image header read implementation or doing + * a custom action before. + * + * @param img_index the index of the image pair + * @param slot slot number + * @param img_head image header structure to be populated + * + * @retval 0: header was read/populated, skip direct header data read + * BOOT_HOOK_REGULAR: follow the normal execution path, + * otherwise an error-code value. + */ +int boot_read_image_header_hook(int img_index, int slot, + struct image_header *img_head); + +/** Hook for Validate image hash/signature + * + * This Hook may be used to overide image validation procedure or doing + * a custom action before. + * + * @param img_index the index of the image pair + * @param slot slot number + * + * @retval FIH_SUCCESS: image is valid, skip direct validation + * FIH_FAILURE: image is invalid, skip direct validation + * FIH_BOOT_HOOK_REGULAR: follow the normal execution path. + */ +fih_ret boot_image_check_hook(int img_index, int slot); + +/** Hook for implement image update + * + * This hook is for for implementing an alternative mechanism of image update or + * doing a custom action before. + * + * @param img_index the index of the image pair + * @param img_head the image header of the secondary image + * @param area the flash area of the secondary image. + * + * @retval 0: update was done, skip performing the update + * BOOT_HOOK_REGULAR: follow the normal execution path, + * otherwise an error-code value. + */ +int boot_perform_update_hook(int img_index, struct image_header *img_head, + const struct flash_area *area); + +/** Hook for implement image's post copying action + * + * This hook is for implement action which might be done right after image was + * copied to the primary slot. This hook is called in MCUBOOT_OVERWRITE_ONLY + * mode only. + * + * @param img_index the index of the image pair + * @param area the flash area of the primary image. + * @param size size of copied image. + * + * @retval 0: success, mcuboot will follow normal code execution flow after + * execution of this call. + * non-zero: an error, mcuboot will return from + * boot_copy_image() with error. + * Update will be undone so might be resume on the next boot. + */ +int boot_copy_region_post_hook(int img_index, const struct flash_area *area, + size_t size); + +/** Hook for implement image's post recovery upload action + * + * This hook is for implement action which might be done right after image was + * copied to the primary slot. This hook is called in serial recovery upload + * operation. + * + * @param img_index the index of the image pair + * @param area the flash area of the primary image. + * @param size size of copied image. + * + * @retval 0: success, mcuboot will follow normal code execution flow after + * execution of this call. + * non-zero: an error, will be transferred as part of comand response + * as "rc" entry. + */ +int boot_serial_uploaded_hook(int img_index, const struct flash_area *area, + size_t size); + +/** Hook for implement the image's slot installation status fetch operation for + * the MGMT custom command. + * + * The image's slot installation status is custom property. It's detailed + * definition depends on user implementation. It is only defined that the status + * will be set to 0 if this hook not provides another value. + * + * @param img_index the index of the image pair + * @param slot slot number + * @param img_install_stat the image installation status to be populated + * + * @retval 0: the installaton status was fetched successfully, + * BOOT_HOOK_REGULAR: follow the normal execution path, status will be + * set to 0 + * otherwise an error-code value. Error-code is ignored, but it is up to + * the implementation to reflect this error in img_install_stat. + */ +int boot_img_install_stat_hook(int image_index, int slot, + int *img_install_stat); + +/** Hook will be invoked when boot_serial requests device reset. + * The hook may be used to prevent device reset. + * + * @param force set to true when request tries to force reset. + * + * @retval 0 when reset should be performed; + * BOOT_RESET_REQUEST_HOOK_BUSY when some processing is still in + * progress; + * BOOT_RESET_REQUEST_HOOK_TIMEOUT internal process timed out; + * BOOT_RESET_REQUEST_HOOK_CHECK_FAILED internal code failed to + * obtian status; + * BOOT_RESET_REQUEST_HOOK_INTERNAL_ERROR unspecified internal + * error while checking status. + */ +int boot_reset_request_hook(bool force); + +#define BOOT_RESET_REQUEST_HOOK_BUSY 1 +#define BOOT_RESET_REQUEST_HOOK_TIMEOUT 2 +#define BOOT_RESET_REQUEST_HOOK_CHECK_FAILED 3 +#define BOOT_RESET_REQUEST_HOOK_INTERNAL_ERROR 4 + +#endif /*H_BOOTUTIL_HOOKS*/ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_public_hooks.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_public_hooks.h new file mode 100644 index 0000000..453edf2 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_public_hooks.h @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file + * @brief Hooks definition implementation API + * + * This file contains API interface definition for hooks which can be + * implemented for overide some of MCUboot's native routines. + */ + +#ifndef H_BOOTUTIL_PUBLIC_HOOKS +#define H_BOOTUTIL_PUBLIC_HOOKS + +#include "bootutil/boot_hooks.h" + +/** Hook for provide primary image swap state. + * + * @param img_index the index of the image pair + * @param state image swap state structure to be populated + * + * @retval 0: header was read/populated + * FIH_FAILURE: image is invalid, + * BOOT_HOOK_REGULAR if hook not implemented for the image-slot, + * othervise an error-code value. + */ +int boot_read_swap_state_primary_slot_hook(int image_index, + struct boot_swap_state *state); + +#endif /*H_BOOTUTIL_PUBLIC_HOOKS*/ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_record.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_record.h new file mode 100644 index 0000000..14b2cd8 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_record.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2021 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BOOT_RECORD_H__ +#define __BOOT_RECORD_H__ + +#include +#include +#include "bootutil/image.h" +#include "bootutil/bootutil.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Error codes for using the shared memory area. */ +enum shared_memory_status { + SHARED_MEMORY_OK = 0, + SHARED_MEMORY_OVERFLOW, + SHARED_MEMORY_OVERWRITE, + SHARED_MEMORY_GEN_ERROR, + SHARED_MEMORY_WRITE_ERROR, + SHARED_MEMORY_READ_ERROR, +}; + +/** + * @brief Add a data item to the shared data area between bootloader and + * runtime SW + * + * @param[in] major_type TLV major type, identify consumer + * @param[in] minor_type TLV minor type, identify TLV type + * @param[in] size length of added data + * @param[in] data pointer to data + * + * @return 0 on success; nonzero on failure. + */ +int boot_add_data_to_shared_area(uint8_t major_type, + uint16_t minor_type, + size_t size, + const uint8_t *data); + +/** + * Add an image's all boot status information to the shared memory area + * between the bootloader and runtime SW. + * + * @param[in] sw_module Identifier of the SW component. + * @param[in] hdr Pointer to the image header stored in RAM. + * @param[in] fap Pointer to the flash area where image is stored. + * + * @return 0 on success; nonzero on failure. + */ +int boot_save_boot_status(uint8_t sw_module, + const struct image_header *hdr, + const struct flash_area *fap); + +/** + * Add application specific data to the shared memory area between the + * bootloader and runtime SW. + * + * @param[in] hdr Pointer to the image header stored in RAM. + * @param[in] fap Pointer to the flash area where image is stored. + * @param[in] slot The currently active slot being booted. + * @param[in] max_app_sizes The maximum sizes of images that can be loaded. + * + * @return 0 on success; nonzero on failure. + */ +int boot_save_shared_data(const struct image_header *hdr, + const struct flash_area *fap, + const uint8_t active_slot, + const struct image_max_size *max_app_sizes); + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOT_RECORD_H__ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_status.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_status.h new file mode 100644 index 0000000..ea3de5e --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/boot_status.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018-2020 Arm Limited + * Copyright (c) 2020 Linaro Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BOOT_STATUS_H__ +#define __BOOT_STATUS_H__ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The shared data between boot loader and runtime SW is TLV encoded. The + * shared data is stored in a well known location in memory and this is a + * contract between boot loader and runtime SW. + * + * The structure of shared data must be the following: + * - At the beginning there must be a header: struct shared_data_tlv_header + * This contains a magic number and a size field which covers the entire + * size of the shared data area including this header. + * - After the header there come the entries which are composed from an entry + * header structure: struct shared_data_tlv_entry and the data. In the entry + * header is a type field (tly_type) which identify the consumer of the + * entry in the runtime SW and specify the subtype of that data item. There + * is a size field (tlv_len) which covers the size of the the data. After + * this structure comes the actual data. + * + * - Arbitrary number and size of data entry can be in the shared memory area. + * + * This table gives of overview about the tlv_type field in the entry header. + * The tlv_type always composed from a major and minor number. Major number + * identifies the addressee in runtime SW, who should process the data entry. + * Minor number used to encode more info about the data entry. The actual + * definition of minor number could change per major number. + * + * In case of boot status data, which can be processed by an attestation + * service the minor number is split further to two part: sw_module and claim. + * The sw_module identifies the SW component in the system which the data item + * belongs to and the claim part identifies the exact type of the data. + * + * |---------------------------------------| + * | tlv_type (16) | + * |---------------------------------------| + * | tlv_major(4)| tlv_minor(12) | + * |---------------------------------------| + * | MAJOR_IAS | sw_module(6) | claim(6) | + * |---------------------------------------| + */ + +/* General macros to handle TLV type */ +#define MAJOR_MASK 0xF /* 4 bit */ +#define MAJOR_POS 12 /* 12 bit */ +#define MINOR_MASK 0xFFF /* 12 bit */ + +#define SET_TLV_TYPE(major, minor) \ + (((uint16_t)((major) & MAJOR_MASK) << MAJOR_POS) \ + | ((minor) & MINOR_MASK)) +#define GET_MAJOR(tlv_type) ((uint16_t)(tlv_type) >> MAJOR_POS) +#define GET_MINOR(tlv_type) ((tlv_type) & MINOR_MASK) + +/* Magic value which marks the beginning of shared data area in memory */ +#define SHARED_DATA_TLV_INFO_MAGIC 0x2016 + +/* Initial attestation specific macros */ + +/** + * Major numbers (4 bit) to identify the + * consumer of shared data in runtime SW. + */ +#define TLV_MAJOR_IAS 0x1 +#define TLV_MAJOR_BLINFO 0x2 + +/* Initial attestation: Claim per SW components / SW modules */ +/* Bits: 0-2 */ +#define SW_VERSION 0x00 +#define SW_SIGNER_ID 0x01 +/* Reserved 0x02 */ +#define SW_TYPE 0x03 +/* Bits: 3-5 */ +#define SW_MEASURE_VALUE 0x08 +#define SW_MEASURE_TYPE 0x09 +#define SW_BOOT_RECORD 0x3F + +#define MODULE_POS 6 /* 6 bit */ +#define CLAIM_MASK 0x3F /* 6 bit */ +#define MEASUREMENT_CLAIM_POS 3 /* 3 bit */ + +#define GET_IAS_MODULE(tlv_type) ((uint16_t)GET_MINOR(tlv_type) >> MODULE_POS) +#define GET_IAS_CLAIM(tlv_type) (GET_MINOR(tlv_type) & CLAIM_MASK) +#define SET_IAS_MINOR(sw_module, claim) \ + (((uint16_t)(sw_module) << MODULE_POS) | (claim)) + +/* Bootloader information */ +#define BLINFO_MODE 0x00 +#define BLINFO_SIGNATURE_TYPE 0x01 +#define BLINFO_RECOVERY 0x02 +#define BLINFO_RUNNING_SLOT 0x03 +#define BLINFO_BOOTLOADER_VERSION 0x04 +#define BLINFO_MAX_APPLICATION_SIZE 0x05 /* Same as BLINFO_MAX_APPLICATION_SIZE_IMAGE_0, legacy name */ +#define BLINFO_MAX_APPLICATION_SIZE_IMAGE_0 BLINFO_MAX_APPLICATION_SIZE +#define BLINFO_MAX_APPLICATION_SIZE_IMAGE_1 0x06 +#define BLINFO_MAX_APPLICATION_SIZE_IMAGE_2 0x07 +#define BLINFO_MAX_APPLICATION_SIZE_IMAGE_3 0x08 +#define BLINFO_MAX_APPLICATION_SIZE_IMAGE_4 0x09 + +enum mcuboot_mode { + MCUBOOT_MODE_SINGLE_SLOT, + MCUBOOT_MODE_SWAP_USING_SCRATCH, + MCUBOOT_MODE_UPGRADE_ONLY, + MCUBOOT_MODE_SWAP_USING_MOVE, + MCUBOOT_MODE_DIRECT_XIP, + MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT, + MCUBOOT_MODE_RAM_LOAD, + MCUBOOT_MODE_FIRMWARE_LOADER +}; + +enum mcuboot_signature_type { + MCUBOOT_SIGNATURE_TYPE_NONE, + MCUBOOT_SIGNATURE_TYPE_RSA, + MCUBOOT_SIGNATURE_TYPE_ECDSA_P256, + MCUBOOT_SIGNATURE_TYPE_ED25519 +}; + +enum mcuboot_recovery_mode { + MCUBOOT_RECOVERY_MODE_NONE, + MCUBOOT_RECOVERY_MODE_SERIAL_RECOVERY, + MCUBOOT_RECOVERY_MODE_DFU, +}; + +/** + * Shared data TLV header. All fields in little endian. + * + * ----------------------------------- + * | tlv_magic(16) | tlv_tot_len(16) | + * ----------------------------------- + */ +struct shared_data_tlv_header { + uint16_t tlv_magic; + uint16_t tlv_tot_len; /* size of whole TLV area (including this header) */ +}; + +#define SHARED_DATA_HEADER_SIZE sizeof(struct shared_data_tlv_header) + +/** + * Shared data TLV entry header format. All fields in little endian. + * + * ------------------------------- + * | tlv_type(16) | tlv_len(16) | + * ------------------------------- + * | Raw data | + * ------------------------------- + */ +struct shared_data_tlv_entry { + uint16_t tlv_type; + uint16_t tlv_len; /* TLV data length (not including this header). */ +}; + +#define SHARED_DATA_ENTRY_HEADER_SIZE sizeof(struct shared_data_tlv_entry) +#define SHARED_DATA_ENTRY_SIZE(size) (size + SHARED_DATA_ENTRY_HEADER_SIZE) + +/* Structure to store the boot data for the runtime SW. */ +struct shared_boot_data { + struct shared_data_tlv_header header; + uint8_t data[]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOT_STATUS_H__ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil.h new file mode 100644 index 0000000..070adaf --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil.h @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2021 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BOOTUTIL_ +#define H_BOOTUTIL_ + +#include +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/bootutil_public.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef MCUBOOT_IMAGE_NUMBER +#define BOOT_IMAGE_NUMBER MCUBOOT_IMAGE_NUMBER +#else +#define BOOT_IMAGE_NUMBER 1 +#endif + +_Static_assert(BOOT_IMAGE_NUMBER > 0, "Invalid value for BOOT_IMAGE_NUMBER"); + +struct image_header; +/** + * A response object provided by the boot loader code; indicates where to jump + * to execute the main image. + */ +struct boot_rsp { + /** A pointer to the header of the image to be executed. */ + const struct image_header *br_hdr; + + /** + * The flash offset of the image to execute. Indicates the position of + * the image header within its flash device. + */ + uint8_t br_flash_dev_id; + uint32_t br_image_off; +}; + +/* This is not actually used by mcuboot's code but can be used by apps + * when attempting to read/write a trailer. + */ +struct image_trailer { + uint8_t swap_type; + uint8_t pad1[BOOT_MAX_ALIGN - 1]; + uint8_t copy_done; + uint8_t pad2[BOOT_MAX_ALIGN - 1]; + uint8_t image_ok; + uint8_t pad3[BOOT_MAX_ALIGN - 1]; +#if BOOT_MAX_ALIGN > BOOT_MAGIC_SZ + uint8_t pad4[BOOT_MAGIC_ALIGN_SIZE - BOOT_MAGIC_SZ]; +#endif + uint8_t magic[BOOT_MAGIC_SZ]; +}; + +struct image_max_size { + bool calculated; + uint32_t max_size; +}; + +/* you must have pre-allocated all the entries within this structure */ +fih_ret boot_go(struct boot_rsp *rsp); +fih_ret boot_go_for_image_id(struct boot_rsp *rsp, uint32_t image_id); + +struct boot_loader_state; +void boot_state_clear(struct boot_loader_state *state); +fih_ret context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp); +const struct image_max_size *boot_get_max_app_size(void); + +#define SPLIT_GO_OK (0) +#define SPLIT_GO_NON_MATCHING (-1) +#define SPLIT_GO_ERR (-2) + +fih_ret split_go(int loader_slot, int split_slot, void **entry); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_log.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_log.h new file mode 100644 index 0000000..f2c70b9 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_log.h @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017 Linaro Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_BOOTUTIL_LOG_H_ +#define H_BOOTUTIL_LOG_H_ + +#include "ignore.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef MCUBOOT_HAVE_LOGGING +#include + +#define BOOT_LOG_ERR(...) MCUBOOT_LOG_ERR(__VA_ARGS__) +#define BOOT_LOG_WRN(...) MCUBOOT_LOG_WRN(__VA_ARGS__) +#define BOOT_LOG_INF(...) MCUBOOT_LOG_INF(__VA_ARGS__) +#define BOOT_LOG_DBG(...) MCUBOOT_LOG_DBG(__VA_ARGS__) +#define BOOT_LOG_SIM(...) MCUBOOT_LOG_SIM(__VA_ARGS__) + +#define BOOT_LOG_MODULE_DECLARE(module) MCUBOOT_LOG_MODULE_DECLARE(module) +#define BOOT_LOG_MODULE_REGISTER(module) MCUBOOT_LOG_MODULE_REGISTER(module) + +#else + +#define BOOT_LOG_ERR(...) IGNORE(__VA_ARGS__) +#define BOOT_LOG_WRN(...) IGNORE(__VA_ARGS__) +#define BOOT_LOG_INF(...) IGNORE(__VA_ARGS__) +#define BOOT_LOG_DBG(...) IGNORE(__VA_ARGS__) +#define BOOT_LOG_SIM(...) IGNORE(__VA_ARGS__) + +#define BOOT_LOG_MODULE_DECLARE(module) +#define BOOT_LOG_MODULE_REGISTER(module) + +#endif /* MCUBOOT_HAVE_LOGGING */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_public.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_public.h new file mode 100644 index 0000000..b2d5a5d --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_public.h @@ -0,0 +1,316 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2021 Arm Limited + * Copyright (c) 2020-2021 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file + * @brief Public MCUBoot interface API + * + * This file contains API which can be combined with the application in order + * to interact with the MCUBoot bootloader. This API are shared code-base betwen + * MCUBoot and the application which controls DFU process. + */ + +#ifndef H_BOOTUTIL_PUBLIC +#define H_BOOTUTIL_PUBLIC + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ALIGN_UP +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#endif + +#ifndef ALIGN_DOWN +#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) +#endif + +/** Attempt to boot the contents of the primary slot. */ +#define BOOT_SWAP_TYPE_NONE 1 + +/** + * Swap to the secondary slot. + * Absent a confirm command, revert back on next boot. + */ +#define BOOT_SWAP_TYPE_TEST 2 + +/** + * Swap to the secondary slot, + * and permanently switch to booting its contents. + */ +#define BOOT_SWAP_TYPE_PERM 3 + +/** Swap back to alternate slot. A confirm changes this state to NONE. */ +#define BOOT_SWAP_TYPE_REVERT 4 + +/** Swap failed because image to be run is not valid */ +#define BOOT_SWAP_TYPE_FAIL 5 + +/** Swapping encountered an unrecoverable error */ +#define BOOT_SWAP_TYPE_PANIC 0xff + +#define BOOT_MAGIC_SZ 16 + +#ifdef MCUBOOT_BOOT_MAX_ALIGN + +#if defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_SCRATCH) +_Static_assert(MCUBOOT_BOOT_MAX_ALIGN >= 8 && MCUBOOT_BOOT_MAX_ALIGN <= 32, + "Unsupported value for MCUBOOT_BOOT_MAX_ALIGN for SWAP upgrade modes"); +#endif + +#define BOOT_MAX_ALIGN MCUBOOT_BOOT_MAX_ALIGN +#define BOOT_MAGIC_ALIGN_SIZE ALIGN_UP(BOOT_MAGIC_SZ, BOOT_MAX_ALIGN) +#else +#define BOOT_MAX_ALIGN 8 +#define BOOT_MAGIC_ALIGN_SIZE BOOT_MAGIC_SZ +#endif + +#define BOOT_MAGIC_GOOD 1 +#define BOOT_MAGIC_BAD 2 +#define BOOT_MAGIC_UNSET 3 +#define BOOT_MAGIC_ANY 4 /* NOTE: control only, not dependent on sector */ +#define BOOT_MAGIC_NOTGOOD 5 /* NOTE: control only, not dependent on sector */ + +/* + * NOTE: leave BOOT_FLAG_SET equal to one, this is written to flash! + */ +#define BOOT_FLAG_SET 1 +#define BOOT_FLAG_BAD 2 +#define BOOT_FLAG_UNSET 3 +#define BOOT_FLAG_ANY 4 /* NOTE: control only, not dependent on sector */ + +#define BOOT_EFLASH 1 +#define BOOT_EFILE 2 +#define BOOT_EBADIMAGE 3 +#define BOOT_EBADVECT 4 +#define BOOT_EBADSTATUS 5 +#define BOOT_ENOMEM 6 +#define BOOT_EBADARGS 7 +#define BOOT_EBADVERSION 8 +#define BOOT_EFLASH_SEC 9 + +#define BOOT_HOOK_REGULAR 1 +/* + * Extract the swap type and image number from image trailers's swap_info + * filed. + */ +#define BOOT_GET_SWAP_TYPE(swap_info) ((swap_info) & 0x0F) +#define BOOT_GET_IMAGE_NUM(swap_info) ((swap_info) >> 4) + +/* Construct the swap_info field from swap type and image number */ +#define BOOT_SET_SWAP_INFO(swap_info, image, type) { \ + assert((image) < 0xF); \ + assert((type) < 0xF); \ + (swap_info) = (image) << 4 \ + | (type); \ + } +#ifdef MCUBOOT_HAVE_ASSERT_H +#include "mcuboot_config/mcuboot_assert.h" +#else +#include +#ifndef ASSERT +#define ASSERT assert +#endif +#endif + +struct boot_swap_state { + uint8_t magic; /* One of the BOOT_MAGIC_[...] values. */ + uint8_t swap_type; /* One of the BOOT_SWAP_TYPE_[...] values. */ + uint8_t copy_done; /* One of the BOOT_FLAG_[...] values. */ + uint8_t image_ok; /* One of the BOOT_FLAG_[...] values. */ + uint8_t image_num; /* Boot status belongs to this image */ +}; + +/** + * @brief Determines the action, if any, that mcuboot will take on a image pair. + * + * @param image_index Image pair index. + * + * @return a BOOT_SWAP_TYPE_[...] constant on success, negative errno code on + * fail. + */ +int boot_swap_type_multi(int image_index); + +/** + * @brief Determines the action, if any, that mcuboot will take. + * + * Works the same as a boot_swap_type_multi(0) call; + * + * @return a BOOT_SWAP_TYPE_[...] constant on success, negative errno code on + * fail. + */ +int boot_swap_type(void); + +/** + * Marks the image with the given index in the secondary slot as pending. On the + * next reboot, the system will perform a one-time boot of the the secondary + * slot image. + * + * @param image_index Image pair index. + * + * @param permanent Whether the image should be used permanently or + * only tested once: + * 0=run image once, then confirm or revert. + * 1=run image forever. + * + * @return 0 on success; nonzero on failure. + */ +int boot_set_pending_multi(int image_index, int permanent); + +/** + * Marks the image with index 0 in the secondary slot as pending. On the next + * reboot, the system will perform a one-time boot of the the secondary slot + * image. Note that this API is kept for compatibility. The + * boot_set_pending_multi() API is recommended. + * + * @param permanent Whether the image should be used permanently or + * only tested once: + * 0=run image once, then confirm or revert. + * 1=run image forever. + * + * @return 0 on success; nonzero on failure. + */ +int boot_set_pending(int permanent); + +/** + * Marks the image with the given index in the primary slot as confirmed. The + * system will continue booting into the image in the primary slot until told to + * boot from a different slot. + * + * @param image_index Image pair index. + * + * @return 0 on success; nonzero on failure. + */ +int boot_set_confirmed_multi(int image_index); + +/** + * Marks the image with index 0 in the primary slot as confirmed. The system + * will continue booting into the image in the primary slot until told to boot + * from a different slot. Note that this API is kept for compatibility. The + * boot_set_confirmed_multi() API is recommended. + * + * @return 0 on success; nonzero on failure. + */ +int boot_set_confirmed(void); + +/** + * @brief Get offset of the swap info field in the image trailer. + * + * @param fap Flash are for which offset is determined. + * + * @retval offset of the swap info field. + */ +uint32_t boot_swap_info_off(const struct flash_area *fap); + +/** + * @brief Get value of image-ok flag of the image. + * + * If called from chin-loaded image the image-ok flag value can be used to check + * whether application itself is already confirmed. + * + * @param fap Flash area of the image. + * @param image_ok[out] image-ok value. + * + * @return 0 on success; nonzero on failure. + */ +int boot_read_image_ok(const struct flash_area *fap, uint8_t *image_ok); + +/** + * @brief Read the image swap state + * + * @param flash_area_id id of flash partition from which state will be read; + * @param state pointer to structure for storing swap state. + * + * @return 0 on success; non-zero error code on failure; + */ +int +boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state); + +/** + * @brief Read the image swap state + * + * @param fa pointer to flash_area object; + * @param state pointer to structure for storing swap state. + * + * @return 0 on success; non-zero error code on failure. + */ +int +boot_read_swap_state(const struct flash_area *fa, + struct boot_swap_state *state); + +/** + * @brief Set next image application slot by flash area pointer + * + * @param fa pointer to flash_area representing image to set for next boot; + * @param active should be true if @fa points to currently running image + * slot, false otherwise; + * @param confirm confirms image; when @p active is true, this is considered + * true, regardless of passed value. + * + * It is users responsibility to identify whether @p fa provided as parameter + * is currently running/active image and provide proper value to @p active. + * Failing to do so may render device non-upgradeable. + * + * Note that in multi-image setup running/active application is the one + * that is currently being executed by any MCU core, from the pair of + * slots dedicated to that MCU core. As confirming application currently + * running on a given slot should be, preferably, done after functional + * tests prove application to function correctly, it may not be a good idea + * to cross-confirm running images. + * An application should only confirm slots designated to MCU core it is + * running on. + * + * @return 0 on success; non-zero error code on failure. + */ +int +boot_set_next(const struct flash_area *fa, bool active, bool confirm); + +/** + * Attempts to load image header from flash; verifies flash header fields. + * + * @param[in] fa_p flash area pointer + * @param[out] hdr buffer for image header + * + * @return 0 on success, error code otherwise + */ +int +boot_image_load_header(const struct flash_area *fa_p, + struct image_header *hdr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_test.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_test.h new file mode 100644 index 0000000..f48bf01 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/bootutil_test.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BOOTUTIL_TEST_ +#define H_BOOTUTIL_TEST_ + +#ifdef __cplusplus +extern "C" { +#endif + +int boot_test_all(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/caps.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/caps.h new file mode 100644 index 0000000..f4ff373 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/caps.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Linaro Limited + * Copyright (c) 2021-2023 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_BOOTUTIL_CAPS_H_ +#define H_BOOTUTIL_CAPS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The bootloader can be compile with different capabilities selected + * at compile time. This function provides runtime access to these + * capabilities. This is intended primarily for testing, although + * these will possibly be available at runtime to the application + * running within the bootloader. + */ +uint32_t bootutil_get_caps(void); + +#define BOOTUTIL_CAP_RSA2048 (1<<0) + /* reserved (1<<1) */ +#define BOOTUTIL_CAP_ECDSA_P256 (1<<2) +#define BOOTUTIL_CAP_SWAP_USING_SCRATCH (1<<3) +#define BOOTUTIL_CAP_OVERWRITE_UPGRADE (1<<4) +#define BOOTUTIL_CAP_ENC_RSA (1<<5) +#define BOOTUTIL_CAP_ENC_KW (1<<6) +#define BOOTUTIL_CAP_VALIDATE_PRIMARY_SLOT (1<<7) +#define BOOTUTIL_CAP_RSA3072 (1<<8) +#define BOOTUTIL_CAP_ED25519 (1<<9) +#define BOOTUTIL_CAP_ENC_EC256 (1<<10) +#define BOOTUTIL_CAP_SWAP_USING_MOVE (1<<11) +#define BOOTUTIL_CAP_DOWNGRADE_PREVENTION (1<<12) +#define BOOTUTIL_CAP_ENC_X25519 (1<<13) +#define BOOTUTIL_CAP_BOOTSTRAP (1<<14) +#define BOOTUTIL_CAP_AES256 (1<<15) +#define BOOTUTIL_CAP_RAM_LOAD (1<<16) +#define BOOTUTIL_CAP_DIRECT_XIP (1<<17) +#define BOOTUTIL_CAP_HW_ROLLBACK_PROT (1<<18) +#define BOOTUTIL_CAP_ECDSA_P384 (1<<19) + +/* + * Query the number of images this bootloader is configured for. This + * is also primarily used for testing. + */ +uint32_t bootutil_get_num_images(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_ctr.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_ctr.h new file mode 100644 index 0000000..4419036 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_ctr.h @@ -0,0 +1,158 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_AES_CTR_H_ +#define __BOOTUTIL_CRYPTO_AES_CTR_H_ + +#include + +#include "mcuboot_config/mcuboot_config.h" + +#if (defined(MCUBOOT_USE_MBED_TLS) + \ + defined(MCUBOOT_USE_TINYCRYPT) + defined(MCUBOOT_USE_PSA_CRYPTO)) != 1 + #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT or PSA" +#endif + +#if defined(MCUBOOT_USE_MBED_TLS) + #include + #include "bootutil/enc_key_public.h" + #define BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE BOOT_ENC_KEY_SIZE + #define BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE (16) +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) + #if defined(MCUBOOT_AES_256) + #error "Cannot use AES-256 for encryption with Tinycrypt library." + #endif + #include + #include + #include + #include + #define BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE TC_AES_KEY_SIZE + #define BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE TC_AES_BLOCK_SIZE +#endif /* MCUBOOT_USE_TINYCRYPT */ + + +#if defined(MCUBOOT_USE_PSA_CRYPTO) + #include + #include "bootutil/enc_key_public.h" + #define BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE BOOT_ENC_KEY_SIZE + #define BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE (16) +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MCUBOOT_USE_PSA_CRYPTO) +typedef struct { + /* Fixme: This should not be, here, psa_key_id should be passed */ + uint8_t key[BOOT_ENC_KEY_SIZE]; +} bootutil_aes_ctr_context; + +void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx); + +static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + memset(ctx, 0, sizeof(ctx)); +} + +static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + memcpy(ctx->key, k, sizeof(ctx->key)); + + return 0; +} + +int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c); +int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m); +#endif + +#if defined(MCUBOOT_USE_MBED_TLS) +typedef mbedtls_aes_context bootutil_aes_ctr_context; +static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) +{ + (void)mbedtls_aes_init(ctx); +} + +static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + mbedtls_aes_free(ctx); +} + +static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + return mbedtls_aes_setkey_enc(ctx, k, BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE * 8); +} + +static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c) +{ + uint8_t stream_block[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE]; + return mbedtls_aes_crypt_ctr(ctx, mlen, &blk_off, counter, stream_block, m, c); +} + +static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m) +{ + uint8_t stream_block[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE]; + return mbedtls_aes_crypt_ctr(ctx, clen, &blk_off, counter, stream_block, c, m); +} +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) +typedef struct tc_aes_key_sched_struct bootutil_aes_ctr_context; +static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + int rc; + rc = tc_aes128_set_encrypt_key(ctx, k); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +static int _bootutil_aes_ctr_crypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *in, uint32_t inlen, uint32_t blk_off, uint8_t *out) +{ + int rc; + rc = tc_ctr_mode(out, inlen, in, inlen, counter, &blk_off, ctx); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, uint32_t blk_off, uint8_t *c) +{ + return _bootutil_aes_ctr_crypt(ctx, counter, m, mlen, blk_off, c); +} + +static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, uint32_t blk_off, uint8_t *m) +{ + return _bootutil_aes_ctr_crypt(ctx, counter, c, clen, blk_off, m); +} +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_AES_CTR_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_kw.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_kw.h new file mode 100644 index 0000000..34045c2 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/aes_kw.h @@ -0,0 +1,142 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_AES_KW_H_ +#define __BOOTUTIL_CRYPTO_AES_KW_H_ + +#include "mcuboot_config/mcuboot_config.h" + +#if (defined(MCUBOOT_USE_MBED_TLS) + \ + defined(MCUBOOT_USE_TINYCRYPT)) != 1 + #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT" +#endif + +#if defined(MCUBOOT_USE_MBED_TLS) + #include + #include +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) + #if defined(MCUBOOT_AES_256) + #error "Cannot use AES-256 for encryption with Tinycrypt library." + #endif + #include + #include +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MCUBOOT_USE_MBED_TLS) +typedef mbedtls_nist_kw_context bootutil_aes_kw_context; +static inline void bootutil_aes_kw_init(bootutil_aes_kw_context *ctx) +{ + (void)mbedtls_nist_kw_init(ctx); +} + +static inline void bootutil_aes_kw_drop(bootutil_aes_kw_context *ctx) +{ + mbedtls_nist_kw_free(ctx); +} + +static inline int bootutil_aes_kw_set_unwrap_key(bootutil_aes_kw_context *ctx, const uint8_t *k, uint32_t klen) +{ + return mbedtls_nist_kw_setkey(ctx, MBEDTLS_CIPHER_ID_AES, k, klen * 8, 0); +} + +static inline int bootutil_aes_kw_unwrap(bootutil_aes_kw_context *ctx, const uint8_t *wrapped_key, uint32_t wrapped_key_len, uint8_t *key, uint32_t key_len) +{ + size_t olen; + return mbedtls_nist_kw_unwrap(ctx, MBEDTLS_KW_MODE_KW, wrapped_key, wrapped_key_len, key, &olen, key_len); +} +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) +typedef struct tc_aes_key_sched_struct bootutil_aes_kw_context; +static inline void bootutil_aes_kw_init(bootutil_aes_kw_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_aes_kw_drop(bootutil_aes_kw_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_aes_kw_set_unwrap_key(bootutil_aes_kw_context *ctx, const uint8_t *k, uint32_t klen) +{ + int rc; + + if (klen != 16) { + return -1; + } + + rc = tc_aes128_set_decrypt_key(ctx, k); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +/* + * Implements AES key unwrapping following RFC-3394 section 2.2.2, using + * tinycrypt for AES-128 decryption. + */ +static int bootutil_aes_kw_unwrap(bootutil_aes_kw_context *ctx, const uint8_t *wrapped_key, uint32_t wrapped_key_len, uint8_t *key, uint32_t key_len) +{ + uint8_t A[8]; + uint8_t B[16]; + int8_t i, j, k; + + if (wrapped_key_len != 24 || key_len != 16) { + return -1; + } + + for (k = 0; k < 8; k++) { + A[k] = wrapped_key[k]; + key[k] = wrapped_key[8 + k]; + key[8 + k] = wrapped_key[16 + k]; + } + + for (j = 5; j >= 0; j--) { + for (i = 2; i > 0; i--) { + for (k = 0; k < 8; k++) { + B[k] = A[k]; + B[8 + k] = key[((i-1) * 8) + k]; + } + B[7] ^= 2 * j + i; + if (tc_aes_decrypt((uint8_t *)&B, (uint8_t *)&B, ctx) != TC_CRYPTO_SUCCESS) { + return -1; + } + for (k = 0; k < 8; k++) { + A[k] = B[k]; + key[((i-1) * 8) + k] = B[8 + k]; + } + } + } + + for (i = 0, k = 0; i < 8; i++) { + k |= A[i] ^ 0xa6; + } + if (k) { + return -1; + } + + return 0; +} +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_AES_KW_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/common.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/common.h new file mode 100644 index 0000000..1fb5c58 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/common.h @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2021 Arm Limited + */ + +#ifndef __BOOTUTIL_CRYPTO_COMMON_H__ +#define __BOOTUTIL_CRYPTO_COMMON_H__ + +/* The check below can be performed even for those cases + * where MCUBOOT_USE_MBED_TLS has not been defined + */ +#include "mbedtls/version.h" +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#define MBEDTLS_CONTEXT_MEMBER(X) MBEDTLS_PRIVATE(X) +#else +#define MBEDTLS_CONTEXT_MEMBER(X) X +#endif + +/* Newer versions of Mbed TLS have removed the private accessor requirement for + * the ASN1 fields. + */ +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) && (MBEDTLS_VERSION_NUMBER < 0x03010000) +#define ASN1_CONTEXT_MEMBER(X) MBEDTLS_PRIVATE(X) +#else +#define ASN1_CONTEXT_MEMBER(X) X +#endif + +#endif /* __BOOTUTIL_CRYPTO_COMMON_H__ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_p256.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_p256.h new file mode 100644 index 0000000..962535c --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_p256.h @@ -0,0 +1,154 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_ECDH_P256_H_ +#define __BOOTUTIL_CRYPTO_ECDH_P256_H_ + +#include "mcuboot_config/mcuboot_config.h" + +#if (defined(MCUBOOT_USE_MBED_TLS) + \ + defined(MCUBOOT_USE_TINYCRYPT)) != 1 + #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT" +#endif + +#if defined(MCUBOOT_USE_MBED_TLS) + #include + #include + #define EC256_PUBK_LEN (65) +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) + #include + #include + #define BOOTUTIL_CRYPTO_ECDH_P256_HASH_SIZE (4 * 8) +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MCUBOOT_USE_TINYCRYPT) +typedef uintptr_t bootutil_ecdh_p256_context; +static inline void bootutil_ecdh_p256_init(bootutil_ecdh_p256_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_ecdh_p256_drop(bootutil_ecdh_p256_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_ecdh_p256_shared_secret(bootutil_ecdh_p256_context *ctx, const uint8_t *pk, const uint8_t *sk, uint8_t *z) +{ + int rc; + (void)ctx; + + if (pk[0] != 0x04) { + return -1; + } + + rc = uECC_valid_public_key(&pk[1], uECC_secp256r1()); + if (rc != 0) { + return -1; + } + + rc = uECC_shared_secret(&pk[1], sk, z, uECC_secp256r1()); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#if defined(MCUBOOT_USE_MBED_TLS) +#define NUM_ECC_BYTES 32 + +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +static int fake_rng(void *p_rng, unsigned char *output, size_t len); +#endif + +typedef struct bootutil_ecdh_p256_context { + mbedtls_ecp_group grp; + mbedtls_ecp_point P; + mbedtls_mpi z; + mbedtls_mpi d; +} bootutil_ecdh_p256_context; + +static inline void bootutil_ecdh_p256_init(bootutil_ecdh_p256_context *ctx) +{ + mbedtls_mpi_init(&ctx->z); + mbedtls_mpi_init(&ctx->d); + + mbedtls_ecp_group_init(&ctx->grp); + mbedtls_ecp_point_init(&ctx->P); + + if (mbedtls_ecp_group_load(&ctx->grp, MBEDTLS_ECP_DP_SECP256R1) != 0) { + mbedtls_ecp_group_free(&ctx->grp); + mbedtls_ecp_point_free(&ctx->P); + } +} + +static inline void bootutil_ecdh_p256_drop(bootutil_ecdh_p256_context *ctx) +{ + mbedtls_mpi_free(&ctx->d); + mbedtls_mpi_free(&ctx->z); + mbedtls_ecp_group_free(&ctx->grp); + mbedtls_ecp_point_free(&ctx->P); +} + +static inline int bootutil_ecdh_p256_shared_secret(bootutil_ecdh_p256_context *ctx, const uint8_t *pk, const uint8_t *sk, uint8_t *z) +{ + int rc; + + rc = mbedtls_ecp_point_read_binary(&ctx->grp, + &ctx->P, + pk, + EC256_PUBK_LEN); + if (rc != 0) { + mbedtls_ecp_group_free(&ctx->grp); + mbedtls_ecp_point_free(&ctx->P); + return -1; + } + + rc = mbedtls_ecp_check_pubkey(&ctx->grp, &ctx->P); + if (rc != 0) { + mbedtls_ecp_group_free(&ctx->grp); + mbedtls_ecp_point_free(&ctx->P); + return -1; + } + + mbedtls_mpi_read_binary(&ctx->d, sk, NUM_ECC_BYTES); + +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + rc = mbedtls_ecdh_compute_shared(&ctx->grp, + &ctx->z, + &ctx->P, + &ctx->d, + fake_rng, + NULL); +#else + rc = mbedtls_ecdh_compute_shared(&ctx->grp, + &ctx->z, + &ctx->P, + &ctx->d, + NULL, + NULL); +#endif + mbedtls_mpi_write_binary(&ctx->z, z, NUM_ECC_BYTES); + + return rc; +} +#endif /* MCUBOOT_USE_MBED_TLS */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_ECDH_P256_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_x25519.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_x25519.h new file mode 100644 index 0000000..1d11b64 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdh_x25519.h @@ -0,0 +1,57 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_ECDH_X25519_H_ +#define __BOOTUTIL_CRYPTO_ECDH_X25519_H_ + +#include "mcuboot_config/mcuboot_config.h" + +#if (defined(MCUBOOT_USE_MBED_TLS) + \ + defined(MCUBOOT_USE_TINYCRYPT)) != 1 + #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MCUBOOT_USE_TINYCRYPT) || defined(MCUBOOT_USE_MBED_TLS) +extern int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32], + const uint8_t peer_public_value[32]); + +typedef uintptr_t bootutil_ecdh_x25519_context; +static inline void bootutil_ecdh_x25519_init(bootutil_ecdh_x25519_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_ecdh_x25519_drop(bootutil_ecdh_x25519_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_ecdh_x25519_shared_secret(bootutil_ecdh_x25519_context *ctx, const uint8_t *pk, const uint8_t *sk, uint8_t *z) +{ + int rc; + (void)ctx; + + rc = X25519(z, sk, pk); + if (rc != 0) { + return -1; + } + + return 0; +} +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_ECDH_X25519_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdsa.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdsa.h new file mode 100644 index 0000000..85355f2 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/ecdsa.h @@ -0,0 +1,672 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023-2024 Arm Limited + */ + +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, the choices are: MCUBOOT_USE_TINYCRYPT, MCUBOOT_USE_CC310, + * MCUBOOT_USE_MBED_TLS, MCUBOOT_USE_PSA_CRYPTO. Note that support for + * MCUBOOT_USE_PSA_CRYPTO is still experimental and it might not support all + * the crypto abstractions that MCUBOOT_USE_MBED_TLS supports. For this + * reason, it's allowed to have both of them defined, and for crypto modules + * that support both abstractions, the MCUBOOT_USE_PSA_CRYPTO will take + * precedence. + */ + +#ifndef __BOOTUTIL_CRYPTO_ECDSA_H_ +#define __BOOTUTIL_CRYPTO_ECDSA_H_ + +#include +#include "mcuboot_config/mcuboot_config.h" + +#if defined(MCUBOOT_USE_PSA_CRYPTO) || defined(MCUBOOT_USE_MBED_TLS) +#define MCUBOOT_USE_PSA_OR_MBED_TLS +#endif /* MCUBOOT_USE_PSA_CRYPTO || MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_SIGN_EC384) && \ + !defined(MCUBOOT_USE_PSA_CRYPTO) + #error "P384 requires PSA_CRYPTO to be defined" +#endif + +#if (defined(MCUBOOT_USE_TINYCRYPT) + \ + defined(MCUBOOT_USE_CC310) + \ + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ + defined(MCUBOOT_USE_PSA_OR_MBED_TLS)) != 1 + #error "One crypto backend must be defined: either CC310/TINYCRYPT/MBED_TLS/PSA_CRYPTO" +#endif + +#if defined(MCUBOOT_USE_TINYCRYPT) + #include + #include +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#if defined(MCUBOOT_USE_CC310) + #include +#endif /* MCUBOOT_USE_CC310 */ + +#if defined(MCUBOOT_USE_PSA_CRYPTO) + #include + #include +#elif defined(MCUBOOT_USE_MBED_TLS) + #include + /* Indicate to the caller that the verify function needs the raw ASN.1 + * signature, not a decoded one. */ + #define MCUBOOT_ECDSA_NEED_ASN1_SIG +#endif /* MCUBOOT_USE_MBED_TLS */ + +/*TODO: remove this after cypress port mbedtls to abstract crypto api */ +#if defined(MCUBOOT_USE_CC310) || defined(MCUBOOT_USE_MBED_TLS) +#define NUM_ECC_BYTES (256 / 8) +#endif + +/* Universal defines */ +#define BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE (32) + +#include "mbedtls/oid.h" +#include "mbedtls/asn1.h" +#include "bootutil/sign_key.h" +#include "common.h" + +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + #include + #define NUM_ECC_BYTES (256 / 8) +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(MCUBOOT_USE_TINYCRYPT) || defined(MCUBOOT_USE_MBED_TLS) || \ + defined(MCUBOOT_USE_CC310) || defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO)) \ + && !defined(MCUBOOT_USE_PSA_CRYPTO) +/* + * Declaring these like this adds NULL termination. + */ +static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED; +static const uint8_t ec_secp256r1_oid[] = MBEDTLS_OID_EC_GRP_SECP256R1; + +/* + * Parse a public key. Helper function. + */ +static int bootutil_import_key(uint8_t **cp, uint8_t *end) +{ + size_t len; + mbedtls_asn1_buf alg; + mbedtls_asn1_buf param; + + if (mbedtls_asn1_get_tag(cp, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return -1; + } + end = *cp + len; + + /* ECParameters (RFC5480) */ + if (mbedtls_asn1_get_alg(cp, end, &alg, ¶m)) { + return -2; + } + /* id-ecPublicKey (RFC5480) */ + if (alg.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_pubkey_oid) - 1 || + memcmp(alg.ASN1_CONTEXT_MEMBER(p), ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) { + return -3; + } + /* namedCurve (RFC5480) */ + if (param.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_secp256r1_oid) - 1 || + memcmp(param.ASN1_CONTEXT_MEMBER(p), ec_secp256r1_oid, sizeof(ec_secp256r1_oid) - 1)) { + return -4; + } + /* ECPoint (RFC5480) */ + if (mbedtls_asn1_get_bitstring_null(cp, end, &len)) { + return -6; + } + if (*cp + len != end) { + return -7; + } + + if (len != 2 * NUM_ECC_BYTES + 1) { + return -8; + } + + return 0; +} +#endif /* (MCUBOOT_USE_TINYCRYPT || MCUBOOT_USE_MBED_TLS || MCUBOOT_USE_CC310) && !MCUBOOT_USE_PSA_CRYPTO */ + +/* + * cp points to ASN1 string containing an integer. + * Verify the tag, and that the length is 32 bytes. Helper function. + */ +static int bootutil_read_bigint(uint8_t i[NUM_ECC_BYTES], uint8_t **cp, uint8_t *end) +{ + size_t len; + + if (mbedtls_asn1_get_tag(cp, end, &len, MBEDTLS_ASN1_INTEGER)) { + return -3; + } + + if (len >= NUM_ECC_BYTES) { + memcpy(i, *cp + len - NUM_ECC_BYTES, NUM_ECC_BYTES); + } else { + memset(i, 0, NUM_ECC_BYTES - len); + memcpy(i + NUM_ECC_BYTES - len, *cp, len); + } + *cp += len; + return 0; +} + +/* + * Read in signature. Signature has r and s encoded as integers. Helper function. + */ +static int bootutil_decode_sig(uint8_t signature[NUM_ECC_BYTES * 2], uint8_t *cp, uint8_t *end) +{ + int rc; + size_t len; + + rc = mbedtls_asn1_get_tag(&cp, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (rc) { + return -1; + } + if (cp + len > end) { + return -2; + } + + rc = bootutil_read_bigint(signature, &cp, end); + if (rc) { + return -3; + } + rc = bootutil_read_bigint(signature + NUM_ECC_BYTES, &cp, end); + if (rc) { + return -4; + } + return 0; +} + +#if defined(MCUBOOT_USE_TINYCRYPT) +typedef uintptr_t bootutil_ecdsa_context; +static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_ecdsa_drop(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t sig_len) +{ + int rc; + (void)ctx; + (void)pk_len; + (void)sig_len; + (void)hash_len; + + uint8_t signature[2 * NUM_ECC_BYTES]; + rc = bootutil_decode_sig(signature, sig, sig + sig_len); + if (rc) { + return -1; + } + + /* Only support uncompressed keys. */ + if (pk[0] != 0x04) { + return -1; + } + pk++; + + rc = uECC_verify(pk, hash, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE, signature, uECC_secp256r1()); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, + uint8_t **cp,uint8_t *end) +{ + (void)ctx; + return bootutil_import_key(cp, end); +} +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#if defined(MCUBOOT_USE_CC310) +typedef uintptr_t bootutil_ecdsa_context; +static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_ecdsa_drop(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t sig_len) +{ + (void)ctx; + (void)pk_len; + (void)hash_len; + uint8_t dsig[2 * NUM_ECC_BYTES]; + + if (bootutil_decode_sig(dsig, sig, sig + sig_len)) { + return -1; + } + + /* Only support uncompressed keys. */ + if (pk[0] != 0x04) { + return -1; + } + pk++; + + return cc310_ecdsa_verify_secp256r1(hash, pk, dsig, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE); +} + +static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, + uint8_t **cp,uint8_t *end) +{ + (void)ctx; + return bootutil_import_key(cp, end); +} +#endif /* MCUBOOT_USE_CC310 */ + +#if defined(MCUBOOT_USE_PSA_CRYPTO) +typedef struct { + psa_key_id_t key_id; + size_t curve_byte_count; + psa_algorithm_t required_algorithm; +} bootutil_ecdsa_context; + +/* ECDSA public key with format specified in RFC5280 et al. in ASN.1 syntax + * + * SEQUENCE { + * SEQUENCE { + * OBJECT idEcPublicKey + * OBJECT namedCurve + * } + * BIT STRING publicKey + * } + * + * ECDSA signature format specified in RFC3279 et al. in ASN.1 syntax + * + * SEQUENCE { + * INTEGER r + * INTEGER s + * } + * + */ + +/* Offset in bytes from the start of the encoding to the length field + * of the innermost SEQUENCE in the ECDSA public key + */ +#define PUB_KEY_LEN_OFF (3) + +/* Offset in bytes from the start of the publicKey encoding of the BIT STRING */ +#define PUB_KEY_VAL_OFF (3) + +/* Computes the pointer to the idEcPublicKey OID from the base of the encoding */ +#define PUB_KEY_OID_OFFSET(p) (*p + PUB_KEY_LEN_OFF+1) + +/* Computes the pointer to the namedCurve OID from the base of the encoding */ +#define CURVE_TYPE_OID_OFFSET(p) PUB_KEY_OID_OFFSET(p) + sizeof(IdEcPublicKey) + +/* This helper function gets a pointer to the bitstring associated to the publicKey + * as encoded per RFC 5280. This function assumes that the public key encoding is not + * bigger than 127 bytes (i.e. usually up until 384 bit curves) + * + * \param[in,out] p Double pointer to a buffer containing the RFC 5280 of the ECDSA public key. + * On output, the pointer is updated to point to the start of the public key + * in BIT STRING form. + * \param[out] size Pointer to a buffer containing the size of the public key extracted + * + */ +static inline void get_public_key_from_rfc5280_encoding(uint8_t **p, size_t *size) +{ + uint8_t *key_start = (*p) + (PUB_KEY_LEN_OFF + 1 + (*p)[PUB_KEY_LEN_OFF] + PUB_KEY_VAL_OFF); + *p = key_start; + *size = key_start[-2]-1; /* -2 from PUB_KEY_VAL_OFF to get the length, -1 to remove the ASN.1 padding byte count */ +} + +/* This helper function parses a signature as specified in RFC3279 into a pair + * (r,s) of contiguous bytes + * + * \param[in] sig Pointer to a buffer containing the encoded signature + * \param[in] num_of_curve_bytes The required number of bytes for r and s + * \param[out] r_s_pair Buffer containing the (r,s) pair extracted. It's caller + * responsibility to ensure the buffer is big enough to + * hold the parsed (r,s) pair. + */ +static void parse_signature_from_rfc5480_encoding(const uint8_t *sig, + size_t num_of_curve_bytes, + uint8_t *r_s_pair) +{ + const uint8_t *sig_ptr = NULL; + + /* r or s can be greater than the expected size by one, due to the way + * ASN.1 encodes signed integers. If either r or s starts with a bit 1, + * a zero byte will be added in front of the encoding + */ + + /* sig[0] == 0x30, sig[1] == , sig[2] == 0x02 */ + + /* Move r in place */ + size_t r_len = sig[3]; + sig_ptr = &sig[4]; + if (r_len >= num_of_curve_bytes) { + sig_ptr = sig_ptr + r_len - num_of_curve_bytes; + memcpy(&r_s_pair[0], sig_ptr, num_of_curve_bytes); + if(r_len % 2) { + r_len--; + } + } else { + /* For encodings that reduce the size of r or s in case of zeros */ + memcpy(&r_s_pair[num_of_curve_bytes - r_len], sig_ptr, r_len); + } + + /* Move s in place */ + size_t s_len = sig_ptr[r_len+1]; /* + 1 to skip SEQUENCE */ + sig_ptr = &sig_ptr[r_len+2]; + if (s_len >= num_of_curve_bytes) { + sig_ptr = sig_ptr + s_len - num_of_curve_bytes; + memcpy(&r_s_pair[num_of_curve_bytes], sig_ptr, num_of_curve_bytes); + } else { + /* For encodings that reduce the size of r or s in case of zeros */ + memcpy(&r_s_pair[2*num_of_curve_bytes - s_len], sig_ptr, s_len); + } +} + +// OID id-ecPublicKey 1.2.840.10045.2.1. +static const uint8_t IdEcPublicKey[] = {0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01}; +#if defined(MCUBOOT_SIGN_EC256) +// OID secp256r1 1.2.840.10045.3.1.7. +static const uint8_t Secp256r1[] = {0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; +#endif /* MCUBOOT_SIGN_EC256 */ +#if defined(MCUBOOT_SIGN_EC384) +// OID secp384r1 1.3.132.0.34 +static const uint8_t Secp384r1[] = {0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22}; +#endif /* MCUBOOT_SIGN_EC384 */ + +static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) +{ +#if !defined(MCUBOOT_BUILTIN_KEY) + ctx->key_id = PSA_KEY_ID_NULL; + ctx->curve_byte_count = 0; + ctx->required_algorithm = 0; + +#else /* !MCUBOOT_BUILTIN_KEY */ + /* The incoming key ID is equal to the image index. The key ID value must be + * shifted (by one in this case) because zero is reserved (PSA_KEY_ID_NULL) + * and considered invalid. + */ + ctx->key_id++; /* Make sure it is not equal to 0. */ +#if defined(MCUBOOT_SIGN_EC256) + ctx->curve_byte_count = 32; + ctx->required_algorithm = PSA_ALG_SHA_256; +#endif /* MCUBOOT_SIGN_EC256 */ +#if defined(MCUBOOT_SIGN_EC384) + ctx->curve_byte_count = 48; + ctx->required_algorithm = PSA_ALG_SHA_384; +#endif /* MCUBOOT_SIGN_EC384 */ +#endif /* !MCUBOOT_BUILTIN_KEY */ +} + +static inline void bootutil_ecdsa_drop(bootutil_ecdsa_context *ctx) +{ + if (ctx->key_id != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(ctx->key_id); + } +} + +#if !defined(MCUBOOT_BUILTIN_KEY) +/* + * Parse a ECDSA public key with format specified in RFC5280 et al. + * + * OID for icEcPublicKey is 1.2.840.10045.2.1 + * OIDs for supported curves are as follows: + * secp256r1 (prime256v1): 1.2.840.10045.3.1.7 + * secp384r1: 1.3.132.0.34 + */ +static int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, + uint8_t **cp, uint8_t *end) +{ + psa_key_attributes_t key_attributes = psa_key_attributes_init(); + size_t key_size; + (void)end; + + /* public key oid is valid */ + if (memcmp(PUB_KEY_OID_OFFSET(cp), IdEcPublicKey, sizeof(IdEcPublicKey))) { + return (int)PSA_ERROR_INVALID_ARGUMENT; + } + +#if defined(MCUBOOT_SIGN_EC256) + if (!memcmp(CURVE_TYPE_OID_OFFSET(cp), Secp256r1, sizeof(Secp256r1))) { + ctx->curve_byte_count = 32; + ctx->required_algorithm = PSA_ALG_SHA_256; + } else +#endif /* MCUBOOT_SIGN_EC256 */ +#if defined(MCUBOOT_SIGN_EC384) + if (!memcmp(CURVE_TYPE_OID_OFFSET(cp), Secp384r1, sizeof(Secp384r1))) { + ctx->curve_byte_count = 48; + ctx->required_algorithm = PSA_ALG_SHA_384; + } else +#endif /* MCUBOOT_SIGN_EC384 */ + { + return (int)PSA_ERROR_INVALID_ARGUMENT; + } + + get_public_key_from_rfc5280_encoding(cp, &key_size); + /* Set attributes and import key */ + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(ctx->required_algorithm)); + psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + + return (int)psa_import_key(&key_attributes, *cp, key_size, &ctx->key_id); +} +#endif /* !MCUBOOT_BUILTIN_KEY */ + +/* Verify the signature against the provided hash. The signature gets parsed from + * the encoding first, then PSA Crypto has a dedicated API for ECDSA verification + */ +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hlen, + uint8_t *sig, size_t slen) +{ + (void)pk; + (void)pk_len; + (void)slen; + + uint8_t reformatted_signature[96] = {0}; /* Enough for P-384 signature sizes */ + parse_signature_from_rfc5480_encoding(sig, ctx->curve_byte_count,reformatted_signature); + + return (int) psa_verify_hash(ctx->key_id, PSA_ALG_ECDSA(ctx->required_algorithm), + hash, hlen, reformatted_signature, 2*ctx->curve_byte_count); +} +#elif defined(MCUBOOT_USE_MBED_TLS) + +typedef mbedtls_ecdsa_context bootutil_ecdsa_context; +static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) +{ + mbedtls_ecdsa_init(ctx); +} + +static inline void bootutil_ecdsa_drop(bootutil_ecdsa_context *ctx) +{ + mbedtls_ecdsa_free(ctx); +} + +#ifdef CY_MBEDTLS_HW_ACCELERATION +/* + * Parse the public key used for signing. + */ +static int bootutil_parse_eckey(bootutil_ecdsa_context *ctx, uint8_t **p, uint8_t *end) +{ + size_t len; + mbedtls_asn1_buf alg; + mbedtls_asn1_buf param; + + if (mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return -1; + } + end = *p + len; + + if (mbedtls_asn1_get_alg(p, end, &alg, ¶m)) { + return -2; + } + if (alg.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_pubkey_oid) - 1 || + memcmp(alg.ASN1_CONTEXT_MEMBER(p), ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) { + return -3; + } + if (param.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_secp256r1_oid) - 1|| + memcmp(param.ASN1_CONTEXT_MEMBER(p), ec_secp256r1_oid, sizeof(ec_secp256r1_oid) - 1)) { + return -4; + } + + if (mbedtls_ecp_group_load(&ctx->grp, MBEDTLS_ECP_DP_SECP256R1)) { + return -5; + } + + if (mbedtls_asn1_get_bitstring_null(p, end, &len)) { + return -6; + } + if (*p + len != end) { + return -7; + } + + if (mbedtls_ecp_point_read_binary(&ctx->grp, &ctx->Q, *p, end - *p)) { + return -8; + } + + if (mbedtls_ecp_check_pubkey(&ctx->grp, &ctx->Q)) { + return -9; + } + return 0; +} + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t sig_len) +{ + (void)pk; + (void)pk_len; + + /* + * This is simplified, as the hash length is also 32 bytes. + */ + while (sig[sig_len - 1] == '\0') { + sig_len--; + } + return mbedtls_ecdsa_read_signature(&ctx, hash, hash_len, sig, sig_len); +} + +#else /* CY_MBEDTLS_HW_ACCELERATION */ + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t sig_len) +{ + int rc; + + (void)sig; + (void)hash; + (void)hash_len; + + rc = mbedtls_ecp_group_load(&ctx->MBEDTLS_CONTEXT_MEMBER(grp), MBEDTLS_ECP_DP_SECP256R1); + if (rc) { + return -1; + } + + rc = mbedtls_ecp_point_read_binary(&ctx->MBEDTLS_CONTEXT_MEMBER(grp), &ctx->MBEDTLS_CONTEXT_MEMBER(Q), pk, pk_len); + if (rc) { + return -1; + } + + rc = mbedtls_ecp_check_pubkey(&ctx->MBEDTLS_CONTEXT_MEMBER(grp), &ctx->MBEDTLS_CONTEXT_MEMBER(Q)); + if (rc) { + return -1; + } + + rc = mbedtls_ecdsa_read_signature(ctx, hash, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE, + sig, sig_len); + if (rc) { + return -1; + } + + return 0; +} + +#endif /* CY_MBEDTLS_HW_ACCELERATION */ + +static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, + uint8_t **cp,uint8_t *end) +{ + int rc; +#ifdef CY_MBEDTLS_HW_ACCELERATION + rc = bootutil_parse_eckey(&ctx, cp, end); +#else + (void)ctx; + rc = bootutil_import_key(cp, end); +#endif + return rc; +} + +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) +typedef uintptr_t bootutil_ecdsa_context; +static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_ecdsa_drop(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t sig_len) +{ + (void)ctx; + (void)pk_len; + (void)hash_len; + uint8_t dsig[2 * NUM_ECC_BYTES]; + + if (bootutil_decode_sig(dsig, sig, sig + sig_len)) { + return -1; + } + + /* Only support uncompressed keys. */ + if (pk[0] != 0x04) { + return -1; + } + pk++; + + return bl_secp256r1_validate(hash, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE, pk, dsig); +} + +static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, + uint8_t **cp,uint8_t *end) +{ + (void)ctx; + return bootutil_import_key(cp, end); +} +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_ECDSA_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/hmac_sha256.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/hmac_sha256.h new file mode 100644 index 0000000..e684018 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/hmac_sha256.h @@ -0,0 +1,134 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_HMAC_SHA256_H_ +#define __BOOTUTIL_CRYPTO_HMAC_SHA256_H_ + +#include "mcuboot_config/mcuboot_config.h" + +#if (defined(MCUBOOT_USE_MBED_TLS) + \ + defined(MCUBOOT_USE_TINYCRYPT)) != 1 + #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT" +#endif + +#if defined(MCUBOOT_USE_MBED_TLS) + #include + #include + #include + #include +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) + #include + #include + #include + #include +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MCUBOOT_USE_TINYCRYPT) +typedef struct tc_hmac_state_struct bootutil_hmac_sha256_context; +static inline void bootutil_hmac_sha256_init(bootutil_hmac_sha256_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_hmac_sha256_drop(bootutil_hmac_sha256_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_hmac_sha256_set_key(bootutil_hmac_sha256_context *ctx, const uint8_t *key, unsigned int key_size) +{ + int rc; + rc = tc_hmac_set_key(ctx, key, key_size); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + rc = tc_hmac_init(ctx); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +static inline int bootutil_hmac_sha256_update(bootutil_hmac_sha256_context *ctx, const void *data, unsigned int data_length) +{ + int rc; + rc = tc_hmac_update(ctx, data, data_length); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +static inline int bootutil_hmac_sha256_finish(bootutil_hmac_sha256_context *ctx, uint8_t *tag, unsigned int taglen) +{ + int rc; + rc = tc_hmac_final(tag, taglen, ctx); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#if defined(MCUBOOT_USE_MBED_TLS) +/** + * The generic message-digest context. + */ +typedef mbedtls_md_context_t bootutil_hmac_sha256_context; + +static inline void bootutil_hmac_sha256_init(bootutil_hmac_sha256_context *ctx) +{ + mbedtls_md_init(ctx); +} + +static inline void bootutil_hmac_sha256_drop(bootutil_hmac_sha256_context *ctx) +{ + mbedtls_md_free(ctx); +} + +static inline int bootutil_hmac_sha256_set_key(bootutil_hmac_sha256_context *ctx, const uint8_t *key, unsigned int key_size) +{ + int rc; + + rc = mbedtls_md_setup(ctx, mbedtls_md_info_from_string("SHA256"), 1); + if (rc != 0) { + return rc; + } + rc = mbedtls_md_hmac_starts(ctx, key, key_size); + return rc; +} + +static inline int bootutil_hmac_sha256_update(bootutil_hmac_sha256_context *ctx, const void *data, unsigned int data_length) +{ + return mbedtls_md_hmac_update(ctx, data, data_length); +} + +static inline int bootutil_hmac_sha256_finish(bootutil_hmac_sha256_context *ctx, uint8_t *tag, unsigned int taglen) +{ + (void)taglen; + /* + * HMAC the key and check that our received MAC matches the generated tag + */ + return mbedtls_md_hmac_finish(ctx, tag); +} +#endif /* MCUBOOT_USE_MBED_TLS */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_HMAC_SHA256_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/rsa.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/rsa.h new file mode 100644 index 0000000..581e4ec --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/rsa.h @@ -0,0 +1,356 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Arm Limited + */ + +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, the choices are: MCUBOOT_USE_MBED_TLS and + * MCUBOOT_USE_PSA_CRYPTO. Note that support for MCUBOOT_USE_PSA_CRYPTO is + * still experimental and it might not support all the crypto abstractions + * that MCUBOOT_USE_MBED_TLS supports. For this reason, it's allowed to have + * both of them defined, and for crypto modules that support both abstractions, + * the MCUBOOT_USE_PSA_CRYPTO will take precedence. + */ + +/* + * Note: The source file that includes this header should either define one of the + * two options BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED or BOOTUTIL_CRYPTO_RSA_SIGN_ENABLED + * This will make the signature functions or encryption functions visible without + * generating a "defined but not used" compiler warning + */ + +#ifndef __BOOTUTIL_CRYPTO_RSA_H_ +#define __BOOTUTIL_CRYPTO_RSA_H_ + +#include "mcuboot_config/mcuboot_config.h" + +#if defined(MCUBOOT_USE_PSA_CRYPTO) || defined(MCUBOOT_USE_MBED_TLS) +#define MCUBOOT_USE_PSA_OR_MBED_TLS +#endif /* MCUBOOT_USE_PSA_CRYPTO || MCUBOOT_USE_MBED_TLS */ + +#if (defined(MCUBOOT_USE_PSA_OR_MBED_TLS)) != 1 + #error "One crypto backend must be defined: either MBED_TLS/PSA_CRYPTO" +#endif + +#if defined(MCUBOOT_USE_PSA_CRYPTO) + +#include +#include "bootutil/enc_key_public.h" + +#elif defined(MCUBOOT_USE_MBED_TLS) + +#include "mbedtls/rsa.h" +#include "mbedtls/version.h" +#if defined(BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED) +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#include "rsa_alt_helpers.h" +#else +#include "mbedtls/rsa_internal.h" +#endif +#endif /* BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED */ +#include "mbedtls/asn1.h" +#include "bootutil/crypto/common.h" + +#endif /* MCUBOOT_USE_MBED_TLS */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MCUBOOT_USE_PSA_CRYPTO) + +typedef struct { + psa_key_id_t key_id; +} bootutil_rsa_context; + +static inline void bootutil_rsa_init(bootutil_rsa_context *ctx) +{ + ctx->key_id = PSA_KEY_ID_NULL; +} + +static inline void bootutil_rsa_drop(bootutil_rsa_context *ctx) +{ + if (ctx->key_id != PSA_KEY_ID_NULL) { + (void)psa_destroy_key(ctx->key_id); + } +} + +#if defined(BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED) +static int bootutil_rsa_oaep_decrypt( + bootutil_rsa_context *ctx, + size_t *olen, + const uint8_t *input, + uint8_t *output, + size_t output_max_len) +{ + psa_status_t status = PSA_ERROR_INVALID_ARGUMENT; + + /* Perform an additional defensive check to compare the modulus of the RSA + * key to the expected input of the decryption function, i.e. TLV_ENC_RSA_SZ + */ + psa_key_attributes_t key_attr = psa_key_attributes_init(); + status = psa_get_key_attributes(ctx->key_id, &key_attr); + if (status != PSA_SUCCESS) { + return -1; + } + size_t input_size = PSA_BITS_TO_BYTES(psa_get_key_bits(&key_attr)); + if (input_size != TLV_ENC_RSA_SZ) { + return -1; + } + + status = psa_asymmetric_decrypt(ctx->key_id, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), + input, TLV_ENC_RSA_SZ, NULL, 0, + output, output_max_len, olen); + return (int)status; +} + +/* + * Parse a RSA private key with format specified in RFC3447 A.1.2 + * + * The key is meant to be used for OAEP decrypt hence algorithm and usage are hardcoded + */ +static int +bootutil_rsa_parse_private_key(bootutil_rsa_context *ctx, uint8_t **p, uint8_t *end) +{ + psa_status_t status = PSA_ERROR_INVALID_ARGUMENT; + psa_key_attributes_t key_attributes = psa_key_attributes_init(); + + /* Set attributes and import key */ + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&key_attributes, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256)); + psa_set_key_type(&key_attributes, PSA_KEY_TYPE_RSA_KEY_PAIR); + + status = psa_import_key(&key_attributes, *p, (end - *p), &ctx->key_id); + return (int)status; +} + +#endif /* BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED */ + +#if defined(BOOTUTIL_CRYPTO_RSA_SIGN_ENABLED) +/* + * Parse a RSA public key with format specified in RFC3447 A.1.1 + * + * The key is meant to be used for PSS signature verification hence algorithm and usage are hardcoded + */ +static int +bootutil_rsa_parse_public_key(bootutil_rsa_context *ctx, uint8_t **p, uint8_t *end) +{ + psa_status_t status = PSA_ERROR_INVALID_ARGUMENT; + psa_key_attributes_t key_attributes = psa_key_attributes_init(); + + /* Set attributes and import key */ + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&key_attributes, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256)); + psa_set_key_type(&key_attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + + status = psa_import_key(&key_attributes, *p, (end - *p), &ctx->key_id); + return (int)status; +} + +/* Get the modulus (N) length in bytes */ +static size_t bootutil_rsa_get_len(const bootutil_rsa_context *ctx) +{ + psa_key_attributes_t key_attributes = psa_key_attributes_init(); + psa_status_t status = psa_get_key_attributes(ctx->key_id, &key_attributes); + if (status != PSA_SUCCESS) { + return 0; + } + return PSA_BITS_TO_BYTES(psa_get_key_bits(&key_attributes)); +} + +/* PSA Crypto has a dedicated API for RSASSA-PSS verification */ +static inline int bootutil_rsassa_pss_verify(const bootutil_rsa_context *ctx, + uint8_t *hash, size_t hlen, uint8_t *sig, size_t slen) +{ + return (int) psa_verify_hash(ctx->key_id, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256), + hash, hlen, sig, slen); +} +#endif /* BOOTUTIL_CRYPTO_RSA_SIGN_ENABLED */ + +#elif defined(MCUBOOT_USE_MBED_TLS) + +typedef mbedtls_rsa_context bootutil_rsa_context; + +static inline void bootutil_rsa_init(bootutil_rsa_context *ctx) +{ +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + mbedtls_rsa_init(ctx); + mbedtls_rsa_set_padding(ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); +#else + mbedtls_rsa_init(ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); +#endif +} + +static inline void bootutil_rsa_drop(bootutil_rsa_context *ctx) +{ + mbedtls_rsa_free(ctx); +} + +#if defined(BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) +static int fake_rng(void *p_rng, unsigned char *output, size_t len); +#endif /* BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED && MBEDTLS_VERSION_NUMBER >= 3.0 */ + +#if defined(BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED) +static inline int bootutil_rsa_oaep_decrypt( + bootutil_rsa_context *ctx, + size_t *olen, + const uint8_t *input, + uint8_t *output, + size_t output_max_len) +{ + int rc = -1; +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + rc = mbedtls_rsa_rsaes_oaep_decrypt(ctx, fake_rng, NULL, + NULL, 0, olen, input, output, output_max_len); +#else + rc = mbedtls_rsa_rsaes_oaep_decrypt(ctx, NULL, NULL, MBEDTLS_RSA_PRIVATE, + NULL, 0, olen, input, output, output_max_len); +#endif + return rc; +} + +/* + * Parse a RSA private key with format specified in RFC3447 A.1.2 + */ +static int +bootutil_rsa_parse_private_key(bootutil_rsa_context *ctx, uint8_t **p, uint8_t *end) +{ + size_t len; + + if (mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { + return -1; + } + + if (*p + len != end) { + return -2; + } + + /* Non-optional fields. */ + if ( /* version */ + mbedtls_asn1_get_int(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(ver)) != 0 || + /* public modulus */ + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(N)) != 0 || + /* public exponent */ + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(E)) != 0 || + /* private exponent */ + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(D)) != 0 || + /* primes */ + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(P)) != 0 || + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(Q)) != 0) { + + return -3; + } + +#if !defined(MBEDTLS_RSA_NO_CRT) + /* + * DP/DQ/QP are only used inside mbedTLS if it was built with the + * Chinese Remainder Theorem enabled (default). In case it is disabled + * we parse, or if not available, we calculate those values. + */ + if (*p < end) { + if ( /* d mod (p-1) and d mod (q-1) */ + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(DP)) != 0 || + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(DQ)) != 0 || + /* q ^ (-1) mod p */ + mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(QP)) != 0) { + + return -4; + } + } else { + if (mbedtls_rsa_deduce_crt(&ctx->MBEDTLS_CONTEXT_MEMBER(P), + &ctx->MBEDTLS_CONTEXT_MEMBER(Q), + &ctx->MBEDTLS_CONTEXT_MEMBER(D), + &ctx->MBEDTLS_CONTEXT_MEMBER(DP), + &ctx->MBEDTLS_CONTEXT_MEMBER(DQ), + &ctx->MBEDTLS_CONTEXT_MEMBER(QP)) != 0) { + return -5; + } + } +#endif /* !MBEDTLS_RSA_NO_CRT */ + + ctx->MBEDTLS_CONTEXT_MEMBER(len) = mbedtls_mpi_size(&ctx->MBEDTLS_CONTEXT_MEMBER(N)); + + if (mbedtls_rsa_check_privkey(ctx) != 0) { + return -6; + } + + return 0; +} +#endif /* BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED */ + +#if defined(BOOTUTIL_CRYPTO_RSA_SIGN_ENABLED) +/* + * Parse a RSA public key with format specified in RFC3447 A.1.1 + */ +static int +bootutil_rsa_parse_public_key(bootutil_rsa_context *ctx, uint8_t **p, uint8_t *end) +{ + int rc; + size_t len; + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return -1; + } + + if (*p + len != end) { + return -2; + } + + if ((rc = mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(N))) != 0 || + (rc = mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(E))) != 0) { + return -3; + } + + ctx->MBEDTLS_CONTEXT_MEMBER(len) = mbedtls_mpi_size(&ctx->MBEDTLS_CONTEXT_MEMBER(N)); + + if (*p != end) { + return -4; + } + + /* The Mbed TLS version is more than 2.6.1 */ +#if MBEDTLS_VERSION_NUMBER > 0x02060100 + rc = mbedtls_rsa_import(ctx, &ctx->MBEDTLS_CONTEXT_MEMBER(N), NULL, + NULL, NULL, &ctx->MBEDTLS_CONTEXT_MEMBER(E)); + if (rc != 0) { + return -5; + } +#endif + + rc = mbedtls_rsa_check_pubkey(ctx); + if (rc != 0) { + return -6; + } + + ctx->MBEDTLS_CONTEXT_MEMBER(len) = mbedtls_mpi_size(&ctx->MBEDTLS_CONTEXT_MEMBER(N)); + + return 0; +} + +/* Get the modulus (N) length in bytes */ +static inline size_t bootutil_rsa_get_len(const bootutil_rsa_context *ctx) +{ + return mbedtls_rsa_get_len(ctx); +} + +/* Performs modular exponentiation using the public key output = input^E mod N */ +static inline int bootutil_rsa_public(bootutil_rsa_context *ctx, const uint8_t *input, uint8_t *output) +{ + return mbedtls_rsa_public(ctx, input, output); +} +#endif /* BOOTUTIL_CRYPTO_RSA_SIGN_ENABLED */ + +#endif /* MCUBOOT_USE_MBED_TLS */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_RSA_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/sha.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/sha.h new file mode 100644 index 0000000..88e94fb --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/crypto/sha.h @@ -0,0 +1,250 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2017-2019 JUUL Labs + * Copyright (c) 2021-2023 Arm Limited + */ + +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, the choices are: MCUBOOT_USE_MBED_TLS, MCUBOOT_USE_TINYCRYPT, + * MCUBOOT_USE_PSA_CRYPTO, MCUBOOT_USE_CC310. Note that support for MCUBOOT_USE_PSA_CRYPTO + * is still experimental and it might not support all the crypto abstractions + * that MCUBOOT_USE_MBED_TLS supports. For this reason, it's allowed to have + * both of them defined, and for crypto modules that support both abstractions, + * the MCUBOOT_USE_PSA_CRYPTO will take precedence. + */ + +#ifndef __BOOTUTIL_CRYPTO_SHA_H_ +#define __BOOTUTIL_CRYPTO_SHA_H_ + +#include "mcuboot_config/mcuboot_config.h" +#include "mcuboot_config/mcuboot_logging.h" + +#if defined(MCUBOOT_USE_PSA_CRYPTO) || defined(MCUBOOT_USE_MBED_TLS) +#define MCUBOOT_USE_PSA_OR_MBED_TLS +#endif /* MCUBOOT_USE_PSA_CRYPTO || MCUBOOT_USE_MBED_TLS */ + +#if (defined(MCUBOOT_USE_PSA_OR_MBED_TLS) + \ + defined(MCUBOOT_USE_TINYCRYPT) + \ + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ + defined(MCUBOOT_USE_CC310)) != 1 + #error "One crypto backend must be defined: either CC310/MBED_TLS/TINYCRYPT/PSA_CRYPTO" +#endif + +#if defined(MCUBOOT_SHA512) + #define IMAGE_HASH_SIZE (64) + #define EXPECTED_HASH_TLV IMAGE_TLV_SHA512 +#elif defined(MCUBOOT_SIGN_EC384) + #define IMAGE_HASH_SIZE (48) + #define EXPECTED_HASH_TLV IMAGE_TLV_SHA384 +#else + #define IMAGE_HASH_SIZE (32) + #define EXPECTED_HASH_TLV IMAGE_TLV_SHA256 +#endif /* MCUBOOT_SIGN */ + +/* Universal defines for SHA-256 */ +#define BOOTUTIL_CRYPTO_SHA256_BLOCK_SIZE (64) +#define BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE (32) + +#if defined(MCUBOOT_USE_PSA_CRYPTO) + +#include + +#elif defined(MCUBOOT_USE_MBED_TLS) + +#include +#include +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +#include +#endif + +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) + #include + #include +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#if defined(MCUBOOT_USE_CC310) + #include +#endif /* MCUBOOT_USE_CC310 */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MCUBOOT_USE_PSA_CRYPTO) + +typedef psa_hash_operation_t bootutil_sha_context; + +static inline int bootutil_sha_init(bootutil_sha_context *ctx) +{ + *ctx = psa_hash_operation_init(); +#if defined(MCUBOOT_SHA512) + psa_status_t status = psa_hash_setup(ctx, PSA_ALG_SHA_512); +#elif defined(MCUBOOT_SIGN_EC384) + psa_status_t status = psa_hash_setup(ctx, PSA_ALG_SHA_384); +#else + psa_status_t status = psa_hash_setup(ctx, PSA_ALG_SHA_256); +#endif + return (int)status; +} + +static inline int bootutil_sha_drop(bootutil_sha_context *ctx) +{ + return (int)psa_hash_abort(ctx); +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + return (int)psa_hash_update(ctx, data, data_len); +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + size_t hash_length = 0; + /* Assumes the output buffer is at least the expected size of the hash */ +#if defined(MCUBOOT_SHA512) + return (int)psa_hash_finish(ctx, output, PSA_HASH_LENGTH(PSA_ALG_SHA_512), &hash_length); +#elif defined(MCUBOOT_SIGN_EC384) + return (int)psa_hash_finish(ctx, output, PSA_HASH_LENGTH(PSA_ALG_SHA_384), &hash_length); +#else + return (int)psa_hash_finish(ctx, output, PSA_HASH_LENGTH(PSA_ALG_SHA_256), &hash_length); +#endif +} + +#elif defined(MCUBOOT_USE_MBED_TLS) + +typedef mbedtls_sha256_context bootutil_sha_context; + +static inline int bootutil_sha_init(bootutil_sha_context *ctx) +{ + mbedtls_sha256_init(ctx); + return mbedtls_sha256_starts_ret(ctx, 0); +} + +static inline int bootutil_sha_drop(bootutil_sha_context *ctx) +{ + mbedtls_sha256_free(ctx); + return 0; +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + return mbedtls_sha256_update_ret(ctx, data, data_len); +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + return mbedtls_sha256_finish_ret(ctx, output); +} + +#endif /* MCUBOOT_USE_MBED_TLS */ + +#if defined(MCUBOOT_USE_TINYCRYPT) +typedef struct tc_sha256_state_struct bootutil_sha_context; + +static inline int bootutil_sha_init(bootutil_sha_context *ctx) +{ + tc_sha256_init(ctx); + return 0; +} + +static inline int bootutil_sha_drop(bootutil_sha_context *ctx) +{ + (void)ctx; + return 0; +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + return tc_sha256_update(ctx, data, data_len); +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + return tc_sha256_final(output, ctx); +} +#endif /* MCUBOOT_USE_TINYCRYPT */ + +#if defined(MCUBOOT_USE_CC310) +static inline int bootutil_sha_init(bootutil_sha_context *ctx) +{ + cc310_sha256_init(ctx); + return 0; +} + +static inline int bootutil_sha_drop(bootutil_sha_context *ctx) +{ + (void)ctx; + nrf_cc310_disable(); + return 0; +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + cc310_sha256_update(ctx, data, data_len); + return 0; +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + cc310_sha256_finalize(ctx, output); + return 0; +} +#endif /* MCUBOOT_USE_CC310 */ + +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + +#include + +typedef bl_sha256_ctx_t bootutil_sha_context; + +static inline void bootutil_sha_init(bootutil_sha_context *ctx) +{ + bl_sha256_init(ctx); +} + +static inline void bootutil_sha_drop(bootutil_sha_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + return bl_sha256_update(ctx, data, data_len); +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + bl_sha256_finalize(ctx, output); + return 0; +} +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_SHA_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key.h new file mode 100644 index 0000000..4ff2432 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key.h @@ -0,0 +1,83 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2018-2019 JUUL Labs + * Copyright (c) 2019 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BOOTUTIL_ENC_KEY_H +#define BOOTUTIL_ENC_KEY_H + +#include +#include +#include +#include "bootutil/crypto/aes_ctr.h" +#include "bootutil/image.h" +#include "bootutil/sign_key.h" +#include "bootutil/enc_key_public.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BOOT_ENC_TLV_ALIGN_SIZE ALIGN_UP(BOOT_ENC_TLV_SIZE, BOOT_MAX_ALIGN) + +struct enc_key_data { + uint8_t valid; + bootutil_aes_ctr_context aes_ctr; +}; + +/** + * Retrieve the private key for image encryption. + * + * @param[out] private_key structure to store the private key and + * its length. + * + * @return 0 on success; nonzero on failure. + * + */ +int boot_enc_retrieve_private_key(struct bootutil_key **private_key); + +struct boot_status; + +/* Decrypt random, symmetric encryption key */ +int boot_decrypt_key(const uint8_t *buf, uint8_t *enckey); + +int boot_enc_init(struct enc_key_data *enc_state, uint8_t slot); +int boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot); +int boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot, + const struct boot_status *bs); +int boot_enc_load(struct enc_key_data *enc_state, int slot, + const struct image_header *hdr, const struct flash_area *fap, + struct boot_status *bs); +bool boot_enc_valid(struct enc_key_data *enc_state, int slot); +void boot_enc_encrypt(struct enc_key_data *enc_state, int slot, + uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf); +void boot_enc_decrypt(struct enc_key_data *enc_state, int slot, + uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf); +void boot_enc_zeroize(struct enc_key_data *enc_state); + +#ifdef __cplusplus +} +#endif + +#endif /* BOOTUTIL_ENC_KEY_H */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key_public.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key_public.h new file mode 100644 index 0000000..6874cfb --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/enc_key_public.h @@ -0,0 +1,66 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2018-2019 JUUL Labs + * Copyright (c) 2019-2021 Arm Limited + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef BOOTUTIL_ENC_KEY_PUBLIC_H +#define BOOTUTIL_ENC_KEY_PUBLIC_H +#include +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ALIGN_UP +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#endif + +#ifdef MCUBOOT_AES_256 +#define BOOT_ENC_KEY_SIZE 32 +#else +#define BOOT_ENC_KEY_SIZE 16 +#endif + +#define BOOT_ENC_KEY_ALIGN_SIZE ALIGN_UP(BOOT_ENC_KEY_SIZE, BOOT_MAX_ALIGN) + +#define TLV_ENC_RSA_SZ 256 +#define TLV_ENC_KW_SZ (BOOT_ENC_KEY_SIZE + 8) +#define TLV_ENC_EC256_SZ (65 + 32 + BOOT_ENC_KEY_SIZE) +#define TLV_ENC_X25519_SZ (32 + 32 + BOOT_ENC_KEY_SIZE) + +#if defined(MCUBOOT_ENCRYPT_RSA) +#define BOOT_ENC_TLV_SIZE TLV_ENC_RSA_SZ +#elif defined(MCUBOOT_ENCRYPT_EC256) +#define BOOT_ENC_TLV_SIZE TLV_ENC_EC256_SZ +#elif defined(MCUBOOT_ENCRYPT_X25519) +#define BOOT_ENC_TLV_SIZE TLV_ENC_X25519_SZ +#else +#define BOOT_ENC_TLV_SIZE TLV_ENC_KW_SZ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* BOOTUTIL_ENC_KEY_PUBLIC_H */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening.h new file mode 100644 index 0000000..fdf01b5 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening.h @@ -0,0 +1,371 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020 Arm Limited + */ + +#ifndef __FAULT_INJECTION_HARDENING_H__ +#define __FAULT_INJECTION_HARDENING_H__ + +/* Fault injection mitigation library. + * + * Has support for different measures, which can either be enabled/disabled + * separately or by defining one of the MCUBOOT_FIH_PROFILEs. + * + * NOTE: These constructs against fault injection attacks are not guaranteed to + * be secure for all compilers, but execution is going to be correct and + * including them will certainly help to harden the code. + * + * FIH_ENABLE_DOUBLE_VARS makes critical variables into a tuple (x, x ^ msk). + * Then the correctness of x can be checked by XORing the two tuple values + * together. This also means that comparisons between fih_ints can be verified + * by doing x == y && x_msk == y_msk. + * + * FIH_ENABLE_GLOBAL_FAIL makes all while(1) failure loops redirect to a global + * failure loop. This loop has mitigations against loop escapes / unlooping. + * This also means that any unlooping won't immediately continue executing the + * function that was executing before the failure. + * + * FIH_ENABLE_CFI (Control Flow Integrity) creates a global counter that is + * incremented before every FIH_CALL of vulnerable functions. On the function + * return the counter is decremented, and after the return it is verified that + * the counter has the same value as before this process. This can be used to + * verify that the function has actually been called. This protection is + * intended to discover that important functions are called in an expected + * sequence and neither of them is missed due to an instruction skip which could + * be a result of glitching attack. It does not provide protection against ROP + * or JOP attacks. + * + * FIH_ENABLE_DELAY causes random delays. This makes it hard to cause faults + * precisely. It requires an RNG. An mbedtls integration is provided in + * fault_injection_hardening_delay_mbedtls.h, but any RNG that has an entropy + * source can be used by implementing the fih_delay_random_uchar function. + * + * The basic call pattern is: + * + * FIH_DECLARE(fih_rc, FIH_FAILURE); + * FIH_CALL(vulnerable_function, fih_rc, arg1, arg2); + * if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + * FIH_PANIC; + * } + * + * Note that any function called by FIH_CALL must only return using FIH_RETURN, + * as otherwise the CFI counter will not be decremented and the CFI check will + * fail causing a panic. + */ + +#include "mcuboot_config/mcuboot_config.h" + +#if defined(MCUBOOT_FIH_PROFILE_HIGH) + +#define FIH_ENABLE_DELAY /* Requires an entropy source */ +#define FIH_ENABLE_DOUBLE_VARS +#define FIH_ENABLE_GLOBAL_FAIL +#define FIH_ENABLE_CFI + +#elif defined(MCUBOOT_FIH_PROFILE_MEDIUM) + +#define FIH_ENABLE_DOUBLE_VARS +#define FIH_ENABLE_GLOBAL_FAIL +#define FIH_ENABLE_CFI + +#elif defined(MCUBOOT_FIH_PROFILE_LOW) + +#define FIH_ENABLE_GLOBAL_FAIL +#define FIH_ENABLE_CFI + +#elif !defined(MCUBOOT_FIH_PROFILE_OFF) +#define MCUBOOT_FIH_PROFILE_OFF +#endif /* MCUBOOT_FIH_PROFILE */ + +#ifdef FIH_ENABLE_DELAY +#include "fault_injection_hardening_delay_rng.h" +#endif /* FIH_ENABLE_DELAY */ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Non-zero success value to defend against register resets. Zero is the most + * common value for a corrupted register so complex bit-patterns are used + */ +#ifndef MCUBOOT_FIH_PROFILE_OFF +#define FIH_POSITIVE_VALUE 0x1AAAAAAA +#define FIH_NEGATIVE_VALUE 0x15555555 +#define FIH_CONST1 0x1FCDEA88 +#define FIH_CONST2 0x19C1F6E1 +#else +#define FIH_POSITIVE_VALUE 0 +#define FIH_NEGATIVE_VALUE -1 +#define FIH_CONST1 1 +#define FIH_CONST2 1 +#endif + +/* A volatile mask is used to prevent compiler optimization - the mask is xored + * with the variable to create the backup and the integrity can be checked with + * another xor. The mask value doesn't _really_ matter that much, as long as + * it has reasonably high hamming weight. + */ +#define _FIH_MASK_VALUE 0xBEEF + +#ifdef FIH_ENABLE_DOUBLE_VARS + +/* All ints are replaced with two int - the normal one and a backup which is + * XORed with the mask. + */ +extern volatile int _fih_mask; +typedef volatile struct { + volatile int val; + volatile int msk; +} fih_int; +typedef volatile int fih_ret; + +#else + +typedef int fih_int; +typedef int fih_ret; + +#endif /* FIH_ENABLE_DOUBLE_VARS */ + +extern fih_ret FIH_SUCCESS; +extern fih_ret FIH_FAILURE; +extern fih_ret FIH_NO_BOOTABLE_IMAGE; +extern fih_ret FIH_BOOT_HOOK_REGULAR; + +#ifdef FIH_ENABLE_GLOBAL_FAIL +/* Global failure handler - more resistant to unlooping. noinline and used are + * used to prevent optimization + */ +__attribute__((noinline)) __attribute__((used)) +void fih_panic_loop(void); +#define FIH_PANIC fih_panic_loop() +#else +#define FIH_PANIC while (1) {} +#endif /* FIH_ENABLE_GLOBAL_FAIL */ + +/* NOTE: For functions to be inlined outside their compilation unit they have to + * have the body in the header file. This is required as function calls are easy + * to skip. + */ +#ifdef FIH_ENABLE_DELAY + +/* Delaying logic, with randomness from a CSPRNG */ +__attribute__((always_inline)) inline +int fih_delay(void) +{ + unsigned char delay; + int foo = 0; + volatile int rc; + + delay = fih_delay_random_uchar(); + + for (volatile int i = 0; i < delay; i++) { + foo++; + } + + rc = 1; + + /* rc is volatile so if it is the return value then the function cannot be + * optimized + */ + return rc; +} + +#else + +__attribute__((always_inline)) inline +int fih_delay_init(void) +{ + return 1; +} + +__attribute__((always_inline)) inline +int fih_delay(void) +{ + return 1; +} +#endif /* FIH_ENABLE_DELAY */ + +#ifdef FIH_ENABLE_DOUBLE_VARS + +__attribute__((always_inline)) inline +void fih_int_validate(fih_int x) +{ + if (x.val != (x.msk ^ _fih_mask)) { + FIH_PANIC; + } +} + +/* Convert a fih_int to an int. Validate for tampering. */ +__attribute__((always_inline)) inline +int fih_int_decode(fih_int x) +{ + fih_int_validate(x); + return x.val; +} + +/* Convert an int to a fih_int, can be used to encode specific error codes. */ +__attribute__((always_inline)) inline +fih_int fih_int_encode(int x) +{ + fih_int ret = {x, x ^ _fih_mask}; + return ret; +} + +/* Standard equality. If A == B then 1, else 0 */ +#define FIH_EQ(x, y) ((x == y) && fih_delay() && !(y != x)) +#define FIH_NOT_EQ(x, y) ((x != y) || !fih_delay() || !(y == x)) +#define FIH_SET(x, y) x = y; if(fih_delay() && (x != y)) FIH_PANIC + +#else + +/* NOOP */ +__attribute__((always_inline)) inline +void fih_int_validate(fih_int x) +{ + (void) x; + return; +} + +/* NOOP */ +__attribute__((always_inline)) inline +int fih_int_decode(fih_int x) +{ + return x; +} + +/* NOOP */ +__attribute__((always_inline)) inline +fih_int fih_int_encode(int x) +{ + return x; +} + +#define FIH_EQ(x, y) (x == y) +#define FIH_NOT_EQ(x, y) (x != y) +#define FIH_SET(x, y) x = y + +#endif /* FIH_ENABLE_DOUBLE_VARS */ + +#define FIH_DECLARE(var, val) \ + fih_ret FIH_SET(var, val) + +/* C has a common return pattern where 0 is a correct value and all others are + * errors. This function converts 0 to FIH_SUCCESS and any other number to a + * value that is not FIH_SUCCESS + */ +__attribute__((always_inline)) inline +fih_ret fih_ret_encode_zero_equality(int x) +{ + if (x) { + return FIH_FAILURE; + } else { + return FIH_SUCCESS; + } +} + +#ifdef FIH_ENABLE_CFI +extern fih_int _fih_cfi_ctr; +#endif /* FIH_ENABLE_CFI */ + +fih_int fih_cfi_get_and_increment(void); +void fih_cfi_validate(fih_int saved); +void fih_cfi_decrement(void); + +/* Label for interacting with FIH testing tool. Can be parsed from the elf file + * after compilation. Does not require debug symbols. + */ +#if defined(__ICCARM__) +#define FIH_LABEL(str, lin, cnt) __asm volatile ("FIH_LABEL_" str "_" #lin "_" #cnt "::" ::); +#elif defined(__APPLE__) +#define FIH_LABEL(str) do {} while (0) +#else +#define FIH_LABEL(str) __asm volatile ("FIH_LABEL_" str "_%=:" ::); +#endif + +/* Main FIH calling macro. return variable is second argument. Does some setup + * before and validation afterwards. Inserts labels for use with testing script. + * + * First perform the precall step - this gets the current value of the CFI + * counter and saves it to a local variable, and then increments the counter. + * + * Then set the return variable to FIH_FAILURE as a base case. + * + * Then perform the function call. As part of the funtion FIH_RET must be called + * which will decrement the counter. + * + * The postcall step gets the value of the counter and compares it to the + * previously saved value. If this is equal then the function call and all child + * function calls were performed. + */ +#if defined(__ICCARM__) +#define FIH_CALL(f, ret, ...) FIH_CALL2(f, ret, __LINE__, __COUNTER__, __VA_ARGS__) + +#define FIH_CALL2(f, ret, l, c, ...) \ + do { \ + FIH_LABEL("FIH_CALL_START", l, c); \ + FIH_CFI_PRECALL_BLOCK; \ + ret = FIH_FAILURE; \ + if (fih_delay()) { \ + ret = f(__VA_ARGS__); \ + } \ + FIH_CFI_POSTCALL_BLOCK; \ + FIH_LABEL("FIH_CALL_END", l, c); \ + } while (0) + +#else + +#define FIH_CALL(f, ret, ...) \ + do { \ + FIH_LABEL("FIH_CALL_START"); \ + FIH_CFI_PRECALL_BLOCK; \ + ret = FIH_FAILURE; \ + if (fih_delay()) { \ + ret = f(__VA_ARGS__); \ + } \ + FIH_CFI_POSTCALL_BLOCK; \ + FIH_LABEL("FIH_CALL_END"); \ + } while (0) +#endif + +/* FIH return changes the state of the internal state machine. If you do a + * FIH_CALL then you need to do a FIH_RET else the state machine will detect + * tampering and panic. + */ +#define FIH_RET(ret) \ + do { \ + FIH_CFI_PRERET; \ + return ret; \ + } while (0) + + +#ifdef FIH_ENABLE_CFI +/* Macro wrappers for functions - Even when the functions have zero body this + * saves a few bytes on noop functions as it doesn't generate the call/ret + * + * CFI precall function saves the CFI counter and then increments it - the + * postcall then checks if the counter is equal to the saved value. In order for + * this to be the case a FIH_RET must have been performed inside the called + * function in order to decrement the counter, so the function must have been + * called. + */ +#define FIH_CFI_PRECALL_BLOCK \ + fih_int _fih_cfi_saved_value = fih_cfi_get_and_increment() + +#define FIH_CFI_POSTCALL_BLOCK \ + fih_cfi_validate(_fih_cfi_saved_value) + +#define FIH_CFI_PRERET \ + fih_cfi_decrement() +#else +#define FIH_CFI_PRECALL_BLOCK +#define FIH_CFI_POSTCALL_BLOCK +#define FIH_CFI_PRERET +#endif /* FIH_ENABLE_CFI */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __FAULT_INJECTION_HARDENING_H__ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening_delay_rng.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening_delay_rng.h new file mode 100644 index 0000000..339fecb --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/fault_injection_hardening_delay_rng.h @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020 Arm Limited + */ + +#ifndef __FAULT_INJECTION_HARDENING_DELAY_RNG_H__ +#define __FAULT_INJECTION_HARDENING_DELAY_RNG_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * \brief Set up the RNG for use with random delays. Called once at startup. + */ +int fih_delay_init(void); + +/** + * \brief Get a random unsigned char from an RNG seeded with an entropy source. + * + * \return A random value that fits inside an unsigned char. + */ +unsigned char fih_delay_random_uchar(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __FAULT_INJECTION_HARDENING_DELAY_RNG_H__ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/ignore.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/ignore.h new file mode 100644 index 0000000..1684c05 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/ignore.h @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_IGNORE_ +#define H_IGNORE_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * These macros prevent the "set but not used" warnings for log writes below + * the log level. + */ + +#define IGN_1(X) ((void)(X)) +#define IGN_2(X, ...) ((void)(X));IGN_1(__VA_ARGS__) +#define IGN_3(X, ...) ((void)(X));IGN_2(__VA_ARGS__) +#define IGN_4(X, ...) ((void)(X));IGN_3(__VA_ARGS__) +#define IGN_5(X, ...) ((void)(X));IGN_4(__VA_ARGS__) +#define IGN_6(X, ...) ((void)(X));IGN_5(__VA_ARGS__) +#define IGN_7(X, ...) ((void)(X));IGN_6(__VA_ARGS__) +#define IGN_8(X, ...) ((void)(X));IGN_7(__VA_ARGS__) +#define IGN_9(X, ...) ((void)(X));IGN_8(__VA_ARGS__) +#define IGN_10(X, ...) ((void)(X));IGN_9(__VA_ARGS__) +#define IGN_11(X, ...) ((void)(X));IGN_10(__VA_ARGS__) +#define IGN_12(X, ...) ((void)(X));IGN_11(__VA_ARGS__) +#define IGN_13(X, ...) ((void)(X));IGN_12(__VA_ARGS__) +#define IGN_14(X, ...) ((void)(X));IGN_13(__VA_ARGS__) +#define IGN_15(X, ...) ((void)(X));IGN_14(__VA_ARGS__) +#define IGN_16(X, ...) ((void)(X));IGN_15(__VA_ARGS__) +#define IGN_17(X, ...) ((void)(X));IGN_16(__VA_ARGS__) +#define IGN_18(X, ...) ((void)(X));IGN_17(__VA_ARGS__) +#define IGN_19(X, ...) ((void)(X));IGN_18(__VA_ARGS__) +#define IGN_20(X, ...) ((void)(X));IGN_19(__VA_ARGS__) + +#define GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \ + _13, _14, _15, _16, _17, _18, _19, _20, NAME, ...) NAME +#define IGNORE(...) \ + GET_MACRO(__VA_ARGS__, IGN_20, IGN_19, IGN_18, IGN_17, IGN_16, IGN_15, \ + IGN_14, IGN_13, IGN_12, IGN_11, IGN_10, IGN_9, IGN_8, IGN_7, \ + IGN_6, IGN_5, IGN_4, IGN_3, IGN_2, IGN_1)(__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/image.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/image.h new file mode 100644 index 0000000..05e0443 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/image.h @@ -0,0 +1,228 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2016-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2023 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_IMAGE_ +#define H_IMAGE_ + +#include +#include +#include "bootutil/fault_injection_hardening.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +struct flash_area; + +#define IMAGE_MAGIC 0x96f3b83d +#define IMAGE_MAGIC_V1 0x96f3b83c +#define IMAGE_MAGIC_NONE 0xffffffff +#define IMAGE_TLV_INFO_MAGIC 0x6907 +#define IMAGE_TLV_PROT_INFO_MAGIC 0x6908 + +#define IMAGE_HEADER_SIZE 32 +#define IMAGE_HASH_LEN 32 /* Size of SHA256 TLV hash */ + +/* + * Image header flags. + */ +#define IMAGE_F_PIC 0x00000001 /* Not supported. */ +#define IMAGE_F_ENCRYPTED_AES128 0x00000004 /* Encrypted using AES128. */ +#define IMAGE_F_ENCRYPTED_AES256 0x00000008 /* Encrypted using AES256. */ +#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */ +/* + * Indicates that this image should be loaded into RAM instead of run + * directly from flash. The address to load should be in the + * ih_load_addr field of the header. + */ +#define IMAGE_F_RAM_LOAD 0x00000020 + +/* + * Indicates that ih_load_addr stores information on flash/ROM address the + * image has been built for. + */ +#define IMAGE_F_ROM_FIXED 0x00000100 + +/* + * Flags that indicate if the image data is compressed + */ +#define IMAGE_F_COMPRESSED_LZMA1 0x00000200 +#define IMAGE_F_COMPRESSED_LZMA2 0x00000400 +#define IMAGE_F_COMPRESSED_ARM_THUMB_FLT 0x00000800 + +/* + * ECSDA224 is with NIST P-224 + * ECSDA256 is with NIST P-256 + */ + +/* + * Image trailer TLV types. + * + * Signature is generated by computing signature over the image hash. + * + * Signature comes in the form of 2 TLVs. + * 1st on identifies the public key which should be used to verify it. + * 2nd one is the actual signature. + */ +#define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */ +#define IMAGE_TLV_PUBKEY 0x02 /* public key */ +#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */ +#define IMAGE_TLV_SHA384 0x11 /* SHA384 of image hdr and body */ +#define IMAGE_TLV_SHA512 0x12 /* SHA512 of image hdr and body */ +#define IMAGE_TLV_RSA2048_PSS 0x20 /* RSA2048 of hash output */ +#define IMAGE_TLV_ECDSA224 0x21 /* ECDSA of hash output - Not supported anymore */ +#define IMAGE_TLV_ECDSA_SIG 0x22 /* ECDSA of hash output */ +#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */ +#define IMAGE_TLV_ED25519 0x24 /* ed25519 of hash output */ +#define IMAGE_TLV_SIG_PURE 0x25 /* Indicator that attached signature has been prepared + * over image rather than its digest. + */ +#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */ +#define IMAGE_TLV_ENC_KW 0x31 /* Key encrypted with AES-KW 128 or 256*/ +#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-EC256 */ +#define IMAGE_TLV_ENC_X25519 0x33 /* Key encrypted with ECIES-X25519 */ +#define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */ +#define IMAGE_TLV_SEC_CNT 0x50 /* security counter */ +#define IMAGE_TLV_BOOT_RECORD 0x60 /* measured boot record */ +/* The following flags relate to compressed images and are for the decompressed image data */ +#define IMAGE_TLV_DECOMP_SIZE 0x70 /* Decompressed image size excluding header/TLVs */ +#define IMAGE_TLV_DECOMP_SHA 0x71 /* + * Decompressed image shaX hash, this field must match + * the format and size of the raw slot (compressed) + * shaX hash + */ +#define IMAGE_TLV_DECOMP_SIGNATURE 0x72 /* + * Decompressed image signature, this field must match + * the format and size of the raw slot (compressed) + * signature + */ + /* + * vendor reserved TLVs at xxA0-xxFF, + * where xx denotes the upper byte + * range. Examples: + * 0x00a0 - 0x00ff + * 0x01a0 - 0x01ff + * 0x02a0 - 0x02ff + * ... + * 0xffa0 - 0xfffe + */ +#define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */ + +struct image_version { + uint8_t iv_major; + uint8_t iv_minor; + uint16_t iv_revision; + uint32_t iv_build_num; +} __packed; + +struct image_dependency { + uint8_t image_id; /* Image index (from 0) */ + uint8_t _pad1; + uint16_t _pad2; + struct image_version image_min_version; /* Indicates at minimum which + * version of firmware must be + * available to satisfy compliance + */ +}; + +/** Image header. All fields are in little endian byte order. */ +struct image_header { + uint32_t ih_magic; + uint32_t ih_load_addr; + uint16_t ih_hdr_size; /* Size of image header (bytes). */ + uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */ + uint32_t ih_img_size; /* Does not include header. */ + uint32_t ih_flags; /* IMAGE_F_[...]. */ + struct image_version ih_ver; + uint32_t _pad1; +} __packed; + +/** Image TLV header. All fields in little endian. */ +struct image_tlv_info { + uint16_t it_magic; + uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */ +} __packed; + +/** Image trailer TLV format. All fields in little endian. */ +struct image_tlv { + uint16_t it_type; /* IMAGE_TLV_[...]. */ + uint16_t it_len; /* Data length (not including TLV header). */ +} __packed; + +#define ENCRYPTIONFLAGS (IMAGE_F_ENCRYPTED_AES128 | IMAGE_F_ENCRYPTED_AES256) +#define IS_ENCRYPTED(hdr) (((hdr)->ih_flags & IMAGE_F_ENCRYPTED_AES128) \ + || ((hdr)->ih_flags & IMAGE_F_ENCRYPTED_AES256)) +#define MUST_DECRYPT(fap, idx, hdr) \ + (flash_area_get_id(fap) == FLASH_AREA_IMAGE_SECONDARY(idx) && IS_ENCRYPTED(hdr)) + +#define COMPRESSIONFLAGS (IMAGE_F_COMPRESSED_LZMA1 | IMAGE_F_COMPRESSED_LZMA2 \ + | IMAGE_F_COMPRESSED_ARM_THUMB_FLT) +#define IS_COMPRESSED(hdr) ((hdr)->ih_flags & COMPRESSIONFLAGS) +#define MUST_DECOMPRESS(fap, idx, hdr) \ + (flash_area_get_id(fap) == FLASH_AREA_IMAGE_SECONDARY(idx) && IS_COMPRESSED(hdr)) + +_Static_assert(sizeof(struct image_header) == IMAGE_HEADER_SIZE, + "struct image_header not required size"); + +struct enc_key_data; +fih_ret bootutil_img_validate(struct enc_key_data *enc_state, int image_index, + struct image_header *hdr, + const struct flash_area *fap, + uint8_t *tmp_buf, uint32_t tmp_buf_sz, + uint8_t *seed, int seed_len, uint8_t *out_hash); + +struct image_tlv_iter { + const struct image_header *hdr; + const struct flash_area *fap; + uint16_t type; + bool prot; + uint32_t prot_end; + uint32_t tlv_off; + uint32_t tlv_end; +}; + +int bootutil_tlv_iter_begin(struct image_tlv_iter *it, + const struct image_header *hdr, + const struct flash_area *fap, uint16_t type, + bool prot); +int bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off, + uint16_t *len, uint16_t *type); +int bootutil_tlv_iter_is_prot(struct image_tlv_iter *it, uint32_t off); + +int32_t bootutil_get_img_security_cnt(struct image_header *hdr, + const struct flash_area *fap, + uint32_t *security_cnt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/mcuboot_status.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/mcuboot_status.h new file mode 100644 index 0000000..b734923 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/mcuboot_status.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022, Laird Connectivity + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_MCUBOOT_STATUS_ +#define H_MCUBOOT_STATUS_ + +/* Enumeration representing the states that MCUboot can be in */ +typedef enum +{ + MCUBOOT_STATUS_STARTUP = 0, + MCUBOOT_STATUS_UPGRADING, + MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND, + MCUBOOT_STATUS_NO_BOOTABLE_IMAGE_FOUND, + MCUBOOT_STATUS_BOOT_FAILED, + MCUBOOT_STATUS_USB_DFU_WAITING, + MCUBOOT_STATUS_USB_DFU_ENTERED, + MCUBOOT_STATUS_USB_DFU_TIMED_OUT, + MCUBOOT_STATUS_SERIAL_DFU_ENTERED, +} mcuboot_status_type_t; + +#if defined(CONFIG_MCUBOOT_ACTION_HOOKS) +extern void mcuboot_status_change(mcuboot_status_type_t status); +#else +#define mcuboot_status_change(_status) do {} while (0) +#endif + +#endif /* H_MCUBOOT_STATUS_ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/ramload.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/ramload.h new file mode 100644 index 0000000..03513b0 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/ramload.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __RAMLOAD_H__ +#define __RAMLOAD_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS +/** + * Provides information about the Executable RAM for a given image ID. + * + * @param image_id Index of the image (from 0). + * @param exec_ram_start Pointer to store the start address of the exec RAM + * @param exec_ram_size Pointer to store the size of the exec RAM + * + * @return 0 on success; nonzero on failure. + */ +int boot_get_image_exec_ram_info(uint32_t image_id, + uint32_t *exec_ram_start, + uint32_t *exec_ram_size); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __RAMLOAD_H__ */ \ No newline at end of file diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/security_cnt.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/security_cnt.h new file mode 100644 index 0000000..e1562d2 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/security_cnt.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SECURITY_CNT_H__ +#define __SECURITY_CNT_H__ + +/** + * @file security_cnt.h + * + * @note The interface must be implemented in a fail-safe way that is + * resistant to asynchronous power failures or it can use hardware + * counters that have this capability, if supported by the platform. + * When a counter incrementation was interrupted it must be able to + * continue the incrementation process or recover the previous consistent + * status of the counters. If the counters have reached a stable status + * (every counter incrementation operation has finished), from that point + * their value cannot decrease due to any kind of power failure. + * + * @note A security counter might be implemented using non-volatile OTP memory + * (i.e. fuses) in which case it is the responsibility of the platform + * code to map each possible security counter values onto the fuse bits + * as the direct usage of counter values can be costly / impractical. + */ + +#include +#include "bootutil/fault_injection_hardening.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialises the security counters. + * + * @return FIH_SUCCESS on success + */ +fih_ret boot_nv_security_counter_init(void); + +/** + * Reads the stored value of a given image's security counter. + * + * @param image_id Index of the image (from 0). + * @param security_cnt Pointer to store the security counter value. + * + * @return FIH_SUCCESS on success + */ +fih_ret boot_nv_security_counter_get(uint32_t image_id, fih_int *security_cnt); + +/** + * Updates the stored value of a given image's security counter with a new + * security counter value if the new one is greater. + * + * @param image_id Index of the image (from 0). + * @param img_security_cnt New security counter value. The new value must be + * between 0 and UINT32_MAX and it must be greater than + * or equal to the current security counter value. + * + * @return 0 on success; nonzero on failure. + */ +int32_t boot_nv_security_counter_update(uint32_t image_id, + uint32_t img_security_cnt); + +#ifdef __cplusplus +} +#endif + +#endif /* __SECURITY_CNT_H__ */ diff --git a/bootloader/mcuboot/boot/bootutil/include/bootutil/sign_key.h b/bootloader/mcuboot/boot/bootutil/include/bootutil/sign_key.h new file mode 100644 index 0000000..a5e81d3 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/include/bootutil/sign_key.h @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __BOOTUTIL_SIGN_KEY_H_ +#define __BOOTUTIL_SIGN_KEY_H_ + +#include +#include + +/* mcuboot_config.h is needed for MCUBOOT_HW_KEY to work */ +#include "mcuboot_config/mcuboot_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MCUBOOT_HW_KEY +struct bootutil_key { + const uint8_t *key; + const unsigned int *len; +}; + +extern const struct bootutil_key bootutil_keys[]; +#else +struct bootutil_key { + uint8_t *key; + unsigned int *len; +}; + +extern struct bootutil_key bootutil_keys[]; + +/** + * Retrieve the hash of the corresponding public key for image authentication. + * + * @param[in] image_index Index of the image to be authenticated. + * @param[out] public_key_hash Buffer to store the key-hash in. + * @param[in,out] key_hash_size As input the size of the buffer. As output + * the actual key-hash length. + * + * @return 0 on success; nonzero on failure. + */ +int boot_retrieve_public_key_hash(uint8_t image_index, + uint8_t *public_key_hash, + size_t *key_hash_size); +#endif /* !MCUBOOT_HW_KEY */ + +extern const int bootutil_key_cnt; + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_SIGN_KEY_H_ */ diff --git a/bootloader/mcuboot/boot/bootutil/pkg.yml b/bootloader/mcuboot/boot/bootutil/pkg.yml new file mode 100644 index 0000000..e919785 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/pkg.yml @@ -0,0 +1,65 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: boot/bootutil +pkg.description: The bootutil library performs most of the functions of a boot loader. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - boot + - bootloader + +pkg.apis: + - bootloader + +pkg.cflags: + - "-DMCUBOOT_MYNEWT=1" + +pkg.cflags.BOOTUTIL_USE_MBED_TLS: + - '-DMBEDTLS_USER_CONFIG_FILE="mbedtls/config_mynewt.h"' + +pkg.deps: + - "@mcuboot/boot/mynewt/mcuboot_config" + - "@apache-mynewt-core/hw/hal" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/defs" + - "@mcuboot/boot/mynewt/flash_map_backend" + +pkg.ign_files.BOOTUTIL_SINGLE_APPLICATION_SLOT: + - "loader.c" + - "swap_scratch.c" + +pkg.deps.BOOTUTIL_USE_MBED_TLS: + - "@apache-mynewt-core/crypto/mbedtls" + +pkg.deps.BOOTUTIL_USE_TINYCRYPT: + - "@mcuboot/ext/tinycrypt/lib" + - "@mcuboot/ext/mbedtls-asn1" + +pkg.deps.BOOTUTIL_SIGN_ED25519: + - "@mcuboot/ext/tinycrypt/lib" + - "@mcuboot/ext/tinycrypt-sha512/lib" + - "@mcuboot/ext/mbedtls-asn1" + - "@mcuboot/ext/fiat" + +pkg.deps.BOOTUTIL_ENCRYPT_X25519: + - "@mcuboot/ext/tinycrypt/lib" + - "@mcuboot/ext/tinycrypt-sha512/lib" + - "@mcuboot/ext/mbedtls-asn1" + - "@mcuboot/ext/fiat" diff --git a/bootloader/mcuboot/boot/bootutil/src/boot_record.c b/bootloader/mcuboot/boot/bootutil/src/boot_record.c new file mode 100644 index 0000000..ccd9e6e --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/boot_record.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2018-2023 Arm Limited + * Copyright (c) 2020 Linaro Limited + * Copyright (c) 2023, Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "mcuboot_config/mcuboot_config.h" +#include "bootutil/crypto/sha.h" + +#if defined(MCUBOOT_MEASURED_BOOT) || defined(MCUBOOT_DATA_SHARING) +#include "bootutil/boot_record.h" +#include "bootutil/boot_status.h" +#include "bootutil_priv.h" +#include "bootutil/image.h" +#include "flash_map_backend/flash_map_backend.h" + +#if !defined(MCUBOOT_CUSTOM_DATA_SHARING_FUNCTION) +/** + * @var shared_memory_init_done + * + * @brief Indicates whether shared memory area was already initialized. + * + */ +static bool shared_memory_init_done; + +/* See in boot_record.h */ +int +boot_add_data_to_shared_area(uint8_t major_type, + uint16_t minor_type, + size_t size, + const uint8_t *data) +{ + struct shared_data_tlv_entry tlv_entry = {0}; + struct shared_boot_data *boot_data; + uint16_t boot_data_size; + uintptr_t tlv_end, offset; + + if (data == NULL) { + return SHARED_MEMORY_GEN_ERROR; + } + + boot_data = (struct shared_boot_data *)MCUBOOT_SHARED_DATA_BASE; + + /* Check whether first time to call this function. If does then initialise + * shared data area. + */ + if (!shared_memory_init_done) { + memset((void *)MCUBOOT_SHARED_DATA_BASE, 0, MCUBOOT_SHARED_DATA_SIZE); + boot_data->header.tlv_magic = SHARED_DATA_TLV_INFO_MAGIC; + boot_data->header.tlv_tot_len = SHARED_DATA_HEADER_SIZE; + shared_memory_init_done = true; + } + + /* Check whether TLV entry is already added. + * Get the boundaries of TLV section + */ + tlv_end = MCUBOOT_SHARED_DATA_BASE + boot_data->header.tlv_tot_len; + offset = MCUBOOT_SHARED_DATA_BASE + SHARED_DATA_HEADER_SIZE; + + /* Iterates over the TLV section looks for the same entry if found then + * returns with error: SHARED_MEMORY_OVERWRITE + */ + while (offset < tlv_end) { + /* Create local copy to avoid unaligned access */ + memcpy(&tlv_entry, (const void *)offset, SHARED_DATA_ENTRY_HEADER_SIZE); + if (GET_MAJOR(tlv_entry.tlv_type) == major_type && + GET_MINOR(tlv_entry.tlv_type) == minor_type) { + return SHARED_MEMORY_OVERWRITE; + } + + offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len); + } + + /* Add TLV entry */ + tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type); + tlv_entry.tlv_len = size; + + if (!boot_u16_safe_add(&boot_data_size, boot_data->header.tlv_tot_len, + SHARED_DATA_ENTRY_SIZE(size))) { + return SHARED_MEMORY_GEN_ERROR; + } + + /* Verify overflow of shared area */ + if (boot_data_size > MCUBOOT_SHARED_DATA_SIZE) { + return SHARED_MEMORY_OVERFLOW; + } + + offset = tlv_end; + memcpy((void *)offset, &tlv_entry, SHARED_DATA_ENTRY_HEADER_SIZE); + + offset += SHARED_DATA_ENTRY_HEADER_SIZE; + memcpy((void *)offset, data, size); + + boot_data->header.tlv_tot_len += SHARED_DATA_ENTRY_SIZE(size); + + return SHARED_MEMORY_OK; +} +#endif /* MCUBOOT_MEASURED_BOOT OR MCUBOOT_DATA_SHARING */ +#endif /* !MCUBOOT_CUSTOM_DATA_SHARING_FUNCTION */ + +#ifdef MCUBOOT_MEASURED_BOOT +/* See in boot_record.h */ +int +boot_save_boot_status(uint8_t sw_module, + const struct image_header *hdr, + const struct flash_area *fap) +{ + + struct image_tlv_iter it; + uint32_t offset; + uint16_t len; + uint16_t type; + uint16_t ias_minor; + size_t record_len = 0; + uint8_t image_hash[IMAGE_HASH_SIZE]; + uint8_t buf[MAX_BOOT_RECORD_SZ]; + bool boot_record_found = false; + bool hash_found = false; + int rc; + + /* Manifest data is concatenated to the end of the image. + * It is encoded in TLV format. + */ + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); + if (rc) { + return -1; + } + + /* Traverse through the TLV area to find the boot record + * and image hash TLVs. + */ + while (true) { + rc = bootutil_tlv_iter_next(&it, &offset, &len, &type); + if (rc < 0) { + return -1; + } else if (rc > 0) { + break; + } + + if (type == IMAGE_TLV_BOOT_RECORD) { + if (len > sizeof(buf)) { + return -1; + } + rc = flash_area_read(fap, offset, buf, len); + if (rc) { + return -1; + } + + record_len = len; + boot_record_found = true; + + } else if (type == EXPECTED_HASH_TLV) { + /* Get the image's hash value from the manifest section. */ + if (len > sizeof(image_hash)) { + return -1; + } + rc = flash_area_read(fap, offset, image_hash, len); + if (rc) { + return -1; + } + + hash_found = true; + + /* The boot record TLV is part of the protected TLV area which is + * located before the other parts of the TLV area (including the + * image hash) so at this point it is okay to break the loop + * as the boot record TLV should have already been found. + */ + break; + } + } + + if (!boot_record_found || !hash_found) { + return -1; + } + + /* Ensure that we have enough in the record for the hash. This + * prevents an underflow in the calculation below. + */ + if (record_len < sizeof(image_hash)) { + return -1; + } + + /* Update the measurement value (hash of the image) data item in the + * boot record. It is always the last item in the structure to make + * it easy to calculate its position. + * The image hash is computed over the image header, the image itself and + * the protected TLV area (which should already include the image hash as + * part of the boot record TLV). For this reason this field has been + * filled with zeros during the image signing process. + */ + offset = record_len - sizeof(image_hash); + /* The size of 'buf' has already been checked when + * the BOOT_RECORD TLV was read, it won't overflow. + */ + memcpy(buf + offset, image_hash, sizeof(image_hash)); + + /* Add the CBOR encoded boot record to the shared data area. */ + ias_minor = SET_IAS_MINOR(sw_module, SW_BOOT_RECORD); + rc = boot_add_data_to_shared_area(TLV_MAJOR_IAS, + ias_minor, + record_len, + buf); + if (rc != SHARED_MEMORY_OK) { + return rc; + } + + return 0; +} +#endif /* MCUBOOT_MEASURED_BOOT */ + +#ifdef MCUBOOT_DATA_SHARING_BOOTINFO +int boot_save_shared_data(const struct image_header *hdr, const struct flash_area *fap, + const uint8_t slot, const struct image_max_size *max_app_sizes) +{ + int rc; + uint8_t image = 0; + +#if defined(MCUBOOT_SINGLE_APPLICATION_SLOT) + uint8_t mode = MCUBOOT_MODE_SINGLE_SLOT; +#elif defined(MCUBOOT_SWAP_USING_SCRATCH) + uint8_t mode = MCUBOOT_MODE_SWAP_USING_SCRATCH; +#elif defined(MCUBOOT_OVERWRITE_ONLY) + uint8_t mode = MCUBOOT_MODE_UPGRADE_ONLY; +#elif defined(MCUBOOT_SWAP_USING_MOVE) + uint8_t mode = MCUBOOT_MODE_SWAP_USING_MOVE; +#elif defined(MCUBOOT_DIRECT_XIP) +#if defined(MCUBOOT_DIRECT_XIP_REVERT) + uint8_t mode = MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT; +#else + uint8_t mode = MCUBOOT_MODE_DIRECT_XIP; +#endif +#elif defined(MCUBOOT_RAM_LOAD) + uint8_t mode = MCUBOOT_MODE_RAM_LOAD; +#elif defined(MCUBOOT_FIRMWARE_LOADER) + uint8_t mode = MCUBOOT_MODE_FIRMWARE_LOADER; +#else +#error "Unknown mcuboot operating mode" +#endif + +#if defined(MCUBOOT_SIGN_RSA) + uint8_t signature_type = MCUBOOT_SIGNATURE_TYPE_RSA; +#elif defined(MCUBOOT_SIGN_EC256) + uint8_t signature_type = MCUBOOT_SIGNATURE_TYPE_ECDSA_P256; +#elif defined(MCUBOOT_SIGN_ED25519) + uint8_t signature_type = MCUBOOT_SIGNATURE_TYPE_ED25519; +#else + uint8_t signature_type = MCUBOOT_SIGNATURE_TYPE_NONE; +#endif + +#if defined(MCUBOOT_SERIAL_RECOVERY) + uint8_t recovery = MCUBOOT_RECOVERY_MODE_SERIAL_RECOVERY; +#elif defined(MCUBOOT_USB_DFU) + uint8_t recovery = MCUBOOT_RECOVERY_MODE_DFU; +#else + uint8_t recovery = MCUBOOT_RECOVERY_MODE_NONE; +#endif + +#if defined(MCUBOOT_VERSION_AVAILABLE) + struct image_version mcuboot_version = { + .iv_major = MCUBOOT_VERSION_MAJOR, + .iv_minor = MCUBOOT_VERSION_MINOR, + +#if defined(MCUBOOT_VERSION_PATCHLEVEL) + .iv_revision = MCUBOOT_VERSION_PATCHLEVEL, +#else + .iv_revision = 0, +#endif + +#if defined(MCUBOOT_VERSION_TWEAK) + .iv_build_num = MCUBOOT_VERSION_TWEAK, +#else + .iv_build_num = 0, +#endif + }; +#endif + + /* Write out all fields */ + rc = boot_add_data_to_shared_area(TLV_MAJOR_BLINFO, BLINFO_MODE, + sizeof(mode), &mode); + + if (!rc) { + rc = boot_add_data_to_shared_area(TLV_MAJOR_BLINFO, + BLINFO_SIGNATURE_TYPE, + sizeof(signature_type), + &signature_type); + } + + if (!rc) { + rc = boot_add_data_to_shared_area(TLV_MAJOR_BLINFO, + BLINFO_RECOVERY, + sizeof(recovery), &recovery); + } + + if (!rc) { + rc = boot_add_data_to_shared_area(TLV_MAJOR_BLINFO, + BLINFO_RUNNING_SLOT, + sizeof(slot), (void *)&slot); + } + +#if defined(MCUBOOT_VERSION_AVAILABLE) + if (!rc) { + rc = boot_add_data_to_shared_area(TLV_MAJOR_BLINFO, + BLINFO_BOOTLOADER_VERSION, + sizeof(mcuboot_version), + (void *)&mcuboot_version); + } +#endif + + while (image < BOOT_IMAGE_NUMBER && !rc) { + if (max_app_sizes[image].calculated == true) { + rc = boot_add_data_to_shared_area(TLV_MAJOR_BLINFO, + (BLINFO_MAX_APPLICATION_SIZE + image), + sizeof(max_app_sizes[image].max_size), + (void *)&max_app_sizes[image].max_size); + + } + + ++image; + } + + return rc; +} +#endif /* MCUBOOT_DATA_SHARING_BOOTINFO */ diff --git a/bootloader/mcuboot/boot/bootutil/src/bootutil_misc.c b/bootloader/mcuboot/boot/bootutil/src/bootutil_misc.c new file mode 100644 index 0000000..4620213 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/bootutil_misc.c @@ -0,0 +1,357 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2020 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include "sysflash/sysflash.h" +#include "flash_map_backend/flash_map_backend.h" + +#include "bootutil/image.h" +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "bootutil_misc.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/fault_injection_hardening.h" +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +/* Currently only used by imgmgr */ +int boot_current_slot; + +/** + * @brief Determine if the data at two memory addresses is equal + * + * @param s1 The first memory region to compare. + * @param s2 The second memory region to compare. + * @param n The amount of bytes to compare. + * + * @note This function does not comply with the specification of memcmp, + * so should not be considered a drop-in replacement. It has no + * constant time execution. The point is to make sure that all the + * bytes are compared and detect if loop was abused and some cycles + * was skipped due to fault injection. + * + * @return FIH_SUCCESS if memory regions are equal, otherwise FIH_FAILURE + */ +#ifdef MCUBOOT_FIH_PROFILE_OFF +inline +fih_ret boot_fih_memequal(const void *s1, const void *s2, size_t n) +{ + return memcmp(s1, s2, n); +} +#else +fih_ret boot_fih_memequal(const void *s1, const void *s2, size_t n) +{ + size_t i; + uint8_t *s1_p = (uint8_t*) s1; + uint8_t *s2_p = (uint8_t*) s2; + FIH_DECLARE(ret, FIH_FAILURE); + + for (i = 0; i < n; i++) { + if (s1_p[i] != s2_p[i]) { + goto out; + } + } + if (i == n) { + ret = FIH_SUCCESS; + } + +out: + FIH_RET(ret); +} +#endif + +/* + * Amount of space used to save information required when doing a swap, + * or while a swap is under progress, but not the status of sector swap + * progress itself. + */ +static inline uint32_t +boot_trailer_info_sz(void) +{ + return ( +#ifdef MCUBOOT_ENC_IMAGES + /* encryption keys */ +# if MCUBOOT_SWAP_SAVE_ENCTLV + BOOT_ENC_TLV_ALIGN_SIZE * 2 + +# else + BOOT_ENC_KEY_ALIGN_SIZE * 2 + +# endif +#endif + /* swap_type + copy_done + image_ok + swap_size */ + BOOT_MAX_ALIGN * 4 + + BOOT_MAGIC_ALIGN_SIZE + ); +} + +/* + * Amount of space used to maintain progress information for a single swap + * operation. + */ +static inline uint32_t +boot_status_entry_sz(uint32_t min_write_sz) +{ + return BOOT_STATUS_STATE_COUNT * min_write_sz; +} + +uint32_t +boot_status_sz(uint32_t min_write_sz) +{ + return BOOT_STATUS_MAX_ENTRIES * boot_status_entry_sz(min_write_sz); +} + +uint32_t +boot_trailer_sz(uint32_t min_write_sz) +{ + return boot_status_sz(min_write_sz) + boot_trailer_info_sz(); +} + +#if MCUBOOT_SWAP_USING_SCRATCH +/* + * Similar to `boot_trailer_sz` but this function returns the space used to + * store status in the scratch partition. The scratch partition only stores + * status during the swap of the last sector from primary/secondary (which + * is the first swap operation) and thus only requires space for one swap. + */ +static uint32_t +boot_scratch_trailer_sz(uint32_t min_write_sz) +{ + return boot_status_entry_sz(min_write_sz) + boot_trailer_info_sz(); +} +#endif + +int +boot_status_entries(int image_index, const struct flash_area *fap) +{ +#if MCUBOOT_SWAP_USING_SCRATCH + if (flash_area_get_id(fap) == FLASH_AREA_IMAGE_SCRATCH) { + return BOOT_STATUS_STATE_COUNT; + } else +#endif + if (flash_area_get_id(fap) == FLASH_AREA_IMAGE_PRIMARY(image_index) || + flash_area_get_id(fap) == FLASH_AREA_IMAGE_SECONDARY(image_index)) { + return BOOT_STATUS_STATE_COUNT * BOOT_STATUS_MAX_ENTRIES; + } + return -1; +} + +uint32_t +boot_status_off(const struct flash_area *fap) +{ + uint32_t off_from_end; + uint32_t elem_sz; + + elem_sz = flash_area_align(fap); + +#if MCUBOOT_SWAP_USING_SCRATCH + if (fap->fa_id == FLASH_AREA_IMAGE_SCRATCH) { + off_from_end = boot_scratch_trailer_sz(elem_sz); + } else { +#endif + off_from_end = boot_trailer_sz(elem_sz); +#if MCUBOOT_SWAP_USING_SCRATCH + } +#endif + + assert(off_from_end <= flash_area_get_size(fap)); + return flash_area_get_size(fap) - off_from_end; +} + +#ifdef MCUBOOT_ENC_IMAGES +static inline uint32_t +boot_enc_key_off(const struct flash_area *fap, uint8_t slot) +{ +#if MCUBOOT_SWAP_SAVE_ENCTLV + return boot_swap_size_off(fap) - ((slot + 1) * BOOT_ENC_TLV_ALIGN_SIZE); +#else + return boot_swap_size_off(fap) - ((slot + 1) * BOOT_ENC_KEY_ALIGN_SIZE); +#endif +} +#endif + +/** + * This functions tries to locate the status area after an aborted swap, + * by looking for the magic in the possible locations. + * + * If the magic is successfully found, a flash_area * is returned and it + * is the responsibility of the called to close it. + * + * @returns 0 on success, -1 on errors + */ +int +boot_find_status(int image_index, const struct flash_area **fap) +{ + uint8_t areas[] = { +#if MCUBOOT_SWAP_USING_SCRATCH + FLASH_AREA_IMAGE_SCRATCH, +#endif + FLASH_AREA_IMAGE_PRIMARY(image_index), + }; + unsigned int i; + + /* + * In the middle a swap, tries to locate the area that is currently + * storing a valid magic, first on the primary slot, then on scratch. + * Both "slots" can end up being temporary storage for a swap and it + * is assumed that if magic is valid then other metadata is too, + * because magic is always written in the last step. + */ + + for (i = 0; i < sizeof(areas) / sizeof(areas[0]); i++) { + uint8_t magic[BOOT_MAGIC_SZ]; + + if (flash_area_open(areas[i], fap)) { + break; + } + + if (flash_area_read(*fap, boot_magic_off(*fap), magic, BOOT_MAGIC_SZ)) { + flash_area_close(*fap); + break; + } + + if (BOOT_MAGIC_GOOD == boot_magic_decode(magic)) { + return 0; + } + + flash_area_close(*fap); + } + + /* If we got here, no magic was found */ + fap = NULL; + return -1; +} + +int +boot_read_swap_size(const struct flash_area *fap, uint32_t *swap_size) +{ + uint32_t off; + int rc; + + off = boot_swap_size_off(fap); + rc = flash_area_read(fap, off, swap_size, sizeof *swap_size); + + return rc; +} + +#ifdef MCUBOOT_ENC_IMAGES +int +boot_read_enc_key(const struct flash_area *fap, uint8_t slot, struct boot_status *bs) +{ + uint32_t off; +#if MCUBOOT_SWAP_SAVE_ENCTLV + uint32_t i; +#endif + int rc; + + off = boot_enc_key_off(fap, slot); +#if MCUBOOT_SWAP_SAVE_ENCTLV + rc = flash_area_read(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE); + if (rc == 0) { + for (i = 0; i < BOOT_ENC_TLV_ALIGN_SIZE; i++) { + if (bs->enctlv[slot][i] != 0xff) { + break; + } + } + /* Only try to decrypt non-erased TLV metadata */ + if (i != BOOT_ENC_TLV_ALIGN_SIZE) { + rc = boot_decrypt_key(bs->enctlv[slot], bs->enckey[slot]); + } + } +#else + rc = flash_area_read(fap, off, bs->enckey[slot], BOOT_ENC_KEY_ALIGN_SIZE); +#endif + + return rc; +} +#endif + +int +boot_write_swap_size(const struct flash_area *fap, uint32_t swap_size) +{ + uint32_t off; + + off = boot_swap_size_off(fap); + BOOT_LOG_DBG("writing swap_size; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)flash_area_get_off(fap) + off); + return boot_write_trailer(fap, off, (const uint8_t *) &swap_size, 4); +} + +#ifdef MCUBOOT_ENC_IMAGES +int +boot_write_enc_key(const struct flash_area *fap, uint8_t slot, + const struct boot_status *bs) +{ + uint32_t off; + int rc; + + off = boot_enc_key_off(fap, slot); + BOOT_LOG_DBG("writing enc_key; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)flash_area_get_off(fap) + off); +#if MCUBOOT_SWAP_SAVE_ENCTLV + rc = flash_area_write(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE); +#else + rc = flash_area_write(fap, off, bs->enckey[slot], BOOT_ENC_KEY_ALIGN_SIZE); +#endif + if (rc != 0) { + return BOOT_EFLASH; + } + + return 0; +} +#endif + +uint32_t bootutil_max_image_size(const struct flash_area *fap) +{ +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \ + defined(MCUBOOT_FIRMWARE_LOADER) + return boot_status_off(fap); +#elif defined(MCUBOOT_SWAP_USING_MOVE) + struct flash_sector sector; + /* get the last sector offset */ + int rc = flash_area_get_sector(fap, boot_status_off(fap), §or); + if (rc) { + BOOT_LOG_ERR("Unable to determine flash sector of the image trailer"); + return 0; /* Returning of zero here should cause any check which uses + * this value to fail. + */ + } + return flash_sector_get_off(§or); +#elif defined(MCUBOOT_OVERWRITE_ONLY) + return boot_swap_info_off(fap); +#elif defined(MCUBOOT_DIRECT_XIP) + return boot_swap_info_off(fap); +#elif defined(MCUBOOT_RAM_LOAD) + return boot_swap_info_off(fap); +#endif +} diff --git a/bootloader/mcuboot/boot/bootutil/src/bootutil_misc.h b/bootloader/mcuboot/boot/bootutil/src/bootutil_misc.h new file mode 100644 index 0000000..1fcaabd --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/bootutil_misc.h @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Nordic Semiconductor ASA + * + */ +#ifndef H_BOOTUTIL_MISC_ +#define H_BOOTUTIL_MISC_ + +#include +#include +#include + +#include "bootutil/bootutil_public.h" +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +static int +boot_magic_decode(const uint8_t *magic) +{ + if (memcmp(magic, BOOT_IMG_MAGIC, BOOT_MAGIC_SZ) == 0) { + return BOOT_MAGIC_GOOD; + } + return BOOT_MAGIC_BAD; +} + +static inline uint32_t +boot_magic_off(const struct flash_area *fap) +{ + return flash_area_get_size(fap) - BOOT_MAGIC_SZ; +} + +static inline uint32_t +boot_image_ok_off(const struct flash_area *fap) +{ + return ALIGN_DOWN(boot_magic_off(fap) - BOOT_MAX_ALIGN, BOOT_MAX_ALIGN); +} + +static inline uint32_t +boot_copy_done_off(const struct flash_area *fap) +{ + return boot_image_ok_off(fap) - BOOT_MAX_ALIGN; +} + +static inline uint32_t +boot_swap_size_off(const struct flash_area *fap) +{ + return boot_swap_info_off(fap) - BOOT_MAX_ALIGN; +} + +#endif /* H_BOOTUTIL_MISC_ */ diff --git a/bootloader/mcuboot/boot/bootutil/src/bootutil_priv.h b/bootloader/mcuboot/boot/bootutil/src/bootutil_priv.h new file mode 100644 index 0000000..c23f616 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/bootutil_priv.h @@ -0,0 +1,486 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2020 Linaro LTD + * Copyright (c) 2017-2019 JUUL Labs + * Copyright (c) 2019-2021 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BOOTUTIL_PRIV_ +#define H_BOOTUTIL_PRIV_ + +#include + +#include "sysflash/sysflash.h" + +#include + +#include "bootutil/bootutil.h" +#include "bootutil/image.h" +#include "bootutil/fault_injection_hardening.h" +#include "mcuboot_config/mcuboot_config.h" + +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct flash_area; + +#define BOOT_TMPBUF_SZ 256 + +/** Number of image slots in flash; currently limited to two. */ +#define BOOT_NUM_SLOTS 2 + +#if (defined(MCUBOOT_OVERWRITE_ONLY) + \ + defined(MCUBOOT_SWAP_USING_MOVE) + \ + defined(MCUBOOT_DIRECT_XIP) + \ + defined(MCUBOOT_RAM_LOAD) + \ + defined(MCUBOOT_FIRMWARE_LOADER) + \ + defined(MCUBOOT_SWAP_USING_SCRATCH)) > 1 +#error "Please enable only one of MCUBOOT_OVERWRITE_ONLY, MCUBOOT_SWAP_USING_MOVE, MCUBOOT_DIRECT_XIP, MCUBOOT_RAM_LOAD or MCUBOOT_FIRMWARE_LOADER" +#endif + +#if !defined(MCUBOOT_DIRECT_XIP) && \ + defined(MCUBOOT_DIRECT_XIP_REVERT) +#error "MCUBOOT_DIRECT_XIP_REVERT cannot be enabled unless MCUBOOT_DIRECT_XIP is used" +#endif + +#if !defined(MCUBOOT_OVERWRITE_ONLY) && \ + !defined(MCUBOOT_SWAP_USING_MOVE) && \ + !defined(MCUBOOT_DIRECT_XIP) && \ + !defined(MCUBOOT_RAM_LOAD) && \ + !defined(MCUBOOT_SINGLE_APPLICATION_SLOT) && \ + !defined(MCUBOOT_FIRMWARE_LOADER) +#define MCUBOOT_SWAP_USING_SCRATCH 1 +#endif + +#define BOOT_STATUS_OP_MOVE 1 +#define BOOT_STATUS_OP_SWAP 2 + +/* + * Maintain state of copy progress. + */ +struct boot_status { + uint32_t idx; /* Which area we're operating on */ + uint8_t state; /* Which part of the swapping process are we at */ + uint8_t op; /* What operation are we performing? */ + uint8_t use_scratch; /* Are status bytes ever written to scratch? */ + uint8_t swap_type; /* The type of swap in effect */ + uint32_t swap_size; /* Total size of swapped image */ +#ifdef MCUBOOT_ENC_IMAGES + uint8_t enckey[BOOT_NUM_SLOTS][BOOT_ENC_KEY_ALIGN_SIZE]; +#if MCUBOOT_SWAP_SAVE_ENCTLV + uint8_t enctlv[BOOT_NUM_SLOTS][BOOT_ENC_TLV_ALIGN_SIZE]; +#endif +#endif + int source; /* Which slot contains swap status metadata */ +}; + +#define BOOT_STATUS_IDX_0 1 + +#define BOOT_STATUS_STATE_0 1 +#define BOOT_STATUS_STATE_1 2 +#define BOOT_STATUS_STATE_2 3 + +/** + * End-of-image slot structure. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~ ~ + * ~ Swap status (BOOT_MAX_IMG_SECTORS * min-write-size * 3) ~ + * ~ ~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Encryption key 0 (16 octets) [*] | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0xff padding as needed | + * | (BOOT_MAX_ALIGN minus 16 octets from Encryption key 0) [*] | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Encryption key 1 (16 octets) [*] | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0xff padding as needed | + * | (BOOT_MAX_ALIGN minus 16 octets from Encryption key 1) [*] | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Swap size (4 octets) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0xff padding as needed | + * | (BOOT_MAX_ALIGN minus 4 octets from Swap size) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Swap info | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Copy done | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Image OK | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0xff padding as needed | + * | (BOOT_MAX_ALIGN minus 16 octets from MAGIC) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MAGIC (16 octets) | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * [*]: Only present if the encryption option is enabled + * (`MCUBOOT_ENC_IMAGES`). + */ + +union boot_img_magic_t +{ + struct { + uint16_t align; + uint8_t magic[14]; + }; + uint8_t val[16]; +}; + +extern const union boot_img_magic_t boot_img_magic; + +#define BOOT_IMG_MAGIC (boot_img_magic.val) + +#if BOOT_MAX_ALIGN == 8 +#define BOOT_IMG_ALIGN (BOOT_MAX_ALIGN) +#else +#define BOOT_IMG_ALIGN (boot_img_magic.align) +#endif + +_Static_assert(sizeof(boot_img_magic) == BOOT_MAGIC_SZ, "Invalid size for image magic"); + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +#define ARE_SLOTS_EQUIVALENT() 0 +#else +#define ARE_SLOTS_EQUIVALENT() 1 + +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_ENC_IMAGES) +#error "Image encryption (MCUBOOT_ENC_IMAGES) is not supported when MCUBOOT_DIRECT_XIP is selected." +#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_ENC_IMAGES */ +#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +#define BOOT_MAX_IMG_SECTORS MCUBOOT_MAX_IMG_SECTORS + +#define BOOT_LOG_IMAGE_INFO(slot, hdr) \ + BOOT_LOG_INF("%-9s slot: version=%u.%u.%u+%u", \ + ((slot) == BOOT_PRIMARY_SLOT) ? "Primary" : "Secondary", \ + (hdr)->ih_ver.iv_major, \ + (hdr)->ih_ver.iv_minor, \ + (hdr)->ih_ver.iv_revision, \ + (hdr)->ih_ver.iv_build_num) + +#if MCUBOOT_SWAP_USING_MOVE +#define BOOT_STATUS_MOVE_STATE_COUNT 1 +#define BOOT_STATUS_SWAP_STATE_COUNT 2 +#define BOOT_STATUS_STATE_COUNT (BOOT_STATUS_MOVE_STATE_COUNT + BOOT_STATUS_SWAP_STATE_COUNT) +#else +#define BOOT_STATUS_STATE_COUNT 3 +#endif + +/** Maximum number of image sectors supported by the bootloader. */ +#define BOOT_STATUS_MAX_ENTRIES BOOT_MAX_IMG_SECTORS + +#define BOOT_PRIMARY_SLOT 0 +#define BOOT_SECONDARY_SLOT 1 + +#define BOOT_STATUS_SOURCE_NONE 0 +#define BOOT_STATUS_SOURCE_SCRATCH 1 +#define BOOT_STATUS_SOURCE_PRIMARY_SLOT 2 + +/** + * Compatibility shim for flash sector type. + * + * This can be deleted when flash_area_to_sectors() is removed. + */ +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS +typedef struct flash_sector boot_sector_t; +#else +typedef struct flash_area boot_sector_t; +#endif + +/** Private state maintained during boot. */ +struct boot_loader_state { + struct { + struct image_header hdr; + const struct flash_area *area; + boot_sector_t *sectors; + uint32_t num_sectors; + } imgs[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS]; + +#if MCUBOOT_SWAP_USING_SCRATCH + struct { + const struct flash_area *area; + boot_sector_t *sectors; + uint32_t num_sectors; + } scratch; +#endif + + uint8_t swap_type[BOOT_IMAGE_NUMBER]; + uint32_t write_sz; + +#if defined(MCUBOOT_ENC_IMAGES) + struct enc_key_data enc[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS]; +#endif + +#if (BOOT_IMAGE_NUMBER > 1) + uint8_t curr_img_idx; + bool img_mask[BOOT_IMAGE_NUMBER]; +#endif + +#if defined(MCUBOOT_DIRECT_XIP) || defined(MCUBOOT_RAM_LOAD) + struct slot_usage_t { + /* Index of the slot chosen to be loaded */ + uint32_t active_slot; + bool slot_available[BOOT_NUM_SLOTS]; +#if defined(MCUBOOT_RAM_LOAD) + /* Image destination and size for the active slot */ + uint32_t img_dst; + uint32_t img_sz; +#elif defined(MCUBOOT_DIRECT_XIP_REVERT) + /* Swap status for the active slot */ + struct boot_swap_state swap_state; +#endif + } slot_usage[BOOT_IMAGE_NUMBER]; +#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ +}; + +fih_ret bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, + size_t slen, uint8_t key_id); + +fih_ret bootutil_verify_img(const uint8_t *img, uint32_t size, + uint8_t *sig, size_t slen, uint8_t key_id); + +fih_ret boot_fih_memequal(const void *s1, const void *s2, size_t n); + +int boot_find_status(int image_index, const struct flash_area **fap); +int boot_magic_compatible_check(uint8_t tbl_val, uint8_t val); +uint32_t boot_status_sz(uint32_t min_write_sz); +uint32_t boot_trailer_sz(uint32_t min_write_sz); +int boot_status_entries(int image_index, const struct flash_area *fap); +uint32_t boot_status_off(const struct flash_area *fap); +int boot_read_swap_state(const struct flash_area *fap, + struct boot_swap_state *state); +int boot_read_swap_state_by_id(int flash_area_id, + struct boot_swap_state *state); +int boot_write_magic(const struct flash_area *fap); +int boot_write_status(const struct boot_loader_state *state, struct boot_status *bs); +int boot_write_copy_done(const struct flash_area *fap); +int boot_write_image_ok(const struct flash_area *fap); +int boot_write_swap_info(const struct flash_area *fap, uint8_t swap_type, + uint8_t image_num); +int boot_write_swap_size(const struct flash_area *fap, uint32_t swap_size); +int boot_write_trailer(const struct flash_area *fap, uint32_t off, + const uint8_t *inbuf, uint8_t inlen); +int boot_write_trailer_flag(const struct flash_area *fap, uint32_t off, + uint8_t flag_val); +int boot_read_swap_size(const struct flash_area *fap, uint32_t *swap_size); +int boot_slots_compatible(struct boot_loader_state *state); +uint32_t boot_status_internal_off(const struct boot_status *bs, int elem_sz); +int boot_read_image_header(struct boot_loader_state *state, int slot, + struct image_header *out_hdr, struct boot_status *bs); +int boot_copy_region(struct boot_loader_state *state, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, + uint32_t off_src, uint32_t off_dst, uint32_t sz); +int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz); +bool boot_status_is_reset(const struct boot_status *bs); + +#ifdef MCUBOOT_ENC_IMAGES +int boot_write_enc_key(const struct flash_area *fap, uint8_t slot, + const struct boot_status *bs); +int boot_read_enc_key(const struct flash_area *fap, uint8_t slot, + struct boot_status *bs); +#endif + +/** + * Checks that a buffer is erased according to what the erase value for the + * flash device provided in `flash_area` is. + * + * @returns true if the buffer is erased; false if any of the bytes is not + * erased, or when buffer is NULL, or when len == 0. + */ +bool bootutil_buffer_is_erased(const struct flash_area *area, + const void *buffer, size_t len); + +/** + * Safe (non-overflowing) uint32_t addition. Returns true, and stores + * the result in *dest if it can be done without overflow. Otherwise, + * returns false. + */ +static inline bool boot_u32_safe_add(uint32_t *dest, uint32_t a, uint32_t b) +{ + /* + * "a + b <= UINT32_MAX", subtract 'b' from both sides to avoid + * the overflow. + */ + if (a > UINT32_MAX - b) { + return false; + } else { + *dest = a + b; + return true; + } +} + +/** + * Safe (non-overflowing) uint16_t addition. Returns true, and stores + * the result in *dest if it can be done without overflow. Otherwise, + * returns false. + */ +static inline bool boot_u16_safe_add(uint16_t *dest, uint16_t a, uint16_t b) +{ + uint32_t tmp = a + b; + if (tmp > UINT16_MAX) { + return false; + } else { + *dest = tmp; + return true; + } +} + +/* + * Accessors for the contents of struct boot_loader_state. + */ + +/* These are macros so they can be used as lvalues. */ +#if (BOOT_IMAGE_NUMBER > 1) +#define BOOT_CURR_IMG(state) ((state)->curr_img_idx) +#else +#define BOOT_CURR_IMG(state) 0 +#endif +#ifdef MCUBOOT_ENC_IMAGES +#define BOOT_CURR_ENC(state) ((state)->enc[BOOT_CURR_IMG(state)]) +#else +#define BOOT_CURR_ENC(state) NULL +#endif +#define BOOT_IMG(state, slot) ((state)->imgs[BOOT_CURR_IMG(state)][(slot)]) +#define BOOT_IMG_AREA(state, slot) (BOOT_IMG(state, slot).area) +#define BOOT_WRITE_SZ(state) ((state)->write_sz) +#define BOOT_SWAP_TYPE(state) ((state)->swap_type[BOOT_CURR_IMG(state)]) +#define BOOT_TLV_OFF(hdr) ((hdr)->ih_hdr_size + (hdr)->ih_img_size) + +#define BOOT_IS_UPGRADE(swap_type) \ + (((swap_type) == BOOT_SWAP_TYPE_TEST) || \ + ((swap_type) == BOOT_SWAP_TYPE_REVERT) || \ + ((swap_type) == BOOT_SWAP_TYPE_PERM)) + +static inline struct image_header* +boot_img_hdr(struct boot_loader_state *state, size_t slot) +{ + return &BOOT_IMG(state, slot).hdr; +} + +static inline size_t +boot_img_num_sectors(const struct boot_loader_state *state, size_t slot) +{ + return BOOT_IMG(state, slot).num_sectors; +} + +/* + * Offset of the slot from the beginning of the flash device. + */ +static inline uint32_t +boot_img_slot_off(struct boot_loader_state *state, size_t slot) +{ + return flash_area_get_off(BOOT_IMG(state, slot).area); +} + +#ifndef MCUBOOT_USE_FLASH_AREA_GET_SECTORS + +static inline size_t +boot_img_sector_size(const struct boot_loader_state *state, + size_t slot, size_t sector) +{ + return flash_area_get_size(&BOOT_IMG(state, slot).sectors[sector]); +} + +/* + * Offset of the sector from the beginning of the image, NOT the flash + * device. + */ +static inline uint32_t +boot_img_sector_off(const struct boot_loader_state *state, size_t slot, + size_t sector) +{ + return flash_area_get_off(&BOOT_IMG(state, slot).sectors[sector]) - + flash_area_get_off(&BOOT_IMG(state, slot).sectors[0]); +} + +#else /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ + +static inline size_t +boot_img_sector_size(const struct boot_loader_state *state, + size_t slot, size_t sector) +{ + return flash_sector_get_size(&BOOT_IMG(state, slot).sectors[sector]); +} + +static inline uint32_t +boot_img_sector_off(const struct boot_loader_state *state, size_t slot, + size_t sector) +{ + return flash_sector_get_off(&BOOT_IMG(state, slot).sectors[sector]) - + flash_sector_get_off(&BOOT_IMG(state, slot).sectors[0]); +} + +#endif /* !defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ + +#ifdef MCUBOOT_RAM_LOAD +# ifdef __BOOTSIM__ + +/* Query for the layout of a RAM buffer appropriate for holding the + * image. This will be per-test-thread, and therefore must be queried + * through this call. */ +struct bootsim_ram_info { + uint32_t start; + uint32_t size; + uintptr_t base; +}; +struct bootsim_ram_info *bootsim_get_ram_info(void); + +#define IMAGE_GET_FIELD(field) (bootsim_get_ram_info()->field) +#define IMAGE_RAM_BASE IMAGE_GET_FIELD(base) +#define IMAGE_EXECUTABLE_RAM_START IMAGE_GET_FIELD(start) +#define IMAGE_EXECUTABLE_RAM_SIZE IMAGE_GET_FIELD(size) + +# else +# define IMAGE_RAM_BASE ((uintptr_t)0) +# endif + +#define LOAD_IMAGE_DATA(hdr, fap, start, output, size) \ + (memcpy((output),(void*)(IMAGE_RAM_BASE + (hdr)->ih_load_addr + (start)), \ + (size)), 0) +#else +#define IMAGE_RAM_BASE ((uintptr_t)0) + +#define LOAD_IMAGE_DATA(hdr, fap, start, output, size) \ + (flash_area_read((fap), (start), (output), (size))) +#endif /* MCUBOOT_RAM_LOAD */ + +uint32_t bootutil_max_image_size(const struct flash_area *fap); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/src/bootutil_public.c b/bootloader/mcuboot/boot/bootutil/src/bootutil_public.c new file mode 100644 index 0000000..afa601c --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/bootutil_public.c @@ -0,0 +1,762 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2023 Arm Limited + * Copyright (c) 2020-2023 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file + * @brief Public MCUBoot interface API implementation + * + * This file contains API implementation which can be combined with + * the application in order to interact with the MCUBoot bootloader. + * This file contains shared code-base betwen MCUBoot and the application + * which controls DFU process. + */ + +#include +#include +#include + +#include "sysflash/sysflash.h" +#include "flash_map_backend/flash_map_backend.h" + +#include "bootutil/image.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/bootutil_log.h" + +#include "bootutil/boot_public_hooks.h" +#include "bootutil_priv.h" +#include "bootutil_misc.h" + +#ifdef CONFIG_MCUBOOT +BOOT_LOG_MODULE_DECLARE(mcuboot); +#else +BOOT_LOG_MODULE_REGISTER(mcuboot_util); +#endif + +#if BOOT_MAX_ALIGN == 8 +const union boot_img_magic_t boot_img_magic = { + .val = { + 0x77, 0xc2, 0x95, 0xf3, + 0x60, 0xd2, 0xef, 0x7f, + 0x35, 0x52, 0x50, 0x0f, + 0x2c, 0xb6, 0x79, 0x80 + } +}; +#else +const union boot_img_magic_t boot_img_magic = { + .align = BOOT_MAX_ALIGN, + .magic = { + 0x2d, 0xe1, + 0x5d, 0x29, 0x41, 0x0b, + 0x8d, 0x77, 0x67, 0x9c, + 0x11, 0x0f, 0x1f, 0x8a + } +}; +#endif + +struct boot_swap_table { + uint8_t magic_primary_slot; + uint8_t magic_secondary_slot; + uint8_t image_ok_primary_slot; + uint8_t image_ok_secondary_slot; + uint8_t copy_done_primary_slot; + + uint8_t swap_type; +}; + +/** + * This set of tables maps image trailer contents to swap operation type. + * When searching for a match, these tables must be iterated sequentially. + * + * NOTE: the table order is very important. The settings in the secondary + * slot always are priority to the primary slot and should be located + * earlier in the table. + * + * The table lists only states where there is action needs to be taken by + * the bootloader, as in starting/finishing a swap operation. + */ +static const struct boot_swap_table boot_swap_tables[] = { + { + .magic_primary_slot = BOOT_MAGIC_ANY, + .magic_secondary_slot = BOOT_MAGIC_GOOD, + .image_ok_primary_slot = BOOT_FLAG_ANY, + .image_ok_secondary_slot = BOOT_FLAG_UNSET, + .copy_done_primary_slot = BOOT_FLAG_ANY, + .swap_type = BOOT_SWAP_TYPE_TEST, + }, + { + .magic_primary_slot = BOOT_MAGIC_ANY, + .magic_secondary_slot = BOOT_MAGIC_GOOD, + .image_ok_primary_slot = BOOT_FLAG_ANY, + .image_ok_secondary_slot = BOOT_FLAG_SET, + .copy_done_primary_slot = BOOT_FLAG_ANY, + .swap_type = BOOT_SWAP_TYPE_PERM, + }, + { + .magic_primary_slot = BOOT_MAGIC_GOOD, + .magic_secondary_slot = BOOT_MAGIC_UNSET, + .image_ok_primary_slot = BOOT_FLAG_UNSET, + .image_ok_secondary_slot = BOOT_FLAG_ANY, + .copy_done_primary_slot = BOOT_FLAG_SET, + .swap_type = BOOT_SWAP_TYPE_REVERT, + }, +}; + +#define BOOT_SWAP_TABLES_COUNT \ + (sizeof boot_swap_tables / sizeof boot_swap_tables[0]) + +static int +boot_flag_decode(uint8_t flag) +{ + if (flag != BOOT_FLAG_SET) { + return BOOT_FLAG_BAD; + } + return BOOT_FLAG_SET; +} + +uint32_t +boot_swap_info_off(const struct flash_area *fap) +{ + return boot_copy_done_off(fap) - BOOT_MAX_ALIGN; +} + +/** + * Determines if a status source table is satisfied by the specified magic + * code. + * + * @param tbl_val A magic field from a status source table. + * @param val The magic value in a trailer, encoded as a + * BOOT_MAGIC_[...]. + * + * @return 1 if the two values are compatible; + * 0 otherwise. + */ +int +boot_magic_compatible_check(uint8_t tbl_val, uint8_t val) +{ + switch (tbl_val) { + case BOOT_MAGIC_ANY: + return 1; + + case BOOT_MAGIC_NOTGOOD: + return val != BOOT_MAGIC_GOOD; + + default: + return tbl_val == val; + } +} + +bool bootutil_buffer_is_erased(const struct flash_area *area, + const void *buffer, size_t len) +{ + size_t i; + uint8_t *u8b; + uint8_t erased_val; + + if (buffer == NULL || len == 0) { + return false; + } + + erased_val = flash_area_erased_val(area); + for (i = 0, u8b = (uint8_t *)buffer; i < len; i++) { + if (u8b[i] != erased_val) { + return false; + } + } + + return true; +} + +static int +boot_read_flag(const struct flash_area *fap, uint8_t *flag, uint32_t off) +{ + int rc; + + rc = flash_area_read(fap, off, flag, sizeof *flag); + if (rc < 0) { + return BOOT_EFLASH; + } + if (bootutil_buffer_is_erased(fap, flag, sizeof *flag)) { + *flag = BOOT_FLAG_UNSET; + } else { + *flag = boot_flag_decode(*flag); + } + + return 0; +} + +static inline int +boot_read_copy_done(const struct flash_area *fap, uint8_t *copy_done) +{ + return boot_read_flag(fap, copy_done, boot_copy_done_off(fap)); +} + + +int +boot_read_swap_state(const struct flash_area *fap, + struct boot_swap_state *state) +{ + uint8_t magic[BOOT_MAGIC_SZ]; + uint32_t off; + uint8_t swap_info; + int rc; + + off = boot_magic_off(fap); + rc = flash_area_read(fap, off, magic, BOOT_MAGIC_SZ); + if (rc < 0) { + return BOOT_EFLASH; + } + if (bootutil_buffer_is_erased(fap, magic, BOOT_MAGIC_SZ)) { + state->magic = BOOT_MAGIC_UNSET; + } else { + state->magic = boot_magic_decode(magic); + } + + off = boot_swap_info_off(fap); + rc = flash_area_read(fap, off, &swap_info, sizeof swap_info); + if (rc < 0) { + return BOOT_EFLASH; + } + + /* Extract the swap type and image number */ + state->swap_type = BOOT_GET_SWAP_TYPE(swap_info); + state->image_num = BOOT_GET_IMAGE_NUM(swap_info); + + if (bootutil_buffer_is_erased(fap, &swap_info, sizeof swap_info) || + state->swap_type > BOOT_SWAP_TYPE_REVERT) { + state->swap_type = BOOT_SWAP_TYPE_NONE; + state->image_num = 0; + } + + rc = boot_read_copy_done(fap, &state->copy_done); + if (rc) { + return BOOT_EFLASH; + } + + return boot_read_image_ok(fap, &state->image_ok); +} + +int +boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state) +{ + const struct flash_area *fap; + int rc; + + rc = flash_area_open(flash_area_id, &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_read_swap_state(fap, state); + flash_area_close(fap); + return rc; +} + +int +boot_write_magic(const struct flash_area *fap) +{ + uint32_t off; + uint32_t pad_off; + int rc; + uint8_t magic[BOOT_MAGIC_ALIGN_SIZE]; + uint8_t erased_val; + + off = boot_magic_off(fap); + + /* image_trailer structure was modified with additional padding such that + * the pad+magic ends up in a flash minimum write region. The address + * returned by boot_magic_off() is the start of magic which is not the + * start of the flash write boundary and thus writes to the magic will fail. + * To account for this change, write to magic is first padded with 0xFF + * before writing to the trailer. + */ + pad_off = ALIGN_DOWN(off, BOOT_MAX_ALIGN); + + erased_val = flash_area_erased_val(fap); + + memset(&magic[0], erased_val, sizeof(magic)); + memcpy(&magic[BOOT_MAGIC_ALIGN_SIZE - BOOT_MAGIC_SZ], BOOT_IMG_MAGIC, BOOT_MAGIC_SZ); + + BOOT_LOG_DBG("writing magic; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)(flash_area_get_off(fap) + off)); + rc = flash_area_write(fap, pad_off, &magic[0], BOOT_MAGIC_ALIGN_SIZE); + + if (rc != 0) { + return BOOT_EFLASH; + } + + return 0; +} + +/** + * Write trailer data; status bytes, swap_size, etc + * + * @returns 0 on success, != 0 on error. + */ +int +boot_write_trailer(const struct flash_area *fap, uint32_t off, + const uint8_t *inbuf, uint8_t inlen) +{ + uint8_t buf[BOOT_MAX_ALIGN]; + uint8_t erased_val; + uint32_t align; + int rc; + + align = flash_area_align(fap); + align = ALIGN_UP(inlen, align); + if (align > BOOT_MAX_ALIGN) { + return -1; + } + erased_val = flash_area_erased_val(fap); + + memcpy(buf, inbuf, inlen); + memset(&buf[inlen], erased_val, align - inlen); + + rc = flash_area_write(fap, off, buf, align); + if (rc != 0) { + return BOOT_EFLASH; + } + + return 0; +} + +int +boot_write_trailer_flag(const struct flash_area *fap, uint32_t off, + uint8_t flag_val) +{ + const uint8_t buf[1] = { flag_val }; + return boot_write_trailer(fap, off, buf, 1); +} + +int +boot_write_image_ok(const struct flash_area *fap) +{ + uint32_t off; + + off = boot_image_ok_off(fap); + BOOT_LOG_DBG("writing image_ok; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)(flash_area_get_off(fap) + off)); + return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET); +} + +int +boot_read_image_ok(const struct flash_area *fap, uint8_t *image_ok) +{ + return boot_read_flag(fap, image_ok, boot_image_ok_off(fap)); +} + +/** + * Writes the specified value to the `swap-type` field of an image trailer. + * This value is persisted so that the boot loader knows what swap operation to + * resume in case of an unexpected reset. + */ +int +boot_write_swap_info(const struct flash_area *fap, uint8_t swap_type, + uint8_t image_num) +{ + uint32_t off; + uint8_t swap_info; + + BOOT_SET_SWAP_INFO(swap_info, image_num, swap_type); + off = boot_swap_info_off(fap); + BOOT_LOG_DBG("writing swap_info; fa_id=%d off=0x%lx (0x%lx), swap_type=0x%x" + " image_num=0x%x", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)(flash_area_get_off(fap) + off), + swap_type, image_num); + return boot_write_trailer(fap, off, (const uint8_t *) &swap_info, 1); +} + +int +boot_swap_type_multi(int image_index) +{ + const struct boot_swap_table *table; + struct boot_swap_state primary_slot; + struct boot_swap_state secondary_slot; + int rc; + size_t i; + + rc = BOOT_HOOK_CALL(boot_read_swap_state_primary_slot_hook, + BOOT_HOOK_REGULAR, image_index, &primary_slot); + if (rc == BOOT_HOOK_REGULAR) + { + rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index), + &primary_slot); + } + if (rc) { + return BOOT_SWAP_TYPE_PANIC; + } + + rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index), + &secondary_slot); + if (rc == BOOT_EFLASH) { + BOOT_LOG_INF("Secondary image of image pair (%d.) " + "is unreachable. Treat it as empty", image_index); + secondary_slot.magic = BOOT_MAGIC_UNSET; + secondary_slot.swap_type = BOOT_SWAP_TYPE_NONE; + secondary_slot.copy_done = BOOT_FLAG_UNSET; + secondary_slot.image_ok = BOOT_FLAG_UNSET; + secondary_slot.image_num = 0; + } else if (rc) { + return BOOT_SWAP_TYPE_PANIC; + } + + for (i = 0; i < BOOT_SWAP_TABLES_COUNT; i++) { + table = boot_swap_tables + i; + + if (boot_magic_compatible_check(table->magic_primary_slot, + primary_slot.magic) && + boot_magic_compatible_check(table->magic_secondary_slot, + secondary_slot.magic) && + (table->image_ok_primary_slot == BOOT_FLAG_ANY || + table->image_ok_primary_slot == primary_slot.image_ok) && + (table->image_ok_secondary_slot == BOOT_FLAG_ANY || + table->image_ok_secondary_slot == secondary_slot.image_ok) && + (table->copy_done_primary_slot == BOOT_FLAG_ANY || + table->copy_done_primary_slot == primary_slot.copy_done)) { + BOOT_LOG_INF("Image index: %d, Swap type: %s", image_index, + table->swap_type == BOOT_SWAP_TYPE_TEST ? "test" : + table->swap_type == BOOT_SWAP_TYPE_PERM ? "perm" : + table->swap_type == BOOT_SWAP_TYPE_REVERT ? "revert" : + "BUG; can't happen"); + if (table->swap_type != BOOT_SWAP_TYPE_TEST && + table->swap_type != BOOT_SWAP_TYPE_PERM && + table->swap_type != BOOT_SWAP_TYPE_REVERT) { + return BOOT_SWAP_TYPE_PANIC; + } + return table->swap_type; + } + } + + BOOT_LOG_INF("Image index: %d, Swap type: none", image_index); + return BOOT_SWAP_TYPE_NONE; +} + +int +boot_write_copy_done(const struct flash_area *fap) +{ + uint32_t off; + + off = boot_copy_done_off(fap); + BOOT_LOG_DBG("writing copy_done; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)(flash_area_get_off(fap) + off)); + return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET); +} + +#ifndef MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP + +static int flash_area_to_image(const struct flash_area *fa) +{ +#if BOOT_IMAGE_NUMBER > 1 + uint8_t i = 0; + int id = flash_area_get_id(fa); + + while (i < BOOT_IMAGE_NUMBER) { + if (FLASH_AREA_IMAGE_PRIMARY(i) == id || (FLASH_AREA_IMAGE_SECONDARY(i) == id)) { + return i; + } + + ++i; + } +#else + (void)fa; +#endif + return 0; +} + +int +boot_set_next(const struct flash_area *fa, bool active, bool confirm) +{ + struct boot_swap_state slot_state; + int rc; + + if (active) { + confirm = true; + } + + rc = boot_read_swap_state(fa, &slot_state); + if (rc != 0) { + return rc; + } + + switch (slot_state.magic) { + case BOOT_MAGIC_GOOD: + /* If non-active then swap already scheduled, else confirm needed.*/ + + if (active && slot_state.image_ok == BOOT_FLAG_UNSET) { + /* Intentionally do not check copy_done flag to be able to + * confirm a padded image which has been programmed using + * a programming interface. + */ + rc = boot_write_image_ok(fa); + } + + break; + + case BOOT_MAGIC_UNSET: + if (!active) { + rc = boot_write_magic(fa); + + if (rc == 0 && confirm) { + rc = boot_write_image_ok(fa); + } + + if (rc == 0) { + uint8_t swap_type; + + if (confirm) { + swap_type = BOOT_SWAP_TYPE_PERM; + } else { + swap_type = BOOT_SWAP_TYPE_TEST; + } + rc = boot_write_swap_info(fa, swap_type, flash_area_to_image(fa)); + } + } + break; + + case BOOT_MAGIC_BAD: + if (active) { + rc = BOOT_EBADVECT; + } else { + /* The image slot is corrupt. There is no way to recover, so erase the + * slot to allow future upgrades. + */ + flash_area_erase(fa, 0, flash_area_get_size(fa)); + rc = BOOT_EBADIMAGE; + } + break; + + default: + /* Something is not OK, this should never happen */ + assert(0); + rc = BOOT_EBADIMAGE; + } + + return rc; +} +#else +int +boot_set_next(const struct flash_area *fa, bool active, bool confirm) +{ + struct boot_swap_state slot_state; + int rc; + + if (active) { + /* The only way to set active slot for next boot is to confirm it, + * as DirectXIP will conclude that, since slot has not been confirmed + * last boot, it is bad and will remove it. + */ + confirm = true; + } + + rc = boot_read_swap_state(fa, &slot_state); + if (rc != 0) { + return rc; + } + + switch (slot_state.magic) { + case BOOT_MAGIC_UNSET: + /* Magic is needed for MCUboot to even consider booting an image */ + rc = boot_write_magic(fa); + if (rc != 0) { + break; + } + /* Pass */ + + case BOOT_MAGIC_GOOD: + if (confirm) { + if (slot_state.copy_done == BOOT_FLAG_UNSET) { + /* Magic is needed for DirectXIP to even try to boot application. + * DirectXIP will set copy-done flag before attempting to boot + * application. Next boot, application that has copy-done flag + * is expected to already have ok flag, otherwise it will be removed. + */ + rc = boot_write_copy_done(fa); + if (rc != 0) { + break; + } + } + + if (slot_state.image_ok == BOOT_FLAG_UNSET) { + rc = boot_write_image_ok(fa); + if (rc != 0) { + break; + } + } + } + break; + + case BOOT_MAGIC_BAD: + /* This image will not be boot next time anyway */ + rc = BOOT_EBADIMAGE; + break; + + default: + /* Something is not OK, this should never happen */ + assert(0); + rc = BOOT_EBADSTATUS; + } + + return rc; +} +#endif + +/* + * This function is not used by the bootloader itself, but its required API + * by external tooling like mcumgr. + */ +int +boot_swap_type(void) +{ + return boot_swap_type_multi(0); +} + +/** + * Marks the image with the given index in the secondary slot as pending. On the + * next reboot, the system will perform a one-time boot of the the secondary + * slot image. + * + * @param image_index Image pair index. + * + * @param permanent Whether the image should be used permanently or + * only tested once: + * 0=run image once, then confirm or revert. + * 1=run image forever. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_set_pending_multi(int image_index, int permanent) +{ + const struct flash_area *fap; + int rc; + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_set_next(fap, false, !(permanent == 0)); + + flash_area_close(fap); + return rc; +} + +/** + * Marks the image with index 0 in the secondary slot as pending. On the next + * reboot, the system will perform a one-time boot of the the secondary slot + * image. Note that this API is kept for compatibility. The + * boot_set_pending_multi() API is recommended. + * + * @param permanent Whether the image should be used permanently or + * only tested once: + * 0=run image once, then confirm or revert. + * 1=run image forever. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_set_pending(int permanent) +{ + return boot_set_pending_multi(0, permanent); +} + +/** + * Marks the image with the given index in the primary slot as confirmed. The + * system will continue booting into the image in the primary slot until told to + * boot from a different slot. + * + * @param image_index Image pair index. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_set_confirmed_multi(int image_index) +{ + const struct flash_area *fap = NULL; + int rc; + + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_set_next(fap, true, true); + + flash_area_close(fap); + return rc; +} + +/** + * Marks the image with index 0 in the primary slot as confirmed. The system + * will continue booting into the image in the primary slot until told to boot + * from a different slot. Note that this API is kept for compatibility. The + * boot_set_confirmed_multi() API is recommended. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_set_confirmed(void) +{ + return boot_set_confirmed_multi(0); +} + +int +boot_image_load_header(const struct flash_area *fa_p, + struct image_header *hdr) +{ + uint32_t size; + int rc = flash_area_read(fa_p, 0, hdr, sizeof *hdr); + + if (rc != 0) { + rc = BOOT_EFLASH; + BOOT_LOG_ERR("Failed reading image header"); + return BOOT_EFLASH; + } + + if (hdr->ih_magic != IMAGE_MAGIC) { + BOOT_LOG_ERR("Bad image magic 0x%lx", (unsigned long)hdr->ih_magic); + + return BOOT_EBADIMAGE; + } + + if (hdr->ih_flags & IMAGE_F_NON_BOOTABLE) { + BOOT_LOG_ERR("Image not bootable"); + + return BOOT_EBADIMAGE; + } + + if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size) || + size >= flash_area_get_size(fa_p)) { + return BOOT_EBADIMAGE; + } + + return 0; +} diff --git a/bootloader/mcuboot/boot/bootutil/src/caps.c b/bootloader/mcuboot/boot/bootutil/src/caps.c new file mode 100644 index 0000000..d7cd590 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/caps.c @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017 Linaro Limited + * Copyright (c) 2021-2023 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "mcuboot_config/mcuboot_config.h" + +uint32_t bootutil_get_caps(void) +{ + uint32_t res = 0; + +#if defined(MCUBOOT_SIGN_RSA) +#if MCUBOOT_SIGN_RSA_LEN == 2048 + res |= BOOTUTIL_CAP_RSA2048; +#endif +#if MCUBOOT_SIGN_RSA_LEN == 3072 + res |= BOOTUTIL_CAP_RSA3072; +#endif +#endif +#if defined(MCUBOOT_SIGN_EC256) + res |= BOOTUTIL_CAP_ECDSA_P256; +#endif +#if defined(MCUBOOT_SIGN_EC384) + res |= BOOTUTIL_CAP_ECDSA_P384; +#endif +#if defined(MCUBOOT_SIGN_ED25519) + res |= BOOTUTIL_CAP_ED25519; +#endif +#if defined(MCUBOOT_OVERWRITE_ONLY) + res |= BOOTUTIL_CAP_OVERWRITE_UPGRADE; +#elif defined(MCUBOOT_SWAP_USING_MOVE) + res |= BOOTUTIL_CAP_SWAP_USING_MOVE; +#else + res |= BOOTUTIL_CAP_SWAP_USING_SCRATCH; +#endif +#if defined(MCUBOOT_ENCRYPT_RSA) + res |= BOOTUTIL_CAP_ENC_RSA; +#endif +#if defined(MCUBOOT_ENCRYPT_KW) + res |= BOOTUTIL_CAP_ENC_KW; +#endif +#if defined(MCUBOOT_ENCRYPT_EC256) + res |= BOOTUTIL_CAP_ENC_EC256; +#endif +#if defined(MCUBOOT_ENCRYPT_X25519) + res |= BOOTUTIL_CAP_ENC_X25519; +#endif +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) + res |= BOOTUTIL_CAP_VALIDATE_PRIMARY_SLOT; +#endif +#if defined(MCUBOOT_DOWNGRADE_PREVENTION) + res |= BOOTUTIL_CAP_DOWNGRADE_PREVENTION; +#endif +#if defined(MCUBOOT_BOOTSTRAP) + res |= BOOTUTIL_CAP_BOOTSTRAP; +#endif +#if defined(MCUBOOT_AES_256) + res |= BOOTUTIL_CAP_AES256; +#endif +#if defined(MCUBOOT_RAM_LOAD) + res |= BOOTUTIL_CAP_RAM_LOAD; +#endif +#if defined(MCUBOOT_DIRECT_XIP) + res |= BOOTUTIL_CAP_DIRECT_XIP; +#endif +#if defined(MCUBOOT_HW_ROLLBACK_PROT) + res |= BOOTUTIL_CAP_HW_ROLLBACK_PROT; +#endif + + return res; +} + +uint32_t bootutil_get_num_images(void) +{ +#if defined(MCUBOOT_IMAGE_NUMBER) + return MCUBOOT_IMAGE_NUMBER; +#else + return 1; +#endif +} diff --git a/bootloader/mcuboot/boot/bootutil/src/ed25519_psa.c b/bootloader/mcuboot/boot/bootutil/src/ed25519_psa.c new file mode 100644 index 0000000..4dbbcb6 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/ed25519_psa.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#include +#include +#include + +#include +#include "bootutil/bootutil_log.h" + +#include +#include +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +#include +#endif + +BOOT_LOG_MODULE_DECLARE(ed25519_psa); + +#define SHA512_DIGEST_LENGTH 64 +#define EDDSA_KEY_LENGTH 32 +#define EDDSA_SIGNAGURE_LENGTH 64 + +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +/* List of KMU stored key ids available for MCUboot */ +#define MAKE_PSA_KMU_KEY_ID(id) PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_RAW, id) +static psa_key_id_t kmu_key_ids[3] = { + MAKE_PSA_KMU_KEY_ID(226), + MAKE_PSA_KMU_KEY_ID(228), + MAKE_PSA_KMU_KEY_ID(230) +}; +#define KMU_KEY_COUNT (sizeof(kmu_key_ids)/sizeof(kmu_key_ids[0])) +#endif + +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +int ED25519_verify(const uint8_t *message, size_t message_len, + const uint8_t signature[EDDSA_SIGNAGURE_LENGTH], + const uint8_t public_key[EDDSA_KEY_LENGTH]) +{ + /* Set to any error */ + psa_status_t status = PSA_ERROR_BAD_STATE; + psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t kid; + int ret = 0; /* Fail by default */ + + /* Initialize PSA Crypto */ + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d\n", status); + return 0; + } + + status = PSA_ERROR_BAD_STATE; + + psa_set_key_type(&key_attr, + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)); + psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_MESSAGE); + psa_set_key_algorithm(&key_attr, PSA_ALG_PURE_EDDSA); + + status = psa_import_key(&key_attr, public_key, EDDSA_KEY_LENGTH, &kid); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("ED25519 key import failed %d", status); + return 0; + } + + status = psa_verify_message(kid, PSA_ALG_PURE_EDDSA, message, message_len, + signature, EDDSA_SIGNAGURE_LENGTH); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("ED25519 signature verification failed %d", status); + ret = 0; + /* Pass through to destroy key */ + } else { + ret = 1; + /* Pass through to destroy key */ + } + + status = psa_destroy_key(kid); + + if (status != PSA_SUCCESS) { + /* Just for logging */ + BOOT_LOG_WRN("Failed to destroy key %d", status); + } + + return ret; +} +#else +int ED25519_verify(const uint8_t *message, size_t message_len, + const uint8_t signature[EDDSA_SIGNAGURE_LENGTH], + const uint8_t public_key[EDDSA_KEY_LENGTH]) +{ + ARG_UNUSED(public_key); + /* Set to any error */ + psa_status_t status = PSA_ERROR_BAD_STATE; + int ret = 0; /* Fail by default */ + + /* Initialize PSA Crypto */ + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", status); + return 0; + } + + status = PSA_ERROR_BAD_STATE; + + for (int i = 0; i < KMU_KEY_COUNT; ++i) { + psa_key_id_t kid = kmu_key_ids[i]; + + status = psa_verify_message(kid, PSA_ALG_PURE_EDDSA, message, + message_len, signature, + EDDSA_SIGNAGURE_LENGTH); + if (status == PSA_SUCCESS) { + ret = 1; + break; + } + + BOOT_LOG_ERR("ED25519 signature verification failed %d", status); + } + + return ret; +} +#endif diff --git a/bootloader/mcuboot/boot/bootutil/src/encrypted.c b/bootloader/mcuboot/boot/bootutil/src/encrypted.c new file mode 100644 index 0000000..67fa819 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/encrypted.c @@ -0,0 +1,755 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2018-2019 JUUL Labs + * Copyright (c) 2019-2024 Arm Limited + */ + +#include "mcuboot_config/mcuboot_config.h" + +#if defined(MCUBOOT_ENC_IMAGES) +#include +#include +#include + +#if defined(MCUBOOT_ENCRYPT_RSA) +#define BOOTUTIL_CRYPTO_RSA_CRYPT_ENABLED +#include "bootutil/crypto/rsa.h" +#endif + +#if defined(MCUBOOT_ENCRYPT_KW) +#include "bootutil/crypto/aes_kw.h" +#endif + +#if defined(MCUBOOT_ENCRYPT_EC256) +#include "bootutil/crypto/ecdh_p256.h" +#endif + +#if !defined(MCUBOOT_USE_PSA_CRYPTO) +#if defined(MCUBOOT_ENCRYPT_X25519) +#include "bootutil/crypto/ecdh_x25519.h" +#endif + +#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) +#include "bootutil/crypto/sha.h" +#include "bootutil/crypto/hmac_sha256.h" +#include "mbedtls/oid.h" +#include "mbedtls/asn1.h" +#endif +#endif + +#include "bootutil/image.h" +#include "bootutil/enc_key.h" +#include "bootutil/sign_key.h" +#include "bootutil/crypto/common.h" + +#include "bootutil_priv.h" + +#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE + +#if defined(MCUBOOT_ENCRYPT_RSA) +# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_RSA2048 +#elif defined(MCUBOOT_ENCRYPT_KW) +# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_KW +#elif defined(MCUBOOT_ENCRYPT_EC256) +# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_EC256 +# define EC_PUBK_INDEX (0) +# define EC_TAG_INDEX (65) +# define EC_CIPHERKEY_INDEX (65 + 32) +_Static_assert(EC_CIPHERKEY_INDEX + BOOT_ENC_KEY_SIZE == EXPECTED_ENC_LEN, + "Please fix ECIES-P256 component indexes"); +#elif defined(MCUBOOT_ENCRYPT_X25519) +# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_X25519 +# define EC_PUBK_INDEX (0) +# define EC_TAG_INDEX (32) +# define EC_CIPHERKEY_INDEX (32 + 32) +_Static_assert(EC_CIPHERKEY_INDEX + BOOT_ENC_KEY_SIZE == EXPECTED_ENC_LEN, + "Please fix ECIES-X25519 component indexes"); +#endif + +/* NOUP Fixme: */ +#if !defined(CONFIG_BOOT_ED25519_PSA) +#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) +#if defined(_compare) +static inline int bootutil_constant_time_compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + return _compare(a, b, size); +} +#else +static int bootutil_constant_time_compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + const uint8_t *tempa = a; + const uint8_t *tempb = b; + uint8_t result = 0; + unsigned int i; + + for (i = 0; i < size; i++) { + result |= tempa[i] ^ tempb[i]; + } + return result; +} +#endif +#endif + +#if defined(MCUBOOT_ENCRYPT_KW) +static int +key_unwrap(const uint8_t *wrapped, uint8_t *enckey, struct bootutil_key *bootutil_enc_key) +{ + bootutil_aes_kw_context aes_kw; + int rc; + + bootutil_aes_kw_init(&aes_kw); + rc = bootutil_aes_kw_set_unwrap_key(&aes_kw, bootutil_enc_key->key, *bootutil_enc_key->len); + if (rc != 0) { + goto done; + } + rc = bootutil_aes_kw_unwrap(&aes_kw, wrapped, TLV_ENC_KW_SZ, enckey, BOOT_ENC_KEY_SIZE); + if (rc != 0) { + goto done; + } + +done: + bootutil_aes_kw_drop(&aes_kw); + return rc; +} +#endif /* MCUBOOT_ENCRYPT_KW */ + +#if defined(MCUBOOT_ENCRYPT_EC256) +static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED; +static const uint8_t ec_secp256r1_oid[] = MBEDTLS_OID_EC_GRP_SECP256R1; + +#define SHARED_KEY_LEN NUM_ECC_BYTES +#define PRIV_KEY_LEN NUM_ECC_BYTES + +/* + * Parses the output of `imgtool keygen`, which produces a PKCS#8 elliptic + * curve keypair. See RFC5208 and RFC5915. + */ +static int +parse_ec256_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) +{ + int rc; + size_t len; + int version; + mbedtls_asn1_buf alg; + mbedtls_asn1_buf param; + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return -1; + } + + if (*p + len != end) { + return -2; + } + + version = 0; + if (mbedtls_asn1_get_int(p, end, &version) || version != 0) { + return -3; + } + + if ((rc = mbedtls_asn1_get_alg(p, end, &alg, ¶m)) != 0) { + return -5; + } + + if (alg.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_pubkey_oid) - 1 || + memcmp(alg.ASN1_CONTEXT_MEMBER(p), ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) { + return -6; + } + if (param.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_secp256r1_oid) - 1 || + memcmp(param.ASN1_CONTEXT_MEMBER(p), ec_secp256r1_oid, sizeof(ec_secp256r1_oid) - 1)) { + return -7; + } + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return -8; + } + + /* RFC5915 - ECPrivateKey */ + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return -9; + } + + version = 0; + if (mbedtls_asn1_get_int(p, end, &version) || version != 1) { + return -10; + } + + /* privateKey */ + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return -11; + } + + if (len != NUM_ECC_BYTES) { + return -12; + } + + memcpy(private_key, *p, len); + + /* publicKey usually follows but is not parsed here */ + + return 0; +} +#endif /* defined(MCUBOOT_ENCRYPT_EC256) */ + +#if defined(MCUBOOT_ENCRYPT_X25519) +#define X25519_OID "\x6e" +static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG \ + MBEDTLS_OID_ORG_GOV X25519_OID; + +#define SHARED_KEY_LEN 32 +#define PRIV_KEY_LEN 32 + +static int +parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) +{ + size_t len; + int version; + mbedtls_asn1_buf alg; + mbedtls_asn1_buf param; + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE) != 0) { + return -1; + } + + if (*p + len != end) { + return -2; + } + + version = 0; + if (mbedtls_asn1_get_int(p, end, &version) || version != 0) { + return -3; + } + + if (mbedtls_asn1_get_alg(p, end, &alg, ¶m) != 0) { + return -4; + } + + if (alg.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_pubkey_oid) - 1 || + memcmp(alg.ASN1_CONTEXT_MEMBER(p), ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) { + return -5; + } + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) { + return -6; + } + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) { + return -7; + } + + if (len != PRIV_KEY_LEN) { + return -8; + } + + memcpy(private_key, *p, PRIV_KEY_LEN); + return 0; +} +#endif /* defined(MCUBOOT_ENCRYPT_X25519) */ + +#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) +/* + * HKDF as described by RFC5869. + * + * @param ikm The input data to be derived. + * @param ikm_len Length of the input data. + * @param info An information tag. + * @param info_len Length of the information tag. + * @param okm Output of the KDF computation. + * @param okm_len On input the requested length; on output the generated length + */ +static int +hkdf(uint8_t *ikm, uint16_t ikm_len, uint8_t *info, uint16_t info_len, + uint8_t *okm, uint16_t *okm_len) +{ + bootutil_hmac_sha256_context hmac; + uint8_t salt[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; + uint8_t prk[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; + uint8_t T[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; + uint16_t off; + uint16_t len; + uint8_t counter; + bool first; + int rc; + + /* + * Extract + */ + + if (ikm == NULL || okm == NULL || ikm_len == 0) { + return -1; + } + + bootutil_hmac_sha256_init(&hmac); + + memset(salt, 0, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + rc = bootutil_hmac_sha256_set_key(&hmac, salt, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + if (rc != 0) { + goto error; + } + + rc = bootutil_hmac_sha256_update(&hmac, ikm, ikm_len); + if (rc != 0) { + goto error; + } + + rc = bootutil_hmac_sha256_finish(&hmac, prk, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + if (rc != 0) { + goto error; + } + + bootutil_hmac_sha256_drop(&hmac); + + /* + * Expand + */ + + len = *okm_len; + counter = 1; + first = true; + for (off = 0; len > 0; off += BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE, ++counter) { + bootutil_hmac_sha256_init(&hmac); + + rc = bootutil_hmac_sha256_set_key(&hmac, prk, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + if (rc != 0) { + goto error; + } + + if (first) { + first = false; + } else { + rc = bootutil_hmac_sha256_update(&hmac, T, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + if (rc != 0) { + goto error; + } + } + + rc = bootutil_hmac_sha256_update(&hmac, info, info_len); + if (rc != 0) { + goto error; + } + + rc = bootutil_hmac_sha256_update(&hmac, &counter, 1); + if (rc != 0) { + goto error; + } + + rc = bootutil_hmac_sha256_finish(&hmac, T, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + if (rc != 0) { + goto error; + } + + bootutil_hmac_sha256_drop(&hmac); + + if (len > BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE) { + memcpy(&okm[off], T, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + len -= BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE; + } else { + memcpy(&okm[off], T, len); + len = 0; + } + } + + return 0; + +error: + bootutil_hmac_sha256_drop(&hmac); + return -1; +} +#endif /* MCUBOOT_ENCRYPT_EC256 || MCUBOOT_ENCRYPT_X25519 */ + +#if !defined(MCUBOOT_ENC_BUILTIN_KEY) +extern const struct bootutil_key bootutil_enc_key; + +/* + * Default implementation to retrieve the private encryption key which is + * embedded in the bootloader code (when MCUBOOT_ENC_BUILTIN_KEY is not defined). + */ +int boot_enc_retrieve_private_key(struct bootutil_key **private_key) +{ + *private_key = (struct bootutil_key *)&bootutil_enc_key; + + return 0; +} +#endif /* !MCUBOOT_ENC_BUILTIN_KEY */ + +#if ( (defined(MCUBOOT_ENCRYPT_RSA) && defined(MCUBOOT_USE_MBED_TLS) && !defined(MCUBOOT_USE_PSA_CRYPTO)) || \ + (defined(MCUBOOT_ENCRYPT_EC256) && defined(MCUBOOT_USE_MBED_TLS)) ) +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 +static int fake_rng(void *p_rng, unsigned char *output, size_t len) +{ + size_t i; + + (void)p_rng; + for (i = 0; i < len; i++) { + output[i] = (char)i; + } + + return 0; +} +#endif /* MBEDTLS_VERSION_NUMBER */ +#endif /* (MCUBOOT_ENCRYPT_RSA && MCUBOOT_USE_MBED_TLS && !MCUBOOT_USE_PSA_CRYPTO) || + (MCUBOOT_ENCRYPT_EC256 && MCUBOOT_USE_MBED_TLS) */ + +/* + * Decrypt an encryption key TLV. + * + * @param buf An encryption TLV read from flash (build time fixed length) + * @param enckey An AES-128 or AES-256 key sized buffer to store to plain key. + */ +int +boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) +{ +#if defined(MCUBOOT_ENCRYPT_RSA) + bootutil_rsa_context rsa; + uint8_t *cp; + uint8_t *cpend; + size_t olen; +#endif +#if defined(MCUBOOT_ENCRYPT_EC256) + bootutil_ecdh_p256_context ecdh_p256; +#endif +#if defined(MCUBOOT_ENCRYPT_X25519) + bootutil_ecdh_x25519_context ecdh_x25519; +#endif +#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) + bootutil_hmac_sha256_context hmac; + bootutil_aes_ctr_context aes_ctr; + uint8_t tag[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; + uint8_t shared[SHARED_KEY_LEN]; + uint8_t derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; + uint8_t *cp; + uint8_t *cpend; + uint8_t private_key[PRIV_KEY_LEN]; + uint8_t counter[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE]; + uint16_t len; +#endif + struct bootutil_key *bootutil_enc_key = NULL; + int rc = -1; + + rc = boot_enc_retrieve_private_key(&bootutil_enc_key); + if (rc) { + return rc; + } + + if (bootutil_enc_key == NULL) { + return rc; + } + +#if defined(MCUBOOT_ENCRYPT_RSA) + + bootutil_rsa_init(&rsa); + cp = (uint8_t *)bootutil_enc_key->key; + cpend = cp + *bootutil_enc_key->len; + + /* The enckey is encrypted through RSA so for decryption we need the private key */ + rc = bootutil_rsa_parse_private_key(&rsa, &cp, cpend); + if (rc) { + bootutil_rsa_drop(&rsa); + return rc; + } + + rc = bootutil_rsa_oaep_decrypt(&rsa, &olen, buf, enckey, BOOT_ENC_KEY_SIZE); + bootutil_rsa_drop(&rsa); + if (rc) { + return rc; + } + +#endif /* defined(MCUBOOT_ENCRYPT_RSA) */ + +#if defined(MCUBOOT_ENCRYPT_KW) + + assert(*bootutil_enc_key->len == BOOT_ENC_KEY_SIZE); + rc = key_unwrap(buf, enckey, bootutil_enc_key); + +#endif /* defined(MCUBOOT_ENCRYPT_KW) */ + +#if defined(MCUBOOT_ENCRYPT_EC256) + + cp = (uint8_t *)bootutil_enc_key->key; + cpend = cp + *bootutil_enc_key->len; + + /* + * Load the stored EC256 decryption private key + */ + + rc = parse_ec256_enckey(&cp, cpend, private_key); + if (rc) { + return rc; + } + + /* + * First "element" in the TLV is the curve point (public key) + */ + bootutil_ecdh_p256_init(&ecdh_p256); + + rc = bootutil_ecdh_p256_shared_secret(&ecdh_p256, &buf[EC_PUBK_INDEX], private_key, shared); + bootutil_ecdh_p256_drop(&ecdh_p256); + if (rc != 0) { + return -1; + } + +#endif /* defined(MCUBOOT_ENCRYPT_EC256) */ + +#if defined(MCUBOOT_ENCRYPT_X25519) + + cp = (uint8_t *)bootutil_enc_key->key; + cpend = cp + *bootutil_enc_key->len; + + /* + * Load the stored X25519 decryption private key + */ + + rc = parse_x25519_enckey(&cp, cpend, private_key); + if (rc) { + return rc; + } + + /* + * First "element" in the TLV is the curve point (public key) + */ + + bootutil_ecdh_x25519_init(&ecdh_x25519); + + rc = bootutil_ecdh_x25519_shared_secret(&ecdh_x25519, &buf[EC_PUBK_INDEX], private_key, shared); + bootutil_ecdh_x25519_drop(&ecdh_x25519); + if (!rc) { + return -1; + } + +#endif /* defined(MCUBOOT_ENCRYPT_X25519) */ + +#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) + + /* + * Expand shared secret to create keys for AES-128-CTR + HMAC-SHA256 + */ + + len = BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE; + rc = hkdf(shared, SHARED_KEY_LEN, (uint8_t *)"MCUBoot_ECIES_v1", 16, + derived_key, &len); + if (rc != 0 || len != (BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE)) { + return -1; + } + + /* + * HMAC the key and check that our received MAC matches the generated tag + */ + + bootutil_hmac_sha256_init(&hmac); + + rc = bootutil_hmac_sha256_set_key(&hmac, &derived_key[BOOT_ENC_KEY_SIZE], 32); + if (rc != 0) { + (void)bootutil_hmac_sha256_drop(&hmac); + return -1; + } + + rc = bootutil_hmac_sha256_update(&hmac, &buf[EC_CIPHERKEY_INDEX], BOOT_ENC_KEY_SIZE); + if (rc != 0) { + (void)bootutil_hmac_sha256_drop(&hmac); + return -1; + } + + /* Assumes the tag buffer is at least sizeof(hmac_tag_size(state)) bytes */ + rc = bootutil_hmac_sha256_finish(&hmac, tag, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + if (rc != 0) { + (void)bootutil_hmac_sha256_drop(&hmac); + return -1; + } + + if (bootutil_constant_time_compare(tag, &buf[EC_TAG_INDEX], 32) != 0) { + (void)bootutil_hmac_sha256_drop(&hmac); + return -1; + } + + bootutil_hmac_sha256_drop(&hmac); + + /* + * Finally decrypt the received ciphered key + */ + + bootutil_aes_ctr_init(&aes_ctr); + if (rc != 0) { + bootutil_aes_ctr_drop(&aes_ctr); + return -1; + } + + rc = bootutil_aes_ctr_set_key(&aes_ctr, derived_key); + if (rc != 0) { + bootutil_aes_ctr_drop(&aes_ctr); + return -1; + } + + memset(counter, 0, BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE); + rc = bootutil_aes_ctr_decrypt(&aes_ctr, counter, &buf[EC_CIPHERKEY_INDEX], BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, 0, enckey); + if (rc != 0) { + bootutil_aes_ctr_drop(&aes_ctr); + return -1; + } + + bootutil_aes_ctr_drop(&aes_ctr); + + rc = 0; + +#endif /* defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) */ + + return rc; +} +#endif /* CONFIG_BOOT_ED25519_PSA */ + +/* + * Load encryption key. + */ +int +boot_enc_load(struct enc_key_data *enc_state, int slot, + const struct image_header *hdr, const struct flash_area *fap, + struct boot_status *bs) +{ + uint32_t off; + uint16_t len; + struct image_tlv_iter it; +#if MCUBOOT_SWAP_SAVE_ENCTLV + uint8_t *buf; +#else + uint8_t buf[EXPECTED_ENC_LEN]; +#endif + int rc; + + /* Already loaded... */ + if (enc_state[slot].valid) { + return 1; + } + + /* Initialize the AES context */ + boot_enc_init(enc_state, slot); + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_ENC_TLV, false); + if (rc) { + return -1; + } + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + if (rc != 0) { + return rc; + } + + if (len != EXPECTED_ENC_LEN) { + return -1; + } + +#if MCUBOOT_SWAP_SAVE_ENCTLV + buf = bs->enctlv[slot]; + memset(buf, 0xff, BOOT_ENC_TLV_ALIGN_SIZE); +#endif + + rc = flash_area_read(fap, off, buf, EXPECTED_ENC_LEN); + if (rc) { + return -1; + } + + return boot_decrypt_key(buf, bs->enckey[slot]); +} + +int +boot_enc_init(struct enc_key_data *enc_state, uint8_t slot) +{ + bootutil_aes_ctr_init(&enc_state[slot].aes_ctr); + return 0; +} + +int +boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot) +{ + bootutil_aes_ctr_drop(&enc_state[slot].aes_ctr); + enc_state[slot].valid = 0; + return 0; +} + +int +boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot, + const struct boot_status *bs) +{ + int rc; + + rc = bootutil_aes_ctr_set_key(&enc_state[slot].aes_ctr, bs->enckey[slot]); + if (rc != 0) { + boot_enc_drop(enc_state, slot); + return -1; + } + + enc_state[slot].valid = 1; + + return 0; +} + + +bool +boot_enc_valid(struct enc_key_data *enc_state, int slot) +{ + return enc_state[slot].valid; +} + +void +boot_enc_encrypt(struct enc_key_data *enc_state, int slot, uint32_t off, + uint32_t sz, uint32_t blk_off, uint8_t *buf) +{ + struct enc_key_data *enc = &enc_state[slot]; + uint8_t nonce[16]; + + /* Nothing to do with size == 0 */ + if (sz == 0) { + return; + } + + memset(nonce, 0, 12); + off >>= 4; + nonce[12] = (uint8_t)(off >> 24); + nonce[13] = (uint8_t)(off >> 16); + nonce[14] = (uint8_t)(off >> 8); + nonce[15] = (uint8_t)off; + + assert(enc->valid == 1); + bootutil_aes_ctr_encrypt(&enc->aes_ctr, nonce, buf, sz, blk_off, buf); +} + +void +boot_enc_decrypt(struct enc_key_data *enc_state, int slot, uint32_t off, + uint32_t sz, uint32_t blk_off, uint8_t *buf) +{ + struct enc_key_data *enc = &enc_state[slot]; + uint8_t nonce[16]; + + /* Nothing to do with size == 0 */ + if (sz == 0) { + return; + } + + memset(nonce, 0, 12); + off >>= 4; + nonce[12] = (uint8_t)(off >> 24); + nonce[13] = (uint8_t)(off >> 16); + nonce[14] = (uint8_t)(off >> 8); + nonce[15] = (uint8_t)off; + + assert(enc->valid == 1); + bootutil_aes_ctr_decrypt(&enc->aes_ctr, nonce, buf, sz, blk_off, buf); +} + +/** + * Clears encrypted state after use. + */ +void +boot_enc_zeroize(struct enc_key_data *enc_state) +{ + uint8_t slot; + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + (void)boot_enc_drop(enc_state, slot); + } + memset(enc_state, 0, sizeof(struct enc_key_data) * BOOT_NUM_SLOTS); +} + +#endif /* MCUBOOT_ENC_IMAGES */ diff --git a/bootloader/mcuboot/boot/bootutil/src/encrypted_psa.c b/bootloader/mcuboot/boot/bootutil/src/encrypted_psa.c new file mode 100644 index 0000000..c3f7288 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/encrypted_psa.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "mcuboot_config/mcuboot_config.h" + +#include +#include +#include + +/* We are not really using the MBEDTLS but need the ASN.1 parsing functions */ +#define MBEDTLS_ASN1_PARSE_C + +#include "bootutil/crypto/sha.h" +#include "mbedtls/oid.h" +#include "mbedtls/asn1.h" + +#include "bootutil/image.h" +#include "bootutil/enc_key.h" +#include "bootutil/sign_key.h" +#include "bootutil/crypto/common.h" + +#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot_psa_enc); + +#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE +#define EXPECTED_ENC_TLV IMAGE_TLV_ENC_X25519 +#define EC_PUBK_INDEX (0) +#define EC_TAG_INDEX (32) +#define EC_CIPHERKEY_INDEX (32 + 32) +_Static_assert(EC_CIPHERKEY_INDEX + BOOT_ENC_KEY_SIZE == EXPECTED_ENC_LEN, + "Please fix ECIES-X25519 component indexes"); + +#define X25519_OID "\x6e" +static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG \ + MBEDTLS_OID_ORG_GOV X25519_OID; + +#define SHARED_KEY_LEN 32 +#define PRIV_KEY_LEN 32 + +/* Fixme: This duplicates code from encrypted.c and depends on mbedtls */ +static int +parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) +{ + size_t len; + int version; + mbedtls_asn1_buf alg; + mbedtls_asn1_buf param; + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE) != 0) { + return -1; + } + + if (*p + len != end) { + return -2; + } + + version = 0; + if (mbedtls_asn1_get_int(p, end, &version) || version != 0) { + return -3; + } + + if (mbedtls_asn1_get_alg(p, end, &alg, ¶m) != 0) { + return -4; + } + + if (alg.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_pubkey_oid) - 1 || + memcmp(alg.ASN1_CONTEXT_MEMBER(p), ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) { + return -5; + } + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) { + return -6; + } + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) { + return -7; + } + + if (len != PRIV_KEY_LEN) { + return -8; + } + + memcpy(private_key, *p, PRIV_KEY_LEN); + return 0; +} + +void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) +{ + psa_status_t psa_ret = psa_crypto_init(); + + (void)ctx; + + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES init PSA crypto init failed %d", psa_ret); + assert(0); + } +} + +#if defined(MCUBOOT_ENC_IMAGES) +extern const struct bootutil_key bootutil_enc_key; +/* + * Decrypt an encryption key TLV. + * + * @param buf An encryption TLV read from flash (build time fixed length) + * @param enckey An AES-128 or AES-256 key sized buffer to store to plain key. + */ +int +boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) +{ + uint8_t derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; + uint8_t *cp; + uint8_t *cpend; + uint8_t private_key[PRIV_KEY_LEN]; + size_t len; + psa_status_t psa_ret = PSA_ERROR_BAD_STATE; + psa_status_t psa_cleanup_ret = PSA_ERROR_BAD_STATE; + psa_key_id_t kid; + psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_derivation_operation_t key_do = PSA_KEY_DERIVATION_OPERATION_INIT; + psa_algorithm_t key_do_alg; + int rc = -1; + + cp = (uint8_t *)bootutil_enc_key.key; + cpend = cp + *bootutil_enc_key.len; + + /* The psa_cipher_decrypt needs initialization vector of proper length at + * the beginning of the input buffer. + */ + uint8_t iv_and_key[PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR) + + BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE]; + + psa_ret = psa_crypto_init(); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES crypto init failed %d", psa_ret); + return -1; + } + + /* + * Load the stored X25519 decryption private key + */ + rc = parse_x25519_enckey(&cp, cpend, private_key); + if (rc) { + return rc; + } + + psa_set_key_type(&kattr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY)); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&kattr, PSA_ALG_ECDH); + + psa_ret = psa_import_key(&kattr, private_key, sizeof(private_key), &kid); + memset(private_key, 0, sizeof(private_key)); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("Built-in key import failed %d", psa_ret); + return -1; + } + + key_do_alg = PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + + psa_ret = psa_key_derivation_setup(&key_do, key_do_alg); + if (psa_ret != PSA_SUCCESS) { + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Built-in key destruction failed %d", psa_cleanup_ret); + } + BOOT_LOG_ERR("Key derivation setup failed %d", psa_ret); + return -1; + } + + /* Note: PSA 1.1.2 does not have psa_key_agreement that would be useful here + * as it could just add the derived key to the storage and return key id. + * Instead, we have to use the code below to generate derived key and put it + * into storage, to obtain the key id we can then use with psa_mac_* functions. + */ + psa_ret = psa_key_derivation_key_agreement(&key_do, PSA_KEY_DERIVATION_INPUT_SECRET, + kid, &buf[EC_PUBK_INDEX], + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Built-in key destruction failed %d", psa_cleanup_ret); + } + if (psa_ret != PSA_SUCCESS) { + psa_cleanup_ret = psa_key_derivation_abort(&key_do); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Key derivation abort failed %d", psa_ret); + } + + BOOT_LOG_ERR("Key derivation failed %d", psa_ret); + return -1; + } + + /* Only info, no salt */ + psa_ret = psa_key_derivation_input_bytes(&key_do, PSA_KEY_DERIVATION_INPUT_INFO, + "MCUBoot_ECIES_v1", 16); + if (psa_ret != PSA_SUCCESS) { + psa_cleanup_ret = psa_key_derivation_abort(&key_do); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Key derivation abort failed %d", psa_ret); + } + BOOT_LOG_ERR("Key derivation failed %d", psa_ret); + return -1; + } + + len = BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE; + psa_ret = psa_key_derivation_output_bytes(&key_do, derived_key, len); + psa_cleanup_ret = psa_key_derivation_abort(&key_do); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Key derivation cleanup failed %d", psa_ret); + } + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("Key derivation failed %d", psa_ret); + return -1; + } + + /* The derived key consists of BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE bytes + * followed by BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE bytes. Both parts will + * be imported at the point where needed and discarded immediately after. + */ + psa_set_key_type(&kattr, PSA_KEY_TYPE_HMAC); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_VERIFY_MESSAGE); + psa_set_key_algorithm(&kattr, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + + /* Import the MAC tag key part of derived key, that is the part that starts + * after BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE and has length of + * BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE bytes. + */ + psa_ret = psa_import_key(&kattr, + &derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE], + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE, &kid); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + memset(derived_key, 0, sizeof(derived_key)); + BOOT_LOG_ERR("MAC key import failed %d", psa_ret); + return -1; + } + + /* Verify the MAC tag of the random encryption key */ + psa_ret = psa_mac_verify(kid, PSA_ALG_HMAC(PSA_ALG_SHA_256), + &buf[EC_CIPHERKEY_INDEX], BOOT_ENC_KEY_SIZE, + &buf[EC_TAG_INDEX], + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("MAC key destruction failed %d", psa_cleanup_ret); + } + if (psa_ret != PSA_SUCCESS) { + memset(derived_key, 0, sizeof(derived_key)); + BOOT_LOG_ERR("MAC verification failed %d", psa_ret); + return -1; + } + + /* The derived key is used in AES decryption of random key */ + psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&kattr, PSA_ALG_CTR); + + /* Import the AES partition of derived key, the first 16 bytes */ + psa_ret = psa_import_key(&kattr, &derived_key[0], + BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, &kid); + memset(derived_key, 0, sizeof(derived_key)); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES key import failed %d", psa_ret); + return -1; + } + + /* Decrypt the random AES encryption key with AES and the key obtained + * at derivation. */ + memset(&iv_and_key[0], 0, PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR)); + memcpy(&iv_and_key[PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR)], + &buf[EC_CIPHERKEY_INDEX], + sizeof(iv_and_key) - PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR)); + + len = 0; + psa_ret = psa_cipher_decrypt(kid, PSA_ALG_CTR, iv_and_key, sizeof(iv_and_key), + enckey, BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, &len); + memset(iv_and_key, 0, sizeof(iv_and_key)); + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("AES key destruction failed %d", psa_cleanup_ret); + } + if (psa_ret != PSA_SUCCESS || len != BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE) { + memset(enckey, 0, BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE); + BOOT_LOG_ERR("Random key decryption failed %d", psa_ret); + return -1; + } + + return 0; +} + +int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c) +{ + int ret = 0; + psa_status_t psa_ret = PSA_ERROR_BAD_STATE; + psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t kid; + psa_cipher_operation_t psa_op; + size_t elen = 0; /* Decrypted length */ + + /* Fixme: calling psa_crypto_init multiple times is not a problem, + * yet the code here is only present because there is not general + * crypto init. */ + psa_ret = psa_crypto_init(); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", psa_ret); + ret = -1; + goto gone; + } + + psa_op = psa_cipher_operation_init(); + + /* Fixme: Import should happen when key is decrypted, but due to lack + * of key destruction there is no way to destroy key stored by + * psa other way than here. */ + psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&kattr, PSA_ALG_CTR); + + psa_ret = psa_import_key(&kattr, ctx->key, BOOT_ENC_KEY_SIZE, &kid); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc import key failed %d", psa_ret); + ret = -1; + goto gone; + } + + /* This could be done with psa_cipher_decrypt one-shot operation, but + * multi-part operation is used to avoid re-allocating input buffer + * to account for IV in front of data. + */ + psa_ret = psa_cipher_encrypt_setup(&psa_op, kid, PSA_ALG_CTR); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc setup failed %d", psa_ret); + ret = -1; + goto gone_with_key; + } + + /* Fixme: hardcoded counter size, but it is hardcoded everywhere */ + psa_ret = psa_cipher_set_iv(&psa_op, counter, 16); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc IV set failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + + psa_ret = psa_cipher_update(&psa_op, m, mlen, c, mlen, &elen); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc encryption failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + +gone_after_setup: + psa_ret = psa_cipher_abort(&psa_op); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("AES enc cipher abort failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone_with_key: + /* Fixme: Should be removed once key is shared by id */ + psa_ret = psa_destroy_key(kid); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("AES enc destroy key failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone: + return ret; +} + +int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m) +{ + int ret = 0; + psa_status_t psa_ret = PSA_ERROR_BAD_STATE; + psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t kid; + psa_cipher_operation_t psa_op; + size_t dlen = 0; /* Decrypted length */ + + /* Fixme: the init should already happen before calling the function, but + * somehow it does not, for example when recovering in swap. + */ + psa_ret = psa_crypto_init(); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", psa_ret); + ret = -1; + goto gone; + } + + psa_op = psa_cipher_operation_init(); + + /* Fixme: Import should happen when key is decrypted, but due to lack + * of key destruction there is no way to destroy key stored by + * psa other way than here. */ + psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&kattr, PSA_ALG_CTR); + + psa_ret = psa_import_key(&kattr, ctx->key, BOOT_ENC_KEY_SIZE, &kid); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec import key failed %d", psa_ret); + ret = -1; + goto gone; + } + + /* This could be done with psa_cipher_decrypt one-shot operation, but + * multi-part operation is used to avoid re-allocating input buffer + * to account for IV in front of data. + */ + psa_ret = psa_cipher_decrypt_setup(&psa_op, kid, PSA_ALG_CTR); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec setup failed %d", psa_ret); + ret = -1; + goto gone_with_key; + } + + /* Fixme: hardcoded counter size, but it is hardcoded everywhere */ + psa_ret = psa_cipher_set_iv(&psa_op, counter, 16); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec IV set failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + + psa_ret = psa_cipher_update(&psa_op, c, clen, m, clen, &dlen); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec decryption failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + +gone_after_setup: + psa_ret = psa_cipher_abort(&psa_op); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("PSA dec abort failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone_with_key: + psa_ret = psa_destroy_key(kid); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("PSA dec key failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone: + return ret; +} +#endif /* defined(MCUBOOT_ENC_IMAGES) */ diff --git a/bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening.c b/bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening.c new file mode 100644 index 0000000..11436cf --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening.c @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020 Arm Limited + */ + +#include "bootutil/fault_injection_hardening.h" + +#ifdef FIH_ENABLE_DOUBLE_VARS +/* Variable that could be (but isn't) changed at runtime to force the compiler + * not to optimize the double check. Value doesn't matter. + */ +volatile int _fih_mask = _FIH_MASK_VALUE; +#endif /* FIH_ENABLE_DOUBLE_VARS */ + +fih_ret FIH_SUCCESS = FIH_POSITIVE_VALUE; +fih_ret FIH_FAILURE = FIH_NEGATIVE_VALUE; +fih_ret FIH_NO_BOOTABLE_IMAGE = FIH_CONST1; +fih_ret FIH_BOOT_HOOK_REGULAR = FIH_CONST2; + +#ifdef FIH_ENABLE_CFI + +#ifdef FIH_ENABLE_DOUBLE_VARS +fih_int _fih_cfi_ctr = {0, 0 ^ _FIH_MASK_VALUE}; +#else +fih_int _fih_cfi_ctr = {0}; +#endif /* FIH_ENABLE_DOUBLE_VARS */ + +/* Increment the CFI counter by one, and return the value before the increment. + */ +fih_int fih_cfi_get_and_increment(void) +{ + fih_int saved = _fih_cfi_ctr; + _fih_cfi_ctr = fih_int_encode(fih_int_decode(saved) + 1); + return saved; +} + +/* Validate that the saved precall value is the same as the value of the global + * counter. For this to be the case, a fih_ret must have been called between + * these functions being executed. If the values aren't the same then panic. + */ +void fih_cfi_validate(fih_int saved) +{ + if (fih_int_decode(saved) != fih_int_decode(_fih_cfi_ctr)) { + FIH_PANIC; + } +} + +/* Decrement the global CFI counter by one, so that it has the same value as + * before the cfi_precall + */ +void fih_cfi_decrement(void) +{ + _fih_cfi_ctr = fih_int_encode(fih_int_decode(_fih_cfi_ctr) - 1); +} + +#endif /* FIH_ENABLE_CFI */ + +#ifdef FIH_ENABLE_GLOBAL_FAIL +/* Global failure loop for bootloader code. Uses attribute used to prevent + * compiler removing due to non-standard calling procedure. Multiple loop jumps + * used to make unlooping difficult. + */ +__attribute__((used)) +__attribute__((noinline)) +void fih_panic_loop(void) +{ + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); + __asm volatile ("b fih_panic_loop"); +} +#endif /* FIH_ENABLE_GLOBAL_FAIL */ diff --git a/bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening_delay_rng_mbedtls.c b/bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening_delay_rng_mbedtls.c new file mode 100644 index 0000000..663999b --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/fault_injection_hardening_delay_rng_mbedtls.c @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020 Arm Limited + */ + +#include "bootutil/fault_injection_hardening.h" + +#ifdef FIH_ENABLE_DELAY + +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/entropy.h" + +/* Mbedtls implementation of the delay RNG. Can be replaced by any other RNG + * implementation that is backed by an entropy source by altering these + * functions. This is not provided as a header API and a C file implementation + * due to issues with inlining. + */ + +#ifdef MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES +#error "FIH_ENABLE_DELAY requires an entropy source" +#endif /* MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES */ + +mbedtls_entropy_context fih_entropy_ctx; +mbedtls_ctr_drbg_context fih_drbg_ctx; + +int fih_delay_init(void) +{ + mbedtls_entropy_init(&fih_entropy_ctx); + mbedtls_ctr_drbg_init(&fih_drbg_ctx); + mbedtls_ctr_drbg_seed(&fih_drbg_ctx , mbedtls_entropy_func, + &fih_entropy_ctx, NULL, 0); + + return 1; +} + +unsigned char fih_delay_random_uchar(void) +{ + unsigned char delay; + + mbedtls_ctr_drbg_random(&fih_drbg_ctx,(unsigned char*) &delay, + sizeof(delay)); + + return delay; +} + +#endif /* FIH_ENABLE_DELAY */ diff --git a/bootloader/mcuboot/boot/bootutil/src/image_ecdsa.c b/bootloader/mcuboot/boot/bootutil/src/image_ecdsa.c new file mode 100644 index 0000000..4604913 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/image_ecdsa.c @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2017 Linaro LTD + * Copyright (C) 2021-2024 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "mcuboot_config/mcuboot_config.h" + +#if defined(MCUBOOT_SIGN_EC256) || defined(MCUBOOT_SIGN_EC384) + +#include "bootutil_priv.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/crypto/ecdsa.h" + +#if !defined(MCUBOOT_BUILTIN_KEY) +fih_ret +bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, + uint8_t key_id) +{ + int rc; + bootutil_ecdsa_context ctx; + FIH_DECLARE(fih_rc, FIH_FAILURE); + uint8_t *pubkey; + uint8_t *end; + + pubkey = (uint8_t *)bootutil_keys[key_id].key; + end = pubkey + *bootutil_keys[key_id].len; + bootutil_ecdsa_init(&ctx); + + rc = bootutil_ecdsa_parse_public_key(&ctx, &pubkey, end); + if (rc) { + goto out; + } + + rc = bootutil_ecdsa_verify(&ctx, pubkey, end-pubkey, hash, hlen, sig, slen); + fih_rc = fih_ret_encode_zero_equality(rc); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + } + +out: + bootutil_ecdsa_drop(&ctx); + + FIH_RET(fih_rc); +} +#else /* !MCUBOOT_BUILTIN_KEY */ +fih_ret +bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, + uint8_t key_id) +{ + int rc; + bootutil_ecdsa_context ctx; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + /* Use builtin key for image verification, no key parsing is required. */ + ctx.key_id = key_id; + bootutil_ecdsa_init(&ctx); + + /* The public key pointer and key size can be omitted. */ + rc = bootutil_ecdsa_verify(&ctx, NULL, 0, hash, hlen, sig, slen); + fih_rc = fih_ret_encode_zero_equality(rc); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + bootutil_ecdsa_drop(&ctx); + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_BUILTIN_KEY */ + +#endif /* MCUBOOT_SIGN_EC256 || MCUBOOT_SIGN_EC384 */ diff --git a/bootloader/mcuboot/boot/bootutil/src/image_ed25519.c b/bootloader/mcuboot/boot/bootutil/src/image_ed25519.c new file mode 100644 index 0000000..e6c792a --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/image_ed25519.c @@ -0,0 +1,160 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2019 JUUL Labs + * Copyright (c) 2021-2023 Arm Limited + */ + +#include + +#include "mcuboot_config/mcuboot_config.h" + +#if defined(CONFIG_NRF_SECURITY) +/* We are not really using the MBEDTLS but need the ASN.1 parsing funcitons */ +#define MBEDTLS_ASN1_PARSE_C +#endif + +#ifdef MCUBOOT_SIGN_ED25519 +#include "bootutil/sign_key.h" + +#include "mbedtls/oid.h" +#include "mbedtls/asn1.h" + +#include "bootutil_priv.h" +#include "bootutil/crypto/common.h" +#include "bootutil/crypto/sha.h" + +#define EDDSA_SIGNATURE_LENGTH 64 +#define NUM_ED25519_BYTES 32 + +extern int ED25519_verify(const uint8_t *message, size_t message_len, + const uint8_t signature[EDDSA_SIGNATURE_LENGTH], + const uint8_t public_key[NUM_ED25519_BYTES]); + +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) + +static const uint8_t ed25519_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x65\x70"; + +/* + * Parse the public key used for signing. + */ +static int +bootutil_import_key(uint8_t **cp, uint8_t *end) +{ + size_t len; + mbedtls_asn1_buf alg; + mbedtls_asn1_buf param; + + if (mbedtls_asn1_get_tag(cp, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return -1; + } + end = *cp + len; + + if (mbedtls_asn1_get_alg(cp, end, &alg, ¶m)) { + return -2; + } + + if (alg.ASN1_CONTEXT_MEMBER(len) != sizeof(ed25519_pubkey_oid) - 1 || + memcmp(alg.ASN1_CONTEXT_MEMBER(p), ed25519_pubkey_oid, sizeof(ed25519_pubkey_oid) - 1)) { + return -3; + } + + if (mbedtls_asn1_get_bitstring_null(cp, end, &len)) { + return -4; + } + if (*cp + len != end) { + return -5; + } + + if (len != NUM_ED25519_BYTES) { + return -6; + } + + return 0; +} +#endif + +fih_ret +bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, + uint8_t key_id) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + uint8_t *pubkey = NULL; +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) + uint8_t *end; +#endif + + if (hlen != IMAGE_HASH_SIZE || slen != EDDSA_SIGNATURE_LENGTH) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) + pubkey = (uint8_t *)bootutil_keys[key_id].key; + end = pubkey + *bootutil_keys[key_id].len; + + rc = bootutil_import_key(&pubkey, end); + if (rc) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } +#endif + + rc = ED25519_verify(hash, IMAGE_HASH_SIZE, sig, pubkey); + + if (rc == 0) { + /* if verify returns 0, there was an error. */ + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + FIH_SET(fih_rc, FIH_SUCCESS); +out: + + FIH_RET(fih_rc); +} + +fih_ret +bootutil_verify_img(const uint8_t *img, uint32_t size, + uint8_t *sig, size_t slen, uint8_t key_id) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + uint8_t *pubkey = NULL; +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) + uint8_t *end; +#endif + + if (slen != EDDSA_SIGNATURE_LENGTH) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) + pubkey = (uint8_t *)bootutil_keys[key_id].key; + end = pubkey + *bootutil_keys[key_id].len; + + rc = bootutil_import_key(&pubkey, end); + if (rc) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } +#endif + + rc = ED25519_verify(img, size, sig, pubkey); + + if (rc == 0) { + /* if verify returns 0, there was an error. */ + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + FIH_SET(fih_rc, FIH_SUCCESS); +out: + + FIH_RET(fih_rc); +} + +#endif /* MCUBOOT_SIGN_ED25519 */ diff --git a/bootloader/mcuboot/boot/bootutil/src/image_rsa.c b/bootloader/mcuboot/boot/bootutil/src/image_rsa.c new file mode 100644 index 0000000..37c35e0 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/image_rsa.c @@ -0,0 +1,288 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2018 Linaro LTD + * Copyright (c) 2017-2019 JUUL Labs + * Copyright (c) 2020-2023 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include "mcuboot_config/mcuboot_config.h" + +#ifdef MCUBOOT_SIGN_RSA +#include "bootutil_priv.h" +#include "bootutil/sign_key.h" +#include "bootutil/fault_injection_hardening.h" + +#define BOOTUTIL_CRYPTO_RSA_SIGN_ENABLED +#include "bootutil/crypto/rsa.h" + +/* PSA Crypto APIs provide an integrated API to perform the verification + * while for other crypto backends we need to implement each step at this + * abstraction level + */ +#if !defined(MCUBOOT_USE_PSA_CRYPTO) + +#include "bootutil/crypto/sha.h" + +/* + * Constants for this particular constrained implementation of + * RSA-PSS. In particular, we support RSA 2048, with a SHA256 hash, + * and a 32-byte salt. A signature with different parameters will be + * rejected as invalid. + */ + +/* The size, in octets, of the message. */ +#define PSS_EMLEN (MCUBOOT_SIGN_RSA_LEN / 8) + +/* The size of the hash function. For SHA256, this is 32 bytes. */ +#define PSS_HLEN 32 + +/* Size of the salt, should be fixed. */ +#define PSS_SLEN 32 + +/* The length of the mask: emLen - hLen - 1. */ +#define PSS_MASK_LEN (PSS_EMLEN - PSS_HLEN - 1) + +#define PSS_HASH_OFFSET PSS_MASK_LEN + +/* For the mask itself, how many bytes should be all zeros. */ +#define PSS_MASK_ZERO_COUNT (PSS_MASK_LEN - PSS_SLEN - 1) +#define PSS_MASK_ONE_POS PSS_MASK_ZERO_COUNT + +/* Where the salt starts. */ +#define PSS_MASK_SALT_POS (PSS_MASK_ONE_POS + 1) + +static const uint8_t pss_zeros[8] = {0}; + +/* + * Compute the RSA-PSS mask-generation function, MGF1. Assumptions + * are that the mask length will be less than 256 * PSS_HLEN, and + * therefore we never need to increment anything other than the low + * byte of the counter. + * + * This is described in PKCS#1, B.2.1. + */ +static void +pss_mgf1(uint8_t *mask, const uint8_t *hash) +{ + bootutil_sha_context ctx; + uint8_t counter[4] = { 0, 0, 0, 0 }; + uint8_t htmp[PSS_HLEN]; + int count = PSS_MASK_LEN; + int bytes; + + while (count > 0) { + bootutil_sha_init(&ctx); + bootutil_sha_update(&ctx, hash, PSS_HLEN); + bootutil_sha_update(&ctx, counter, 4); + bootutil_sha_finish(&ctx, htmp); + + counter[3]++; + + bytes = PSS_HLEN; + if (bytes > count) + bytes = count; + + memcpy(mask, htmp, bytes); + mask += bytes; + count -= bytes; + } + + bootutil_sha_drop(&ctx); +} + +/* + * Validate an RSA signature, using RSA-PSS, as described in PKCS #1 + * v2.2, section 9.1.2, with many parameters required to have fixed + * values. RSASSA-PSS-VERIFY RFC8017 section 8.1.2 + */ +static fih_ret +bootutil_cmp_rsasig(bootutil_rsa_context *ctx, uint8_t *hash, uint32_t hlen, + uint8_t *sig, size_t slen) +{ + bootutil_sha_context shactx; + uint8_t em[MBEDTLS_MPI_MAX_SIZE]; + uint8_t db_mask[PSS_MASK_LEN]; + uint8_t h2[PSS_HLEN]; + int i; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + /* The caller has already verified that slen == bootutil_rsa_get_len(ctx) */ + if (slen != PSS_EMLEN || + PSS_EMLEN > MBEDTLS_MPI_MAX_SIZE) { + goto out; + } + + if (hlen != PSS_HLEN) { + goto out; + } + + /* Apply RSAVP1 to produce em = sig^E mod N using the public key */ + if (bootutil_rsa_public(ctx, sig, em)) { + goto out; + } + + /* + * PKCS #1 v2.2, 9.1.2 EMSA-PSS-Verify + * + * emBits is 2048 + * emLen = ceil(emBits/8) = 256 + * + * The salt length is not known at the beginning. + */ + + /* Step 1. The message is constrained by the address space of a + * 32-bit processor, which is far less than the 2^61-1 limit of + * SHA-256. + */ + + /* Step 2. mHash is passed in as 'hash', with hLen the hlen + * argument. */ + + /* Step 3. if emLen < hLen + sLen + 2, inconsistent and stop. + * The salt length is not known at this point. + */ + + /* Step 4. If the rightmost octet of EM does have the value + * 0xbc, output inconsistent and stop. + */ + if (em[PSS_EMLEN - 1] != 0xbc) { + goto out; + } + + /* Step 5. Let maskedDB be the leftmost emLen - hLen - 1 octets + * of EM, and H be the next hLen octets. + * + * maskedDB is then the first 256 - 32 - 1 = 0-222 + * H is 32 bytes 223-254 + */ + + /* Step 6. If the leftmost 8emLen - emBits bits of the leftmost + * octet in maskedDB are not all equal to zero, output + * inconsistent and stop. + * + * 8emLen - emBits is zero, so there is nothing to test here. + */ + + /* Step 7. let dbMask = MGF(H, emLen - hLen - 1). */ + pss_mgf1(db_mask, &em[PSS_HASH_OFFSET]); + + /* Step 8. let DB = maskedDB xor dbMask. + * To avoid needing an additional buffer, store the 'db' in the + * same buffer as db_mask. From now, to the end of this function, + * db_mask refers to the unmasked 'db'. */ + for (i = 0; i < PSS_MASK_LEN; i++) { + db_mask[i] ^= em[i]; + } + + /* Step 9. Set the leftmost 8emLen - emBits bits of the leftmost + * octet in DB to zero. + * pycrypto seems to always make the emBits 2047, so we need to + * clear the top bit. */ + db_mask[0] &= 0x7F; + + /* Step 10. If the emLen - hLen - sLen - 2 leftmost octets of DB + * are not zero or if the octet at position emLen - hLen - sLen - + * 1 (the leftmost position is "position 1") does not have + * hexadecimal value 0x01, output "inconsistent" and stop. */ + for (i = 0; i < PSS_MASK_ZERO_COUNT; i++) { + if (db_mask[i] != 0) { + goto out; + } + } + + if (db_mask[PSS_MASK_ONE_POS] != 1) { + goto out; + } + + /* Step 11. Let salt be the last sLen octets of DB */ + + /* Step 12. Let M' = 0x00 00 00 00 00 00 00 00 || mHash || salt; */ + + /* Step 13. Let H' = Hash(M') */ + bootutil_sha_init(&shactx); + bootutil_sha_update(&shactx, pss_zeros, 8); + bootutil_sha_update(&shactx, hash, PSS_HLEN); + bootutil_sha_update(&shactx, &db_mask[PSS_MASK_SALT_POS], PSS_SLEN); + bootutil_sha_finish(&shactx, h2); + bootutil_sha_drop(&shactx); + + /* Step 14. If H = H', output "consistent". Otherwise, output + * "inconsistent". */ + FIH_CALL(boot_fih_memequal, fih_rc, h2, &em[PSS_HASH_OFFSET], PSS_HLEN); + +out: + FIH_RET(fih_rc); +} + +#else /* MCUBOOT_USE_PSA_CRYPTO */ + +static fih_ret +bootutil_cmp_rsasig(bootutil_rsa_context *ctx, uint8_t *hash, uint32_t hlen, + uint8_t *sig, size_t slen) +{ + int rc = -1; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + /* PSA Crypto APIs allow the verification in a single call */ + rc = bootutil_rsassa_pss_verify(ctx, hash, hlen, sig, slen); + + fih_rc = fih_ret_encode_zero_equality(rc); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} + +#endif /* MCUBOOT_USE_PSA_CRYPTO */ + +fih_ret +bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, + uint8_t key_id) +{ + bootutil_rsa_context ctx; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + uint8_t *cp; + uint8_t *end; + + bootutil_rsa_init(&ctx); + + cp = (uint8_t *)bootutil_keys[key_id].key; + end = cp + *bootutil_keys[key_id].len; + + /* The key used for signature verification is a public RSA key */ + rc = bootutil_rsa_parse_public_key(&ctx, &cp, end); + if (rc || slen != bootutil_rsa_get_len(&ctx)) { + goto out; + } + FIH_CALL(bootutil_cmp_rsasig, fih_rc, &ctx, hash, hlen, sig, slen); + +out: + bootutil_rsa_drop(&ctx); + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_SIGN_RSA */ diff --git a/bootloader/mcuboot/boot/bootutil/src/image_validate.c b/bootloader/mcuboot/boot/bootutil/src/image_validate.c new file mode 100644 index 0000000..f71d1d9 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/image_validate.c @@ -0,0 +1,920 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2024 Arm Limited + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include + +#include "bootutil/image.h" +#include "bootutil/crypto/sha.h" +#include "bootutil/sign_key.h" +#include "bootutil/security_cnt.h" +#include "bootutil/fault_injection_hardening.h" + +#include "mcuboot_config/mcuboot_config.h" + +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif +#if defined(MCUBOOT_SIGN_RSA) +#include "mbedtls/rsa.h" +#endif +#if defined(MCUBOOT_SIGN_EC256) +#include "mbedtls/ecdsa.h" +#endif +#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SIGN_RSA) || \ + defined(MCUBOOT_SIGN_EC256) +#include "mbedtls/asn1.h" +#endif + +#include "bootutil_priv.h" + +#ifndef MCUBOOT_SIGN_PURE +/* + * Compute SHA hash over the image. + * (SHA384 if ECDSA-P384 is being used, + * SHA256 otherwise). + */ +static int +bootutil_img_hash(struct enc_key_data *enc_state, int image_index, + struct image_header *hdr, const struct flash_area *fap, + uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len) +{ + bootutil_sha_context sha_ctx; + uint32_t size; + uint16_t hdr_size; + uint32_t blk_off; + uint32_t tlv_off; +#if !defined(MCUBOOT_HASH_STORAGE_DIRECTLY) + int rc; + uint32_t off; + uint32_t blk_sz; +#endif + +#if (BOOT_IMAGE_NUMBER == 1) || !defined(MCUBOOT_ENC_IMAGES) || \ + defined(MCUBOOT_RAM_LOAD) + (void)enc_state; + (void)image_index; + (void)hdr_size; + (void)blk_off; + (void)tlv_off; +#ifdef MCUBOOT_RAM_LOAD + (void)blk_sz; + (void)off; + (void)rc; + (void)fap; + (void)tmp_buf; + (void)tmp_buf_sz; +#endif +#endif + +#ifdef MCUBOOT_ENC_IMAGES + /* Encrypted images only exist in the secondary slot */ + if (MUST_DECRYPT(fap, image_index, hdr) && + !boot_enc_valid(enc_state, 1)) { + return -1; + } +#endif + + bootutil_sha_init(&sha_ctx); + + /* in some cases (split image) the hash is seeded with data from + * the loader image */ + if (seed && (seed_len > 0)) { + bootutil_sha_update(&sha_ctx, seed, seed_len); + } + + /* Hash is computed over image header and image itself. */ + size = hdr_size = hdr->ih_hdr_size; + size += hdr->ih_img_size; + tlv_off = size; + + /* If protected TLVs are present they are also hashed. */ + size += hdr->ih_protect_tlv_size; + +#ifdef MCUBOOT_HASH_STORAGE_DIRECTLY + /* No chunk loading, storage is mapped to address space and can + * be directly given to hashing function. + */ + bootutil_sha_update(&sha_ctx, (void *)flash_area_get_off(fap), size); +#else /* MCUBOOT_HASH_STORAGE_DIRECTLY */ +#ifdef MCUBOOT_RAM_LOAD + bootutil_sha_update(&sha_ctx, + (void*)(IMAGE_RAM_BASE + hdr->ih_load_addr), + size); +#else + for (off = 0; off < size; off += blk_sz) { + blk_sz = size - off; + if (blk_sz > tmp_buf_sz) { + blk_sz = tmp_buf_sz; + } +#ifdef MCUBOOT_ENC_IMAGES + /* The only data that is encrypted in an image is the payload; + * both header and TLVs (when protected) are not. + */ + if ((off < hdr_size) && ((off + blk_sz) > hdr_size)) { + /* read only the header */ + blk_sz = hdr_size - off; + } + if ((off < tlv_off) && ((off + blk_sz) > tlv_off)) { + /* read only up to the end of the image payload */ + blk_sz = tlv_off - off; + } +#endif + rc = flash_area_read(fap, off, tmp_buf, blk_sz); + if (rc) { + bootutil_sha_drop(&sha_ctx); + return rc; + } +#ifdef MCUBOOT_ENC_IMAGES + if (MUST_DECRYPT(fap, image_index, hdr)) { + /* Only payload is encrypted (area between header and TLVs) */ + int slot = flash_area_id_to_multi_image_slot(image_index, + flash_area_get_id(fap)); + + if (off >= hdr_size && off < tlv_off) { + blk_off = (off - hdr_size) & 0xf; + boot_enc_decrypt(enc_state, slot, off - hdr_size, + blk_sz, blk_off, tmp_buf); + } + } +#endif + bootutil_sha_update(&sha_ctx, tmp_buf, blk_sz); + } +#endif /* MCUBOOT_RAM_LOAD */ +#endif /* MCUBOOT_HASH_STORAGE_DIRECTLY */ + bootutil_sha_finish(&sha_ctx, hash_result); + bootutil_sha_drop(&sha_ctx); + + return 0; +} +#endif + +/* + * Currently, we only support being able to verify one type of + * signature, because there is a single verification function that we + * call. List the type of TLV we are expecting. If we aren't + * configured for any signature, don't define this macro. + */ +#if (defined(MCUBOOT_SIGN_RSA) + \ + defined(MCUBOOT_SIGN_EC256) + \ + defined(MCUBOOT_SIGN_EC384) + \ + defined(MCUBOOT_SIGN_ED25519)) > 1 +#error "Only a single signature type is supported!" +#endif + +#if defined(MCUBOOT_SIGN_RSA) +# if MCUBOOT_SIGN_RSA_LEN == 2048 +# define EXPECTED_SIG_TLV IMAGE_TLV_RSA2048_PSS +# elif MCUBOOT_SIGN_RSA_LEN == 3072 +# define EXPECTED_SIG_TLV IMAGE_TLV_RSA3072_PSS +# else +# error "Unsupported RSA signature length" +# endif +# define SIG_BUF_SIZE (MCUBOOT_SIGN_RSA_LEN / 8) +# define EXPECTED_SIG_LEN(x) ((x) == SIG_BUF_SIZE) /* 2048 bits */ +#elif defined(MCUBOOT_SIGN_EC256) || \ + defined(MCUBOOT_SIGN_EC384) || \ + defined(MCUBOOT_SIGN_EC) +# define EXPECTED_SIG_TLV IMAGE_TLV_ECDSA_SIG +# define SIG_BUF_SIZE 128 +# define EXPECTED_SIG_LEN(x) (1) /* always true, ASN.1 will validate */ +#elif defined(MCUBOOT_SIGN_ED25519) +# define EXPECTED_SIG_TLV IMAGE_TLV_ED25519 +# define SIG_BUF_SIZE 64 +# define EXPECTED_SIG_LEN(x) ((x) == SIG_BUF_SIZE) +#else +# define SIG_BUF_SIZE 32 /* no signing, sha256 digest only */ +#endif + +#if (defined(MCUBOOT_HW_KEY) + \ + defined(MCUBOOT_BUILTIN_KEY)) > 1 +#error "Please use either MCUBOOT_HW_KEY or the MCUBOOT_BUILTIN_KEY feature." +#endif + +#ifdef EXPECTED_SIG_TLV + +#if !defined(MCUBOOT_BUILTIN_KEY) +#if !defined(MCUBOOT_HW_KEY) +/* The key TLV contains the hash of the public key. */ +# define EXPECTED_KEY_TLV IMAGE_TLV_KEYHASH +# define KEY_BUF_SIZE IMAGE_HASH_SIZE +#else +/* The key TLV contains the whole public key. + * Add a few extra bytes to the key buffer size for encoding and + * for public exponent. + */ +# define EXPECTED_KEY_TLV IMAGE_TLV_PUBKEY +# define KEY_BUF_SIZE (SIG_BUF_SIZE + 24) +#endif /* !MCUBOOT_HW_KEY */ + +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +#if !defined(MCUBOOT_HW_KEY) +static int +bootutil_find_key(uint8_t *keyhash, uint8_t keyhash_len) +{ + bootutil_sha_context sha_ctx; + int i; + const struct bootutil_key *key; + uint8_t hash[IMAGE_HASH_SIZE]; + + if (keyhash_len > IMAGE_HASH_SIZE) { + return -1; + } + + for (i = 0; i < bootutil_key_cnt; i++) { + key = &bootutil_keys[i]; + bootutil_sha_init(&sha_ctx); + bootutil_sha_update(&sha_ctx, key->key, *key->len); + bootutil_sha_finish(&sha_ctx, hash); + if (!memcmp(hash, keyhash, keyhash_len)) { + bootutil_sha_drop(&sha_ctx); + return i; + } + } + bootutil_sha_drop(&sha_ctx); + return -1; +} +#else /* !MCUBOOT_HW_KEY */ +extern unsigned int pub_key_len; +static int +bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len) +{ + bootutil_sha_context sha_ctx; + uint8_t hash[IMAGE_HASH_SIZE]; + uint8_t key_hash[IMAGE_HASH_SIZE]; + size_t key_hash_size = sizeof(key_hash); + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + bootutil_sha_init(&sha_ctx); + bootutil_sha_update(&sha_ctx, key, key_len); + bootutil_sha_finish(&sha_ctx, hash); + bootutil_sha_drop(&sha_ctx); + + rc = boot_retrieve_public_key_hash(image_index, key_hash, &key_hash_size); + if (rc) { + return -1; + } + + /* Adding hardening to avoid this potential attack: + * - Image is signed with an arbitrary key and the corresponding public + * key is added as a TLV field. + * - During public key validation (comparing against key-hash read from + * HW) a fault is injected to accept the public key as valid one. + */ + FIH_CALL(boot_fih_memequal, fih_rc, hash, key_hash, key_hash_size); + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + bootutil_keys[0].key = key; + pub_key_len = key_len; + return 0; + } + + return -1; +} +#endif /* !MCUBOOT_HW_KEY */ +#endif /* !MCUBOOT_BUILTIN_KEY */ +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) */ +#endif /* EXPECTED_SIG_TLV */ + +/** + * Reads the value of an image's security counter. + * + * @param hdr Pointer to the image header structure. + * @param fap Pointer to a description structure of the image's + * flash area. + * @param security_cnt Pointer to store the security counter value. + * + * @return 0 on success; nonzero on failure. + */ +int32_t +bootutil_get_img_security_cnt(struct image_header *hdr, + const struct flash_area *fap, + uint32_t *img_security_cnt) +{ + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + if ((hdr == NULL) || + (fap == NULL) || + (img_security_cnt == NULL)) { + /* Invalid parameter. */ + return BOOT_EBADARGS; + } + + /* The security counter TLV is in the protected part of the TLV area. */ + if (hdr->ih_protect_tlv_size == 0) { + return BOOT_EBADIMAGE; + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_SEC_CNT, true); + if (rc) { + return rc; + } + + /* Traverse through the protected TLV area to find + * the security counter TLV. + */ + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + if (rc != 0) { + /* Security counter TLV has not been found. */ + return -1; + } + + if (len != sizeof(*img_security_cnt)) { + /* Security counter is not valid. */ + return BOOT_EBADIMAGE; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, img_security_cnt, len); + if (rc != 0) { + return BOOT_EFLASH; + } + + return 0; +} + +#if defined(MCUBOOT_SIGN_PURE) +/* Returns: + * 0 -- found + * 1 -- not found or found but not true + * -1 -- failed for some reason + * + * Value of TLV does not matter, presence decides. + */ +static int bootutil_check_for_pure(const struct image_header *hdr, + const struct flash_area *fap) +{ + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_SIG_PURE, false); + if (rc) { + return rc; + } + + /* Search for the TLV */ + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + if (rc == 0 && len == 1) { + bool val; + + rc = LOAD_IMAGE_DATA(hdr, fap, off, &val, 1); + if (rc == 0) { + rc = !val; + } + } + + return rc; +} +#endif + + +#ifndef ALLOW_ROGUE_TLVS +/* + * The following list of TLVs are the only entries allowed in the unprotected + * TLV section. All other TLV entries must be in the protected section. + */ +static const uint16_t allowed_unprot_tlvs[] = { + IMAGE_TLV_KEYHASH, + IMAGE_TLV_PUBKEY, + IMAGE_TLV_SHA256, + IMAGE_TLV_SHA384, + IMAGE_TLV_SHA512, + IMAGE_TLV_RSA2048_PSS, + IMAGE_TLV_ECDSA224, + IMAGE_TLV_ECDSA_SIG, + IMAGE_TLV_RSA3072_PSS, + IMAGE_TLV_ED25519, +#if defined(MCUBOOT_SIGN_PURE) + IMAGE_TLV_SIG_PURE, +#endif + IMAGE_TLV_ENC_RSA2048, + IMAGE_TLV_ENC_KW, + IMAGE_TLV_ENC_EC256, + IMAGE_TLV_ENC_X25519, + /* Mark end with ANY. */ + IMAGE_TLV_ANY, +}; +#endif + +/* + * Verify the integrity of the image. + * Return non-zero if image could not be validated/does not validate. + */ +fih_ret +bootutil_img_validate(struct enc_key_data *enc_state, int image_index, + struct image_header *hdr, const struct flash_area *fap, + uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *seed, + int seed_len, uint8_t *out_hash) +{ + uint32_t off; + uint16_t len; + uint16_t type; +#ifdef EXPECTED_SIG_TLV + FIH_DECLARE(valid_signature, FIH_FAILURE); +#ifndef MCUBOOT_BUILTIN_KEY + int key_id = -1; +#else + /* Pass a key ID equal to the image index, the underlying crypto library + * is responsible for mapping the image index to a builtin key ID. + */ + int key_id = image_index; +#endif /* !MCUBOOT_BUILTIN_KEY */ +#ifdef MCUBOOT_HW_KEY + uint8_t key_buf[KEY_BUF_SIZE]; +#endif +#endif /* EXPECTED_SIG_TLV */ + struct image_tlv_iter it; + uint8_t buf[SIG_BUF_SIZE]; +#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) + int image_hash_valid = 0; + uint8_t hash[IMAGE_HASH_SIZE]; +#endif + int rc = 0; + FIH_DECLARE(fih_rc, FIH_FAILURE); +#ifdef MCUBOOT_HW_ROLLBACK_PROT + fih_int security_cnt = fih_int_encode(INT_MAX); + uint32_t img_security_cnt = 0; + FIH_DECLARE(security_counter_valid, FIH_FAILURE); +#endif + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + /* If the image is compressed, the integrity of the image must also be validated */ + if (MUST_DECOMPRESS(fap, image_index, hdr)) { + bool found_decompressed_size = false; + bool found_decompressed_sha = false; + bool found_decompressed_signature = false; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(fap)) { + rc = -1; + goto out; + } + + while (true) { + uint16_t expected_size = 0; + bool *found_flag = NULL; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + switch (type) { + case IMAGE_TLV_DECOMP_SIZE: + expected_size = sizeof(size_t); + found_flag = &found_decompressed_size; + break; + case IMAGE_TLV_DECOMP_SHA: + expected_size = IMAGE_HASH_SIZE; + found_flag = &found_decompressed_sha; + break; + case IMAGE_TLV_DECOMP_SIGNATURE: + found_flag = &found_decompressed_signature; + break; + default: + continue; + }; + + if (type == IMAGE_TLV_DECOMP_SIGNATURE && !EXPECTED_SIG_LEN(len)) { + rc = -1; + goto out; + } else if (type != IMAGE_TLV_DECOMP_SIGNATURE && len != expected_size) { + rc = -1; + goto out; + } + + *found_flag = true; + } + + rc = (!found_decompressed_size || !found_decompressed_sha || !found_decompressed_signature); + if (rc) { + goto out; + } + } +#endif + +#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) + rc = bootutil_img_hash(enc_state, image_index, hdr, fap, tmp_buf, + tmp_buf_sz, hash, seed, seed_len); + if (rc) { + goto out; + } + + if (out_hash) { + memcpy(out_hash, hash, IMAGE_HASH_SIZE); + } +#endif + +#if defined(MCUBOOT_SIGN_PURE) + /* If Pure type signature is expected then it has to be there */ + rc = bootutil_check_for_pure(hdr, fap); + if (rc != 0) { + goto out; + } +#endif + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(fap)) { + rc = -1; + goto out; + } + + /* + * Traverse through all of the TLVs, performing any checks we know + * and are able to do. + */ + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + +#ifndef ALLOW_ROGUE_TLVS + /* + * Ensure that the non-protected TLV only has entries necessary to hold + * the signature. We also allow encryption related keys to be in the + * unprotected area. + */ + if (!bootutil_tlv_iter_is_prot(&it, off)) { + bool found = false; + for (const uint16_t *p = allowed_unprot_tlvs; *p != IMAGE_TLV_ANY; p++) { + if (type == *p) { + found = true; + break; + } + } + if (!found) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } +#endif + switch(type) { +#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) + case EXPECTED_HASH_TLV: + { + /* Verify the image hash. This must always be present. */ + if (len != sizeof(hash)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, sizeof(hash)); + if (rc) { + goto out; + } + + FIH_CALL(boot_fih_memequal, fih_rc, hash, buf, sizeof(hash)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + image_hash_valid = 1; + break; + } +#endif /* defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) */ +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +#ifdef EXPECTED_KEY_TLV + case EXPECTED_KEY_TLV: + { + /* + * Determine which key we should be checking. + */ + if (len > KEY_BUF_SIZE) { + rc = -1; + goto out; + } +#ifndef MCUBOOT_HW_KEY + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(buf, len); +#else + rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(image_index, key_buf, len); +#endif /* !MCUBOOT_HW_KEY */ + /* + * The key may not be found, which is acceptable. There + * can be multiple signatures, each preceded by a key. + */ + break; + } +#endif /* EXPECTED_KEY_TLV */ +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) */ +#ifdef EXPECTED_SIG_TLV + case EXPECTED_SIG_TLV: + { +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) + /* Ignore this signature if it is out of bounds. */ + if (key_id < 0 || key_id >= bootutil_key_cnt) { + key_id = -1; + continue; + } +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) */ + if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } +#ifndef MCUBOOT_SIGN_PURE + FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash), + buf, len, key_id); +#else + /* Directly check signature on the image, by using the mapping of + * a device to memory. The pointer is beginning of image in flash, + * so offset of area, the range is header + image + protected tlvs. + */ + FIH_CALL(bootutil_verify_img, valid_signature, (void *)flash_area_get_off(fap), + hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_protect_tlv_size, + buf, len, key_id); +#endif + key_id = -1; + break; + } +#endif /* EXPECTED_SIG_TLV */ +#ifdef MCUBOOT_HW_ROLLBACK_PROT + case IMAGE_TLV_SEC_CNT: + { + /* + * Verify the image's security counter. + * This must always be present. + */ + if (len != sizeof(img_security_cnt)) { + /* Security counter is not valid. */ + rc = -1; + goto out; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, &img_security_cnt, len); + if (rc) { + goto out; + } + + FIH_CALL(boot_nv_security_counter_get, fih_rc, image_index, + &security_cnt); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + /* Compare the new image's security counter value against the + * stored security counter value. + */ + fih_rc = fih_ret_encode_zero_equality(img_security_cnt < + (uint32_t)fih_int_decode(security_cnt)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + /* The image's security counter has been successfully verified. */ + security_counter_valid = fih_rc; + break; + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + } + } + +#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) + rc = !image_hash_valid; + if (rc) { + goto out; + } +#elif defined(MCUBOOT_SIGN_PURE) + /* This returns true on EQ, rc is err on non-0 */ + rc = FIH_NOT_EQ(valid_signature, FIH_SUCCESS); +#endif +#ifdef EXPECTED_SIG_TLV + FIH_SET(fih_rc, valid_signature); +#endif +#ifdef MCUBOOT_HW_ROLLBACK_PROT + if (FIH_NOT_EQ(security_counter_valid, FIH_SUCCESS)) { + rc = -1; + goto out; + } +#endif + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + /* Only after all previous verifications have passed, perform a dry-run of the decompression + * and ensure the image is valid + */ + if (!rc && MUST_DECOMPRESS(fap, image_index, hdr)) { + image_hash_valid = 0; + FIH_SET(valid_signature, FIH_FAILURE); + + rc = bootutil_img_hash_decompress(enc_state, image_index, hdr, fap, tmp_buf, tmp_buf_sz, + hash, seed, seed_len); + if (rc) { + goto out; + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SHA, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + if (type == IMAGE_TLV_DECOMP_SHA) { + /* Verify the image hash. This must always be present. */ + if (len != sizeof(hash)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, sizeof(hash)); + if (rc) { + goto out; + } + + FIH_CALL(boot_fih_memequal, fih_rc, hash, buf, sizeof(hash)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + image_hash_valid = 1; + } + } + + rc = !image_hash_valid; + if (rc) { + goto out; + } + +#ifdef EXPECTED_SIG_TLV +#ifdef EXPECTED_KEY_TLV + rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_KEY_TLV, false); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + if (type == EXPECTED_KEY_TLV) { + /* + * Determine which key we should be checking. + */ + if (len > KEY_BUF_SIZE) { + rc = -1; + goto out; + } +#ifndef MCUBOOT_HW_KEY + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(buf, len); +#else + rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(image_index, key_buf, len); +#endif /* !MCUBOOT_HW_KEY */ + /* + * The key may not be found, which is acceptable. There + * can be multiple signatures, each preceded by a key. + */ + } + } +#endif /* EXPECTED_KEY_TLV */ + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SIGNATURE, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIGNATURE) { + /* Ignore this signature if it is out of bounds. */ + if (key_id < 0 || key_id >= bootutil_key_cnt) { + key_id = -1; + continue; + } + + if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + + FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash), + buf, len, key_id); + key_id = -1; + } + } +#endif /* EXPECTED_SIG_TLV */ + } +#endif + +#ifdef EXPECTED_SIG_TLV + FIH_SET(fih_rc, valid_signature); +#endif + +out: + if (rc) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} diff --git a/bootloader/mcuboot/boot/bootutil/src/loader.c b/bootloader/mcuboot/boot/bootutil/src/loader.c new file mode 100644 index 0000000..f35ec78 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/loader.c @@ -0,0 +1,3974 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2016-2020 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2023 Arm Limited + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * This file provides an interface to the boot loader. Functions defined in + * this file should only be called while the boot loader is running. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/security_cnt.h" +#include "bootutil/boot_record.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/ramload.h" +#include "bootutil/boot_hooks.h" +#include "bootutil/mcuboot_status.h" + +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#ifdef __ZEPHYR__ +#include +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) +#include +#ifdef CONFIG_PCD_READ_NETCORE_APP_VERSION +#include +int pcd_version_cmp_net(const struct flash_area *fap, struct image_header *hdr); +#endif +#endif + +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +#include +#endif + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static struct boot_loader_state boot_data; +#ifdef PM_S1_ADDRESS +static bool owner_nsib[BOOT_IMAGE_NUMBER] = {false}; +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0}; +#endif + +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +#if !defined(__BOOTSIM__) +/* Used for holding static buffers in multiple functions to work around issues + * in older versions of gcc (e.g. 4.8.4) + */ +struct sector_buffer_t { + boot_sector_t primary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + boot_sector_t secondary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; +#if MCUBOOT_SWAP_USING_SCRATCH + boot_sector_t scratch[BOOT_MAX_IMG_SECTORS]; +#endif +}; + +static struct sector_buffer_t sector_buffers; +#endif +#endif + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 && defined(MCUBOOT_OVERWRITE_ONLY) && \ + defined(MCUBOOT_DOWNGRADE_PREVENTION) +/* s0/s1 package version of the current MCUboot image */ +static const struct image_version mcuboot_s0_s1_image_version = { + .iv_major = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MAJOR, + .iv_minor = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MINOR, + .iv_revision = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_REVISION, + .iv_build_num = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_BUILD_NUMBER, +}; +#endif + +#if (BOOT_IMAGE_NUMBER > 1) +#define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x)) +#else +#define IMAGES_ITER(x) +#endif + +/* + * This macro allows some control on the allocation of local variables. + * When running natively on a target, we don't want to allocated huge + * variables on the stack, so make them global instead. For the simulator + * we want to run as many threads as there are tests, and it's safer + * to just make those variables stack allocated. + */ +#if !defined(__BOOTSIM__) +#define TARGET_STATIC static +#else +#define TARGET_STATIC +#endif + +#if BOOT_MAX_ALIGN > 1024 +#define BUF_SZ BOOT_MAX_ALIGN +#else +#define BUF_SZ 1024 +#endif + +#define NO_ACTIVE_SLOT UINT32_MAX + +static int +boot_read_image_headers(struct boot_loader_state *state, bool require_all, + struct boot_status *bs) +{ + int rc; + int i; + + for (i = 0; i < BOOT_NUM_SLOTS; i++) { + rc = BOOT_HOOK_CALL(boot_read_image_header_hook, BOOT_HOOK_REGULAR, + BOOT_CURR_IMG(state), i, boot_img_hdr(state, i)); + if (rc == BOOT_HOOK_REGULAR) + { + rc = boot_read_image_header(state, i, boot_img_hdr(state, i), bs); + } + if (rc != 0) { + /* If `require_all` is set, fail on any single fail, otherwise + * if at least the first slot's header was read successfully, + * then the boot loader can attempt a boot. + * + * Failure to read any headers is a fatal error. + */ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + /* Patch needed for NCS. The primary slot of the second image + * (image 1) will not contain a valid image header until an upgrade + * of mcuboot has happened (filling S1 with the new version). + */ + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER && i == 0) { + continue; + } +#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ + if (i > 0 && !require_all) { + return 0; + } else { + return rc; + } + } + } + + return 0; +} + +/** + * Saves boot status and shared data for current image. + * + * @param state Boot loader status information. + * @param active_slot Index of the slot will be loaded for current image. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_add_shared_data(struct boot_loader_state *state, + uint8_t active_slot) +{ +#if defined(MCUBOOT_MEASURED_BOOT) || defined(MCUBOOT_DATA_SHARING) + int rc; + +#ifdef MCUBOOT_MEASURED_BOOT + rc = boot_save_boot_status(BOOT_CURR_IMG(state), + boot_img_hdr(state, active_slot), + BOOT_IMG_AREA(state, active_slot)); + if (rc != 0) { + BOOT_LOG_ERR("Failed to add image data to shared area"); + return rc; + } +#endif /* MCUBOOT_MEASURED_BOOT */ + +#ifdef MCUBOOT_DATA_SHARING + rc = boot_save_shared_data(boot_img_hdr(state, active_slot), + BOOT_IMG_AREA(state, active_slot), + active_slot, image_max_sizes); + if (rc != 0) { + BOOT_LOG_ERR("Failed to add data to shared memory area."); + return rc; + } +#endif /* MCUBOOT_DATA_SHARING */ + + return 0; + +#else /* MCUBOOT_MEASURED_BOOT || MCUBOOT_DATA_SHARING */ + (void) (state); + (void) (active_slot); + + return 0; +#endif +} + +/** + * Fills rsp to indicate how booting should occur. + * + * @param state Boot loader status information. + * @param rsp boot_rsp struct to fill. + */ +static void +fill_rsp(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + uint32_t active_slot; + +#if (BOOT_IMAGE_NUMBER > 1) + /* Always boot from the first enabled image. */ + BOOT_CURR_IMG(state) = 0; + IMAGES_ITER(BOOT_CURR_IMG(state)) { + if (!state->img_mask[BOOT_CURR_IMG(state)]) { + break; + } + } + /* At least one image must be active, otherwise skip the execution */ + if (BOOT_CURR_IMG(state) >= BOOT_IMAGE_NUMBER) { + return; + } +#endif + +#if defined(MCUBOOT_DIRECT_XIP) || defined(MCUBOOT_RAM_LOAD) + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; +#else + active_slot = BOOT_PRIMARY_SLOT; +#endif + + rsp->br_flash_dev_id = flash_area_get_device_id(BOOT_IMG_AREA(state, active_slot)); + rsp->br_image_off = boot_img_slot_off(state, active_slot); + rsp->br_hdr = boot_img_hdr(state, active_slot); +} + +/** + * Closes all flash areas. + * + * @param state Boot loader status information. + */ +static void +close_all_flash_areas(struct boot_loader_state *state) +{ + uint32_t slot; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif +#if MCUBOOT_SWAP_USING_SCRATCH + flash_area_close(BOOT_SCRATCH_AREA(state)); +#endif + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + flash_area_close(BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot)); + } + } +} + +#if (BOOT_IMAGE_NUMBER > 1) || \ + defined(MCUBOOT_DIRECT_XIP) || \ + defined(MCUBOOT_RAM_LOAD) || \ + defined(MCUBOOT_DOWNGRADE_PREVENTION) +/** + * Compare image version numbers + * + * By default, the comparison does not take build number into account. + * Enable MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER to take the build number into account. + * + * @param ver1 Pointer to the first image version to compare. + * @param ver2 Pointer to the second image version to compare. + * + * @retval -1 If ver1 is less than ver2. + * @retval 0 If the image version numbers are equal. + * @retval 1 If ver1 is greater than ver2. + */ +static int +boot_version_cmp(const struct image_version *ver1, + const struct image_version *ver2) +{ + if (ver1->iv_major > ver2->iv_major) { + return 1; + } + if (ver1->iv_major < ver2->iv_major) { + return -1; + } + /* The major version numbers are equal, continue comparison. */ + if (ver1->iv_minor > ver2->iv_minor) { + return 1; + } + if (ver1->iv_minor < ver2->iv_minor) { + return -1; + } + /* The minor version numbers are equal, continue comparison. */ + if (ver1->iv_revision > ver2->iv_revision) { + return 1; + } + if (ver1->iv_revision < ver2->iv_revision) { + return -1; + } + +#if defined(MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER) + /* The revisions are equal, continue comparison. */ + if (ver1->iv_build_num > ver2->iv_build_num) { + return 1; + } + if (ver1->iv_build_num < ver2->iv_build_num) { + return -1; + } +#endif + + return 0; +} +#endif + +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_initialize_area(struct boot_loader_state *state, int flash_area) +{ + uint32_t num_sectors = BOOT_MAX_IMG_SECTORS; + boot_sector_t *out_sectors; + uint32_t *out_num_sectors; + int rc; + + num_sectors = BOOT_MAX_IMG_SECTORS; + + if (flash_area == FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state))) { + out_sectors = BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors; + out_num_sectors = &BOOT_IMG(state, BOOT_PRIMARY_SLOT).num_sectors; + } else if (flash_area == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { + out_sectors = BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors; + out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors; +#if MCUBOOT_SWAP_USING_SCRATCH + } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) { + out_sectors = state->scratch.sectors; + out_num_sectors = &state->scratch.num_sectors; +#endif + } else { + return BOOT_EFLASH; + } + +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS + rc = flash_area_get_sectors(flash_area, &num_sectors, out_sectors); +#else + _Static_assert(sizeof(int) <= sizeof(uint32_t), "Fix needed"); + rc = flash_area_to_sectors(flash_area, (int *)&num_sectors, out_sectors); +#endif /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ + if (rc != 0) { + return rc; + } + *out_num_sectors = num_sectors; + return 0; +} +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_read_sectors_recovery(struct boot_loader_state *state) +{ + uint8_t image_index; + int rc; + + image_index = BOOT_CURR_IMG(state); + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SECONDARY(image_index)); + if (rc != 0) { + /* We need to differentiate from the primary image issue */ + return BOOT_EFLASH_SEC; + } + + return 0; +} +#endif + + +#if (BOOT_IMAGE_NUMBER > 1) + +static int +boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot); + +/** + * Check the image dependency whether it is satisfied and modify + * the swap type if necessary. + * + * @param dep Image dependency which has to be verified. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_slot_dependency(struct boot_loader_state *state, + struct image_dependency *dep) +{ + struct image_version *dep_version; + size_t dep_slot; + int rc; + + /* Determine the source of the image which is the subject of + * the dependency and get it's version. */ +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + uint8_t swap_type = state->swap_type[dep->image_id]; + dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SECONDARY_SLOT + : BOOT_PRIMARY_SLOT; +#else + dep_slot = state->slot_usage[dep->image_id].active_slot; +#endif + + dep_version = &state->imgs[dep->image_id][dep_slot].hdr.ih_ver; + + rc = boot_version_cmp(dep_version, &dep->image_min_version); +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + if (rc < 0) { + /* Dependency not satisfied. + * Modify the swap type to decrease the version number of the image + * (which will be located in the primary slot after the boot process), + * consequently the number of unsatisfied dependencies will be + * decreased or remain the same. + */ + switch (BOOT_SWAP_TYPE(state)) { + case BOOT_SWAP_TYPE_TEST: + case BOOT_SWAP_TYPE_PERM: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + break; + case BOOT_SWAP_TYPE_NONE: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_REVERT; + break; + default: + break; + } + } else { + /* Dependency satisfied. */ + rc = 0; + } +#else + if (rc >= 0) { + /* Dependency satisfied. */ + rc = 0; + } +#endif + + return rc; +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +/** + * Iterate over all the images and verify whether the image dependencies in the + * TLV area are all satisfied and update the related swap type if necessary. + */ +static int +boot_verify_dependencies(struct boot_loader_state *state) +{ + int rc = -1; + uint8_t slot; + + BOOT_CURR_IMG(state) = 0; + while (BOOT_CURR_IMG(state) < BOOT_IMAGE_NUMBER) { + if (state->img_mask[BOOT_CURR_IMG(state)]) { + BOOT_CURR_IMG(state)++; + continue; + } + if (BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_NONE && + BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_FAIL) { + slot = BOOT_SECONDARY_SLOT; + } else { + slot = BOOT_PRIMARY_SLOT; + } + + rc = boot_verify_slot_dependencies(state, slot); + if (rc == 0) { + /* All dependencies've been satisfied, continue with next image. */ + BOOT_CURR_IMG(state)++; + } else if (rc == BOOT_EBADIMAGE) { + /* Cannot upgrade due to non-met dependencies, so disable all + * image upgrades. + */ + for (int idx = 0; idx < BOOT_IMAGE_NUMBER; idx++) { + BOOT_CURR_IMG(state) = idx; + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } + break; + } else { + /* Other error happened, images are inconsistent */ + return rc; + } + } + return rc; +} +#else + +#if defined MCUBOOT_RAM_LOAD +static inline int +boot_remove_image_from_sram(struct boot_loader_state *state); +#endif + +/** + * Checks the dependency of all the active slots. If an image found with + * invalid or not satisfied dependencies the image is removed from SRAM (in + * case of MCUBOOT_RAM_LOAD strategy) and its slot is set to unavailable. + * + * @param state Boot loader status information. + * + * @return 0 if dependencies are met; nonzero otherwise. + */ +static int +boot_verify_dependencies(struct boot_loader_state *state) +{ + int rc = -1; + uint32_t active_slot; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + rc = boot_verify_slot_dependencies(state, active_slot); + if (rc != 0) { + /* Dependencies not met or invalid dependencies. */ + +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(state); +#endif /* MCUBOOT_RAM_LOAD */ + + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + + return rc; + } + } + + return rc; +} +#endif + +/** + * Read all dependency TLVs of an image from the flash and verify + * one after another to see if they are all satisfied. + * + * @param slot Image slot number. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) +{ + const struct flash_area *fap; + struct image_tlv_iter it; + struct image_dependency dep; + uint32_t off; + uint16_t len; + int area_id; + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = bootutil_tlv_iter_begin(&it, boot_img_hdr(state, slot), fap, + IMAGE_TLV_DEPENDENCY, true); + if (rc != 0) { + goto done; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + if (rc < 0) { + return -1; + } else if (rc > 0) { + rc = 0; + break; + } + + if (len != sizeof(dep)) { + rc = BOOT_EBADIMAGE; + goto done; + } + + rc = LOAD_IMAGE_DATA(boot_img_hdr(state, slot), + fap, off, &dep, len); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + if (dep.image_id >= BOOT_IMAGE_NUMBER) { + rc = BOOT_EBADARGS; + goto done; + } + + /* Verify dependency and modify the swap type if not satisfied. */ + rc = boot_verify_slot_dependency(state, &dep); + if (rc != 0) { + /* Dependency not satisfied */ + goto done; + } + } + +done: + flash_area_close(fap); + return rc; +} + +#endif /* (BOOT_IMAGE_NUMBER > 1) */ + +#if !defined(MCUBOOT_DIRECT_XIP) +/* + * Compute the total size of the given image. Includes the size of + * the TLVs. + */ +#if !defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_OVERWRITE_ONLY_FAST) +static int +boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) +{ + const struct flash_area *fap; + struct image_tlv_info info; + uint32_t off; + uint32_t protect_tlv_size; + int area_id; + int rc; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), boot_img_hdr(state, slot))) { + uint32_t tmp_size = 0; + + rc = bootutil_get_img_decomp_size(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off = boot_img_hdr(state, slot)->ih_hdr_size + tmp_size; + + rc = boot_size_protected_tlvs(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off += tmp_size; + + if (flash_area_read(fap, (BOOT_TLV_OFF(boot_img_hdr(state, slot)) + + boot_img_hdr(state, slot)->ih_protect_tlv_size), &info, + sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + info.it_tlv_tot; + } else { +#else + if (1) { +#endif + off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); + + if (flash_area_read(fap, off, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (protect_tlv_size != info.it_tlv_tot) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + } else if (protect_tlv_size != 0) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + protect_tlv_size + info.it_tlv_tot; + } + + rc = 0; + +done: + flash_area_close(fap); + return rc; +} +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + +#if !defined(MCUBOOT_RAM_LOAD) +static uint32_t +boot_write_sz(struct boot_loader_state *state) +{ + uint32_t elem_sz; +#if MCUBOOT_SWAP_USING_SCRATCH + uint32_t align; +#endif + + /* Figure out what size to write update status update as. The size depends + * on what the minimum write size is for scratch area, active image slot. + * We need to use the bigger of those 2 values. + */ + elem_sz = flash_area_align(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); +#if MCUBOOT_SWAP_USING_SCRATCH + align = flash_area_align(BOOT_SCRATCH_AREA(state)); + if (align > elem_sz) { + elem_sz = align; + } +#endif + + return elem_sz; +} + +/** + * Determines the sector layout of both image slots and the scratch area. + * This information is necessary for calculating the number of bytes to erase + * and copy during an image swap. The information collected during this + * function is used to populate the state. + */ +static int +boot_read_sectors(struct boot_loader_state *state) +{ + uint8_t image_index; + int rc; + + image_index = BOOT_CURR_IMG(state); + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SECONDARY(image_index)); + if (rc != 0) { + /* We need to differentiate from the primary image issue */ + return BOOT_EFLASH_SEC; + } + +#if MCUBOOT_SWAP_USING_SCRATCH + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SCRATCH); + if (rc != 0) { + return BOOT_EFLASH; + } +#endif + + BOOT_WRITE_SZ(state) = boot_write_sz(state); + + return 0; +} + +void +boot_status_reset(struct boot_status *bs) +{ +#ifdef MCUBOOT_ENC_IMAGES + memset(&bs->enckey, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_KEY_ALIGN_SIZE); +#if MCUBOOT_SWAP_SAVE_ENCTLV + memset(&bs->enctlv, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_TLV_ALIGN_SIZE); +#endif +#endif /* MCUBOOT_ENC_IMAGES */ + + bs->use_scratch = 0; + bs->swap_size = 0; + bs->source = 0; + + bs->op = BOOT_STATUS_OP_MOVE; + bs->idx = BOOT_STATUS_IDX_0; + bs->state = BOOT_STATUS_STATE_0; + bs->swap_type = BOOT_SWAP_TYPE_NONE; +} + +bool +boot_status_is_reset(const struct boot_status *bs) +{ + return (bs->op == BOOT_STATUS_OP_MOVE && + bs->idx == BOOT_STATUS_IDX_0 && + bs->state == BOOT_STATUS_STATE_0); +} + +/** + * Writes the supplied boot status to the flash file system. The boot status + * contains the current state of an in-progress image copy operation. + * + * @param bs The boot status to write. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_write_status(const struct boot_loader_state *state, struct boot_status *bs) +{ + const struct flash_area *fap; + uint32_t off; + int area_id; + int rc = 0; + uint8_t buf[BOOT_MAX_ALIGN]; + uint32_t align; + uint8_t erased_val; + + /* NOTE: The first sector copied (that is the last sector on slot) contains + * the trailer. Since in the last step the primary slot is erased, the + * first two status writes go to the scratch which will be copied to + * the primary slot! + */ + +#if MCUBOOT_SWAP_USING_SCRATCH + if (bs->use_scratch) { + /* Write to scratch. */ + area_id = FLASH_AREA_IMAGE_SCRATCH; + } else { +#endif + /* Write to the primary slot. */ + area_id = FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state)); +#if MCUBOOT_SWAP_USING_SCRATCH + } +#endif + + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + off = boot_status_off(fap) + + boot_status_internal_off(bs, BOOT_WRITE_SZ(state)); + align = flash_area_align(fap); + erased_val = flash_area_erased_val(fap); + memset(buf, erased_val, BOOT_MAX_ALIGN); + buf[0] = bs->state; + + BOOT_LOG_DBG("writing swap status; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)flash_area_get_off(fap) + off); + + rc = flash_area_write(fap, off, buf, align); + if (rc != 0) { + rc = BOOT_EFLASH; + } + + flash_area_close(fap); + + return rc; +} +#endif /* !MCUBOOT_RAM_LOAD */ +#endif /* !MCUBOOT_DIRECT_XIP */ + +/* + * Validate image hash/signature and optionally the security counter in a slot. + */ +static fih_ret +boot_image_check(struct boot_loader_state *state, struct image_header *hdr, + const struct flash_area *fap, struct boot_status *bs) +{ + TARGET_STATIC uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + (void)bs; + (void)rc; + +/* In the case of ram loading the image has already been decrypted as it is + * decrypted when copied in ram */ +#if defined(MCUBOOT_ENC_IMAGES) && !defined(MCUBOOT_RAM_LOAD) + if (MUST_DECRYPT(fap, BOOT_CURR_IMG(state), hdr)) { + rc = boot_enc_load(BOOT_CURR_ENC(state), 1, hdr, fap, bs); + if (rc < 0) { + FIH_RET(fih_rc); + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) { + FIH_RET(fih_rc); + } + } +#endif + + FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), + BOOT_CURR_IMG(state), hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, + NULL, 0, NULL); + + FIH_RET(fih_rc); +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +static fih_ret +split_image_check(struct image_header *app_hdr, + const struct flash_area *app_fap, + struct image_header *loader_hdr, + const struct flash_area *loader_fap) +{ + static void *tmpbuf; + uint8_t loader_hash[32]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + if (!tmpbuf) { + tmpbuf = malloc(BOOT_TMPBUF_SZ); + if (!tmpbuf) { + goto out; + } + } + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, loader_hdr, loader_fap, + tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, loader_hash); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(fih_rc); + } + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, app_hdr, app_fap, + tmpbuf, BOOT_TMPBUF_SZ, loader_hash, 32, NULL); + +out: + FIH_RET(fih_rc); +} +#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */ + +/* + * Check that this is a valid header. Valid means that the magic is + * correct, and that the sizes/offsets are "sane". Sane means that + * there is no overflow on the arithmetic, and that the result fits + * within the flash area we are in. Also check the flags in the image + * and class the image as invalid if flags for encryption/compression + * are present but these features are not enabled. + */ +static bool +boot_is_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state) +{ + uint32_t size; + + (void)state; + + if (hdr->ih_magic != IMAGE_MAGIC) { + return false; + } + + if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size)) { + return false; + } + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (!MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { +#else + if (1) { +#endif + if (!boot_u32_safe_add(&size, size, hdr->ih_protect_tlv_size)) { + return false; + } + } + + if (size >= flash_area_get_size(fap)) { + return false; + } + +#if !defined(MCUBOOT_ENC_IMAGES) + if (IS_ENCRYPTED(hdr)) { + return false; + } +#else + if ((hdr->ih_flags & IMAGE_F_ENCRYPTED_AES128) && + (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES256)) + { + return false; + } +#endif + +#if !defined(MCUBOOT_DECOMPRESS_IMAGES) + if (IS_COMPRESSED(hdr)) { + return false; + } +#else + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { + if (!boot_is_compressed_header_valid(hdr, fap, state)) { + return false; + } + } +#endif + + return true; +} + +/* + * Check that a memory area consists of a given value. + */ +static inline bool +boot_data_is_set_to(uint8_t val, void *data, size_t len) +{ + uint8_t i; + uint8_t *p = (uint8_t *)data; + for (i = 0; i < len; i++) { + if (val != p[i]) { + return false; + } + } + return true; +} + +static int +boot_check_header_erased(struct boot_loader_state *state, int slot) +{ + const struct flash_area *fap; + struct image_header *hdr; + uint8_t erased_val; + int area_id; + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + return -1; + } + + erased_val = flash_area_erased_val(fap); + flash_area_close(fap); + + hdr = boot_img_hdr(state, slot); + if (!boot_data_is_set_to(erased_val, &hdr->ih_magic, sizeof(hdr->ih_magic))) { + return -1; + } + + return 0; +} + +#if defined(MCUBOOT_DIRECT_XIP) +/** + * Check if image in slot has been set with specific ROM address to run from + * and whether the slot starts at that address. + * + * @returns 0 if IMAGE_F_ROM_FIXED flag is not set; + * 0 if IMAGE_F_ROM_FIXED flag is set and ROM address specified in + * header matches the slot address; + * 1 if IMF_F_ROM_FIXED flag is set but ROM address specified in header + * does not match the slot address. + */ +static bool +boot_rom_address_check(struct boot_loader_state *state) +{ + uint32_t active_slot; + const struct image_header *hdr; + uint32_t f_off; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + hdr = boot_img_hdr(state, active_slot); + f_off = boot_img_slot_off(state, active_slot); + + if (hdr->ih_flags & IMAGE_F_ROM_FIXED && hdr->ih_load_addr != f_off) { + BOOT_LOG_WRN("Image in %s slot at 0x%x has been built for offset 0x%x"\ + ", skipping", + active_slot == 0 ? "primary" : "secondary", f_off, + hdr->ih_load_addr); + + /* If there is address mismatch, the image is not bootable from this + * slot. + */ + return 1; + } + return 0; +} +#endif + +/* + * Check that there is a valid image in a slot + * + * @returns + * FIH_SUCCESS if image was successfully validated + * FIH_NO_BOOTABLE_IMAGE if no bootloable image was found + * FIH_FAILURE on any errors + */ +static fih_ret +boot_validate_slot(struct boot_loader_state *state, int slot, + struct boot_status *bs) +{ + const struct flash_area *fap; + struct image_header *hdr; + int area_id; + FIH_DECLARE(fih_rc, FIH_FAILURE); + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + FIH_RET(fih_rc); + } + + hdr = boot_img_hdr(state, slot); + if (boot_check_header_erased(state, slot) == 0 || + (hdr->ih_flags & IMAGE_F_NON_BOOTABLE)) { + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* + * This fixes an issue where an image might be erased, but a trailer + * be left behind. It can happen if the image is in the secondary slot + * and did not pass validation, in which case the whole slot is erased. + * If during the erase operation, a reset occurs, parts of the slot + * might have been erased while some did not. The concerning part is + * the trailer because it might disable a new image from being loaded + * through mcumgr; so we just get rid of the trailer here, if the header + * is erased. + */ + if (slot != BOOT_PRIMARY_SLOT) { + swap_erase_trailer_sectors(state, fap); + } +#endif + + /* No bootable image in slot; continue booting from the primary slot. */ + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY) && defined(MCUBOOT_DOWNGRADE_PREVENTION) + if (slot != BOOT_PRIMARY_SLOT) { + /* Check if version of secondary slot is sufficient */ + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) \ + && defined(CONFIG_PCD_APP) && defined(CONFIG_PCD_READ_NETCORE_APP_VERSION) + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + } else { + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif + } +#else + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif +#endif + if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) { + BOOT_LOG_ERR("insufficient version in secondary slot"); + flash_area_erase(fap, 0, flash_area_get_size(fap)); + /* Image in the secondary slot does not satisfy version requirement. + * Erase the image and continue booting from the primary slot. + */ + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + } +#endif + if (!boot_is_header_valid(hdr, fap, state)) { + fih_rc = FIH_FAILURE; + } else { + BOOT_HOOK_CALL_FIH(boot_image_check_hook, FIH_BOOT_HOOK_REGULAR, + fih_rc, BOOT_CURR_IMG(state), slot); + if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) { + FIH_CALL(boot_image_check, fih_rc, state, hdr, fap, bs); + } + } + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if ((slot != BOOT_PRIMARY_SLOT) || ARE_SLOTS_EQUIVALENT()) { + flash_area_erase(fap, 0, flash_area_get_size(fap)); + /* Image is invalid, erase it to prevent further unnecessary + * attempts to validate and boot it. + */ + } + +#if !defined(__BOOTSIM__) + BOOT_LOG_ERR("Image in the %s slot is not valid!", + (slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); +#endif + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +#if MCUBOOT_IMAGE_NUMBER > 1 && !defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_VERIFY_IMG_ADDRESS) + /* Verify that the image in the secondary slot has a reset address + * located in the primary slot. This is done to avoid users incorrectly + * overwriting an application written to the incorrect slot. + * This feature is only supported by ARM platforms. + */ +#if MCUBOOT_IMAGE_NUMBER >= 3 + /* Currently the MCUboot can be configured for up to 3 image, where image number 2 is + * designated for XIP, where it is the second part of image stored in slots of image + * 0. This part of image is not bootable, as the XIP setup is done by the app in + * image 0 slot, and it does not carry the reset vector. + */ + if (area_id == FLASH_AREA_IMAGE_SECONDARY(2)) { + goto out; + } +#endif + if (area_id == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { + const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); + struct image_header *secondary_hdr = boot_img_hdr(state, slot); + uint32_t reset_value = 0; + uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value); + uint32_t min_addr, max_addr; + bool check_addresses = false; + + rc = flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)); + if (rc != 0) { + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +#ifdef PM_CPUNET_APP_ADDRESS + /* The primary slot for the network core is emulated in RAM. + * Its flash_area hasn't got relevant boundaries. + * Therfore need to override its boundaries for the check. + */ + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + min_addr = PM_CPUNET_APP_ADDRESS; + max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE; + check_addresses = true; + } else +#endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + min_addr = PM_S0_ADDRESS; + max_addr = (PM_S0_ADDRESS + PM_S0_SIZE); +#else + min_addr = PM_S1_ADDRESS; + max_addr = (PM_S1_ADDRESS + PM_S1_SIZE); +#endif + check_addresses = true; + } else +#endif + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + min_addr = MIN(pri_fa->fa_off, PM_S0_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S0_ADDRESS + PM_S0_SIZE)); +#else + min_addr = MIN(pri_fa->fa_off, PM_S1_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S1_ADDRESS + PM_S1_SIZE)); +#endif +#else + min_addr = pri_fa->fa_off; + max_addr = pri_fa->fa_off + pri_fa->fa_size; +#endif + check_addresses = true; + } + + if (check_addresses == true && (reset_value < min_addr || reset_value > max_addr)) { + BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot"); + BOOT_LOG_ERR("Erasing image from secondary slot"); + + /* The vector table in the image located in the secondary + * slot does not target the primary slot. This might + * indicate that the image was loaded to the wrong slot. + * + * Erase the image and continue booting from the primary slot. + */ + flash_area_erase(fap, 0, fap->fa_size); + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + } +#endif + +out: + flash_area_close(fap); + + FIH_RET(fih_rc); +} + +#ifdef MCUBOOT_HW_ROLLBACK_PROT +/** + * Updates the stored security counter value with the image's security counter + * value which resides in the given slot, only if it's greater than the stored + * value. + * + * @param image_index Index of the image to determine which security + * counter to update. + * @param slot Slot number of the image. + * @param hdr Pointer to the image header structure of the image + * that is currently stored in the given slot. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_update_security_counter(uint8_t image_index, int slot, + struct image_header *hdr) +{ + const struct flash_area *fap = NULL; + uint32_t img_security_cnt; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(image_index, slot), + &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = bootutil_get_img_security_cnt(hdr, fap, &img_security_cnt); + if (rc != 0) { + goto done; + } + + rc = boot_nv_security_counter_update(image_index, img_security_cnt); + if (rc != 0) { + goto done; + } + +done: + flash_area_close(fap); + return rc; +} +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ +(defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP)) + +#define SEC_SLOT_VIRGIN 0 +#define SEC_SLOT_TOUCHED 1 +#define SEC_SLOT_ASSIGNED 2 + +static uint8_t sec_slot_assignmnet[MCUBOOT_IMAGE_NUMBER] = {0}; + +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + if (sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + if (sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } +#endif + + if (sec_slot_assignmnet[BOOT_CURR_IMG(state)] == SEC_SLOT_VIRGIN) { + sec_slot_assignmnet[BOOT_CURR_IMG(state)] = SEC_SLOT_TOUCHED; + } +} + +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } +#endif + + sec_slot_assignmnet[BOOT_CURR_IMG(state)] = SEC_SLOT_ASSIGNED; +} + +/** + * Cleanup up all secondary slot which couldn't be assigned to any primary slot. + * + * This function erases content of each secondary slot which contains valid + * header but couldn't be assigned to any of supported primary images. + * + * This function is supposed to be called after boot_validated_swap_type() + * iterates over all the images in context_boot_go(). + */ +static void sec_slot_cleanup_if_unusable(void) +{ + uint8_t idx; + + for (idx = 0; idx < MCUBOOT_IMAGE_NUMBER; idx++) { + if (SEC_SLOT_TOUCHED == sec_slot_assignmnet[idx]) { + const struct flash_area *secondary_fa; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(idx, BOOT_SECONDARY_SLOT), + &secondary_fa); + if (!rc) { + rc = flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + if (!rc) { + BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", idx); + } + } + + if (rc) { + BOOT_LOG_ERR("Failed to clean-up secondary slot of image %d: %d", idx, rc); + } + } + } +} +#else +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +} +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +} +static inline void sec_slot_cleanup_if_unusable(void) +{ +} +#endif /* defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ + defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) */ + +/** + * Determines which swap operation to perform, if any. If it is determined + * that a swap operation is required, the image in the secondary slot is checked + * for validity. If the image in the secondary slot is invalid, it is erased, + * and a swap type of "none" is indicated. + * + * @return The type of swap to perform (BOOT_SWAP_TYPE...) + */ +static int +boot_validated_swap_type(struct boot_loader_state *state, + struct boot_status *bs) +{ + int swap_type; + FIH_DECLARE(fih_rc, FIH_FAILURE); + bool upgrade_valid = false; +#if defined(PM_S1_ADDRESS) + owner_nsib[BOOT_CURR_IMG(state)] = false; +#endif + +#if defined(PM_S1_ADDRESS) || defined(PM_CPUNET_B0N_ADDRESS) + const struct flash_area *secondary_fa = + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); + struct image_header *hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + uint32_t reset_addr = 0; + int rc = 0; + /* Patch needed for NCS. Since image 0 (the app) and image 1 (the other + * B1 slot S0 or S1) share the same secondary slot, we need to check + * whether the update candidate in the secondary slot is intended for + * image 0 or image 1 primary by looking at the address of the reset + * vector. Note that there are good reasons for not using img_num from + * the swap info. + */ + + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = flash_area_read(secondary_fa, hdr->ih_hdr_size + + sizeof(uint32_t), &reset_addr, + sizeof(reset_addr)); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + sec_slot_touch(state); + +#ifdef PM_S1_ADDRESS +#ifdef PM_CPUNET_B0N_ADDRESS + if(!(reset_addr >= PM_CPUNET_APP_ADDRESS && reset_addr < PM_CPUNET_APP_END_ADDRESS)) +#endif + { + const struct flash_area *primary_fa; + rc = flash_area_open(flash_area_id_from_multi_image_slot( + BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT), + &primary_fa); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Check start and end of primary slot for current image */ +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { +#else + if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { +#endif + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + /* This is not the s0/s1 upgrade image but the application image, pretend + * there is no image so the NSIB update can be loaded + */ + return BOOT_SWAP_TYPE_NONE; + } + + owner_nsib[BOOT_CURR_IMG(state)] = true; +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + } else if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { +#else + } else if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { +#endif + /* NSIB upgrade but for the wrong slot, must be erased */ + BOOT_LOG_ERR("Image in slot is for wrong s0/s1 image"); + flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + return BOOT_SWAP_TYPE_FAIL; + } else if (reset_addr < primary_fa->fa_off || reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) { + /* The image in the secondary slot is not intended for any */ + return BOOT_SWAP_TYPE_NONE; + } + + if ((primary_fa->fa_off == PM_S0_ADDRESS) || (primary_fa->fa_off == PM_S1_ADDRESS)) { + owner_nsib[BOOT_CURR_IMG(state)] = true; + } + } +#endif /* PM_S1_ADDRESS */ + sec_slot_mark_assigned(state); + } + +#endif /* PM_S1_ADDRESS || PM_CPUNET_B0N_ADDRESS */ + + swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state)); + if (BOOT_IS_UPGRADE(swap_type)) { + /* Boot loader wants to switch to the secondary slot. + * Ensure image is valid. + */ + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_SECONDARY_SLOT, bs); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if (FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + swap_type = BOOT_SWAP_TYPE_NONE; + } else { + swap_type = BOOT_SWAP_TYPE_FAIL; + } + } else { + upgrade_valid = true; + } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) \ + && !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) && defined(CONFIG_PCD_APP) + /* If the update is valid, and it targets the network core: perform the + * update and indicate to the caller of this function that no update is + * available + */ + if (upgrade_valid && reset_addr >= PM_CPUNET_APP_ADDRESS && + reset_addr < PM_CPUNET_APP_END_ADDRESS) { + struct image_header *hdr = (struct image_header *)secondary_fa->fa_off; + uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size; + uint32_t *net_core_fw_addr = (uint32_t *)(vtable_addr); + uint32_t fw_size = hdr->ih_img_size; + BOOT_LOG_INF("Starting network core update"); + rc = pcd_network_core_update(net_core_fw_addr, fw_size); + + if (rc != 0) { + swap_type = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_LOG_INF("Done updating network core"); +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* swap_erase_trailer_sectors is undefined if upgrade only + * method is used. There is no need to erase sectors, because + * the image cannot be reverted. + */ + rc = swap_erase_trailer_sectors(state, + secondary_fa); +#endif + swap_type = BOOT_SWAP_TYPE_NONE; + } + } +#endif /* CONFIG_SOC_NRF5340_CPUAPP && PM_CPUNET_B0N_ADDRESS && + !CONFIG_NRF53_MULTI_IMAGE_UPDATE && CONFIG_PCD_APP */ + } + + return swap_type; +} +#endif + +/** + * Erases a region of flash. + * + * @param flash_area The flash_area containing the region to erase. + * @param off The offset within the flash area to start the + * erase. + * @param sz The number of bytes to erase. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz) +{ + return flash_area_erase(fap, off, sz); +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SWAP_SAVE_ENCTLV) +/* Replacement for memset(p, 0, sizeof(*p) that does not get + * optimized out. + */ +static void like_mbedtls_zeroize(void *p, size_t n) +{ + volatile unsigned char *v = (unsigned char *)p; + + for (size_t i = 0; i < n; i++) { + v[i] = 0; + } +} +#endif + +/** + * Copies the contents of one flash region to another. You must erase the + * destination region prior to calling this function. + * + * @param flash_area_id_src The ID of the source flash area. + * @param flash_area_id_dst The ID of the destination flash area. + * @param off_src The offset within the source flash area to + * copy from. + * @param off_dst The offset within the destination flash area to + * copy to. + * @param sz The number of bytes to copy. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_copy_region(struct boot_loader_state *state, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, + uint32_t off_src, uint32_t off_dst, uint32_t sz) +{ + uint32_t bytes_copied; + int chunk_sz; + int rc; +#ifdef MCUBOOT_ENC_IMAGES + uint32_t off = off_dst; + uint32_t tlv_off; + size_t blk_off; + struct image_header *hdr; + uint16_t idx; + uint32_t blk_sz; + uint8_t image_index = BOOT_CURR_IMG(state); + bool encrypted_src; + bool encrypted_dst; + /* Assuming the secondary slot is source; note that 0 here not only + * means that primary slot is source, but also that there will be + * encryption happening, if it is 1 then there is decryption from + * secondary slot. + */ + int source_slot = 1; + /* In case of encryption enabled, we may have to do more work than + * just copy bytes */ + bool only_copy = false; +#else + (void)state; +#endif +#ifdef MCUBOOT_DECOMPRESS_IMAGES + struct image_header *hdr; +#endif + + TARGET_STATIC uint8_t buf[BUF_SZ] __attribute__((aligned(4))); + +#ifdef MCUBOOT_ENC_IMAGES + encrypted_src = (flash_area_get_id(fap_src) != FLASH_AREA_IMAGE_PRIMARY(image_index)); + encrypted_dst = (flash_area_get_id(fap_dst) != FLASH_AREA_IMAGE_PRIMARY(image_index)); + + if (encrypted_src != encrypted_dst) { + if (encrypted_dst) { + /* Need encryption, metadata from the primary slot */ + hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT); + source_slot = 0; + } else { + /* Need decryption, metadata from the secondary slot */ + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + source_slot = 1; + } + } else { + /* In case when source and targe is the same area, this means that we + * only have to copy bytes, no encryption or decryption. + */ + only_copy = true; + } +#endif + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + + if (MUST_DECOMPRESS(fap_src, BOOT_CURR_IMG(state), hdr)) { + /* Use alternative function for compressed images */ + return boot_copy_region_decompress(state, fap_src, fap_dst, off_src, off_dst, sz, buf, + BUF_SZ); + } +#endif + + bytes_copied = 0; + while (bytes_copied < sz) { + if (sz - bytes_copied > sizeof buf) { + chunk_sz = sizeof buf; + } else { + chunk_sz = sz - bytes_copied; + } + + rc = flash_area_read(fap_src, off_src + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + +#ifdef MCUBOOT_ENC_IMAGES + /* If only copy, then does not matter if header indicates need for + * encryptio/decryptio, we just copy data. */ + if (!only_copy && IS_ENCRYPTED(hdr)) { + uint32_t abs_off = off + bytes_copied; + if (abs_off < hdr->ih_hdr_size) { + /* do not decrypt header */ + if (abs_off + chunk_sz > hdr->ih_hdr_size) { + /* The lower part of the chunk contains header data */ + blk_off = 0; + blk_sz = chunk_sz - (hdr->ih_hdr_size - abs_off); + idx = hdr->ih_hdr_size - abs_off; + } else { + /* The chunk contains exclusively header data */ + blk_sz = 0; /* nothing to decrypt */ + } + } else { + idx = 0; + blk_sz = chunk_sz; + blk_off = (abs_off - hdr->ih_hdr_size) & 0xf; + } + + if (blk_sz > 0) + { + tlv_off = BOOT_TLV_OFF(hdr); + if (abs_off + chunk_sz > tlv_off) { + /* do not decrypt TLVs */ + if (abs_off >= tlv_off) { + blk_sz = 0; + } else { + blk_sz = tlv_off - abs_off; + } + } + if (source_slot == 0) { + boot_enc_encrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } else { + boot_enc_decrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } + } + } +#endif + + rc = flash_area_write(fap_dst, off_dst + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + + bytes_copied += chunk_sz; + + MCUBOOT_WATCHDOG_FEED(); + } + + return 0; +} + +/** + * Overwrite primary slot with the image contained in the secondary slot. + * If a prior copy operation was interrupted by a system reset, this function + * redos the copy. + * + * @param bs The current boot status. This function reads + * this struct to determine if it is resuming + * an interrupted swap operation. This + * function writes the updated status to this + * function on return. + * + * @return 0 on success; nonzero on failure. + */ +#if defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_BOOTSTRAP) +static int +boot_copy_image(struct boot_loader_state *state, struct boot_status *bs) +{ + size_t sect_count; + size_t sect; + int rc; + size_t size; + size_t this_size; + size_t last_sector; + const struct flash_area *fap_primary_slot; + const struct flash_area *fap_secondary_slot; + uint8_t image_index; + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + uint32_t sector; + uint32_t trailer_sz; + uint32_t off; + uint32_t sz; +#endif + + (void)bs; + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + uint32_t src_size = 0; + rc = boot_read_image_size(state, BOOT_SECONDARY_SLOT, &src_size); + assert(rc == 0); +#endif + + image_index = BOOT_CURR_IMG(state); + + BOOT_LOG_INF("Image %d upgrade secondary slot -> primary slot", image_index); + BOOT_LOG_INF("Erasing the primary slot"); + + rc = flash_area_open(flash_area_get_id(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)), + &fap_primary_slot); + assert (rc == 0); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), + &fap_secondary_slot); + assert (rc == 0); + + sect_count = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); + for (sect = 0, size = 0; sect < sect_count; sect++) { + this_size = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, sect); + rc = boot_erase_region(fap_primary_slot, size, this_size); + assert(rc == 0); + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + if ((size + this_size) >= src_size) { + size += src_size - size; + size += BOOT_WRITE_SZ(state) - (size % BOOT_WRITE_SZ(state)); + break; + } +#endif + + size += this_size; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1; + sz = 0; + do { + sz += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, sector); + off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, sector); + sector--; + } while (sz < trailer_sz); + + rc = boot_erase_region(fap_primary_slot, off, sz); + assert(rc == 0); +#endif + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(boot_img_hdr(state, BOOT_SECONDARY_SLOT))) { + rc = boot_enc_load(BOOT_CURR_ENC(state), BOOT_SECONDARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT), + fap_secondary_slot, bs); + + if (rc < 0) { + return BOOT_EBADIMAGE; + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) { + return BOOT_EBADIMAGE; + } + } +#endif + + BOOT_LOG_INF("Image %d copying the secondary slot to the primary slot: 0x%zx bytes", + image_index, size); + rc = boot_copy_region(state, fap_secondary_slot, fap_primary_slot, 0, 0, size); + if (rc != 0) { + return rc; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + rc = boot_write_magic(fap_primary_slot); + if (rc != 0) { + return rc; + } +#endif + + rc = BOOT_HOOK_CALL(boot_copy_region_post_hook, 0, BOOT_CURR_IMG(state), + BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT), size); + if (rc != 0) { + return rc; + } + +#ifdef MCUBOOT_HW_ROLLBACK_PROT + /* Update the stored security counter with the new image's security counter + * value. Both slots hold the new image at this point, but the secondary + * slot's image header must be passed since the image headers in the + * boot_data structure have not been updated yet. + */ + rc = boot_update_security_counter(BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image upgrade."); + return rc; + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +#ifndef MCUBOOT_OVERWRITE_ONLY_KEEP_BACKUP + /* + * Erases header and trailer. The trailer is erased because when a new + * image is written without a trailer as is the case when using newt, the + * trailer that was left might trigger a new upgrade. + */ + BOOT_LOG_DBG("erasing secondary header"); + rc = boot_erase_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, 0), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0)); + assert(rc == 0); +#endif + + last_sector = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) - 1; + BOOT_LOG_DBG("erasing secondary trailer"); + rc = boot_erase_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, + last_sector), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, + last_sector)); + assert(rc == 0); + + flash_area_close(fap_primary_slot); + flash_area_close(fap_secondary_slot); + + /* TODO: Perhaps verify the primary slot's signature again? */ + + return 0; +} +#endif + +#if !defined(MCUBOOT_OVERWRITE_ONLY) +/** + * Swaps the two images in flash. If a prior copy operation was interrupted + * by a system reset, this function completes that operation. + * + * @param bs The current boot status. This function reads + * this struct to determine if it is resuming + * an interrupted swap operation. This + * function writes the updated status to this + * function on return. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) +{ + struct image_header *hdr; + const struct flash_area *fap; +#ifdef MCUBOOT_ENC_IMAGES + uint8_t slot; + uint8_t i; +#endif + uint32_t size; + uint32_t copy_size; + uint8_t image_index; + int rc; + + /* FIXME: just do this if asked by user? */ + + size = copy_size = 0; + image_index = BOOT_CURR_IMG(state); + + if (boot_status_is_reset(bs)) { + /* + * No swap ever happened, so need to find the largest image which + * will be used to determine the amount of sectors to swap. + */ + hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT); + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = boot_read_image_size(state, BOOT_PRIMARY_SLOT, ©_size); + assert(rc == 0); + } + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(hdr)) { + fap = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); + rc = boot_enc_load(BOOT_CURR_ENC(state), 0, hdr, fap, bs); + assert(rc >= 0); + + if (rc == 0) { + rc = boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs); + assert(rc == 0); + } else { + rc = 0; + } + } else { + memset(bs->enckey[0], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + } +#endif + + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = boot_read_image_size(state, BOOT_SECONDARY_SLOT, &size); + assert(rc == 0); + } + +#ifdef MCUBOOT_ENC_IMAGES + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + if (IS_ENCRYPTED(hdr)) { + fap = BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); + rc = boot_enc_load(BOOT_CURR_ENC(state), 1, hdr, fap, bs); + assert(rc >= 0); + + if (rc == 0) { + rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs); + assert(rc == 0); + } else { + rc = 0; + } + } else { + memset(bs->enckey[1], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + } +#endif + + if (size > copy_size) { + copy_size = size; + } + + bs->swap_size = copy_size; + } else { + /* + * If a swap was under way, the swap_size should already be present + * in the trailer... + */ + + rc = boot_find_status(image_index, &fap); + assert(fap != NULL); + rc = boot_read_swap_size(fap, &bs->swap_size); + assert(rc == 0); + + copy_size = bs->swap_size; + +#ifdef MCUBOOT_ENC_IMAGES + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + rc = boot_read_enc_key(fap, slot, bs); + assert(rc == 0); + + for (i = 0; i < BOOT_ENC_KEY_SIZE; i++) { + if (bs->enckey[slot][i] != 0xff) { + break; + } + } + + boot_enc_init(BOOT_CURR_ENC(state), slot); + + if (i != BOOT_ENC_KEY_SIZE) { + boot_enc_set_key(BOOT_CURR_ENC(state), slot, bs); + } + } +#endif + flash_area_close(fap); + } + +#if defined(PM_S1_ADDRESS) && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (owner_nsib[BOOT_CURR_IMG(state)]) { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* For NSIB, move the image instead of swapping it */ + nsib_swap_run(state, bs); + +#if defined(CONFIG_REBOOT) + /* Should also reboot at this point so the new S0/S1 update is applied */ + sys_reboot(SYS_REBOOT_COLD); +#endif + } + } else +#endif + { + swap_run(state, bs, copy_size); + } + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT + extern int boot_status_fails; + if (boot_status_fails > 0) { + BOOT_LOG_WRN("%d status write fails performing the swap", + boot_status_fails); + } +#endif + rc = BOOT_HOOK_CALL(boot_copy_region_post_hook, 0, BOOT_CURR_IMG(state), + BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT), size); + + return 0; +} +#endif + +/** + * Performs a clean (not aborted) image update. + * + * @param bs The current boot status. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_perform_update(struct boot_loader_state *state, struct boot_status *bs) +{ + int rc; +#ifndef MCUBOOT_OVERWRITE_ONLY + uint8_t swap_type; +#endif + + /* At this point there are no aborted swaps. */ +#if defined(MCUBOOT_OVERWRITE_ONLY) + rc = boot_copy_image(state, bs); +#elif defined(MCUBOOT_BOOTSTRAP) + /* Check if the image update was triggered by a bad image in the + * primary slot (the validity of the image in the secondary slot had + * already been checked). + */ + FIH_DECLARE(fih_rc, FIH_FAILURE); + rc = boot_check_header_erased(state, BOOT_PRIMARY_SLOT); + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, bs); + if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + rc = boot_copy_image(state, bs); + } else { + rc = boot_swap_image(state, bs); + } +#else + rc = boot_swap_image(state, bs); +#endif + assert(rc == 0); + +#ifndef MCUBOOT_OVERWRITE_ONLY + /* The following state needs image_ok be explicitly set after the + * swap was finished to avoid a new revert. + */ + swap_type = BOOT_SWAP_TYPE(state); + if (swap_type == BOOT_SWAP_TYPE_REVERT || + swap_type == BOOT_SWAP_TYPE_PERM) { + rc = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = swap_type = BOOT_SWAP_TYPE_PANIC; + } + } + +#ifdef MCUBOOT_HW_ROLLBACK_PROT + if (swap_type == BOOT_SWAP_TYPE_PERM) { + /* Update the stored security counter with the new image's security + * counter value. The primary slot holds the new image at this point, + * but the secondary slot's image header must be passed since image + * headers in the boot_data structure have not been updated yet. + * + * In case of a permanent image swap mcuboot will never attempt to + * revert the images on the next reboot. Therefore, the security + * counter must be increased right after the image upgrade. + */ + rc = boot_update_security_counter( + BOOT_CURR_IMG(state), + BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after " + "image upgrade."); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + + if (BOOT_IS_UPGRADE(swap_type)) { + rc = swap_set_copy_done(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + + return rc; +} + +/** + * Completes a previously aborted image swap. + * + * @param bs The current boot status. + * + * @return 0 on success; nonzero on failure. + */ +#if !defined(MCUBOOT_OVERWRITE_ONLY) +static int +boot_complete_partial_swap(struct boot_loader_state *state, + struct boot_status *bs) +{ + int rc; + + /* Determine the type of swap operation being resumed from the + * `swap-type` trailer field. + */ + rc = boot_swap_image(state, bs); + assert(rc == 0); + + BOOT_SWAP_TYPE(state) = bs->swap_type; + + /* The following states need image_ok be explicitly set after the + * swap was finished to avoid a new revert. + */ + if (bs->swap_type == BOOT_SWAP_TYPE_REVERT || + bs->swap_type == BOOT_SWAP_TYPE_PERM) { + rc = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } + + if (BOOT_IS_UPGRADE(bs->swap_type)) { + rc = swap_set_copy_done(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } + + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_PANIC) { + BOOT_LOG_ERR("panic!"); + assert(0); + + /* Loop forever... */ + while (1) {} + } + + return rc; +} +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + +#if (BOOT_IMAGE_NUMBER > 1) +/** + * Review the validity of previously determined swap types of other images. + * + * @param aborted_swap The current image upgrade is a + * partial/aborted swap. + */ +static void +boot_review_image_swap_types(struct boot_loader_state *state, + bool aborted_swap) +{ + /* In that case if we rebooted in the middle of an image upgrade process, we + * must review the validity of swap types, that were previously determined + * for other images. The image_ok flag had not been set before the reboot + * for any of the updated images (only the copy_done flag) and thus falsely + * the REVERT swap type has been determined for the previous images that had + * been updated before the reboot. + * + * There are two separate scenarios that we have to deal with: + * + * 1. The reboot has happened during swapping an image: + * The current image upgrade has been determined as a + * partial/aborted swap. + * 2. The reboot has happened between two separate image upgrades: + * In this scenario we must check the swap type of the current image. + * In those cases if it is NONE or REVERT we cannot certainly determine + * the fact of a reboot. In a consistent state images must move in the + * same direction or stay in place, e.g. in practice REVERT and TEST + * swap types cannot be present at the same time. If the swap type of + * the current image is either TEST, PERM or FAIL we must review the + * already determined swap types of other images and set each false + * REVERT swap types to NONE (these images had been successfully + * updated before the system rebooted between two separate image + * upgrades). + */ + + if (BOOT_CURR_IMG(state) == 0) { + /* Nothing to do */ + return; + } + + if (!aborted_swap) { + if ((BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) || + (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_REVERT)) { + /* Nothing to do */ + return; + } + } + + for (uint8_t i = 0; i < BOOT_CURR_IMG(state); i++) { + if (state->swap_type[i] == BOOT_SWAP_TYPE_REVERT) { + state->swap_type[i] = BOOT_SWAP_TYPE_NONE; + } + } +} +#endif + +/** + * Prepare image to be updated if required. + * + * Prepare image to be updated if required with completing an image swap + * operation if one was aborted and/or determining the type of the + * swap operation. In case of any error set the swap type to NONE. + * + * @param state TODO + * @param bs Pointer where the read and possibly updated + * boot status can be written to. + */ +static void +boot_prepare_image_for_update(struct boot_loader_state *state, + struct boot_status *bs) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) + int max_size; +#endif + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors(state); + if (rc != 0) { + BOOT_LOG_WRN("Failed reading sectors; BOOT_MAX_IMG_SECTORS=%d" + " - too small?", BOOT_MAX_IMG_SECTORS); + /* Unable to determine sector layout, continue with next image + * if there is one. + */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + if (rc == BOOT_EFLASH) + { + /* Only return on error from the primary image flash */ + return; + } + } + + /* Attempt to read an image header from each slot. */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + /* Continue with next image if there is one. */ + BOOT_LOG_WRN("Failed reading image headers; Image=%u", + BOOT_CURR_IMG(state)); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) + /* Fetch information on maximum sizes for later usage, if needed */ + max_size = app_max_size(state); + + if (max_size > 0) { + image_max_sizes[BOOT_CURR_IMG(state)].calculated = true; + image_max_sizes[BOOT_CURR_IMG(state)].max_size = max_size; + } +#endif + + /* If the current image's slots aren't compatible, no swap is possible. + * Just boot into primary slot. + */ + if (boot_slots_compatible(state)) { + boot_status_reset(bs); + +#ifndef MCUBOOT_OVERWRITE_ONLY + rc = swap_read_status(state, bs); + if (rc != 0) { + BOOT_LOG_WRN("Failed reading boot status; Image=%u", + BOOT_CURR_IMG(state)); + /* Continue with next image if there is one. */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } +#endif + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* + * Must re-read image headers because the boot status might + * have been updated in the previous function call. + */ + rc = boot_read_image_headers(state, !boot_status_is_reset(bs), bs); +#ifdef MCUBOOT_BOOTSTRAP + /* When bootstrapping it's OK to not have image magic in the primary slot */ + if (rc != 0 && (BOOT_CURR_IMG(state) != BOOT_PRIMARY_SLOT || + boot_check_header_erased(state, BOOT_PRIMARY_SLOT) != 0)) { +#else + if (rc != 0) { +#endif + + /* Continue with next image if there is one. */ + BOOT_LOG_WRN("Failed reading image headers; Image=%u", + BOOT_CURR_IMG(state)); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } +#endif + + /* Determine if we rebooted in the middle of an image swap + * operation. If a partial swap was detected, complete it. + */ + if (!boot_status_is_reset(bs)) { + +#if (BOOT_IMAGE_NUMBER > 1) + boot_review_image_swap_types(state, true); +#endif + +#ifdef MCUBOOT_OVERWRITE_ONLY + /* Should never arrive here, overwrite-only mode has + * no swap state. + */ + assert(0); +#else + /* Determine the type of swap operation being resumed from the + * `swap-type` trailer field. + */ + rc = boot_complete_partial_swap(state, bs); + assert(rc == 0); +#endif + /* Attempt to read an image header from each slot. Ensure that + * image headers in slots are aligned with headers in boot_data. + */ + rc = boot_read_image_headers(state, false, bs); + assert(rc == 0); + + /* Swap has finished set to NONE */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } else { + /* There was no partial swap, determine swap type. */ + if (bs->swap_type == BOOT_SWAP_TYPE_NONE) { + BOOT_SWAP_TYPE(state) = boot_validated_swap_type(state, bs); + } else { + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_SECONDARY_SLOT, bs); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_SWAP_TYPE(state) = bs->swap_type; + } + } + +#if (BOOT_IMAGE_NUMBER > 1) + boot_review_image_swap_types(state, false); +#endif + +#ifdef MCUBOOT_BOOTSTRAP + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) { + /* Header checks are done first because they are + * inexpensive. Since overwrite-only copies starting from + * offset 0, if interrupted, it might leave a valid header + * magic, so also run validation on the primary slot to be + * sure it's not OK. + */ + rc = boot_check_header_erased(state, BOOT_PRIMARY_SLOT); + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_PRIMARY_SLOT, bs); + + if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + + rc = (boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_magic == IMAGE_MAGIC) ? 1: 0; + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_SECONDARY_SLOT, bs); + + if (rc == 1 && FIH_EQ(fih_rc, FIH_SUCCESS)) { + /* Set swap type to REVERT to overwrite the primary + * slot with the image contained in secondary slot + * and to trigger the explicit setting of the + * image_ok flag. + */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_REVERT; + } + } + } +#endif + } + } else { + /* In that case if slots are not compatible. */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } +} + +/** + * Updates the security counter for the current image. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_update_hw_rollback_protection(struct boot_loader_state *state) +{ +#ifdef MCUBOOT_HW_ROLLBACK_PROT + int rc; + + /* Update the stored security counter with the active image's security + * counter value. It will only be updated if the new security counter is + * greater than the stored value. + * + * In case of a successful image swapping when the swap type is TEST the + * security counter can be increased only after a reset, when the swap + * type is NONE and the image has marked itself "OK" (the image_ok flag + * has been set). This way a "revert" can be performed when it's + * necessary. + */ + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) { + rc = boot_update_security_counter( + BOOT_CURR_IMG(state), + BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_PRIMARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image " + "validation."); + return rc; + } + } + + return 0; + +#else /* MCUBOOT_HW_ROLLBACK_PROT */ + (void) (state); + + return 0; +#endif +} + +/** + * Checks test swap downgrade prevention conditions. + * + * Function called only for swap upgrades test run. It may prevent + * swap if slot 1 image has <= version number or < security counter + * + * @param state Boot loader status information. + * + * @return 0 - image can be swapped, -1 downgrade prevention + */ +static int +check_downgrade_prevention(struct boot_loader_state *state) +{ +#if defined(MCUBOOT_DOWNGRADE_PREVENTION) && \ + (defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_SCRATCH)) + uint32_t security_counter[2]; + int rc; + +#if defined(PM_S1_ADDRESS) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + /* Downgrade prevention on S0/S1 image is managed by NSIB */ + return 0; + } +#endif + + if (MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER) { + /* If there was security no counter in slot 0, allow swap */ + rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 0).hdr), + BOOT_IMG(state, 0).area, + &security_counter[0]); + if (rc != 0) { + return 0; + } + /* If there is no security counter in slot 1, or it's lower than + * that of slot 0, prevent downgrade */ + rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 1).hdr), + BOOT_IMG(state, 1).area, + &security_counter[1]); + if (rc != 0 || security_counter[0] > security_counter[1]) { + rc = -1; + } + } + else { + rc = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + } + if (rc < 0) { + /* Image in slot 0 prevents downgrade, delete image in slot 1 */ + BOOT_LOG_INF("Image %d in slot 1 erased due to downgrade prevention", BOOT_CURR_IMG(state)); + flash_area_erase(BOOT_IMG(state, 1).area, 0, + flash_area_get_size(BOOT_IMG(state, 1).area)); + } else { + rc = 0; + } + return rc; +#else + (void)state; + return 0; +#endif +} + +fih_ret +context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + size_t slot; + struct boot_status bs; + int rc = -1; + FIH_DECLARE(fih_rc, FIH_FAILURE); + int fa_id; + int image_index; + bool has_upgrade; + volatile int fih_cnt; + +#if defined(__BOOTSIM__) + /* The array of slot sectors are defined here (as opposed to file scope) so + * that they don't get allocated for non-boot-loader apps. This is + * necessary because the gcc option "-fdata-sections" doesn't seem to have + * any effect in older gcc versions (e.g., 4.8.4). + */ + TARGET_STATIC boot_sector_t primary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + TARGET_STATIC boot_sector_t secondary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; +#if MCUBOOT_SWAP_USING_SCRATCH + TARGET_STATIC boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS]; +#endif +#endif + + has_upgrade = false; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)has_upgrade; +#endif + + /* Iterate over all the images. By the end of the loop the swap type has + * to be determined for each image and all aborted swaps have to be + * completed. + */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif +#if defined(MCUBOOT_ENC_IMAGES) && (BOOT_IMAGE_NUMBER > 1) + /* The keys used for encryption may no longer be valid (could belong to + * another images). Therefore, mark them as invalid to force their reload + * by boot_enc_load(). + */ + boot_enc_zeroize(BOOT_CURR_ENC(state)); +#endif + + image_index = BOOT_CURR_IMG(state); + +#if !defined(__BOOTSIM__) + BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = + sector_buffers.primary[image_index]; + BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors = + sector_buffers.secondary[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + state->scratch.sectors = sector_buffers.scratch; +#endif +#else + BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = + primary_slot_sectors[image_index]; + BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors = + secondary_slot_sectors[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + state->scratch.sectors = scratch_sectors; +#endif +#endif + + /* Open primary and secondary image areas for the duration + * of this call. + */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + fa_id = flash_area_id_from_multi_image_slot(image_index, slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(state, slot)); + assert(rc == 0); + + if (rc != 0) { + BOOT_LOG_ERR("Failed to open flash area ID %d (image %d slot %d): %d, " + "cannot continue", fa_id, image_index, (int8_t)slot, rc); + FIH_PANIC; + } + } +#if MCUBOOT_SWAP_USING_SCRATCH + rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, + &BOOT_SCRATCH_AREA(state)); + assert(rc == 0); + + if (rc != 0) { + BOOT_LOG_ERR("Failed to open scratch flash area: %d, cannot continue", rc); + FIH_PANIC; + } +#endif + + /* Determine swap type and complete swap if it has been aborted. */ + boot_prepare_image_for_update(state, &bs); + + if (BOOT_IS_UPGRADE(BOOT_SWAP_TYPE(state))) { + has_upgrade = true; + } + } + + /* cleanup secondary slots which were recognized unusable*/ + sec_slot_cleanup_if_unusable(); + +#if (BOOT_IMAGE_NUMBER > 1) + if (has_upgrade) { + /* Iterate over all the images and verify whether the image dependencies + * are all satisfied and update swap type if necessary. + */ + rc = boot_verify_dependencies(state); + if (rc != 0) { + /* + * It was impossible to upgrade because the expected dependency version + * was not available. Here we already changed the swap_type so that + * instead of asserting the bootloader, we continue and no upgrade is + * performed. + */ + rc = 0; + } + } +#endif + + /* Trigger status change callback with upgrading status */ + mcuboot_status_change(MCUBOOT_STATUS_UPGRADING); + + /* Iterate over all the images. At this point there are no aborted swaps + * and the swap types are determined for each image. By the end of the loop + * all required update operations will have been finished. + */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if (BOOT_IMAGE_NUMBER > 1) + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } + +#ifdef MCUBOOT_ENC_IMAGES + /* The keys used for encryption may no longer be valid (could belong to + * another images). Therefore, mark them as invalid to force their reload + * by boot_enc_load(). + */ + boot_enc_zeroize(BOOT_CURR_ENC(state)); +#endif /* MCUBOOT_ENC_IMAGES */ + + /* Indicate that swap is not aborted */ + boot_status_reset(&bs); +#endif /* (BOOT_IMAGE_NUMBER > 1) */ + + /* Set the previously determined swap type */ + bs.swap_type = BOOT_SWAP_TYPE(state); + + switch (BOOT_SWAP_TYPE(state)) { + case BOOT_SWAP_TYPE_NONE: + break; + + case BOOT_SWAP_TYPE_TEST: + /* fallthrough */ + case BOOT_SWAP_TYPE_PERM: + if (check_downgrade_prevention(state) != 0) { + /* Downgrade prevented */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + break; + } + /* fallthrough */ + case BOOT_SWAP_TYPE_REVERT: + rc = BOOT_HOOK_CALL(boot_perform_update_hook, BOOT_HOOK_REGULAR, + BOOT_CURR_IMG(state), &(BOOT_IMG(state, 1).hdr), + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT)); + if (rc == BOOT_HOOK_REGULAR) + { + rc = boot_perform_update(state, &bs); + } + assert(rc == 0); + break; + + case BOOT_SWAP_TYPE_FAIL: + /* The image in secondary slot was invalid and is now erased. Ensure + * we don't try to boot into it again on the next reboot. Do this by + * pretending we just reverted back to primary slot. + */ +#ifndef MCUBOOT_OVERWRITE_ONLY + /* image_ok needs to be explicitly set to avoid a new revert. */ + rc = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + break; + + default: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_PANIC) { + BOOT_LOG_ERR("panic!"); + assert(0); + + /* Loop forever... */ + FIH_PANIC; + } + } + + /* Iterate over all the images. At this point all required update operations + * have finished. By the end of the loop each image in the primary slot will + * have been re-validated. + */ + FIH_SET(fih_cnt, 0); + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + /* Hardenned to prevent from skipping check of a given image, + * tmp_img_mask is declared volatile + */ + volatile bool tmp_img_mask; + FIH_SET(tmp_img_mask, state->img_mask[BOOT_CURR_IMG(state)]); + if (FIH_EQ(tmp_img_mask, true)) { + ++fih_cnt; + continue; + } +#endif + if (BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_NONE) { + /* Attempt to read an image header from each slot. Ensure that image + * headers in slots are aligned with headers in boot_data. + * Note: Quite complicated internal logic of boot_read_image_headers + * uses boot state, the last parm, to figure out in which slot which + * header is located; when boot state is not provided, then it + * is assumed that headers are at proper slots (we are not in + * the middle of moving images, etc). + */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + /* Since headers were reloaded, it can be assumed we just performed + * a swap or overwrite. Now the header info that should be used to + * provide the data for the bootstrap, which previously was at + * secondary slot, was updated to primary slot. + */ + } + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. Image 1 primary is the currently + * executing MCUBoot image, and is therefore already validated by NSIB and + * does not need to also be validated by MCUBoot. + */ + bool image_validated_by_nsib = BOOT_CURR_IMG(state) == + CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER; + if (!image_validated_by_nsib) +#endif + { + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL); + /* Check for all possible values is redundant in normal operation it + * is meant to prevent FI attack. + */ + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) || + FIH_EQ(fih_rc, FIH_FAILURE) || + FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } +#else + /* Even if we're not re-validating the primary slot, we could be booting + * onto an empty flash chip. At least do a basic sanity check that + * the magic number on the image is OK. + */ + if (BOOT_IMG(state, BOOT_PRIMARY_SLOT).hdr.ih_magic != IMAGE_MAGIC) { + BOOT_LOG_ERR("bad image magic 0x%lx; Image=%u", (unsigned long) + BOOT_IMG(state, BOOT_PRIMARY_SLOT).hdr.ih_magic, + BOOT_CURR_IMG(state)); + rc = BOOT_EBADIMAGE; + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ + +#ifdef PM_S1_ADDRESS + if (!image_validated_by_nsib) +#endif + { + rc = boot_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } + + rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + ++fih_cnt; + } + /* + * fih_cnt should be equal to BOOT_IMAGE_NUMBER now. + * If this is not the case, at least one iteration of the loop + * has been skipped. + */ + if(FIH_NOT_EQ(fih_cnt, BOOT_IMAGE_NUMBER)) { + FIH_PANIC; + } + + fill_rsp(state, rsp); + + fih_rc = FIH_SUCCESS; +out: + /* + * Since the boot_status struct stores plaintext encryption keys, reset + * them here to avoid the possibility of jumping into an image that could + * easily recover them. + */ +#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SWAP_SAVE_ENCTLV) + like_mbedtls_zeroize(&bs, sizeof(bs)); +#else + memset(&bs, 0, sizeof(struct boot_status)); +#endif + + close_all_flash_areas(state); + FIH_RET(fih_rc); +} + +fih_ret +split_go(int loader_slot, int split_slot, void **entry) +{ + boot_sector_t *sectors; + uintptr_t entry_val; + int loader_flash_id; + int split_flash_id; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + sectors = malloc(BOOT_MAX_IMG_SECTORS * 2 * sizeof *sectors); + if (sectors == NULL) { + FIH_RET(FIH_FAILURE); + } + BOOT_IMG(&boot_data, loader_slot).sectors = sectors + 0; + BOOT_IMG(&boot_data, split_slot).sectors = sectors + BOOT_MAX_IMG_SECTORS; + + loader_flash_id = flash_area_id_from_image_slot(loader_slot); + rc = flash_area_open(loader_flash_id, + &BOOT_IMG_AREA(&boot_data, loader_slot)); + assert(rc == 0); + split_flash_id = flash_area_id_from_image_slot(split_slot); + rc = flash_area_open(split_flash_id, + &BOOT_IMG_AREA(&boot_data, split_slot)); + assert(rc == 0); + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors(&boot_data); + if (rc != 0) { + rc = SPLIT_GO_ERR; + goto done; + } + + rc = boot_read_image_headers(&boot_data, true, NULL); + if (rc != 0) { + goto done; + } + + /* Don't check the bootable image flag because we could really call a + * bootable or non-bootable image. Just validate that the image check + * passes which is distinct from the normal check. + */ + FIH_CALL(split_image_check, fih_rc, + boot_img_hdr(&boot_data, split_slot), + BOOT_IMG_AREA(&boot_data, split_slot), + boot_img_hdr(&boot_data, loader_slot), + BOOT_IMG_AREA(&boot_data, loader_slot)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto done; + } + + entry_val = boot_img_slot_off(&boot_data, split_slot) + + boot_img_hdr(&boot_data, split_slot)->ih_hdr_size; + *entry = (void *) entry_val; + rc = SPLIT_GO_OK; + +done: + flash_area_close(BOOT_IMG_AREA(&boot_data, split_slot)); + flash_area_close(BOOT_IMG_AREA(&boot_data, loader_slot)); + free(sectors); + + if (rc) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} + +#else /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +/** + * Opens all flash areas and checks which contain an image with a valid header. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_get_slot_usage(struct boot_loader_state *state) +{ + uint32_t slot; + int fa_id; + int rc; + struct image_header *hdr = NULL; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + /* Open all the slots */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + fa_id = flash_area_id_from_multi_image_slot( + BOOT_CURR_IMG(state), slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(state, slot)); + assert(rc == 0); + } + + /* Attempt to read an image header from each slot. */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + BOOT_LOG_WRN("Failed reading image headers."); + return rc; + } + + /* Check headers in all slots */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + hdr = boot_img_hdr(state, slot); + + if (boot_is_header_valid(hdr, BOOT_IMG_AREA(state, slot), state)) { + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = true; + BOOT_LOG_IMAGE_INFO(slot, hdr); + } else { + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false; + BOOT_LOG_INF("Image %d %s slot: Image not found", + BOOT_CURR_IMG(state), + (slot == BOOT_PRIMARY_SLOT) + ? "Primary" : "Secondary"); + } + } + + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + } + + return 0; +} + +/** + * Finds the slot containing the image with the highest version number for the + * current image. + * + * @param state Boot loader status information. + * + * @return NO_ACTIVE_SLOT if no available slot found, number of + * the found slot otherwise. + */ +static uint32_t +find_slot_with_highest_version(struct boot_loader_state *state) +{ + uint32_t slot; + uint32_t candidate_slot = NO_ACTIVE_SLOT; + int rc; + + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + if (state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot]) { + if (candidate_slot == NO_ACTIVE_SLOT) { + candidate_slot = slot; + } else { + rc = boot_version_cmp( + &boot_img_hdr(state, slot)->ih_ver, + &boot_img_hdr(state, candidate_slot)->ih_ver); + if (rc == 1) { + /* The version of the image being examined is greater than + * the version of the current candidate. + */ + candidate_slot = slot; + } + } + } + } + + return candidate_slot; +} + +#ifdef MCUBOOT_HAVE_LOGGING +/** + * Prints the state of the loaded images. + * + * @param state Boot loader status information. + */ +static void +print_loaded_images(struct boot_loader_state *state) +{ + uint32_t active_slot; + + (void)state; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + BOOT_LOG_INF("Image %d loaded from the %s slot", + BOOT_CURR_IMG(state), + (active_slot == BOOT_PRIMARY_SLOT) ? + "primary" : "secondary"); + } +} +#endif + +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) +/** + * Checks whether the active slot of the current image was previously selected + * to run. Erases the image if it was selected but its execution failed, + * otherwise marks it as selected if it has not been before. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_select_or_erase(struct boot_loader_state *state) +{ + const struct flash_area *fap; + int fa_id; + int rc; + uint32_t active_slot; + struct boot_swap_state* active_swap_state; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot); + rc = flash_area_open(fa_id, &fap); + assert(rc == 0); + + active_swap_state = &(state->slot_usage[BOOT_CURR_IMG(state)].swap_state); + + memset(active_swap_state, 0, sizeof(struct boot_swap_state)); + rc = boot_read_swap_state(fap, active_swap_state); + assert(rc == 0); + + if (active_swap_state->magic != BOOT_MAGIC_GOOD || + (active_swap_state->copy_done == BOOT_FLAG_SET && + active_swap_state->image_ok != BOOT_FLAG_SET)) { + /* + * A reboot happened without the image being confirmed at + * runtime or its trailer is corrupted/invalid. Erase the image + * to prevent it from being selected again on the next reboot. + */ + BOOT_LOG_DBG("Erasing faulty image in the %s slot.", + (active_slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); + rc = flash_area_erase(fap, 0, flash_area_get_size(fap)); + assert(rc == 0); + + flash_area_close(fap); + rc = -1; + } else { + if (active_swap_state->copy_done != BOOT_FLAG_SET) { + if (active_swap_state->copy_done == BOOT_FLAG_BAD) { + BOOT_LOG_DBG("The copy_done flag had an unexpected value. Its " + "value was neither 'set' nor 'unset', but 'bad'."); + } + /* + * Set the copy_done flag, indicating that the image has been + * selected to boot. It can be set in advance, before even + * validating the image, because in case the validation fails, the + * entire image slot will be erased (including the trailer). + */ + rc = boot_write_copy_done(fap); + if (rc != 0) { + BOOT_LOG_WRN("Failed to set copy_done flag of the image in " + "the %s slot.", (active_slot == BOOT_PRIMARY_SLOT) ? + "primary" : "secondary"); + rc = 0; + } + } + flash_area_close(fap); + } + + return rc; +} +#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */ + +#ifdef MCUBOOT_RAM_LOAD + +#ifndef MULTIPLE_EXECUTABLE_RAM_REGIONS +#if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE) +#error "Platform MUST define executable RAM bounds in case of RAM_LOAD" +#endif +#endif + +/** + * Verifies that the active slot of the current image can be loaded within the + * predefined bounds that are allowed to be used by executable images. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_ram_load_address(struct boot_loader_state *state) +{ + uint32_t img_dst; + uint32_t img_sz; + uint32_t img_end_addr; + uint32_t exec_ram_start; + uint32_t exec_ram_size; + + (void)state; + +#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS + int rc; + + rc = boot_get_image_exec_ram_info(BOOT_CURR_IMG(state), &exec_ram_start, + &exec_ram_size); + if (rc != 0) { + return BOOT_EBADSTATUS; + } +#else + exec_ram_start = IMAGE_EXECUTABLE_RAM_START; + exec_ram_size = IMAGE_EXECUTABLE_RAM_SIZE; +#endif + + img_dst = state->slot_usage[BOOT_CURR_IMG(state)].img_dst; + img_sz = state->slot_usage[BOOT_CURR_IMG(state)].img_sz; + + if (img_dst < exec_ram_start) { + return BOOT_EBADIMAGE; + } + + if (!boot_u32_safe_add(&img_end_addr, img_dst, img_sz)) { + return BOOT_EBADIMAGE; + } + + if (img_end_addr > (exec_ram_start + exec_ram_size)) { + return BOOT_EBADIMAGE; + } + + return 0; +} + +#ifdef MCUBOOT_ENC_IMAGES + +/** + * Copies and decrypts an image from a slot in the flash to an SRAM address. + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be copied to SRAM. + * @param hdr The image header. + * @param src_sz Size of the image. + * @param img_dst Pointer to the address at which the image needs to be + * copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, + uint32_t slot, struct image_header *hdr, + uint32_t src_sz, uint32_t img_dst) +{ + /* The flow for the decryption and copy of the image is as follows : + * 1. The whole image is copied to the RAM (header + payload + TLV). + * 2. The encryption key is loaded from the TLV in flash. + * 3. The image is then decrypted chunk by chunk in RAM (1 chunk + * is 1024 bytes). Only the payload section is decrypted. + * 4. The image is authenticated in RAM. + */ + const struct flash_area *fap_src = NULL; + struct boot_status bs; + uint32_t blk_off; + uint32_t tlv_off; + uint32_t blk_sz; + uint32_t bytes_copied = hdr->ih_hdr_size; + uint32_t chunk_sz; + uint32_t max_sz = 1024; + uint16_t idx; + uint8_t * cur_dst; + int area_id; + int rc; + uint8_t * ram_dst = (void *)(IMAGE_RAM_BASE + img_dst); + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap_src); + if (rc != 0){ + return BOOT_EFLASH; + } + + tlv_off = BOOT_TLV_OFF(hdr); + + /* Copying the whole image in RAM */ + rc = flash_area_read(fap_src, 0, ram_dst, src_sz); + if (rc != 0) { + goto done; + } + + rc = boot_enc_load(BOOT_CURR_ENC(state), slot, hdr, fap_src, &bs); + if (rc < 0) { + goto done; + } + + /* if rc > 0 then the key has already been loaded */ + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), slot, &bs)) { + goto done; + } + + /* Starting at the end of the header as the header section is not encrypted */ + while (bytes_copied < tlv_off) { /* TLV section copied previously */ + if (src_sz - bytes_copied > max_sz) { + chunk_sz = max_sz; + } else { + chunk_sz = src_sz - bytes_copied; + } + + cur_dst = ram_dst + bytes_copied; + blk_sz = chunk_sz; + idx = 0; + blk_off = ((bytes_copied) - hdr->ih_hdr_size) & 0xf; + if (bytes_copied + chunk_sz > tlv_off) { + /* Going over TLV section + * Part of the chunk is encrypted payload */ + blk_sz = tlv_off - (bytes_copied); + } + boot_enc_decrypt(BOOT_CURR_ENC(state), slot, + (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, cur_dst); + bytes_copied += chunk_sz; + } + rc = 0; + +done: + flash_area_close(fap_src); + + return rc; +} + +#endif /* MCUBOOT_ENC_IMAGES */ +/** + * Copies a slot of the current image into SRAM. + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be copied to SRAM. + * @param img_dst The address at which the image needs to be copied to + * SRAM. + * @param img_sz The size of the image that needs to be copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_copy_image_to_sram(struct boot_loader_state *state, int slot, + uint32_t img_dst, uint32_t img_sz) +{ + int rc; + const struct flash_area *fap_src = NULL; + int area_id; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + + rc = flash_area_open(area_id, &fap_src); + if (rc != 0) { + return BOOT_EFLASH; + } + + /* Direct copy from flash to its new location in SRAM. */ + rc = flash_area_read(fap_src, 0, (void *)(IMAGE_RAM_BASE + img_dst), img_sz); + if (rc != 0) { + BOOT_LOG_INF("Error whilst copying image %d from Flash to SRAM: %d", + BOOT_CURR_IMG(state), rc); + } + + flash_area_close(fap_src); + + return rc; +} + +#if (BOOT_IMAGE_NUMBER > 1) +/** + * Checks if two memory regions (A and B) are overlap or not. + * + * @param start_a Start of the A region. + * @param end_a End of the A region. + * @param start_b Start of the B region. + * @param end_b End of the B region. + * + * @return true if there is overlap; false otherwise. + */ +static bool +do_regions_overlap(uint32_t start_a, uint32_t end_a, + uint32_t start_b, uint32_t end_b) +{ + if (start_b > end_a) { + return false; + } else if (start_b >= start_a) { + return true; + } else if (end_b > start_a) { + return true; + } + + return false; +} + +/** + * Checks if the image we want to load to memory overlap with an already + * ramloaded image. + * + * @param state Boot loader status information. + * + * @return 0 if there is no overlap; nonzero otherwise. + */ +static int +boot_check_ram_load_overlapping(struct boot_loader_state *state) +{ + uint32_t i; + + uint32_t start_a; + uint32_t end_a; + uint32_t start_b; + uint32_t end_b; + uint32_t image_id_to_check = BOOT_CURR_IMG(state); + + start_a = state->slot_usage[image_id_to_check].img_dst; + /* Safe to add here, values are already verified in + * boot_verify_ram_load_address() */ + end_a = start_a + state->slot_usage[image_id_to_check].img_sz; + + for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { + if (state->slot_usage[i].active_slot == NO_ACTIVE_SLOT + || i == image_id_to_check) { + continue; + } + + start_b = state->slot_usage[i].img_dst; + /* Safe to add here, values are already verified in + * boot_verify_ram_load_address() */ + end_b = start_b + state->slot_usage[i].img_sz; + + if (do_regions_overlap(start_a, end_a, start_b, end_b)) { + return -1; + } + } + + return 0; +} +#endif + +/** + * Loads the active slot of the current image into SRAM. The load address and + * image size is extracted from the image header. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_load_image_to_sram(struct boot_loader_state *state) +{ + uint32_t active_slot; + struct image_header *hdr = NULL; + uint32_t img_dst; + uint32_t img_sz; + int rc; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + hdr = boot_img_hdr(state, active_slot); + + if (hdr->ih_flags & IMAGE_F_RAM_LOAD) { + + img_dst = hdr->ih_load_addr; + + rc = boot_read_image_size(state, active_slot, &img_sz); + if (rc != 0) { + return rc; + } + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = img_dst; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = img_sz; + + rc = boot_verify_ram_load_address(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM load address 0x%x is invalid.", BOOT_CURR_IMG(state), img_dst); + return rc; + } + +#if (BOOT_IMAGE_NUMBER > 1) + rc = boot_check_ram_load_overlapping(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to address 0x%x would overlap with\ + another image.", BOOT_CURR_IMG(state), img_dst); + return rc; + } +#endif +#ifdef MCUBOOT_ENC_IMAGES + /* decrypt image if encrypted and copy it to RAM */ + if (IS_ENCRYPTED(hdr)) { + rc = boot_decrypt_and_copy_image_to_sram(state, active_slot, hdr, img_sz, img_dst); + } else { + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); + } +#else + /* Copy image to the load address from where it currently resides in + * flash. + */ + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); +#endif + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is failed.", BOOT_CURR_IMG(state), img_dst); + } else { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is succeeded.", BOOT_CURR_IMG(state), img_dst); + } + } else { + /* Only images that support IMAGE_F_RAM_LOAD are allowed if + * MCUBOOT_RAM_LOAD is set. + */ + rc = BOOT_EBADIMAGE; + } + + if (rc != 0) { + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; + } + + return rc; +} + +/** + * Removes an image from SRAM, by overwriting it with zeros. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static inline int +boot_remove_image_from_sram(struct boot_loader_state *state) +{ + (void)state; + + BOOT_LOG_INF("Removing image %d from SRAM at address 0x%x", + BOOT_CURR_IMG(state), + state->slot_usage[BOOT_CURR_IMG(state)].img_dst); + + memset((void*)(IMAGE_RAM_BASE + state->slot_usage[BOOT_CURR_IMG(state)].img_dst), + 0, state->slot_usage[BOOT_CURR_IMG(state)].img_sz); + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; + + return 0; +} + +/** + * Removes an image from flash by erasing the corresponding flash area + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be erased. + * + * @return 0 on success; nonzero on failure. + */ +static inline int +boot_remove_image_from_flash(struct boot_loader_state *state, uint32_t slot) +{ + int area_id; + int rc; + const struct flash_area *fap; + + (void)state; + + BOOT_LOG_INF("Removing image %d slot %d from flash", BOOT_CURR_IMG(state), + slot); + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc == 0) { + flash_area_erase(fap, 0, flash_area_get_size(fap)); + flash_area_close(fap); + } + + return rc; +} +#endif /* MCUBOOT_RAM_LOAD */ + + +/** + * Tries to load a slot for all the images with validation. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +fih_ret +boot_load_and_validate_images(struct boot_loader_state *state) +{ + uint32_t active_slot; + int rc; + fih_ret fih_rc; + + /* Go over all the images and try to load one */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { + /* All slots tried until a valid image found. Breaking from this loop + * means that a valid image found or already loaded. If no slot is + * found the function returns with error code. */ + while (true) { + /* Go over all the slots and try to load one */ + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + if (active_slot != NO_ACTIVE_SLOT){ + /* A slot is already active, go to next image. */ + break; + } + + active_slot = find_slot_with_highest_version(state); + if (active_slot == NO_ACTIVE_SLOT) { + BOOT_LOG_INF("No slot to load for image %d", + BOOT_CURR_IMG(state)); + FIH_RET(FIH_FAILURE); + } + + /* Save the number of the active slot. */ + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot; + +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + +#ifdef MCUBOOT_DIRECT_XIP + rc = boot_rom_address_check(state); + if (rc != 0) { + /* The image is placed in an unsuitable slot. */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } + +#ifdef MCUBOOT_DIRECT_XIP_REVERT + rc = boot_select_or_erase(state); + if (rc != 0) { + /* The selected image slot has been erased. */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } +#endif /* MCUBOOT_DIRECT_XIP_REVERT */ +#endif /* MCUBOOT_DIRECT_XIP */ + +#ifdef MCUBOOT_RAM_LOAD + /* Image is first loaded to RAM and authenticated there in order to + * prevent TOCTOU attack during image copy. This could be applied + * when loading images from external (untrusted) flash to internal + * (trusted) RAM and image is authenticated before copying. + */ + rc = boot_load_image_to_sram(state); + if (rc != 0 ) { + /* Image cannot be ramloaded. */ + boot_remove_image_from_flash(state, active_slot); + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } +#endif /* MCUBOOT_RAM_LOAD */ + + FIH_CALL(boot_validate_slot, fih_rc, state, active_slot, NULL); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + /* Image is invalid. */ +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(state); +#endif /* MCUBOOT_RAM_LOAD */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } + + /* Valid image loaded from a slot, go to next image. */ + break; + } + } + + FIH_RET(FIH_SUCCESS); +} + +/** + * Updates the security counter for the current image. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_update_hw_rollback_protection(struct boot_loader_state *state) +{ +#ifdef MCUBOOT_HW_ROLLBACK_PROT + int rc; + + /* Update the stored security counter with the newer (active) image's + * security counter value. + */ +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) + /* When the 'revert' mechanism is enabled in direct-xip mode, the + * security counter can be increased only after reboot, if the image + * has been confirmed at runtime (the image_ok flag has been set). + * This way a 'revert' can be performed when it's necessary. + */ + if (state->slot_usage[BOOT_CURR_IMG(state)].swap_state.image_ok == BOOT_FLAG_SET) { +#endif + rc = boot_update_security_counter(BOOT_CURR_IMG(state), + state->slot_usage[BOOT_CURR_IMG(state)].active_slot, + boot_img_hdr(state, state->slot_usage[BOOT_CURR_IMG(state)].active_slot)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image %d validation.", BOOT_CURR_IMG(state)); + return rc; + } +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) + } +#endif + + return 0; + +#else /* MCUBOOT_HW_ROLLBACK_PROT */ + (void) (state); + return 0; +#endif +} + +fih_ret +context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + rc = boot_get_slot_usage(state); + if (rc != 0) { + goto out; + } + +#if (BOOT_IMAGE_NUMBER > 1) + while (true) { +#endif + FIH_CALL(boot_load_and_validate_images, fih_rc, state); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + +#if (BOOT_IMAGE_NUMBER > 1) + rc = boot_verify_dependencies(state); + if (rc != 0) { + /* Dependency check failed for an image, it has been removed from + * SRAM in case of MCUBOOT_RAM_LOAD strategy, and set to + * unavailable. Try to load an image from another slot. + */ + continue; + } + /* Dependency check was successful. */ + break; + } +#endif + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + rc = boot_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + rc = boot_add_shared_data(state, (uint8_t)state->slot_usage[BOOT_CURR_IMG(state)].active_slot); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } + + /* All image loaded successfully. */ +#ifdef MCUBOOT_HAVE_LOGGING + print_loaded_images(state); +#endif + + fill_rsp(state, rsp); + +out: + close_all_flash_areas(state); + + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +/** + * Prepares the booting process. This function moves images around in flash as + * appropriate, and tells you what address to boot from. + * + * @param rsp On success, indicates how booting should occur. + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + + boot_state_clear(NULL); + + FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + FIH_RET(fih_rc); +} + +/** + * Prepares the booting process, considering only a single image. This function + * moves images around in flash as appropriate, and tells you what address to + * boot from. + * + * @param rsp On success, indicates how booting should occur. + * + * @param image_id The image ID to prepare the boot process for. + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go_for_image_id(struct boot_rsp *rsp, uint32_t image_id) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + + if (image_id >= BOOT_IMAGE_NUMBER) { + FIH_RET(FIH_FAILURE); + } + +#if BOOT_IMAGE_NUMBER > 1 + memset(&boot_data.img_mask, 1, BOOT_IMAGE_NUMBER); + boot_data.img_mask[image_id] = 0; +#endif + + FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + FIH_RET(fih_rc); +} + +/** + * Clears the boot state, so that previous operations have no effect on new + * ones. + * + * @param state The state that should be cleared. If the value + * is NULL, the default bootloader state will be + * cleared. + */ +void boot_state_clear(struct boot_loader_state *state) +{ + if (state != NULL) { + memset(state, 0, sizeof(struct boot_loader_state)); + } else { + memset(&boot_data, 0, sizeof(struct boot_loader_state)); + } +} + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +/** + * Reads image data to find out the maximum application sizes. Only needs to + * be called in serial recovery mode, as the state information is unpopulated + * at that time + */ +static void boot_fetch_slot_state_sizes(void) +{ + size_t slot; + int rc = -1; + int fa_id; + int image_index; + + IMAGES_ITER(BOOT_CURR_IMG(&boot_data)) { + int max_size = 0; + + image_index = BOOT_CURR_IMG(&boot_data); + + BOOT_IMG(&boot_data, BOOT_PRIMARY_SLOT).sectors = + sector_buffers.primary[image_index]; + BOOT_IMG(&boot_data, BOOT_SECONDARY_SLOT).sectors = + sector_buffers.secondary[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + boot_data.scratch.sectors = sector_buffers.scratch; +#endif + + /* Open primary and secondary image areas for the duration + * of this call. + */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + fa_id = flash_area_id_from_multi_image_slot(image_index, slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(&boot_data, slot)); + assert(rc == 0); + + if (rc != 0) { + goto finish; + } + } + +#if MCUBOOT_SWAP_USING_SCRATCH + rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, + &BOOT_SCRATCH_AREA(&boot_data)); + assert(rc == 0); + + if (rc != 0) { + goto finish; + } +#endif + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors_recovery(&boot_data); + + if (rc == 0) { + max_size = app_max_size(&boot_data); + + if (max_size > 0) { + image_max_sizes[image_index].calculated = true; + image_max_sizes[image_index].max_size = max_size; + } + } + } + +finish: + close_all_flash_areas(&boot_data); + memset(&boot_data, 0x00, sizeof(boot_data)); +} +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +/** + * Fetches the maximum allowed size of the image + */ +const struct image_max_size *boot_get_max_app_size(void) +{ +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) + uint8_t i = 0; + + while (i < BOOT_IMAGE_NUMBER) { + if (image_max_sizes[i].calculated == true) { + break; + } + + ++i; + } + + if (i == BOOT_IMAGE_NUMBER) { + /* Information not available, need to fetch it */ + boot_fetch_slot_state_sizes(); + } +#endif + + return image_max_sizes; +} +#endif diff --git a/bootloader/mcuboot/boot/bootutil/src/loader_legacy_child_parent.c b/bootloader/mcuboot/boot/bootutil/src/loader_legacy_child_parent.c new file mode 100644 index 0000000..53f40c3 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/loader_legacy_child_parent.c @@ -0,0 +1,3896 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2016-2020 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2023 Arm Limited + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * This file provides an interface to the boot loader. Functions defined in + * this file should only be called while the boot loader is running. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/security_cnt.h" +#include "bootutil/boot_record.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/ramload.h" +#include "bootutil/boot_hooks.h" +#include "bootutil/mcuboot_status.h" + +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#ifdef __ZEPHYR__ +#include +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) +#include +#ifdef CONFIG_PCD_READ_NETCORE_APP_VERSION +#include +int pcd_version_cmp_net(const struct flash_area *fap, struct image_header *hdr); +#endif +#endif + +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +#include +#endif + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static struct boot_loader_state boot_data; +#ifdef PM_S1_ADDRESS +static bool owner_nsib[BOOT_IMAGE_NUMBER] = {false}; +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0}; +#endif + +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +#if !defined(__BOOTSIM__) +/* Used for holding static buffers in multiple functions to work around issues + * in older versions of gcc (e.g. 4.8.4) + */ +struct sector_buffer_t { + boot_sector_t primary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + boot_sector_t secondary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; +#if MCUBOOT_SWAP_USING_SCRATCH + boot_sector_t scratch[BOOT_MAX_IMG_SECTORS]; +#endif +}; + +static struct sector_buffer_t sector_buffers; +#endif +#endif + +#if (BOOT_IMAGE_NUMBER > 1) +#define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x)) +#else +#define IMAGES_ITER(x) +#endif + +/* + * This macro allows some control on the allocation of local variables. + * When running natively on a target, we don't want to allocated huge + * variables on the stack, so make them global instead. For the simulator + * we want to run as many threads as there are tests, and it's safer + * to just make those variables stack allocated. + */ +#if !defined(__BOOTSIM__) +#define TARGET_STATIC static +#else +#define TARGET_STATIC +#endif + +#if BOOT_MAX_ALIGN > 1024 +#define BUF_SZ BOOT_MAX_ALIGN +#else +#define BUF_SZ 1024 +#endif + +#define NO_ACTIVE_SLOT UINT32_MAX + +static int +boot_read_image_headers(struct boot_loader_state *state, bool require_all, + struct boot_status *bs) +{ + int rc; + int i; + + for (i = 0; i < BOOT_NUM_SLOTS; i++) { + rc = BOOT_HOOK_CALL(boot_read_image_header_hook, BOOT_HOOK_REGULAR, + BOOT_CURR_IMG(state), i, boot_img_hdr(state, i)); + if (rc == BOOT_HOOK_REGULAR) + { + rc = boot_read_image_header(state, i, boot_img_hdr(state, i), bs); + } + if (rc != 0) { + /* If `require_all` is set, fail on any single fail, otherwise + * if at least the first slot's header was read successfully, + * then the boot loader can attempt a boot. + * + * Failure to read any headers is a fatal error. + */ +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. The primary slot of the second image + * (image 1) will not contain a valid image header until an upgrade + * of mcuboot has happened (filling S1 with the new version). + */ + if (BOOT_CURR_IMG(state) == 1 && i == 0) { + continue; + } +#endif /* PM_S1_ADDRESS */ + if (i > 0 && !require_all) { + return 0; + } else { + return rc; + } + } + } + + return 0; +} + +/** + * Saves boot status and shared data for current image. + * + * @param state Boot loader status information. + * @param active_slot Index of the slot will be loaded for current image. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_add_shared_data(struct boot_loader_state *state, + uint8_t active_slot) +{ +#if defined(MCUBOOT_MEASURED_BOOT) || defined(MCUBOOT_DATA_SHARING) + int rc; + +#ifdef MCUBOOT_MEASURED_BOOT + rc = boot_save_boot_status(BOOT_CURR_IMG(state), + boot_img_hdr(state, active_slot), + BOOT_IMG_AREA(state, active_slot)); + if (rc != 0) { + BOOT_LOG_ERR("Failed to add image data to shared area"); + return rc; + } +#endif /* MCUBOOT_MEASURED_BOOT */ + +#ifdef MCUBOOT_DATA_SHARING + rc = boot_save_shared_data(boot_img_hdr(state, active_slot), + BOOT_IMG_AREA(state, active_slot), + active_slot, image_max_sizes); + if (rc != 0) { + BOOT_LOG_ERR("Failed to add data to shared memory area."); + return rc; + } +#endif /* MCUBOOT_DATA_SHARING */ + + return 0; + +#else /* MCUBOOT_MEASURED_BOOT || MCUBOOT_DATA_SHARING */ + (void) (state); + (void) (active_slot); + + return 0; +#endif +} + +/** + * Fills rsp to indicate how booting should occur. + * + * @param state Boot loader status information. + * @param rsp boot_rsp struct to fill. + */ +static void +fill_rsp(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + uint32_t active_slot; + +#if (BOOT_IMAGE_NUMBER > 1) + /* Always boot from the first enabled image. */ + BOOT_CURR_IMG(state) = 0; + IMAGES_ITER(BOOT_CURR_IMG(state)) { + if (!state->img_mask[BOOT_CURR_IMG(state)]) { + break; + } + } + /* At least one image must be active, otherwise skip the execution */ + if (BOOT_CURR_IMG(state) >= BOOT_IMAGE_NUMBER) { + return; + } +#endif + +#if defined(MCUBOOT_DIRECT_XIP) || defined(MCUBOOT_RAM_LOAD) + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; +#else + active_slot = BOOT_PRIMARY_SLOT; +#endif + + rsp->br_flash_dev_id = flash_area_get_device_id(BOOT_IMG_AREA(state, active_slot)); + rsp->br_image_off = boot_img_slot_off(state, active_slot); + rsp->br_hdr = boot_img_hdr(state, active_slot); +} + +/** + * Closes all flash areas. + * + * @param state Boot loader status information. + */ +static void +close_all_flash_areas(struct boot_loader_state *state) +{ + uint32_t slot; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif +#if MCUBOOT_SWAP_USING_SCRATCH + flash_area_close(BOOT_SCRATCH_AREA(state)); +#endif + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + flash_area_close(BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot)); + } + } +} + +#if (BOOT_IMAGE_NUMBER > 1) || \ + defined(MCUBOOT_DIRECT_XIP) || \ + defined(MCUBOOT_RAM_LOAD) || \ + defined(MCUBOOT_DOWNGRADE_PREVENTION) +/** + * Compare image version numbers + * + * By default, the comparison does not take build number into account. + * Enable MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER to take the build number into account. + * + * @param ver1 Pointer to the first image version to compare. + * @param ver2 Pointer to the second image version to compare. + * + * @retval -1 If ver1 is less than ver2. + * @retval 0 If the image version numbers are equal. + * @retval 1 If ver1 is greater than ver2. + */ +static int +boot_version_cmp(const struct image_version *ver1, + const struct image_version *ver2) +{ + if (ver1->iv_major > ver2->iv_major) { + return 1; + } + if (ver1->iv_major < ver2->iv_major) { + return -1; + } + /* The major version numbers are equal, continue comparison. */ + if (ver1->iv_minor > ver2->iv_minor) { + return 1; + } + if (ver1->iv_minor < ver2->iv_minor) { + return -1; + } + /* The minor version numbers are equal, continue comparison. */ + if (ver1->iv_revision > ver2->iv_revision) { + return 1; + } + if (ver1->iv_revision < ver2->iv_revision) { + return -1; + } + +#if defined(MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER) + /* The revisions are equal, continue comparison. */ + if (ver1->iv_build_num > ver2->iv_build_num) { + return 1; + } + if (ver1->iv_build_num < ver2->iv_build_num) { + return -1; + } +#endif + + return 0; +} +#endif + +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_initialize_area(struct boot_loader_state *state, int flash_area) +{ + uint32_t num_sectors = BOOT_MAX_IMG_SECTORS; + boot_sector_t *out_sectors; + uint32_t *out_num_sectors; + int rc; + + num_sectors = BOOT_MAX_IMG_SECTORS; + + if (flash_area == FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state))) { + out_sectors = BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors; + out_num_sectors = &BOOT_IMG(state, BOOT_PRIMARY_SLOT).num_sectors; + } else if (flash_area == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { + out_sectors = BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors; + out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors; +#if MCUBOOT_SWAP_USING_SCRATCH + } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) { + out_sectors = state->scratch.sectors; + out_num_sectors = &state->scratch.num_sectors; +#endif + } else { + return BOOT_EFLASH; + } + +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS + rc = flash_area_get_sectors(flash_area, &num_sectors, out_sectors); +#else + _Static_assert(sizeof(int) <= sizeof(uint32_t), "Fix needed"); + rc = flash_area_to_sectors(flash_area, (int *)&num_sectors, out_sectors); +#endif /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ + if (rc != 0) { + return rc; + } + *out_num_sectors = num_sectors; + return 0; +} +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_read_sectors_recovery(struct boot_loader_state *state) +{ + uint8_t image_index; + int rc; + + image_index = BOOT_CURR_IMG(state); + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SECONDARY(image_index)); + if (rc != 0) { + /* We need to differentiate from the primary image issue */ + return BOOT_EFLASH_SEC; + } + + return 0; +} +#endif + + +#if (BOOT_IMAGE_NUMBER > 1) + +static int +boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot); + +/** + * Check the image dependency whether it is satisfied and modify + * the swap type if necessary. + * + * @param dep Image dependency which has to be verified. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_slot_dependency(struct boot_loader_state *state, + struct image_dependency *dep) +{ + struct image_version *dep_version; + size_t dep_slot; + int rc; + + /* Determine the source of the image which is the subject of + * the dependency and get it's version. */ +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + uint8_t swap_type = state->swap_type[dep->image_id]; + dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SECONDARY_SLOT + : BOOT_PRIMARY_SLOT; +#else + dep_slot = state->slot_usage[dep->image_id].active_slot; +#endif + + dep_version = &state->imgs[dep->image_id][dep_slot].hdr.ih_ver; + + rc = boot_version_cmp(dep_version, &dep->image_min_version); +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + if (rc < 0) { + /* Dependency not satisfied. + * Modify the swap type to decrease the version number of the image + * (which will be located in the primary slot after the boot process), + * consequently the number of unsatisfied dependencies will be + * decreased or remain the same. + */ + switch (BOOT_SWAP_TYPE(state)) { + case BOOT_SWAP_TYPE_TEST: + case BOOT_SWAP_TYPE_PERM: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + break; + case BOOT_SWAP_TYPE_NONE: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_REVERT; + break; + default: + break; + } + } else { + /* Dependency satisfied. */ + rc = 0; + } +#else + if (rc >= 0) { + /* Dependency satisfied. */ + rc = 0; + } +#endif + + return rc; +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +/** + * Iterate over all the images and verify whether the image dependencies in the + * TLV area are all satisfied and update the related swap type if necessary. + */ +static int +boot_verify_dependencies(struct boot_loader_state *state) +{ + int rc = -1; + uint8_t slot; + + BOOT_CURR_IMG(state) = 0; + while (BOOT_CURR_IMG(state) < BOOT_IMAGE_NUMBER) { + if (state->img_mask[BOOT_CURR_IMG(state)]) { + BOOT_CURR_IMG(state)++; + continue; + } + if (BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_NONE && + BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_FAIL) { + slot = BOOT_SECONDARY_SLOT; + } else { + slot = BOOT_PRIMARY_SLOT; + } + + rc = boot_verify_slot_dependencies(state, slot); + if (rc == 0) { + /* All dependencies've been satisfied, continue with next image. */ + BOOT_CURR_IMG(state)++; + } else if (rc == BOOT_EBADIMAGE) { + /* Cannot upgrade due to non-met dependencies, so disable all + * image upgrades. + */ + for (int idx = 0; idx < BOOT_IMAGE_NUMBER; idx++) { + BOOT_CURR_IMG(state) = idx; + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } + break; + } else { + /* Other error happened, images are inconsistent */ + return rc; + } + } + return rc; +} +#else + +#if defined MCUBOOT_RAM_LOAD +static inline int +boot_remove_image_from_sram(struct boot_loader_state *state); +#endif + +/** + * Checks the dependency of all the active slots. If an image found with + * invalid or not satisfied dependencies the image is removed from SRAM (in + * case of MCUBOOT_RAM_LOAD strategy) and its slot is set to unavailable. + * + * @param state Boot loader status information. + * + * @return 0 if dependencies are met; nonzero otherwise. + */ +static int +boot_verify_dependencies(struct boot_loader_state *state) +{ + int rc = -1; + uint32_t active_slot; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + rc = boot_verify_slot_dependencies(state, active_slot); + if (rc != 0) { + /* Dependencies not met or invalid dependencies. */ + +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(state); +#endif /* MCUBOOT_RAM_LOAD */ + + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + + return rc; + } + } + + return rc; +} +#endif + +/** + * Read all dependency TLVs of an image from the flash and verify + * one after another to see if they are all satisfied. + * + * @param slot Image slot number. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) +{ + const struct flash_area *fap; + struct image_tlv_iter it; + struct image_dependency dep; + uint32_t off; + uint16_t len; + int area_id; + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = bootutil_tlv_iter_begin(&it, boot_img_hdr(state, slot), fap, + IMAGE_TLV_DEPENDENCY, true); + if (rc != 0) { + goto done; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + if (rc < 0) { + return -1; + } else if (rc > 0) { + rc = 0; + break; + } + + if (len != sizeof(dep)) { + rc = BOOT_EBADIMAGE; + goto done; + } + + rc = LOAD_IMAGE_DATA(boot_img_hdr(state, slot), + fap, off, &dep, len); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + if (dep.image_id >= BOOT_IMAGE_NUMBER) { + rc = BOOT_EBADARGS; + goto done; + } + + /* Verify dependency and modify the swap type if not satisfied. */ + rc = boot_verify_slot_dependency(state, &dep); + if (rc != 0) { + /* Dependency not satisfied */ + goto done; + } + } + +done: + flash_area_close(fap); + return rc; +} + +#endif /* (BOOT_IMAGE_NUMBER > 1) */ + +#if !defined(MCUBOOT_DIRECT_XIP) +/* + * Compute the total size of the given image. Includes the size of + * the TLVs. + */ +#if !defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_OVERWRITE_ONLY_FAST) +static int +boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) +{ + const struct flash_area *fap; + struct image_tlv_info info; + uint32_t off; + uint32_t protect_tlv_size; + int area_id; + int rc; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), boot_img_hdr(state, slot))) { + uint32_t tmp_size = 0; + + rc = bootutil_get_img_decomp_size(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off = boot_img_hdr(state, slot)->ih_hdr_size + tmp_size; + + rc = boot_size_protected_tlvs(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off += tmp_size; + + if (flash_area_read(fap, (BOOT_TLV_OFF(boot_img_hdr(state, slot)) + + boot_img_hdr(state, slot)->ih_protect_tlv_size), &info, + sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + info.it_tlv_tot; + } else { +#else + if (1) { +#endif + off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); + + if (flash_area_read(fap, off, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (protect_tlv_size != info.it_tlv_tot) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + } else if (protect_tlv_size != 0) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + protect_tlv_size + info.it_tlv_tot; + } + + rc = 0; + +done: + flash_area_close(fap); + return rc; +} +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + +#if !defined(MCUBOOT_RAM_LOAD) +static uint32_t +boot_write_sz(struct boot_loader_state *state) +{ + uint32_t elem_sz; +#if MCUBOOT_SWAP_USING_SCRATCH + uint32_t align; +#endif + + /* Figure out what size to write update status update as. The size depends + * on what the minimum write size is for scratch area, active image slot. + * We need to use the bigger of those 2 values. + */ + elem_sz = flash_area_align(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); +#if MCUBOOT_SWAP_USING_SCRATCH + align = flash_area_align(BOOT_SCRATCH_AREA(state)); + if (align > elem_sz) { + elem_sz = align; + } +#endif + + return elem_sz; +} + +/** + * Determines the sector layout of both image slots and the scratch area. + * This information is necessary for calculating the number of bytes to erase + * and copy during an image swap. The information collected during this + * function is used to populate the state. + */ +static int +boot_read_sectors(struct boot_loader_state *state) +{ + uint8_t image_index; + int rc; + + image_index = BOOT_CURR_IMG(state); + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SECONDARY(image_index)); + if (rc != 0) { + /* We need to differentiate from the primary image issue */ + return BOOT_EFLASH_SEC; + } + +#if MCUBOOT_SWAP_USING_SCRATCH + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SCRATCH); + if (rc != 0) { + return BOOT_EFLASH; + } +#endif + + BOOT_WRITE_SZ(state) = boot_write_sz(state); + + return 0; +} + +void +boot_status_reset(struct boot_status *bs) +{ +#ifdef MCUBOOT_ENC_IMAGES + memset(&bs->enckey, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_KEY_ALIGN_SIZE); +#if MCUBOOT_SWAP_SAVE_ENCTLV + memset(&bs->enctlv, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_TLV_ALIGN_SIZE); +#endif +#endif /* MCUBOOT_ENC_IMAGES */ + + bs->use_scratch = 0; + bs->swap_size = 0; + bs->source = 0; + + bs->op = BOOT_STATUS_OP_MOVE; + bs->idx = BOOT_STATUS_IDX_0; + bs->state = BOOT_STATUS_STATE_0; + bs->swap_type = BOOT_SWAP_TYPE_NONE; +} + +bool +boot_status_is_reset(const struct boot_status *bs) +{ + return (bs->op == BOOT_STATUS_OP_MOVE && + bs->idx == BOOT_STATUS_IDX_0 && + bs->state == BOOT_STATUS_STATE_0); +} + +/** + * Writes the supplied boot status to the flash file system. The boot status + * contains the current state of an in-progress image copy operation. + * + * @param bs The boot status to write. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_write_status(const struct boot_loader_state *state, struct boot_status *bs) +{ + const struct flash_area *fap; + uint32_t off; + int area_id; + int rc = 0; + uint8_t buf[BOOT_MAX_ALIGN]; + uint32_t align; + uint8_t erased_val; + + /* NOTE: The first sector copied (that is the last sector on slot) contains + * the trailer. Since in the last step the primary slot is erased, the + * first two status writes go to the scratch which will be copied to + * the primary slot! + */ + +#if MCUBOOT_SWAP_USING_SCRATCH + if (bs->use_scratch) { + /* Write to scratch. */ + area_id = FLASH_AREA_IMAGE_SCRATCH; + } else { +#endif + /* Write to the primary slot. */ + area_id = FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state)); +#if MCUBOOT_SWAP_USING_SCRATCH + } +#endif + + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + off = boot_status_off(fap) + + boot_status_internal_off(bs, BOOT_WRITE_SZ(state)); + align = flash_area_align(fap); + erased_val = flash_area_erased_val(fap); + memset(buf, erased_val, BOOT_MAX_ALIGN); + buf[0] = bs->state; + + BOOT_LOG_DBG("writing swap status; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)flash_area_get_off(fap) + off); + + rc = flash_area_write(fap, off, buf, align); + if (rc != 0) { + rc = BOOT_EFLASH; + } + + flash_area_close(fap); + + return rc; +} +#endif /* !MCUBOOT_RAM_LOAD */ +#endif /* !MCUBOOT_DIRECT_XIP */ + +/* + * Validate image hash/signature and optionally the security counter in a slot. + */ +static fih_ret +boot_image_check(struct boot_loader_state *state, struct image_header *hdr, + const struct flash_area *fap, struct boot_status *bs) +{ + TARGET_STATIC uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + (void)bs; + (void)rc; + +/* In the case of ram loading the image has already been decrypted as it is + * decrypted when copied in ram */ +#if defined(MCUBOOT_ENC_IMAGES) && !defined(MCUBOOT_RAM_LOAD) + if (MUST_DECRYPT(fap, BOOT_CURR_IMG(state), hdr)) { + rc = boot_enc_load(BOOT_CURR_ENC(state), 1, hdr, fap, bs); + if (rc < 0) { + FIH_RET(fih_rc); + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) { + FIH_RET(fih_rc); + } + } +#endif + + FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), + BOOT_CURR_IMG(state), hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, + NULL, 0, NULL); + + FIH_RET(fih_rc); +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +static fih_ret +split_image_check(struct image_header *app_hdr, + const struct flash_area *app_fap, + struct image_header *loader_hdr, + const struct flash_area *loader_fap) +{ + static void *tmpbuf; + uint8_t loader_hash[32]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + if (!tmpbuf) { + tmpbuf = malloc(BOOT_TMPBUF_SZ); + if (!tmpbuf) { + goto out; + } + } + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, loader_hdr, loader_fap, + tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, loader_hash); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(fih_rc); + } + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, app_hdr, app_fap, + tmpbuf, BOOT_TMPBUF_SZ, loader_hash, 32, NULL); + +out: + FIH_RET(fih_rc); +} +#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */ + +/* + * Check that this is a valid header. Valid means that the magic is + * correct, and that the sizes/offsets are "sane". Sane means that + * there is no overflow on the arithmetic, and that the result fits + * within the flash area we are in. Also check the flags in the image + * and class the image as invalid if flags for encryption/compression + * are present but these features are not enabled. + */ +static bool +boot_is_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state) +{ + uint32_t size; + + (void)state; + + if (hdr->ih_magic != IMAGE_MAGIC) { + return false; + } + + if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size)) { + return false; + } + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (!MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { +#else + if (1) { +#endif + if (!boot_u32_safe_add(&size, size, hdr->ih_protect_tlv_size)) { + return false; + } + } + + if (size >= flash_area_get_size(fap)) { + return false; + } + +#if !defined(MCUBOOT_ENC_IMAGES) + if (IS_ENCRYPTED(hdr)) { + return false; + } +#else + if ((hdr->ih_flags & IMAGE_F_ENCRYPTED_AES128) && + (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES256)) + { + return false; + } +#endif + +#if !defined(MCUBOOT_DECOMPRESS_IMAGES) + if (IS_COMPRESSED(hdr)) { + return false; + } +#else + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { + if (!boot_is_compressed_header_valid(hdr, fap, state)) { + return false; + } + } +#endif + + return true; +} + +/* + * Check that a memory area consists of a given value. + */ +static inline bool +boot_data_is_set_to(uint8_t val, void *data, size_t len) +{ + uint8_t i; + uint8_t *p = (uint8_t *)data; + for (i = 0; i < len; i++) { + if (val != p[i]) { + return false; + } + } + return true; +} + +static int +boot_check_header_erased(struct boot_loader_state *state, int slot) +{ + const struct flash_area *fap; + struct image_header *hdr; + uint8_t erased_val; + int area_id; + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + return -1; + } + + erased_val = flash_area_erased_val(fap); + flash_area_close(fap); + + hdr = boot_img_hdr(state, slot); + if (!boot_data_is_set_to(erased_val, &hdr->ih_magic, sizeof(hdr->ih_magic))) { + return -1; + } + + return 0; +} + +#if defined(MCUBOOT_DIRECT_XIP) +/** + * Check if image in slot has been set with specific ROM address to run from + * and whether the slot starts at that address. + * + * @returns 0 if IMAGE_F_ROM_FIXED flag is not set; + * 0 if IMAGE_F_ROM_FIXED flag is set and ROM address specified in + * header matches the slot address; + * 1 if IMF_F_ROM_FIXED flag is set but ROM address specified in header + * does not match the slot address. + */ +static bool +boot_rom_address_check(struct boot_loader_state *state) +{ + uint32_t active_slot; + const struct image_header *hdr; + uint32_t f_off; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + hdr = boot_img_hdr(state, active_slot); + f_off = boot_img_slot_off(state, active_slot); + + if (hdr->ih_flags & IMAGE_F_ROM_FIXED && hdr->ih_load_addr != f_off) { + BOOT_LOG_WRN("Image in %s slot at 0x%x has been built for offset 0x%x"\ + ", skipping", + active_slot == 0 ? "primary" : "secondary", f_off, + hdr->ih_load_addr); + + /* If there is address mismatch, the image is not bootable from this + * slot. + */ + return 1; + } + return 0; +} +#endif + +/* + * Check that there is a valid image in a slot + * + * @returns + * FIH_SUCCESS if image was successfully validated + * FIH_NO_BOOTABLE_IMAGE if no bootloable image was found + * FIH_FAILURE on any errors + */ +static fih_ret +boot_validate_slot(struct boot_loader_state *state, int slot, + struct boot_status *bs) +{ + const struct flash_area *fap; + struct image_header *hdr; + int area_id; + FIH_DECLARE(fih_rc, FIH_FAILURE); + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + FIH_RET(fih_rc); + } + + hdr = boot_img_hdr(state, slot); + if (boot_check_header_erased(state, slot) == 0 || + (hdr->ih_flags & IMAGE_F_NON_BOOTABLE)) { + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* + * This fixes an issue where an image might be erased, but a trailer + * be left behind. It can happen if the image is in the secondary slot + * and did not pass validation, in which case the whole slot is erased. + * If during the erase operation, a reset occurs, parts of the slot + * might have been erased while some did not. The concerning part is + * the trailer because it might disable a new image from being loaded + * through mcumgr; so we just get rid of the trailer here, if the header + * is erased. + */ + if (slot != BOOT_PRIMARY_SLOT) { + swap_erase_trailer_sectors(state, fap); + } +#endif + + /* No bootable image in slot; continue booting from the primary slot. */ + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY) && defined(MCUBOOT_DOWNGRADE_PREVENTION) + if (slot != BOOT_PRIMARY_SLOT) { + /* Check if version of secondary slot is sufficient */ + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) \ + && defined(CONFIG_PCD_APP) && defined(CONFIG_PCD_READ_NETCORE_APP_VERSION) + if (BOOT_CURR_IMG(state) == 1) { + rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + } else { + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + } +#else + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); +#endif + if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) { + BOOT_LOG_ERR("insufficient version in secondary slot"); + flash_area_erase(fap, 0, flash_area_get_size(fap)); + /* Image in the secondary slot does not satisfy version requirement. + * Erase the image and continue booting from the primary slot. + */ + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + } +#endif + if (!boot_is_header_valid(hdr, fap, state)) { + fih_rc = FIH_FAILURE; + } else { + BOOT_HOOK_CALL_FIH(boot_image_check_hook, FIH_BOOT_HOOK_REGULAR, + fih_rc, BOOT_CURR_IMG(state), slot); + if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) { + FIH_CALL(boot_image_check, fih_rc, state, hdr, fap, bs); + } + } + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if ((slot != BOOT_PRIMARY_SLOT) || ARE_SLOTS_EQUIVALENT()) { + flash_area_erase(fap, 0, flash_area_get_size(fap)); + /* Image is invalid, erase it to prevent further unnecessary + * attempts to validate and boot it. + */ + } + +#if !defined(__BOOTSIM__) + BOOT_LOG_ERR("Image in the %s slot is not valid!", + (slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); +#endif + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +#if MCUBOOT_IMAGE_NUMBER > 1 && !defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_VERIFY_IMG_ADDRESS) + /* Verify that the image in the secondary slot has a reset address + * located in the primary slot. This is done to avoid users incorrectly + * overwriting an application written to the incorrect slot. + * This feature is only supported by ARM platforms. + */ +#if MCUBOOT_IMAGE_NUMBER >= 3 + /* Currently the MCUboot can be configured for up to 3 image, where image number 2 is + * designated for XIP, where it is the second part of image stored in slots of image + * 0. This part of image is not bootable, as the XIP setup is done by the app in + * image 0 slot, and it does not carry the reset vector. + */ + if (area_id == FLASH_AREA_IMAGE_SECONDARY(2)) { + goto out; + } +#endif + if (area_id == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { + const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); + struct image_header *secondary_hdr = boot_img_hdr(state, slot); + uint32_t reset_value = 0; + uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value); + + rc = flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)); + if (rc != 0) { + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + + uint32_t min_addr, max_addr; + +#ifdef PM_CPUNET_APP_ADDRESS + /* The primary slot for the network core is emulated in RAM. + * Its flash_area hasn't got relevant boundaries. + * Therfore need to override its boundaries for the check. + */ + if (BOOT_CURR_IMG(state) == 1) { + min_addr = PM_CPUNET_APP_ADDRESS; + max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE; +#ifdef PM_S1_ADDRESS + } else if (BOOT_CURR_IMG(state) == 0) { + min_addr = PM_S0_ADDRESS; + max_addr = pri_fa->fa_off + pri_fa->fa_size; +#endif + } else +#endif + { + min_addr = pri_fa->fa_off; + max_addr = pri_fa->fa_off + pri_fa->fa_size; + } + + if (reset_value < min_addr || reset_value> (max_addr)) { + BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot"); + BOOT_LOG_ERR("Erasing image from secondary slot"); + + /* The vector table in the image located in the secondary + * slot does not target the primary slot. This might + * indicate that the image was loaded to the wrong slot. + * + * Erase the image and continue booting from the primary slot. + */ + flash_area_erase(fap, 0, fap->fa_size); + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + } +#endif + +out: + flash_area_close(fap); + + FIH_RET(fih_rc); +} + +#ifdef MCUBOOT_HW_ROLLBACK_PROT +/** + * Updates the stored security counter value with the image's security counter + * value which resides in the given slot, only if it's greater than the stored + * value. + * + * @param image_index Index of the image to determine which security + * counter to update. + * @param slot Slot number of the image. + * @param hdr Pointer to the image header structure of the image + * that is currently stored in the given slot. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_update_security_counter(uint8_t image_index, int slot, + struct image_header *hdr) +{ + const struct flash_area *fap = NULL; + uint32_t img_security_cnt; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(image_index, slot), + &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = bootutil_get_img_security_cnt(hdr, fap, &img_security_cnt); + if (rc != 0) { + goto done; + } + + rc = boot_nv_security_counter_update(image_index, img_security_cnt); + if (rc != 0) { + goto done; + } + +done: + flash_area_close(fap); + return rc; +} +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ +(defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP)) + +#define SEC_SLOT_VIRGIN 0 +#define SEC_SLOT_TOUCHED 1 +#define SEC_SLOT_ASSIGNED 2 + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) +/* This configuration is peculiar - the one physical secondary slot is + * mocking two logical secondary + */ +#define SEC_SLOT_PHYSICAL_CNT 1 +#else +#define SEC_SLOT_PHYSICAL_CNT MCUBOOT_IMAGE_NUMBER +#endif + +static uint8_t sec_slot_assignmnet[SEC_SLOT_PHYSICAL_CNT] = {0}; + +static inline void sec_slot_touch(struct boot_loader_state *state) +{ + uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); + + if (SEC_SLOT_VIRGIN == sec_slot_assignmnet[idx]) { + sec_slot_assignmnet[idx] = SEC_SLOT_TOUCHED; + } +} + +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ + uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); + + sec_slot_assignmnet[idx] = SEC_SLOT_ASSIGNED; +} + +/** + * Cleanu up all secondary slot which couldn't be assigned to any primary slot. + * + * This function erases content of each secondary slot which contains valid + * header but couldn't be assigned to any of supported primary images. + * + * This function is supposed to be called after boot_validated_swap_type() + * iterates over all the images in context_boot_go(). + */ +static void sec_slot_cleanup_if_unusable(void) +{ + uint8_t idx; + + for (idx = 0; idx < SEC_SLOT_PHYSICAL_CNT; idx++) { + if (SEC_SLOT_TOUCHED == sec_slot_assignmnet[idx]) { + const struct flash_area *secondary_fa; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(idx, BOOT_SECONDARY_SLOT), + &secondary_fa); + if (!rc) { + rc = flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + if (!rc) { + BOOT_LOG_ERR("Cleaned-up secondary slot of %d. image.", idx); + } + } + + if (rc) { + BOOT_LOG_ERR("Can not cleanup secondary slot of %d. image.", idx); + } + } + } +} +#else +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +} +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +} +static inline void sec_slot_cleanup_if_unusable(void) +{ +} +#endif /* defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ + defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) */ + +/** + * Determines which swap operation to perform, if any. If it is determined + * that a swap operation is required, the image in the secondary slot is checked + * for validity. If the image in the secondary slot is invalid, it is erased, + * and a swap type of "none" is indicated. + * + * @return The type of swap to perform (BOOT_SWAP_TYPE...) + */ +static int +boot_validated_swap_type(struct boot_loader_state *state, + struct boot_status *bs) +{ + int swap_type; + FIH_DECLARE(fih_rc, FIH_FAILURE); + bool upgrade_valid = false; +#if defined(PM_S1_ADDRESS) + owner_nsib[BOOT_CURR_IMG(state)] = false; +#endif + +#if defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) + const struct flash_area *secondary_fa = + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); + struct image_header *hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + uint32_t reset_addr = 0; + int rc = 0; + /* Patch needed for NCS. Since image 0 (the app) and image 1 (the other + * B1 slot S0 or S1) share the same secondary slot, we need to check + * whether the update candidate in the secondary slot is intended for + * image 0 or image 1 primary by looking at the address of the reset + * vector. Note that there are good reasons for not using img_num from + * the swap info. + */ + + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = flash_area_read(secondary_fa, hdr->ih_hdr_size + + sizeof(uint32_t), &reset_addr, + sizeof(reset_addr)); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + sec_slot_touch(state); + +#ifdef PM_S1_ADDRESS +#ifdef PM_CPUNET_B0N_ADDRESS + if(!(reset_addr >= PM_CPUNET_APP_ADDRESS && reset_addr < PM_CPUNET_APP_END_ADDRESS)) +#endif + { + const struct flash_area *primary_fa; + rc = flash_area_open(flash_area_id_from_multi_image_slot( + BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT), + &primary_fa); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Check start and end of primary slot for current image */ + if (reset_addr < primary_fa->fa_off) { +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + const struct flash_area *nsib_fa; + + /* NSIB upgrade slot */ + rc = flash_area_open((uint32_t)_image_1_primary_slot_id, + &nsib_fa); + + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Image is placed before Primary and within the NSIB slot */ + if (reset_addr > nsib_fa->fa_off + && reset_addr < (nsib_fa->fa_off + nsib_fa->fa_size)) { + /* Set primary to be NSIB upgrade slot */ + BOOT_IMG_AREA(state, 0) = nsib_fa; + owner_nsib[BOOT_CURR_IMG(state)] = true; + } +#else + return BOOT_SWAP_TYPE_NONE; + +#endif + + } else if (reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) { + /* The image in the secondary slot is not intended for any */ + return BOOT_SWAP_TYPE_NONE; + } + + if ((primary_fa->fa_off == PM_S0_ADDRESS) || (primary_fa->fa_off == PM_S1_ADDRESS)) { + owner_nsib[BOOT_CURR_IMG(state)] = true; + } + } +#endif /* PM_S1_ADDRESS */ + sec_slot_mark_assigned(state); + } + +#endif /* PM_S1_ADDRESS || CONFIG_SOC_NRF5340_CPUAPP */ + + swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state)); + if (BOOT_IS_UPGRADE(swap_type)) { + /* Boot loader wants to switch to the secondary slot. + * Ensure image is valid. + */ + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_SECONDARY_SLOT, bs); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if (FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + swap_type = BOOT_SWAP_TYPE_NONE; + } else { + swap_type = BOOT_SWAP_TYPE_FAIL; + } + } else { + upgrade_valid = true; + } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) \ + && !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) && defined(CONFIG_PCD_APP) + /* If the update is valid, and it targets the network core: perform the + * update and indicate to the caller of this function that no update is + * available + */ + if (upgrade_valid && reset_addr >= PM_CPUNET_APP_ADDRESS && + reset_addr < PM_CPUNET_APP_END_ADDRESS) { + struct image_header *hdr = (struct image_header *)secondary_fa->fa_off; + uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size; + uint32_t *net_core_fw_addr = (uint32_t *)(vtable_addr); + uint32_t fw_size = hdr->ih_img_size; + BOOT_LOG_INF("Starting network core update"); + rc = pcd_network_core_update(net_core_fw_addr, fw_size); + + if (rc != 0) { + swap_type = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_LOG_INF("Done updating network core"); +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* swap_erase_trailer_sectors is undefined if upgrade only + * method is used. There is no need to erase sectors, because + * the image cannot be reverted. + */ + rc = swap_erase_trailer_sectors(state, + secondary_fa); +#endif + swap_type = BOOT_SWAP_TYPE_NONE; + } + } +#endif /* CONFIG_SOC_NRF5340_CPUAPP && PM_CPUNET_B0N_ADDRESS && + !CONFIG_NRF53_MULTI_IMAGE_UPDATE && CONFIG_PCD_APP */ + } + + return swap_type; +} +#endif + +/** + * Erases a region of flash. + * + * @param flash_area The flash_area containing the region to erase. + * @param off The offset within the flash area to start the + * erase. + * @param sz The number of bytes to erase. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz) +{ + return flash_area_erase(fap, off, sz); +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SWAP_SAVE_ENCTLV) +/* Replacement for memset(p, 0, sizeof(*p) that does not get + * optimized out. + */ +static void like_mbedtls_zeroize(void *p, size_t n) +{ + volatile unsigned char *v = (unsigned char *)p; + + for (size_t i = 0; i < n; i++) { + v[i] = 0; + } +} +#endif + +/** + * Copies the contents of one flash region to another. You must erase the + * destination region prior to calling this function. + * + * @param flash_area_id_src The ID of the source flash area. + * @param flash_area_id_dst The ID of the destination flash area. + * @param off_src The offset within the source flash area to + * copy from. + * @param off_dst The offset within the destination flash area to + * copy to. + * @param sz The number of bytes to copy. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_copy_region(struct boot_loader_state *state, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, + uint32_t off_src, uint32_t off_dst, uint32_t sz) +{ + uint32_t bytes_copied; + int chunk_sz; + int rc; +#ifdef MCUBOOT_ENC_IMAGES + uint32_t off = off_dst; + uint32_t tlv_off; + size_t blk_off; + struct image_header *hdr; + uint16_t idx; + uint32_t blk_sz; + uint8_t image_index = BOOT_CURR_IMG(state); + bool encrypted_src; + bool encrypted_dst; + /* Assuming the secondary slot is source; note that 0 here not only + * means that primary slot is source, but also that there will be + * encryption happening, if it is 1 then there is decryption from + * secondary slot. + */ + int source_slot = 1; + /* In case of encryption enabled, we may have to do more work than + * just copy bytes */ + bool only_copy = false; +#else + (void)state; +#endif +#ifdef MCUBOOT_DECOMPRESS_IMAGES + struct image_header *hdr; +#endif + + TARGET_STATIC uint8_t buf[BUF_SZ] __attribute__((aligned(4))); + +#ifdef MCUBOOT_ENC_IMAGES + encrypted_src = (flash_area_get_id(fap_src) != FLASH_AREA_IMAGE_PRIMARY(image_index)); + encrypted_dst = (flash_area_get_id(fap_dst) != FLASH_AREA_IMAGE_PRIMARY(image_index)); + + if (encrypted_src != encrypted_dst) { + if (encrypted_dst) { + /* Need encryption, metadata from the primary slot */ + hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT); + source_slot = 0; + } else { + /* Need decryption, metadata from the secondary slot */ + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + source_slot = 1; + } + } else { + /* In case when source and targe is the same area, this means that we + * only have to copy bytes, no encryption or decryption. + */ + only_copy = true; + } +#endif + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + + if (MUST_DECOMPRESS(fap_src, BOOT_CURR_IMG(state), hdr)) { + /* Use alternative function for compressed images */ + return boot_copy_region_decompress(state, fap_src, fap_dst, off_src, off_dst, sz, buf, + BUF_SZ); + } +#endif + + bytes_copied = 0; + while (bytes_copied < sz) { + if (sz - bytes_copied > sizeof buf) { + chunk_sz = sizeof buf; + } else { + chunk_sz = sz - bytes_copied; + } + + rc = flash_area_read(fap_src, off_src + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + +#ifdef MCUBOOT_ENC_IMAGES + /* If only copy, then does not matter if header indicates need for + * encryptio/decryptio, we just copy data. */ + if (!only_copy && IS_ENCRYPTED(hdr)) { + uint32_t abs_off = off + bytes_copied; + if (abs_off < hdr->ih_hdr_size) { + /* do not decrypt header */ + if (abs_off + chunk_sz > hdr->ih_hdr_size) { + /* The lower part of the chunk contains header data */ + blk_off = 0; + blk_sz = chunk_sz - (hdr->ih_hdr_size - abs_off); + idx = hdr->ih_hdr_size - abs_off; + } else { + /* The chunk contains exclusively header data */ + blk_sz = 0; /* nothing to decrypt */ + } + } else { + idx = 0; + blk_sz = chunk_sz; + blk_off = (abs_off - hdr->ih_hdr_size) & 0xf; + } + + if (blk_sz > 0) + { + tlv_off = BOOT_TLV_OFF(hdr); + if (abs_off + chunk_sz > tlv_off) { + /* do not decrypt TLVs */ + if (abs_off >= tlv_off) { + blk_sz = 0; + } else { + blk_sz = tlv_off - abs_off; + } + } + if (source_slot == 0) { + boot_enc_encrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } else { + boot_enc_decrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } + } + } +#endif + + rc = flash_area_write(fap_dst, off_dst + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + + bytes_copied += chunk_sz; + + MCUBOOT_WATCHDOG_FEED(); + } + + return 0; +} + +/** + * Overwrite primary slot with the image contained in the secondary slot. + * If a prior copy operation was interrupted by a system reset, this function + * redos the copy. + * + * @param bs The current boot status. This function reads + * this struct to determine if it is resuming + * an interrupted swap operation. This + * function writes the updated status to this + * function on return. + * + * @return 0 on success; nonzero on failure. + */ +#if defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_BOOTSTRAP) +static int +boot_copy_image(struct boot_loader_state *state, struct boot_status *bs) +{ + size_t sect_count; + size_t sect; + int rc; + size_t size; + size_t this_size; + size_t last_sector; + const struct flash_area *fap_primary_slot; + const struct flash_area *fap_secondary_slot; + uint8_t image_index; + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + uint32_t sector; + uint32_t trailer_sz; + uint32_t off; + uint32_t sz; +#endif + + (void)bs; + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + uint32_t src_size = 0; + rc = boot_read_image_size(state, BOOT_SECONDARY_SLOT, &src_size); + assert(rc == 0); +#endif + + image_index = BOOT_CURR_IMG(state); + + BOOT_LOG_INF("Image %d upgrade secondary slot -> primary slot", image_index); + BOOT_LOG_INF("Erasing the primary slot"); + + rc = flash_area_open(flash_area_get_id(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)), + &fap_primary_slot); + assert (rc == 0); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), + &fap_secondary_slot); + assert (rc == 0); + + sect_count = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); + for (sect = 0, size = 0; sect < sect_count; sect++) { + this_size = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, sect); + rc = boot_erase_region(fap_primary_slot, size, this_size); + assert(rc == 0); + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + if ((size + this_size) >= src_size) { + size += src_size - size; + size += BOOT_WRITE_SZ(state) - (size % BOOT_WRITE_SZ(state)); + break; + } +#endif + + size += this_size; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1; + sz = 0; + do { + sz += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, sector); + off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, sector); + sector--; + } while (sz < trailer_sz); + + rc = boot_erase_region(fap_primary_slot, off, sz); + assert(rc == 0); +#endif + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(boot_img_hdr(state, BOOT_SECONDARY_SLOT))) { + rc = boot_enc_load(BOOT_CURR_ENC(state), BOOT_SECONDARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT), + fap_secondary_slot, bs); + + if (rc < 0) { + return BOOT_EBADIMAGE; + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) { + return BOOT_EBADIMAGE; + } + } +#endif + + BOOT_LOG_INF("Image %d copying the secondary slot to the primary slot: 0x%zx bytes", + image_index, size); + rc = boot_copy_region(state, fap_secondary_slot, fap_primary_slot, 0, 0, size); + if (rc != 0) { + return rc; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + rc = boot_write_magic(fap_primary_slot); + if (rc != 0) { + return rc; + } +#endif + + rc = BOOT_HOOK_CALL(boot_copy_region_post_hook, 0, BOOT_CURR_IMG(state), + BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT), size); + if (rc != 0) { + return rc; + } + +#ifdef MCUBOOT_HW_ROLLBACK_PROT + /* Update the stored security counter with the new image's security counter + * value. Both slots hold the new image at this point, but the secondary + * slot's image header must be passed since the image headers in the + * boot_data structure have not been updated yet. + */ + rc = boot_update_security_counter(BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image upgrade."); + return rc; + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +#ifndef MCUBOOT_OVERWRITE_ONLY_KEEP_BACKUP + /* + * Erases header and trailer. The trailer is erased because when a new + * image is written without a trailer as is the case when using newt, the + * trailer that was left might trigger a new upgrade. + */ + BOOT_LOG_DBG("erasing secondary header"); + rc = boot_erase_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, 0), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0)); + assert(rc == 0); +#endif + + last_sector = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) - 1; + BOOT_LOG_DBG("erasing secondary trailer"); + rc = boot_erase_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, + last_sector), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, + last_sector)); + assert(rc == 0); + + flash_area_close(fap_primary_slot); + flash_area_close(fap_secondary_slot); + + /* TODO: Perhaps verify the primary slot's signature again? */ + + return 0; +} +#endif + +#if !defined(MCUBOOT_OVERWRITE_ONLY) +/** + * Swaps the two images in flash. If a prior copy operation was interrupted + * by a system reset, this function completes that operation. + * + * @param bs The current boot status. This function reads + * this struct to determine if it is resuming + * an interrupted swap operation. This + * function writes the updated status to this + * function on return. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) +{ + struct image_header *hdr; + const struct flash_area *fap; +#ifdef MCUBOOT_ENC_IMAGES + uint8_t slot; + uint8_t i; +#endif + uint32_t size; + uint32_t copy_size; + uint8_t image_index; + int rc; + + /* FIXME: just do this if asked by user? */ + + size = copy_size = 0; + image_index = BOOT_CURR_IMG(state); + + if (boot_status_is_reset(bs)) { + /* + * No swap ever happened, so need to find the largest image which + * will be used to determine the amount of sectors to swap. + */ + hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT); + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = boot_read_image_size(state, BOOT_PRIMARY_SLOT, ©_size); + assert(rc == 0); + } + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(hdr)) { + fap = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); + rc = boot_enc_load(BOOT_CURR_ENC(state), 0, hdr, fap, bs); + assert(rc >= 0); + + if (rc == 0) { + rc = boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs); + assert(rc == 0); + } else { + rc = 0; + } + } else { + memset(bs->enckey[0], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + } +#endif + + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = boot_read_image_size(state, BOOT_SECONDARY_SLOT, &size); + assert(rc == 0); + } + +#ifdef MCUBOOT_ENC_IMAGES + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + if (IS_ENCRYPTED(hdr)) { + fap = BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); + rc = boot_enc_load(BOOT_CURR_ENC(state), 1, hdr, fap, bs); + assert(rc >= 0); + + if (rc == 0) { + rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs); + assert(rc == 0); + } else { + rc = 0; + } + } else { + memset(bs->enckey[1], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + } +#endif + + if (size > copy_size) { + copy_size = size; + } + + bs->swap_size = copy_size; + } else { + /* + * If a swap was under way, the swap_size should already be present + * in the trailer... + */ + + rc = boot_find_status(image_index, &fap); + assert(fap != NULL); + rc = boot_read_swap_size(fap, &bs->swap_size); + assert(rc == 0); + + copy_size = bs->swap_size; + +#ifdef MCUBOOT_ENC_IMAGES + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + rc = boot_read_enc_key(fap, slot, bs); + assert(rc == 0); + + for (i = 0; i < BOOT_ENC_KEY_SIZE; i++) { + if (bs->enckey[slot][i] != 0xff) { + break; + } + } + + boot_enc_init(BOOT_CURR_ENC(state), slot); + + if (i != BOOT_ENC_KEY_SIZE) { + boot_enc_set_key(BOOT_CURR_ENC(state), slot, bs); + } + } +#endif + flash_area_close(fap); + } + + swap_run(state, bs, copy_size); + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT + extern int boot_status_fails; + if (boot_status_fails > 0) { + BOOT_LOG_WRN("%d status write fails performing the swap", + boot_status_fails); + } +#endif + rc = BOOT_HOOK_CALL(boot_copy_region_post_hook, 0, BOOT_CURR_IMG(state), + BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT), size); + + return 0; +} +#endif + +/** + * Performs a clean (not aborted) image update. + * + * @param bs The current boot status. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_perform_update(struct boot_loader_state *state, struct boot_status *bs) +{ + int rc; +#ifndef MCUBOOT_OVERWRITE_ONLY + uint8_t swap_type; +#endif + + /* At this point there are no aborted swaps. */ +#if defined(MCUBOOT_OVERWRITE_ONLY) + rc = boot_copy_image(state, bs); +#elif defined(MCUBOOT_BOOTSTRAP) + /* Check if the image update was triggered by a bad image in the + * primary slot (the validity of the image in the secondary slot had + * already been checked). + */ + FIH_DECLARE(fih_rc, FIH_FAILURE); + rc = boot_check_header_erased(state, BOOT_PRIMARY_SLOT); + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, bs); + if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + rc = boot_copy_image(state, bs); + } else { + rc = boot_swap_image(state, bs); + } +#else + rc = boot_swap_image(state, bs); +#endif + assert(rc == 0); + +#ifndef MCUBOOT_OVERWRITE_ONLY + /* The following state needs image_ok be explicitly set after the + * swap was finished to avoid a new revert. + */ + swap_type = BOOT_SWAP_TYPE(state); + if (swap_type == BOOT_SWAP_TYPE_REVERT || + swap_type == BOOT_SWAP_TYPE_PERM) { + rc = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = swap_type = BOOT_SWAP_TYPE_PANIC; + } + } + +#ifdef MCUBOOT_HW_ROLLBACK_PROT + if (swap_type == BOOT_SWAP_TYPE_PERM) { + /* Update the stored security counter with the new image's security + * counter value. The primary slot holds the new image at this point, + * but the secondary slot's image header must be passed since image + * headers in the boot_data structure have not been updated yet. + * + * In case of a permanent image swap mcuboot will never attempt to + * revert the images on the next reboot. Therefore, the security + * counter must be increased right after the image upgrade. + */ + rc = boot_update_security_counter( + BOOT_CURR_IMG(state), + BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after " + "image upgrade."); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + + if (BOOT_IS_UPGRADE(swap_type)) { + rc = swap_set_copy_done(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + + return rc; +} + +/** + * Completes a previously aborted image swap. + * + * @param bs The current boot status. + * + * @return 0 on success; nonzero on failure. + */ +#if !defined(MCUBOOT_OVERWRITE_ONLY) +static int +boot_complete_partial_swap(struct boot_loader_state *state, + struct boot_status *bs) +{ + int rc; + + /* Determine the type of swap operation being resumed from the + * `swap-type` trailer field. + */ + rc = boot_swap_image(state, bs); + assert(rc == 0); + + BOOT_SWAP_TYPE(state) = bs->swap_type; + + /* The following states need image_ok be explicitly set after the + * swap was finished to avoid a new revert. + */ + if (bs->swap_type == BOOT_SWAP_TYPE_REVERT || + bs->swap_type == BOOT_SWAP_TYPE_PERM) { + rc = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } + + if (BOOT_IS_UPGRADE(bs->swap_type)) { + rc = swap_set_copy_done(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } + + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_PANIC) { + BOOT_LOG_ERR("panic!"); + assert(0); + + /* Loop forever... */ + while (1) {} + } + + return rc; +} +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + +#if (BOOT_IMAGE_NUMBER > 1) +/** + * Review the validity of previously determined swap types of other images. + * + * @param aborted_swap The current image upgrade is a + * partial/aborted swap. + */ +static void +boot_review_image_swap_types(struct boot_loader_state *state, + bool aborted_swap) +{ + /* In that case if we rebooted in the middle of an image upgrade process, we + * must review the validity of swap types, that were previously determined + * for other images. The image_ok flag had not been set before the reboot + * for any of the updated images (only the copy_done flag) and thus falsely + * the REVERT swap type has been determined for the previous images that had + * been updated before the reboot. + * + * There are two separate scenarios that we have to deal with: + * + * 1. The reboot has happened during swapping an image: + * The current image upgrade has been determined as a + * partial/aborted swap. + * 2. The reboot has happened between two separate image upgrades: + * In this scenario we must check the swap type of the current image. + * In those cases if it is NONE or REVERT we cannot certainly determine + * the fact of a reboot. In a consistent state images must move in the + * same direction or stay in place, e.g. in practice REVERT and TEST + * swap types cannot be present at the same time. If the swap type of + * the current image is either TEST, PERM or FAIL we must review the + * already determined swap types of other images and set each false + * REVERT swap types to NONE (these images had been successfully + * updated before the system rebooted between two separate image + * upgrades). + */ + + if (BOOT_CURR_IMG(state) == 0) { + /* Nothing to do */ + return; + } + + if (!aborted_swap) { + if ((BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) || + (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_REVERT)) { + /* Nothing to do */ + return; + } + } + + for (uint8_t i = 0; i < BOOT_CURR_IMG(state); i++) { + if (state->swap_type[i] == BOOT_SWAP_TYPE_REVERT) { + state->swap_type[i] = BOOT_SWAP_TYPE_NONE; + } + } +} +#endif + +/** + * Prepare image to be updated if required. + * + * Prepare image to be updated if required with completing an image swap + * operation if one was aborted and/or determining the type of the + * swap operation. In case of any error set the swap type to NONE. + * + * @param state TODO + * @param bs Pointer where the read and possibly updated + * boot status can be written to. + */ +static void +boot_prepare_image_for_update(struct boot_loader_state *state, + struct boot_status *bs) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) + int max_size; +#endif + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors(state); + if (rc != 0) { + BOOT_LOG_WRN("Failed reading sectors; BOOT_MAX_IMG_SECTORS=%d" + " - too small?", BOOT_MAX_IMG_SECTORS); + /* Unable to determine sector layout, continue with next image + * if there is one. + */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + if (rc == BOOT_EFLASH) + { + /* Only return on error from the primary image flash */ + return; + } + } + + /* Attempt to read an image header from each slot. */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + /* Continue with next image if there is one. */ + BOOT_LOG_WRN("Failed reading image headers; Image=%u", + BOOT_CURR_IMG(state)); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) + /* Fetch information on maximum sizes for later usage, if needed */ + max_size = app_max_size(state); + + if (max_size > 0) { + image_max_sizes[BOOT_CURR_IMG(state)].calculated = true; + image_max_sizes[BOOT_CURR_IMG(state)].max_size = max_size; + } +#endif + + /* If the current image's slots aren't compatible, no swap is possible. + * Just boot into primary slot. + */ + if (boot_slots_compatible(state)) { + boot_status_reset(bs); + +#ifndef MCUBOOT_OVERWRITE_ONLY + rc = swap_read_status(state, bs); + if (rc != 0) { + BOOT_LOG_WRN("Failed reading boot status; Image=%u", + BOOT_CURR_IMG(state)); + /* Continue with next image if there is one. */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } +#endif + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* + * Must re-read image headers because the boot status might + * have been updated in the previous function call. + */ + rc = boot_read_image_headers(state, !boot_status_is_reset(bs), bs); +#ifdef MCUBOOT_BOOTSTRAP + /* When bootstrapping it's OK to not have image magic in the primary slot */ + if (rc != 0 && (BOOT_CURR_IMG(state) != BOOT_PRIMARY_SLOT || + boot_check_header_erased(state, BOOT_PRIMARY_SLOT) != 0)) { +#else + if (rc != 0) { +#endif + + /* Continue with next image if there is one. */ + BOOT_LOG_WRN("Failed reading image headers; Image=%u", + BOOT_CURR_IMG(state)); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } +#endif + + /* Determine if we rebooted in the middle of an image swap + * operation. If a partial swap was detected, complete it. + */ + if (!boot_status_is_reset(bs)) { + +#if (BOOT_IMAGE_NUMBER > 1) + boot_review_image_swap_types(state, true); +#endif + +#ifdef MCUBOOT_OVERWRITE_ONLY + /* Should never arrive here, overwrite-only mode has + * no swap state. + */ + assert(0); +#else + /* Determine the type of swap operation being resumed from the + * `swap-type` trailer field. + */ + rc = boot_complete_partial_swap(state, bs); + assert(rc == 0); +#endif + /* Attempt to read an image header from each slot. Ensure that + * image headers in slots are aligned with headers in boot_data. + */ + rc = boot_read_image_headers(state, false, bs); + assert(rc == 0); + + /* Swap has finished set to NONE */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } else { + /* There was no partial swap, determine swap type. */ + if (bs->swap_type == BOOT_SWAP_TYPE_NONE) { + BOOT_SWAP_TYPE(state) = boot_validated_swap_type(state, bs); + } else { + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_SECONDARY_SLOT, bs); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_SWAP_TYPE(state) = bs->swap_type; + } + } + +#if (BOOT_IMAGE_NUMBER > 1) + boot_review_image_swap_types(state, false); +#endif + +#ifdef MCUBOOT_BOOTSTRAP + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) { + /* Header checks are done first because they are + * inexpensive. Since overwrite-only copies starting from + * offset 0, if interrupted, it might leave a valid header + * magic, so also run validation on the primary slot to be + * sure it's not OK. + */ + rc = boot_check_header_erased(state, BOOT_PRIMARY_SLOT); + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_PRIMARY_SLOT, bs); + + if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + + rc = (boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_magic == IMAGE_MAGIC) ? 1: 0; + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_SECONDARY_SLOT, bs); + + if (rc == 1 && FIH_EQ(fih_rc, FIH_SUCCESS)) { + /* Set swap type to REVERT to overwrite the primary + * slot with the image contained in secondary slot + * and to trigger the explicit setting of the + * image_ok flag. + */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_REVERT; + } + } + } +#endif + } + } else { + /* In that case if slots are not compatible. */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } +} + +/** + * Updates the security counter for the current image. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_update_hw_rollback_protection(struct boot_loader_state *state) +{ +#ifdef MCUBOOT_HW_ROLLBACK_PROT + int rc; + + /* Update the stored security counter with the active image's security + * counter value. It will only be updated if the new security counter is + * greater than the stored value. + * + * In case of a successful image swapping when the swap type is TEST the + * security counter can be increased only after a reset, when the swap + * type is NONE and the image has marked itself "OK" (the image_ok flag + * has been set). This way a "revert" can be performed when it's + * necessary. + */ + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) { + rc = boot_update_security_counter( + BOOT_CURR_IMG(state), + BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_PRIMARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image " + "validation."); + return rc; + } + } + + return 0; + +#else /* MCUBOOT_HW_ROLLBACK_PROT */ + (void) (state); + + return 0; +#endif +} + +/** + * Checks test swap downgrade prevention conditions. + * + * Function called only for swap upgrades test run. It may prevent + * swap if slot 1 image has <= version number or < security counter + * + * @param state Boot loader status information. + * + * @return 0 - image can be swapped, -1 downgrade prevention + */ +static int +check_downgrade_prevention(struct boot_loader_state *state) +{ +#if defined(MCUBOOT_DOWNGRADE_PREVENTION) && \ + (defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_SCRATCH)) + uint32_t security_counter[2]; + int rc; + +#if defined(PM_S1_ADDRESS) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + /* Downgrade prevention on S0/S1 image is managed by NSIB */ + return 0; + } +#endif + + if (MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER) { + /* If there was security no counter in slot 0, allow swap */ + rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 0).hdr), + BOOT_IMG(state, 0).area, + &security_counter[0]); + if (rc != 0) { + return 0; + } + /* If there is no security counter in slot 1, or it's lower than + * that of slot 0, prevent downgrade */ + rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 1).hdr), + BOOT_IMG(state, 1).area, + &security_counter[1]); + if (rc != 0 || security_counter[0] > security_counter[1]) { + rc = -1; + } + } + else { + rc = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + } + if (rc < 0) { + /* Image in slot 0 prevents downgrade, delete image in slot 1 */ + BOOT_LOG_INF("Image %d in slot 1 erased due to downgrade prevention", BOOT_CURR_IMG(state)); + flash_area_erase(BOOT_IMG(state, 1).area, 0, + flash_area_get_size(BOOT_IMG(state, 1).area)); + } else { + rc = 0; + } + return rc; +#else + (void)state; + return 0; +#endif +} + +fih_ret +context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + size_t slot; + struct boot_status bs; + int rc = -1; + FIH_DECLARE(fih_rc, FIH_FAILURE); + int fa_id; + int image_index; + bool has_upgrade; + volatile int fih_cnt; + +#if defined(__BOOTSIM__) + /* The array of slot sectors are defined here (as opposed to file scope) so + * that they don't get allocated for non-boot-loader apps. This is + * necessary because the gcc option "-fdata-sections" doesn't seem to have + * any effect in older gcc versions (e.g., 4.8.4). + */ + TARGET_STATIC boot_sector_t primary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + TARGET_STATIC boot_sector_t secondary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; +#if MCUBOOT_SWAP_USING_SCRATCH + TARGET_STATIC boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS]; +#endif +#endif + + has_upgrade = false; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)has_upgrade; +#endif + + /* Iterate over all the images. By the end of the loop the swap type has + * to be determined for each image and all aborted swaps have to be + * completed. + */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif +#if defined(MCUBOOT_ENC_IMAGES) && (BOOT_IMAGE_NUMBER > 1) + /* The keys used for encryption may no longer be valid (could belong to + * another images). Therefore, mark them as invalid to force their reload + * by boot_enc_load(). + */ + boot_enc_zeroize(BOOT_CURR_ENC(state)); +#endif + + image_index = BOOT_CURR_IMG(state); + +#if !defined(__BOOTSIM__) + BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = + sector_buffers.primary[image_index]; + BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors = + sector_buffers.secondary[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + state->scratch.sectors = sector_buffers.scratch; +#endif +#else + BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = + primary_slot_sectors[image_index]; + BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors = + secondary_slot_sectors[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + state->scratch.sectors = scratch_sectors; +#endif +#endif + + /* Open primary and secondary image areas for the duration + * of this call. + */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + fa_id = flash_area_id_from_multi_image_slot(image_index, slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(state, slot)); + assert(rc == 0); + + if (rc != 0) { + BOOT_LOG_ERR("Failed to open flash area ID %d (image %d slot %d): %d, " + "cannot continue", fa_id, image_index, (int8_t)slot, rc); + FIH_PANIC; + } + } +#if MCUBOOT_SWAP_USING_SCRATCH + rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, + &BOOT_SCRATCH_AREA(state)); + assert(rc == 0); + + if (rc != 0) { + BOOT_LOG_ERR("Failed to open scratch flash area: %d, cannot continue", rc); + FIH_PANIC; + } +#endif + + /* Determine swap type and complete swap if it has been aborted. */ + boot_prepare_image_for_update(state, &bs); + + if (BOOT_IS_UPGRADE(BOOT_SWAP_TYPE(state))) { + has_upgrade = true; + } + } + + /* cleanup secondary slots which were recognized unusable*/ + sec_slot_cleanup_if_unusable(); + +#if (BOOT_IMAGE_NUMBER > 1) + if (has_upgrade) { + /* Iterate over all the images and verify whether the image dependencies + * are all satisfied and update swap type if necessary. + */ + rc = boot_verify_dependencies(state); + if (rc != 0) { + /* + * It was impossible to upgrade because the expected dependency version + * was not available. Here we already changed the swap_type so that + * instead of asserting the bootloader, we continue and no upgrade is + * performed. + */ + rc = 0; + } + } +#endif + + /* Trigger status change callback with upgrading status */ + mcuboot_status_change(MCUBOOT_STATUS_UPGRADING); + + /* Iterate over all the images. At this point there are no aborted swaps + * and the swap types are determined for each image. By the end of the loop + * all required update operations will have been finished. + */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if (BOOT_IMAGE_NUMBER > 1) + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } + +#ifdef MCUBOOT_ENC_IMAGES + /* The keys used for encryption may no longer be valid (could belong to + * another images). Therefore, mark them as invalid to force their reload + * by boot_enc_load(). + */ + boot_enc_zeroize(BOOT_CURR_ENC(state)); +#endif /* MCUBOOT_ENC_IMAGES */ + + /* Indicate that swap is not aborted */ + boot_status_reset(&bs); +#endif /* (BOOT_IMAGE_NUMBER > 1) */ + + /* Set the previously determined swap type */ + bs.swap_type = BOOT_SWAP_TYPE(state); + + switch (BOOT_SWAP_TYPE(state)) { + case BOOT_SWAP_TYPE_NONE: + break; + + case BOOT_SWAP_TYPE_TEST: + /* fallthrough */ + case BOOT_SWAP_TYPE_PERM: + if (check_downgrade_prevention(state) != 0) { + /* Downgrade prevented */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + break; + } + /* fallthrough */ + case BOOT_SWAP_TYPE_REVERT: + rc = BOOT_HOOK_CALL(boot_perform_update_hook, BOOT_HOOK_REGULAR, + BOOT_CURR_IMG(state), &(BOOT_IMG(state, 1).hdr), + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT)); + if (rc == BOOT_HOOK_REGULAR) + { + rc = boot_perform_update(state, &bs); + } + assert(rc == 0); +#if defined(PM_S1_ADDRESS) && defined(CONFIG_REBOOT) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + sys_reboot(SYS_REBOOT_COLD); + + } +#endif + break; + + case BOOT_SWAP_TYPE_FAIL: + /* The image in secondary slot was invalid and is now erased. Ensure + * we don't try to boot into it again on the next reboot. Do this by + * pretending we just reverted back to primary slot. + */ +#ifndef MCUBOOT_OVERWRITE_ONLY + /* image_ok needs to be explicitly set to avoid a new revert. */ + rc = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + break; + + default: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_PANIC) { + BOOT_LOG_ERR("panic!"); + assert(0); + + /* Loop forever... */ + FIH_PANIC; + } + } + + /* Iterate over all the images. At this point all required update operations + * have finished. By the end of the loop each image in the primary slot will + * have been re-validated. + */ + FIH_SET(fih_cnt, 0); + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + /* Hardenned to prevent from skipping check of a given image, + * tmp_img_mask is declared volatile + */ + volatile bool tmp_img_mask; + FIH_SET(tmp_img_mask, state->img_mask[BOOT_CURR_IMG(state)]); + if (FIH_EQ(tmp_img_mask, true)) { + ++fih_cnt; + continue; + } +#endif + if (BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_NONE) { + /* Attempt to read an image header from each slot. Ensure that image + * headers in slots are aligned with headers in boot_data. + * Note: Quite complicated internal logic of boot_read_image_headers + * uses boot state, the last parm, to figure out in which slot which + * header is located; when boot state is not provided, then it + * is assumed that headers are at proper slots (we are not in + * the middle of moving images, etc). + */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + /* Since headers were reloaded, it can be assumed we just performed + * a swap or overwrite. Now the header info that should be used to + * provide the data for the bootstrap, which previously was at + * secondary slot, was updated to primary slot. + */ + } + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. Image 1 primary is the currently + * executing MCUBoot image, and is therefore already validated by NSIB and + * does not need to also be validated by MCUBoot. + */ + bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1; + if (!image_validated_by_nsib) +#endif + { + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL); + /* Check for all possible values is redundant in normal operation it + * is meant to prevent FI attack. + */ + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) || + FIH_EQ(fih_rc, FIH_FAILURE) || + FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } +#else + /* Even if we're not re-validating the primary slot, we could be booting + * onto an empty flash chip. At least do a basic sanity check that + * the magic number on the image is OK. + */ + if (BOOT_IMG(state, BOOT_PRIMARY_SLOT).hdr.ih_magic != IMAGE_MAGIC) { + BOOT_LOG_ERR("bad image magic 0x%lx; Image=%u", (unsigned long) + BOOT_IMG(state, BOOT_PRIMARY_SLOT).hdr.ih_magic, + BOOT_CURR_IMG(state)); + rc = BOOT_EBADIMAGE; + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ + +#ifdef PM_S1_ADDRESS + if (!image_validated_by_nsib) +#endif + { + rc = boot_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } + + rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + ++fih_cnt; + } + /* + * fih_cnt should be equal to BOOT_IMAGE_NUMBER now. + * If this is not the case, at least one iteration of the loop + * has been skipped. + */ + if(FIH_NOT_EQ(fih_cnt, BOOT_IMAGE_NUMBER)) { + FIH_PANIC; + } + + fill_rsp(state, rsp); + + fih_rc = FIH_SUCCESS; +out: + /* + * Since the boot_status struct stores plaintext encryption keys, reset + * them here to avoid the possibility of jumping into an image that could + * easily recover them. + */ +#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SWAP_SAVE_ENCTLV) + like_mbedtls_zeroize(&bs, sizeof(bs)); +#else + memset(&bs, 0, sizeof(struct boot_status)); +#endif + + close_all_flash_areas(state); + FIH_RET(fih_rc); +} + +fih_ret +split_go(int loader_slot, int split_slot, void **entry) +{ + boot_sector_t *sectors; + uintptr_t entry_val; + int loader_flash_id; + int split_flash_id; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + sectors = malloc(BOOT_MAX_IMG_SECTORS * 2 * sizeof *sectors); + if (sectors == NULL) { + FIH_RET(FIH_FAILURE); + } + BOOT_IMG(&boot_data, loader_slot).sectors = sectors + 0; + BOOT_IMG(&boot_data, split_slot).sectors = sectors + BOOT_MAX_IMG_SECTORS; + + loader_flash_id = flash_area_id_from_image_slot(loader_slot); + rc = flash_area_open(loader_flash_id, + &BOOT_IMG_AREA(&boot_data, loader_slot)); + assert(rc == 0); + split_flash_id = flash_area_id_from_image_slot(split_slot); + rc = flash_area_open(split_flash_id, + &BOOT_IMG_AREA(&boot_data, split_slot)); + assert(rc == 0); + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors(&boot_data); + if (rc != 0) { + rc = SPLIT_GO_ERR; + goto done; + } + + rc = boot_read_image_headers(&boot_data, true, NULL); + if (rc != 0) { + goto done; + } + + /* Don't check the bootable image flag because we could really call a + * bootable or non-bootable image. Just validate that the image check + * passes which is distinct from the normal check. + */ + FIH_CALL(split_image_check, fih_rc, + boot_img_hdr(&boot_data, split_slot), + BOOT_IMG_AREA(&boot_data, split_slot), + boot_img_hdr(&boot_data, loader_slot), + BOOT_IMG_AREA(&boot_data, loader_slot)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto done; + } + + entry_val = boot_img_slot_off(&boot_data, split_slot) + + boot_img_hdr(&boot_data, split_slot)->ih_hdr_size; + *entry = (void *) entry_val; + rc = SPLIT_GO_OK; + +done: + flash_area_close(BOOT_IMG_AREA(&boot_data, split_slot)); + flash_area_close(BOOT_IMG_AREA(&boot_data, loader_slot)); + free(sectors); + + if (rc) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} + +#else /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +/** + * Opens all flash areas and checks which contain an image with a valid header. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_get_slot_usage(struct boot_loader_state *state) +{ + uint32_t slot; + int fa_id; + int rc; + struct image_header *hdr = NULL; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + /* Open all the slots */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + fa_id = flash_area_id_from_multi_image_slot( + BOOT_CURR_IMG(state), slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(state, slot)); + assert(rc == 0); + } + + /* Attempt to read an image header from each slot. */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + BOOT_LOG_WRN("Failed reading image headers."); + return rc; + } + + /* Check headers in all slots */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + hdr = boot_img_hdr(state, slot); + + if (boot_is_header_valid(hdr, BOOT_IMG_AREA(state, slot), state)) { + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = true; + BOOT_LOG_IMAGE_INFO(slot, hdr); + } else { + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false; + BOOT_LOG_INF("Image %d %s slot: Image not found", + BOOT_CURR_IMG(state), + (slot == BOOT_PRIMARY_SLOT) + ? "Primary" : "Secondary"); + } + } + + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + } + + return 0; +} + +/** + * Finds the slot containing the image with the highest version number for the + * current image. + * + * @param state Boot loader status information. + * + * @return NO_ACTIVE_SLOT if no available slot found, number of + * the found slot otherwise. + */ +static uint32_t +find_slot_with_highest_version(struct boot_loader_state *state) +{ + uint32_t slot; + uint32_t candidate_slot = NO_ACTIVE_SLOT; + int rc; + + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + if (state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot]) { + if (candidate_slot == NO_ACTIVE_SLOT) { + candidate_slot = slot; + } else { + rc = boot_version_cmp( + &boot_img_hdr(state, slot)->ih_ver, + &boot_img_hdr(state, candidate_slot)->ih_ver); + if (rc == 1) { + /* The version of the image being examined is greater than + * the version of the current candidate. + */ + candidate_slot = slot; + } + } + } + } + + return candidate_slot; +} + +#ifdef MCUBOOT_HAVE_LOGGING +/** + * Prints the state of the loaded images. + * + * @param state Boot loader status information. + */ +static void +print_loaded_images(struct boot_loader_state *state) +{ + uint32_t active_slot; + + (void)state; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + BOOT_LOG_INF("Image %d loaded from the %s slot", + BOOT_CURR_IMG(state), + (active_slot == BOOT_PRIMARY_SLOT) ? + "primary" : "secondary"); + } +} +#endif + +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) +/** + * Checks whether the active slot of the current image was previously selected + * to run. Erases the image if it was selected but its execution failed, + * otherwise marks it as selected if it has not been before. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_select_or_erase(struct boot_loader_state *state) +{ + const struct flash_area *fap; + int fa_id; + int rc; + uint32_t active_slot; + struct boot_swap_state* active_swap_state; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot); + rc = flash_area_open(fa_id, &fap); + assert(rc == 0); + + active_swap_state = &(state->slot_usage[BOOT_CURR_IMG(state)].swap_state); + + memset(active_swap_state, 0, sizeof(struct boot_swap_state)); + rc = boot_read_swap_state(fap, active_swap_state); + assert(rc == 0); + + if (active_swap_state->magic != BOOT_MAGIC_GOOD || + (active_swap_state->copy_done == BOOT_FLAG_SET && + active_swap_state->image_ok != BOOT_FLAG_SET)) { + /* + * A reboot happened without the image being confirmed at + * runtime or its trailer is corrupted/invalid. Erase the image + * to prevent it from being selected again on the next reboot. + */ + BOOT_LOG_DBG("Erasing faulty image in the %s slot.", + (active_slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); + rc = flash_area_erase(fap, 0, flash_area_get_size(fap)); + assert(rc == 0); + + flash_area_close(fap); + rc = -1; + } else { + if (active_swap_state->copy_done != BOOT_FLAG_SET) { + if (active_swap_state->copy_done == BOOT_FLAG_BAD) { + BOOT_LOG_DBG("The copy_done flag had an unexpected value. Its " + "value was neither 'set' nor 'unset', but 'bad'."); + } + /* + * Set the copy_done flag, indicating that the image has been + * selected to boot. It can be set in advance, before even + * validating the image, because in case the validation fails, the + * entire image slot will be erased (including the trailer). + */ + rc = boot_write_copy_done(fap); + if (rc != 0) { + BOOT_LOG_WRN("Failed to set copy_done flag of the image in " + "the %s slot.", (active_slot == BOOT_PRIMARY_SLOT) ? + "primary" : "secondary"); + rc = 0; + } + } + flash_area_close(fap); + } + + return rc; +} +#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */ + +#ifdef MCUBOOT_RAM_LOAD + +#ifndef MULTIPLE_EXECUTABLE_RAM_REGIONS +#if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE) +#error "Platform MUST define executable RAM bounds in case of RAM_LOAD" +#endif +#endif + +/** + * Verifies that the active slot of the current image can be loaded within the + * predefined bounds that are allowed to be used by executable images. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_ram_load_address(struct boot_loader_state *state) +{ + uint32_t img_dst; + uint32_t img_sz; + uint32_t img_end_addr; + uint32_t exec_ram_start; + uint32_t exec_ram_size; + + (void)state; + +#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS + int rc; + + rc = boot_get_image_exec_ram_info(BOOT_CURR_IMG(state), &exec_ram_start, + &exec_ram_size); + if (rc != 0) { + return BOOT_EBADSTATUS; + } +#else + exec_ram_start = IMAGE_EXECUTABLE_RAM_START; + exec_ram_size = IMAGE_EXECUTABLE_RAM_SIZE; +#endif + + img_dst = state->slot_usage[BOOT_CURR_IMG(state)].img_dst; + img_sz = state->slot_usage[BOOT_CURR_IMG(state)].img_sz; + + if (img_dst < exec_ram_start) { + return BOOT_EBADIMAGE; + } + + if (!boot_u32_safe_add(&img_end_addr, img_dst, img_sz)) { + return BOOT_EBADIMAGE; + } + + if (img_end_addr > (exec_ram_start + exec_ram_size)) { + return BOOT_EBADIMAGE; + } + + return 0; +} + +#ifdef MCUBOOT_ENC_IMAGES + +/** + * Copies and decrypts an image from a slot in the flash to an SRAM address. + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be copied to SRAM. + * @param hdr The image header. + * @param src_sz Size of the image. + * @param img_dst Pointer to the address at which the image needs to be + * copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, + uint32_t slot, struct image_header *hdr, + uint32_t src_sz, uint32_t img_dst) +{ + /* The flow for the decryption and copy of the image is as follows : + * 1. The whole image is copied to the RAM (header + payload + TLV). + * 2. The encryption key is loaded from the TLV in flash. + * 3. The image is then decrypted chunk by chunk in RAM (1 chunk + * is 1024 bytes). Only the payload section is decrypted. + * 4. The image is authenticated in RAM. + */ + const struct flash_area *fap_src = NULL; + struct boot_status bs; + uint32_t blk_off; + uint32_t tlv_off; + uint32_t blk_sz; + uint32_t bytes_copied = hdr->ih_hdr_size; + uint32_t chunk_sz; + uint32_t max_sz = 1024; + uint16_t idx; + uint8_t * cur_dst; + int area_id; + int rc; + uint8_t * ram_dst = (void *)(IMAGE_RAM_BASE + img_dst); + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap_src); + if (rc != 0){ + return BOOT_EFLASH; + } + + tlv_off = BOOT_TLV_OFF(hdr); + + /* Copying the whole image in RAM */ + rc = flash_area_read(fap_src, 0, ram_dst, src_sz); + if (rc != 0) { + goto done; + } + + rc = boot_enc_load(BOOT_CURR_ENC(state), slot, hdr, fap_src, &bs); + if (rc < 0) { + goto done; + } + + /* if rc > 0 then the key has already been loaded */ + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), slot, &bs)) { + goto done; + } + + /* Starting at the end of the header as the header section is not encrypted */ + while (bytes_copied < tlv_off) { /* TLV section copied previously */ + if (src_sz - bytes_copied > max_sz) { + chunk_sz = max_sz; + } else { + chunk_sz = src_sz - bytes_copied; + } + + cur_dst = ram_dst + bytes_copied; + blk_sz = chunk_sz; + idx = 0; + blk_off = ((bytes_copied) - hdr->ih_hdr_size) & 0xf; + if (bytes_copied + chunk_sz > tlv_off) { + /* Going over TLV section + * Part of the chunk is encrypted payload */ + blk_sz = tlv_off - (bytes_copied); + } + boot_enc_decrypt(BOOT_CURR_ENC(state), slot, + (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, cur_dst); + bytes_copied += chunk_sz; + } + rc = 0; + +done: + flash_area_close(fap_src); + + return rc; +} + +#endif /* MCUBOOT_ENC_IMAGES */ +/** + * Copies a slot of the current image into SRAM. + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be copied to SRAM. + * @param img_dst The address at which the image needs to be copied to + * SRAM. + * @param img_sz The size of the image that needs to be copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_copy_image_to_sram(struct boot_loader_state *state, int slot, + uint32_t img_dst, uint32_t img_sz) +{ + int rc; + const struct flash_area *fap_src = NULL; + int area_id; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + + rc = flash_area_open(area_id, &fap_src); + if (rc != 0) { + return BOOT_EFLASH; + } + + /* Direct copy from flash to its new location in SRAM. */ + rc = flash_area_read(fap_src, 0, (void *)(IMAGE_RAM_BASE + img_dst), img_sz); + if (rc != 0) { + BOOT_LOG_INF("Error whilst copying image %d from Flash to SRAM: %d", + BOOT_CURR_IMG(state), rc); + } + + flash_area_close(fap_src); + + return rc; +} + +#if (BOOT_IMAGE_NUMBER > 1) +/** + * Checks if two memory regions (A and B) are overlap or not. + * + * @param start_a Start of the A region. + * @param end_a End of the A region. + * @param start_b Start of the B region. + * @param end_b End of the B region. + * + * @return true if there is overlap; false otherwise. + */ +static bool +do_regions_overlap(uint32_t start_a, uint32_t end_a, + uint32_t start_b, uint32_t end_b) +{ + if (start_b > end_a) { + return false; + } else if (start_b >= start_a) { + return true; + } else if (end_b > start_a) { + return true; + } + + return false; +} + +/** + * Checks if the image we want to load to memory overlap with an already + * ramloaded image. + * + * @param state Boot loader status information. + * + * @return 0 if there is no overlap; nonzero otherwise. + */ +static int +boot_check_ram_load_overlapping(struct boot_loader_state *state) +{ + uint32_t i; + + uint32_t start_a; + uint32_t end_a; + uint32_t start_b; + uint32_t end_b; + uint32_t image_id_to_check = BOOT_CURR_IMG(state); + + start_a = state->slot_usage[image_id_to_check].img_dst; + /* Safe to add here, values are already verified in + * boot_verify_ram_load_address() */ + end_a = start_a + state->slot_usage[image_id_to_check].img_sz; + + for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { + if (state->slot_usage[i].active_slot == NO_ACTIVE_SLOT + || i == image_id_to_check) { + continue; + } + + start_b = state->slot_usage[i].img_dst; + /* Safe to add here, values are already verified in + * boot_verify_ram_load_address() */ + end_b = start_b + state->slot_usage[i].img_sz; + + if (do_regions_overlap(start_a, end_a, start_b, end_b)) { + return -1; + } + } + + return 0; +} +#endif + +/** + * Loads the active slot of the current image into SRAM. The load address and + * image size is extracted from the image header. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_load_image_to_sram(struct boot_loader_state *state) +{ + uint32_t active_slot; + struct image_header *hdr = NULL; + uint32_t img_dst; + uint32_t img_sz; + int rc; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + hdr = boot_img_hdr(state, active_slot); + + if (hdr->ih_flags & IMAGE_F_RAM_LOAD) { + + img_dst = hdr->ih_load_addr; + + rc = boot_read_image_size(state, active_slot, &img_sz); + if (rc != 0) { + return rc; + } + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = img_dst; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = img_sz; + + rc = boot_verify_ram_load_address(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM load address 0x%x is invalid.", BOOT_CURR_IMG(state), img_dst); + return rc; + } + +#if (BOOT_IMAGE_NUMBER > 1) + rc = boot_check_ram_load_overlapping(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to address 0x%x would overlap with\ + another image.", BOOT_CURR_IMG(state), img_dst); + return rc; + } +#endif +#ifdef MCUBOOT_ENC_IMAGES + /* decrypt image if encrypted and copy it to RAM */ + if (IS_ENCRYPTED(hdr)) { + rc = boot_decrypt_and_copy_image_to_sram(state, active_slot, hdr, img_sz, img_dst); + } else { + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); + } +#else + /* Copy image to the load address from where it currently resides in + * flash. + */ + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); +#endif + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is failed.", BOOT_CURR_IMG(state), img_dst); + } else { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is succeeded.", BOOT_CURR_IMG(state), img_dst); + } + } else { + /* Only images that support IMAGE_F_RAM_LOAD are allowed if + * MCUBOOT_RAM_LOAD is set. + */ + rc = BOOT_EBADIMAGE; + } + + if (rc != 0) { + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; + } + + return rc; +} + +/** + * Removes an image from SRAM, by overwriting it with zeros. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static inline int +boot_remove_image_from_sram(struct boot_loader_state *state) +{ + (void)state; + + BOOT_LOG_INF("Removing image %d from SRAM at address 0x%x", + BOOT_CURR_IMG(state), + state->slot_usage[BOOT_CURR_IMG(state)].img_dst); + + memset((void*)(IMAGE_RAM_BASE + state->slot_usage[BOOT_CURR_IMG(state)].img_dst), + 0, state->slot_usage[BOOT_CURR_IMG(state)].img_sz); + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; + + return 0; +} + +/** + * Removes an image from flash by erasing the corresponding flash area + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be erased. + * + * @return 0 on success; nonzero on failure. + */ +static inline int +boot_remove_image_from_flash(struct boot_loader_state *state, uint32_t slot) +{ + int area_id; + int rc; + const struct flash_area *fap; + + (void)state; + + BOOT_LOG_INF("Removing image %d slot %d from flash", BOOT_CURR_IMG(state), + slot); + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc == 0) { + flash_area_erase(fap, 0, flash_area_get_size(fap)); + flash_area_close(fap); + } + + return rc; +} +#endif /* MCUBOOT_RAM_LOAD */ + + +/** + * Tries to load a slot for all the images with validation. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +fih_ret +boot_load_and_validate_images(struct boot_loader_state *state) +{ + uint32_t active_slot; + int rc; + fih_ret fih_rc; + + /* Go over all the images and try to load one */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { + /* All slots tried until a valid image found. Breaking from this loop + * means that a valid image found or already loaded. If no slot is + * found the function returns with error code. */ + while (true) { + /* Go over all the slots and try to load one */ + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + if (active_slot != NO_ACTIVE_SLOT){ + /* A slot is already active, go to next image. */ + break; + } + + active_slot = find_slot_with_highest_version(state); + if (active_slot == NO_ACTIVE_SLOT) { + BOOT_LOG_INF("No slot to load for image %d", + BOOT_CURR_IMG(state)); + FIH_RET(FIH_FAILURE); + } + + /* Save the number of the active slot. */ + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot; + +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + +#ifdef MCUBOOT_DIRECT_XIP + rc = boot_rom_address_check(state); + if (rc != 0) { + /* The image is placed in an unsuitable slot. */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } + +#ifdef MCUBOOT_DIRECT_XIP_REVERT + rc = boot_select_or_erase(state); + if (rc != 0) { + /* The selected image slot has been erased. */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } +#endif /* MCUBOOT_DIRECT_XIP_REVERT */ +#endif /* MCUBOOT_DIRECT_XIP */ + +#ifdef MCUBOOT_RAM_LOAD + /* Image is first loaded to RAM and authenticated there in order to + * prevent TOCTOU attack during image copy. This could be applied + * when loading images from external (untrusted) flash to internal + * (trusted) RAM and image is authenticated before copying. + */ + rc = boot_load_image_to_sram(state); + if (rc != 0 ) { + /* Image cannot be ramloaded. */ + boot_remove_image_from_flash(state, active_slot); + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } +#endif /* MCUBOOT_RAM_LOAD */ + + FIH_CALL(boot_validate_slot, fih_rc, state, active_slot, NULL); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + /* Image is invalid. */ +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(state); +#endif /* MCUBOOT_RAM_LOAD */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } + + /* Valid image loaded from a slot, go to next image. */ + break; + } + } + + FIH_RET(FIH_SUCCESS); +} + +/** + * Updates the security counter for the current image. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_update_hw_rollback_protection(struct boot_loader_state *state) +{ +#ifdef MCUBOOT_HW_ROLLBACK_PROT + int rc; + + /* Update the stored security counter with the newer (active) image's + * security counter value. + */ +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) + /* When the 'revert' mechanism is enabled in direct-xip mode, the + * security counter can be increased only after reboot, if the image + * has been confirmed at runtime (the image_ok flag has been set). + * This way a 'revert' can be performed when it's necessary. + */ + if (state->slot_usage[BOOT_CURR_IMG(state)].swap_state.image_ok == BOOT_FLAG_SET) { +#endif + rc = boot_update_security_counter(BOOT_CURR_IMG(state), + state->slot_usage[BOOT_CURR_IMG(state)].active_slot, + boot_img_hdr(state, state->slot_usage[BOOT_CURR_IMG(state)].active_slot)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image %d validation.", BOOT_CURR_IMG(state)); + return rc; + } +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) + } +#endif + + return 0; + +#else /* MCUBOOT_HW_ROLLBACK_PROT */ + (void) (state); + return 0; +#endif +} + +fih_ret +context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + rc = boot_get_slot_usage(state); + if (rc != 0) { + goto out; + } + +#if (BOOT_IMAGE_NUMBER > 1) + while (true) { +#endif + FIH_CALL(boot_load_and_validate_images, fih_rc, state); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + +#if (BOOT_IMAGE_NUMBER > 1) + rc = boot_verify_dependencies(state); + if (rc != 0) { + /* Dependency check failed for an image, it has been removed from + * SRAM in case of MCUBOOT_RAM_LOAD strategy, and set to + * unavailable. Try to load an image from another slot. + */ + continue; + } + /* Dependency check was successful. */ + break; + } +#endif + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + rc = boot_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + rc = boot_add_shared_data(state, (uint8_t)state->slot_usage[BOOT_CURR_IMG(state)].active_slot); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } + + /* All image loaded successfully. */ +#ifdef MCUBOOT_HAVE_LOGGING + print_loaded_images(state); +#endif + + fill_rsp(state, rsp); + +out: + close_all_flash_areas(state); + + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +/** + * Prepares the booting process. This function moves images around in flash as + * appropriate, and tells you what address to boot from. + * + * @param rsp On success, indicates how booting should occur. + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + + boot_state_clear(NULL); + + FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + FIH_RET(fih_rc); +} + +/** + * Prepares the booting process, considering only a single image. This function + * moves images around in flash as appropriate, and tells you what address to + * boot from. + * + * @param rsp On success, indicates how booting should occur. + * + * @param image_id The image ID to prepare the boot process for. + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go_for_image_id(struct boot_rsp *rsp, uint32_t image_id) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + + if (image_id >= BOOT_IMAGE_NUMBER) { + FIH_RET(FIH_FAILURE); + } + +#if BOOT_IMAGE_NUMBER > 1 + memset(&boot_data.img_mask, 1, BOOT_IMAGE_NUMBER); + boot_data.img_mask[image_id] = 0; +#endif + + FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + FIH_RET(fih_rc); +} + +/** + * Clears the boot state, so that previous operations have no effect on new + * ones. + * + * @param state The state that should be cleared. If the value + * is NULL, the default bootloader state will be + * cleared. + */ +void boot_state_clear(struct boot_loader_state *state) +{ + if (state != NULL) { + memset(state, 0, sizeof(struct boot_loader_state)); + } else { + memset(&boot_data, 0, sizeof(struct boot_loader_state)); + } +} + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +/** + * Reads image data to find out the maximum application sizes. Only needs to + * be called in serial recovery mode, as the state information is unpopulated + * at that time + */ +static void boot_fetch_slot_state_sizes(void) +{ + size_t slot; + int rc = -1; + int fa_id; + int image_index; + + IMAGES_ITER(BOOT_CURR_IMG(&boot_data)) { + int max_size = 0; + + image_index = BOOT_CURR_IMG(&boot_data); + + BOOT_IMG(&boot_data, BOOT_PRIMARY_SLOT).sectors = + sector_buffers.primary[image_index]; + BOOT_IMG(&boot_data, BOOT_SECONDARY_SLOT).sectors = + sector_buffers.secondary[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + boot_data.scratch.sectors = sector_buffers.scratch; +#endif + + /* Open primary and secondary image areas for the duration + * of this call. + */ + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + fa_id = flash_area_id_from_multi_image_slot(image_index, slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(&boot_data, slot)); + assert(rc == 0); + + if (rc != 0) { + goto finish; + } + } + +#if MCUBOOT_SWAP_USING_SCRATCH + rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, + &BOOT_SCRATCH_AREA(&boot_data)); + assert(rc == 0); + + if (rc != 0) { + goto finish; + } +#endif + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors_recovery(&boot_data); + + if (rc == 0) { + max_size = app_max_size(&boot_data); + + if (max_size > 0) { + image_max_sizes[image_index].calculated = true; + image_max_sizes[image_index].max_size = max_size; + } + } + } + +finish: + close_all_flash_areas(&boot_data); + memset(&boot_data, 0x00, sizeof(boot_data)); +} +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +/** + * Fetches the maximum allowed size of the image + */ +const struct image_max_size *boot_get_max_app_size(void) +{ +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) + uint8_t i = 0; + + while (i < BOOT_IMAGE_NUMBER) { + if (image_max_sizes[i].calculated == true) { + break; + } + + ++i; + } + + if (i == BOOT_IMAGE_NUMBER) { + /* Information not available, need to fetch it */ + boot_fetch_slot_state_sizes(); + } +#endif + + return image_max_sizes; +} +#endif diff --git a/bootloader/mcuboot/boot/bootutil/src/swap_misc.c b/bootloader/mcuboot/boot/bootutil/src/swap_misc.c new file mode 100644 index 0000000..733a397 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/swap_misc.c @@ -0,0 +1,234 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2019 JUUL Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) +int +swap_erase_trailer_sectors(const struct boot_loader_state *state, + const struct flash_area *fap) +{ + uint8_t slot; + uint32_t sector; + uint32_t trailer_sz; + uint32_t total_sz; + uint32_t off; + uint32_t sz; + int fa_id_primary; + int fa_id_secondary; + uint8_t image_index; + int rc; + + BOOT_LOG_DBG("erasing trailer; fa_id=%d", flash_area_get_id(fap)); + + image_index = BOOT_CURR_IMG(state); + fa_id_primary = flash_area_id_from_multi_image_slot(image_index, + BOOT_PRIMARY_SLOT); + fa_id_secondary = flash_area_id_from_multi_image_slot(image_index, + BOOT_SECONDARY_SLOT); + + if (flash_area_get_id(fap) == fa_id_primary) { + slot = BOOT_PRIMARY_SLOT; + } else if (flash_area_get_id(fap) == fa_id_secondary) { + slot = BOOT_SECONDARY_SLOT; + } else { + return BOOT_EFLASH; + } + + /* delete starting from last sector and moving to beginning */ + sector = boot_img_num_sectors(state, slot) - 1; + trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + total_sz = 0; + do { + sz = boot_img_sector_size(state, slot, sector); + off = boot_img_sector_off(state, slot, sector); + rc = boot_erase_region(fap, off, sz); + assert(rc == 0); + + sector--; + total_sz += sz; + } while (total_sz < trailer_sz); + + return rc; +} + +int +swap_status_init(const struct boot_loader_state *state, + const struct flash_area *fap, + const struct boot_status *bs) +{ + struct boot_swap_state swap_state; + uint8_t image_index; + int rc; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + image_index = BOOT_CURR_IMG(state); + + BOOT_LOG_DBG("initializing status; fa_id=%d", flash_area_get_id(fap)); + + rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index), + &swap_state); + assert(rc == 0); + + if (bs->swap_type != BOOT_SWAP_TYPE_NONE) { + rc = boot_write_swap_info(fap, bs->swap_type, image_index); + assert(rc == 0); + } + + if (swap_state.image_ok == BOOT_FLAG_SET) { + rc = boot_write_image_ok(fap); + assert(rc == 0); + } + + rc = boot_write_swap_size(fap, bs->swap_size); + assert(rc == 0); + +#ifdef MCUBOOT_ENC_IMAGES + rc = boot_write_enc_key(fap, 0, bs); + assert(rc == 0); + + rc = boot_write_enc_key(fap, 1, bs); + assert(rc == 0); +#endif + + rc = boot_write_magic(fap); + assert(rc == 0); + + return 0; +} + +int +swap_read_status(struct boot_loader_state *state, struct boot_status *bs) +{ + const struct flash_area *fap; + uint32_t off; + uint8_t swap_info; + int area_id; + int rc; + + bs->source = swap_status_source(state); + switch (bs->source) { + case BOOT_STATUS_SOURCE_NONE: + return 0; + +#if MCUBOOT_SWAP_USING_SCRATCH + case BOOT_STATUS_SOURCE_SCRATCH: + area_id = FLASH_AREA_IMAGE_SCRATCH; + break; +#endif + + case BOOT_STATUS_SOURCE_PRIMARY_SLOT: + area_id = FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state)); + break; + + default: + assert(0); + return BOOT_EBADARGS; + } + + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = swap_read_status_bytes(fap, state, bs); + if (rc == 0) { + off = boot_swap_info_off(fap); + rc = flash_area_read(fap, off, &swap_info, sizeof swap_info); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + if (bootutil_buffer_is_erased(fap, &swap_info, sizeof swap_info)) { + BOOT_SET_SWAP_INFO(swap_info, 0, BOOT_SWAP_TYPE_NONE); + rc = 0; + } + + /* Extract the swap type info */ + bs->swap_type = BOOT_GET_SWAP_TYPE(swap_info); + } + +done: + flash_area_close(fap); + + return rc; +} + +int +swap_set_copy_done(uint8_t image_index) +{ + const struct flash_area *fap; + int rc; + + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), + &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_write_copy_done(fap); + flash_area_close(fap); + return rc; +} + +int +swap_set_image_ok(uint8_t image_index) +{ + const struct flash_area *fap; + struct boot_swap_state state; + int rc; + + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), + &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_read_swap_state(fap, &state); + if (rc != 0) { + rc = BOOT_EFLASH; + goto out; + } + + if (state.image_ok == BOOT_FLAG_UNSET) { + rc = boot_write_image_ok(fap); + } + +out: + flash_area_close(fap); + return rc; +} + + +#endif /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) */ diff --git a/bootloader/mcuboot/boot/bootutil/src/swap_move.c b/bootloader/mcuboot/boot/bootutil/src/swap_move.c new file mode 100644 index 0000000..20caa2b --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/swap_move.c @@ -0,0 +1,608 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2019 JUUL Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#ifdef MCUBOOT_SWAP_USING_MOVE + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) +/* + * FIXME: this might have to be updated for threaded sim + */ +int boot_status_fails = 0; +#define BOOT_STATUS_ASSERT(x) \ + do { \ + if (!(x)) { \ + boot_status_fails++; \ + } \ + } while (0) +#else +#define BOOT_STATUS_ASSERT(x) ASSERT(x) +#endif + +uint32_t +find_last_idx(struct boot_loader_state *state, uint32_t swap_size) +{ + uint32_t sector_sz; + uint32_t sz; + uint32_t last_idx; + + sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + sz = 0; + last_idx = 0; + while (1) { + sz += sector_sz; + last_idx++; + if (sz >= swap_size) { + break; + } + } + + return last_idx; +} + +int +boot_read_image_header(struct boot_loader_state *state, int slot, + struct image_header *out_hdr, struct boot_status *bs) +{ + const struct flash_area *fap; + uint32_t off; + uint32_t sz; + uint32_t last_idx; + uint32_t swap_size; + int area_id; + int rc; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + off = 0; + if (bs && !boot_status_is_reset(bs)) { + boot_find_status(BOOT_CURR_IMG(state), &fap); + if (fap == NULL || boot_read_swap_size(fap, &swap_size)) { + rc = BOOT_EFLASH; + goto done; + } + flash_area_close(fap); + + last_idx = find_last_idx(state, swap_size); + sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + + /* + * Find the correct offset or slot where the image header is expected to + * be found for the steps where it is moved or swapped. + */ + if (bs->op == BOOT_STATUS_OP_MOVE && slot == 0 && bs->idx > last_idx) { + off = sz; + } else if (bs->op == BOOT_STATUS_OP_SWAP) { + if (bs->idx > 1 && bs->idx <= last_idx) { + slot = (slot == 0) ? 1 : 0; + } else if (bs->idx == 1) { + if (slot == 0) { + off = sz; + } else if (slot == 1 && bs->state == 2) { + slot = 0; + } + } + } + } + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = flash_area_read(fap, off, out_hdr, sizeof *out_hdr); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + /* We only know where the headers are located when bs is valid */ + if (bs != NULL && out_hdr->ih_magic != IMAGE_MAGIC) { + rc = -1; + goto done; + } + + rc = 0; + +done: + flash_area_close(fap); + return rc; +} + +int +swap_read_status_bytes(const struct flash_area *fap, + struct boot_loader_state *state, struct boot_status *bs) +{ + uint32_t off; + uint8_t status; + int max_entries; + int found_idx; + uint8_t write_sz; + int move_entries; + int rc; + int last_rc; + int erased_sections; + int i; + + max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap); + if (max_entries < 0) { + return BOOT_EBADARGS; + } + + erased_sections = 0; + found_idx = -1; + /* skip erased sectors at the end */ + last_rc = 1; + write_sz = BOOT_WRITE_SZ(state); + off = boot_status_off(fap); + for (i = max_entries; i > 0; i--) { + rc = flash_area_read(fap, off + (i - 1) * write_sz, &status, 1); + if (rc < 0) { + return BOOT_EFLASH; + } + + if (bootutil_buffer_is_erased(fap, &status, 1)) { + if (rc != last_rc) { + erased_sections++; + } + } else { + if (found_idx == -1) { + found_idx = i; + } + } + last_rc = rc; + } + + if (erased_sections > 1) { + /* This means there was an error writing status on the last + * swap. Tell user and move on to validation! + */ +#if !defined(__BOOTSIM__) + BOOT_LOG_ERR("Detected inconsistent status!"); +#endif + +#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) + /* With validation of the primary slot disabled, there is no way + * to be sure the swapped primary slot is OK, so abort! + */ + assert(0); +#endif + } + + move_entries = BOOT_MAX_IMG_SECTORS * BOOT_STATUS_MOVE_STATE_COUNT; + if (found_idx == -1) { + /* no swap status found; nothing to do */ + } else if (found_idx < move_entries) { + bs->op = BOOT_STATUS_OP_MOVE; + bs->idx = (found_idx / BOOT_STATUS_MOVE_STATE_COUNT) + BOOT_STATUS_IDX_0; + bs->state = (found_idx % BOOT_STATUS_MOVE_STATE_COUNT) + BOOT_STATUS_STATE_0;; + } else { + bs->op = BOOT_STATUS_OP_SWAP; + bs->idx = ((found_idx - move_entries) / BOOT_STATUS_SWAP_STATE_COUNT) + BOOT_STATUS_IDX_0; + bs->state = ((found_idx - move_entries) % BOOT_STATUS_SWAP_STATE_COUNT) + BOOT_STATUS_STATE_0; + } + + return 0; +} + +uint32_t +boot_status_internal_off(const struct boot_status *bs, int elem_sz) +{ + uint32_t off; + int idx_sz; + + idx_sz = elem_sz * ((bs->op == BOOT_STATUS_OP_MOVE) ? + BOOT_STATUS_MOVE_STATE_COUNT : BOOT_STATUS_SWAP_STATE_COUNT); + + off = ((bs->op == BOOT_STATUS_OP_MOVE) ? + 0 : (BOOT_MAX_IMG_SECTORS * BOOT_STATUS_MOVE_STATE_COUNT * elem_sz)) + + (bs->idx - BOOT_STATUS_IDX_0) * idx_sz + + (bs->state - BOOT_STATUS_STATE_0) * elem_sz; + + return off; +} + +static int app_max_sectors(struct boot_loader_state *state) +{ + uint32_t sz = 0; + uint32_t sector_sz; + uint32_t trailer_sz; + uint32_t first_trailer_idx; + + sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + first_trailer_idx = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1; + + while (1) { + sz += sector_sz; + if (sz >= trailer_sz) { + break; + } + first_trailer_idx--; + } + + return first_trailer_idx; +} + +int +boot_slots_compatible(struct boot_loader_state *state) +{ +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. In this case, image 1 primary points to the other + * B1 slot (ie S0 or S1), and image 0 primary points to the app. + * With this configuration, image 0 and image 1 share the secondary slot. + * Hence, the primary slot of image 1 will be *smaller* than image 1's + * secondary slot. This is not allowed in upstream mcuboot, so we need + * this patch to allow it. Also, all of these checks are redundant when + * partition manager is in use, and since we have the same sector size + * in all of our flash. + */ + return 1; +#else + size_t num_sectors_pri; + size_t num_sectors_sec; + size_t sector_sz_pri = 0; + size_t sector_sz_sec = 0; + size_t i; + size_t num_usable_sectors_pri; + + num_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); + num_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT); + num_usable_sectors_pri = app_max_sectors(state); + + if ((num_sectors_pri != num_sectors_sec) && + (num_sectors_pri != (num_sectors_sec + 1)) && + (num_usable_sectors_pri != (num_sectors_sec + 1))) { + BOOT_LOG_WRN("Cannot upgrade: not a compatible amount of sectors"); + BOOT_LOG_DBG("slot0 sectors: %d, slot1 sectors: %d, usable slot0 sectors: %d", + (int)num_sectors_pri, (int)num_sectors_sec, + (int)(num_usable_sectors_pri - 1)); + return 0; + } else if (num_sectors_pri > BOOT_MAX_IMG_SECTORS) { + BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed"); + return 0; + } + + if (num_usable_sectors_pri != (num_sectors_sec + 1)) { + BOOT_LOG_DBG("Non-optimal sector distribution, slot0 has %d usable sectors (%d assigned) " + "but slot1 has %d assigned", (int)(num_usable_sectors_pri - 1), + (int)num_sectors_pri, (int)num_sectors_sec); + } + + for (i = 0; i < num_sectors_sec; i++) { + sector_sz_pri = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i); + sector_sz_sec = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, i); + if (sector_sz_pri != sector_sz_sec) { + BOOT_LOG_WRN("Cannot upgrade: not same sector layout"); + return 0; + } + } + + if (num_sectors_pri > num_sectors_sec) { + if (sector_sz_pri != boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i)) { + BOOT_LOG_WRN("Cannot upgrade: not same sector layout"); + return 0; + } + } + + return 1; +#endif /* PM_S1_ADDRESS */ +} + +#define BOOT_LOG_SWAP_STATE(area, state) \ + BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \ + "image_ok=0x%x", \ + (area), \ + ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \ + (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \ + "bad"), \ + (state)->swap_type, \ + (state)->copy_done, \ + (state)->image_ok) + +int +swap_status_source(struct boot_loader_state *state) +{ + struct boot_swap_state state_primary_slot; + struct boot_swap_state state_secondary_slot; + int rc; + uint8_t source; + uint8_t image_index; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + image_index = BOOT_CURR_IMG(state); + + rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index), + &state_primary_slot); + assert(rc == 0); + + BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot); + + rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index), + &state_secondary_slot); + assert(rc == 0); + + BOOT_LOG_SWAP_STATE("Secondary image", &state_secondary_slot); + + if (state_primary_slot.magic == BOOT_MAGIC_GOOD && + state_primary_slot.copy_done == BOOT_FLAG_UNSET && + state_secondary_slot.magic != BOOT_MAGIC_GOOD) { + + source = BOOT_STATUS_SOURCE_PRIMARY_SLOT; + + BOOT_LOG_INF("Boot source: primary slot"); + return source; + } + + BOOT_LOG_INF("Boot source: none"); + return BOOT_STATUS_SOURCE_NONE; +} + +/* + * "Moves" the sector located at idx - 1 to idx. + */ +static void +boot_move_sector_up(int idx, uint32_t sz, struct boot_loader_state *state, + struct boot_status *bs, const struct flash_area *fap_pri, + const struct flash_area *fap_sec) +{ + uint32_t new_off; + uint32_t old_off; + int rc; + + /* + * FIXME: assuming sectors of size == sz, a single off variable + * would be enough + */ + + /* Calculate offset from start of image area. */ + new_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx); + old_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx - 1); + + if (bs->idx == BOOT_STATUS_IDX_0) { + if (bs->source != BOOT_STATUS_SOURCE_PRIMARY_SLOT) { + rc = swap_erase_trailer_sectors(state, fap_pri); + assert(rc == 0); + + rc = swap_status_init(state, fap_pri, bs); + assert(rc == 0); + } + + rc = swap_erase_trailer_sectors(state, fap_sec); + assert(rc == 0); + } + + rc = boot_erase_region(fap_pri, new_off, sz); + assert(rc == 0); + + rc = boot_copy_region(state, fap_pri, fap_pri, old_off, new_off, sz); + assert(rc == 0); + + rc = boot_write_status(state, bs); + + bs->idx++; + BOOT_STATUS_ASSERT(rc == 0); +} + +static void +boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state, + struct boot_status *bs, const struct flash_area *fap_pri, + const struct flash_area *fap_sec) +{ + uint32_t pri_off; + uint32_t pri_up_off; + uint32_t sec_off; + int rc; + + pri_up_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx); + pri_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx - 1); + sec_off = boot_img_sector_off(state, BOOT_SECONDARY_SLOT, idx - 1); + + if (bs->state == BOOT_STATUS_STATE_0) { + rc = boot_erase_region(fap_pri, pri_off, sz); + assert(rc == 0); + + rc = boot_copy_region(state, fap_sec, fap_pri, sec_off, pri_off, sz); + assert(rc == 0); + + rc = boot_write_status(state, bs); + bs->state = BOOT_STATUS_STATE_1; + BOOT_STATUS_ASSERT(rc == 0); + } + + if (bs->state == BOOT_STATUS_STATE_1) { + rc = boot_erase_region(fap_sec, sec_off, sz); + assert(rc == 0); + + rc = boot_copy_region(state, fap_pri, fap_sec, pri_up_off, sec_off, sz); + assert(rc == 0); + + rc = boot_write_status(state, bs); + bs->idx++; + bs->state = BOOT_STATUS_STATE_0; + BOOT_STATUS_ASSERT(rc == 0); + } +} + +/* + * When starting a revert the swap status exists in the primary slot, and + * the status in the secondary slot is erased. To start the swap, the status + * area in the primary slot must be re-initialized; if during the small + * window of time between re-initializing it and writing the first metadata + * a reset happens, the swap process is broken and cannot be resumed. + * + * This function handles the issue by making the revert look like a permanent + * upgrade (by initializing the secondary slot). + */ +void +fixup_revert(const struct boot_loader_state *state, struct boot_status *bs, + const struct flash_area *fap_sec) +{ + struct boot_swap_state swap_state; + int rc; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + /* No fixup required */ + if (bs->swap_type != BOOT_SWAP_TYPE_REVERT || + bs->op != BOOT_STATUS_OP_MOVE || + bs->idx != BOOT_STATUS_IDX_0) { + return; + } + + rc = boot_read_swap_state(fap_sec, &swap_state); + assert(rc == 0); + + BOOT_LOG_SWAP_STATE("Secondary image", &swap_state); + + if (swap_state.magic == BOOT_MAGIC_UNSET) { + rc = swap_erase_trailer_sectors(state, fap_sec); + assert(rc == 0); + + rc = boot_write_image_ok(fap_sec); + assert(rc == 0); + + rc = boot_write_swap_size(fap_sec, bs->swap_size); + assert(rc == 0); + + rc = boot_write_magic(fap_sec); + assert(rc == 0); + } +} + +void +swap_run(struct boot_loader_state *state, struct boot_status *bs, + uint32_t copy_size) +{ + uint32_t sz; + uint32_t sector_sz; + uint32_t idx; + uint32_t trailer_sz; + uint32_t first_trailer_idx; + uint32_t last_idx; + uint8_t image_index; + const struct flash_area *fap_pri; + const struct flash_area *fap_sec; + int rc; + + BOOT_LOG_INF("Starting swap using move algorithm."); + + last_idx = find_last_idx(state, copy_size); + sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + + /* + * When starting a new swap upgrade, check that there is enough space. + */ + if (boot_status_is_reset(bs)) { + sz = 0; + trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + first_trailer_idx = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1; + + while (1) { + sz += sector_sz; + if (sz >= trailer_sz) { + break; + } + first_trailer_idx--; + } + + if (last_idx >= first_trailer_idx) { + BOOT_LOG_WRN("Not enough free space to run swap upgrade"); + BOOT_LOG_WRN("required %d bytes but only %d are available", + (last_idx + 1) * sector_sz, + first_trailer_idx * sector_sz); + bs->swap_type = BOOT_SWAP_TYPE_NONE; + return; + } + } + + image_index = BOOT_CURR_IMG(state); + + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), &fap_pri); + assert (rc == 0); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap_sec); + assert (rc == 0); + + fixup_revert(state, bs, fap_sec); + + if (bs->op == BOOT_STATUS_OP_MOVE) { + idx = last_idx; + while (idx > 0) { + if (idx <= (last_idx - bs->idx + 1)) { + boot_move_sector_up(idx, sector_sz, state, bs, fap_pri, fap_sec); + } + idx--; + } + bs->idx = BOOT_STATUS_IDX_0; + } + + bs->op = BOOT_STATUS_OP_SWAP; + + idx = 1; + while (idx <= last_idx) { + if (idx >= bs->idx) { + boot_swap_sectors(idx, sector_sz, state, bs, fap_pri, fap_sec); + } + idx++; + } + + flash_area_close(fap_pri); + flash_area_close(fap_sec); +} + +int app_max_size(struct boot_loader_state *state) +{ + uint32_t sector_sz_primary; + uint32_t sector_sz_secondary; + uint32_t sz_primary; + uint32_t sz_secondary; + + sector_sz_primary = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + sector_sz_secondary = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0); + + /* Account for image flags and move sector */ + sz_primary = app_max_sectors(state) * sector_sz_primary - sector_sz_primary; + sz_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) * sector_sz_secondary; + + return (sz_primary <= sz_secondary ? sz_primary : sz_secondary); +} + +#endif diff --git a/bootloader/mcuboot/boot/bootutil/src/swap_nsib.c b/bootloader/mcuboot/boot/bootutil/src/swap_nsib.c new file mode 100644 index 0000000..39ed4c6 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/swap_nsib.c @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs) +{ + uint32_t sector_sz; + uint8_t image_index; + const struct flash_area *fap_pri; + const struct flash_area *fap_sec; + int rc; + + BOOT_LOG_INF("Starting swap using nsib algorithm."); + + sector_sz = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0); + +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + rc = flash_area_open(PM_S0_ID, &fap_pri); +#else + rc = flash_area_open(PM_S1_ID, &fap_pri); +#endif + assert (rc == 0); + image_index = BOOT_CURR_IMG(state); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap_sec); + assert (rc == 0); + + rc = boot_erase_region(fap_pri, 0, fap_pri->fa_size); + assert(rc == 0); + + rc = boot_copy_region(state, fap_sec, fap_pri, 0, 0, fap_pri->fa_size); + assert(rc == 0); + + rc = swap_erase_trailer_sectors(state, fap_sec); + assert(rc == 0); + + rc = boot_erase_region(fap_sec, 0, MIN((fap_pri->fa_size + sector_sz), fap_sec->fa_size)); + assert(rc == 0); + + flash_area_close(fap_pri); + flash_area_close(fap_sec); +} diff --git a/bootloader/mcuboot/boot/bootutil/src/swap_priv.h b/bootloader/mcuboot/boot/bootutil/src/swap_priv.h new file mode 100644 index 0000000..2734d62 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/swap_priv.h @@ -0,0 +1,117 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2019 JUUL Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_SWAP_PRIV_ +#define H_SWAP_PRIV_ + +#include "mcuboot_config/mcuboot_config.h" + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + +/** + * Calculates the amount of space required to store the trailer, and erases + * all sectors required for this storage in the given flash_area. + */ +int swap_erase_trailer_sectors(const struct boot_loader_state *state, + const struct flash_area *fap); + +/** + * Initialize the given flash_area with the metadata required to start a new + * swap upgrade. + */ +int swap_status_init(const struct boot_loader_state *state, + const struct flash_area *fap, + const struct boot_status *bs); + +/** + * Tries to locate an interrupted swap status (metadata). If not metadata + * was found returns BOOT_STATUS_SOURCE_NONE. + * + * Must return one of: + * - BOOT_STATUS_SOURCE_NONE + * - BOOT_STATUS_SOURCE_SCRATCH + * - BOOT_STATUS_SOURCE_PRIMARY_SLOT + */ +int swap_status_source(struct boot_loader_state *state); + +/** + * Reads the boot status from the flash. The boot status contains + * the current state of an interrupted image copy operation. If the boot + * status is not present, or it indicates that previous copy finished, + * there is no operation in progress. + */ +int swap_read_status(struct boot_loader_state *state, struct boot_status *bs); + +/** + * Iterate over the swap status bytes in the given flash_area and populate + * the given boot_status with the calculated index where a swap upgrade was + * interrupted. + */ +int swap_read_status_bytes(const struct flash_area *fap, + struct boot_loader_state *state, + struct boot_status *bs); + +/** + * Marks the image in the primary slot as fully copied. + */ +int swap_set_copy_done(uint8_t image_index); + +/** + * Marks a reverted image in the primary slot as confirmed. This is necessary to + * ensure the status bytes from the image revert operation don't get processed + * on a subsequent boot. + * + * NOTE: image_ok is tested before writing because if there's a valid permanent + * image installed on the primary slot and the new image to be upgrade to has a + * bad sig, image_ok would be overwritten. + */ +int swap_set_image_ok(uint8_t image_index); + +/** + * Start a new or resume an interrupted swap according to the parameters + * found in the given boot_status. + */ +void swap_run(struct boot_loader_state *state, + struct boot_status *bs, + uint32_t copy_size); + +#if MCUBOOT_SWAP_USING_SCRATCH +#define BOOT_SCRATCH_AREA(state) ((state)->scratch.area) + +static inline size_t boot_scratch_area_size(const struct boot_loader_state *state) +{ + return flash_area_get_size(BOOT_SCRATCH_AREA(state)); +} +#endif + +#endif /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) */ + +/** + * Returns the maximum size of an application that can be loaded to a slot. + */ +int app_max_size(struct boot_loader_state *state); + +#if defined(PM_S1_ADDRESS) && !defined(MCUBOOT_OVERWRITE_ONLY) && \ +(CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 || defined(LEGACY_CHILD_PARENT_S0_S1_UPDATE_ENABLED)) +/** + * Performs an NSIB update + */ +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs); +#endif + +#endif /* H_SWAP_PRIV_ */ diff --git a/bootloader/mcuboot/boot/bootutil/src/swap_scratch.c b/bootloader/mcuboot/boot/bootutil/src/swap_scratch.c new file mode 100644 index 0000000..66dca83 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/swap_scratch.c @@ -0,0 +1,992 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2019 JUUL Labs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#if !defined(MCUBOOT_SWAP_USING_MOVE) + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) +/* + * FIXME: this might have to be updated for threaded sim + */ +int boot_status_fails = 0; +#define BOOT_STATUS_ASSERT(x) \ + do { \ + if (!(x)) { \ + boot_status_fails++; \ + } \ + } while (0) +#else +#define BOOT_STATUS_ASSERT(x) ASSERT(x) +#endif + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +/** + * Reads the status of a partially-completed swap, if any. This is necessary + * to recover in case the boot lodaer was reset in the middle of a swap + * operation. + */ +int +swap_read_status_bytes(const struct flash_area *fap, + struct boot_loader_state *state, struct boot_status *bs) +{ + uint32_t off; + uint8_t status; + int max_entries; + int found; + int found_idx; + int invalid; + int rc; + int i; + + off = boot_status_off(fap); + max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap); + if (max_entries < 0) { + return BOOT_EBADARGS; + } + + found = 0; + found_idx = 0; + invalid = 0; + for (i = 0; i < max_entries; i++) { + rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state), + &status, 1); + if (rc < 0) { + return BOOT_EFLASH; + } + + if (bootutil_buffer_is_erased(fap, &status, 1)) { + if (found && !found_idx) { + found_idx = i; + } + } else if (!found) { + found = 1; + } else if (found_idx) { + invalid = 1; + break; + } + } + + if (invalid) { + /* This means there was an error writing status on the last + * swap. Tell user and move on to validation! + */ +#if !defined(__BOOTSIM__) + BOOT_LOG_ERR("Detected inconsistent status!"); +#endif + +#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) + /* With validation of the primary slot disabled, there is no way + * to be sure the swapped primary slot is OK, so abort! + */ + assert(0); +#endif + } + + if (found) { + if (!found_idx) { + found_idx = i; + } + bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1; + bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1; + } + + return 0; +} + +uint32_t +boot_status_internal_off(const struct boot_status *bs, int elem_sz) +{ + int idx_sz; + + idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT; + + return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz + + (bs->state - BOOT_STATUS_STATE_0) * elem_sz; +} + +/* + * Slots are compatible when all sectors that store up to to size of the image + * round up to sector size, in both slot's are able to fit in the scratch + * area, and have sizes that are a multiple of each other (powers of two + * presumably!). + */ +int +boot_slots_compatible(struct boot_loader_state *state) +{ +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. In this case, image 1 primary points to the other + * B1 slot (ie S0 or S1), and image 0 primary points to the app. + * With this configuration, image 0 and image 1 share the secondary slot. + * Hence, the primary slot of image 1 will be *smaller* than image 1's + * secondary slot. This is not allowed in upstream mcuboot, so we need + * this patch to allow it. Also, all of these checks are redundant when + * partition manager is in use, and since we have the same sector size + * in all of our flash. + */ + return 1; +#else + size_t num_sectors_primary; + size_t num_sectors_secondary; + size_t sz0, sz1; + size_t primary_slot_sz, secondary_slot_sz; +#ifndef MCUBOOT_OVERWRITE_ONLY + size_t scratch_sz; +#endif + size_t i, j; + int8_t smaller; + + num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); + num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT); + if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) || + (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) { + BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed"); + return 0; + } + +#ifndef MCUBOOT_OVERWRITE_ONLY + scratch_sz = boot_scratch_area_size(state); +#endif + + /* + * The following loop scans all sectors in a linear fashion, assuring that + * for each possible sector in each slot, it is able to fit in the other + * slot's sector or sectors. Slot's should be compatible as long as any + * number of a slot's sectors are able to fit into another, which only + * excludes cases where sector sizes are not a multiple of each other. + */ + i = sz0 = primary_slot_sz = 0; + j = sz1 = secondary_slot_sz = 0; + smaller = 0; + while (i < num_sectors_primary || j < num_sectors_secondary) { + if (sz0 == sz1) { + sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i); + sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j); + i++; + j++; + } else if (sz0 < sz1) { + sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i); + /* Guarantee that multiple sectors of the secondary slot + * fit into the primary slot. + */ + if (smaller == 2) { + BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors"); + return 0; + } + smaller = 1; + i++; + } else { + size_t sector_size = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j); + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (sector_size == 0) { + /* Since this supports decompressed images, we can safely exit if slot1 is + * smaller than slot0. + */ + break; + } +#endif + sz1 += sector_size; + /* Guarantee that multiple sectors of the primary slot + * fit into the secondary slot. + */ + if (smaller == 1) { + BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors"); + return 0; + } + smaller = 2; + j++; + } +#ifndef MCUBOOT_OVERWRITE_ONLY + if (sz0 == sz1) { + primary_slot_sz += sz0; + secondary_slot_sz += sz1; + /* Scratch has to fit each swap operation to the size of the larger + * sector among the primary slot and the secondary slot. + */ + if (sz0 > scratch_sz || sz1 > scratch_sz) { + BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch"); + return 0; + } + smaller = sz0 = sz1 = 0; + } +#endif + } + +#ifndef MCUBOOT_DECOMPRESS_IMAGES + if ((i != num_sectors_primary) || + (j != num_sectors_secondary) || + (primary_slot_sz != secondary_slot_sz)) { + BOOT_LOG_WRN("Cannot upgrade: slots are not compatible"); + return 0; + } +#endif + + return 1; +#endif /* PM_S1_ADDRESS */ +} + +#define BOOT_LOG_SWAP_STATE(area, state) \ + BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \ + "image_ok=0x%x", \ + (area), \ + ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \ + (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \ + "bad"), \ + (state)->swap_type, \ + (state)->copy_done, \ + (state)->image_ok) + +struct boot_status_table { + uint8_t bst_magic_primary_slot; + uint8_t bst_magic_scratch; + uint8_t bst_copy_done_primary_slot; + uint8_t bst_status_source; +}; + +/** + * This set of tables maps swap state contents to boot status location. + * When searching for a match, these tables must be iterated in order. + */ +static const struct boot_status_table boot_status_tables[] = { + { + /* | primary slot | scratch | + * ----------+--------------+--------------| + * magic | Good | Any | + * copy-done | Set | N/A | + * ----------+--------------+--------------' + * source: none | + * ----------------------------------------' + */ + .bst_magic_primary_slot = BOOT_MAGIC_GOOD, + .bst_magic_scratch = BOOT_MAGIC_NOTGOOD, + .bst_copy_done_primary_slot = BOOT_FLAG_SET, + .bst_status_source = BOOT_STATUS_SOURCE_NONE, + }, + + { + /* | primary slot | scratch | + * ----------+--------------+--------------| + * magic | Good | Any | + * copy-done | Unset | N/A | + * ----------+--------------+--------------' + * source: primary slot | + * ----------------------------------------' + */ + .bst_magic_primary_slot = BOOT_MAGIC_GOOD, + .bst_magic_scratch = BOOT_MAGIC_NOTGOOD, + .bst_copy_done_primary_slot = BOOT_FLAG_UNSET, + .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT, + }, + + { + /* | primary slot | scratch | + * ----------+--------------+--------------| + * magic | Any | Good | + * copy-done | Any | N/A | + * ----------+--------------+--------------' + * source: scratch | + * ----------------------------------------' + */ + .bst_magic_primary_slot = BOOT_MAGIC_ANY, + .bst_magic_scratch = BOOT_MAGIC_GOOD, + .bst_copy_done_primary_slot = BOOT_FLAG_ANY, + .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH, + }, + { + /* | primary slot | scratch | + * ----------+--------------+--------------| + * magic | Unset | Any | + * copy-done | Unset | N/A | + * ----------+--------------+--------------| + * source: varies | + * ----------------------------------------+--------------------------+ + * This represents one of two cases: | + * o No swaps ever (no status to read, so no harm in checking). | + * o Mid-revert; status in primary slot. | + * -------------------------------------------------------------------' + */ + .bst_magic_primary_slot = BOOT_MAGIC_UNSET, + .bst_magic_scratch = BOOT_MAGIC_ANY, + .bst_copy_done_primary_slot = BOOT_FLAG_UNSET, + .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT, + }, +}; + +#define BOOT_STATUS_TABLES_COUNT \ + (sizeof boot_status_tables / sizeof boot_status_tables[0]) + +/** + * Determines where in flash the most recent boot status is stored. The boot + * status is necessary for completing a swap that was interrupted by a boot + * loader reset. + * + * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should + * be read from. + */ +int +swap_status_source(struct boot_loader_state *state) +{ + const struct boot_status_table *table; +#if MCUBOOT_SWAP_USING_SCRATCH + struct boot_swap_state state_scratch; +#endif + struct boot_swap_state state_primary_slot; + int rc; + size_t i; + uint8_t source; + uint8_t image_index; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + image_index = BOOT_CURR_IMG(state); + rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index), + &state_primary_slot); + assert(rc == 0); + +#if MCUBOOT_SWAP_USING_SCRATCH + rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch); + assert(rc == 0); +#endif + + BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot); +#if MCUBOOT_SWAP_USING_SCRATCH + BOOT_LOG_SWAP_STATE("Scratch", &state_scratch); +#endif + for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) { + table = &boot_status_tables[i]; + + if (boot_magic_compatible_check(table->bst_magic_primary_slot, + state_primary_slot.magic) && +#if MCUBOOT_SWAP_USING_SCRATCH + boot_magic_compatible_check(table->bst_magic_scratch, + state_scratch.magic) && +#endif + (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY || + table->bst_copy_done_primary_slot == state_primary_slot.copy_done)) + { + source = table->bst_status_source; + +#if (BOOT_IMAGE_NUMBER > 1) && MCUBOOT_SWAP_USING_SCRATCH + /* In case of multi-image boot it can happen that if boot status + * info is found on scratch area then it does not belong to the + * currently examined image. + */ + if (source == BOOT_STATUS_SOURCE_SCRATCH && + state_scratch.image_num != BOOT_CURR_IMG(state)) { + source = BOOT_STATUS_SOURCE_NONE; + } +#endif + + BOOT_LOG_INF("Boot source: %s", + source == BOOT_STATUS_SOURCE_NONE ? "none" : + source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" : + source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ? + "primary slot" : "BUG; can't happen"); + return source; + } + } + + BOOT_LOG_INF("Boot source: none"); + return BOOT_STATUS_SOURCE_NONE; +} + +#ifndef MCUBOOT_OVERWRITE_ONLY +/** + * Calculates the number of sectors the scratch area can contain. A "last" + * source sector is specified because images are copied backwards in flash + * (final index to index number 0). + * + * @param last_sector_idx The index of the last source sector + * (inclusive). + * @param out_first_sector_idx The index of the first source sector + * (inclusive) gets written here. + * + * @return The number of bytes comprised by the + * [first-sector, last-sector] range. + */ +static uint32_t +boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx, + int *out_first_sector_idx) +{ + size_t scratch_sz; + uint32_t new_sz; + uint32_t sz; + int i; + + sz = 0; + + scratch_sz = boot_scratch_area_size(state); + for (i = last_sector_idx; i >= 0; i--) { + new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i); + /* + * The secondary slot is not being checked here, because + * `boot_slots_compatible` already provides assurance that the copy size + * will be compatible with the primary slot and scratch. + */ + if (new_sz > scratch_sz) { + break; + } + sz = new_sz; + } + + /* i currently refers to a sector that doesn't fit or it is -1 because all + * sectors have been processed. In both cases, exclude sector i. + */ + *out_first_sector_idx = i + 1; + return sz; +} + +/** + * Finds the index of the last sector in the primary slot that needs swapping. + * + * @param state Current bootloader's state. + * @param copy_size Total number of bytes to swap. + * + * @return Index of the last sector in the primary slot that needs swapping. + */ +static int +find_last_sector_idx(const struct boot_loader_state *state, uint32_t copy_size) +{ + int last_sector_idx; + uint32_t primary_slot_size; + uint32_t secondary_slot_size; + + primary_slot_size = 0; + secondary_slot_size = 0; + last_sector_idx = 0; + + /* + * Knowing the size of the largest image between both slots, here we + * find what is the last sector in the primary slot that needs swapping. + * Since we already know that both slots are compatible, the secondary + * slot's last sector is not really required after this check is finished. + */ + while (1) { + if ((primary_slot_size < copy_size) || + (primary_slot_size < secondary_slot_size)) { + primary_slot_size += boot_img_sector_size(state, + BOOT_PRIMARY_SLOT, + last_sector_idx); + } + if ((secondary_slot_size < copy_size) || + (secondary_slot_size < primary_slot_size)) { + secondary_slot_size += boot_img_sector_size(state, + BOOT_SECONDARY_SLOT, + last_sector_idx); + } + if (primary_slot_size >= copy_size && + secondary_slot_size >= copy_size && + primary_slot_size == secondary_slot_size) { + break; + } + last_sector_idx++; + } + + return last_sector_idx; +} + +/** + * Finds the number of swap operations that have to be performed to swap the two images. + * + * @param state Current bootloader's state. + * @param copy_size Total number of bytes to swap. + * + * @return The number of swap operations that have to be performed. +*/ +static uint32_t +find_swap_count(const struct boot_loader_state *state, uint32_t copy_size) +{ + int first_sector_idx; + int last_sector_idx; + uint32_t swap_count; + + last_sector_idx = find_last_sector_idx(state, copy_size); + + swap_count = 0; + + while (last_sector_idx >= 0) { + boot_copy_sz(state, last_sector_idx, &first_sector_idx); + + last_sector_idx = first_sector_idx - 1; + swap_count++; + } + + return swap_count; +} + +/** + * Swaps the contents of two flash regions within the two image slots. + * + * @param idx The index of the first sector in the range of + * sectors being swapped. + * @param sz The number of bytes to swap. + * @param bs The current boot status. This struct gets + * updated according to the outcome. + * + * @return 0 on success; nonzero on failure. + */ +static void +boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state, + struct boot_status *bs) +{ + const struct flash_area *fap_primary_slot; + const struct flash_area *fap_secondary_slot; + const struct flash_area *fap_scratch; + uint32_t copy_sz; + uint32_t trailer_sz; + uint32_t sector_sz; + uint32_t img_off; + uint32_t scratch_trailer_off; + struct boot_swap_state swap_state; + size_t last_sector; + bool erase_scratch; + uint8_t image_index; + int rc; + + /* Calculate offset from start of image area. */ + img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx); + + copy_sz = sz; + trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + + /* sz in this function is always sized on a multiple of the sector size. + * The check against the start offset of the last sector + * is to determine if we're swapping the last sector. The last sector + * needs special handling because it's where the trailer lives. If we're + * copying it, we need to use scratch to write the trailer temporarily. + * + * NOTE: `use_scratch` is a temporary flag (never written to flash) which + * controls if special handling is needed (swapping last sector). + */ + last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1; + sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, last_sector); + + if (sector_sz < trailer_sz) { + uint32_t trailer_sector_sz = sector_sz; + + while (trailer_sector_sz < trailer_sz) { + /* Consider that the image trailer may span across sectors of + * different sizes. + */ + sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, --last_sector); + + trailer_sector_sz += sector_sz; + } + } + + if ((img_off + sz) > + boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) { + copy_sz -= trailer_sz; + } + + bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz); + + image_index = BOOT_CURR_IMG(state); + + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), + &fap_primary_slot); + assert (rc == 0); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), + &fap_secondary_slot); + assert (rc == 0); + + rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch); + assert (rc == 0); + + if (bs->state == BOOT_STATUS_STATE_0) { + BOOT_LOG_DBG("erasing scratch area"); + rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch)); + assert(rc == 0); + + if (bs->idx == BOOT_STATUS_IDX_0) { + /* Write a trailer to the scratch area, even if we don't need the + * scratch area for status. We need a temporary place to store the + * `swap-type` while we erase the primary trailer. + */ + rc = swap_status_init(state, fap_scratch, bs); + assert(rc == 0); + + if (!bs->use_scratch) { + /* Prepare the primary status area... here it is known that the + * last sector is not being used by the image data so it's safe + * to erase. + */ + rc = swap_erase_trailer_sectors(state, fap_primary_slot); + assert(rc == 0); + + rc = swap_status_init(state, fap_primary_slot, bs); + assert(rc == 0); + + /* Erase the temporary trailer from the scratch area. */ + rc = boot_erase_region(fap_scratch, 0, + flash_area_get_size(fap_scratch)); + assert(rc == 0); + } + } + + rc = boot_copy_region(state, fap_secondary_slot, fap_scratch, + img_off, 0, copy_sz); + assert(rc == 0); + + rc = boot_write_status(state, bs); + bs->state = BOOT_STATUS_STATE_1; + BOOT_STATUS_ASSERT(rc == 0); + } + + if (bs->state == BOOT_STATUS_STATE_1) { + rc = boot_erase_region(fap_secondary_slot, img_off, sz); + assert(rc == 0); + + rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot, + img_off, img_off, copy_sz); + assert(rc == 0); + + if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) { + /* If not all sectors of the slot are being swapped, + * guarantee here that only the primary slot will have the state. + */ + rc = swap_erase_trailer_sectors(state, fap_secondary_slot); + assert(rc == 0); + } + + rc = boot_write_status(state, bs); + bs->state = BOOT_STATUS_STATE_2; + BOOT_STATUS_ASSERT(rc == 0); + } + + if (bs->state == BOOT_STATUS_STATE_2) { + rc = boot_erase_region(fap_primary_slot, img_off, sz); + assert(rc == 0); + + /* NOTE: If this is the final sector, we exclude the image trailer from + * this copy (copy_sz was truncated earlier). + */ + rc = boot_copy_region(state, fap_scratch, fap_primary_slot, + 0, img_off, copy_sz); + assert(rc == 0); + + if (bs->use_scratch) { + scratch_trailer_off = boot_status_off(fap_scratch); + + /* copy current status that is being maintained in scratch */ + rc = boot_copy_region(state, fap_scratch, fap_primary_slot, + scratch_trailer_off, img_off + copy_sz, + (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state)); + BOOT_STATUS_ASSERT(rc == 0); + + rc = boot_read_swap_state(fap_scratch, &swap_state); + assert(rc == 0); + + if (swap_state.image_ok == BOOT_FLAG_SET) { + rc = boot_write_image_ok(fap_primary_slot); + assert(rc == 0); + } + + if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) { + rc = boot_write_swap_info(fap_primary_slot, + swap_state.swap_type, image_index); + assert(rc == 0); + } + + rc = boot_write_swap_size(fap_primary_slot, bs->swap_size); + assert(rc == 0); + +#ifdef MCUBOOT_ENC_IMAGES + rc = boot_write_enc_key(fap_primary_slot, 0, bs); + assert(rc == 0); + + rc = boot_write_enc_key(fap_primary_slot, 1, bs); + assert(rc == 0); +#endif + rc = boot_write_magic(fap_primary_slot); + assert(rc == 0); + } + + /* If we wrote a trailer to the scratch area, erase it after we persist + * a trailer to the primary slot. We do this to prevent mcuboot from + * reading a stale status from the scratch area in case of immediate + * reset. + */ + erase_scratch = bs->use_scratch; + bs->use_scratch = 0; + + rc = boot_write_status(state, bs); + bs->idx++; + bs->state = BOOT_STATUS_STATE_0; + BOOT_STATUS_ASSERT(rc == 0); + + if (erase_scratch) { + rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch)); + assert(rc == 0); + } + } + + flash_area_close(fap_primary_slot); + flash_area_close(fap_secondary_slot); + flash_area_close(fap_scratch); +} + +void +swap_run(struct boot_loader_state *state, struct boot_status *bs, + uint32_t copy_size) +{ + uint32_t sz; + int first_sector_idx; + int last_sector_idx; + uint32_t swap_idx; + + BOOT_LOG_INF("Starting swap using scratch algorithm."); + + last_sector_idx = find_last_sector_idx(state, copy_size); + + swap_idx = 0; + while (last_sector_idx >= 0) { + sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx); + if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) { + boot_swap_sectors(first_sector_idx, sz, state, bs); + } + + last_sector_idx = first_sector_idx - 1; + swap_idx++; + } + +} +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + +int app_max_size(struct boot_loader_state *state) +{ + size_t num_sectors_primary; + size_t num_sectors_secondary; + size_t sz0, sz1; + size_t primary_slot_sz, secondary_slot_sz; +#ifndef MCUBOOT_OVERWRITE_ONLY + size_t scratch_sz; +#endif + size_t i, j; + int8_t smaller; + + num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); + num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT); + +#ifndef MCUBOOT_OVERWRITE_ONLY + scratch_sz = boot_scratch_area_size(state); +#endif + + /* + * The following loop scans all sectors in a linear fashion, assuring that + * for each possible sector in each slot, it is able to fit in the other + * slot's sector or sectors. Slot's should be compatible as long as any + * number of a slot's sectors are able to fit into another, which only + * excludes cases where sector sizes are not a multiple of each other. + */ + i = sz0 = primary_slot_sz = 0; + j = sz1 = secondary_slot_sz = 0; + smaller = 0; + while (i < num_sectors_primary || j < num_sectors_secondary) { + if (sz0 == sz1) { + sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i); + sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j); + i++; + j++; + } else if (sz0 < sz1) { + sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i); + /* Guarantee that multiple sectors of the secondary slot + * fit into the primary slot. + */ + if (smaller == 2) { + BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors"); + return 0; + } + smaller = 1; + i++; + } else { + sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j); + /* Guarantee that multiple sectors of the primary slot + * fit into the secondary slot. + */ + if (smaller == 1) { + BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors"); + return 0; + } + smaller = 2; + j++; + } +#ifndef MCUBOOT_OVERWRITE_ONLY + if (sz0 == sz1) { + primary_slot_sz += sz0; + secondary_slot_sz += sz1; + /* Scratch has to fit each swap operation to the size of the larger + * sector among the primary slot and the secondary slot. + */ + if (sz0 > scratch_sz || sz1 > scratch_sz) { + BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch"); + return 0; + } + smaller = sz0 = sz1 = 0; + } +#endif + } + +#ifdef MCUBOOT_OVERWRITE_ONLY + return (sz1 < sz0 ? sz1 : sz0); +#else + return (secondary_slot_sz < primary_slot_sz ? secondary_slot_sz : primary_slot_sz); +#endif +} +#else +int app_max_size(struct boot_loader_state *state) +{ + const struct flash_area *fap; + int fa_id; + int rc; + uint32_t active_slot; + int primary_sz, secondary_sz; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot); + rc = flash_area_open(fa_id, &fap); + assert(rc == 0); + primary_sz = flash_area_get_size(fap); + flash_area_close(fap); + + if (active_slot == BOOT_PRIMARY_SLOT) { + active_slot = BOOT_SECONDARY_SLOT; + } else { + active_slot = BOOT_PRIMARY_SLOT; + } + + fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot); + rc = flash_area_open(fa_id, &fap); + assert(rc == 0); + secondary_sz = flash_area_get_size(fap); + flash_area_close(fap); + + return (secondary_sz < primary_sz ? secondary_sz : primary_sz); +} + +#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */ + +int +boot_read_image_header(struct boot_loader_state *state, int slot, + struct image_header *out_hdr, struct boot_status *bs) +{ + const struct flash_area *fap; +#ifdef MCUBOOT_SWAP_USING_SCRATCH + uint32_t swap_count; + uint32_t swap_size; +#endif + int area_id; + int hdr_slot; + int rc = 0; + +#ifndef MCUBOOT_SWAP_USING_SCRATCH + (void)bs; +#endif + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + hdr_slot = slot; + +#ifdef MCUBOOT_SWAP_USING_SCRATCH + /* If the slots are being swapped, the headers might have been moved to scratch area or to the + * other slot depending on the progress of the swap process. + */ + if (bs && !boot_status_is_reset(bs)) { + rc = boot_find_status(BOOT_CURR_IMG(state), &fap); + + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = boot_read_swap_size(fap, &swap_size); + flash_area_close(fap); + + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + swap_count = find_swap_count(state, swap_size); + + if (bs->idx - BOOT_STATUS_IDX_0 >= swap_count) { + /* If all segments have been swapped, the header is located in the other slot */ + hdr_slot = (slot == BOOT_PRIMARY_SLOT) ? BOOT_SECONDARY_SLOT : BOOT_PRIMARY_SLOT; + } else if (bs->idx - BOOT_STATUS_IDX_0 == swap_count - 1) { + /* If the last swap operation is in progress, the headers are currently being swapped + * since the first segment of each slot is the last to be processed. + */ + + if (slot == BOOT_SECONDARY_SLOT && bs->state >= BOOT_STATUS_STATE_1) { + /* After BOOT_STATUS_STATE_1, the secondary image's header has been moved to the + * scratch area. + */ + hdr_slot = BOOT_NUM_SLOTS; + } else if (slot == BOOT_PRIMARY_SLOT && bs->state >= BOOT_STATUS_STATE_2) { + /* After BOOT_STATUS_STATE_2, the primary image's header has been moved to the + * secondary slot. + */ + hdr_slot = BOOT_SECONDARY_SLOT; + } + } + } + + if (hdr_slot == BOOT_NUM_SLOTS) { + area_id = FLASH_AREA_IMAGE_SCRATCH; + } else { + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), hdr_slot); + } +#else + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), hdr_slot); +#endif + + rc = flash_area_open(area_id, &fap); + if (rc == 0) { + rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr); + flash_area_close(fap); + } + + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + +done: + return rc; +} + +#endif /* !MCUBOOT_SWAP_USING_MOVE */ diff --git a/bootloader/mcuboot/boot/bootutil/src/tlv.c b/bootloader/mcuboot/boot/bootutil/src/tlv.c new file mode 100644 index 0000000..a763c96 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/src/tlv.c @@ -0,0 +1,154 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2019 JUUL Labs + * Copyright (c) 2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "bootutil/bootutil.h" +#include "bootutil/image.h" +#include "bootutil_priv.h" + +/* + * Initialize a TLV iterator. + * + * @param it An iterator struct + * @param hdr image_header of the slot's image + * @param fap flash_area of the slot which is storing the image + * @param type Type of TLV to look for + * @param prot true if TLV has to be stored in the protected area, false otherwise + * + * @returns 0 if the TLV iterator was successfully started + * -1 on errors + */ +int +bootutil_tlv_iter_begin(struct image_tlv_iter *it, const struct image_header *hdr, + const struct flash_area *fap, uint16_t type, bool prot) +{ + uint32_t off_; + struct image_tlv_info info; + + if (it == NULL || hdr == NULL || fap == NULL) { + return -1; + } + + off_ = BOOT_TLV_OFF(hdr); + if (LOAD_IMAGE_DATA(hdr, fap, off_, &info, sizeof(info))) { + return -1; + } + + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (hdr->ih_protect_tlv_size != info.it_tlv_tot) { + return -1; + } + + if (LOAD_IMAGE_DATA(hdr, fap, off_ + info.it_tlv_tot, + &info, sizeof(info))) { + return -1; + } + } else if (hdr->ih_protect_tlv_size != 0) { + return -1; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + return -1; + } + + it->hdr = hdr; + it->fap = fap; + it->type = type; + it->prot = prot; + it->prot_end = off_ + it->hdr->ih_protect_tlv_size; + it->tlv_end = off_ + it->hdr->ih_protect_tlv_size + info.it_tlv_tot; + // position on first TLV + it->tlv_off = off_ + sizeof(info); + return 0; +} + +/* + * Find next TLV + * + * @param it The image TLV iterator struct + * @param off The offset of the TLV's payload in flash + * @param len The length of the TLV's payload + * @param type If not NULL returns the type of TLV found + * + * @returns 0 if a TLV with with matching type was found + * 1 if no more TLVs with matching type are available + * -1 on errors + */ +int +bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off, uint16_t *len, + uint16_t *type) +{ + struct image_tlv tlv; + int rc; + + if (it == NULL || it->hdr == NULL || it->fap == NULL) { + return -1; + } + + while (it->tlv_off < it->tlv_end) { + if (it->hdr->ih_protect_tlv_size > 0 && it->tlv_off == it->prot_end) { + it->tlv_off += sizeof(struct image_tlv_info); + } + + rc = LOAD_IMAGE_DATA(it->hdr, it->fap, it->tlv_off, &tlv, sizeof tlv); + if (rc) { + return -1; + } + + /* No more TLVs in the protected area */ + if (it->prot && it->tlv_off >= it->prot_end) { + return 1; + } + + if (it->type == IMAGE_TLV_ANY || tlv.it_type == it->type) { + if (type != NULL) { + *type = tlv.it_type; + } + *off = it->tlv_off + sizeof(tlv); + *len = tlv.it_len; + it->tlv_off += sizeof(tlv) + tlv.it_len; + return 0; + } + + it->tlv_off += sizeof(tlv) + tlv.it_len; + } + + return 1; +} + +/* + * Return if a TLV entry is in the protected area. + * + * @param it The image TLV iterator struct + * @param off The offset of the entry to check. + * + * @return 0 if this TLV iterator entry is not protected. + * 1 if this TLV iterator entry is in the protected region + * -1 if the iterator is invalid. + */ +int +bootutil_tlv_iter_is_prot(struct image_tlv_iter *it, uint32_t off) +{ + if (it == NULL || it->hdr == NULL || it->fap == NULL) { + return -1; + } + + return off < it->prot_end; +} diff --git a/bootloader/mcuboot/boot/bootutil/zephyr/CMakeLists.txt b/bootloader/mcuboot/boot/bootutil/zephyr/CMakeLists.txt new file mode 100644 index 0000000..d5364d0 --- /dev/null +++ b/bootloader/mcuboot/boot/bootutil/zephyr/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_MCUBOOT_BOOTUTIL_LIB) + +zephyr_interface_library_named(MCUBOOT_BOOTUTIL) + +target_include_directories(MCUBOOT_BOOTUTIL INTERFACE + ../ + ../include + ../../zephyr/include +) + +zephyr_library_named(mcuboot_util) +zephyr_library_sources( + ../src/bootutil_public.c + ) + +# Sensitivity to the TEST_BOOT_IMAGE_ACCESS_HOOKS define is implemented for +# allowing the test-build with the hooks feature enabled. +if(TEST_BOOT_IMAGE_ACCESS_HOOKS) + zephyr_library_sources( + ${APPLICATION_SOURCE_DIR}/hooks_sample.c + ) +endif() + +zephyr_library_link_libraries(MCUBOOT_BOOTUTIL) +target_link_libraries(MCUBOOT_BOOTUTIL INTERFACE zephyr_interface) + +if(CONFIG_BOOT_USE_TINYCRYPT) + target_include_directories(MCUBOOT_BOOTUTIL INTERFACE + ../../../ext/tinycrypt/lib/include + ) +endif() + +if(CONFIG_BOOT_USE_PSA_CRYPTO) + target_include_directories(MCUBOOT_BOOTUTIL INTERFACE + ${ZEPHYR_MBEDTLS_MODULE_DIR}/include + ) +endif() + +if(CONFIG_BOOT_USE_MBEDTLS OR CONFIG_BOOT_USE_PSA_CRYPTO AND NOT CONFIG_PSA_CORE_OBERON) + zephyr_link_libraries(mbedTLS) +endif() +endif() diff --git a/bootloader/mcuboot/boot/cypress/.gitignore b/bootloader/mcuboot/boot/cypress/.gitignore new file mode 100644 index 0000000..9a2c99f --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/.gitignore @@ -0,0 +1,41 @@ +outdir/ +.*.swp +target.sh +*.pyc +tags +rusty-tags.* + +# mynewt +/repos/ +/project.state +/bin/ +/targets/ +**/build/**/* + +#Eclipse project files +.cproject +.project + +# Compiled python modules. +*.pyc + +# Setuptools distribution folder. +/scripts/dist/ + +# Python egg metadata, regenerated from source files by setuptools. +/scripts/*.egg-info +/scripts/*.egg + +# Build dirs +*out/*/* +*out/obj/* + +# Build files +*.o +*.d +*.map +*.elf +*.bin +*.hex +*.log +*.lst \ No newline at end of file diff --git a/bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp.mk b/bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp.mk new file mode 100644 index 0000000..5ff2080 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp.mk @@ -0,0 +1,132 @@ +################################################################################ +# \file BlinkyApp.mk +# \version 1.0 +# +# \brief +# Makefile to describe demo application BlinkyApp for Cypress MCUBoot based applications. +# +################################################################################ +# \copyright +# Copyright 2018-2019 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +include host.mk + +# Cypress' MCUBoot Application supports GCC ARM only at this moment +# Set defaults to: +# - compiler GCC +# - build configuration to Debug +# - image type to BOOT +COMPILER ?= GCC_ARM +IMG_TYPE ?= BOOT + +# For which core this application is built +CORE ?= CM4 + +# image type can be BOOT or UPGRADE +IMG_TYPES = BOOT UPGRADE + +# possible values are 0 and 0xff +# internal Flash by default +ERASED_VALUE ?= 0 + +ifneq ($(COMPILER), GCC_ARM) +$(error Only GCC ARM is supported at this moment) +endif + +CUR_APP_PATH = $(PRJ_DIR)/$(APP_NAME) + +include $(PRJ_DIR)/platforms.mk +include $(PRJ_DIR)/common_libs.mk +include $(PRJ_DIR)/toolchains.mk + +# Application-specific DEFINES +ifeq ($(IMG_TYPE), BOOT) + DEFINES_APP := -DBOOT_IMG +else + DEFINES_APP := -DUPGRADE_IMG +endif + +# Define start of application, RAM start and size, slot size +ifeq ($(PLATFORM), PSOC_062_2M) + DEFINES_APP += -DRAM_START=0x08040000 + DEFINES_APP += -DRAM_SIZE=0x10000 +else ifeq ($(PLATFORM), PSOC_062_1M) + DEFINES_APP += -DRAM_START=0x08020000 + DEFINES_APP += -DRAM_SIZE=0x10000 +else ifeq ($(PLATFORM), PSOC_062_512K) + DEFINES_APP += -DRAM_START=0x08020000 + DEFINES_APP += -DRAM_SIZE=0x10000 +endif + + +DEFINES_APP += -DRAM_SIZE=0x10000 +DEFINES_APP += -DUSER_APP_START=0x10018000 +SLOT_SIZE ?= 0x10000 + + +# Collect Test Application sources +SOURCES_APP_SRC := $(wildcard $(CUR_APP_PATH)/*.c) +# Collect all the sources +SOURCES_APP += $(SOURCES_APP_SRC) + +# Collect includes for BlinkyApp +INCLUDE_DIRS_APP := $(addprefix -I, $(CURDIR)) +INCLUDE_DIRS_APP += $(addprefix -I, $(CUR_APP_PATH)) + +# Overwite path to linker script if custom is required, otherwise default from BSP is used +ifeq ($(COMPILER), GCC_ARM) +LINKER_SCRIPT := $(subst /cygdrive/c,c:,$(CUR_APP_PATH)/linker/$(APP_NAME).ld) +else +$(error Only GCC ARM is supported at this moment) +endif + +ASM_FILES_APP := +ASM_FILES_APP += $(ASM_FILES_STARTUP) + +# We still need this for MCUBoot apps signing +IMGTOOL_PATH ?= ../../scripts/imgtool.py + +SIGN_ARGS := sign --header-size 1024 --pad-header --align 8 -v "2.0" -S $(SLOT_SIZE) -M 512 --overwrite-only -R $(ERASED_VALUE) -k keys/$(SIGN_KEY_FILE).pem + +# Output folder +OUT := $(APP_NAME)/out +# Output folder to contain build artifacts +OUT_TARGET := $(OUT)/$(PLATFORM) + +OUT_CFG := $(OUT_TARGET)/$(BUILDCFG) + +# Set build directory for BOOT and UPGRADE images +ifeq ($(IMG_TYPE), UPGRADE) + ifeq ($(ENC_IMG), 1) + SIGN_ARGS += --encrypt ../../$(ENC_KEY_FILE).pem + endif + SIGN_ARGS += --pad + UPGRADE_SUFFIX :=_upgrade + OUT_CFG := $(OUT_CFG)/upgrade +else + OUT_CFG := $(OUT_CFG)/boot +endif + +pre_build: + $(info [PRE_BUILD] - Generating linker script for application $(CUR_APP_PATH)/linker/$(APP_NAME).ld) + @$(CC) -E -x c $(CFLAGS) $(INCLUDE_DIRS) $(CUR_APP_PATH)/linker/$(APP_NAME)_template.ld | grep -v '^#' >$(CUR_APP_PATH)/linker/$(APP_NAME).ld + +# Post build action to execute after main build job +post_build: $(OUT_CFG)/$(APP_NAME).hex + $(info [POST_BUILD] - Executing post build script for $(APP_NAME)) + mv -f $(OUT_CFG)/$(APP_NAME).hex $(OUT_CFG)/$(APP_NAME)_unsigned.hex + $(PYTHON_PATH) $(IMGTOOL_PATH) $(SIGN_ARGS) $(OUT_CFG)/$(APP_NAME)_unsigned.hex $(OUT_CFG)/$(APP_NAME)$(UPGRADE_SUFFIX).hex diff --git a/bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp_CM4_Debug.launch b/bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp_CM4_Debug.launch new file mode 100644 index 0000000..34a173b --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/BlinkyApp/BlinkyApp_CM4_Debug.launch @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bootloader/mcuboot/boot/cypress/BlinkyApp/Readme.md b/bootloader/mcuboot/boot/cypress/BlinkyApp/Readme.md new file mode 100644 index 0000000..ba5340e --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/BlinkyApp/Readme.md @@ -0,0 +1,165 @@ +### Blinking LED test application for MCUboot bootloader + +### Description + +Implements simple Blinky LED CM4 application to demonstrate MCUboot Application operation in terms of BOOT and UPGRADE process. + +It is started by MCUboot Application which is running on CM0p. + +Functionality: + +* Blinks RED led with 2 different rates, depending on type of image - BOOT or UPGRADE. +* Prints debug info and version of itself to terminal at 115200 baud. +* Can be built for BOOT slot or UPGRADE slot of bootloader. + +Currently supported platforms + +* PSOC_062_2M +* PSOC_062_1M +* PSOC_062_512K + +### Hardware limitations + +Since this application is created to demonstrate MCUboot library features and not as reference examples some considerations are taken. + +1. Port/pin `P5_0` and `P5_1` used to configure serial port for debug prints. These pins are the most commonly used for serial port connection among available Cypress PSoC 6 kits. If you try to use custom hardware with this application - change definitions of `CY_DEBUG_UART_TX` and `CY_DEBUG_UART_RX` in `main.c` of BlinkyApp to port/pin pairs corresponding to your design. +2. Port `GPIO_PRT13` pin `7U` used to define user connection LED. This pin is the most commonly used for USER_LED connection among available Cypress PSoC 6 kits. If you try to use custom hardware with this application - change definitions of `LED_PORT` and `LED_PIN` in `main.c` of BlinkyApp to port/pin pairs corresponding to your design. + +### Pre-build action + +Pre-build action is implemented for defining start address and size of flash, as well as RAM start address and size for BlinkyApp. +These values are set by specifing following macros: `-DUSER_APP_SIZE`, `-DUSER_APP_START`, `-DRAM_SIZE`, `-DRAM_START` in makefile. + +Pre-build action calls GCC preprocessor which intantiates defines for particular values in `BlinkyApp_template.ld`. + +Default values set for currently supported targets: +* `BlinkyApp.mk` to `-DUSER_APP_START=0x10018000` + +**Important**: make sure RAM areas of CM4-based BlinkyApp and CM0p-based MCUBootApp bootloader do not overlap. +Memory (stack) corruption of CM0p application can cause failure if SystemCall-served operations invoked from CM4. + +### Building an application + +Root directory for build is **boot/cypress.** + +The following command will build regular HEX file of a BlinkyApp for BOOT slot. Substitute `PLATFORM=` to a paltform name you use in all following commands. + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=BOOT + +This have following defaults suggested: + + BUILDCFG=Debug + IMG_TYPE=BOOT + +To build UPGRADE image use following command: + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=UPGRADE HEADER_OFFSET=0x10000 + + Note: HEADER_OFFSET=%SLOT_SIZE% + +Example command-line for single-image: + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=BOOT + +**Building Multi-Image** + +`BlinkyApp` can be built to use in multi-image bootloader configuration. + +To get appropriate artifacts to use with multi image MCUBootApp, makefile flag `HEADER_OFFSET=` can be used. + +Example usage: + +Considering default config: + +* first image BOOT (PRIMARY) slot start `0x10018000` +* slot size `0x10000` +* second image BOOT (PRIMARY) slot start `0x10038000` + +To get appropriate artifact for second image PRIMARY slot run this command: + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=BOOT HEADER_OFFSET=0x20000 + +*Note:* only 2 images are supported at the moment. + +**How to build upgrade image for external memory:** + +To prepare MCUBootApp for work with external memory please refer to `MCUBootApp/ExternalMemory.md`. + +For build BlinkyApp upgrade image for external memory use command: + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=UPGRADE HEADER_OFFSET=0x7FE8000 ERASED_VALUE=0xff + +`HEADER_OFFSET` defines the offset from original boot image address. This one in line above suggests secondary slot will start from `0x18000000`. + +`ERASED_VALUE` defines the memory cell contents in erased state. It is `0x00` for PSoC6's internal Flash and `0xff` for S25FL512S. + +In case of using muti-image configuration, upgrade image for second application can be built using next command: + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=UPGRADE HEADER_OFFSET=0x8028000 ERASED_VALUE=0xff + + Note: for S25FL512S block address shuld be mutiple by 0x40000 + +**How to build encrypted upgrade image :** + +To prepare MCUBootApp for work with encrypted upgrade image please refer to `MCUBootApp/Readme.md`. + +To obtain encrypted upgrade image of BlinkyApp extra flag `ENC_IMG=1` should be passed in command line, for example: + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=UPGRADE HEADER_OFFSET=0x20000 ENC_IMG=1 + +This also suggests user already placed corresponing `*.pem` key in `\keys` folder. The key variables are defined in root `Makefile` as `SIGN_KEY_FILE` and `ENC_KEY_FILE` + +### Post-build + +Post build action is executed at compile time for `BlinkyApp`. In case of build for `PSOC_062_2M` platform it calls `imgtool` from `MCUboot` scripts and adds signature to compiled image. + +Flags passed to `imgtool` for signature are defined in `SIGN_ARGS` variable in BlinkyApp.mk. + +### How to program an application + +Use any preferred tool for programming hex files. + +Hex file names to use for programming: + +`BlinkyApp` always produce build artifacts in 2 separate folders - `boot` and `upgrade`. + +`BlinkyApp` built to run with `MCUBootApp` produces files with name BlinkyApp.hex in `boot` directory and `BlinkyApp_upgrade.hex` in `upgrade` folder. These files are ready to be flashed to the board. + +`BlinkyApp_unsigned.hex` hex file is also preserved in both cases for possible troubleshooting. + +Files to use for programming are: + +`BOOT` - boot/BlinkyApp.hex +`UPGRADE` - upgrade/BlinkyApp_upgrade.hex + +**Flags:** +- `BUILDCFG` - configuration **Release** or **Debug** +- `MAKEINFO` - 0 (default) - less build info, 1 - verbose output of compilation. +- `HEADER_OFFSET` - 0 (default) - no offset of output hex file, 0x%VALUE% - offset for output hex file. Value 0x10000 is slot size MCUboot Bootloader in this example. +- `IMG_TYPE` - `BOOT` (default) - build image for BOOT slot of MCUboot Bootloader, `UPGRADE` - build image for UPGRADE slot of MCUboot Bootloader. +- `ENC_IMG` - 0 (default) - build regular upgrade image, `1` - build encrypted upgrade image (MCUBootApp should also be built with this flash set 1) + +**NOTE**: In case of `UPGRADE` image `HEADER_OFFSET` should be set to MCUboot Bootloader slot size. + +### Example terminal output + +When user application programmed in BOOT slot: + + =========================== + [BlinkyApp] BlinkyApp v1.0 [CM4] + =========================== + [BlinkyApp] GPIO initialized + [BlinkyApp] UART initialized + [BlinkyApp] Retarget I/O set to 115200 baudrate + [BlinkyApp] Red led blinks with 1 sec period + +When user application programmed in UPRADE slot and upgrade procedure was successful: + + =========================== + [BlinkyApp] BlinkyApp v2.0 [+] + =========================== + + [BlinkyApp] GPIO initialized + [BlinkyApp] UART initialized + [BlinkyApp] Retarget I/O set to 115200 baudrate + [BlinkyApp] Red led blinks with 0.25 sec period diff --git a/bootloader/mcuboot/boot/cypress/BlinkyApp/libs.mk b/bootloader/mcuboot/boot/cypress/BlinkyApp/libs.mk new file mode 100644 index 0000000..7994d13 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/BlinkyApp/libs.mk @@ -0,0 +1,60 @@ +################################################################################ +# \file libs.mk +# \version 1.0 +# +# \brief +# Makefile to describe libraries needed for Cypress MCUBoot based applications. +# +################################################################################ +# \copyright +# Copyright 2018-2019 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +################################################################################ +# PDL library +################################################################################ +PDL_VERSION = 121 +# +CUR_LIBS_PATH = $(PRJ_DIR)/libs + +# Collect source files for Retarget-io +SOURCES_RETARGET_IO := $(wildcard $(CUR_LIBS_PATH)/retarget-io/*.c) +SOURCES_WATCHDOG := $(wildcard $(CUR_LIBS_PATH)/watchdog/*.c) + +# Collect source files for HAL +SOURCES_HAL := $(wildcard $(CUR_LIBS_PATH)/psoc6hal/COMPONENT_PSOC6HAL/source/*.c) +SOURCES_HAL += $(wildcard $(CUR_LIBS_PATH)/psoc6hal/COMPONENT_PSOC6HAL/source/triggers/*.c) +SOURCES_HAL += $(wildcard $(CUR_LIBS_PATH)/psoc6hal/COMPONENT_PSOC6HAL/source/pin_packages/*.c) + +# Retarget-io related include directories +INCLUDE_DIRS_RETARGET_IO := $(CUR_LIBS_PATH)/retarget-io +INCLUDE_DIRS_WATCHDOG := $(CUR_LIBS_PATH)/watchdog + +# Collect dirrectories containing headers for PSOC6 HAL +INCLUDE_DIRS_HAL := $(CUR_LIBS_PATH)/psoc6hal/include +INCLUDE_DIRS_HAL += $(CUR_LIBS_PATH)/psoc6hal/COMPONENT_PSOC6HAL/include +INCLUDE_DIRS_HAL += $(CUR_LIBS_PATH)/psoc6hal/COMPONENT_PSOC6HAL/include/pin_packages +INCLUDE_DIRS_HAL += $(CUR_LIBS_PATH)/psoc6hal/COMPONENT_PSOC6HAL/include/triggers + +# Collected source files for libraries +SOURCES_LIBS += $(SOURCES_RETARGET_IO) +SOURCES_LIBS += $(SOURCES_WATCHDOG) +SOURCES_LIBS += $(SOURCES_HAL) + +# Collected include directories for libraries +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_RETARGET_IO)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_WATCHDOG)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_HAL)) diff --git a/bootloader/mcuboot/boot/cypress/BlinkyApp/linker/BlinkyApp_template.ld b/bootloader/mcuboot/boot/cypress/BlinkyApp/linker/BlinkyApp_template.ld new file mode 100644 index 0000000..81fbc22 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/BlinkyApp/linker/BlinkyApp_template.ld @@ -0,0 +1,425 @@ +/***************************************************************************//** +* \file cy8c6xxa_cm4_dual.ld +* \version 2.60 +* +* Linker file for the GNU C compiler. +* +* The main purpose of the linker script is to describe how the sections in the +* input files should be mapped into the output file, and to control the memory +* layout of the output file. +* +* \note The entry point location is fixed and starts at 0x10000000. The valid +* application image should be placed there. +* +* \note The linker files included with the PDL template projects must be generic +* and handle all common use cases. Your project may not use every section +* defined in the linker files. In that case you may see warnings during the +* build process. In your project, you can simply comment out or remove the +* relevant code in the linker file. +* +******************************************************************************** +* \copyright +* Copyright 2016-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ +#include + +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +SEARCH_DIR(.) +GROUP(-lgcc -lc -lnosys) +ENTRY(Reset_Handler) + +/* Size of the stack section at the end of CM4 SRAM */ +STACK_SIZE = 0x1000; + +/* The size of the MCU boot header area at the start of FLASH */ +BOOT_HEADER_SIZE = 0x400; + +/* Force symbol to be entered in the output file as an undefined symbol. Doing +* this may, for example, trigger linking of additional modules from standard +* libraries. You may list several symbols for each EXTERN, and you may use +* EXTERN multiple times. This command has the same effect as the -u command-line +* option. +*/ +EXTERN(Reset_Handler) + +/* The MEMORY section below describes the location and size of blocks of memory in the target. +* Use this section to specify the memory regions available for allocation. +*/ +MEMORY +{ + /* The ram and flash regions control RAM and flash memory allocation for the CM4 core. + * You can change the memory allocation by editing the 'ram' and 'flash' regions. + * Note that 2 KB of RAM (at the end of the SRAM) are reserved for system use. + * Using this memory region for other purposes will lead to unexpected behavior. + * Your changes must be aligned with the corresponding memory regions for CM0+ core in 'xx_cm0plus.ld', + * where 'xx' is the device group; for example, 'cy8c6xx7_cm0plus.ld'. + */ + ram (rwx) : ORIGIN = RAM_START, LENGTH = RAM_SIZE + flash (rx) : ORIGIN = USER_APP_START, LENGTH = USER_APP_SIZE + + /* This is a 32K flash region used for EEPROM emulation. This region can also be used as the general purpose flash. + * You can assign sections to this memory region for only one of the cores. + * Note some middleware (e.g. BLE, Emulated EEPROM) can place their data into this memory region. + * Therefore, repurposing this memory region will prevent such middleware from operation. + */ + em_eeprom (rx) : ORIGIN = 0x14000000, LENGTH = 0x8000 /* 32 KB */ + + /* The following regions define device specific memory regions and must not be changed. */ + sflash_user_data (rx) : ORIGIN = 0x16000800, LENGTH = 0x800 /* Supervisory flash: User data */ + sflash_nar (rx) : ORIGIN = 0x16001A00, LENGTH = 0x200 /* Supervisory flash: Normal Access Restrictions (NAR) */ + sflash_public_key (rx) : ORIGIN = 0x16005A00, LENGTH = 0xC00 /* Supervisory flash: Public Key */ + sflash_toc_2 (rx) : ORIGIN = 0x16007C00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 */ + sflash_rtoc_2 (rx) : ORIGIN = 0x16007E00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 Copy */ + xip (rx) : ORIGIN = 0x18000000, LENGTH = 0x8000000 /* 128 MB */ + efuse (r) : ORIGIN = 0x90700000, LENGTH = 0x100000 /* 1 MB */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ + + +SECTIONS +{ + /* Cortex-M4 application flash area */ + .text ORIGIN(flash) + BOOT_HEADER_SIZE : + { + . = ALIGN(4); + __Vectors = . ; + KEEP(*(.vectors)) + . = ALIGN(4); + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + . = ALIGN(4); + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + /* Read-only code (constants). */ + *(.rodata .rodata.* .constdata .constdata.* .conststring .conststring.*) + + KEEP(*(.eh_frame*)) + } > flash + + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > flash + + __exidx_start = .; + + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > flash + __exidx_end = .; + + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_psoc6_02_cm4.S */ + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + + /* Copy interrupt vectors from flash to RAM */ + LONG (__Vectors) /* From */ + LONG (__ram_vectors_start__) /* To */ + LONG (__Vectors_End - __Vectors) /* Size */ + + /* Copy data section to RAM */ + LONG (__etext) /* From */ + LONG (__data_start__) /* To */ + LONG (__data_end__ - __data_start__) /* Size */ + + __copy_table_end__ = .; + } > flash + + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_psoc6_02_cm4.S */ + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + __zero_table_end__ = .; + } > flash + + __etext = . ; + + + .ramVectors (NOLOAD) : ALIGN(8) + { + __ram_vectors_start__ = .; + KEEP(*(.ram_vectors)) + __ram_vectors_end__ = .; + } > ram + + + .data __ram_vectors_end__ : AT (__etext) + { + __data_start__ = .; + + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + + KEEP(*(.cy_ramfunc*)) + . = ALIGN(4); + + __data_end__ = .; + + } > ram + + + /* Place variables in the section that should not be initialized during the + * device startup. + */ + .noinit (NOLOAD) : ALIGN(8) + { + KEEP(*(.noinit)) + } > ram + + + /* The uninitialized global or static variables are placed in this section. + * + * The NOLOAD attribute tells linker that .bss section does not consume + * any space in the image. The NOLOAD attribute changes the .bss type to + * NOBITS, and that makes linker to A) not allocate section in memory, and + * A) put information to clear the section with all zeros during application + * loading. + * + * Without the NOLOAD attribute, the .bss section might get PROGBITS type. + * This makes linker to A) allocate zeroed section in memory, and B) copy + * this section to RAM during application loading. + */ + .bss (NOLOAD): + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > ram + + + .heap (NOLOAD): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + . = ORIGIN(ram) + LENGTH(ram) - STACK_SIZE; + __HeapLimit = .; + } > ram + + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > ram + + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(ram) + LENGTH(ram); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + + + /* Used for the digital signature of the secure application and the Bootloader SDK application. + * The size of the section depends on the required data size. */ + .cy_app_signature ORIGIN(flash) + LENGTH(flash) - 256 : + { + KEEP(*(.cy_app_signature)) + } > flash + + + /* Emulated EEPROM Flash area */ + .cy_em_eeprom : + { + KEEP(*(.cy_em_eeprom)) + } > em_eeprom + + + /* Supervisory Flash: User data */ + .cy_sflash_user_data : + { + KEEP(*(.cy_sflash_user_data)) + } > sflash_user_data + + + /* Supervisory Flash: Normal Access Restrictions (NAR) */ + .cy_sflash_nar : + { + KEEP(*(.cy_sflash_nar)) + } > sflash_nar + + + /* Supervisory Flash: Public Key */ + .cy_sflash_public_key : + { + KEEP(*(.cy_sflash_public_key)) + } > sflash_public_key + + + /* Supervisory Flash: Table of Content # 2 */ + .cy_toc_part2 : + { + KEEP(*(.cy_toc_part2)) + } > sflash_toc_2 + + + /* Supervisory Flash: Table of Content # 2 Copy */ + .cy_rtoc_part2 : + { + KEEP(*(.cy_rtoc_part2)) + } > sflash_rtoc_2 + + + /* Places the code in the Execute in Place (XIP) section. See the smif driver + * documentation for details. + */ + .cy_xip : + { + KEEP(*(.cy_xip)) + } > xip + + + /* eFuse */ + .cy_efuse : + { + KEEP(*(.cy_efuse)) + } > efuse + + + /* These sections are used for additional metadata (silicon revision, + * Silicon/JTAG ID, etc.) storage. + */ + .cymeta 0x90500000 : { KEEP(*(.cymeta)) } :NONE +} + + +/* The following symbols used by the cymcuelftool. */ +/* Flash */ +__cy_memory_0_start = 0x10000000; +__cy_memory_0_length = 0x00200000; +__cy_memory_0_row_size = 0x200; + +/* Emulated EEPROM Flash area */ +__cy_memory_1_start = 0x14000000; +__cy_memory_1_length = 0x8000; +__cy_memory_1_row_size = 0x200; + +/* Supervisory Flash */ +__cy_memory_2_start = 0x16000000; +__cy_memory_2_length = 0x8000; +__cy_memory_2_row_size = 0x200; + +/* XIP */ +__cy_memory_3_start = 0x18000000; +__cy_memory_3_length = 0x08000000; +__cy_memory_3_row_size = 0x200; + +/* eFuse */ +__cy_memory_4_start = 0x90700000; +__cy_memory_4_length = 0x100000; +__cy_memory_4_row_size = 1; + +/* EOF */ diff --git a/bootloader/mcuboot/boot/cypress/BlinkyApp/main.c b/bootloader/mcuboot/boot/cypress/BlinkyApp/main.c new file mode 100644 index 0000000..97e1c84 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/BlinkyApp/main.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2020 Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /*******************************************************************************/ + +#include "system_psoc6.h" +#include "cy_pdl.h" +#include "cyhal.h" +#include "cy_retarget_io.h" +#include "watchdog.h" + +/* Define pins for UART debug output */ + +#define CY_DEBUG_UART_TX (P5_1) +#define CY_DEBUG_UART_RX (P5_0) + +#if defined(PSOC_062_2M) +#warning "Check if User LED is correct for your target board." +#define LED_PORT GPIO_PRT13 +#define LED_PIN 7U +#elif defined(PSOC_062_1M) +#define LED_PORT GPIO_PRT13 +#define LED_PIN 7U +#elif defined(PSOC_062_512K) +#define LED_PORT GPIO_PRT11 +#define LED_PIN 1U +#endif + +#define LED_NUM 5U +#define LED_DRIVEMODE CY_GPIO_DM_STRONG_IN_OFF +#define LED_INIT_DRIVESTATE 1 + +const cy_stc_gpio_pin_config_t LED_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG_IN_OFF, + .hsiom = HSIOM_SEL_GPIO, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_FULL, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; + +#define WATCHDOG_UPD_MESSAGE "[BlinkyApp] Update watchdog timer started in MCUBootApp to mark successful start of user app\r\n" +#define WATCHDOG_FREE_MESSAGE "[BlinkyApp] Turn off watchdog timer\r\n" + +#ifdef BOOT_IMG + #define BLINK_PERIOD (1000u) + #define GREETING_MESSAGE_VER "[BlinkyApp] BlinkyApp v1.0 [CM4]\r\n" + #define GREETING_MESSAGE_INFO "[BlinkyApp] Red led blinks with 1 sec period\r\n" +#elif defined(UPGRADE_IMG) + #define BLINK_PERIOD (250u) + #define GREETING_MESSAGE_VER "[BlinkyApp] BlinkyApp v2.0 [+]\r\n" + #define GREETING_MESSAGE_INFO "[BlinkyApp] Red led blinks with 0.25 sec period\r\n" +#else + #error "[BlinkyApp] Please specify type of image: -DBOOT_IMG or -DUPGRADE_IMG\r\n" +#endif + +void check_result(int res) +{ + if (res != CY_RSLT_SUCCESS) { + CY_ASSERT(0); + } +} + +void test_app_init_hardware(void) +{ + /* enable interrupts */ + __enable_irq(); + + /* Disabling watchdog so it will not interrupt normal flow later */ + Cy_GPIO_Pin_Init(LED_PORT, LED_PIN, &LED_config); + /* Initialize retarget-io to use the debug UART port */ + check_result(cy_retarget_io_init(CY_DEBUG_UART_TX, CY_DEBUG_UART_RX, + CY_RETARGET_IO_BAUDRATE)); + + printf("\n===========================\r\n"); + printf(GREETING_MESSAGE_VER); + printf("===========================\r\n"); + + printf("[BlinkyApp] GPIO initialized \r\n"); + printf("[BlinkyApp] UART initialized \r\n"); + printf("[BlinkyApp] Retarget I/O set to 115200 baudrate \r\n"); + +} + +int main(void) +{ + uint32_t blinky_period = BLINK_PERIOD; + + test_app_init_hardware(); + + printf(GREETING_MESSAGE_INFO); + + /* Update watchdog timer to mark successful start up of application */ + printf(WATCHDOG_UPD_MESSAGE); + cy_wdg_kick(); + printf(WATCHDOG_FREE_MESSAGE); + cy_wdg_free(); + + for (;;) + { + /* Toggle the user LED periodically */ + Cy_SysLib_Delay(blinky_period/2); + + /* Invert the USER LED state */ + Cy_GPIO_Inv(LED_PORT, LED_PIN); + } + return 0; +} diff --git a/bootloader/mcuboot/boot/cypress/BlinkyApp/main.h b/bootloader/mcuboot/boot/cypress/BlinkyApp/main.h new file mode 100644 index 0000000..4fafb4e --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/BlinkyApp/main.h @@ -0,0 +1,25 @@ +/* +\copyright +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#ifndef USER_APP_START +#define USER_APP_START 0x10000000 +#endif + +#ifndef USER_APP_SIZE +#define USER_APP_SIZE 0x10000 +#endif \ No newline at end of file diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/ExternalMemory.md b/bootloader/mcuboot/boot/cypress/MCUBootApp/ExternalMemory.md new file mode 100644 index 0000000..e1281c8 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/ExternalMemory.md @@ -0,0 +1,100 @@ +### External Memory support for Secondary Slot + +**Description** + +Given document describes the use of external memory module as a secondary (upgrade) slot with Cypress' PSoC6 devices. + +The demonstration device is CY8CPROTO-062-4343W board which is PSoC6 device with 2M of Flash available. +The memory module present on board is S25FL512SAGMFI010 512-Mbit external Quad SPI NOR Flash. + +Using external memory for secondary slot allows to nearly double the size of Boot Image. + +**Operation Design and Flow** + +The design is based on using SFDP command's auto-discovery functionality of memory module IC and Cypress' SMIF PDL driver. + +It is assumed that user's design meets following: +* The memory-module used is SFDP-compliant; +* There only one module is being used for secondary slot; +* Only "OWERWRITE" bootloading scheme is used; +* The address for secondary slot should start from 0x18000000. +This corresponds to PSoC6's SMIF (Serial Memory InterFace) IP block mapping. +* The slot size for upgrade slot is even (or smaller) to erase size (0x40000) of given memory module. +This requirement is accepted for code simplicity. + +The default flash map implemented is the following: + +Single-image mode. + +`[0x10000000, 0x10018000]` - MCUBootApp (bootloader) area; + +`[0x10018000, 0x10028000]` - primary slot for BlinkyApp; + +`[0x18000000, 0x18010000]` - secondary slot for BlinkyApp; + +`[0x10038000, 0x10039000]` - scratch area (not used); + +Multi(dual)-image mode. + +`[0x10000000, 0x10018000]` - MCUBootApp (bootloader) area; + +`[0x10018000, 0x10028000]` - primary1 slot for BlinkyApp; + +`[0x18000000, 0x18010000]` - secondary1 slot for BlinkyApp; + +`[0x10038000, 0x10048000]` - primary2 slot for user app ; + +`[0x18040000, 0x18050000]` - secondary2 slot for user app; + +`[0x10058000, 0x10059000]` - scratch area (not used); + +Size of slots `0x10000` - 64kB + +**Note 1**: make sure primary, secondary slot and bootloader app sizes are appropriate and correspond to flash area size defined in Applications' linker files. + +**Note 2**: make sure secondary slot start address is aligned (or smaller) to erase size (0x40000 - 256kB). + +MCUBootApp's `main.c` contains the call to Init-SFDP API which performs required GPIO configurations, SMIF IP block configurations, SFDP protocol read and memory-config structure initialization. + +After that MCUBootApp is ready to accept upgrade image from external memory module. + +Once valid upgrade image was accepted the image in external memory will be erased. + +**How to enable external memory support:** + +1. Pass `USE_EXTERNAL_FLASH=1` flag to `make` command when building MCUBootApp. +2. Navigate to `cy_flash_map.c` and check if secondary slot start address and size meet the application's needs. +3. Define which slave select is used for external memory on a board by setting `smif_id` value in `main.c`. +4. Build MCUBootApp as described in `Readme.md`. + +**Note 3**: External memory code is developed basing on PDL and can be run on CM0p core only. It may require modifications if used on CM4. + +**How to build upgrade image for external memory:** + + make app APP_NAME=BlinkyApp PLATFORM=PSOC_062_2M IMG_TYPE=UPGRADE HEADER_OFFSET=0x7FE8000 ERASED_VALUE=0xff + +`HEADER_OFFSET` defines the offset from original boot image address. This one in line above suggests secondary slot will start from `0x18000000`. + +`ERASED_VALUE` defines the memory cell contents in erased state. It is `0x00` for PSoC6's internal Flash and `0xff` for S25FL512S. + +**Programming to external memory** + +The MCUBootApp programming can be done similarly to described in `Readme.md`: + + export OPENOCD=/Applications/ModusToolbox/tools_2.1/openocd + + ${OPENOCD}/bin/openocd -s ${OPENOCD}/scripts \ + -f ${OPENOCD}/scripts/interface/kitprog3.cfg \ + -f ${OPENOCD}/scripts/target/psoc6_2m.cfg \ + -c "init; psoc6 sflash_restrictions 1" \ + -c "init; reset init; program PATH_TO_APPLICATION.hex" \ + -c "resume; reset; exit" + +There is a NULL-pointer placed for SMIF configuration pointer in TOC2 (Table Of Contents, `cy_serial_flash_prog.c`). +This is done to force CY8PROTO-062-4343W DAP Link firmware to program external memory with hardcoded values. + +1. Press SW3 Mode button on a board to switch the board into DAP Link mode. +2. Once DAP Link removable disk appeared drop (copy) the upgrade image HEX file to it. +This will invoke firmware to program external memory. + +**Note 3:** the programming of external memory is limited to S25FL512S p/n only at this moment. diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.ld b/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.ld new file mode 100644 index 0000000..c78c598 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.ld @@ -0,0 +1,418 @@ +/***************************************************************************//** +* \file cy8c6xxa_cm0plus.ld +* \version 2.60 +* +* Linker file for the GNU C compiler. +* +* The main purpose of the linker script is to describe how the sections in the +* input files should be mapped into the output file, and to control the memory +* layout of the output file. +* +* \note The entry point location is fixed and starts at 0x10000000. The valid +* application image should be placed there. +* +* \note The linker files included with the PDL template projects must be generic +* and handle all common use cases. Your project may not use every section +* defined in the linker files. In that case you may see warnings during the +* build process. In your project, you can simply comment out or remove the +* relevant code in the linker file. +* +******************************************************************************** +* \copyright +* Copyright 2016-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +SEARCH_DIR(.) +GROUP(-lgcc -lc -lnosys) +ENTRY(Reset_Handler) + +/* Size of the stack section at the end of CM0+ SRAM */ +STACK_SIZE = 0x1000; + +/* Force symbol to be entered in the output file as an undefined symbol. Doing +* this may, for example, trigger linking of additional modules from standard +* libraries. You may list several symbols for each EXTERN, and you may use +* EXTERN multiple times. This command has the same effect as the -u command-line +* option. +*/ +EXTERN(Reset_Handler) + +/* The MEMORY section below describes the location and size of blocks of memory in the target. +* Use this section to specify the memory regions available for allocation. +*/ +MEMORY +{ + /* The ram and flash regions control RAM and flash memory allocation for the CM0+ core. + * You can change the memory allocation by editing the 'ram' and 'flash' regions. + * Note that 2 KB of RAM (at the end of the SRAM) are reserved for system use. + * Using this memory region for other purposes will lead to unexpected behavior. + * Your changes must be aligned with the corresponding memory regions for the CM4 core in 'xx_cm4_dual.ld', + * where 'xx' is the device group; for example, 'cy8c6xx7_cm4_dual.ld'. + */ + public_ram (rw) : ORIGIN = 0x08000000, LENGTH = 0x800 + ram (rwx) : ORIGIN = 0x08000800, LENGTH = 0x1F800 + flash (rx) : ORIGIN = 0x10000000, LENGTH = 0x18000 + + /* This is a 32K flash region used for EEPROM emulation. This region can also be used as the general purpose flash. + * You can assign sections to this memory region for only one of the cores. + * Note some middleware (e.g. BLE, Emulated EEPROM) can place their data into this memory region. + * Therefore, repurposing this memory region will prevent such middleware from operation. + */ + em_eeprom (rx) : ORIGIN = 0x14000000, LENGTH = 0x8000 /* 32 KB */ + + /* The following regions define device specific memory regions and must not be changed. */ + sflash_user_data (rx) : ORIGIN = 0x16000800, LENGTH = 0x800 /* Supervisory flash: User data */ + sflash_nar (rx) : ORIGIN = 0x16001A00, LENGTH = 0x200 /* Supervisory flash: Normal Access Restrictions (NAR) */ + sflash_public_key (rx) : ORIGIN = 0x16005A00, LENGTH = 0xC00 /* Supervisory flash: Public Key */ + sflash_toc_2 (rx) : ORIGIN = 0x16007C00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 */ + sflash_rtoc_2 (rx) : ORIGIN = 0x16007E00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 Copy */ + xip (rx) : ORIGIN = 0x18000000, LENGTH = 0x8000000 /* 128 MB */ + efuse (r) : ORIGIN = 0x90700000, LENGTH = 0x100000 /* 1 MB */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ + + +SECTIONS +{ + .cy_app_header : + { + KEEP(*(.cy_app_header)) + } > flash + + /* Cortex-M0+ application flash area */ + .text : + { + . = ALIGN(4); + __Vectors = . ; + KEEP(*(.vectors)) + . = ALIGN(4); + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + . = ALIGN(4); + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + /* Read-only code (constants). */ + *(.rodata .rodata.* .constdata .constdata.* .conststring .conststring.*) + + KEEP(*(.eh_frame*)) + } > flash + + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > flash + + __exidx_start = .; + + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > flash + __exidx_end = .; + + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_psoc6_02_cm0plus.S */ + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + + /* Copy interrupt vectors from flash to RAM */ + LONG (__Vectors) /* From */ + LONG (__ram_vectors_start__) /* To */ + LONG (__Vectors_End - __Vectors) /* Size */ + + /* Copy data section to RAM */ + LONG (__etext) /* From */ + LONG (__data_start__) /* To */ + LONG (__data_end__ - __data_start__) /* Size */ + + __copy_table_end__ = .; + } > flash + + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_psoc6_02_cm0plus.S */ + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + __zero_table_end__ = .; + } > flash + + __etext = . ; + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(ram) + LENGTH(ram); + __StackLimit = __StackTop - STACK_SIZE ; + PROVIDE(__stack = __StackTop); + + .stackSpace (NOLOAD) : ALIGN(8) + { + . = . + STACK_SIZE ; + } > ram + + .ramVectors (NOLOAD) : ALIGN(8) + { + __ram_vectors_start__ = .; + KEEP(*(.ram_vectors)) + __ram_vectors_end__ = .; + } > ram + + /* Unprotected public RAM */ + .cy_sharedmem (NOLOAD): + { + . = ALIGN(4); + __public_ram_start__ = .; + KEEP(*(.cy_sharedmem)) + . = ALIGN(4); + __public_ram_end__ = .; + } > public_ram + + .data __ram_vectors_end__ : AT (__etext) + { + __data_start__ = .; + + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + + KEEP(*(.cy_ramfunc*)) + . = ALIGN(4); + + __data_end__ = .; + + } > ram + + + /* Place variables in the section that should not be initialized during the + * device startup. + */ + .noinit (NOLOAD) : ALIGN(8) + { + KEEP(*(.noinit)) + } > ram + + + /* The uninitialized global or static variables are placed in this section. + * + * The NOLOAD attribute tells linker that .bss section does not consume + * any space in the image. The NOLOAD attribute changes the .bss type to + * NOBITS, and that makes linker to A) not allocate section in memory, and + * A) put information to clear the section with all zeros during application + * loading. + * + * Without the NOLOAD attribute, the .bss section might get PROGBITS type. + * This makes linker to A) allocate zeroed section in memory, and B) copy + * this section to RAM during application loading. + */ + .bss (NOLOAD): + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > ram + + .heap (NOLOAD): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + . = ORIGIN(ram) + LENGTH(ram); + __HeapLimit = .; + } > ram + + + /* Emulated EEPROM Flash area */ + .cy_em_eeprom : + { + KEEP(*(.cy_em_eeprom)) + } > em_eeprom + + + /* Supervisory Flash: User data */ + .cy_sflash_user_data : + { + KEEP(*(.cy_sflash_user_data)) + } > sflash_user_data + + + /* Supervisory Flash: Normal Access Restrictions (NAR) */ + .cy_sflash_nar : + { + KEEP(*(.cy_sflash_nar)) + } > sflash_nar + + + /* Supervisory Flash: Public Key */ + .cy_sflash_public_key : + { + KEEP(*(.cy_sflash_public_key)) + } > sflash_public_key + + + /* Supervisory Flash: Table of Content # 2 */ + .cy_toc_part2 : + { + KEEP(*(.cy_toc_part2)) + } > sflash_toc_2 + + + /* Supervisory Flash: Table of Content # 2 Copy */ + .cy_rtoc_part2 : + { + KEEP(*(.cy_rtoc_part2)) + } > sflash_rtoc_2 + + + /* Places the code in the Execute in Place (XIP) section. See the smif driver + * documentation for details. + */ + .cy_xip : + { + KEEP(*(.cy_xip)) + } > xip + + + /* eFuse */ + .cy_efuse : + { + KEEP(*(.cy_efuse)) + } > efuse + + + /* These sections are used for additional metadata (silicon revision, + * Silicon/JTAG ID, etc.) storage. + */ + .cymeta 0x90500000 : { KEEP(*(.cymeta)) } :NONE +} + + +/* The following symbols used by the cymcuelftool. */ +/* Flash */ +__cy_memory_0_start = 0x10000000; +__cy_memory_0_length = 0x00200000; +__cy_memory_0_row_size = 0x200; + +/* Emulated EEPROM Flash area */ +__cy_memory_1_start = 0x14000000; +__cy_memory_1_length = 0x8000; +__cy_memory_1_row_size = 0x200; + +/* Supervisory Flash */ +__cy_memory_2_start = 0x16000000; +__cy_memory_2_length = 0x8000; +__cy_memory_2_row_size = 0x200; + +/* XIP */ +__cy_memory_3_start = 0x18000000; +__cy_memory_3_length = 0x08000000; +__cy_memory_3_row_size = 0x200; + +/* eFuse */ +__cy_memory_4_start = 0x90700000; +__cy_memory_4_length = 0x100000; +__cy_memory_4_row_size = 1; + +/* EOF */ diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.mk b/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.mk new file mode 100644 index 0000000..bba1160 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp.mk @@ -0,0 +1,113 @@ +################################################################################ +# \file MCUBootApp.mk +# \version 1.0 +# +# \brief +# Makefile for Cypress MCUBoot-based application. +# +################################################################################ +# \copyright +# Copyright 2018-2019 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +include host.mk + +# Cypress' MCUBoot Application supports GCC ARM only at this moment +# Set default compiler to GCC if not specified from command line +COMPILER ?= GCC_ARM + +USE_CRYPTO_HW ?= 0 +USE_EXTERNAL_FLASH ?= 0 +MCUBOOT_IMAGE_NUMBER ?= 1 +ENC_IMG ?= 0 + +# For which core this application is built +CORE ?= CM0P + +ifneq ($(COMPILER), GCC_ARM) +$(error Only GCC ARM is supported at this moment) +endif + +CUR_APP_PATH = $(PRJ_DIR)/$(APP_NAME) + +include $(PRJ_DIR)/platforms.mk +include $(PRJ_DIR)/common_libs.mk +include $(PRJ_DIR)/toolchains.mk + +# default slot size is 0x10000, 512bytes per row/sector, so 128 sectors +MAX_IMG_SECTORS ?= 128 + +# Application-specific DEFINES +DEFINES_APP := -DMBEDTLS_CONFIG_FILE="\"mcuboot_crypto_config.h\"" +DEFINES_APP += -DECC256_KEY_FILE="\"keys/$(SIGN_KEY_FILE).pub\"" +DEFINES_APP += -DCORE=$(CORE) +DEFINES_APP += -DMCUBOOT_IMAGE_NUMBER=$(MCUBOOT_IMAGE_NUMBER) +ifeq ($(USE_EXTERNAL_FLASH), 1) +DEFINES_APP += -DCY_BOOT_USE_EXTERNAL_FLASH +endif +DEFINES_APP += -DMCUBOOT_MAX_IMG_SECTORS=$(MAX_IMG_SECTORS) +# Hardrware acceleration support +ifeq ($(USE_CRYPTO_HW), 1) +DEFINES_APP += -DMBEDTLS_USER_CONFIG_FILE="\"mcuboot_crypto_acc_config.h\"" +DEFINES_APP += -DCY_CRYPTO_HAL_DISABLE +DEFINES_APP += -DCY_MBEDTLS_HW_ACCELERATION +endif +# Encrypted image support +ifeq ($(ENC_IMG), 1) +DEFINES_APP += -DENC_IMG=1 +endif + +# Collect MCUBoot sourses +SOURCES_MCUBOOT := $(wildcard $(CURDIR)/../bootutil/src/*.c) +# Collect MCUBoot Application sources +SOURCES_APP_SRC := $(wildcard $(CUR_APP_PATH)/*.c) + +# Collect Flash Layer port sources +SOURCES_FLASH_PORT := $(wildcard $(CURDIR)/cy_flash_pal/*.c) +SOURCES_FLASH_PORT += $(wildcard $(CURDIR)/cy_flash_pal/flash_qspi/*.c) +# Collect all the sources +SOURCES_APP := $(SOURCES_MCUBOOT) +SOURCES_APP += $(SOURCES_APP_SRC) +SOURCES_APP += $(SOURCES_FLASH_PORT) + +INCLUDE_DIRS_MCUBOOT := $(addprefix -I, $(CURDIR)/../bootutil/include) +INCLUDE_DIRS_MCUBOOT += $(addprefix -I, $(CURDIR)/../bootutil/src) +INCLUDE_DIRS_MCUBOOT += $(addprefix -I, $(CURDIR)/..) + +INCLUDE_DIRS_APP := $(addprefix -I, $(CURDIR)) +INCLUDE_DIRS_APP += $(addprefix -I, $(CURDIR)/cy_flash_pal/flash_qspi) +INCLUDE_DIRS_APP += $(addprefix -I, $(CURDIR)/cy_flash_pal/include) +INCLUDE_DIRS_APP += $(addprefix -I, $(CURDIR)/cy_flash_pal/include/flash_map_backend) +INCLUDE_DIRS_APP += $(addprefix -I, $(CUR_APP_PATH)) +INCLUDE_DIRS_APP += $(addprefix -I, $(CUR_APP_PATH)/config) +INCLUDE_DIRS_APP += $(addprefix -I, $(CUR_APP_PATH)/os) + +ASM_FILES_APP := +ASM_FILES_APP += $(ASM_FILES_STARTUP) + +# Output folder +OUT := $(APP_NAME)/out +# Output folder to contain build artifacts +OUT_TARGET := $(OUT)/$(PLATFORM) + +OUT_CFG := $(OUT_TARGET)/$(BUILDCFG) + +# Overwite path to linker script if custom is required +ifeq ($(COMPILER), GCC_ARM) +LINKER_SCRIPT := $(subst /cygdrive/c,c:,$(CUR_APP_PATH)/$(APP_NAME).ld) +else +$(error Only GCC ARM is supported at this moment) +endif \ No newline at end of file diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp_CM0P_Debug.launch b/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp_CM0P_Debug.launch new file mode 100644 index 0000000..c23487b --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/MCUBootApp_CM0P_Debug.launch @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/README.md b/bootloader/mcuboot/boot/cypress/MCUBootApp/README.md new file mode 100644 index 0000000..798d72d --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/README.md @@ -0,0 +1,221 @@ +### Port of MCUboot library to be used with Cypress targets + +**Solution Description** + +Given solution demonstrates operation of MCUboot on Cypress' PSoC6 device. + +There are two applications implemented: +* MCUBootApp - PSoC6 MCUboot-based bootloading application; +* BlinkyApp - simple PSoC6 blinking LED application which is a target of BOOT/UPGRADE; + +Cypress boards, that can be used with this evaluation example: +- CY8CPROTO-062-4343W - PSoC 6 2M on board +- CY8CKIT-062-WIFI-BT - PSoC 6 1M on board +- CY8CPROTO-062S3-4343W - PSoC 6 512K on board +The default flash map implemented is the following: + +Single-image mode. + +`[0x10000000, 0x10018000]` - MCUBootApp (bootloader) area; + +`[0x10018000, 0x10028000]` - primary slot for BlinkyApp; + +`[0x10028000, 0x10038000]` - secondary slot for BlinkyApp; + +`[0x10038000, 0x10039000]` - scratch area (not used); + +Size of slots `0x10000` - 64kb + +MCUBootApp checks image integrity with SHA256, image authenticity with EC256 digital signature verification and uses completely SW implementation of cryptographic functions based on Mbed TLS Library. + +**Important**: make sure primary, secondary slot and bootloader app sizes are appropriate and correspond to flash area size defined in Applications' linker files. + +**Important**: make sure RAM areas of CM0p-based MCUBootApp bootloader and CM4-based BlinkyApp do not overlap. +Memory (stack) corruption of CM0p application can cause failure if SystemCall-served operations invoked from CM4. + +### Hardware cryptography acceleration + +Cypress PSOC6 MCU family supports hardware acceleration of cryptography based on Mbed TLS Library via shim layer. Implementation of this layer is supplied as separate submodule `cy-mbedtls-acceleration`. HW acceleration of cryptography shortens boot time more then 4 times, comparing to software implementation (observation results). + +To enable hardware acceleration in `MCUBootApp` pass flag `USE_CRYPTO_HW=1` to `make` while build. + +Hardware acceleration of cryptography is enabled for PSOC6 devices by default. + +### How to modify memory map + +__Option 1.__ + +Navigate to `sysflash.h` and modify the flash area(s) / slots sizes to meet your needs. + +__Option 2.__ + +Navigate to `sysflash.h`, uncomment `CY_FLASH_MAP_EXT_DESC` definition. +Now define and initialize `struct flash_area *boot_area_descs[]` with flash memory addresses and sizes you need at the beginning of application, so flash APIs from `cy_flash_map.c` will use it. + +__Note:__ for both options make sure you have updated `MCUBOOT_MAX_IMG_SECTORS` appropriatery with sector size assumed to be 512. + +**How to override the flash map values during build process:** + +Navigate to MCUBootApp.mk, find section `DEFINES_APP +=` +Update this line and or add similar for flash map parameters to override. + +The possible list could be: + +* MCUBOOT_MAX_IMG_SECTORS +* CY_FLASH_MAP_EXT_DESC +* CY_BOOT_SCRATCH_SIZE +* CY_BOOT_BOOTLOADER_SIZE +* CY_BOOT_PRIMARY_1_SIZE +* CY_BOOT_SECONDARY_1_SIZE +* CY_BOOT_PRIMARY_2_SIZE +* CY_BOOT_SECONDARY_2_SIZE + +As an example in a makefile it should look like following: + +`DEFINES_APP +=-DCY_FLASH_MAP_EXT_DESC` + +`DEFINES_APP +=-DMCUBOOT_MAX_IMG_SECTORS=512` + +`DEFINES_APP +=-DCY_BOOT_PRIMARY_1_SIZE=0x15000` + +**Multi-Image Operation** + +Multi-image operation considers upgrading and verification of more then one image on the device. + +To enable multi-image operation define `MCUBOOT_IMAGE_NUMBER` in `MCUBootApp/config/mcuboot_config.h` file should be set to 2 (only dual-image is supported at the moment). This could also be done on build time by passing `MCUBOOT_IMAGE_NUMBER=2` as parameter to `make`. + +Default value of `MCUBOOT_IMAGE_NUMBER` is 1, which corresponds to single image configuratios. + +In multi-image operation (two images are considered for simplicity) MCUboot Bootloader application operates as following: + +* Verifies Primary_1 and Primary_2 images; +* Verifies Secondary_1 and Secondary_2 images; +* Upgrades Secondary to Primary if valid images found; +* Boots image from Primary_1 slot only; +* Boots Primary_1 only if both - Primary_1 and Primary_2 are present and valid; + +This ensures two dependent applications can be accepted by device only in case both images are valid. + +**Default Flash map for Multi-Image operation:** + +`0x10000000 - 0x10018000` - MCUboot Bootloader + +`0x10018000 - 0x10028000` - Primary_1 (BOOT) slot of Bootloader + +`0x10028000 - 0x10038000` - Secondary_1 (UPGRADE) slot of Bootloader + +`0x10038000 - 0x10048000` - Primary_2 (BOOT) slot of Bootloader + +`0x10048000 - 0x10058000` - Secondary_2 (UPGRADE) slot of Bootloader + +`0x10058000 - 0x10059000` - Scratch of Bootloader + +Size of slots `0x10000` - 64kb + +__Note:__ It is also possible to place secondary (upgrade) slots in external memory module so resulting image size can be doubled. +For more details about External Memory usage, please refer to separate guiding document `ExternalMemory.md`. + +### Hardware limitations + +Since this application is created to demonstrate MCUboot library features and not as reference examples some considerations are taken. + +1. `SCB5` used to configure serial port for debug prints. This is the most commonly used Serial Communication Block number among available Cypress PSoC 6 kits. If you try to use custom hardware with this application - change definition of `CYBSP_UART_HW` in `main.c` of MCUBootApp to SCB* that correspond to your design. + +2. `CY_SMIF_SLAVE_SELECT_0` is used as definition SMIF driver API. This configuration is used on evaluation kit for this example CY8CPROTO-062-4343W, CY8PROTO-062S3-4343W, CY8CKIT-062-4343W. If you try to use custom hardware with this application - change value of `smif_id` in `main.c` of MCUBootApp to value that corresponds to your design. + + +### Downloading solution's assets + +There is a set assets required: + +* MCUBooot Library (root repository) +* PSoC6 HAL Library +* PSoC6 Peripheral Drivers Library (PDL) +* Mbed TLS Cryptographic Library + +To get submodules - run the following command: + + git submodule update --init --recursive + +### Building solution + +This folder contains make files infrastructure for building MCUBoot Bootloader. Same approach used in sample BlinkyLedApp application. Example command are provided below for couple different build configurations. + +* Build MCUBootApp in `Debug` for signle image use case. + + make app APP_NAME=MCUBootApp PLATFORM=PSOC_062_2M BUILDCFG=Debug MCUBOOT_IMAGE_NUMBER=1 + +* Build MCUBootApp in `Release` for multi image use case. + + make app APP_NAME=MCUBootApp PLATFORM=PSOC_062_2M BUILDCFG=Release MCUBOOT_IMAGE_NUMBER=2 + +* To Build MCUBootApp with external memory support - pass `USE_EXTERNAL_FLASH=1` flag to `make` command in examples above. In this case UPGRADE image will be located in external memory. Refer to ExternalMemory.md for additional details. + +Root directory for build is **boot/cypress.** + +**Encrypted Image Support** + +To protect user image from unwanted read - Upgrade Image Encryption can be applied. The ECDH/HKDF with EC256 scheme is used in a given solution as well as Mbed TLS as a crypto provider. + +To enable image encryption support use `ENC_IMG=1` build flag (BlinkyApp should also be built with this flash set 1). + +User is also responsible for providing corresponding binary key data in `enc_priv_key[]` (file `\MCUBootApp\keys.c`). The public part will be used by imgtool when signing and encrypting upgrade image. Signing image with encryption is described in `\BlinkyApp\Readme.md`. + +After MCUBootApp is built with these settings unencrypted and encrypted images will be accepted in secondary (upgrade) slot. + +Example command: + + make app APP_NAME=MCUBootApp PLATFORM=PSOC_062_2M BUILDCFG=Debug MCUBOOT_IMAGE_NUMBER=1 ENC_IMG=1 + +**Programming solution** + +There are couple ways of programming hex of MCUBootApp and BlinkyApp. Following instructions assume one of Cypress development kits, for example `CY8CPROTO_062_4343W`. + +1. Direct usage of OpenOCD. +OpenOCD package is supplied with ModuToolbox IDE and can be found in installation folder under `./tools_2.1/openocd`. +Open terminal application - and execute following command after substitution `PATH_TO_APPLICATION.hex` and `OPENOCD` paths. + +Connect a board to your computer. Switch Kitprog3 to DAP-BULK mode by pressing `SW3 MODE` button until `LED2 STATUS` constantly shines. + + export OPENOCD=/Applications/ModusToolbox/tools_2.1/openocd + + ${OPENOCD}/bin/openocd -s ${OPENOCD}/scripts \ + -f ${OPENOCD}/scripts/interface/kitprog3.cfg \ + -f ${OPENOCD}/scripts/target/psoc6_2m.cfg \ + -c "init; reset init; program PATH_TO_APPLICATION.hex" \ + -c "resume; reset; exit" + +2. Using GUI tool `Cypress Programmer` - follow [link](https://www.cypress.com/products/psoc-programming-solutions) to download. + Connect board to your computer. Switch Kitprog3 to DAP-BULK mode by pressing `SW3 MODE` button until `LED2 STATUS` constantly shines. Open `Cypress Programmer` and click `Connect`, then choose hex file: `MCUBootApp.hex` or `BlinkyApp.hex` and click `Program`. Check log to ensure programming success. Reset board. + +3. Using `DAPLINK`. + Connect board to your computer. Switch embeded Kitprog3 to `DAPLINK` mode by pressing `SW3 MODE` button until `LED2 STATUS` blinks fast and mass storage device appeared in OS. Drag and drop `hex` files you wish to program to `DAPLINK` drive in your OS. + + + +**Currently supported platforms:** + +* PSOC_062_2M +* PSOC_062_1M +* PSOC_062_512K + +**Build environment troubleshooting:** + +Regular shell/terminal combination on Linux and MacOS. + +On Windows: + +* Cygwin +* Msys2 + +Also IDE may be used: +* Eclipse / ModusToolbox ("makefile project from existing source") + +*Make* - make sure it is added to system's `PATH` variable and correct path is first in the list; + +*Python/Python3* - make sure you have correct path referenced in `PATH`; + +*Msys2* - to use systems PATH navigate to msys2 folder, open `msys2_shell.cmd`, uncomment set `MSYS2_PATH_TYPE=inherit`, restart MSYS2 shell. + +This will iherit system's PATH so should find `python3.7` installed in regular way as well as imgtool and its dependencies. + diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_assert.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_assert.h new file mode 100644 index 0000000..5c07711 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_assert.h @@ -0,0 +1,22 @@ +/* + * mcuboot_assert.h + * + * Cypress-specific assert() macro redefinition + * + */ + +#ifndef MCUBOOT_ASSERT_H +#define MCUBOOT_ASSERT_H + +//#include "cy_bootloader_services.h" + +#define CYBL_ASSERT(...) Cy_BLServ_Assert(__VA_ARGS__) + +#if !defined(NDEBUG) +#undef assert +#define assert(...) CYBL_ASSERT(__VA_ARGS__) +#else +#define assert +#endif + +#endif /* MCUBOOT_ASSERT_H */ diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_config.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000..7f8472b --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_config.h @@ -0,0 +1,166 @@ +/* Copyright 2019 Cypress Semiconductor Corporation + * + * Copyright (c) 2018 Open Source Foundries Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef MCUBOOT_CONFIG_H +#define MCUBOOT_CONFIG_H + +/* + * Template configuration file for MCUboot. + * + * When porting MCUboot to a new target, copy it somewhere that your + * include path can find it as mcuboot_config/mcuboot_config.h, and + * make adjustments to suit your platform. + * + * For examples, see: + * + * boot/zephyr/include/mcuboot_config/mcuboot_config.h + * boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h + */ +/* Default maximum number of flash sectors per image slot; change + * as desirable. */ +#ifndef MCUBOOT_MAX_IMG_SECTORS +#define MCUBOOT_MAX_IMG_SECTORS 2560 +#endif + +/* + * Signature types + * + * You must choose exactly one signature type. + */ + +/* Uncomment for RSA signature support */ +//#define MCUBOOT_SIGN_RSA + +/* Uncomment for ECDSA signatures using curve P-256. */ +#define MCUBOOT_SIGN_EC256 + +/* + * Upgrade mode + * + * The default is to support A/B image swapping with rollback. A + * simpler code path, which only supports overwriting the + * existing image with the update image, is also available. + */ + +/* Uncomment to enable the overwrite-only code path. */ +#define MCUBOOT_OVERWRITE_ONLY + +#ifdef MCUBOOT_OVERWRITE_ONLY +/* Uncomment to only erase and overwrite those slot 0 sectors needed + * to install the new image, rather than the entire image slot. */ +/* #define MCUBOOT_OVERWRITE_ONLY_FAST */ +#endif + +/* + * Cryptographic settings + * + * You must choose between mbedTLS and Tinycrypt as source of + * cryptographic primitives. Other cryptographic settings are also + * available. + */ + +/* Uncomment to use ARM's mbedTLS cryptographic primitives */ +#define MCUBOOT_USE_MBED_TLS +/* Uncomment to use Tinycrypt's. */ +/* #define MCUBOOT_USE_TINYCRYPT */ + +/* + * Always check the signature of the image in slot 0 before booting, + * even if no upgrade was performed. This is recommended if the boot + * time penalty is acceptable. + */ +#define MCUBOOT_VALIDATE_PRIMARY_SLOT + +/* + * Flash abstraction + */ + +/* Uncomment if your flash map API supports flash_area_get_sectors(). + * See the flash APIs for more details. */ +// TODO: FWSECURITY-755 +#define MCUBOOT_USE_FLASH_AREA_GET_SECTORS + +/* Default number of separately updateable images; change in case of + * multiple images. */ +#ifndef MCUBOOT_IMAGE_NUMBER +#define MCUBOOT_IMAGE_NUMBER 1 +#endif + +/* + * Currently there is no configuration option, for this platform, + * that enables the system specific mcumgr commands in mcuboot + */ +#define MCUBOOT_PERUSER_MGMT_GROUP_ENABLED 0 + +/* + * Logging + */ + +/* + * If logging is enabled the following functions must be defined by the + * platform: + * + * MCUBOOT_LOG_ERR(...) + * MCUBOOT_LOG_WRN(...) + * MCUBOOT_LOG_INF(...) + * MCUBOOT_LOG_DBG(...) + * + * The following global logging level configuration macros must also be + * defined, each with a unique value. Those will be used to define a global + * configuration and will allow any source files to override the global + * configuration: + * + * MCUBOOT_LOG_LEVEL_OFF + * MCUBOOT_LOG_LEVEL_ERROR + * MCUBOOT_LOG_LEVEL_WARNING + * MCUBOOT_LOG_LEVEL_INFO + * MCUBOOT_LOG_LEVEL_DEBUG + * + * The global logging level must be defined, with one of the previously defined + * logging levels: + * + * #define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_(OFF|ERROR|WARNING|INFO|DEBUG) + * + * MCUBOOT_LOG_LEVEL sets the minimum level that will be logged. The function + * priority is: + * + * MCUBOOT_LOG_ERR > MCUBOOT_LOG_WRN > MCUBOOT_LOG_INF > MCUBOOT_LOG_DBG + * + * NOTE: Each source file is still able to request its own logging level by + * defining BOOT_LOG_LEVEL before #including `bootutil_log.h` + */ +#define MCUBOOT_HAVE_LOGGING 1 +/* Define this to support native mcuboot logging system */ +#define CONFIG_MCUBOOT 1 +/* + * Assertions + */ + +/* Uncomment if your platform has its own mcuboot_config/mcuboot_assert.h. + * If so, it must provide an ASSERT macro for use by bootutil. Otherwise, + * "assert" is used. */ +//#define MCUBOOT_HAVE_ASSERT_H + +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + /* TODO: to be implemented */ \ + } while (0) + +/* Uncomment these if support of encrypted upgrade image is needed */ +#ifdef ENC_IMG +#define MCUBOOT_ENC_IMAGES +#define MCUBOOT_ENCRYPT_EC256 +#define NUM_ECC_BYTES (256 / 8) +#endif /* ENC_IMG */ + +/* + * No direct idle call implemented + */ +#define MCUBOOT_CPU_IDLE() \ + do { \ + } while (0) + +#endif /* MCUBOOT_CONFIG_H */ diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_logging.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000..0d6f787 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * Copyright (c) 2020 Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /*******************************************************************************/ + +#ifndef MCUBOOT_LOGGING_H +#define MCUBOOT_LOGGING_H + +#include + +#define MCUBOOT_LOG_LEVEL_OFF 0 +#define MCUBOOT_LOG_LEVEL_ERROR 1 +#define MCUBOOT_LOG_LEVEL_WARNING 2 +#define MCUBOOT_LOG_LEVEL_INFO 3 +#define MCUBOOT_LOG_LEVEL_DEBUG 4 + +/* + * The compiled log level determines the maximum level that can be + * printed. Messages at or below this level can be printed, provided + * they are also enabled through the Rust logging system, such as by + * setting RUST_LOG to bootsim::api=info. + */ +#ifndef MCUBOOT_LOG_LEVEL +#define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_INFO +#endif + +int sim_log_enabled(int level); + +#define sim_log_enabled(x) 1 + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_ERROR +#define MCUBOOT_LOG_ERR(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_ERROR)) { \ + fprintf(stderr, "[ERR] " _fmt "\n\r", ##__VA_ARGS__); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_ERR(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_WARNING +#define MCUBOOT_LOG_WRN(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_WARNING)) { \ + fprintf(stderr, "[WRN] " _fmt "\n\r", ##__VA_ARGS__); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_WRN(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_INFO +#define MCUBOOT_LOG_INF(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_INFO)) { \ + fprintf(stderr, "[INF] " _fmt "\n\r", ##__VA_ARGS__); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_INF(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_DEBUG +#define MCUBOOT_LOG_DBG(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_DEBUG)) { \ + fprintf(stderr, "[DBG] " _fmt "\n\r", ##__VA_ARGS__); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_DBG(...) IGNORE(__VA_ARGS__) +#endif + +#define MCUBOOT_LOG_MODULE_DECLARE(...) + +#endif /* MCUBOOT_LOGGING_H */ diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_acc_config.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_acc_config.h new file mode 100644 index 0000000..b172740 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_acc_config.h @@ -0,0 +1,54 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2019 Cypress Semiconductor Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file mcuboot_crypto_acc_config.h + * \version 1.1 + */ + +#ifndef MCUBOOT_MBEDTLS_DEVICE_H +#define MCUBOOT_MBEDTLS_DEVICE_H + +/* Currently this target supports SHA1 */ +// #define MBEDTLS_SHA1_C + +#define MBEDTLS_SHA1_ALT +#define MBEDTLS_SHA256_ALT +#define MBEDTLS_SHA512_ALT + +/* Currently this target supports CBC, CFB, OFB, CTR and XTS cipher modes */ +#define MBEDTLS_AES_ALT +// #define MBEDTLS_CIPHER_MODE_CBC +// #define MBEDTLS_CIPHER_MODE_CFB +// #define MBEDTLS_CIPHER_MODE_OFB +#ifdef MCUBOOT_ENC_IMAGES +#define MBEDTLS_CIPHER_MODE_CTR +#endif +// #define MBEDTLS_CIPHER_MODE_XTS + +/* Only NIST-P curves are currently supported */ +#define MBEDTLS_ECP_ALT +// #define MBEDTLS_ECP_DP_SECP192R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP224R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP256R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP384R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP521R1_ENABLED + +#define MBEDTLS_ECDSA_SIGN_ALT +#define MBEDTLS_ECDSA_VERIFY_ALT + +#endif /* MCUBOOT_MBEDTLS_DEVICE_H */ diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_config.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_config.h new file mode 100644 index 0000000..e699800 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/config/mcuboot_crypto_config.h @@ -0,0 +1,3604 @@ +/** + * \file config.h + * + * \brief Configuration options (set of defines) + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +/* + * Copyright (C) 2006-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_CONFIG_H +#define MBEDTLS_CONFIG_H + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def MBEDTLS_HAVE_ASM + * + * The compiler has support for asm(). + * + * Requires support for asm() in compiler. + * + * Used in: + * library/aria.c + * library/timing.c + * include/mbedtls/bn_mul.h + * + * Required by: + * MBEDTLS_AESNI_C + * MBEDTLS_PADLOCK_C + * + * Comment to disable the use of assembly code. + */ +#define MBEDTLS_HAVE_ASM + +/** + * \def MBEDTLS_NO_UDBL_DIVISION + * + * The platform lacks support for double-width integer division (64-bit + * division on a 32-bit platform, 128-bit division on a 64-bit platform). + * + * Used in: + * include/mbedtls/bignum.h + * library/bignum.c + * + * The bignum code uses double-width division to speed up some operations. + * Double-width division is often implemented in software that needs to + * be linked with the program. The presence of a double-width integer + * type is usually detected automatically through preprocessor macros, + * but the automatic detection cannot know whether the code needs to + * and can be linked with an implementation of division for that type. + * By default division is assumed to be usable if the type is present. + * Uncomment this option to prevent the use of double-width division. + * + * Note that division for the native integer type is always required. + * Furthermore, a 64-bit type is always required even on a 32-bit + * platform, but it need not support multiplication or division. In some + * cases it is also desirable to disable some double-width operations. For + * example, if double-width division is implemented in software, disabling + * it can reduce code size in some embedded targets. + */ +//#define MBEDTLS_NO_UDBL_DIVISION + +/** + * \def MBEDTLS_NO_64BIT_MULTIPLICATION + * + * The platform lacks support for 32x32 -> 64-bit multiplication. + * + * Used in: + * library/poly1305.c + * + * Some parts of the library may use multiplication of two unsigned 32-bit + * operands with a 64-bit result in order to speed up computations. On some + * platforms, this is not available in hardware and has to be implemented in + * software, usually in a library provided by the toolchain. + * + * Sometimes it is not desirable to have to link to that library. This option + * removes the dependency of that library on platforms that lack a hardware + * 64-bit multiplier by embedding a software implementation in Mbed TLS. + * + * Note that depending on the compiler, this may decrease performance compared + * to using the library function provided by the toolchain. + */ +//#define MBEDTLS_NO_64BIT_MULTIPLICATION + +/** + * \def MBEDTLS_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + */ +//#define MBEDTLS_HAVE_SSE2 + +/** + * \def MBEDTLS_HAVE_TIME + * + * System has time.h and time(). + * The time does not need to be correct, only time differences are used, + * by contrast with MBEDTLS_HAVE_TIME_DATE + * + * Defining MBEDTLS_HAVE_TIME allows you to specify MBEDTLS_PLATFORM_TIME_ALT, + * MBEDTLS_PLATFORM_TIME_MACRO, MBEDTLS_PLATFORM_TIME_TYPE_MACRO and + * MBEDTLS_PLATFORM_STD_TIME. + * + * Comment if your system does not support time functions + */ +#define MBEDTLS_HAVE_TIME + +/** + * \def MBEDTLS_HAVE_TIME_DATE + * + * System has time.h, time(), and an implementation for + * mbedtls_platform_gmtime_r() (see below). + * The time needs to be correct (not necessarily very accurate, but at least + * the date should be correct). This is used to verify the validity period of + * X.509 certificates. + * + * Comment if your system does not have a correct clock. + * + * \note mbedtls_platform_gmtime_r() is an abstraction in platform_util.h that + * behaves similarly to the gmtime_r() function from the C standard. Refer to + * the documentation for mbedtls_platform_gmtime_r() for more information. + * + * \note It is possible to configure an implementation for + * mbedtls_platform_gmtime_r() at compile-time by using the macro + * MBEDTLS_PLATFORM_GMTIME_R_ALT. + */ +#define MBEDTLS_HAVE_TIME_DATE + +/** + * \def MBEDTLS_PLATFORM_MEMORY + * + * Enable the memory allocation layer. + * + * By default mbed TLS uses the system-provided calloc() and free(). + * This allows different allocators (self-implemented or provided) to be + * provided to the platform abstraction layer. + * + * Enabling MBEDTLS_PLATFORM_MEMORY without the + * MBEDTLS_PLATFORM_{FREE,CALLOC}_MACROs will provide + * "mbedtls_platform_set_calloc_free()" allowing you to set an alternative calloc() and + * free() function pointer at runtime. + * + * Enabling MBEDTLS_PLATFORM_MEMORY and specifying + * MBEDTLS_PLATFORM_{CALLOC,FREE}_MACROs will allow you to specify the + * alternate function at compile time. + * + * Requires: MBEDTLS_PLATFORM_C + * + * Enable this layer to allow use of alternative memory allocators. + */ +//#define MBEDTLS_PLATFORM_MEMORY + +/** + * \def MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + * + * Do not assign standard functions in the platform layer (e.g. calloc() to + * MBEDTLS_PLATFORM_STD_CALLOC and printf() to MBEDTLS_PLATFORM_STD_PRINTF) + * + * This makes sure there are no linking errors on platforms that do not support + * these functions. You will HAVE to provide alternatives, either at runtime + * via the platform_set_xxx() functions or at compile time by setting + * the MBEDTLS_PLATFORM_STD_XXX defines, or enabling a + * MBEDTLS_PLATFORM_XXX_MACRO. + * + * Requires: MBEDTLS_PLATFORM_C + * + * Uncomment to prevent default assignment of standard functions in the + * platform layer. + */ +//#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +/** + * \def MBEDTLS_PLATFORM_EXIT_ALT + * + * MBEDTLS_PLATFORM_XXX_ALT: Uncomment a macro to let mbed TLS support the + * function in the platform abstraction layer. + * + * Example: In case you uncomment MBEDTLS_PLATFORM_PRINTF_ALT, mbed TLS will + * provide a function "mbedtls_platform_set_printf()" that allows you to set an + * alternative printf function pointer. + * + * All these define require MBEDTLS_PLATFORM_C to be defined! + * + * \note MBEDTLS_PLATFORM_SNPRINTF_ALT is required on Windows; + * it will be enabled automatically by check_config.h + * + * \warning MBEDTLS_PLATFORM_XXX_ALT cannot be defined at the same time as + * MBEDTLS_PLATFORM_XXX_MACRO! + * + * Requires: MBEDTLS_PLATFORM_TIME_ALT requires MBEDTLS_HAVE_TIME + * + * Uncomment a macro to enable alternate implementation of specific base + * platform function + */ +//#define MBEDTLS_PLATFORM_EXIT_ALT +//#define MBEDTLS_PLATFORM_TIME_ALT +//#define MBEDTLS_PLATFORM_FPRINTF_ALT +//#define MBEDTLS_PLATFORM_PRINTF_ALT +//#define MBEDTLS_PLATFORM_SNPRINTF_ALT +//#define MBEDTLS_PLATFORM_VSNPRINTF_ALT +//#define MBEDTLS_PLATFORM_NV_SEED_ALT +//#define MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT + +/** + * \def MBEDTLS_DEPRECATED_WARNING + * + * Mark deprecated functions so that they generate a warning if used. + * Functions deprecated in one version will usually be removed in the next + * version. You can enable this to help you prepare the transition to a new + * major version by making sure your code is not using these functions. + * + * This only works with GCC and Clang. With other compilers, you may want to + * use MBEDTLS_DEPRECATED_REMOVED + * + * Uncomment to get warnings on using deprecated functions. + */ +//#define MBEDTLS_DEPRECATED_WARNING + +/** + * \def MBEDTLS_DEPRECATED_REMOVED + * + * Remove deprecated functions so that they generate an error if used. + * Functions deprecated in one version will usually be removed in the next + * version. You can enable this to help you prepare the transition to a new + * major version by making sure your code is not using these functions. + * + * Uncomment to get errors on using deprecated functions. + */ +//#define MBEDTLS_DEPRECATED_REMOVED + +/** + * \def MBEDTLS_CHECK_PARAMS + * + * This configuration option controls whether the library validates more of + * the parameters passed to it. + * + * When this flag is not defined, the library only attempts to validate an + * input parameter if: (1) they may come from the outside world (such as the + * network, the filesystem, etc.) or (2) not validating them could result in + * internal memory errors such as overflowing a buffer controlled by the + * library. On the other hand, it doesn't attempt to validate parameters whose + * values are fully controlled by the application (such as pointers). + * + * When this flag is defined, the library additionally attempts to validate + * parameters that are fully controlled by the application, and should always + * be valid if the application code is fully correct and trusted. + * + * For example, when a function accepts as input a pointer to a buffer that may + * contain untrusted data, and its documentation mentions that this pointer + * must not be NULL: + * - The pointer is checked to be non-NULL only if this option is enabled. + * - The content of the buffer is always validated. + * + * When this flag is defined, if a library function receives a parameter that + * is invalid: + * 1. The function will invoke the macro MBEDTLS_PARAM_FAILED(). + * 2. If MBEDTLS_PARAM_FAILED() did not terminate the program, the function + * will immediately return. If the function returns an Mbed TLS error code, + * the error code in this case is MBEDTLS_ERR_xxx_BAD_INPUT_DATA. + * + * When defining this flag, you also need to arrange a definition for + * MBEDTLS_PARAM_FAILED(). You can do this by any of the following methods: + * - By default, the library defines MBEDTLS_PARAM_FAILED() to call a + * function mbedtls_param_failed(), but the library does not define this + * function. If you do not make any other arrangements, you must provide + * the function mbedtls_param_failed() in your application. + * See `platform_util.h` for its prototype. + * - If you enable the macro #MBEDTLS_CHECK_PARAMS_ASSERT, then the + * library defines MBEDTLS_PARAM_FAILED(\c cond) to be `assert(cond)`. + * You can still supply an alternative definition of + * MBEDTLS_PARAM_FAILED(), which may call `assert`. + * - If you define a macro MBEDTLS_PARAM_FAILED() before including `config.h` + * or you uncomment the definition of MBEDTLS_PARAM_FAILED() in `config.h`, + * the library will call the macro that you defined and will not supply + * its own version. Note that if MBEDTLS_PARAM_FAILED() calls `assert`, + * you need to enable #MBEDTLS_CHECK_PARAMS_ASSERT so that library source + * files include ``. + * + * Uncomment to enable validation of application-controlled parameters. + */ +//#define MBEDTLS_CHECK_PARAMS + +/** + * \def MBEDTLS_CHECK_PARAMS_ASSERT + * + * Allow MBEDTLS_PARAM_FAILED() to call `assert`, and make it default to + * `assert`. This macro is only used if #MBEDTLS_CHECK_PARAMS is defined. + * + * If this macro is not defined, then MBEDTLS_PARAM_FAILED() defaults to + * calling a function mbedtls_param_failed(). See the documentation of + * #MBEDTLS_CHECK_PARAMS for details. + * + * Uncomment to allow MBEDTLS_PARAM_FAILED() to call `assert`. + */ +//#define MBEDTLS_CHECK_PARAMS_ASSERT + +/* \} name SECTION: System support */ + +/** + * \name SECTION: mbed TLS feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def MBEDTLS_TIMING_ALT + * + * Uncomment to provide your own alternate implementation for mbedtls_timing_hardclock(), + * mbedtls_timing_get_timer(), mbedtls_set_alarm(), mbedtls_set/get_delay() + * + * Only works if you have MBEDTLS_TIMING_C enabled. + * + * You will need to provide a header "timing_alt.h" and an implementation at + * compile time. + */ +//#define MBEDTLS_TIMING_ALT + +/** + * \def MBEDTLS_AES_ALT + * + * MBEDTLS__MODULE_NAME__ALT: Uncomment a macro to let mbed TLS use your + * alternate core implementation of a symmetric crypto, an arithmetic or hash + * module (e.g. platform specific assembly optimized implementations). Keep + * in mind that the function prototypes should remain the same. + * + * This replaces the whole module. If you only want to replace one of the + * functions, use one of the MBEDTLS__FUNCTION_NAME__ALT flags. + * + * Example: In case you uncomment MBEDTLS_AES_ALT, mbed TLS will no longer + * provide the "struct mbedtls_aes_context" definition and omit the base + * function declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * module. + * + * \warning MD2, MD4, MD5, ARC4, DES and SHA-1 are considered weak and their + * use constitutes a security risk. If possible, we recommend + * avoiding dependencies on them, and considering stronger message + * digests and ciphers instead. + * + */ +//#define MBEDTLS_AES_ALT +//#define MBEDTLS_ARC4_ALT +//#define MBEDTLS_ARIA_ALT +//#define MBEDTLS_BLOWFISH_ALT +//#define MBEDTLS_CAMELLIA_ALT +//#define MBEDTLS_CCM_ALT +//#define MBEDTLS_CHACHA20_ALT +//#define MBEDTLS_CHACHAPOLY_ALT +//#define MBEDTLS_CMAC_ALT +//#define MBEDTLS_DES_ALT +//#define MBEDTLS_DHM_ALT +//#define MBEDTLS_ECJPAKE_ALT +//#define MBEDTLS_GCM_ALT +//#define MBEDTLS_NIST_KW_ALT +//#define MBEDTLS_MD2_ALT +//#define MBEDTLS_MD4_ALT +//#define MBEDTLS_MD5_ALT +//#define MBEDTLS_POLY1305_ALT +//#define MBEDTLS_RIPEMD160_ALT +//#define MBEDTLS_RSA_ALT +//#define MBEDTLS_SHA1_ALT +//#define MBEDTLS_SHA256_ALT +//#define MBEDTLS_SHA512_ALT +//#define MBEDTLS_XTEA_ALT + +/* + * When replacing the elliptic curve module, pleace consider, that it is + * implemented with two .c files: + * - ecp.c + * - ecp_curves.c + * You can replace them very much like all the other MBEDTLS__MODULE_NAME__ALT + * macros as described above. The only difference is that you have to make sure + * that you provide functionality for both .c files. + */ +//#define MBEDTLS_ECP_ALT + +/** + * \def MBEDTLS_MD2_PROCESS_ALT + * + * MBEDTLS__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use you + * alternate core implementation of symmetric crypto or hash function. Keep in + * mind that function prototypes should remain the same. + * + * This replaces only one function. The header file from mbed TLS is still + * used, in contrast to the MBEDTLS__MODULE_NAME__ALT flags. + * + * Example: In case you uncomment MBEDTLS_SHA256_PROCESS_ALT, mbed TLS will + * no longer provide the mbedtls_sha1_process() function, but it will still provide + * the other function (using your mbedtls_sha1_process() function) and the definition + * of mbedtls_sha1_context, so your implementation of mbedtls_sha1_process must be compatible + * with this definition. + * + * \note Because of a signature change, the core AES encryption and decryption routines are + * currently named mbedtls_aes_internal_encrypt and mbedtls_aes_internal_decrypt, + * respectively. When setting up alternative implementations, these functions should + * be overridden, but the wrapper functions mbedtls_aes_decrypt and mbedtls_aes_encrypt + * must stay untouched. + * + * \note If you use the AES_xxx_ALT macros, then is is recommended to also set + * MBEDTLS_AES_ROM_TABLES in order to help the linker garbage-collect the AES + * tables. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * function. + * + * \warning MD2, MD4, MD5, DES and SHA-1 are considered weak and their use + * constitutes a security risk. If possible, we recommend avoiding + * dependencies on them, and considering stronger message digests + * and ciphers instead. + * + */ +//#define MBEDTLS_MD2_PROCESS_ALT +//#define MBEDTLS_MD4_PROCESS_ALT +//#define MBEDTLS_MD5_PROCESS_ALT +//#define MBEDTLS_RIPEMD160_PROCESS_ALT +//#define MBEDTLS_SHA1_PROCESS_ALT +//#define MBEDTLS_SHA256_PROCESS_ALT +//#define MBEDTLS_SHA512_PROCESS_ALT +//#define MBEDTLS_DES_SETKEY_ALT +//#define MBEDTLS_DES_CRYPT_ECB_ALT +//#define MBEDTLS_DES3_CRYPT_ECB_ALT +//#define MBEDTLS_AES_SETKEY_ENC_ALT +//#define MBEDTLS_AES_SETKEY_DEC_ALT +//#define MBEDTLS_AES_ENCRYPT_ALT +//#define MBEDTLS_AES_DECRYPT_ALT +//#define MBEDTLS_ECDH_GEN_PUBLIC_ALT +//#define MBEDTLS_ECDH_COMPUTE_SHARED_ALT +//#define MBEDTLS_ECDSA_VERIFY_ALT +//#define MBEDTLS_ECDSA_SIGN_ALT +//#define MBEDTLS_ECDSA_GENKEY_ALT + +/** + * \def MBEDTLS_ECP_INTERNAL_ALT + * + * Expose a part of the internal interface of the Elliptic Curve Point module. + * + * MBEDTLS_ECP__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use your + * alternative core implementation of elliptic curve arithmetic. Keep in mind + * that function prototypes should remain the same. + * + * This partially replaces one function. The header file from mbed TLS is still + * used, in contrast to the MBEDTLS_ECP_ALT flag. The original implementation + * is still present and it is used for group structures not supported by the + * alternative. + * + * Any of these options become available by defining MBEDTLS_ECP_INTERNAL_ALT + * and implementing the following functions: + * unsigned char mbedtls_internal_ecp_grp_capable( + * const mbedtls_ecp_group *grp ) + * int mbedtls_internal_ecp_init( const mbedtls_ecp_group *grp ) + * void mbedtls_internal_ecp_free( const mbedtls_ecp_group *grp ) + * The mbedtls_internal_ecp_grp_capable function should return 1 if the + * replacement functions implement arithmetic for the given group and 0 + * otherwise. + * The functions mbedtls_internal_ecp_init and mbedtls_internal_ecp_free are + * called before and after each point operation and provide an opportunity to + * implement optimized set up and tear down instructions. + * + * Example: In case you uncomment MBEDTLS_ECP_INTERNAL_ALT and + * MBEDTLS_ECP_DOUBLE_JAC_ALT, mbed TLS will still provide the ecp_double_jac + * function, but will use your mbedtls_internal_ecp_double_jac if the group is + * supported (your mbedtls_internal_ecp_grp_capable function returns 1 when + * receives it as an argument). If the group is not supported then the original + * implementation is used. The other functions and the definition of + * mbedtls_ecp_group and mbedtls_ecp_point will not change, so your + * implementation of mbedtls_internal_ecp_double_jac and + * mbedtls_internal_ecp_grp_capable must be compatible with this definition. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * function. + */ +/* Required for all the functions in this section */ +//#define MBEDTLS_ECP_INTERNAL_ALT +/* Support for Weierstrass curves with Jacobi representation */ +//#define MBEDTLS_ECP_RANDOMIZE_JAC_ALT +//#define MBEDTLS_ECP_ADD_MIXED_ALT +//#define MBEDTLS_ECP_DOUBLE_JAC_ALT +//#define MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT +//#define MBEDTLS_ECP_NORMALIZE_JAC_ALT +/* Support for curves with Montgomery arithmetic */ +//#define MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT +//#define MBEDTLS_ECP_RANDOMIZE_MXZ_ALT +//#define MBEDTLS_ECP_NORMALIZE_MXZ_ALT + +/** + * \def MBEDTLS_TEST_NULL_ENTROPY + * + * Enables testing and use of mbed TLS without any configured entropy sources. + * This permits use of the library on platforms before an entropy source has + * been integrated (see for example the MBEDTLS_ENTROPY_HARDWARE_ALT or the + * MBEDTLS_ENTROPY_NV_SEED switches). + * + * WARNING! This switch MUST be disabled in production builds, and is suitable + * only for development. + * Enabling the switch negates any security provided by the library. + * + * Requires MBEDTLS_ENTROPY_C, MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + * + */ +//#define MBEDTLS_TEST_NULL_ENTROPY + +/** + * \def MBEDTLS_ENTROPY_HARDWARE_ALT + * + * Uncomment this macro to let mbed TLS use your own implementation of a + * hardware entropy collector. + * + * Your function must be called \c mbedtls_hardware_poll(), have the same + * prototype as declared in entropy_poll.h, and accept NULL as first argument. + * + * Uncomment to use your own hardware entropy collector. + */ +//#define MBEDTLS_ENTROPY_HARDWARE_ALT + +/** + * \def MBEDTLS_AES_ROM_TABLES + * + * Use precomputed AES tables stored in ROM. + * + * Uncomment this macro to use precomputed AES tables stored in ROM. + * Comment this macro to generate AES tables in RAM at runtime. + * + * Tradeoff: Using precomputed ROM tables reduces RAM usage by ~8kb + * (or ~2kb if \c MBEDTLS_AES_FEWER_TABLES is used) and reduces the + * initialization time before the first AES operation can be performed. + * It comes at the cost of additional ~8kb ROM use (resp. ~2kb if \c + * MBEDTLS_AES_FEWER_TABLES below is used), and potentially degraded + * performance if ROM access is slower than RAM access. + * + * This option is independent of \c MBEDTLS_AES_FEWER_TABLES. + * + */ +//#define MBEDTLS_AES_ROM_TABLES + +/** + * \def MBEDTLS_AES_FEWER_TABLES + * + * Use less ROM/RAM for AES tables. + * + * Uncommenting this macro omits 75% of the AES tables from + * ROM / RAM (depending on the value of \c MBEDTLS_AES_ROM_TABLES) + * by computing their values on the fly during operations + * (the tables are entry-wise rotations of one another). + * + * Tradeoff: Uncommenting this reduces the RAM / ROM footprint + * by ~6kb but at the cost of more arithmetic operations during + * runtime. Specifically, one has to compare 4 accesses within + * different tables to 4 accesses with additional arithmetic + * operations within the same table. The performance gain/loss + * depends on the system and memory details. + * + * This option is independent of \c MBEDTLS_AES_ROM_TABLES. + * + */ +//#define MBEDTLS_AES_FEWER_TABLES + +/** + * \def MBEDTLS_CAMELLIA_SMALL_MEMORY + * + * Use less ROM for the Camellia implementation (saves about 768 bytes). + * + * Uncomment this macro to use less memory for Camellia. + */ +//#define MBEDTLS_CAMELLIA_SMALL_MEMORY + +/** + * \def MBEDTLS_CIPHER_MODE_CBC + * + * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CBC + +/** + * \def MBEDTLS_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CFB + +/** + * \def MBEDTLS_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CTR + +/** + * \def MBEDTLS_CIPHER_MODE_OFB + * + * Enable Output Feedback mode (OFB) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_OFB + +/** + * \def MBEDTLS_CIPHER_MODE_XTS + * + * Enable Xor-encrypt-xor with ciphertext stealing mode (XTS) for AES. + */ +#define MBEDTLS_CIPHER_MODE_XTS + +/** + * \def MBEDTLS_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * Requires MBEDTLS_ENABLE_WEAK_CIPHERSUITES as well to enable + * the following ciphersuites: + * MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_RSA_WITH_NULL_SHA256 + * MBEDTLS_TLS_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_RSA_WITH_NULL_MD5 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_PSK_WITH_NULL_SHA + * + * Uncomment this macro to enable the NULL cipher and ciphersuites + */ +//#define MBEDTLS_CIPHER_NULL_CIPHER + +/** + * \def MBEDTLS_CIPHER_PADDING_PKCS7 + * + * MBEDTLS_CIPHER_PADDING_XXX: Uncomment or comment macros to add support for + * specific padding modes in the cipher layer with cipher modes that support + * padding (e.g. CBC) + * + * If you disable all padding modes, only full blocks can be used with CBC. + * + * Enable padding modes in the cipher layer. + */ +#define MBEDTLS_CIPHER_PADDING_PKCS7 +#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS +#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN +#define MBEDTLS_CIPHER_PADDING_ZEROS + +/** + * \def MBEDTLS_ENABLE_WEAK_CIPHERSUITES + * + * Enable weak ciphersuites in SSL / TLS. + * Warning: Only do so when you know what you are doing. This allows for + * channels with virtually no security at all! + * + * This enables the following ciphersuites: + * MBEDTLS_TLS_RSA_WITH_DES_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_DES_CBC_SHA + * + * Uncomment this macro to enable weak ciphersuites + * + * \warning DES is considered a weak cipher and its use constitutes a + * security risk. We recommend considering stronger ciphers instead. + */ +//#define MBEDTLS_ENABLE_WEAK_CIPHERSUITES + +/** + * \def MBEDTLS_REMOVE_ARC4_CIPHERSUITES + * + * Remove RC4 ciphersuites by default in SSL / TLS. + * This flag removes the ciphersuites based on RC4 from the default list as + * returned by mbedtls_ssl_list_ciphersuites(). However, it is still possible to + * enable (some of) them with mbedtls_ssl_conf_ciphersuites() by including them + * explicitly. + * + * Uncomment this macro to remove RC4 ciphersuites by default. + */ +#define MBEDTLS_REMOVE_ARC4_CIPHERSUITES + +/** + * \def MBEDTLS_REMOVE_3DES_CIPHERSUITES + * + * Remove 3DES ciphersuites by default in SSL / TLS. + * This flag removes the ciphersuites based on 3DES from the default list as + * returned by mbedtls_ssl_list_ciphersuites(). However, it is still possible + * to enable (some of) them with mbedtls_ssl_conf_ciphersuites() by including + * them explicitly. + * + * A man-in-the-browser attacker can recover authentication tokens sent through + * a TLS connection using a 3DES based cipher suite (see "On the Practical + * (In-)Security of 64-bit Block Ciphers" by Karthikeyan Bhargavan and Gaëtan + * Leurent, see https://sweet32.info/SWEET32_CCS16.pdf). If this attack falls + * in your threat model or you are unsure, then you should keep this option + * enabled to remove 3DES based cipher suites. + * + * Comment this macro to keep 3DES in the default ciphersuite list. + */ +#define MBEDTLS_REMOVE_3DES_CIPHERSUITES + +/** + * \def MBEDTLS_ECP_DP_SECP192R1_ENABLED + * + * MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve + * module. By default all supported curves are enabled. + * + * Comment macros to disable the curve and functions for it + */ +// #define MBEDTLS_ECP_DP_SECP192R1_ENABLED +#define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP384R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP521R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP192K1_ENABLED +// #define MBEDTLS_ECP_DP_SECP224K1_ENABLED +// #define MBEDTLS_ECP_DP_SECP256K1_ENABLED +// #define MBEDTLS_ECP_DP_BP256R1_ENABLED +// #define MBEDTLS_ECP_DP_BP384R1_ENABLED +// #define MBEDTLS_ECP_DP_BP512R1_ENABLED +// #define MBEDTLS_ECP_DP_CURVE25519_ENABLED +// #define MBEDTLS_ECP_DP_CURVE448_ENABLED + +/** + * \def MBEDTLS_ECP_NIST_OPTIM + * + * Enable specific 'modulo p' routines for each NIST prime. + * Depending on the prime and architecture, makes operations 4 to 8 times + * faster on the corresponding curve. + * + * Comment this macro to disable NIST curves optimisation. + */ +#define MBEDTLS_ECP_NIST_OPTIM + +/** + * \def MBEDTLS_ECP_RESTARTABLE + * + * Enable "non-blocking" ECC operations that can return early and be resumed. + * + * This allows various functions to pause by returning + * #MBEDTLS_ERR_ECP_IN_PROGRESS (or, for functions in the SSL module, + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) and then be called later again in + * order to further progress and eventually complete their operation. This is + * controlled through mbedtls_ecp_set_max_ops() which limits the maximum + * number of ECC operations a function may perform before pausing; see + * mbedtls_ecp_set_max_ops() for more information. + * + * This is useful in non-threaded environments if you want to avoid blocking + * for too long on ECC (and, hence, X.509 or SSL/TLS) operations. + * + * Uncomment this macro to enable restartable ECC computations. + * + * \note This option only works with the default software implementation of + * elliptic curve functionality. It is incompatible with + * MBEDTLS_ECP_ALT, MBEDTLS_ECDH_XXX_ALT, MBEDTLS_ECDSA_XXX_ALT + * and MBEDTLS_ECDH_LEGACY_CONTEXT. + */ +//#define MBEDTLS_ECP_RESTARTABLE + +/** + * \def MBEDTLS_ECDH_LEGACY_CONTEXT + * + * Use a backward compatible ECDH context. + * + * Mbed TLS supports two formats for ECDH contexts (#mbedtls_ecdh_context + * defined in `ecdh.h`). For most applications, the choice of format makes + * no difference, since all library functions can work with either format, + * except that the new format is incompatible with MBEDTLS_ECP_RESTARTABLE. + + * The new format used when this option is disabled is smaller + * (56 bytes on a 32-bit platform). In future versions of the library, it + * will support alternative implementations of ECDH operations. + * The new format is incompatible with applications that access + * context fields directly and with restartable ECP operations. + * + * Define this macro if you enable MBEDTLS_ECP_RESTARTABLE or if you + * want to access ECDH context fields directly. Otherwise you should + * comment out this macro definition. + * + * This option has no effect if #MBEDTLS_ECDH_C is not enabled. + * + * \note This configuration option is experimental. Future versions of the + * library may modify the way the ECDH context layout is configured + * and may modify the layout of the new context type. + */ +#define MBEDTLS_ECDH_LEGACY_CONTEXT + +/** + * \def MBEDTLS_ECDSA_DETERMINISTIC + * + * Enable deterministic ECDSA (RFC 6979). + * Standard ECDSA is "fragile" in the sense that lack of entropy when signing + * may result in a compromise of the long-term signing key. This is avoided by + * the deterministic variant. + * + * Requires: MBEDTLS_HMAC_DRBG_C + * + * Comment this macro to disable deterministic ECDSA. + */ +#define MBEDTLS_ECDSA_DETERMINISTIC + +/** + * \def MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + * + * Enable the PSK based ciphersuite modes in SSL / TLS. + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_RC4_128_SHA + */ +#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED + * + * Enable the DHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_DHM_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED + * + * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED + * + * Enable the RSA-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA + */ +#define MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_RSA_ENABLED + * + * Enable the RSA-only based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_RSA_WITH_RC4_128_MD5 + */ +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED + * + * Enable the DHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_DHM_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED + * + * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + * + * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_ECDSA_C, MBEDTLS_X509_CRT_PARSE_C, + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + * + * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED + * + * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED + * + * Enable the ECJPAKE based ciphersuite modes in SSL / TLS. + * + * \warning This is currently experimental. EC J-PAKE support is based on the + * Thread v1.0.0 specification; incompatible changes to the specification + * might still happen. For this reason, this is disabled by default. + * + * Requires: MBEDTLS_ECJPAKE_C + * MBEDTLS_SHA256_C + * MBEDTLS_ECP_DP_SECP256R1_ENABLED + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + */ +//#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED + +/** + * \def MBEDTLS_PK_PARSE_EC_EXTENDED + * + * Enhance support for reading EC keys using variants of SEC1 not allowed by + * RFC 5915 and RFC 5480. + * + * Currently this means parsing the SpecifiedECDomain choice of EC + * parameters (only known groups are supported, not arbitrary domains, to + * avoid validation issues). + * + * Disable if you only need to support RFC 5915 + 5480 key formats. + */ +#define MBEDTLS_PK_PARSE_EC_EXTENDED + +/** + * \def MBEDTLS_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of mbedtls_strerror() in + * third party libraries easier when MBEDTLS_ERROR_C is disabled + * (no effect when MBEDTLS_ERROR_C is enabled). + * + * You can safely disable this if MBEDTLS_ERROR_C is enabled, or if you're + * not using mbedtls_strerror() or error_strerror() in your application. + * + * Disable if you run into name conflicts and want to really remove the + * mbedtls_strerror() + */ +// #define MBEDTLS_ERROR_STRERROR_DUMMY + +/** + * \def MBEDTLS_GENPRIME + * + * Enable the prime-number generation code. + * + * Requires: MBEDTLS_BIGNUM_C + */ +#define MBEDTLS_GENPRIME + +/** + * \def MBEDTLS_FS_IO + * + * Enable functions that use the filesystem. + */ +// #define MBEDTLS_FS_IO + +/** + * \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources. These are the platform specific, + * mbedtls_timing_hardclock and HAVEGE based poll functions. + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. + */ +//#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/** + * \def MBEDTLS_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. + */ +#define MBEDTLS_NO_PLATFORM_ENTROPY + +/** + * \def MBEDTLS_ENTROPY_FORCE_SHA256 + * + * Force the entropy accumulator to use a SHA-256 accumulator instead of the + * default SHA-512 based one (if both are available). + * + * Requires: MBEDTLS_SHA256_C + * + * On 32-bit systems SHA-256 can be much faster than SHA-512. Use this option + * if you have performance concerns. + * + * This option is only useful if both MBEDTLS_SHA256_C and + * MBEDTLS_SHA512_C are defined. Otherwise the available hash module is used. + */ +//#define MBEDTLS_ENTROPY_FORCE_SHA256 + +/** + * \def MBEDTLS_ENTROPY_NV_SEED + * + * Enable the non-volatile (NV) seed file-based entropy source. + * (Also enables the NV seed read/write functions in the platform layer) + * + * This is crucial (if not required) on systems that do not have a + * cryptographic entropy source (in hardware or kernel) available. + * + * Requires: MBEDTLS_ENTROPY_C, MBEDTLS_PLATFORM_C + * + * \note The read/write functions that are used by the entropy source are + * determined in the platform layer, and can be modified at runtime and/or + * compile-time depending on the flags (MBEDTLS_PLATFORM_NV_SEED_*) used. + * + * \note If you use the default implementation functions that read a seedfile + * with regular fopen(), please make sure you make a seedfile with the + * proper name (defined in MBEDTLS_PLATFORM_STD_NV_SEED_FILE) and at + * least MBEDTLS_ENTROPY_BLOCK_SIZE bytes in size that can be read from + * and written to or you will get an entropy source error! The default + * implementation will only use the first MBEDTLS_ENTROPY_BLOCK_SIZE + * bytes from the file. + * + * \note The entropy collector will write to the seed file before entropy is + * given to an external source, to update it. + */ +//#define MBEDTLS_ENTROPY_NV_SEED + +/** + * \def MBEDTLS_MEMORY_DEBUG + * + * Enable debugging of buffer allocator memory issues. Automatically prints + * (to stderr) all (fatal) messages on memory allocation issues. Enables + * function for 'debug output' of allocated memory. + * + * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C + * + * Uncomment this macro to let the buffer allocator print out error messages. + */ +//#define MBEDTLS_MEMORY_DEBUG + +/** + * \def MBEDTLS_MEMORY_BACKTRACE + * + * Include backtrace information with each allocated block. + * + * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C + * GLIBC-compatible backtrace() an backtrace_symbols() support + * + * Uncomment this macro to include backtrace information + */ +//#define MBEDTLS_MEMORY_BACKTRACE + +/** + * \def MBEDTLS_PK_RSA_ALT_SUPPORT + * + * Support external private RSA keys (eg from a HSM) in the PK layer. + * + * Comment this macro to disable support for external private RSA keys. + */ +// #define MBEDTLS_PK_RSA_ALT_SUPPORT + +/** + * \def MBEDTLS_PKCS1_V15 + * + * Enable support for PKCS#1 v1.5 encoding. + * + * Requires: MBEDTLS_RSA_C + * + * This enables support for PKCS#1 v1.5 operations. + */ +#define MBEDTLS_PKCS1_V15 + +/** + * \def MBEDTLS_PKCS1_V21 + * + * Enable support for PKCS#1 v2.1 encoding. + * + * Requires: MBEDTLS_MD_C, MBEDTLS_RSA_C + * + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + */ +#define MBEDTLS_PKCS1_V21 + +/** + * \def MBEDTLS_PSA_CRYPTO_SPM + * + * When MBEDTLS_PSA_CRYPTO_SPM is defined, the code is built for SPM (Secure + * Partition Manager) integration which separates the code into two parts: a + * NSPE (Non-Secure Process Environment) and an SPE (Secure Process + * Environment). + * + * Module: library/psa_crypto.c + * Requires: MBEDTLS_PSA_CRYPTO_C + * + */ +//#define MBEDTLS_PSA_CRYPTO_SPM + +/** + * \def MBEDTLS_PSA_INJECT_ENTROPY + * + * Enable support for entropy injection at first boot. This feature is + * required on systems that do not have a built-in entropy source (TRNG). + * This feature is currently not supported on systems that have a built-in + * entropy source. + * + * Requires: MBEDTLS_PSA_CRYPTO_STORAGE_C, MBEDTLS_ENTROPY_NV_SEED + * + */ +//#define MBEDTLS_PSA_INJECT_ENTROPY + +/** + * \def MBEDTLS_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem + * for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * + */ +//#define MBEDTLS_RSA_NO_CRT + +/** + * \def MBEDTLS_SELF_TEST + * + * Enable the checkup functions (*_self_test). + */ +#define MBEDTLS_SELF_TEST + +/** + * \def MBEDTLS_SHA256_SMALLER + * + * Enable an implementation of SHA-256 that has lower ROM footprint but also + * lower performance. + * + * The default implementation is meant to be a reasonnable compromise between + * performance and size. This version optimizes more aggressively for size at + * the expense of performance. Eg on Cortex-M4 it reduces the size of + * mbedtls_sha256_process() from ~2KB to ~0.5KB for a performance hit of about + * 30%. + * + * Uncomment to enable the smaller implementation of SHA256. + */ +//#define MBEDTLS_SHA256_SMALLER + +/** + * \def MBEDTLS_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, mbed TLS can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define MBEDTLS_SSL_ALL_ALERT_MESSAGES + +/** + * \def MBEDTLS_SSL_RECORD_CHECKING + * + * Enable the function mbedtls_ssl_check_record() which can be used to check + * the validity and authenticity of an incoming record, to verify that it has + * not been seen before. These checks are performed without modifying the + * externally visible state of the SSL context. + * + * See mbedtls_ssl_check_record() for more information. + * + * Uncomment to enable support for record checking. + */ +#define MBEDTLS_SSL_RECORD_CHECKING + +/** + * \def MBEDTLS_SSL_DTLS_CONNECTION_ID + * + * Enable support for the DTLS Connection ID extension + * (version draft-ietf-tls-dtls-connection-id-05, + * https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05) + * which allows to identify DTLS connections across changes + * in the underlying transport. + * + * Setting this option enables the SSL APIs `mbedtls_ssl_set_cid()`, + * `mbedtls_ssl_get_peer_cid()` and `mbedtls_ssl_conf_cid()`. + * See the corresponding documentation for more information. + * + * \warning The Connection ID extension is still in draft state. + * We make no stability promises for the availability + * or the shape of the API controlled by this option. + * + * The maximum lengths of outgoing and incoming CIDs can be configured + * through the options + * - MBEDTLS_SSL_CID_OUT_LEN_MAX + * - MBEDTLS_SSL_CID_IN_LEN_MAX. + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Uncomment to enable the Connection ID extension. + */ +//#define MBEDTLS_SSL_DTLS_CONNECTION_ID + +/** + * \def MBEDTLS_SSL_ASYNC_PRIVATE + * + * Enable asynchronous external private key operations in SSL. This allows + * you to configure an SSL connection to call an external cryptographic + * module to perform private key operations instead of performing the + * operation inside the library. + * + */ +//#define MBEDTLS_SSL_ASYNC_PRIVATE + +/** + * \def MBEDTLS_SSL_CONTEXT_SERIALIZATION + * + * Enable serialization of the TLS context structures, through use of the + * functions mbedtls_ssl_context_save() and mbedtls_ssl_context_load(). + * + * This pair of functions allows one side of a connection to serialize the + * context associated with the connection, then free or re-use that context + * while the serialized state is persisted elsewhere, and finally deserialize + * that state to a live context for resuming read/write operations on the + * connection. From a protocol perspective, the state of the connection is + * unaffected, in particular this is entirely transparent to the peer. + * + * Note: this is distinct from TLS session resumption, which is part of the + * protocol and fully visible by the peer. TLS session resumption enables + * establishing new connections associated to a saved session with shorter, + * lighter handshakes, while context serialization is a local optimization in + * handling a single, potentially long-lived connection. + * + * Enabling these APIs makes some SSL structures larger, as 64 extra bytes are + * saved after the handshake to allow for more efficient serialization, so if + * you don't need this feature you'll save RAM by disabling it. + * + * Comment to disable the context serialization APIs. + */ +#define MBEDTLS_SSL_CONTEXT_SERIALIZATION + +/** + * \def MBEDTLS_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * + */ +//#define MBEDTLS_SSL_DEBUG_ALL + +/** \def MBEDTLS_SSL_ENCRYPT_THEN_MAC + * + * Enable support for Encrypt-then-MAC, RFC 7366. + * + * This allows peers that both support it to use a more robust protection for + * ciphersuites using CBC, providing deep resistance against timing attacks + * on the padding or underlying cipher. + * + * This only affects CBC ciphersuites, and is useless if none is defined. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1 or + * MBEDTLS_SSL_PROTO_TLS1_1 or + * MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for Encrypt-then-MAC + */ +#define MBEDTLS_SSL_ENCRYPT_THEN_MAC + +/** \def MBEDTLS_SSL_EXTENDED_MASTER_SECRET + * + * Enable support for Extended Master Secret, aka Session Hash + * (draft-ietf-tls-session-hash-02). + * + * This was introduced as "the proper fix" to the Triple Handshake familiy of + * attacks, but it is recommended to always use it (even if you disable + * renegotiation), since it actually fixes a more fundamental issue in the + * original SSL/TLS design, and has implications beyond Triple Handshake. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1 or + * MBEDTLS_SSL_PROTO_TLS1_1 or + * MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for Extended Master Secret. + */ +#define MBEDTLS_SSL_EXTENDED_MASTER_SECRET + +/** + * \def MBEDTLS_SSL_FALLBACK_SCSV + * + * Enable support for FALLBACK_SCSV (draft-ietf-tls-downgrade-scsv-00). + * + * For servers, it is recommended to always enable this, unless you support + * only one version of TLS, or know for sure that none of your clients + * implements a fallback strategy. + * + * For clients, you only need this if you're using a fallback strategy, which + * is not recommended in the first place, unless you absolutely need it to + * interoperate with buggy (version-intolerant) servers. + * + * Comment this macro to disable support for FALLBACK_SCSV + */ +#define MBEDTLS_SSL_FALLBACK_SCSV + +/** + * \def MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + * + * This option controls the availability of the API mbedtls_ssl_get_peer_cert() + * giving access to the peer's certificate after completion of the handshake. + * + * Unless you need mbedtls_ssl_peer_cert() in your application, it is + * recommended to disable this option for reduced RAM usage. + * + * \note If this option is disabled, mbedtls_ssl_get_peer_cert() is still + * defined, but always returns \c NULL. + * + * \note This option has no influence on the protection against the + * triple handshake attack. Even if it is disabled, Mbed TLS will + * still ensure that certificates do not change during renegotiation, + * for exaple by keeping a hash of the peer's certificate. + * + * Comment this macro to disable storing the peer's certificate + * after the handshake. + */ +#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + +/** + * \def MBEDTLS_SSL_HW_RECORD_ACCEL + * + * Enable hooking functions in SSL module for hardware acceleration of + * individual records. + * + * Uncomment this macro to enable hooking functions. + */ +//#define MBEDTLS_SSL_HW_RECORD_ACCEL + +/** + * \def MBEDTLS_SSL_CBC_RECORD_SPLITTING + * + * Enable 1/n-1 record splitting for CBC mode in SSLv3 and TLS 1.0. + * + * This is a countermeasure to the BEAST attack, which also minimizes the risk + * of interoperability issues compared to sending 0-length records. + * + * Comment this macro to disable 1/n-1 record splitting. + */ +#define MBEDTLS_SSL_CBC_RECORD_SPLITTING + +/** + * \def MBEDTLS_SSL_RENEGOTIATION + * + * Enable support for TLS renegotiation. + * + * The two main uses of renegotiation are (1) refresh keys on long-lived + * connections and (2) client authentication after the initial handshake. + * If you don't need renegotiation, it's probably better to disable it, since + * it has been associated with security issues in the past and is easy to + * misuse/misunderstand. + * + * Comment this to disable support for renegotiation. + * + * \note Even if this option is disabled, both client and server are aware + * of the Renegotiation Indication Extension (RFC 5746) used to + * prevent the SSL renegotiation attack (see RFC 5746 Sect. 1). + * (See \c mbedtls_ssl_conf_legacy_renegotiation for the + * configuration of this extension). + * + */ +#define MBEDTLS_SSL_RENEGOTIATION + +/** + * \def MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + * + * Enable support for receiving and parsing SSLv2 Client Hello messages for the + * SSL Server module (MBEDTLS_SSL_SRV_C). + * + * Uncomment this macro to enable support for SSLv2 Client Hello messages. + */ +//#define MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + +/** + * \def MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE + * + * Pick the ciphersuite according to the client's preferences rather than ours + * in the SSL Server module (MBEDTLS_SSL_SRV_C). + * + * Uncomment this macro to respect client's ciphersuite order + */ +//#define MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE + +/** + * \def MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + * + * Enable support for RFC 6066 max_fragment_length extension in SSL. + * + * Comment this macro to disable support for the max_fragment_length extension + */ +#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + +/** + * \def MBEDTLS_SSL_PROTO_SSL3 + * + * Enable support for SSL 3.0. + * + * Requires: MBEDTLS_MD5_C + * MBEDTLS_SHA1_C + * + * Comment this macro to disable support for SSL 3.0 + */ +//#define MBEDTLS_SSL_PROTO_SSL3 + +/** + * \def MBEDTLS_SSL_PROTO_TLS1 + * + * Enable support for TLS 1.0. + * + * Requires: MBEDTLS_MD5_C + * MBEDTLS_SHA1_C + * + * Comment this macro to disable support for TLS 1.0 + */ +#define MBEDTLS_SSL_PROTO_TLS1 + +/** + * \def MBEDTLS_SSL_PROTO_TLS1_1 + * + * Enable support for TLS 1.1 (and DTLS 1.0 if DTLS is enabled). + * + * Requires: MBEDTLS_MD5_C + * MBEDTLS_SHA1_C + * + * Comment this macro to disable support for TLS 1.1 / DTLS 1.0 + */ +#define MBEDTLS_SSL_PROTO_TLS1_1 + +/** + * \def MBEDTLS_SSL_PROTO_TLS1_2 + * + * Enable support for TLS 1.2 (and DTLS 1.2 if DTLS is enabled). + * + * Requires: MBEDTLS_SHA1_C or MBEDTLS_SHA256_C or MBEDTLS_SHA512_C + * (Depends on ciphersuites) + * + * Comment this macro to disable support for TLS 1.2 / DTLS 1.2 + */ +#define MBEDTLS_SSL_PROTO_TLS1_2 + +/** + * \def MBEDTLS_SSL_PROTO_DTLS + * + * Enable support for DTLS (all available versions). + * + * Enable this and MBEDTLS_SSL_PROTO_TLS1_1 to enable DTLS 1.0, + * and/or this and MBEDTLS_SSL_PROTO_TLS1_2 to enable DTLS 1.2. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_1 + * or MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for DTLS + */ +#define MBEDTLS_SSL_PROTO_DTLS + +/** + * \def MBEDTLS_SSL_ALPN + * + * Enable support for RFC 7301 Application Layer Protocol Negotiation. + * + * Comment this macro to disable support for ALPN. + */ +#define MBEDTLS_SSL_ALPN + +/** + * \def MBEDTLS_SSL_DTLS_ANTI_REPLAY + * + * Enable support for the anti-replay mechanism in DTLS. + * + * Requires: MBEDTLS_SSL_TLS_C + * MBEDTLS_SSL_PROTO_DTLS + * + * \warning Disabling this is often a security risk! + * See mbedtls_ssl_conf_dtls_anti_replay() for details. + * + * Comment this to disable anti-replay in DTLS. + */ +#define MBEDTLS_SSL_DTLS_ANTI_REPLAY + +/** + * \def MBEDTLS_SSL_DTLS_HELLO_VERIFY + * + * Enable support for HelloVerifyRequest on DTLS servers. + * + * This feature is highly recommended to prevent DTLS servers being used as + * amplifiers in DoS attacks against other hosts. It should always be enabled + * unless you know for sure amplification cannot be a problem in the + * environment in which your server operates. + * + * \warning Disabling this can ba a security risk! (see above) + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Comment this to disable support for HelloVerifyRequest. + */ +#define MBEDTLS_SSL_DTLS_HELLO_VERIFY + +/** + * \def MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + * + * Enable server-side support for clients that reconnect from the same port. + * + * Some clients unexpectedly close the connection and try to reconnect using the + * same source port. This needs special support from the server to handle the + * new connection securely, as described in section 4.2.8 of RFC 6347. This + * flag enables that support. + * + * Requires: MBEDTLS_SSL_DTLS_HELLO_VERIFY + * + * Comment this to disable support for clients reusing the source port. + */ +#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + +/** + * \def MBEDTLS_SSL_DTLS_BADMAC_LIMIT + * + * Enable support for a limit of records with bad MAC. + * + * See mbedtls_ssl_conf_dtls_badmac_limit(). + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + */ +#define MBEDTLS_SSL_DTLS_BADMAC_LIMIT + +/** + * \def MBEDTLS_SSL_SESSION_TICKETS + * + * Enable support for RFC 5077 session tickets in SSL. + * Client-side, provides full support for session tickets (maintenance of a + * session store remains the responsibility of the application, though). + * Server-side, you also need to provide callbacks for writing and parsing + * tickets, including authenticated encryption and key management. Example + * callbacks are provided by MBEDTLS_SSL_TICKET_C. + * + * Comment this macro to disable support for SSL session tickets + */ +#define MBEDTLS_SSL_SESSION_TICKETS + +/** + * \def MBEDTLS_SSL_EXPORT_KEYS + * + * Enable support for exporting key block and master secret. + * This is required for certain users of TLS, e.g. EAP-TLS. + * + * Comment this macro to disable support for key export + */ +#define MBEDTLS_SSL_EXPORT_KEYS + +/** + * \def MBEDTLS_SSL_SERVER_NAME_INDICATION + * + * Enable support for RFC 6066 server name indication (SNI) in SSL. + * + * Requires: MBEDTLS_X509_CRT_PARSE_C + * + * Comment this macro to disable support for server name indication in SSL + */ +#define MBEDTLS_SSL_SERVER_NAME_INDICATION + +/** + * \def MBEDTLS_SSL_TRUNCATED_HMAC + * + * Enable support for RFC 6066 truncated HMAC in SSL. + * + * Comment this macro to disable support for truncated HMAC in SSL + */ +#define MBEDTLS_SSL_TRUNCATED_HMAC + +/** + * \def MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT + * + * Fallback to old (pre-2.7), non-conforming implementation of the truncated + * HMAC extension which also truncates the HMAC key. Note that this option is + * only meant for a transitory upgrade period and is likely to be removed in + * a future version of the library. + * + * \warning The old implementation is non-compliant and has a security weakness + * (2^80 brute force attack on the HMAC key used for a single, + * uninterrupted connection). This should only be enabled temporarily + * when (1) the use of truncated HMAC is essential in order to save + * bandwidth, and (2) the peer is an Mbed TLS stack that doesn't use + * the fixed implementation yet (pre-2.7). + * + * \deprecated This option is deprecated and will likely be removed in a + * future version of Mbed TLS. + * + * Uncomment to fallback to old, non-compliant truncated HMAC implementation. + * + * Requires: MBEDTLS_SSL_TRUNCATED_HMAC + */ +//#define MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT + +/** + * \def MBEDTLS_THREADING_ALT + * + * Provide your own alternate threading implementation. + * + * Requires: MBEDTLS_THREADING_C + * + * Uncomment this to allow your own alternate threading implementation. + */ +//#define MBEDTLS_THREADING_ALT + +/** + * \def MBEDTLS_THREADING_PTHREAD + * + * Enable the pthread wrapper layer for the threading layer. + * + * Requires: MBEDTLS_THREADING_C + * + * Uncomment this to enable pthread mutexes. + */ +//#define MBEDTLS_THREADING_PTHREAD + +/** + * \def MBEDTLS_USE_PSA_CRYPTO + * + * Make the X.509 and TLS library use PSA for cryptographic operations, and + * enable new APIs for using keys handled by PSA Crypto. + * + * \note Development of this option is currently in progress, and parts + * of the X.509 and TLS modules are not ported to PSA yet. However, these parts + * will still continue to work as usual, so enabling this option should not + * break backwards compatibility. + * + * \warning The PSA Crypto API is in beta stage. While you're welcome to + * experiment using it, incompatible API changes are still possible, and some + * parts may not have reached the same quality as the rest of Mbed TLS yet. + * + * \warning This option enables new Mbed TLS APIs that are dependent on the + * PSA Crypto API, so can't come with the same stability guarantees as the + * rest of the Mbed TLS APIs. You're welcome to experiment with them, but for + * now, access to these APIs is opt-in (via enabling the present option), in + * order to clearly differentiate them from the stable Mbed TLS APIs. + * + * Requires: MBEDTLS_PSA_CRYPTO_C. + * + * Uncomment this to enable internal use of PSA Crypto and new associated APIs. + */ +//#define MBEDTLS_USE_PSA_CRYPTO + +/** + * \def MBEDTLS_VERSION_FEATURES + * + * Allow run-time checking of compile-time enabled features. Thus allowing users + * to check at run-time if the library is for instance compiled with threading + * support via mbedtls_version_check_feature(). + * + * Requires: MBEDTLS_VERSION_C + * + * Comment this to disable run-time checking and save ROM space + */ +// #define MBEDTLS_VERSION_FEATURES + +/** + * \def MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3 + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an extension in a v1 or v2 certificate. + * + * Uncomment to prevent an error. + */ +//#define MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3 + +/** + * \def MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an unknown critical extension. + * + * \warning Depending on your PKI use, enabling this can be a security risk! + * + * Uncomment to prevent an error. + */ +//#define MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + +/** + * \def MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK + * + * If set, this enables the X.509 API `mbedtls_x509_crt_verify_with_ca_cb()` + * and the SSL API `mbedtls_ssl_conf_ca_cb()` which allow users to configure + * the set of trusted certificates through a callback instead of a linked + * list. + * + * This is useful for example in environments where a large number of trusted + * certificates is present and storing them in a linked list isn't efficient + * enough, or when the set of trusted certificates changes frequently. + * + * See the documentation of `mbedtls_x509_crt_verify_with_ca_cb()` and + * `mbedtls_ssl_conf_ca_cb()` for more information. + * + * Uncomment to enable trusted certificate callbacks. + */ +//#define MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK + +/** + * \def MBEDTLS_X509_CHECK_KEY_USAGE + * + * Enable verification of the keyUsage extension (CA and leaf certificates). + * + * Disabling this avoids problems with mis-issued and/or misused + * (intermediate) CA and leaf certificates. + * + * \warning Depending on your PKI use, disabling this can be a security risk! + * + * Comment to skip keyUsage checking for both CA and leaf certificates. + */ +#define MBEDTLS_X509_CHECK_KEY_USAGE + +/** + * \def MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE + * + * Enable verification of the extendedKeyUsage extension (leaf certificates). + * + * Disabling this avoids problems with mis-issued and/or misused certificates. + * + * \warning Depending on your PKI use, disabling this can be a security risk! + * + * Comment to skip extendedKeyUsage checking for certificates. + */ +#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE + +/** + * \def MBEDTLS_X509_RSASSA_PSS_SUPPORT + * + * Enable parsing and verification of X.509 certificates, CRLs and CSRS + * signed with RSASSA-PSS (aka PKCS#1 v2.1). + * + * Comment this macro to disallow using RSASSA-PSS in certificates. + */ +#define MBEDTLS_X509_RSASSA_PSS_SUPPORT + +/** + * \def MBEDTLS_ZLIB_SUPPORT + * + * If set, the SSL/TLS module uses ZLIB to support compression and + * decompression of packet data. + * + * \warning TLS-level compression MAY REDUCE SECURITY! See for example the + * CRIME attack. Before enabling this option, you should examine with care if + * CRIME or similar exploits may be applicable to your use case. + * + * \note Currently compression can't be used with DTLS. + * + * \deprecated This feature is deprecated and will be removed + * in the next major revision of the library. + * + * Used in: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This feature requires zlib library and headers to be present. + * + * Uncomment to enable use of ZLIB + */ +//#define MBEDTLS_ZLIB_SUPPORT +/* \} name SECTION: mbed TLS feature support */ + +/** + * \name SECTION: mbed TLS modules + * + * This section enables or disables entire modules in mbed TLS + * \{ + */ + +/** + * \def MBEDTLS_AESNI_C + * + * Enable AES-NI support on x86-64. + * + * Module: library/aesni.c + * Caller: library/aes.c + * + * Requires: MBEDTLS_HAVE_ASM + * + * This modules adds support for the AES-NI instructions on x86-64 + */ +#define MBEDTLS_AESNI_C + +/** + * \def MBEDTLS_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/cipher.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA + * + * PEM_PARSE uses AES for decrypting encrypted keys. + */ +#define MBEDTLS_AES_C + +/** + * \def MBEDTLS_ARC4_C + * + * Enable the ARCFOUR stream cipher. + * + * Module: library/arc4.c + * Caller: library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA + * MBEDTLS_TLS_RSA_WITH_RC4_128_SHA + * MBEDTLS_TLS_RSA_WITH_RC4_128_MD5 + * MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA + * MBEDTLS_TLS_PSK_WITH_RC4_128_SHA + * + * \warning ARC4 is considered a weak cipher and its use constitutes a + * security risk. If possible, we recommend avoidng dependencies on + * it, and considering stronger ciphers instead. + * + */ +#define MBEDTLS_ARC4_C + +/** + * \def MBEDTLS_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509.c + * library/dhm.c + * library/pkcs12.c + * library/pkcs5.c + * library/pkparse.c + */ +#define MBEDTLS_ASN1_PARSE_C + +/** + * \def MBEDTLS_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + * Caller: library/ecdsa.c + * library/pkwrite.c + * library/x509_create.c + * library/x509write_crt.c + * library/x509write_csr.c + */ +#define MBEDTLS_ASN1_WRITE_C + +/** + * \def MBEDTLS_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +#define MBEDTLS_BASE64_C + +/** + * \def MBEDTLS_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * Caller: library/dhm.c + * library/ecp.c + * library/ecdsa.c + * library/rsa.c + * library/rsa_internal.c + * library/ssl_tls.c + * + * This module is required for RSA, DHM and ECC (ECDH, ECDSA) support. + */ +#define MBEDTLS_BIGNUM_C + +/** + * \def MBEDTLS_BLOWFISH_C + * + * Enable the Blowfish block cipher. + * + * Module: library/blowfish.c + */ +#define MBEDTLS_BLOWFISH_C + +/** + * \def MBEDTLS_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_CAMELLIA_C + +/** + * \def MBEDTLS_ARIA_C + * + * Enable the ARIA block cipher. + * + * Module: library/aria.c + * Caller: library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * + * MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 + */ +//#define MBEDTLS_ARIA_C + +/** + * \def MBEDTLS_CCM_C + * + * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher. + * + * Module: library/ccm.c + * + * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C + * + * This module enables the AES-CCM ciphersuites, if other requisites are + * enabled as well. + */ +#define MBEDTLS_CCM_C + +/** + * \def MBEDTLS_CERTS_C + * + * Enable the test certificates. + * + * Module: library/certs.c + * Caller: + * + * This module is used for testing (ssl_client/server). + */ +#define MBEDTLS_CERTS_C + +/** + * \def MBEDTLS_CHACHA20_C + * + * Enable the ChaCha20 stream cipher. + * + * Module: library/chacha20.c + */ +#define MBEDTLS_CHACHA20_C + +/** + * \def MBEDTLS_CHACHAPOLY_C + * + * Enable the ChaCha20-Poly1305 AEAD algorithm. + * + * Module: library/chachapoly.c + * + * This module requires: MBEDTLS_CHACHA20_C, MBEDTLS_POLY1305_C + */ +#define MBEDTLS_CHACHAPOLY_C + +/** + * \def MBEDTLS_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: library/ssl_tls.c + * + * Uncomment to enable generic cipher wrappers. + */ +#define MBEDTLS_CIPHER_C + +/** + * \def MBEDTLS_CMAC_C + * + * Enable the CMAC (Cipher-based Message Authentication Code) mode for block + * ciphers. + * + * Module: library/cmac.c + * + * Requires: MBEDTLS_AES_C or MBEDTLS_DES_C + * + */ +//#define MBEDTLS_CMAC_C + +/** + * \def MBEDTLS_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-based random generator. + * The CTR_DRBG generator uses AES-256 by default. + * To use AES-128 instead, enable MBEDTLS_CTR_DRBG_USE_128_BIT_KEY below. + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: MBEDTLS_AES_C + * + * This module provides the CTR_DRBG AES random number generator. + */ +#define MBEDTLS_CTR_DRBG_C + +/** + * \def MBEDTLS_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +#define MBEDTLS_DEBUG_C + +/** + * \def MBEDTLS_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA + * + * PEM_PARSE uses DES/3DES for decrypting encrypted keys. + * + * \warning DES is considered a weak cipher and its use constitutes a + * security risk. We recommend considering stronger ciphers instead. + */ +#define MBEDTLS_DES_C + +/** + * \def MBEDTLS_DHM_C + * + * Enable the Diffie-Hellman-Merkle module. + * + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * DHE-RSA, DHE-PSK + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_DHM_C + +/** + * \def MBEDTLS_ECDH_C + * + * Enable the elliptic curve Diffie-Hellman library. + * + * Module: library/ecdh.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA, ECDHE-RSA, DHE-PSK + * + * Requires: MBEDTLS_ECP_C + */ +#define MBEDTLS_ECDH_C + +/** + * \def MBEDTLS_ECDSA_C + * + * Enable the elliptic curve DSA library. + * + * Module: library/ecdsa.c + * Caller: + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA + * + * Requires: MBEDTLS_ECP_C, MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C + */ +#define MBEDTLS_ECDSA_C + +/** + * \def MBEDTLS_ECJPAKE_C + * + * Enable the elliptic curve J-PAKE library. + * + * \warning This is currently experimental. EC J-PAKE support is based on the + * Thread v1.0.0 specification; incompatible changes to the specification + * might still happen. For this reason, this is disabled by default. + * + * Module: library/ecjpake.c + * Caller: + * + * This module is used by the following key exchanges: + * ECJPAKE + * + * Requires: MBEDTLS_ECP_C, MBEDTLS_MD_C + */ +//#define MBEDTLS_ECJPAKE_C + +/** + * \def MBEDTLS_ECP_C + * + * Enable the elliptic curve over GF(p) library. + * + * Module: library/ecp.c + * Caller: library/ecdh.c + * library/ecdsa.c + * library/ecjpake.c + * + * Requires: MBEDTLS_BIGNUM_C and at least one MBEDTLS_ECP_DP_XXX_ENABLED + */ +#define MBEDTLS_ECP_C + +/** + * \def MBEDTLS_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: MBEDTLS_SHA512_C or MBEDTLS_SHA256_C + * + * This module provides a generic entropy pool + */ +#define MBEDTLS_ENTROPY_C + +/** + * \def MBEDTLS_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables mbedtls_strerror(). + */ +// #define MBEDTLS_ERROR_C + +/** + * \def MBEDTLS_GCM_C + * + * Enable the Galois/Counter Mode (GCM) for AES. + * + * Module: library/gcm.c + * + * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C + * + * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other + * requisites are enabled as well. + */ +#define MBEDTLS_GCM_C + +/** + * \def MBEDTLS_HAVEGE_C + * + * Enable the HAVEGE random generator. + * + * Warning: the HAVEGE random generator is not suitable for virtualized + * environments + * + * Warning: the HAVEGE random generator is dependent on timing and specific + * processor traits. It is therefore not advised to use HAVEGE as + * your applications primary random generator or primary entropy pool + * input. As a secondary input to your entropy pool, it IS able add + * the (limited) extra entropy it provides. + * + * Module: library/havege.c + * Caller: + * + * Requires: MBEDTLS_TIMING_C + * + * Uncomment to enable the HAVEGE random generator. + */ +//#define MBEDTLS_HAVEGE_C + +/** + * \def MBEDTLS_HKDF_C + * + * Enable the HKDF algorithm (RFC 5869). + * + * Module: library/hkdf.c + * Caller: + * + * Requires: MBEDTLS_MD_C + * + * This module adds support for the Hashed Message Authentication Code + * (HMAC)-based key derivation function (HKDF). + */ +#define MBEDTLS_HKDF_C + +/** + * \def MBEDTLS_HMAC_DRBG_C + * + * Enable the HMAC_DRBG random generator. + * + * Module: library/hmac_drbg.c + * Caller: + * + * Requires: MBEDTLS_MD_C + * + * Uncomment to enable the HMAC_DRBG random number geerator. + */ +#define MBEDTLS_HMAC_DRBG_C + +/** + * \def MBEDTLS_NIST_KW_C + * + * Enable the Key Wrapping mode for 128-bit block ciphers, + * as defined in NIST SP 800-38F. Only KW and KWP modes + * are supported. At the moment, only AES is approved by NIST. + * + * Module: library/nist_kw.c + * + * Requires: MBEDTLS_AES_C and MBEDTLS_CIPHER_C + */ +//#define MBEDTLS_NIST_KW_C + +/** + * \def MBEDTLS_MD_C + * + * Enable the generic message digest layer. + * + * Module: library/md.c + * Caller: + * + * Uncomment to enable generic message digest wrappers. + */ +#define MBEDTLS_MD_C + +/** + * \def MBEDTLS_MD2_C + * + * Enable the MD2 hash algorithm. + * + * Module: library/md2.c + * Caller: + * + * Uncomment to enable support for (rare) MD2-signed X.509 certs. + * + * \warning MD2 is considered a weak message digest and its use constitutes a + * security risk. If possible, we recommend avoiding dependencies on + * it, and considering stronger message digests instead. + * + */ +//#define MBEDTLS_MD2_C + +/** + * \def MBEDTLS_MD4_C + * + * Enable the MD4 hash algorithm. + * + * Module: library/md4.c + * Caller: + * + * Uncomment to enable support for (rare) MD4-signed X.509 certs. + * + * \warning MD4 is considered a weak message digest and its use constitutes a + * security risk. If possible, we recommend avoiding dependencies on + * it, and considering stronger message digests instead. + * + */ +//#define MBEDTLS_MD4_C + +/** + * \def MBEDTLS_MD5_C + * + * Enable the MD5 hash algorithm. + * + * Module: library/md5.c + * Caller: library/md.c + * library/pem.c + * library/ssl_tls.c + * + * This module is required for SSL/TLS up to version 1.1, and for TLS 1.2 + * depending on the handshake parameters. Further, it is used for checking + * MD5-signed certificates, and for PBKDF1 when decrypting PEM-encoded + * encrypted keys. + * + * \warning MD5 is considered a weak message digest and its use constitutes a + * security risk. If possible, we recommend avoiding dependencies on + * it, and considering stronger message digests instead. + * + */ +#define MBEDTLS_MD5_C + +/** + * \def MBEDTLS_MEMORY_BUFFER_ALLOC_C + * + * Enable the buffer allocator implementation that makes use of a (stack) + * based buffer to 'allocate' dynamic memory. (replaces calloc() and free() + * calls) + * + * Module: library/memory_buffer_alloc.c + * + * Requires: MBEDTLS_PLATFORM_C + * MBEDTLS_PLATFORM_MEMORY (to use it within mbed TLS) + * + * Enable this module to enable the buffer memory allocator. + */ +//#define MBEDTLS_MEMORY_BUFFER_ALLOC_C + +/** + * \def MBEDTLS_NET_C + * + * Enable the TCP and UDP over IPv6/IPv4 networking routines. + * + * \note This module only works on POSIX/Unix (including Linux, BSD and OS X) + * and Windows. For other platforms, you'll want to disable it, and write your + * own networking callbacks to be passed to \c mbedtls_ssl_set_bio(). + * + * \note See also our Knowledge Base article about porting to a new + * environment: + * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * + * Module: library/net_sockets.c + * + * This module provides networking routines. + */ +// #define MBEDTLS_NET_C + +/** + * \def MBEDTLS_OID_C + * + * Enable the OID database. + * + * Module: library/oid.c + * Caller: library/asn1write.c + * library/pkcs5.c + * library/pkparse.c + * library/pkwrite.c + * library/rsa.c + * library/x509.c + * library/x509_create.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * This modules translates between OIDs and internal values. + */ +#define MBEDTLS_OID_C + +/** + * \def MBEDTLS_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * Requires: MBEDTLS_HAVE_ASM + * + * This modules adds support for the VIA PadLock on x86. + */ +#define MBEDTLS_PADLOCK_C + +/** + * \def MBEDTLS_PEM_PARSE_C + * + * Enable PEM decoding / parsing. + * + * Module: library/pem.c + * Caller: library/dhm.c + * library/pkparse.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_BASE64_C + * + * This modules adds support for decoding / parsing PEM files. + */ +#define MBEDTLS_PEM_PARSE_C + +/** + * \def MBEDTLS_PEM_WRITE_C + * + * Enable PEM encoding / writing. + * + * Module: library/pem.c + * Caller: library/pkwrite.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * Requires: MBEDTLS_BASE64_C + * + * This modules adds support for encoding / writing PEM files. + */ +#define MBEDTLS_PEM_WRITE_C + +/** + * \def MBEDTLS_PK_C + * + * Enable the generic public (asymetric) key layer. + * + * Module: library/pk.c + * Caller: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: MBEDTLS_RSA_C or MBEDTLS_ECP_C + * + * Uncomment to enable generic public key wrappers. + */ +#define MBEDTLS_PK_C + +/** + * \def MBEDTLS_PK_PARSE_C + * + * Enable the generic public (asymetric) key parser. + * + * Module: library/pkparse.c + * Caller: library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_PK_C + * + * Uncomment to enable generic public key parse functions. + */ +#define MBEDTLS_PK_PARSE_C + +/** + * \def MBEDTLS_PK_WRITE_C + * + * Enable the generic public (asymetric) key writer. + * + * Module: library/pkwrite.c + * Caller: library/x509write.c + * + * Requires: MBEDTLS_PK_C + * + * Uncomment to enable generic public key write functions. + */ +#define MBEDTLS_PK_WRITE_C + +/** + * \def MBEDTLS_PKCS5_C + * + * Enable PKCS#5 functions. + * + * Module: library/pkcs5.c + * + * Requires: MBEDTLS_MD_C + * + * This module adds support for the PKCS#5 functions. + */ +#define MBEDTLS_PKCS5_C + +/** + * \def MBEDTLS_PKCS11_C + * + * Enable wrapper for PKCS#11 smartcard support. + * + * Module: library/pkcs11.c + * Caller: library/pk.c + * + * Requires: MBEDTLS_PK_C + * + * This module enables SSL/TLS PKCS #11 smartcard support. + * Requires the presence of the PKCS#11 helper library (libpkcs11-helper) + */ +//#define MBEDTLS_PKCS11_C + +/** + * \def MBEDTLS_PKCS12_C + * + * Enable PKCS#12 PBE functions. + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/pkparse.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_CIPHER_C, MBEDTLS_MD_C + * Can use: MBEDTLS_ARC4_C + * + * This module enables PKCS#12 functions. + */ +#define MBEDTLS_PKCS12_C + +/** + * \def MBEDTLS_PLATFORM_C + * + * Enable the platform abstraction layer that allows you to re-assign + * functions like calloc(), free(), snprintf(), printf(), fprintf(), exit(). + * + * Enabling MBEDTLS_PLATFORM_C enables to use of MBEDTLS_PLATFORM_XXX_ALT + * or MBEDTLS_PLATFORM_XXX_MACRO directives, allowing the functions mentioned + * above to be specified at runtime or compile time respectively. + * + * \note This abstraction layer must be enabled on Windows (including MSYS2) + * as other module rely on it for a fixed snprintf implementation. + * + * Module: library/platform.c + * Caller: Most other .c files + * + * This module enables abstraction of common (libc) functions. + */ +#define MBEDTLS_PLATFORM_C + +/** + * \def MBEDTLS_POLY1305_C + * + * Enable the Poly1305 MAC algorithm. + * + * Module: library/poly1305.c + * Caller: library/chachapoly.c + */ +#define MBEDTLS_POLY1305_C + +/** + * \def MBEDTLS_PSA_CRYPTO_C + * + * Enable the Platform Security Architecture cryptography API. + * + * \warning The PSA Crypto API is still beta status. While you're welcome to + * experiment using it, incompatible API changes are still possible, and some + * parts may not have reached the same quality as the rest of Mbed TLS yet. + * + * Module: crypto/library/psa_crypto.c + * + * Requires: MBEDTLS_CTR_DRBG_C, MBEDTLS_ENTROPY_C + * + */ +#define MBEDTLS_PSA_CRYPTO_C + +/** + * \def MBEDTLS_PSA_CRYPTO_STORAGE_C + * + * Enable the Platform Security Architecture persistent key storage. + * + * Module: crypto/library/psa_crypto_storage.c + * + * Requires: MBEDTLS_PSA_CRYPTO_C, + * either MBEDTLS_PSA_ITS_FILE_C or a native implementation of + * the PSA ITS interface + */ +//#define MBEDTLS_PSA_CRYPTO_STORAGE_C + +/** + * \def MBEDTLS_PSA_ITS_FILE_C + * + * Enable the emulation of the Platform Security Architecture + * Internal Trusted Storage (PSA ITS) over files. + * + * Module: crypto/library/psa_its_file.c + * + * Requires: MBEDTLS_FS_IO + * + */ +//#define MBEDTLS_PSA_ITS_FILE_C + +/** + * \def MBEDTLS_RIPEMD160_C + * + * Enable the RIPEMD-160 hash algorithm. + * + * Module: library/ripemd160.c + * Caller: library/md.c + * + */ +#define MBEDTLS_RIPEMD160_C + +/** + * \def MBEDTLS_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * library/rsa_internal.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * This module is used by the following key exchanges: + * RSA, DHE-RSA, ECDHE-RSA, RSA-PSK + * + * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C + */ +#define MBEDTLS_RSA_C + +/** + * \def MBEDTLS_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509write_crt.c + * + * This module is required for SSL/TLS up to version 1.1, for TLS 1.2 + * depending on the handshake parameters, and for SHA1-signed certificates. + * + * \warning SHA-1 is considered a weak message digest and its use constitutes + * a security risk. If possible, we recommend avoiding dependencies + * on it, and considering stronger message digests instead. + * + */ +#define MBEDTLS_SHA1_C + +/** + * \def MBEDTLS_SHA256_C + * + * Enable the SHA-224 and SHA-256 cryptographic hash algorithms. + * + * Module: library/sha256.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module adds support for SHA-224 and SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define MBEDTLS_SHA256_C + +/** + * \def MBEDTLS_SHA512_C + * + * Enable the SHA-384 and SHA-512 cryptographic hash algorithms. + * + * Module: library/sha512.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This module adds support for SHA-384 and SHA-512. + */ +#define MBEDTLS_SHA512_C + +/** + * \def MBEDTLS_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: MBEDTLS_SSL_CACHE_C + */ +#define MBEDTLS_SSL_CACHE_C + +/** + * \def MBEDTLS_SSL_COOKIE_C + * + * Enable basic implementation of DTLS cookies for hello verification. + * + * Module: library/ssl_cookie.c + * Caller: + */ +#define MBEDTLS_SSL_COOKIE_C + +/** + * \def MBEDTLS_SSL_TICKET_C + * + * Enable an implementation of TLS server-side callbacks for session tickets. + * + * Module: library/ssl_ticket.c + * Caller: + * + * Requires: MBEDTLS_CIPHER_C + */ +#define MBEDTLS_SSL_TICKET_C + +/** + * \def MBEDTLS_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl_cli.c + * Caller: + * + * Requires: MBEDTLS_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define MBEDTLS_SSL_CLI_C + +/** + * \def MBEDTLS_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl_srv.c + * Caller: + * + * Requires: MBEDTLS_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +#define MBEDTLS_SSL_SRV_C + +/** + * \def MBEDTLS_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: MBEDTLS_CIPHER_C, MBEDTLS_MD_C + * and at least one of the MBEDTLS_SSL_PROTO_XXX defines + * + * This module is required for SSL/TLS. + */ +#define MBEDTLS_SSL_TLS_C + +/** + * \def MBEDTLS_THREADING_C + * + * Enable the threading abstraction layer. + * By default mbed TLS assumes it is used in a non-threaded environment or that + * contexts are not shared between threads. If you do intend to use contexts + * between threads, you will need to enable this layer to prevent race + * conditions. See also our Knowledge Base article about threading: + * https://tls.mbed.org/kb/development/thread-safety-and-multi-threading + * + * Module: library/threading.c + * + * This allows different threading implementations (self-implemented or + * provided). + * + * You will have to enable either MBEDTLS_THREADING_ALT or + * MBEDTLS_THREADING_PTHREAD. + * + * Enable this layer to allow use of mutexes within mbed TLS + */ +//#define MBEDTLS_THREADING_C + +/** + * \def MBEDTLS_TIMING_C + * + * Enable the semi-portable timing interface. + * + * \note The provided implementation only works on POSIX/Unix (including Linux, + * BSD and OS X) and Windows. On other platforms, you can either disable that + * module and provide your own implementations of the callbacks needed by + * \c mbedtls_ssl_set_timer_cb() for DTLS, or leave it enabled and provide + * your own implementation of the whole module by setting + * \c MBEDTLS_TIMING_ALT in the current file. + * + * \note See also our Knowledge Base article about porting to a new + * environment: + * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +// #define MBEDTLS_TIMING_C + +/** + * \def MBEDTLS_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +// #define MBEDTLS_VERSION_C + +/** + * \def MBEDTLS_X509_USE_C + * + * Enable X.509 core for using certificates. + * + * Module: library/x509.c + * Caller: library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, + * MBEDTLS_PK_PARSE_C + * + * This module is required for the X.509 parsing modules. + */ +#define MBEDTLS_X509_USE_C + +/** + * \def MBEDTLS_X509_CRT_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509_crt.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is required for X.509 certificate parsing. + */ +#define MBEDTLS_X509_CRT_PARSE_C + +/** + * \def MBEDTLS_X509_CRL_PARSE_C + * + * Enable X.509 CRL parsing. + * + * Module: library/x509_crl.c + * Caller: library/x509_crt.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is required for X.509 CRL parsing. + */ +#define MBEDTLS_X509_CRL_PARSE_C + +/** + * \def MBEDTLS_X509_CSR_PARSE_C + * + * Enable X.509 Certificate Signing Request (CSR) parsing. + * + * Module: library/x509_csr.c + * Caller: library/x509_crt_write.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is used for reading X.509 certificate request. + */ +#define MBEDTLS_X509_CSR_PARSE_C + +/** + * \def MBEDTLS_X509_CREATE_C + * + * Enable X.509 core for creating certificates. + * + * Module: library/x509_create.c + * + * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, MBEDTLS_PK_WRITE_C + * + * This module is the basis for creating X.509 certificates and CSRs. + */ +#define MBEDTLS_X509_CREATE_C + +/** + * \def MBEDTLS_X509_CRT_WRITE_C + * + * Enable creating X.509 certificates. + * + * Module: library/x509_crt_write.c + * + * Requires: MBEDTLS_X509_CREATE_C + * + * This module is required for X.509 certificate creation. + */ +#define MBEDTLS_X509_CRT_WRITE_C + +/** + * \def MBEDTLS_X509_CSR_WRITE_C + * + * Enable creating X.509 Certificate Signing Requests (CSR). + * + * Module: library/x509_csr_write.c + * + * Requires: MBEDTLS_X509_CREATE_C + * + * This module is required for X.509 certificate request writing. + */ +#define MBEDTLS_X509_CSR_WRITE_C + +/** + * \def MBEDTLS_XTEA_C + * + * Enable the XTEA block cipher. + * + * Module: library/xtea.c + * Caller: + */ +#define MBEDTLS_XTEA_C + +/* \} name SECTION: mbed TLS modules */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * + * Our advice is to enable options and change their values here + * only if you have a good reason and know the consequences. + * + * Please check the respective header file for documentation on these + * parameters (to prevent duplicate documentation). + * \{ + */ + +/* MPI / BIGNUM options */ +//#define MBEDTLS_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +//#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */ + +/* CTR_DRBG options */ +//#define MBEDTLS_CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +//#define MBEDTLS_CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define MBEDTLS_CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define MBEDTLS_CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define MBEDTLS_CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ +//#define MBEDTLS_CTR_DRBG_USE_128_BIT_KEY /**< Use 128-bit key for CTR_DRBG - may reduce security (see ctr_drbg.h) */ + +/* HMAC_DRBG options */ +//#define MBEDTLS_HMAC_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define MBEDTLS_HMAC_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define MBEDTLS_HMAC_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* ECP options */ +//#define MBEDTLS_ECP_MAX_BITS 521 /**< Maximum bit size of groups */ +//#define MBEDTLS_ECP_WINDOW_SIZE 6 /**< Maximum window size used */ +//#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */ + +/* Entropy options */ +//#define MBEDTLS_ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +//#define MBEDTLS_ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ +//#define MBEDTLS_ENTROPY_MIN_HARDWARE 32 /**< Default minimum number of bytes required for the hardware entropy source mbedtls_hardware_poll() before entropy is released */ + +/* Memory buffer allocator options */ +//#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ + +/* Platform options */ +//#define MBEDTLS_PLATFORM_STD_MEM_HDR /**< Header to include if MBEDTLS_PLATFORM_NO_STD_FUNCTIONS is defined. Don't define if no header is needed. */ +//#define MBEDTLS_PLATFORM_STD_CALLOC calloc /**< Default allocator to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_FREE free /**< Default free to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT exit /**< Default exit to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_TIME time /**< Default time to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_STD_FPRINTF fprintf /**< Default fprintf to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_PRINTF printf /**< Default printf to use, can be undefined */ +/* Note: your snprintf must correctly zero-terminate the buffer! */ +//#define MBEDTLS_PLATFORM_STD_SNPRINTF snprintf /**< Default snprintf to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT_SUCCESS 0 /**< Default exit value to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT_FAILURE 1 /**< Default exit value to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_READ mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_WRITE mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_FILE "seedfile" /**< Seed file to read/write with default implementation */ + +/* To Use Function Macros MBEDTLS_PLATFORM_C must be enabled */ +/* MBEDTLS_PLATFORM_XXX_MACRO and MBEDTLS_PLATFORM_XXX_ALT cannot both be defined */ +//#define MBEDTLS_PLATFORM_CALLOC_MACRO calloc /**< Default allocator macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_FREE_MACRO free /**< Default free macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_EXIT_MACRO exit /**< Default exit macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_TIME_MACRO time /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_TIME_TYPE_MACRO time_t /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_FPRINTF_MACRO fprintf /**< Default fprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_PRINTF_MACRO printf /**< Default printf macro to use, can be undefined */ +/* Note: your snprintf must correctly zero-terminate the buffer! */ +//#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf /**< Default snprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_VSNPRINTF_MACRO vsnprintf /**< Default vsnprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_NV_SEED_READ_MACRO mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */ + +/** + * \brief This macro is invoked by the library when an invalid parameter + * is detected that is only checked with #MBEDTLS_CHECK_PARAMS + * (see the documentation of that option for context). + * + * When you leave this undefined here, the library provides + * a default definition. If the macro #MBEDTLS_CHECK_PARAMS_ASSERT + * is defined, the default definition is `assert(cond)`, + * otherwise the default definition calls a function + * mbedtls_param_failed(). This function is declared in + * `platform_util.h` for the benefit of the library, but + * you need to define in your application. + * + * When you define this here, this replaces the default + * definition in platform_util.h (which no longer declares the + * function mbedtls_param_failed()) and it is your responsibility + * to make sure this macro expands to something suitable (in + * particular, that all the necessary declarations are visible + * from within the library - you can ensure that by providing + * them in this file next to the macro definition). + * If you define this macro to call `assert`, also define + * #MBEDTLS_CHECK_PARAMS_ASSERT so that library source files + * include ``. + * + * Note that you may define this macro to expand to nothing, in + * which case you don't have to worry about declarations or + * definitions. However, you will then be notified about invalid + * parameters only in non-void functions, and void function will + * just silently return early on invalid parameters, which + * partially negates the benefits of enabling + * #MBEDTLS_CHECK_PARAMS in the first place, so is discouraged. + * + * \param cond The expression that should evaluate to true, but doesn't. + */ +//#define MBEDTLS_PARAM_FAILED( cond ) assert( cond ) + +/* SSL Cache options */ +//#define MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +//#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +/* SSL options */ + +/** \def MBEDTLS_SSL_MAX_CONTENT_LEN + * + * Maximum length (in bytes) of incoming and outgoing plaintext fragments. + * + * This determines the size of both the incoming and outgoing TLS I/O buffers + * in such a way that both are capable of holding the specified amount of + * plaintext data, regardless of the protection mechanism used. + * + * To configure incoming and outgoing I/O buffers separately, use + * #MBEDTLS_SSL_IN_CONTENT_LEN and #MBEDTLS_SSL_OUT_CONTENT_LEN, + * which overwrite the value set by this option. + * + * \note When using a value less than the default of 16KB on the client, it is + * recommended to use the Maximum Fragment Length (MFL) extension to + * inform the server about this limitation. On the server, there + * is no supported, standardized way of informing the client about + * restriction on the maximum size of incoming messages, and unless + * the limitation has been communicated by other means, it is recommended + * to only change the outgoing buffer size #MBEDTLS_SSL_OUT_CONTENT_LEN + * while keeping the default value of 16KB for the incoming buffer. + * + * Uncomment to set the maximum plaintext size of both + * incoming and outgoing I/O buffers. + */ +//#define MBEDTLS_SSL_MAX_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_IN_CONTENT_LEN + * + * Maximum length (in bytes) of incoming plaintext fragments. + * + * This determines the size of the incoming TLS I/O buffer in such a way + * that it is capable of holding the specified amount of plaintext data, + * regardless of the protection mechanism used. + * + * If this option is undefined, it inherits its value from + * #MBEDTLS_SSL_MAX_CONTENT_LEN. + * + * \note When using a value less than the default of 16KB on the client, it is + * recommended to use the Maximum Fragment Length (MFL) extension to + * inform the server about this limitation. On the server, there + * is no supported, standardized way of informing the client about + * restriction on the maximum size of incoming messages, and unless + * the limitation has been communicated by other means, it is recommended + * to only change the outgoing buffer size #MBEDTLS_SSL_OUT_CONTENT_LEN + * while keeping the default value of 16KB for the incoming buffer. + * + * Uncomment to set the maximum plaintext size of the incoming I/O buffer + * independently of the outgoing I/O buffer. + */ +//#define MBEDTLS_SSL_IN_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_CID_IN_LEN_MAX + * + * The maximum length of CIDs used for incoming DTLS messages. + * + */ +//#define MBEDTLS_SSL_CID_IN_LEN_MAX 32 + +/** \def MBEDTLS_SSL_CID_OUT_LEN_MAX + * + * The maximum length of CIDs used for outgoing DTLS messages. + * + */ +//#define MBEDTLS_SSL_CID_OUT_LEN_MAX 32 + +/** \def MBEDTLS_SSL_CID_PADDING_GRANULARITY + * + * This option controls the use of record plaintext padding + * when using the Connection ID extension in DTLS 1.2. + * + * The padding will always be chosen so that the length of the + * padded plaintext is a multiple of the value of this option. + * + * Note: A value of \c 1 means that no padding will be used + * for outgoing records. + * + * Note: On systems lacking division instructions, + * a power of two should be preferred. + * + */ +//#define MBEDTLS_SSL_CID_PADDING_GRANULARITY 16 + +/** \def MBEDTLS_SSL_OUT_CONTENT_LEN + * + * Maximum length (in bytes) of outgoing plaintext fragments. + * + * This determines the size of the outgoing TLS I/O buffer in such a way + * that it is capable of holding the specified amount of plaintext data, + * regardless of the protection mechanism used. + * + * If this option undefined, it inherits its value from + * #MBEDTLS_SSL_MAX_CONTENT_LEN. + * + * It is possible to save RAM by setting a smaller outward buffer, while keeping + * the default inward 16384 byte buffer to conform to the TLS specification. + * + * The minimum required outward buffer size is determined by the handshake + * protocol's usage. Handshaking will fail if the outward buffer is too small. + * The specific size requirement depends on the configured ciphers and any + * certificate data which is sent during the handshake. + * + * Uncomment to set the maximum plaintext size of the outgoing I/O buffer + * independently of the incoming I/O buffer. + */ +//#define MBEDTLS_SSL_OUT_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_DTLS_MAX_BUFFERING + * + * Maximum number of heap-allocated bytes for the purpose of + * DTLS handshake message reassembly and future message buffering. + * + * This should be at least 9/8 * MBEDTLSSL_IN_CONTENT_LEN + * to account for a reassembled handshake message of maximum size, + * together with its reassembly bitmap. + * + * A value of 2 * MBEDTLS_SSL_IN_CONTENT_LEN (32768 by default) + * should be sufficient for all practical situations as it allows + * to reassembly a large handshake message (such as a certificate) + * while buffering multiple smaller handshake messages. + * + */ +//#define MBEDTLS_SSL_DTLS_MAX_BUFFERING 32768 + +//#define MBEDTLS_SSL_DEFAULT_TICKET_LIFETIME 86400 /**< Lifetime of session tickets (if enabled) */ +//#define MBEDTLS_PSK_MAX_LEN 32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */ +//#define MBEDTLS_SSL_COOKIE_TIMEOUT 60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */ + +/** + * Complete list of ciphersuites to use, in order of preference. + * + * \warning No dependency checking is done on that field! This option can only + * be used to restrict the set of available ciphersuites. It is your + * responsibility to make sure the needed modules are active. + * + * Use this to save a few hundred bytes of ROM (default ordering of all + * available ciphersuites) and a few to a few hundred bytes of RAM. + * + * The value below is only an example, not the default. + */ +//#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + +/* X509 options */ +//#define MBEDTLS_X509_MAX_INTERMEDIATE_CA 8 /**< Maximum number of intermediate CAs in a verification chain. */ +//#define MBEDTLS_X509_MAX_FILE_PATH_LEN 512 /**< Maximum length of a path/filename string in bytes including the null terminator character ('\0'). */ + +/** + * Allow SHA-1 in the default TLS configuration for certificate signing. + * Without this build-time option, SHA-1 support must be activated explicitly + * through mbedtls_ssl_conf_cert_profile. Turning on this option is not + * recommended because of it is possible to generate SHA-1 collisions, however + * this may be safe for legacy infrastructure where additional controls apply. + * + * \warning SHA-1 is considered a weak message digest and its use constitutes + * a security risk. If possible, we recommend avoiding dependencies + * on it, and considering stronger message digests instead. + * + */ +//#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES + +/** + * Allow SHA-1 in the default TLS configuration for TLS 1.2 handshake + * signature and ciphersuite selection. Without this build-time option, SHA-1 + * support must be activated explicitly through mbedtls_ssl_conf_sig_hashes. + * The use of SHA-1 in TLS <= 1.1 and in HMAC-SHA-1 is always allowed by + * default. At the time of writing, there is no practical attack on the use + * of SHA-1 in handshake signatures, hence this option is turned on by default + * to preserve compatibility with existing peers, but the general + * warning applies nonetheless: + * + * \warning SHA-1 is considered a weak message digest and its use constitutes + * a security risk. If possible, we recommend avoiding dependencies + * on it, and considering stronger message digests instead. + * + */ +#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE + +/** + * Uncomment the macro to let mbed TLS use your alternate implementation of + * mbedtls_platform_zeroize(). This replaces the default implementation in + * platform_util.c. + * + * mbedtls_platform_zeroize() is a widely used function across the library to + * zero a block of memory. The implementation is expected to be secure in the + * sense that it has been written to prevent the compiler from removing calls + * to mbedtls_platform_zeroize() as part of redundant code elimination + * optimizations. However, it is difficult to guarantee that calls to + * mbedtls_platform_zeroize() will not be optimized by the compiler as older + * versions of the C language standards do not provide a secure implementation + * of memset(). Therefore, MBEDTLS_PLATFORM_ZEROIZE_ALT enables users to + * configure their own implementation of mbedtls_platform_zeroize(), for + * example by using directives specific to their compiler, features from newer + * C standards (e.g using memset_s() in C11) or calling a secure memset() from + * their system (e.g explicit_bzero() in BSD). + */ +//#define MBEDTLS_PLATFORM_ZEROIZE_ALT + +/** + * Uncomment the macro to let Mbed TLS use your alternate implementation of + * mbedtls_platform_gmtime_r(). This replaces the default implementation in + * platform_util.c. + * + * gmtime() is not a thread-safe function as defined in the C standard. The + * library will try to use safer implementations of this function, such as + * gmtime_r() when available. However, if Mbed TLS cannot identify the target + * system, the implementation of mbedtls_platform_gmtime_r() will default to + * using the standard gmtime(). In this case, calls from the library to + * gmtime() will be guarded by the global mutex mbedtls_threading_gmtime_mutex + * if MBEDTLS_THREADING_C is enabled. We recommend that calls from outside the + * library are also guarded with this mutex to avoid race conditions. However, + * if the macro MBEDTLS_PLATFORM_GMTIME_R_ALT is defined, Mbed TLS will + * unconditionally use the implementation for mbedtls_platform_gmtime_r() + * supplied at compile time. + */ +//#define MBEDTLS_PLATFORM_GMTIME_R_ALT + +/** + * Enable the verified implementations of ECDH primitives from Project Everest + * (currently only Curve25519). This feature changes the layout of ECDH + * contexts and therefore is a compatibility break for applications that access + * fields of a mbedtls_ecdh_context structure directly. See also + * MBEDTLS_ECDH_LEGACY_CONTEXT in include/mbedtls/ecdh.h. + */ +//#define MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED + +/* \} name SECTION: Customisation configuration options */ + +/* Target and application specific configurations + * + * Allow user to override any previous default. + * + */ +#if defined(MBEDTLS_USER_CONFIG_FILE) +#include MBEDTLS_USER_CONFIG_FILE +#endif + +#ifdef MCUBOOT_ENC_IMAGES +#define MBEDTLS_SHA256_DIGEST_SIZE (32) +#define MBEDTLS_AES_KEY_SIZE 16 +#endif + +#endif /* MBEDTLS_CONFIG_H */ diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/cy_security_cnt.c b/bootloader/mcuboot/boot/cypress/MCUBootApp/cy_security_cnt.c new file mode 100644 index 0000000..3c3b2ec --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/cy_security_cnt.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Arm Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bootutil/security_cnt.h" +#include + +fih_ret +boot_nv_security_counter_init(void) +{ + /* Do nothing. */ + return FIH_SUCCESS; +} + +fih_ret +boot_nv_security_counter_get(uint32_t image_id, fih_int *security_cnt) +{ + (void)image_id; + *security_cnt = 30; + + return FIH_SUCCESS; +} + +int32_t +boot_nv_security_counter_update(uint32_t image_id, uint32_t img_security_cnt) +{ + (void)image_id; + (void)img_security_cnt; + + /* Do nothing. */ + return 0; +} diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/cy_serial_flash_prog.c b/bootloader/mcuboot/boot/cypress/MCUBootApp/cy_serial_flash_prog.c new file mode 100644 index 0000000..512df93 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/cy_serial_flash_prog.c @@ -0,0 +1,100 @@ +/***************************************************************************//** +* \file cy_serial_flash_prog.c +* +* \brief +* Provides variables necessary to inform programming tools how to program the +* attached serial flash memory. The variables used here must be placed at +* specific locations in order to be detected and used by programming tools +* to know that there is an attached memory and its characteristics. Uses the +* configuration provided as part of BSP. +* +******************************************************************************** +* \copyright +* Copyright 2018-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +/** +* \addtogroup group_serial_flash Serial Flash +* \{ +* Variables for informing programming tools that there is an attached memory device and what +* its characteristics are so it can be programmed just like the on-chip memory. +* +* \defgroup group_serial_flash_variables Variables +*/ + +#include +#include "flash_qspi.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct +{ + const cy_stc_smif_block_config_t * smifCfg; /* Pointer to SMIF top-level configuration */ + const uint32_t null_t; /* NULL termination */ +} stc_smif_ipblocks_arr_t; + +/** +* \addtogroup group_serial_flash_variables +* \{ +*/ + +/** + * This data can be placed anywhere in the internal memory, but it must be at a location that + * can be determined and used for the calculation of the CRC16 checksum in the cyToc below. There + * are multiple ways this can be accomplished including: + * 1) Placing it in a dedicated memory block with a known address. (as done here) + * 2) Placing it at an absolute location via a the linker script + * 3) Using 'cymcuelftool -S' to recompute the checksum and patch the elf file after linking + */ +CY_SECTION(".cy_sflash_user_data") __attribute__( (used) ) +/* const stc_smif_ipblocks_arr_t smifIpBlocksArr = {&smifBlockConfig_sfdp, 0x00000000}; */ +/* if used zero-pointer to config, DAP link will use hardcoded config for CY8CPROTO-062-4343W */ +const stc_smif_ipblocks_arr_t smifIpBlocksArr = {0x00000000, 0x00000000}; + +/** + * This data is used to populate the table of contents part 2. When present, it is used by the boot + * process and programming tools to determine key characteristics about the memory usage including + * where the boot process should start the application from and what external memories are connected + * (if any). This must consume a full row of flash memory row. The last entry is a checksum of the + * other values in the ToC which must be updated if any other value changes. This can be done manually + * or by running 'cymcuelftool -S' to recompute the checksum. + */ +CY_SECTION(".cy_toc_part2") __attribute__( (used) ) +const uint32_t cyToc[128] = +{ + 0x200-4, /* Offset=0x0000: Object Size, bytes */ + 0x01211220, /* Offset=0x0004: Magic Number (TOC Part 2, ID) */ + 0, /* Offset=0x0008: Key Storage Address */ + (int)&smifIpBlocksArr, /* Offset=0x000C: This points to a null terminated array of SMIF structures. */ + 0x10000000u, /* Offset=0x0010: App image start address */ + /* Offset=0x0014-0x01F7: Reserved */ + [126] = 0x000002C2, /* Offset=0x01F8: Bits[ 1: 0] CLOCK_CONFIG (0=8MHz, 1=25MHz, 2=50MHz, 3=100MHz) + Bits[ 4: 2] LISTEN_WINDOW (0=20ms, 1=10ms, 2=1ms, 3=0ms, 4=100ms) + Bits[ 6: 5] SWJ_PINS_CTL (0/1/3=Disable SWJ, 2=Enable SWJ) + Bits[ 8: 7] APP_AUTHENTICATION (0/2/3=Enable, 1=Disable) + Bits[10: 9] FB_BOOTLOADER_CTL: UNUSED */ + [127] = 0x3BB30000 /* Offset=0x01FC: CRC16-CCITT (the upper 2 bytes contain the CRC and the lower 2 bytes are 0) */ +}; + +/** \} group_serial_flash_variables */ + +#if defined(__cplusplus) +} +#endif + +/** \} group_serial_flash */ diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/keys.c b/bootloader/mcuboot/boot/cypress/MCUBootApp/keys.c new file mode 100644 index 0000000..20c0332 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/keys.c @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /******************************************************************************* +* +* This software is a port of the open source MCUBoot project. +* +* This file was modified to fit PSoC6-based MCUBoot applications. +* +* Portions of this software, including source code, documentation and related +* materials ("Software"), are owned by Cypress Semiconductor +* Corporation or one of its subsidiaries ("Cypress") and is protected by +* and subject to worldwide patent protection (United States and foreign), +* United States copyright laws and international treaty provisions. +* Therefore, you may use this Software only as provided in the license +* agreement accompanying the software package from which you +* obtained this Software ("EULA"). +* +* If no EULA applies, Cypress hereby grants you a personal, non- +* exclusive, non-transferable license to copy, modify, and compile the +* Software source code solely for use in connection with Cypress's +* integrated circuit products. Any reproduction, modification, translation, +* compilation, or representation of this Software except as specified +* above is prohibited without the express written permission of Cypress. +* +* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO +* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, +* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +* PARTICULAR PURPOSE. Cypress reserves the right to make +* changes to the Software without notice. Cypress does not assume any +* liability arising out of the application or use of the Software or any +* product or circuit described in the Software. Cypress does not +* authorize its products for use in any products where a malfunction or +* failure of the Cypress product may reasonably be expected to result in +* significant property damage, injury or death ("High Risk Product"). By +* including Cypress's product in a High Risk Product, the manufacturer +* of such system or application assumes all risk of such use and in doing +* so agrees to indemnify Cypress against all liability. +* +********************************************************************************/ +#include +#include + +#if !defined(MCUBOOT_HW_KEY) +#if defined(MCUBOOT_SIGN_RSA) +const unsigned char rsa_pub_key[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd1, 0x06, 0x08, + 0x1a, 0x18, 0x44, 0x2c, 0x18, 0xe8, 0xfb, 0xfd, 0xf7, 0x0d, 0xa3, 0x4f, + 0x1f, 0xbb, 0xee, 0x5e, 0xf9, 0xaa, 0xd2, 0x4b, 0x18, 0xd3, 0x5a, 0xe9, + 0x6d, 0x18, 0x80, 0x19, 0xf9, 0xf0, 0x9c, 0x34, 0x1b, 0xcb, 0xf3, 0xbc, + 0x74, 0xdb, 0x42, 0xe7, 0x8c, 0x7f, 0x10, 0x53, 0x7e, 0x43, 0x5e, 0x0d, + 0x57, 0x2c, 0x44, 0xd1, 0x67, 0x08, 0x0f, 0x0d, 0xbb, 0x5c, 0xee, 0xec, + 0xb3, 0x99, 0xdf, 0xe0, 0x4d, 0x84, 0x0b, 0xaa, 0x77, 0x41, 0x60, 0xed, + 0x15, 0x28, 0x49, 0xa7, 0x01, 0xb4, 0x3c, 0x10, 0xe6, 0x69, 0x8c, 0x2f, + 0x5f, 0xac, 0x41, 0x4d, 0x9e, 0x5c, 0x14, 0xdf, 0xf2, 0xf8, 0xcf, 0x3d, + 0x1e, 0x6f, 0xe7, 0x5b, 0xba, 0xb4, 0xa9, 0xc8, 0x88, 0x7e, 0x47, 0x3c, + 0x94, 0xc3, 0x77, 0x67, 0x54, 0x4b, 0xaa, 0x8d, 0x38, 0x35, 0xca, 0x62, + 0x61, 0x7e, 0xb7, 0xe1, 0x15, 0xdb, 0x77, 0x73, 0xd4, 0xbe, 0x7b, 0x72, + 0x21, 0x89, 0x69, 0x24, 0xfb, 0xf8, 0x65, 0x6e, 0x64, 0x3e, 0xc8, 0x0e, + 0xd7, 0x85, 0xd5, 0x5c, 0x4a, 0xe4, 0x53, 0x0d, 0x2f, 0xff, 0xb7, 0xfd, + 0xf3, 0x13, 0x39, 0x83, 0x3f, 0xa3, 0xae, 0xd2, 0x0f, 0xa7, 0x6a, 0x9d, + 0xf9, 0xfe, 0xb8, 0xce, 0xfa, 0x2a, 0xbe, 0xaf, 0xb8, 0xe0, 0xfa, 0x82, + 0x37, 0x54, 0xf4, 0x3e, 0xe1, 0x2b, 0xd0, 0xd3, 0x08, 0x58, 0x18, 0xf6, + 0x5e, 0x4c, 0xc8, 0x88, 0x81, 0x31, 0xad, 0x5f, 0xb0, 0x82, 0x17, 0xf2, + 0x8a, 0x69, 0x27, 0x23, 0xf3, 0xab, 0x87, 0x3e, 0x93, 0x1a, 0x1d, 0xfe, + 0xe8, 0xf8, 0x1a, 0x24, 0x66, 0x59, 0xf8, 0x1c, 0xab, 0xdc, 0xce, 0x68, + 0x1b, 0x66, 0x64, 0x35, 0xec, 0xfa, 0x0d, 0x11, 0x9d, 0xaf, 0x5c, 0x3a, + 0xa7, 0xd1, 0x67, 0xc6, 0x47, 0xef, 0xb1, 0x4b, 0x2c, 0x62, 0xe1, 0xd1, + 0xc9, 0x02, 0x03, 0x01, 0x00, 0x01 +}; +const unsigned int rsa_pub_key_len = 270; +#elif defined(MCUBOOT_SIGN_EC256) +/* Format of PEM : + * -----BEGIN PUBLIC KEY----- + * base64encode(DER) + * -----END PUBLIC KEY----- */ +#if defined(ECC256_KEY_FILE) +#include ECC256_KEY_FILE +#else +#warning "Used default ECC256 ecdsa_pub_key" +/* It is OEM_PUB_KEY at this moment for debug purposes */ +const unsigned char ecdsa_pub_key[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0xbd, 0x59, 0x9d, 0x15, 0xe0, + 0xff, 0x66, 0x12, 0x37, 0x28, 0xdf, 0x50, 0x38, + 0xb1, 0x9a, 0x73, 0x9b, 0xbd, 0xd1, 0xb3, 0x8a, + 0x6f, 0xd2, 0x70, 0xed, 0x7f, 0xdb, 0x57, 0x53, + 0xde, 0x9e, 0x77, 0x0f, 0x9c, 0x17, 0x22, 0x69, + 0xa6, 0x75, 0x48, 0x1f, 0xa4, 0xbc, 0x49, 0xe2, + 0x01, 0xe0, 0x5e, 0x3d, 0xec, 0xa8, 0xc1, 0xca, + 0xc5, 0x5c, 0xa2, 0xc6, 0xfd, 0xb0, 0x24, 0xb1, + 0x0a, 0x46, 0xf5, +}; +const unsigned int ecdsa_pub_key_len = 91; +#endif +#else +#warning "No public key available for given signing algorithm." +#endif + +#if defined(MCUBOOT_SIGN_RSA) || \ + defined(MCUBOOT_SIGN_EC256) +const struct bootutil_key bootutil_keys[] = { +#if defined(MCUBOOT_SIGN_RSA) + { + .key = rsa_pub_key, + .len = &rsa_pub_key_len, + }, +#elif defined(MCUBOOT_SIGN_EC256) + { + .key = ecdsa_pub_key, + .len = &ecdsa_pub_key_len, + }, +#else + { + .key = NULL, + .len = 0x00, + }, +#endif +}; +const int bootutil_key_cnt = 1; +#endif +#else +unsigned int pub_key_len; +struct bootutil_key bootutil_keys[1] = { + { + .key = 0, + .len = &pub_key_len, + } +}; +const int bootutil_key_cnt = 1; +#endif /* !MCUBOOT_HW_KEY */ + +unsigned char enc_priv_key[] = { + 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, + 0xf6, 0x1e, 0x51, 0x9d, 0xf8, 0xfa, 0xdd, 0xa1, 0xb7, 0xd9, 0xa9, 0x64, + 0x64, 0x3b, 0x54, 0xd0, 0x3d, 0xd0, 0x1f, 0xe5, 0x78, 0xd9, 0x17, 0x98, + 0xa5, 0x28, 0xca, 0xcc, 0x6b, 0x67, 0x9e, 0x06, 0xa1, 0x44, 0x03, 0x42, + 0x00, 0x04, 0x8a, 0x44, 0x73, 0x00, 0x94, 0xc9, 0x80, 0x27, 0x31, 0x0d, + 0x23, 0x36, 0x6b, 0xe9, 0x69, 0x9f, 0xcb, 0xc5, 0x7c, 0xc8, 0x44, 0x1a, + 0x93, 0xe6, 0xee, 0x7d, 0x86, 0xa6, 0xae, 0x5e, 0x93, 0x72, 0x74, 0xd9, + 0xe1, 0x5a, 0x1c, 0x9b, 0x65, 0x1a, 0x2b, 0x61, 0x41, 0x28, 0x02, 0x73, + 0x84, 0x12, 0x97, 0x3a, 0x2d, 0xa2, 0xa0, 0x67, 0x77, 0x02, 0xda, 0x67, + 0x1a, 0x4b, 0xdd, 0xd7, 0x71, 0xcc, +}; +static unsigned int enc_priv_key_len = 138; +const struct bootutil_key bootutil_enc_key = { + .key = enc_priv_key, + .len = &enc_priv_key_len, +}; diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/libs.mk b/bootloader/mcuboot/boot/cypress/MCUBootApp/libs.mk new file mode 100644 index 0000000..1f22674 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/libs.mk @@ -0,0 +1,95 @@ +################################################################################ +# \file libs.mk +# \version 1.0 +# +# \brief +# Makefile to describe libraries needed for Cypress MCUBoot based applications. +# +################################################################################ +# \copyright +# Copyright 2018-2019 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +################################################################################ +# PDL library +################################################################################ +PDL_VERSION = 121 + +THIS_APP_PATH = $(PRJ_DIR)/libs +MBEDTLS_PATH = $(PRJ_DIR)/../../ext + +# Add platform folder to build +SOURCES_PLATFORM += $(wildcard $(PRJ_DIR)/platforms/*.c) +SOURCES_WATCHDOG := $(wildcard $(THIS_APP_PATH)/watchdog/*.c) + +# Add retartget IO implementation using pdl +SOURCES_RETARGET_IO_PDL += $(wildcard $(THIS_APP_PATH)/retarget_io_pdl/*.c) + +# Collect dirrectories containing headers for PLATFORM +INCLUDE_RETARGET_IO_PDL += $(THIS_APP_PATH)/retarget_io_pdl + +# PSOC6HAL source files +SOURCES_HAL += $(THIS_APP_PATH)/psoc6hal/COMPONENT_PSOC6HAL/source/cyhal_crypto_common.c +SOURCES_HAL += $(THIS_APP_PATH)/psoc6hal/COMPONENT_PSOC6HAL/source/cyhal_hwmgr.c + +# MbedTLS source files +SOURCES_MBEDTLS := $(wildcard $(MBEDTLS_PATH)/mbedtls/library/*.c) + +# Collected source files for libraries +SOURCES_LIBS += $(SOURCES_HAL) +SOURCES_LIBS += $(SOURCES_MBEDTLS) +SOURCES_LIBS += $(SOURCES_WATCHDOG) +SOURCES_LIBS += $(SOURCES_PLATFORM) +SOURCES_LIBS += $(SOURCES_RETARGET_IO_PDL) + +# Include platforms folder +INCLUDE_DIRS_PLATFORM := $(PRJ_DIR)/platforms + +# needed for Crypto HW Acceleration and headers inclusion, do not use for peripherals +# peripherals should be accessed +INCLUDE_DIRS_HAL := $(THIS_APP_PATH)/psoc6hal/COMPONENT_PSOC6HAL/include +INCLUDE_DIRS_HAL += $(THIS_APP_PATH)/psoc6hal/include +INCLUDE_DIRS_HAL += $(THIS_APP_PATH)/psoc6hal/COMPONENT_PSOC6HAL/include/pin_packages + +# MbedTLS related include directories +INCLUDE_DIRS_MBEDTLS += $(MBEDTLS_PATH)/mbedtls/include +INCLUDE_DIRS_MBEDTLS += $(MBEDTLS_PATH)/mbedtls/include/mbedtls +INCLUDE_DIRS_MBEDTLS += $(MBEDTLS_PATH)/mbedtls/include/psa +INCLUDE_DIRS_MBEDTLS += $(MBEDTLS_PATH)/mbedtls/library + +# Watchdog related includes +INCLUDE_DIRS_WATCHDOG := $(THIS_APP_PATH)/watchdog + +# Collected include directories for libraries +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_HAL)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_WATCHDOG)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_MBEDTLS)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_RETARGET_IO_PDL)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_PLATFORM)) + +################################################################################ +# mbedTLS hardware acceleration settings +################################################################################ +ifeq ($(USE_CRYPTO_HW), 1) +# cy-mbedtls-acceleration related include directories +INCLUDE_DIRS_MBEDTLS_MXCRYPTO := $(THIS_APP_PATH)/cy-mbedtls-acceleration/mbedtls_MXCRYPTO +# Collect source files for MbedTLS acceleration +SOURCES_MBEDTLS_MXCRYPTO := $(wildcard $(THIS_APP_PATH)/cy-mbedtls-acceleration/mbedtls_MXCRYPTO/*.c) +# +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_MBEDTLS_MXCRYPTO)) +# Collected source files for libraries +SOURCES_LIBS += $(SOURCES_MBEDTLS_MXCRYPTO) +endif diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/main.c b/bootloader/mcuboot/boot/cypress/MCUBootApp/main.c new file mode 100644 index 0000000..a87d981 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/main.c @@ -0,0 +1,181 @@ +/***************************************************************************//** +* \file main.c +* \version 1.0 +******************************************************************************** +* \copyright +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ +/* Cypress pdl headers */ +#include "cy_pdl.h" +#include "cy_retarget_io_pdl.h" +#include "cy_result.h" + +#include "cycfg_clocks.h" +#include "cycfg_peripherals.h" +#include "cycfg_pins.h" + +#include "flash_qspi.h" +#include "sysflash/sysflash.h" +#include "flash_map_backend/flash_map_backend.h" + +#include "bootutil/image.h" +#include "bootutil/bootutil.h" +#include "bootutil/sign_key.h" + +#include "bootutil/bootutil_log.h" + +#include "bootutil/fault_injection_hardening.h" + +#include "watchdog.h" + +/* WDT time out for reset mode, in milliseconds. */ +#define WDT_TIME_OUT_MS 4000 + +/* Define pins for UART debug output */ +#define CYBSP_UART_ENABLED 1U +#define CYBSP_UART_HW SCB5 +#define CYBSP_UART_IRQ scb_5_interrupt_IRQn + +#ifdef CY_BOOT_USE_EXTERNAL_FLASH +/* Choose SMIF slot number (slave select). + * Acceptable values are: + * 0 - SMIF disabled (no external memory); + * 1, 2, 3 or 4 - slave select line memory module is connected to. + */ +uint32_t smif_id = 1; /* Assume SlaveSelect_0 is used for External Memory */ +#endif + + +void hw_deinit(void); + +static void do_boot(struct boot_rsp *rsp) +{ + uint32_t app_addr = 0; + + app_addr = (rsp->br_image_off + rsp->br_hdr->ih_hdr_size); + + BOOT_LOG_INF("Starting User Application on CM4 (wait)..."); + BOOT_LOG_INF("Start Address: 0x%08lx", app_addr); + BOOT_LOG_INF("Deinitializing hardware..."); + + cy_retarget_io_wait_tx_complete(CYBSP_UART_HW, 10); + + hw_deinit(); + + Cy_SysEnableCM4(app_addr); +} + +int main(void) +{ + struct boot_rsp rsp; + cy_rslt_t rc = CY_RSLT_TYPE_ERROR; + bool boot_succeeded = false; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + SystemInit(); + //init_cycfg_clocks(); + init_cycfg_peripherals(); + init_cycfg_pins(); + + /* Certain PSoC 6 devices enable CM4 by default at startup. It must be + * either disabled or enabled & running a valid application for flash write + * to work from CM0+. Since flash write may happen in boot_go() for updating + * the image before this bootloader app can enable CM4 in do_boot(), we need + * to keep CM4 disabled. Note that debugging of CM4 is not supported when it + * is disabled. + */ + #if defined(CY_DEVICE_PSOC6ABLE2) + if (CY_SYS_CM4_STATUS_ENABLED == Cy_SysGetCM4Status()) + { + Cy_SysDisableCM4(); + } + #endif /* #if defined(CY_DEVICE_PSOC6ABLE2) */ + + /* enable interrupts */ + __enable_irq(); + + /* Initialize retarget-io to use the debug UART port (CYBSP_UART_HW) */ + rc = cy_retarget_io_pdl_init(115200u); + + if (rc != CY_RSLT_SUCCESS) + { + CY_ASSERT(0); + } + + BOOT_LOG_INF("MCUBoot Bootloader Started"); + +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + rc = CY_SMIF_CMD_NOT_FOUND; + + #undef MCUBOOT_MAX_IMG_SECTORS + /* redefine number of sectors as there 2MB will be + * available on PSoC062-2M in case of external + * memory usage */ + #define MCUBOOT_MAX_IMG_SECTORS 4096 + rc = qspi_init_sfdp(smif_id); + if (rc == CY_SMIF_SUCCESS) + { + BOOT_LOG_INF("External Memory initialized w/ SFDP."); + } + else + { + BOOT_LOG_ERR("External Memory initialization w/ SFDP FAILED: 0x%02x", (int)rc); + } + if (CY_SMIF_SUCCESS == rc) +#endif + { + + FIH_CALL(boot_go, fih_rc, &rsp); + if (FIH_EQ(fih_rc, FIH_SUCCESS)) + { + BOOT_LOG_INF("User Application validated successfully"); + /* initialize watchdog timer. it should be updated from user app + * to mark successful start up of this app. if the watchdog is not updated, + * reset will be initiated by watchdog timer and swap revert operation started + * to roll back to operable image. + */ + cy_wdg_init(WDT_TIME_OUT_MS); + do_boot(&rsp); + boot_succeeded = true; + } + else + { + BOOT_LOG_INF("MCUBoot Bootloader found none of bootable images"); + } + } + + while (1) + { + if (boot_succeeded) { + Cy_SysPm_CpuEnterDeepSleep(CY_SYSPM_WAIT_FOR_INTERRUPT); + } + else { + __WFI(); + } + } + + return 0; +} + +void hw_deinit(void) +{ + cy_retarget_io_pdl_deinit(); + Cy_GPIO_Port_Deinit(CYBSP_UART_RX_PORT); + Cy_GPIO_Port_Deinit(CYBSP_UART_TX_PORT); + +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + qspi_deinit(smif_id); +#endif +} diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os.h new file mode 100644 index 0000000..8d581ca --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os.h @@ -0,0 +1,18 @@ +/***************************************************************************//** +* \file os.h +* \version 1.0 +* +* \brief +* Objective: +* Heap init prototype +* +******************************************************************************** +* \copyright +* Copyright 2017-2018, Cypress Semiconductor Corporation. All rights reserved. +* You may use this file only in accordance with the license, terms, conditions, +* disclaimers, and limitations in the end user license agreement accompanying +* the software package with which this file was provided. +*******************************************************************************/ + + +void os_heap_init(void); diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_heap.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_heap.h new file mode 100644 index 0000000..0ea954c --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_heap.h @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_OS_HEAP_ +#define H_OS_HEAP_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *os_malloc(size_t size); +void os_free(void *mem); +void *os_realloc(void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_malloc.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_malloc.h new file mode 100644 index 0000000..249b12c --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/os/os_malloc.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_OS_MALLOC_ +#define H_OS_MALLOC_ + +#include "os/os_heap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#undef malloc +#define malloc os_malloc + +#undef free +#define free os_free + +#undef realloc +#define realloc os_realloc + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/cypress/MCUBootApp/sysflash/sysflash.h b/bootloader/mcuboot/boot/cypress/MCUBootApp/sysflash/sysflash.h new file mode 100644 index 0000000..f1fc841 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/MCUBootApp/sysflash/sysflash.h @@ -0,0 +1,81 @@ +/* Manual version of auto-generated version. */ + +#ifndef __SYSFLASH_H__ +#define __SYSFLASH_H__ + +#define FLASH_DEVICE_INTERNAL_FLASH (0x7F) + +#define FLASH_AREA_BOOTLOADER 0 +#define FLASH_AREA_IMAGE_0 1 +#define FLASH_AREA_IMAGE_1 2 +#define FLASH_AREA_IMAGE_SCRATCH 3 +#define FLASH_AREA_IMAGE_2 5 +#define FLASH_AREA_IMAGE_3 6 + +/* Uncomment if external flash is being used */ +/* #define CY_BOOT_USE_EXTERNAL_FLASH */ + +/* use PDL-defined offset or one from SMFI config */ +#define CY_SMIF_BASE_MEM_OFFSET (0x18000000) + +#define CY_FLASH_ALIGN (CY_FLASH_SIZEOF_ROW) +#define CY_FLASH_DEVICE_BASE (CY_FLASH_BASE) + +#ifndef CY_BOOT_SCRATCH_SIZE +#define CY_BOOT_SCRATCH_SIZE (0x1000) +#endif + +#ifndef CY_BOOT_BOOTLOADER_SIZE +#define CY_BOOT_BOOTLOADER_SIZE (0x18000) +#endif + +#ifndef CY_BOOT_PRIMARY_1_SIZE +#define CY_BOOT_PRIMARY_1_SIZE (0x10000) +#endif + +#ifndef CY_BOOT_SECONDARY_1_SIZE +#define CY_BOOT_SECONDARY_1_SIZE (0x10000) +#endif + +#if (MCUBOOT_IMAGE_NUMBER == 2) /* if dual-image */ +#ifndef CY_BOOT_PRIMARY_2_SIZE +#define CY_BOOT_PRIMARY_2_SIZE (0x10000) +#endif + +#ifndef CY_BOOT_SECONDARY_2_SIZE +#define CY_BOOT_SECONDARY_2_SIZE (0x10000) +#endif +#endif + +#if (MCUBOOT_IMAGE_NUMBER == 1) +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0 : \ + FLASH_AREA_IMAGE_0) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_1 : \ + FLASH_AREA_IMAGE_1) + +#elif (MCUBOOT_IMAGE_NUMBER == 2) + +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0 : \ + ((x) == 1) ? \ + FLASH_AREA_IMAGE_2 : \ + 255) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_1 : \ + ((x) == 1) ? \ + FLASH_AREA_IMAGE_3 : \ + 255) + +#else +#warning "Image slot and flash area mapping is not defined" +#endif +#define CY_IMG_HDR_SIZE 0x400 + +#ifndef CY_FLASH_MAP_EXT_DESC +/* Uncomment in case you want to use separately defined table of flash area descriptors */ +/* #define CY_FLASH_MAP_EXT_DESC */ +#endif + +#endif /* __SYSFLASH_H__ */ diff --git a/bootloader/mcuboot/boot/cypress/Makefile b/bootloader/mcuboot/boot/cypress/Makefile new file mode 100644 index 0000000..78ac445 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/Makefile @@ -0,0 +1,212 @@ +################################################################################ +# \file Makefile +# \version 1.0 +# +# \brief +# Main Makefile for building MCUBoot application for Cypress target. +# +################################################################################ +# \copyright +# Copyright 2018-2021 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +# minimum Python 3.7 is required +# Python path definition +ifeq ($(OS),Windows_NT) +PYTHON_PATH?=python +else +PYTHON_PATH?=python3 +endif + +################################################################################ +# Main settings +################################################################################ + +# Defines whether or not show verbose build output +MAKEINFO ?= 1 +# Application name by default +APP_NAME ?= MCUBootApp +# Weather or now execute post build script after build - set to 0 for CI +POST_BUILD ?= 1 + +SIGN_KEY_FILE ?= cypress-test-ec-p256 +ENC_KEY_FILE ?= enc-ec256-pub +ENC_IMG ?= 0 + +# set this variable to a path, where cysecuretools python package is installed +# use command `python -m pip show cysecuretools` to find out this path +# or rely on scripts that automates this action, bit not work for virtual envs +CY_SEC_TOOLS_PATH ?= $(shell $(PYTHON_PATH) $(CURDIR)/scripts/find_cysectools.py) + +BUILDCFG ?= Debug + +# Set of supported applications +APPS := MCUBootApp BlinkyApp + +HEADER_OFFSET ?= 0 + +ifneq ($(filter $(APP_NAME), $(APPS)),) +include ./$(APP_NAME)/$(APP_NAME).mk +include ./$(APP_NAME)/libs.mk +else +$(error Not supported application: '$(APP_NAME)') +endif + +ASM_FILES := $(ASM_FILES_APP) +ASM_FILES += $(ASM_FILES_LIBS) + +C_FILES := $(SOURCES_APP) +C_FILES += $(SOURCES_LIBS) + +INCLUDE_DIRS := $(INCLUDE_DIRS_APP) +INCLUDE_DIRS += $(INCLUDE_DIRS_MCUBOOT) +INCLUDE_DIRS += $(INCLUDE_DIRS_LIBS) + +#INCLUDE_FILES := $(INCLUDE_FILES_APP) + +#INCLUDES := $(addprefix -include , $(INCLUDE_FILES)) + +O_FILES := $(notdir $(C_FILES:.c=.o)) $(addsuffix .o, $(notdir $(basename $(ASM_FILES)))) + +DEFINES := $(DEFINES_APP) +DEFINES += $(DEFINES_LIBS) +AS_FLAGS += $(DEFINES) + +ifeq ($(MAKEINFO), 1) +$(info ==============================================================================) +$(info = Directories to look for header files: =) +$(info ==============================================================================) +$(info $(INCLUDE_DIRS)) + +$(info ==============================================================================) +$(info = Collected Defines string: =) +$(info ==============================================================================) +$(info $(DEFINES)) +endif + +# updating CFLAGS at this point as DEFINES are completed +CFLAGS += $(DEFINES) + +VPATH = $(dir $(C_FILES) $(ASM_FILES)) + +# +# STDE: For cygwin, adjust paths for compiler +# +MY_FILES := $(subst /cygdrive/c,c:,$(C_FILES)) +#$(info MY_FILES $(MY_FILES)) +C_FILES=$(MY_FILES) + +MY_DIRS := $(subst /cygdrive/c,c:,$(INCLUDE_DIRS)) +#$(info MY_DIRS $(MY_DIRS)) +INCLUDE_DIRS=$(MY_DIRS) + +MY_ASM_FILES := $(subst /cygdrive/c,c:,$(ASM_FILES)) +#$(info MY_ASM_FILES $(MY_ASM_FILES)) +ASM_FILES=$(MY_ASM_FILES) + +MY_LDFLAGS := $(subst /cygdrive/c,c:,$(LDFLAGS)) +#$(info MY_LDFLAGS $(MY_LDFLAGS)) +LDFLAGS=$(MY_LDFLAGS) + +# Default name pattern for output files +# may be modified in %Application%.mk file +OUT_FILE_NAME ?= $(OUT_APP)/$(APP_NAME) + +OUT_OBJ := $(OUT_CFG)/obj +OUT_APP := $(OUT_CFG) + +.PHONY: all app build clean pre_build post_build + +all: clean app + +app: + @`mkdir -p ./$(OUT)` + @`mkdir -p ./$(OUT_TARGET)` + @`mkdir -p ./$(OUT_CFG)` + @`mkdir -p ./$(OUT_OBJ)` + $(MAKE) pre_build + $(MAKE) build -j8 + $(MAKE) post_build + +build: $(OUT_APP)/$(APP_NAME).hex + $(GCC_PATH)/bin/arm-none-eabi-objdump $(OUT_APP)/$(APP_NAME).elf -S --disassemble > $(OUT_APP)/$(APP_NAME).lst + $(GCC_PATH)/bin/arm-none-eabi-objdump -h $(OUT_APP)/$(APP_NAME).elf + $(GCC_PATH)/bin/arm-none-eabi-size --format=SysV $(OUT_APP)/$(APP_NAME).elf + +$(OUT_APP)/$(APP_NAME).hex: $(OUT_APP)/$(APP_NAME).elf + $(GCC_PATH)/bin/arm-none-eabi-objcopy --change-addresses=$(HEADER_OFFSET) -O ihex $(OUT_APP)/$(APP_NAME).elf $(OUT_APP)/$(APP_NAME).hex + +$(OUT_APP)/$(APP_NAME).elf: $(addprefix $(OUT_OBJ)/, $(O_FILES)) + @echo "LD $@" +ifeq ($(MAKEINFO), 1) + @echo $(LD) $(O_FILES) $(CC_DEPEND) $(@:.o=.d) -o $@ $(LDFLAGS) -T $(LINKER_SCRIPT) -Wl,-Map,$(OUT_FILE_NAME).map +endif + @$(LD) $(addprefix $(OUT_OBJ)/, $(O_FILES)) $(CC_DEPEND) $(@:.o=.d) -o $@ $(LDFLAGS) -T $(LINKER_SCRIPT) -Wl,-Map,$(OUT_FILE_NAME).map + + +$(OUT_OBJ)/%.o: %.c + @echo "CC $<" +ifeq ($(MAKEINFO), 1) + @echo $(CC) $(CFLAGS) $(INCLUDE_DIRS) $(CC_DEPEND) $(@:.o=.d) -c $< -o $@ +endif + @$(CC) $(CFLAGS) $(INCLUDE_DIRS) $(CC_DEPEND) $(@:.o=.d) -c $< -o $@ +ifeq ($(MAKEINFO), 1) + @echo +endif + +$(OUT_OBJ)/%.o: %.S + @echo "AS $<" +ifeq ($(COMPILER), GCC_ARM) +ifeq ($(MAKEINFO), 1) + @echo @$(CC) $(CFLAGS) $(INCLUDE_DIRS) $(CC_DEPEND) $(@:.o=.d) -c $< -o $@ +endif + @$(CC) $(CFLAGS) $(INCLUDE_DIRS) $(CC_DEPEND) $(@:.o=.d) -c $< -o $@ +else + @echo $(AS) $< -o $@ $(AS_FLAGS) + @$(AS) $< -o $@ $(AS_FLAGS) +endif +ifeq ($(MAKEINFO), 1) + @echo +endif + +clean: + @echo "Cleanup out directory..." + rm -rf $(OUT_TARGET)/$(BUILDCFG) + +clean_boot: + @echo "Cleanup out BOOT directory of $(APP_NAME)..." + rm -rf $(OUT_TARGET)/$(BUILDCFG)/boot + +clean_upgrade: + @echo "Cleanup out UPGRADE directory of $(APP_NAME)..." + rm -rf $(OUT_TARGET)/$(BUILDCFG)/upgrade + +run_cppcheck: + @echo "Performing static code analysis with Cppcheck tool..." + cppcheck/cppcheck.sh $(APP_NAME) $(PLATFORM) "$(DEFINES)" "$(INCLUDE_DIRS)" "$(C_FILES)" $(CPP_CHECK_SCOPE) $(BUILDCFG) + +gen_key_ecc256: + @echo Generate ECC256 keys: $(SIGN_KEY_FILE).pem and $(SIGN_KEY_FILE).pub + ../../scripts/imgtool.py keygen -k keys/$(SIGN_KEY_FILE).pem -t ecdsa-p256 + ../../scripts/imgtool.py getpub -k keys/$(SIGN_KEY_FILE).pem > keys/$(SIGN_KEY_FILE).pub + +ifeq ($(MAKEINFO) , 1) +$(info ASM_FILES: $(ASM_FILES)) +$(info C_FILES: $(C_FILES)) +$(info INCLUDE_DIRS: $(INCLUDE_DIRS)) +$(info DEFINES: $(DEFINES)) +$(info CC: $(CC)) +endif diff --git a/bootloader/mcuboot/boot/cypress/README.md b/bootloader/mcuboot/boot/cypress/README.md new file mode 100644 index 0000000..2b3e46b --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/README.md @@ -0,0 +1,84 @@ +### Port of MCUBoot library for evaluation with Cypress PSoC 6 chips + +### Disclaimer + +Given solution is included in `MCUboot` repository with purpose to demonstrate basic consepts and features of MCUboot library on Cypress PSoC 6 device. Applications are created per MCUboot library maintainers requirements. Implemetation differs from conventional and recomended by Cypress Semiconductors development flow for PSoC 6 devices. These applications are not recomended as a starting point for development and should not be considered as supported examples for PSoC 6 devices. + +Examples provided to use with **ModusToolbox® Software Environment** are a recommended reference point to start development of MCUboot based bootloaders for PSoC 6 devices. + +Refer to **Cypress Semiconductors** [github](https://github.com/cypresssemiconductorco) page to find examples. + +1. MCUboot-Based Basic Bootloader [mtb-example-psoc6-mcuboot-basic](https://github.com/cypresssemiconductorco/mtb-example-psoc6-mcuboot-basic) +2. MCUboot-Based Bootloader with Rollback to Factory App in External Flash [mtb-example-anycloud-mcuboot-rollback](https://github.com/cypresssemiconductorco/mtb-example-anycloud-mcuboot-rollback) + +### Solution description + +There are two applications implemented: +* MCUBootApp - PSoC6 MCUboot-based bootloading application; +* BlinkyApp - simple PSoC6 blinking LED application which is a target of BOOT/UPGRADE; + +The default flash map for MCUBootApp implemented is next: + +* [0x10000000, 0x10018000] - MCUBootApp (bootloader) area; +* [0x10018000, 0x10028000] - primary slot for BlinkyApp; +* [0x10028000, 0x10038000] - secondary slot for BlinkyApp; +* [0x10038000, 0x10039000] - scratch area; + +The flash map is defined through sysflash.h and cy_flash_map.c. + +It is also possible to place secondary (upgrade) slots in external memory module. In this case primary slot can be doubled in size. +For more details about External Memory usage, please refer to separate guiding document `MCUBootApp/ExternalMemory.md`. + +MCUBootApp checks image integrity with SHA256, image authenticity with EC256 digital signature verification and uses either completely software implementation of cryptographic functions or accelerated by hardware - both based on Mbed TLS Library. + +### Downloading solution's assets + +There is a set assets required: + +* MCUBooot Library (root repository) +* PSoC6 Peripheral Drivers Library (PDL) +* Mbed TLS Cryptographic Library + +Those are represented as submodules. + +To retrieve source code with subsequent submodules pull: + + git clone --recursive https://github.com/mcu-tools/mcuboot.git + +Submodules can also be updated and initialized separately: + + cd mcuboot + git submodule update --init --recursive + + + +### Building solution + +Root directory for build is **boot/cypress.** + +This folder contains make files infrastructure for building both MCUboot Bootloader and sample BlinkyApp application used for Bootloader demo functionality. + +Instructions on how to build and upload MCUBootApp bootloader application and sample user applocation are located in `Readme.md` files in corresponding folders. + +Supported platforms for `MCUboot`, `BlinkyApp`: + +* PSOC_062_2M +* PSOC_062_1M +* PSOC_062_512K + +### Build environment troubleshooting + +Following CLI / IDE are supported for project build: + +* Cygwin on Windows systems +* unix style shells on *nix systems +* Eclipse / ModusToolbox ("makefile project from existing source") + +*Make* - make sure it is added to system's `PATH` variable and correct path is first in the list; + +*Python/Python3* - make sure you have correct path referenced in `PATH`; + +*Msys2* - to use systems PATH navigate to msys2 folder, open `msys2_shell.cmd`, uncomment set `MSYS2_PATH_TYPE=inherit`, restart MSYS2 shell. + +This will inherit system's PATH so should find `python3.7` installed in regular way as well as imgtool and its dependencies. + diff --git a/bootloader/mcuboot/boot/cypress/common_libs.mk b/bootloader/mcuboot/boot/cypress/common_libs.mk new file mode 100644 index 0000000..f5d11f8 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/common_libs.mk @@ -0,0 +1,89 @@ +################################################################################ +# \file common_libs.mk +# \version 1.0 +# +# \brief +# Makefile to describe libraries needed for Cypress MCUBoot based applications. +# +################################################################################ +# \copyright +# Copyright 2018-2021 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +include host.mk + +################################################################################ +# PDL library +################################################################################ +PSOC6_LIBS_PATH = $(PRJ_DIR)/libs + +ifeq ($(CORE),CM0P) +CORE_SIFFX=m0plus +else +CORE_SIFFX=m4 +endif + +# Collect source files for PDL +SOURCES_PDL := $(wildcard $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/drivers/source/*.c) +SOURCES_PDL += $(wildcard $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/devices/COMPONENT_CAT1A/source/*.c) + +# PDL startup related files +SOURCES_PDL_STARTUP := $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/devices/COMPONENT_CAT1A/templates/COMPONENT_MTB/COMPONENT_$(CORE)/system_psoc6_c$(CORE_SIFFX).c + +# PDL related include directories +INCLUDE_DIRS_PDL := $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/drivers/include +INCLUDE_DIRS_PDL += $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/devices/COMPONENT_CAT1A/include/ip +INCLUDE_DIRS_PDL += $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/devices/COMPONENT_CAT1A/include +INCLUDE_DIRS_PDL += $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/cmsis/include + +# PDL startup related files +INCLUDE_DIRS_PDL_STARTUP := $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/devices/COMPONENT_CAT1A/templates/COMPONENT_MTB + +# core-libs related include directories +INCLUDE_DIRS_CORE_LIB := $(PSOC6_LIBS_PATH)/core-lib/include + +STARTUP_FILE := $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/devices/COMPONENT_CAT1A/templates/COMPONENT_MTB/COMPONENT_$(CORE)/TOOLCHAIN_$(COMPILER)/startup_psoc6_$(PLATFORM_SUFFIX)_c$(CORE_SIFFX) + +ifeq ($(COMPILER), GCC_ARM) + ASM_FILES_STARTUP := $(STARTUP_FILE).S +else +$(error Only GCC ARM is supported at this moment) +endif + + +# Collected source files for libraries +SOURCES_LIBS := $(SOURCES_PDL) +SOURCES_LIBS += $(SOURCES_PDL_STARTUP) + +# Collected include directories for libraries +INCLUDE_DIRS_LIBS := $(addprefix -I,$(INCLUDE_DIRS_PDL)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_PDL_STARTUP)) +INCLUDE_DIRS_LIBS += $(addprefix -I,$(INCLUDE_DIRS_CORE_LIB)) + +ASM_FILES_PDL := +ifeq ($(COMPILER), GCC_ARM) +ASM_FILES_PDL += $(PSOC6_LIBS_PATH)/mtb-pdl-cat1/drivers/source/TOOLCHAIN_GCC_ARM/cy_syslib_gcc.S +else +$(error Only GCC ARM is supported at this moment) +endif + +ASM_FILES_LIBS := $(ASM_FILES_PDL) + +# Add define for PDL version +DEFINES_PDL += -DPDL_VERSION=$(PDL_VERSION) + +DEFINES_LIBS := $(DEFINES_PLATFORM) +DEFINES_LIBS += $(DEFINES_PDL) diff --git a/bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_flash_map.c b/bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_flash_map.c new file mode 100644 index 0000000..af52bab --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_flash_map.c @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2020 Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /*******************************************************************************/ + +#ifdef MCUBOOT_HAVE_ASSERT_H +#include "mcuboot_config/mcuboot_assert.h" +#else +#include +#endif + +#include +#include +#include + +#include "mcuboot_config/mcuboot_config.h" +#include "flash_map_backend/flash_map_backend.h" +#include + +#include "bootutil/bootutil_log.h" + +#include "cy_pdl.h" + +#ifdef CY_BOOT_USE_EXTERNAL_FLASH +#include "cy_smif_psoc6.h" +#endif +/* + * For now, we only support one flash device. + * + * Pick a random device ID for it that's unlikely to collide with + * anything "real". + */ +#define FLASH_DEVICE_ID 111 +#define FLASH_MAP_ENTRY_MAGIC 0xd00dbeef + +#define FLASH_AREA_IMAGE_SECTOR_SIZE FLASH_AREA_IMAGE_SCRATCH_SIZE + +#ifndef CY_BOOTLOADER_START_ADDRESS +#define CY_BOOTLOADER_START_ADDRESS (0x10000000) +#endif + +#ifndef CY_BOOT_INTERNAL_FLASH_ERASE_VALUE +/* This is the value of internal flash bytes after an erase */ +#define CY_BOOT_INTERNAL_FLASH_ERASE_VALUE (0x00) +#endif + +#ifndef CY_BOOT_EXTERNAL_FLASH_ERASE_VALUE +/* This is the value of external flash bytes after an erase */ +#define CY_BOOT_EXTERNAL_FLASH_ERASE_VALUE (0xff) +#endif + +#ifdef CY_FLASH_MAP_EXT_DESC +/* Nothing to be there when external FlashMap Descriptors are used */ +#else +static struct flash_area bootloader = +{ + .fa_id = FLASH_AREA_BOOTLOADER, + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = CY_BOOTLOADER_START_ADDRESS, + .fa_size = CY_BOOT_BOOTLOADER_SIZE +}; + +static struct flash_area primary_1 = +{ + .fa_id = FLASH_AREA_IMAGE_PRIMARY(0), + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = CY_FLASH_BASE + CY_BOOT_BOOTLOADER_SIZE, + .fa_size = CY_BOOT_PRIMARY_1_SIZE +}; + +#ifndef CY_BOOT_USE_EXTERNAL_FLASH +static struct flash_area secondary_1 = +{ + .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = CY_FLASH_BASE +\ + CY_BOOT_BOOTLOADER_SIZE +\ + CY_BOOT_PRIMARY_1_SIZE, + .fa_size = CY_BOOT_SECONDARY_1_SIZE +}; +#else +static struct flash_area secondary_1 = +{ + .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), + .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(CY_BOOT_EXTERNAL_DEVICE_INDEX), + .fa_off = CY_SMIF_BASE_MEM_OFFSET, + .fa_size = CY_BOOT_SECONDARY_1_SIZE +}; +#endif +#if (MCUBOOT_IMAGE_NUMBER == 2) /* if dual-image */ +static struct flash_area primary_2 = +{ + .fa_id = FLASH_AREA_IMAGE_PRIMARY(1), + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = CY_FLASH_BASE +\ + CY_BOOT_BOOTLOADER_SIZE +\ + CY_BOOT_PRIMARY_1_SIZE +\ + CY_BOOT_SECONDARY_1_SIZE, + .fa_size = CY_BOOT_PRIMARY_2_SIZE +}; + +static struct flash_area secondary_2 = +{ + .fa_id = FLASH_AREA_IMAGE_SECONDARY(1), + /* it is for external flash memory + .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(CY_BOOT_EXTERNAL_DEVICE_INDEX), */ +#ifndef CY_BOOT_USE_EXTERNAL_FLASH + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = CY_FLASH_BASE +\ + CY_BOOT_BOOTLOADER_SIZE +\ + CY_BOOT_PRIMARY_1_SIZE +\ + CY_BOOT_SECONDARY_1_SIZE +\ + CY_BOOT_PRIMARY_2_SIZE, +#else + .fa_device_id = FLASH_DEVICE_EXTERNAL_FLASH(CY_BOOT_EXTERNAL_DEVICE_INDEX), + .fa_off = CY_SMIF_BASE_MEM_OFFSET + 0x40000, +#endif + .fa_size = CY_BOOT_SECONDARY_2_SIZE +}; +#endif +#endif + +#ifdef MCUBOOT_SWAP_USING_SCRATCH +static struct flash_area scratch = +{ + .fa_id = FLASH_AREA_IMAGE_SCRATCH, + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, +#if (MCUBOOT_IMAGE_NUMBER == 1) /* if single-image */ + .fa_off = CY_FLASH_BASE +\ + CY_BOOT_BOOTLOADER_SIZE +\ + CY_BOOT_PRIMARY_1_SIZE +\ + CY_BOOT_SECONDARY_1_SIZE, +#elif (MCUBOOT_IMAGE_NUMBER == 2) /* if dual-image */ + .fa_off = CY_FLASH_BASE +\ + CY_BOOT_BOOTLOADER_SIZE +\ + CY_BOOT_PRIMARY_1_SIZE +\ + CY_BOOT_SECONDARY_1_SIZE +\ + CY_BOOT_PRIMARY_2_SIZE +\ + CY_BOOT_SECONDARY_2_SIZE, +#endif + .fa_size = CY_BOOT_SCRATCH_SIZE +}; +#endif + +#ifdef CY_FLASH_MAP_EXT_DESC +/* Use external Flash Map Descriptors */ +extern struct flash_area *boot_area_descs[]; +#else +struct flash_area *boot_area_descs[] = +{ + &bootloader, + &primary_1, + &secondary_1, +#if (MCUBOOT_IMAGE_NUMBER == 2) /* if dual-image */ + &primary_2, + &secondary_2, +#endif +#ifdef MCUBOOT_SWAP_USING_SCRATCH + &scratch, +#endif + NULL +}; +#endif + +/* Returns device flash start based on supported fa_id */ +int flash_device_base(uint8_t fd_id, uintptr_t *ret) +{ + if (fd_id != FLASH_DEVICE_INTERNAL_FLASH) { + BOOT_LOG_ERR("invalid flash ID %d; expected %d", + fd_id, FLASH_DEVICE_INTERNAL_FLASH); + return -1; + } + *ret = CY_FLASH_BASE; + return 0; +} + +/* Opens the area for use. id is one of the `fa_id`s */ +int flash_area_open(uint8_t id, const struct flash_area **fa) +{ + int ret = -1; + uint32_t i = 0; + + while(NULL != boot_area_descs[i]) + { + if(id == boot_area_descs[i]->fa_id) + { + *fa = boot_area_descs[i]; + ret = 0; + break; + } + i++; + } + return ret; +} + +void flash_area_close(const struct flash_area *fa) +{ + (void)fa;/* Nothing to do there */ +} + +/* +* Reads `len` bytes of flash memory at `off` to the buffer at `dst` +*/ +int flash_area_read(const struct flash_area *fa, uint32_t off, void *dst, + uint32_t len) +{ + int rc = 0; + size_t addr; + + /* check if requested offset not less then flash area (fa) start */ + assert(off < fa->fa_off); + assert(off + len < fa->fa_off); + /* convert to absolute address inside a device*/ + addr = fa->fa_off + off; + + if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) + { + /* flash read by simple memory copying */ + memcpy((void *)dst, (const void*)addr, (size_t)len); + } +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) + { + rc = psoc6_smif_read(fa, addr, dst, len); + } +#endif + else + { + /* incorrect/non-existing flash device id */ + rc = -1; + } + + if (rc != 0) { + BOOT_LOG_ERR("Flash area read error, rc = %d", (int)rc); + } + return rc; +} + +/* +* Writes `len` bytes of flash memory at `off` from the buffer at `src` + */ +int flash_area_write(const struct flash_area *fa, uint32_t off, + const void *src, uint32_t len) +{ + cy_en_flashdrv_status_t rc = CY_FLASH_DRV_SUCCESS; + size_t write_start_addr; + size_t write_end_addr; + const uint32_t * row_ptr = NULL; + + assert(off < fa->fa_off); + assert(off + len < fa->fa_off); + + /* convert to absolute address inside a device */ + write_start_addr = fa->fa_off + off; + write_end_addr = fa->fa_off + off + len; + + if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) + { + uint32_t row_number = 0; + uint32_t row_addr = 0; + + assert(!(len % CY_FLASH_SIZEOF_ROW)); + assert(!(write_start_addr % CY_FLASH_SIZEOF_ROW)); + + row_number = (write_end_addr - write_start_addr) / CY_FLASH_SIZEOF_ROW; + row_addr = write_start_addr; + + row_ptr = (uint32_t *) src; + + for (uint32_t i = 0; i < row_number; i++) + { + rc = Cy_Flash_WriteRow(row_addr, row_ptr); + + row_addr += (uint32_t) CY_FLASH_SIZEOF_ROW; + row_ptr = row_ptr + CY_FLASH_SIZEOF_ROW / 4; + } + } +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) + { + rc = psoc6_smif_write(fa, write_start_addr, src, len); + } +#endif + else + { + /* incorrect/non-existing flash device id */ + rc = -1; + } + + return (int) rc; +} + +/*< Erases `len` bytes of flash memory at `off` */ +int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len) +{ + cy_en_flashdrv_status_t rc = CY_FLASH_DRV_SUCCESS; + size_t erase_start_addr; + size_t erase_end_addr; + + assert(off < fa->fa_off); + assert(off + len < fa->fa_off); + assert(!(len % CY_FLASH_SIZEOF_ROW)); + + /* convert to absolute address inside a device*/ + erase_start_addr = fa->fa_off + off; + erase_end_addr = fa->fa_off + off + len; + + if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) + { + int row_number = 0; + uint32_t row_addr = 0; + + row_number = (erase_end_addr - erase_start_addr) / CY_FLASH_SIZEOF_ROW; + + while (row_number != 0) + { + row_number--; + row_addr = erase_start_addr + row_number * (uint32_t) CY_FLASH_SIZEOF_ROW; + rc = Cy_Flash_EraseRow(row_addr); + } + } +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) + { + rc = psoc6_smif_erase(erase_start_addr, len); + } +#endif + else + { + /* incorrect/non-existing flash device id */ + rc = -1; + } + return (int) rc; +} + +/*< Returns this `flash_area`s alignment */ +uint32_t flash_area_align(const struct flash_area *fa) +{ + int ret = -1; + if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) + { + ret = CY_FLASH_ALIGN; + } +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) + { + return qspi_get_prog_size(); + } +#endif + else + { + /* incorrect/non-existing flash device id */ + ret = -1; + } + return ret; +} + +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS +/*< Initializes an array of flash_area elements for the slot's sectors */ +int flash_area_to_sectors(int idx, int *cnt, struct flash_area *fa) +{ + int rc = 0; + + if (fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) + { + (void)idx; + (void)cnt; + rc = 0; + } +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + else if ((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) + { + (void)idx; + (void)cnt; + rc = 0; + } +#endif + else + { + /* incorrect/non-existing flash device id */ + rc = -1; + } + return rc; +} +#endif + +/* + * This depends on the mappings defined in sysflash.h. + * MCUBoot uses continuous numbering for the primary slot, the secondary slot, + * and the scratch while zephyr might number it differently. + */ +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + switch (slot) { + case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); + case 1: return FLASH_AREA_IMAGE_SECONDARY(image_index); + case 2: return FLASH_AREA_IMAGE_SCRATCH; + } + + return -1; /* flash_area_open will fail on that */ +} + +int flash_area_id_from_image_slot(int slot) +{ + return flash_area_id_from_multi_image_slot(0, slot); +} + +int flash_area_id_to_multi_image_slot(int image_index, int area_id) +{ + if (area_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) { + return 0; + } + if (area_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) { + return 1; + } + + return -1; +} + +uint8_t flash_area_erased_val(const struct flash_area *fap) +{ + int ret = 0; + + if (fap->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) + { + ret = CY_BOOT_INTERNAL_FLASH_ERASE_VALUE; + } +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + else if ((fap->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) + { + ret = CY_BOOT_EXTERNAL_FLASH_ERASE_VALUE; + } +#endif + else + { + assert(false) ; + } + + return ret ; +} + +int flash_area_read_is_empty(const struct flash_area *fa, uint32_t off, + void *dst, uint32_t len) +{ + uint8_t *mem_dest; + int rc; + + mem_dest = (uint8_t *)dst; + rc = flash_area_read(fa, off, dst, len); + if (rc) { + return -1; + } + + for (uint8_t i = 0; i < len; i++) { + if (mem_dest[i] != flash_area_erased_val(fa)) { + return 0; + } + } + return 1; +} + +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS +int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret) +{ + int rc = 0; + uint32_t i = 0; + struct flash_area *fa = NULL; + + while(NULL != boot_area_descs[i]) + { + if(idx == boot_area_descs[i]->fa_id) + { + fa = boot_area_descs[i]; + break; + } + i++; + } + + if(NULL != boot_area_descs[i]) + { + size_t sector_size = 0; + + if(fa->fa_device_id == FLASH_DEVICE_INTERNAL_FLASH) + { + sector_size = CY_FLASH_SIZEOF_ROW; + } +#ifdef CY_BOOT_USE_EXTERNAL_FLASH + else if((fa->fa_device_id & FLASH_DEVICE_EXTERNAL_FLAG) == FLASH_DEVICE_EXTERNAL_FLAG) + { + /* implement for SMIF */ + /* lets assume they are equal */ + sector_size = CY_FLASH_SIZEOF_ROW; + } +#endif + else + { + rc = -1; + } + + if(0 == rc) + { + uint32_t addr = 0; + size_t sectors_n = 0; + + sectors_n = (fa->fa_size + (sector_size - 1)) / sector_size; + assert(sectors_n <= *cnt); + + addr = fa->fa_off; + for(i = 0; i < sectors_n; i++) + { + ret[i].fs_size = sector_size ; + ret[i].fs_off = addr ; + addr += sector_size ; + } + + *cnt = sectors_n; + } + } + else + { + rc = -1; + } + + return rc; +} +#endif diff --git a/bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_smif_psoc6.c b/bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_smif_psoc6.c new file mode 100644 index 0000000..62f7137 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/cy_flash_pal/cy_smif_psoc6.c @@ -0,0 +1,143 @@ +/***************************************************************************//** +* \file cy_smif_psoc6.c +* \version 1.0 +* +* \brief +* This is the source file of external flash driver adoption layer between PSoC6 +* and standard MCUBoot code. +* +******************************************************************************** +* \copyright +* +* (c) 2020, Cypress Semiconductor Corporation +* or a subsidiary of Cypress Semiconductor Corporation. All rights +* reserved. +* +* This software, including source code, documentation and related +* materials ("Software"), is owned by Cypress Semiconductor +* Corporation or one of its subsidiaries ("Cypress") and is protected by +* and subject to worldwide patent protection (United States and foreign), +* United States copyright laws and international treaty provisions. +* Therefore, you may use this Software only as provided in the license +* agreement accompanying the software package from which you +* obtained this Software ("EULA"). +* +* If no EULA applies, Cypress hereby grants you a personal, non- +* exclusive, non-transferable license to copy, modify, and compile the +* Software source code solely for use in connection with Cypress?s +* integrated circuit products. Any reproduction, modification, translation, +* compilation, or representation of this Software except as specified +* above is prohibited without the express written permission of Cypress. +* +* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO +* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, +* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +* PARTICULAR PURPOSE. Cypress reserves the right to make +* changes to the Software without notice. Cypress does not assume any +* liability arising out of the application or use of the Software or any +* product or circuit described in the Software. Cypress does not +* authorize its products for use in any products where a malfunction or +* failure of the Cypress product may reasonably be expected to result in +* significant property damage, injury or death ("High Risk Product"). By +* including Cypress's product in a High Risk Product, the manufacturer +* of such system or application assumes all risk of such use and in doing +* so agrees to indemnify Cypress against all liability. +* +******************************************************************************/ +#include "string.h" +#include "stdlib.h" +#include "stdbool.h" + +#ifdef MCUBOOT_HAVE_ASSERT_H +#include "mcuboot_config/mcuboot_assert.h" +#else +#include +#endif + +#include "flash_map_backend/flash_map_backend.h" +#include + +#include "cy_device_headers.h" +#include "cy_smif_psoc6.h" +#include "cy_flash.h" +#include "cy_syspm.h" + +#include "flash_qspi.h" + +#define PSOC6_WR_SUCCESS (0) +#define PSOC6_WR_ERROR_INVALID_PARAMETER (1) +#define PSOC6_WR_ERROR_FLASH_WRITE (2) + +#define PSOC6_FLASH_ERASE_BLOCK_SIZE CY_FLASH_SIZEOF_ROW /* PSoC6 Flash erases by Row */ + +int psoc6_smif_read(const struct flash_area *fap, + off_t addr, + void *data, + size_t len) +{ + int rc = -1; + cy_stc_smif_mem_config_t *cfg; + cy_en_smif_status_t st; + uint32_t address; + + cfg = qspi_get_memory_config(FLASH_DEVICE_GET_EXT_INDEX(fap->fa_device_id)); + + address = addr - CY_SMIF_BASE_MEM_OFFSET; + + st = Cy_SMIF_MemRead(qspi_get_device(), cfg, address, data, len, qspi_get_context()); + if (st == CY_SMIF_SUCCESS) { + rc = 0; + } + return rc; +} + +int psoc6_smif_write(const struct flash_area *fap, + off_t addr, + const void *data, + size_t len) +{ + int rc = -1; + cy_en_smif_status_t st; + cy_stc_smif_mem_config_t *cfg; + uint32_t address; + + cfg = qspi_get_memory_config(FLASH_DEVICE_GET_EXT_INDEX(fap->fa_device_id)); + + address = addr - CY_SMIF_BASE_MEM_OFFSET; + + st = Cy_SMIF_MemWrite(qspi_get_device(), cfg, address, data, len, qspi_get_context()); + if (st == CY_SMIF_SUCCESS) { + rc = 0; + } + return rc; +} + +int psoc6_smif_erase(off_t addr, size_t size) +{ + int rc = -1; + cy_en_smif_status_t st; + uint32_t address; + + /* It is erase sector-only + * + * There is no power-safe way to erase flash partially + * this leads upgrade slots have to be at least + * eraseSectorSize far from each other; + */ + cy_stc_smif_mem_config_t *memCfg = qspi_get_memory_config(0); + + address = (addr - CY_SMIF_BASE_MEM_OFFSET ) & ~((uint32_t)(memCfg->deviceCfg->eraseSize - 1u)); + + (void)size; + + st = Cy_SMIF_MemEraseSector(qspi_get_device(), + memCfg, + address, + memCfg->deviceCfg->eraseSize, + qspi_get_context()); + if (st == CY_SMIF_SUCCESS) { + rc = 0; + } + return rc; +} diff --git a/bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.c b/bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.c new file mode 100644 index 0000000..e1f6c27 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.c @@ -0,0 +1,494 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/***************************************************************************//** +* \file flash_qspi.c +* \version 1.0 +* +* \brief +* This is the source file of external flash driver adaptation layer between PSoC6 +* and standard MCUBoot code. +* +******************************************************************************** +* \copyright +* +* (c) 2020, Cypress Semiconductor Corporation +* or a subsidiary of Cypress Semiconductor Corporation. All rights +* reserved. +* +* This software, including source code, documentation and related +* materials ("Software"), is owned by Cypress Semiconductor +* Corporation or one of its subsidiaries ("Cypress") and is protected by +* and subject to worldwide patent protection (United States and foreign), +* United States copyright laws and international treaty provisions. +* Therefore, you may use this Software only as provided in the license +* agreement accompanying the software package from which you +* obtained this Software ("EULA"). +* +* If no EULA applies, Cypress hereby grants you a personal, non- +* exclusive, non-transferable license to copy, modify, and compile the +* Software source code solely for use in connection with Cypress?s +* integrated circuit products. Any reproduction, modification, translation, +* compilation, or representation of this Software except as specified +* above is prohibited without the express written permission of Cypress. +* +* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO +* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, +* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +* PARTICULAR PURPOSE. Cypress reserves the right to make +* changes to the Software without notice. Cypress does not assume any +* liability arising out of the application or use of the Software or any +* product or circuit described in the Software. Cypress does not +* authorize its products for use in any products where a malfunction or +* failure of the Cypress product may reasonably be expected to result in +* significant property damage, injury or death ("High Risk Product"). By +* including Cypress's product in a High Risk Product, the manufacturer +* of such system or application assumes all risk of such use and in doing +* so agrees to indemnify Cypress against all liability. +* +******************************************************************************/ +#include "cy_pdl.h" +#include +#include "flash_qspi.h" + +#define CY_SMIF_SYSCLK_HFCLK_DIVIDER CY_SYSCLK_CLKHF_DIVIDE_BY_4 + +/* This is the board specific stuff that should align with your board. + * + * QSPI resources: + * + * SS0 - P11_2 + * SS1 - P11_1 + * SS2 - P11_0 + * SS3 - P12_4 + * + * D3 - P11_3 + * D2 - P11_4 + * D1 - P11_5 + * D0 - P11_6 + * + * SCK - P11_7 + * + * SMIF Block - SMIF0 + * + */ + +/* SMIF SlaveSelect Configurations */ +struct qspi_ss_config +{ + GPIO_PRT_Type* SS_Port; + int SS_Pin; + en_hsiom_sel_t SS_Mux; +}; + +#if (defined(PSOC_064_2M) || \ + defined(PSOC_064_1M) || \ + defined(PSOC_062_2M) || \ + defined(PSOC_062_1M)) + #define CY_BOOTLOADER_SMIF_SS_CFG_NUM 4 +#elif defined(PSOC_064_512K) || defined(PSOC_062_512K) + #define CY_BOOTLOADER_SMIF_SS_CFG_NUM 3 +#else +#error "Platform device name is unsupported." +#endif +struct qspi_ss_config qspi_SS_Configuration[CY_BOOTLOADER_SMIF_SS_CFG_NUM] = +{ + { + .SS_Port = GPIO_PRT11, + .SS_Pin = 2, + .SS_Mux = P11_2_SMIF_SPI_SELECT0 + }, + { + .SS_Port = GPIO_PRT11, + .SS_Pin = 1, + .SS_Mux = P11_1_SMIF_SPI_SELECT1 + }, + { + .SS_Port = GPIO_PRT11, + .SS_Pin = 0, + .SS_Mux = P11_0_SMIF_SPI_SELECT2 + }, +#if(CY_BOOTLOADER_SMIF_SS_CFG_NUM > 3) + { + .SS_Port = GPIO_PRT12, + .SS_Pin = 4, + .SS_Mux = P12_4_SMIF_SPI_SELECT3 + } +#endif +}; + +static GPIO_PRT_Type *D3Port = GPIO_PRT11; +static int D3Pin = 3; +static en_hsiom_sel_t D3MuxPort = P11_3_SMIF_SPI_DATA3; + +static GPIO_PRT_Type *D2Port = GPIO_PRT11; +static int D2Pin = 4; +static en_hsiom_sel_t D2MuxPort = P11_4_SMIF_SPI_DATA2; + +static GPIO_PRT_Type *D1Port = GPIO_PRT11; +static int D1Pin = 5; +static en_hsiom_sel_t D1MuxPort = P11_5_SMIF_SPI_DATA1; + +static GPIO_PRT_Type *D0Port = GPIO_PRT11; +static int D0Pin = 6; +static en_hsiom_sel_t D0MuxPort = P11_6_SMIF_SPI_DATA0; + +static GPIO_PRT_Type *SCKPort = GPIO_PRT11; +static int SCKPin = 7; +static en_hsiom_sel_t SCKMuxPort = P11_7_SMIF_SPI_CLK; + +static SMIF_Type *QSPIPort = SMIF0; + +cy_stc_smif_mem_cmd_t sfdpcmd = +{ + .command = 0x5A, + .cmdWidth = CY_SMIF_WIDTH_SINGLE, + .addrWidth = CY_SMIF_WIDTH_SINGLE, + .mode = 0xFFFFFFFFU, + .dummyCycles = 8, + .dataWidth = CY_SMIF_WIDTH_SINGLE, +}; + +static cy_stc_smif_mem_cmd_t rdcmd0; +static cy_stc_smif_mem_cmd_t wrencmd0; +static cy_stc_smif_mem_cmd_t wrdiscmd0; +static cy_stc_smif_mem_cmd_t erasecmd0; +static cy_stc_smif_mem_cmd_t chiperasecmd0; +static cy_stc_smif_mem_cmd_t pgmcmd0; +static cy_stc_smif_mem_cmd_t readsts0; +static cy_stc_smif_mem_cmd_t readstsqecmd0; +static cy_stc_smif_mem_cmd_t writestseqcmd0; + +static cy_stc_smif_mem_device_cfg_t dev_sfdp_0 = +{ + .numOfAddrBytes = 4, + .readSfdpCmd = &sfdpcmd, + .readCmd = &rdcmd0, + .writeEnCmd = &wrencmd0, + .writeDisCmd = &wrdiscmd0, + .programCmd = &pgmcmd0, + .eraseCmd = &erasecmd0, + .chipEraseCmd = &chiperasecmd0, + .readStsRegWipCmd = &readsts0, + .readStsRegQeCmd = &readstsqecmd0, + .writeStsRegQeCmd = &writestseqcmd0, +}; + +static cy_stc_smif_mem_config_t mem_sfdp_0 = +{ + /* The base address the memory slave is mapped to in the PSoC memory map. + Valid when the memory-mapped mode is enabled. */ + .baseAddress = 0x18000000U, + /* The size allocated in the PSoC memory map, for the memory slave device. + The size is allocated from the base address. Valid when the memory mapped mode is enabled. */ +/* .memMappedSize = 0x4000000U, */ + .flags = CY_SMIF_FLAG_DETECT_SFDP, + .slaveSelect = CY_SMIF_SLAVE_SELECT_0, + .dataSelect = CY_SMIF_DATA_SEL0, + .deviceCfg = &dev_sfdp_0 +}; + +cy_stc_smif_mem_config_t *mems_sfdp[1] = +{ + &mem_sfdp_0 +}; + +/* make it exported if used in TOC (cy_serial_flash_prog.c) */ +/* cy_stc_smif_block_config_t smifBlockConfig_sfdp = */ +static cy_stc_smif_block_config_t smifBlockConfig_sfdp = +{ + .memCount = 1, + .memConfig = mems_sfdp, +}; + +static cy_stc_smif_block_config_t *smif_blk_config; + +static cy_stc_smif_context_t QSPI_context; + +cy_stc_smif_config_t const QSPI_config = +{ + .mode = CY_SMIF_NORMAL, + .deselectDelay = 1, + .rxClockSel = CY_SMIF_SEL_INV_INTERNAL_CLK, + .blockEvent = CY_SMIF_BUS_ERROR +}; + +cy_stc_sysint_t smifIntConfig = +{/* ATTENTION: make sure proper Interrupts configured for CM0p or M4 cores */ + .intrSrc = NvicMux7_IRQn, + .cm0pSrc = smif_interrupt_IRQn, + .intrPriority = 1 +}; + +/* SMIF pinouts configurations */ +static cy_stc_gpio_pin_config_t QSPI_SS_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG_IN_OFF, + .hsiom = P11_2_SMIF_SPI_SELECT0, /* lets use SS0 by default */ + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; +const cy_stc_gpio_pin_config_t QSPI_DATA3_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG, + .hsiom = P11_3_SMIF_SPI_DATA3, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; +const cy_stc_gpio_pin_config_t QSPI_DATA2_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG, + .hsiom = P11_4_SMIF_SPI_DATA2, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; +const cy_stc_gpio_pin_config_t QSPI_DATA1_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG, + .hsiom = P11_5_SMIF_SPI_DATA1, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; +const cy_stc_gpio_pin_config_t QSPI_DATA0_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG, + .hsiom = P11_6_SMIF_SPI_DATA0, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; +const cy_stc_gpio_pin_config_t QSPI_SCK_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG_IN_OFF, + .hsiom = P11_7_SMIF_SPI_CLK, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; + +void Isr_SMIF(void) +{ + Cy_SMIF_Interrupt(QSPIPort, &QSPI_context); +} + +cy_en_smif_status_t qspi_init_hardware() +{ + cy_en_smif_status_t st; + + + Cy_GPIO_Pin_Init(D3Port, D3Pin, &QSPI_DATA3_config); + Cy_GPIO_SetHSIOM(D3Port, D3Pin, D3MuxPort); + + Cy_GPIO_Pin_Init(D2Port, D2Pin, &QSPI_DATA2_config); + Cy_GPIO_SetHSIOM(D2Port, D2Pin, D2MuxPort); + + Cy_GPIO_Pin_Init(D1Port, D1Pin, &QSPI_DATA1_config); + Cy_GPIO_SetHSIOM(D1Port, D1Pin, D1MuxPort); + + Cy_GPIO_Pin_Init(D0Port, D0Pin, &QSPI_DATA0_config); + Cy_GPIO_SetHSIOM(D0Port, D0Pin, D0MuxPort); + + Cy_GPIO_Pin_Init(SCKPort, SCKPin, &QSPI_SCK_config); + Cy_GPIO_SetHSIOM(SCKPort, SCKPin, SCKMuxPort); + + Cy_SysClk_ClkHfSetSource(CY_SYSCLK_CLKHF_IN_CLKPATH2, CY_SYSCLK_CLKHF_IN_CLKPATH0); + Cy_SysClk_ClkHfSetDivider(CY_SYSCLK_CLKHF_IN_CLKPATH2, CY_SMIF_SYSCLK_HFCLK_DIVIDER); + Cy_SysClk_ClkHfEnable(CY_SYSCLK_CLKHF_IN_CLKPATH2); + + /* + * Setup the interrupt for the SMIF block. For the CM0 there + * is a two stage process to setup the interrupts. + */ + Cy_SysInt_Init(&smifIntConfig, Isr_SMIF); + + st = Cy_SMIF_Init(QSPIPort, &QSPI_config, 1000, &QSPI_context); + if (st != CY_SMIF_SUCCESS) + { + return st; + } + NVIC_EnableIRQ(smifIntConfig.intrSrc); /* Finally, Enable the SMIF interrupt */ + + Cy_SMIF_Enable(QSPIPort, &QSPI_context); + + return CY_SMIF_SUCCESS; +} + +cy_stc_smif_mem_config_t *qspi_get_memory_config(int index) +{ + return smif_blk_config->memConfig[index]; +} + +SMIF_Type *qspi_get_device() +{ + return QSPIPort; +} + +cy_stc_smif_context_t *qspi_get_context() +{ + return &QSPI_context; +} + +cy_en_smif_status_t qspi_init(cy_stc_smif_block_config_t *blk_config) +{ + cy_en_smif_status_t st; + + st = qspi_init_hardware(); + if (st == CY_SMIF_SUCCESS) + { + smif_blk_config = blk_config; + st = Cy_SMIF_MemInit(QSPIPort, smif_blk_config, &QSPI_context); + } + return st; +} + +cy_en_smif_status_t qspi_init_sfdp(uint32_t smif_id) +{ + cy_en_smif_status_t stat = CY_SMIF_SUCCESS; + + cy_stc_smif_mem_config_t **memCfg = smifBlockConfig_sfdp.memConfig; + + GPIO_PRT_Type *SS_Port; + int SS_Pin; + en_hsiom_sel_t SS_MuxPort; + + switch(smif_id) + { + case 1: + (*memCfg)->slaveSelect = CY_SMIF_SLAVE_SELECT_0; + break; + case 2: + (*memCfg)->slaveSelect = CY_SMIF_SLAVE_SELECT_1; + break; + case 3: + (*memCfg)->slaveSelect = CY_SMIF_SLAVE_SELECT_2; + break; +#if(CY_BOOTLOADER_SMIF_SS_CFG_NUM > 3) + case 4: + (*memCfg)->slaveSelect = CY_SMIF_SLAVE_SELECT_3; + break; +#endif + default: + stat = -1; + break; + } + + if(CY_SMIF_SUCCESS == stat) + { + SS_Port = qspi_SS_Configuration[smif_id-1].SS_Port; + SS_Pin = qspi_SS_Configuration[smif_id-1].SS_Pin; + SS_MuxPort = qspi_SS_Configuration[smif_id-1].SS_Mux; + + QSPI_SS_config.hsiom = SS_MuxPort; + + Cy_GPIO_Pin_Init(SS_Port, SS_Pin, &QSPI_SS_config); + Cy_GPIO_SetHSIOM(SS_Port, SS_Pin, SS_MuxPort); + + stat = qspi_init(&smifBlockConfig_sfdp); + } + return stat; +} + +uint32_t qspi_get_prog_size(void) +{ + cy_stc_smif_mem_config_t **memCfg = smifBlockConfig_sfdp.memConfig; + return (*memCfg)->deviceCfg->programSize; +} + +uint32_t qspi_get_erase_size(void) +{ + cy_stc_smif_mem_config_t **memCfg = smifBlockConfig_sfdp.memConfig; + return (*memCfg)->deviceCfg->eraseSize; +} + +uint32_t qspi_get_mem_size(void) +{ + cy_stc_smif_mem_config_t **memCfg = smifBlockConfig_sfdp.memConfig; + return (*memCfg)->deviceCfg->memSize; +} + +void qspi_deinit(uint32_t smif_id) +{ + Cy_SMIF_MemDeInit(QSPIPort); + + Cy_SMIF_Disable(QSPIPort); + + Cy_SysClk_ClkHfDisable(CY_SYSCLK_CLKHF_IN_CLKPATH2); + + NVIC_DisableIRQ(smifIntConfig.intrSrc); + Cy_SysInt_DisconnectInterruptSource(smifIntConfig.intrSrc, smifIntConfig.cm0pSrc); + + Cy_GPIO_Port_Deinit(qspi_SS_Configuration[smif_id-1].SS_Port); + Cy_GPIO_Port_Deinit(SCKPort); + Cy_GPIO_Port_Deinit(D0Port); + Cy_GPIO_Port_Deinit(D1Port); + Cy_GPIO_Port_Deinit(D2Port); + Cy_GPIO_Port_Deinit(D3Port); +} diff --git a/bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.h b/bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.h new file mode 100644 index 0000000..80e0d51 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/cy_flash_pal/flash_qspi/flash_qspi.h @@ -0,0 +1,70 @@ +/***************************************************************************//** +* \file flash_qspi.h +* \version 1.0 +* +* \brief +* This is the header file for PSoC6 external flash driver adoption layer. +* +******************************************************************************** +* \copyright +* +* © 2020, Cypress Semiconductor Corporation +* or a subsidiary of Cypress Semiconductor Corporation. All rights +* reserved. +* +* This software, including source code, documentation and related +* materials ("Software"), is owned by Cypress Semiconductor +* Corporation or one of its subsidiaries ("Cypress") and is protected by +* and subject to worldwide patent protection (United States and foreign), +* United States copyright laws and international treaty provisions. +* Therefore, you may use this Software only as provided in the license +* agreement accompanying the software package from which you +* obtained this Software ("EULA"). +* +* If no EULA applies, Cypress hereby grants you a personal, non- +* exclusive, non-transferable license to copy, modify, and compile the +* Software source code solely for use in connection with Cypress?s +* integrated circuit products. Any reproduction, modification, translation, +* compilation, or representation of this Software except as specified +* above is prohibited without the express written permission of Cypress. +* +* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO +* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, +* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +* PARTICULAR PURPOSE. Cypress reserves the right to make +* changes to the Software without notice. Cypress does not assume any +* liability arising out of the application or use of the Software or any +* product or circuit described in the Software. Cypress does not +* authorize its products for use in any products where a malfunction or +* failure of the Cypress product may reasonably be expected to result in +* significant property damage, injury or death ("High Risk Product"). By +* including Cypress's product in a High Risk Product, the manufacturer +* of such system or application assumes all risk of such use and in doing +* so agrees to indemnify Cypress against all liability. +* +******************************************************************************/ +#ifndef __FLASH_QSPI_H__ +#define __FLASH_QSPI_H__ + +#include +#include "cy_pdl.h" + +/* make it exported if used in TOC (cy_serial_flash_prog.c) */ +/* cy_stc_smif_block_config_t smifBlockConfig_sfdp; */ + +cy_en_smif_status_t qspi_init_sfdp(uint32_t smif_id); +cy_en_smif_status_t qspi_init(cy_stc_smif_block_config_t *blk_config); +cy_en_smif_status_t qspi_init_hardware(void); +uint32_t qspi_get_prog_size(void); +uint32_t qspi_get_erase_size(void); +uint32_t qspi_get_mem_size(void); + +SMIF_Type *qspi_get_device(void); +cy_stc_smif_context_t *qspi_get_context(void); +cy_stc_smif_mem_config_t *qspi_get_memory_config(int index); +void qspi_dump_device(cy_stc_smif_mem_device_cfg_t *dev); + +void qspi_deinit(uint32_t smif_id); + +#endif /* __FLASH_QSPI_H__ */ diff --git a/bootloader/mcuboot/boot/cypress/cy_flash_pal/include/cy_smif_psoc6.h b/bootloader/mcuboot/boot/cypress/cy_flash_pal/include/cy_smif_psoc6.h new file mode 100644 index 0000000..fe1150d --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/cy_flash_pal/include/cy_smif_psoc6.h @@ -0,0 +1,64 @@ +/***************************************************************************//** +* \file cy_smif_psoc6.h +* \version 1.0 +* +* \brief +* This is the header file for PSoC6 SMIF driver adoption layer. +* +******************************************************************************** +* \copyright +* +* © 2019, Cypress Semiconductor Corporation +* or a subsidiary of Cypress Semiconductor Corporation. All rights +* reserved. +* +* This software, including source code, documentation and related +* materials ("Software"), is owned by Cypress Semiconductor +* Corporation or one of its subsidiaries ("Cypress") and is protected by +* and subject to worldwide patent protection (United States and foreign), +* United States copyright laws and international treaty provisions. +* Therefore, you may use this Software only as provided in the license +* agreement accompanying the software package from which you +* obtained this Software ("EULA"). +* +* If no EULA applies, Cypress hereby grants you a personal, non- +* exclusive, non-transferable license to copy, modify, and compile the +* Software source code solely for use in connection with Cypress?s +* integrated circuit products. Any reproduction, modification, translation, +* compilation, or representation of this Software except as specified +* above is prohibited without the express written permission of Cypress. +* +* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO +* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, +* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +* PARTICULAR PURPOSE. Cypress reserves the right to make +* changes to the Software without notice. Cypress does not assume any +* liability arising out of the application or use of the Software or any +* product or circuit described in the Software. Cypress does not +* authorize its products for use in any products where a malfunction or +* failure of the Cypress product may reasonably be expected to result in +* significant property damage, injury or death ("High Risk Product"). By +* including Cypress's product in a High Risk Product, the manufacturer +* of such system or application assumes all risk of such use and in doing +* so agrees to indemnify Cypress against all liability. +* +******************************************************************************/ + +#ifndef CY_SMIF_PSOC6_H_ +#define CY_SMIF_PSOC6_H_ + +#include "stddef.h" +#include "stdbool.h" + +#include "flash_qspi.h" + +#ifndef off_t +typedef long int off_t; +#endif + +int psoc6_smif_read(const struct flash_area *fap, off_t addr, void *data, size_t len); +int psoc6_smif_write(const struct flash_area *fap, off_t addr, const void *data, size_t len); +int psoc6_smif_erase(off_t addr, size_t size); + +#endif /* CY_SMIF_PSOC6_H_ */ diff --git a/bootloader/mcuboot/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h b/bootloader/mcuboot/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000..6330934 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * Copyright (c) 2020 Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /*******************************************************************************/ + +#ifndef __FLASH_MAP_BACKEND_H__ +#define __FLASH_MAP_BACKEND_H__ + +#include +#include "cy_flash.h" +#define FLASH_DEVICE_INDEX_MASK (0x7F) +#define FLASH_DEVICE_GET_EXT_INDEX(n) ((n) & FLASH_DEVICE_INDEX_MASK) +#define FLASH_DEVICE_EXTERNAL_FLAG (0x80) +#define FLASH_DEVICE_INTERNAL_FLASH (0x7F) +#define FLASH_DEVICE_EXTERNAL_FLASH(index) (FLASH_DEVICE_EXTERNAL_FLAG | index) + +#ifndef CY_BOOT_EXTERNAL_DEVICE_INDEX +/* assume first(one) SMIF device is used */ +#define CY_BOOT_EXTERNAL_DEVICE_INDEX (0) +#endif + +/** + * + * Provides abstraction of flash regions for type of use. + * I.e. dude where's my image? + * + * System will contain a map which contains flash areas. Every + * region will contain flash identifier, offset within flash and length. + * + * 1. This system map could be in a file within filesystem (Initializer + * must know/figure out where the filesystem is at). + * 2. Map could be at fixed location for project (compiled to code) + * 3. Map could be at specific place in flash (put in place at mfg time). + * + * Note that the map you use must be valid for BSP it's for, + * match the linker scripts when platform executes from flash, + * and match the target offset specified in download script. + */ +#include + +/** + * @brief Structure describing an area on a flash device. + * + * Multiple flash devices may be available in the system, each of + * which may have its own areas. For this reason, flash areas track + * which flash device they are part of. + */ +struct flash_area { + /** + * This flash area's ID; unique in the system. + */ + uint8_t fa_id; + + /** + * ID of the flash device this area is a part of. + */ + uint8_t fa_device_id; + + uint16_t pad16; + + /** + * This area's offset, relative to the beginning of its flash + * device's storage. + */ + uint32_t fa_off; + + /** + * This area's size, in bytes. + */ + uint32_t fa_size; +}; + +static inline uint8_t flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +static inline uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + return fa->fa_device_id; +} + +static inline uint32_t flash_area_get_off(const struct flash_area *fa) +{ + return fa->fa_off; +} + +static inline uint32_t flash_area_get_size(const struct flash_area *fa) +{ + return fa->fa_size; +} + +/** + * @brief Structure describing a sector within a flash area. + * + * Each sector has an offset relative to the start of its flash area + * (NOT relative to the start of its flash device), and a size. A + * flash area may contain sectors with different sizes. + */ +struct flash_sector { + /** + * Offset of this sector, from the start of its flash area (not device). + */ + uint32_t fs_off; + + /** + * Size of this sector, in bytes. + */ + uint32_t fs_size; +}; + +static inline uint32_t flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +static inline uint32_t flash_sector_get_size(const struct flash_sector *fs) +{ + return fs->fs_size; +} + +struct flash_map_entry { + uint32_t magic; + struct flash_area area; + unsigned int ref_count; +}; + +/* + * Retrieve a memory-mapped flash device's base address. + * On success, the address will be stored in the value pointed to by + * ret. + * Returns 0 on success, or an error code on failure. + */ +int flash_device_base(uint8_t fd_id, uintptr_t *ret); + +/*< Opens the area for use. id is one of the `fa_id`s */ +int flash_area_open(uint8_t id, const struct flash_area **); +void flash_area_close(const struct flash_area *); +/*< Reads `len` bytes of flash memory at `off` to the buffer at `dst` */ +int flash_area_read(const struct flash_area *, uint32_t off, void *dst, + uint32_t len); +/*< Writes `len` bytes of flash memory at `off` from the buffer at `src` */ +int flash_area_write(const struct flash_area *, uint32_t off, + const void *src, uint32_t len); +/*< Erases `len` bytes of flash memory at `off` */ +int flash_area_erase(const struct flash_area *, uint32_t off, uint32_t len); +/*< Returns this `flash_area`s alignment */ +uint32_t flash_area_align(const struct flash_area *); +/*< Initializes an array of flash_area elements for the slot's sectors */ +int flash_area_to_sectors(int idx, int *cnt, struct flash_area *ret); +/*< Returns the `fa_id` for slot, where slot is 0 (primary) or 1 (secondary) */ +int flash_area_id_from_image_slot(int slot); + +int flash_area_id_from_multi_image_slot(int image_index, int slot); +int flash_area_id_to_multi_image_slot(int image_index, int area_id); +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS +int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret); +#endif +/* + * Returns the value expected to be read when accesing any erased + * flash byte. + */ +uint8_t flash_area_erased_val(const struct flash_area *fap); + +/* + * Reads len bytes from off, and checks if the read data is erased. + * + * Returns 1 if erased, 0 if non-erased, and -1 on failure. + */ +int flash_area_read_is_empty(const struct flash_area *fa, uint32_t off, + void *dst, uint32_t len); + +#endif /* __FLASH_MAP_BACKEND_H__ */ diff --git a/bootloader/mcuboot/boot/cypress/host.mk b/bootloader/mcuboot/boot/cypress/host.mk new file mode 100644 index 0000000..6b6851e --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/host.mk @@ -0,0 +1,48 @@ +################################################################################ +# \file host.mk +# \version 1.0 +# +# \brief +# Makefile to describe host environment for Cypress MCUBoot based applications. +# +################################################################################ +# \copyright +# Copyright 2018-2021 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +# Detect host OS to make resolving compiler pathes easier +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S), Darwin) + HOST_OS = osx +else + ifeq ($(UNAME_S), Linux) + HOST_OS = linux + else + HOST_OS = win + endif +endif + +ifeq ($(HOST_OS), win) + define get_os_path +$(shell cygpath -m $(1)) + endef +else + define get_os_path +$(1) + endef +endif + +PRJ_DIR=$(call get_os_path, $(CURDIR)) diff --git a/bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pem b/bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pem new file mode 100644 index 0000000..e1f8e15 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQguR/Jq6LjMgp8DVtE +7pKguttNo6L239aEcijzGOr5C72hRANCAAT/NroNASdTGo6bS8r0+C+30YcG0WLV +chWs+99DnOr3SZoalv6/pCNIVrwFv3KkJsmsZUbNNmxeMPr+IlfGGPg0 +-----END PRIVATE KEY----- diff --git a/bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pub b/bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pub new file mode 100644 index 0000000..8174b5d --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/keys/cypress-test-ec-p256.pub @@ -0,0 +1,16 @@ +/* Autogenerated by imgtool.py, do not edit. */ +const unsigned char ecdsa_pub_key[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0xff, 0x36, 0xba, 0x0d, 0x01, + 0x27, 0x53, 0x1a, 0x8e, 0x9b, 0x4b, 0xca, 0xf4, + 0xf8, 0x2f, 0xb7, 0xd1, 0x87, 0x06, 0xd1, 0x62, + 0xd5, 0x72, 0x15, 0xac, 0xfb, 0xdf, 0x43, 0x9c, + 0xea, 0xf7, 0x49, 0x9a, 0x1a, 0x96, 0xfe, 0xbf, + 0xa4, 0x23, 0x48, 0x56, 0xbc, 0x05, 0xbf, 0x72, + 0xa4, 0x26, 0xc9, 0xac, 0x65, 0x46, 0xcd, 0x36, + 0x6c, 0x5e, 0x30, 0xfa, 0xfe, 0x22, 0x57, 0xc6, + 0x18, 0xf8, 0x34, +}; +const unsigned int ecdsa_pub_key_len = 91; diff --git a/bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.c b/bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.c new file mode 100644 index 0000000..0472f3e --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.c @@ -0,0 +1,270 @@ +/***************************************************************************//** +* \file cy_retarget_io.c +* +* \brief +* Provides APIs for retargeting stdio to UART hardware contained on the Cypress +* kits. +* +******************************************************************************** +* \copyright +* Copyright 2018-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#include "cy_retarget_io_pdl.h" + +#include "cycfg_peripherals.h" + +#include "cy_sysint.h" +#include "cy_scb_uart.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Tracks the previous character sent to output stream */ +#ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF +static char cy_retarget_io_stdout_prev_char = 0; +#endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + +cy_stc_scb_uart_context_t CYBSP_UART_context; + +static uint8_t cy_retarget_io_getchar(void); +static void cy_retarget_io_putchar(char c); + +#if defined(__ARMCC_VERSION) /* ARM-MDK */ + /*************************************************************************** + * Function Name: fputc + ***************************************************************************/ + __attribute__((weak)) int fputc(int ch, FILE *f) + { + (void)f; + #ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF + if ((char)ch == '\n' && cy_retarget_io_stdout_prev_char != '\r') + { + cy_retarget_io_putchar('\r'); + } + + cy_retarget_io_stdout_prev_char = (char)ch; + #endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + cy_retarget_io_putchar(ch); + return (ch); + } +#elif defined (__ICCARM__) /* IAR */ + #include + + /*************************************************************************** + * Function Name: __write + ***************************************************************************/ + __weak size_t __write(int handle, const unsigned char * buffer, size_t size) + { + size_t nChars = 0; + /* This template only writes to "standard out", for all other file + * handles it returns failure. */ + if (handle != _LLIO_STDOUT) + { + return (_LLIO_ERROR); + } + if (buffer != NULL) + { + for (/* Empty */; nChars < size; ++nChars) + { + #ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF + if (*buffer == '\n' && cy_retarget_io_stdout_prev_char != '\r') + { + cy_retarget_io_putchar('\r'); + } + + cy_retarget_io_stdout_prev_char = *buffer; + #endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + cy_retarget_io_putchar(*buffer); + ++buffer; + } + } + return (nChars); + } +#else /* (__GNUC__) GCC */ + /* Add an explicit reference to the floating point printf library to allow + the usage of floating point conversion specifier. */ + __asm (".global _printf_float"); + /*************************************************************************** + * Function Name: _write + ***************************************************************************/ + __attribute__((weak)) int _write (int fd, const char *ptr, int len) + { + int nChars = 0; + (void)fd; + if (ptr != NULL) + { + for (/* Empty */; nChars < len; ++nChars) + { + #ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF + if (*ptr == '\n' && cy_retarget_io_stdout_prev_char != '\r') + { + cy_retarget_io_putchar('\r'); + } + + cy_retarget_io_stdout_prev_char = *ptr; + #endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + cy_retarget_io_putchar((uint32_t)*ptr); + ++ptr; + } + } + return (nChars); + } +#endif + + +#if defined(__ARMCC_VERSION) /* ARM-MDK */ + /*************************************************************************** + * Function Name: fgetc + ***************************************************************************/ + __attribute__((weak)) int fgetc(FILE *f) + { + (void)f; + return (cy_retarget_io_getchar()); + } +#elif defined (__ICCARM__) /* IAR */ + __weak size_t __read(int handle, unsigned char * buffer, size_t size) + { + /* This template only reads from "standard in", for all other file + handles it returns failure. */ + if ((handle != _LLIO_STDIN) || (buffer == NULL)) + { + return (_LLIO_ERROR); + } + else + { + *buffer = cy_retarget_io_getchar(); + return (1); + } + } +#else /* (__GNUC__) GCC */ + /* Add an explicit reference to the floating point scanf library to allow + the usage of floating point conversion specifier. */ + __asm (".global _scanf_float"); + __attribute__((weak)) int _read (int fd, char *ptr, int len) + { + int nChars = 0; + (void)fd; + if (ptr != NULL) + { + for(/* Empty */;nChars < len;++ptr) + { + *ptr = (char)cy_retarget_io_getchar(); + ++nChars; + if((*ptr == '\n') || (*ptr == '\r')) + { + break; + } + } + } + return (nChars); + } +#endif + +static uint8_t cy_retarget_io_getchar(void) +{ + uint32_t read_value = Cy_SCB_UART_Get(CYBSP_UART_HW); + while (read_value == CY_SCB_UART_RX_NO_DATA) + { + read_value = Cy_SCB_UART_Get(CYBSP_UART_HW); + } + + return (uint8_t)read_value; +} + +static void cy_retarget_io_putchar(char c) +{ + uint32_t count = 0; + while (count == 0) + { + count = Cy_SCB_UART_Put(CYBSP_UART_HW, c); + } +} + +static cy_rslt_t cy_retarget_io_pdl_setbaud(CySCB_Type *base, uint32_t baudrate) +{ + cy_rslt_t result = CY_RSLT_TYPE_ERROR; + + uint8_t oversample_value = 8u; + uint8_t frac_bits = 0u; + uint32_t divider; + + Cy_SCB_UART_Disable(base, NULL); + + result = (cy_rslt_t) Cy_SysClk_PeriphDisableDivider(CY_SYSCLK_DIV_16_BIT, 0); + + divider = ((Cy_SysClk_ClkPeriGetFrequency() * (1 << frac_bits)) + ((baudrate * oversample_value) / 2)) / (baudrate * oversample_value) - 1; + + if (result == CY_RSLT_SUCCESS) + { + result = (cy_rslt_t) Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_16_BIT, 0u, divider); + } + + if (result == CY_RSLT_SUCCESS) + { + result = Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_16_BIT, 0u); + } + + Cy_SCB_UART_Enable(base); + + return result; +} + +cy_rslt_t cy_retarget_io_pdl_init(uint32_t baudrate) +{ + cy_rslt_t result = CY_RSLT_TYPE_ERROR; + + result = (cy_rslt_t)Cy_SCB_UART_Init(CYBSP_UART_HW, &CYBSP_UART_config, &CYBSP_UART_context); + + if (result == CY_RSLT_SUCCESS) + { + result = cy_retarget_io_pdl_setbaud(CYBSP_UART_HW, baudrate); + } + + if (result == CY_RSLT_SUCCESS) + { + Cy_SCB_UART_Enable(CYBSP_UART_HW); + } + + return result; +} + +/** + * @brief Wait while UART completes transfer. Try for tries_count times - + * once each 10 millisecons. + */ +void cy_retarget_io_wait_tx_complete(CySCB_Type *base, uint32_t tries_count) +{ + while(tries_count > 0) + { + if (!Cy_SCB_UART_IsTxComplete(base)) { + Cy_SysLib_DelayCycles(10 * cy_delayFreqKhz); + tries_count -= 1; + } else { + return; + } + } +} + +void cy_retarget_io_pdl_deinit() +{ + Cy_SCB_UART_DeInit(CYBSP_UART_HW); +} + +#if defined(__cplusplus) +} +#endif diff --git a/bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.h b/bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.h new file mode 100644 index 0000000..f0c8733 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/libs/retarget_io_pdl/cy_retarget_io_pdl.h @@ -0,0 +1,62 @@ +/***************************************************************************//** +* \file cy_retarget_io.h +* +* \brief +* Provides APIs for transmitting messages to or from the board via standard +* printf/scanf functions. Messages are transmitted over a UART connection which +* is generally connected to a host machine. Transmission is done at 115200 baud +* using the tx and rx pins provided by the user of this library. The UART +* instance is made available via cy_retarget_io_uart_obj in case any changes +* to the default configuration are desired. +* NOTE: If the application is built using newlib-nano, by default, floating +* point format strings (%f) are not supported. To enable this support you must +* add '-u _printf_float' to the linker command line. +* +******************************************************************************** +* \copyright +* Copyright 2018-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#pragma once + +#include +#include "cy_result.h" +#include "cy_pdl.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/** UART baud rate */ +#define CY_RETARGET_IO_BAUDRATE (115200) + +/** Defining this macro enables conversion of line feed (LF) into carriage + * return followed by line feed (CR & LF) on the output direction (STDOUT). You + * can define this macro through the DEFINES variable in the application + * Makefile. + */ +#define CY_RETARGET_IO_CONVERT_LF_TO_CRLF + +cy_rslt_t cy_retarget_io_pdl_init(uint32_t baudrate); + +void cy_retarget_io_wait_tx_complete(CySCB_Type *base, uint32_t tries_count); + +void cy_retarget_io_pdl_deinit(void); + +#if defined(__cplusplus) +} +#endif + diff --git a/bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.c b/bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.c new file mode 100644 index 0000000..286e9c3 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.c @@ -0,0 +1,183 @@ +/***************************************************************************//** +* \file cy_wdg.c +* +* \brief +* Provides a high level interface for interacting with the Cypress Watchdog Timer. +* This interface abstracts out the chip specific details. If any chip specific +* functionality is necessary, or performance is critical the low level functions +* can be used directly. +* +* +******************************************************************************** +* \copyright +* Copyright 2019-2020 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#include +#include "watchdog.h" +#include "cy_wdt.h" +#include "cy_utils.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(COMPONENT_PSOC6) +#define _cy_wdg_lock() Cy_WDT_Lock() +#define _cy_wdg_unlock() Cy_WDT_Unlock() +#else +#define _cy_wdg_lock() +#define _cy_wdg_unlock() +#endif + +// ((2^16 * 2) + (2^16 - 1)) * .030518 ms +/** Maximum WDT timeout in milliseconds */ +#define _cy_wdg_MAX_TIMEOUT_MS 6000 + +/** Maximum number of ignore bits */ +#define _cy_wdg_MAX_IGNORE_BITS 12 + +typedef struct { + uint16_t min_period_ms; // Minimum period in milliseconds that can be represented with this many ignored bits + uint16_t round_threshold_ms; // Timeout threshold in milliseconds from which to round up to the minimum period +} _cy_wdg_ignore_bits_data_t; + +// ILO Frequency = 32768 Hz +// ILO Period = 1 / 32768 Hz = .030518 ms +// WDT Reset Period (timeout_ms) = .030518 ms * (2 * 2^(16 - ignore_bits) + match) +// ignore_bits range: 0 - 12 +// match range: 0 - (2^(16 - ignore_bits) - 1) +static const _cy_wdg_ignore_bits_data_t _cy_wdg_ignore_data[] = { + {4001, 3001}, // 0 bits: min period: 4001ms, max period: 6000ms, round up from 3001+ms + {2001, 1500}, // 1 bit: min period: 2001ms, max period: 3000ms, round up from 1500+ms + {1001, 750}, // 2 bits: min period: 1001ms, max period: 1499ms, round up from 750+ms + {501, 375}, // 3 bits: min period: 501ms, max period: 749ms, round up from 375+ms + {251, 188}, // 4 bits: min period: 251ms, max period: 374ms, round up from 188+ms + {126, 94}, // 5 bits: min period: 126ms, max period: 187ms, round up from 94+ms + {63, 47}, // 6 bits: min period: 63ms, max period: 93ms, round up from 47+ms + {32, 24}, // 7 bits: min period: 32ms, max period: 46ms, round up from 24+ms + {16, 12}, // 8 bits: min period: 16ms, max period: 23ms, round up from 12+ms + {8, 6}, // 9 bits: min period: 8ms, max period: 11ms, round up from 6+ms + {4, 3}, // 10 bits: min period: 4ms, max period: 5ms, round up from 3+ms + {2, 2}, // 11 bits: min period: 2ms, max period: 2ms + {1, 1} // 12 bits: min period: 1ms, max period: 1ms +}; + +static bool _cy_wdg_initialized = false; +static bool _cy_wdg_pdl_initialized = false; +static uint16_t _cy_wdg_initial_timeout_ms = 0; +static uint8_t _cy_wdg_initial_ignore_bits = 0; + +static __INLINE uint32_t _cy_wdg_timeout_to_ignore_bits(uint32_t *timeout_ms) { + for (uint32_t i = 0; i <= _cy_wdg_MAX_IGNORE_BITS; i++) + { + if (*timeout_ms >= _cy_wdg_ignore_data[i].round_threshold_ms) + { + if (*timeout_ms < _cy_wdg_ignore_data[i].min_period_ms) + *timeout_ms = _cy_wdg_ignore_data[i].min_period_ms; + return i; + } + } + return _cy_wdg_MAX_IGNORE_BITS; // Should never reach this +} + +static __INLINE uint16_t _cy_wdg_timeout_to_match(uint16_t timeout_ms, uint16_t ignore_bits) +{ + // match = (timeout_ms / .030518 ms) - (2 * 2^(16 - ignore_bits)) + return (uint16_t)(timeout_ms / .030518) - (1UL << (17 - ignore_bits)) + Cy_WDT_GetCount(); +} + +/* Start API implementing */ + +cy_rslt_t cy_wdg_init(uint32_t timeout_ms) +{ + if (timeout_ms == 0 || timeout_ms > _cy_wdg_MAX_TIMEOUT_MS) + { + return -1; + } + + if (_cy_wdg_initialized) + { + return -1; + } + + _cy_wdg_initialized = true; + + if (!_cy_wdg_pdl_initialized) + { + Cy_WDT_Enable(); + Cy_WDT_MaskInterrupt(); + _cy_wdg_pdl_initialized = true; + } + + cy_wdg_stop(); + + _cy_wdg_initial_timeout_ms = timeout_ms; + uint8_t ignore_bits = _cy_wdg_timeout_to_ignore_bits(&timeout_ms); + _cy_wdg_initial_ignore_bits = ignore_bits; + + Cy_WDT_SetIgnoreBits(ignore_bits); + + Cy_WDT_SetMatch(_cy_wdg_timeout_to_match(timeout_ms, ignore_bits)); + + cy_wdg_start(); + + return CY_RSLT_SUCCESS; +} + +void cy_wdg_free() +{ + cy_wdg_stop(); + + _cy_wdg_initialized = false; +} + +void cy_wdg_kick() +{ + /* Clear to prevent reset from WDT */ + Cy_WDT_ClearWatchdog(); + + _cy_wdg_unlock(); + Cy_WDT_SetMatch(_cy_wdg_timeout_to_match(_cy_wdg_initial_timeout_ms, _cy_wdg_initial_ignore_bits)); + _cy_wdg_lock(); +} + +void cy_wdg_start() +{ + _cy_wdg_unlock(); + Cy_WDT_Enable(); + _cy_wdg_lock(); +} + +void cy_wdg_stop() +{ + _cy_wdg_unlock(); + Cy_WDT_Disable(); +} + +uint32_t cy_wdg_get_timeout_ms() +{ + return _cy_wdg_initial_timeout_ms; +} + +uint32_t cy_wdg_get_max_timeout_ms(void) +{ + return _cy_wdg_MAX_TIMEOUT_MS; +} + +#if defined(__cplusplus) +} +#endif diff --git a/bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.h b/bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.h new file mode 100644 index 0000000..0d45f82 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/libs/watchdog/watchdog.h @@ -0,0 +1,90 @@ +/***************************************************************************//** +* \file cy_wdg.h +* +* \brief +* Provides a high level interface for interacting with the Watchdog Timer. +* This interface abstracts out the chip specific details. If any chip specific +* functionality is necessary, or performance is critical the low level functions +* can be used directly. +* +******************************************************************************** +* \copyright +* Copyright 2019-2020 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include +#include "cy_result.h" + +/** Initialize and start the WDT +* +* The specified timeout must be at least 1ms and at most the WDT's maximum timeout (see cy_wdg_get_max_timeout_ms()). +* @param[inout] timeout_ms The time in milliseconds before the WDT times out (1ms - max) (see cy_wdg_get_max_timeout_ms()) +* @return The status of the init request +* +* Returns CY_RSLT_SUCCESS if the operation was successfull. +*/ +cy_rslt_t cy_wdg_init(uint32_t timeout_ms); + +/** Free the WDT +* +* Powers down the WDT. +* After calling this function no other WDT functions should be called except +* cy_wdg_init(). +*/ + +void cy_wdg_free(); + +/** Resets the WDT +* +* This function should be called periodically to prevent the WDT from timing out and resetting the device. +*/ +void cy_wdg_kick(); + +/** Start (enable) the WDT +* +* @return The status of the start request +*/ +void cy_wdg_start(); + +/** Stop (disable) the WDT +* +* @return The status of the stop request +*/ +void cy_wdg_stop(); + +/** Get the WDT timeout +* +* Gets the time in milliseconds before the WDT times out. +* @return The time in milliseconds before the WDT times out +*/ +uint32_t cy_wdg_get_timeout_ms(); + +/** Gets the maximum WDT timeout in milliseconds +* +* @return The maximum timeout for the WDT +*/ +uint32_t cy_wdg_get_max_timeout_ms(void); + +#if defined(__cplusplus) +} +#endif diff --git a/bootloader/mcuboot/boot/cypress/platforms.mk b/bootloader/mcuboot/boot/cypress/platforms.mk new file mode 100644 index 0000000..39fd34a --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms.mk @@ -0,0 +1,78 @@ +################################################################################ +# \file platforms.mk +# \version 1.0 +# +# \brief +# Makefile to describe supported boards and platforms for Cypress MCUBoot based applications. +# +################################################################################ +# \copyright +# Copyright 2018-2019 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +include host.mk + +# Target platform is built for. PSOC_062_2M is set by default +# Supported: +# - PSOC_062_2M +# - PSOC_062_1M +# - PSOC_062_512K + +# default PLATFORM +PLATFORM ?= PSOC_062_2M + +# supported platforms +PLATFORMS := PSOC_062_2M PSOC_062_1M PSOC_062_512K + +ifneq ($(filter $(PLATFORM), $(PLATFORMS)),) +else +$(error Not supported platform: '$(PLATFORM)') +endif + +# For which core this application is built +CORE ?= CM0P + +# MCU device selection, based on target device. +# Default chips are used for supported platforms +# This can be redefined in case of other chip usage +ifeq ($(PLATFORM), PSOC_062_2M) +# base kit CY8CPROTO-062-4343W +DEVICE ?= CY8C624ABZI-D44 +PLATFORM_SUFFIX := 02 +else ifeq ($(PLATFORM), PSOC_062_1M) +# base kit CY8CKIT-062-WIFI-BT +DEVICE ?= CY8C6247BZI-D54 +PLATFORM_SUFFIX := 01 +else ifeq ($(PLATFORM), PSOC_062_512K) +# base kit CY8CPROTO-062S3-4343W +DEVICE ?= CY8C6245LQI-S3D72 +PLATFORM_SUFFIX := 03 +endif + +# Add device name to defines +DEFINES += $(DEVICE) +DEFINES += $(PLATFORM) + +# Convert defines to regular -DMY_NAME style +ifneq ($(DEFINES),) + DEFINES_PLATFORM :=$(addprefix -D, $(subst -,_,$(DEFINES))) +endif + +ifeq ($(MAKEINFO) , 1) +$(info $(PLATFORM_SUFFIX)) +$(info $(DEVICE)) +$(info $(DEFINES_PLATFORM)) +endif diff --git a/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/cy8c6xxa_cm0plus.ld b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/cy8c6xxa_cm0plus.ld new file mode 100644 index 0000000..1f3a5a9 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/cy8c6xxa_cm0plus.ld @@ -0,0 +1,418 @@ +/***************************************************************************//** +* \file cy8c6xxa_cm0plus.ld +* \version 2.70 +* +* Linker file for the GNU C compiler. +* +* The main purpose of the linker script is to describe how the sections in the +* input files should be mapped into the output file, and to control the memory +* layout of the output file. +* +* \note The entry point location is fixed and starts at 0x10000000. The valid +* application image should be placed there. +* +* \note The linker files included with the PDL template projects must be generic +* and handle all common use cases. Your project may not use every section +* defined in the linker files. In that case you may see warnings during the +* build process. In your project, you can simply comment out or remove the +* relevant code in the linker file. +* +******************************************************************************** +* \copyright +* Copyright 2016-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +SEARCH_DIR(.) +GROUP(-lgcc -lc -lnosys) +ENTRY(Reset_Handler) + +/* The size of the stack section at the end of CM0+ SRAM */ +STACK_SIZE = 0x1000; + +/* Force symbol to be entered in the output file as an undefined symbol. Doing +* this may, for example, trigger linking of additional modules from standard +* libraries. You may list several symbols for each EXTERN, and you may use +* EXTERN multiple times. This command has the same effect as the -u command-line +* option. +*/ +EXTERN(Reset_Handler) + +/* The MEMORY section below describes the location and size of blocks of memory in the target. +* Use this section to specify the memory regions available for allocation. +*/ +MEMORY +{ + /* The ram and flash regions control RAM and flash memory allocation for the CM0+ core. + * You can change the memory allocation by editing the 'ram' and 'flash' regions. + * Note that 2 KB of RAM (at the end of the SRAM) are reserved for system use. + * Using this memory region for other purposes will lead to unexpected behavior. + * Your changes must be aligned with the corresponding memory regions for the CM4 core in 'xx_cm4_dual.ld', + * where 'xx' is the device group; for example, 'cy8c6xx7_cm4_dual.ld'. + */ + ram (rwx) : ORIGIN = 0x08000000, LENGTH = 0x2000 + flash (rx) : ORIGIN = 0x10000000, LENGTH = 0x2000 + + /* This is a 32K flash region used for EEPROM emulation. This region can also be used as the general purpose flash. + * You can assign sections to this memory region for only one of the cores. + * Note some middleware (e.g. BLE, Emulated EEPROM) can place their data into this memory region. + * Therefore, repurposing this memory region will prevent such middleware from operation. + */ + em_eeprom (rx) : ORIGIN = 0x14000000, LENGTH = 0x8000 /* 32 KB */ + + /* The following regions define device specific memory regions and must not be changed. */ + sflash_user_data (rx) : ORIGIN = 0x16000800, LENGTH = 0x800 /* Supervisory flash: User data */ + sflash_nar (rx) : ORIGIN = 0x16001A00, LENGTH = 0x200 /* Supervisory flash: Normal Access Restrictions (NAR) */ + sflash_public_key (rx) : ORIGIN = 0x16005A00, LENGTH = 0xC00 /* Supervisory flash: Public Key */ + sflash_toc_2 (rx) : ORIGIN = 0x16007C00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 */ + sflash_rtoc_2 (rx) : ORIGIN = 0x16007E00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 Copy */ + xip (rx) : ORIGIN = 0x18000000, LENGTH = 0x8000000 /* 128 MB */ + efuse (r) : ORIGIN = 0x90700000, LENGTH = 0x100000 /* 1 MB */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ + + +SECTIONS +{ + .cy_app_header : + { + KEEP(*(.cy_app_header)) + } > flash + + /* Cortex-M0+ application flash area */ + .text : + { + . = ALIGN(4); + __Vectors = . ; + KEEP(*(.vectors)) + . = ALIGN(4); + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + . = ALIGN(4); + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + /* Read-only code (constants). */ + *(.rodata .rodata.* .constdata .constdata.* .conststring .conststring.*) + + KEEP(*(.eh_frame*)) + } > flash + + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > flash + + __exidx_start = .; + + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > flash + __exidx_end = .; + + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_psoc6_02_cm0plus.S */ + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + + /* Copy interrupt vectors from flash to RAM */ + LONG (__Vectors) /* From */ + LONG (__ram_vectors_start__) /* To */ + LONG (__Vectors_End - __Vectors) /* Size */ + + /* Copy data section to RAM */ + LONG (__etext) /* From */ + LONG (__data_start__) /* To */ + LONG (__data_end__ - __data_start__) /* Size */ + + __copy_table_end__ = .; + } > flash + + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_psoc6_02_cm0plus.S */ + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + __zero_table_end__ = .; + } > flash + + __etext = . ; + + + .ramVectors (NOLOAD) : ALIGN(8) + { + __ram_vectors_start__ = .; + KEEP(*(.ram_vectors)) + __ram_vectors_end__ = .; + } > ram + + + .data __ram_vectors_end__ : AT (__etext) + { + __data_start__ = .; + + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + + KEEP(*(.cy_ramfunc*)) + . = ALIGN(4); + + __data_end__ = .; + + } > ram + + + /* Place variables in the section that should not be initialized during the + * device startup. + */ + .noinit (NOLOAD) : ALIGN(8) + { + KEEP(*(.noinit)) + } > ram + + + /* The uninitialized global or static variables are placed in this section. + * + * The NOLOAD attribute tells linker that .bss section does not consume + * any space in the image. The NOLOAD attribute changes the .bss type to + * NOBITS, and that makes linker to A) not allocate section in memory, and + * A) put information to clear the section with all zeros during application + * loading. + * + * Without the NOLOAD attribute, the .bss section might get PROGBITS type. + * This makes linker to A) allocate zeroed section in memory, and B) copy + * this section to RAM during application loading. + */ + .bss (NOLOAD): + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > ram + + + .heap (NOLOAD): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + . = ORIGIN(ram) + LENGTH(ram) - STACK_SIZE; + __HeapLimit = .; + } > ram + + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > ram + + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(ram) + LENGTH(ram); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + + + /* Emulated EEPROM Flash area */ + .cy_em_eeprom : + { + KEEP(*(.cy_em_eeprom)) + } > em_eeprom + + + /* Supervisory Flash: User data */ + .cy_sflash_user_data : + { + KEEP(*(.cy_sflash_user_data)) + } > sflash_user_data + + + /* Supervisory Flash: Normal Access Restrictions (NAR) */ + .cy_sflash_nar : + { + KEEP(*(.cy_sflash_nar)) + } > sflash_nar + + + /* Supervisory Flash: Public Key */ + .cy_sflash_public_key : + { + KEEP(*(.cy_sflash_public_key)) + } > sflash_public_key + + + /* Supervisory Flash: Table of Content # 2 */ + .cy_toc_part2 : + { + KEEP(*(.cy_toc_part2)) + } > sflash_toc_2 + + + /* Supervisory Flash: Table of Content # 2 Copy */ + .cy_rtoc_part2 : + { + KEEP(*(.cy_rtoc_part2)) + } > sflash_rtoc_2 + + + /* Places the code in the Execute in Place (XIP) section. See the smif driver + * documentation for details. + */ + .cy_xip : + { + KEEP(*(.cy_xip)) + } > xip + + + /* eFuse */ + .cy_efuse : + { + KEEP(*(.cy_efuse)) + } > efuse + + + /* These sections are used for additional metadata (silicon revision, + * Silicon/JTAG ID, etc.) storage. + */ + .cymeta 0x90500000 : { KEEP(*(.cymeta)) } :NONE +} + + +/* The following symbols used by the cymcuelftool. */ +/* Flash */ +__cy_memory_0_start = 0x10000000; +__cy_memory_0_length = 0x00200000; +__cy_memory_0_row_size = 0x200; + +/* Emulated EEPROM Flash area */ +__cy_memory_1_start = 0x14000000; +__cy_memory_1_length = 0x8000; +__cy_memory_1_row_size = 0x200; + +/* Supervisory Flash */ +__cy_memory_2_start = 0x16000000; +__cy_memory_2_length = 0x8000; +__cy_memory_2_row_size = 0x200; + +/* XIP */ +__cy_memory_3_start = 0x18000000; +__cy_memory_3_length = 0x08000000; +__cy_memory_3_row_size = 0x200; + +/* eFuse */ +__cy_memory_4_start = 0x90700000; +__cy_memory_4_length = 0x100000; +__cy_memory_4_row_size = 1; + +/* EOF */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/startup_psoc6_02_cm0plus.S b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/startup_psoc6_02_cm0plus.S new file mode 100644 index 0000000..2641f62 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM0P/GCC_ARM/startup_psoc6_02_cm0plus.S @@ -0,0 +1,372 @@ +/**************************************************************************//** + * @file startup_psoc6_02_cm0plus.S + * @brief CMSIS Core Device Startup File for + * ARMCM0plus Device Series + * @version V5.00 + * @date 02. March 2016 + ******************************************************************************/ +/* + * Copyright (c) 2009-2016 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /* Address of the NMI handler */ + #define CY_NMI_HANLDER_ADDR 0x0000000D + + /* The CPU VTOR register */ + #define CY_CPU_VTOR_ADDR 0xE000ED08 + + /* Copy flash vectors and data section to RAM */ + #define __STARTUP_COPY_MULTIPLE + + /* Clear single BSS section */ + #define __STARTUP_CLEAR_BSS + + .syntax unified + .arch armv6-m + + .section .stack + .align 3 +#ifdef __STACK_SIZE + .equ Stack_Size, __STACK_SIZE +#else + .equ Stack_Size, 0x00001000 +#endif + .globl __StackTop + .globl __StackLimit +__StackLimit: + .space Stack_Size + .size __StackLimit, . - __StackLimit +__StackTop: + .size __StackTop, . - __StackTop + + .section .heap + .align 3 +#ifdef __HEAP_SIZE + .equ Heap_Size, __HEAP_SIZE +#else + .equ Heap_Size, 0x00000400 +#endif + .globl __HeapBase + .globl __HeapLimit +__HeapBase: + .if Heap_Size + .space Heap_Size + .endif + .size __HeapBase, . - __HeapBase +__HeapLimit: + .size __HeapLimit, . - __HeapLimit + + .section .vectors + .align 2 + .globl __Vectors +__Vectors: + .long __StackTop /* Top of Stack */ + .long Reset_Handler /* Reset Handler */ + .long CY_NMI_HANLDER_ADDR /* NMI Handler */ + .long HardFault_Handler /* Hard Fault Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long SVC_Handler /* SVCall Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long PendSV_Handler /* PendSV Handler */ + .long SysTick_Handler /* SysTick Handler */ + + /* External interrupts Description */ + .long NvicMux0_IRQHandler /* CPU User Interrupt #0 */ + .long NvicMux1_IRQHandler /* CPU User Interrupt #1 */ + .long NvicMux2_IRQHandler /* CPU User Interrupt #2 */ + .long NvicMux3_IRQHandler /* CPU User Interrupt #3 */ + .long NvicMux4_IRQHandler /* CPU User Interrupt #4 */ + .long NvicMux5_IRQHandler /* CPU User Interrupt #5 */ + .long NvicMux6_IRQHandler /* CPU User Interrupt #6 */ + .long NvicMux7_IRQHandler /* CPU User Interrupt #7 */ + .long Internal0_IRQHandler /* Internal SW Interrupt #0 */ + .long Internal1_IRQHandler /* Internal SW Interrupt #1 */ + .long Internal2_IRQHandler /* Internal SW Interrupt #2 */ + .long Internal3_IRQHandler /* Internal SW Interrupt #3 */ + .long Internal4_IRQHandler /* Internal SW Interrupt #4 */ + .long Internal5_IRQHandler /* Internal SW Interrupt #5 */ + .long Internal6_IRQHandler /* Internal SW Interrupt #6 */ + .long Internal7_IRQHandler /* Internal SW Interrupt #7 */ + + .size __Vectors, . - __Vectors + .equ __VectorsSize, . - __Vectors + + .section .ram_vectors + .align 2 + .globl __ramVectors +__ramVectors: + .space __VectorsSize + .size __ramVectors, . - __ramVectors + + + .text + .thumb + .thumb_func + .align 2 + + /* + * Device startup customization + * + * Note. The global resources are not yet initialized (for example global variables, peripherals, clocks) + * because this function is executed as the first instruction in the ResetHandler. + * The PDL is also not initialized to use the proper register offsets. + * The user of this function is responsible for initializing the PDL and resources before using them. + */ + .weak Cy_OnResetUser + .func Cy_OnResetUser, Cy_OnResetUser + .type Cy_OnResetUser, %function + +Cy_OnResetUser: + bx lr + .size Cy_OnResetUser, . - Cy_OnResetUser + .endfunc + + /* Reset handler */ + .weak Reset_Handler + .type Reset_Handler, %function + +Reset_Handler: + bl Cy_OnResetUser + cpsid i + +/* Firstly it copies data from read only memory to RAM. There are two schemes + * to copy. One can copy more than one sections. Another can only copy + * one section. The former scheme needs more instructions and read-only + * data to implement than the latter. + * Macro __STARTUP_COPY_MULTIPLE is used to choose between two schemes. */ + +#ifdef __STARTUP_COPY_MULTIPLE +/* Multiple sections scheme. + * + * Between symbol address __copy_table_start__ and __copy_table_end__, + * there are array of triplets, each of which specify: + * offset 0: LMA of start of a section to copy from + * offset 4: VMA of start of a section to copy to + * offset 8: size of the section to copy. Must be multiply of 4 + * + * All addresses must be aligned to 4 bytes boundary. + */ + ldr r4, =__copy_table_start__ + ldr r5, =__copy_table_end__ + +.L_loop0: + cmp r4, r5 + bge .L_loop0_done + ldr r1, [r4] + ldr r2, [r4, #4] + ldr r3, [r4, #8] + +.L_loop0_0: + subs r3, #4 + blt .L_loop0_0_done + ldr r0, [r1, r3] + str r0, [r2, r3] + b .L_loop0_0 + +.L_loop0_0_done: + adds r4, #12 + b .L_loop0 + +.L_loop0_done: +#else +/* Single section scheme. + * + * The ranges of copy from/to are specified by following symbols + * __etext: LMA of start of the section to copy from. Usually end of text + * __data_start__: VMA of start of the section to copy to + * __data_end__: VMA of end of the section to copy to + * + * All addresses must be aligned to 4 bytes boundary. + */ + ldr r1, =__etext + ldr r2, =__data_start__ + ldr r3, =__data_end__ + + subs r3, r2 + ble .L_loop1_done + +.L_loop1: + subs r3, #4 + ldr r0, [r1,r3] + str r0, [r2,r3] + bgt .L_loop1 + +.L_loop1_done: +#endif /*__STARTUP_COPY_MULTIPLE */ + +/* This part of work usually is done in C library startup code. Otherwise, + * define this macro to enable it in this startup. + * + * There are two schemes too. One can clear multiple BSS sections. Another + * can only clear one section. The former is more size expensive than the + * latter. + * + * Define macro __STARTUP_CLEAR_BSS_MULTIPLE to choose the former. + * Otherwise define macro __STARTUP_CLEAR_BSS to choose the later. + */ +#ifdef __STARTUP_CLEAR_BSS_MULTIPLE +/* Multiple sections scheme. + * + * Between symbol address __copy_table_start__ and __copy_table_end__, + * there are array of tuples specifying: + * offset 0: Start of a BSS section + * offset 4: Size of this BSS section. Must be multiply of 4 + */ + ldr r3, =__zero_table_start__ + ldr r4, =__zero_table_end__ + +.L_loop2: + cmp r3, r4 + bge .L_loop2_done + ldr r1, [r3] + ldr r2, [r3, #4] + movs r0, 0 + +.L_loop2_0: + subs r2, #4 + blt .L_loop2_0_done + str r0, [r1, r2] + b .L_loop2_0 +.L_loop2_0_done: + + adds r3, #8 + b .L_loop2 +.L_loop2_done: +#elif defined (__STARTUP_CLEAR_BSS) +/* Single BSS section scheme. + * + * The BSS section is specified by following symbols + * __bss_start__: start of the BSS section. + * __bss_end__: end of the BSS section. + * + * Both addresses must be aligned to 4 bytes boundary. + */ + ldr r1, =__bss_start__ + ldr r2, =__bss_end__ + + movs r0, 0 + + subs r2, r1 + ble .L_loop3_done + +.L_loop3: + subs r2, #4 + str r0, [r1, r2] + bgt .L_loop3 +.L_loop3_done: +#endif /* __STARTUP_CLEAR_BSS_MULTIPLE || __STARTUP_CLEAR_BSS */ + + /* Update Vector Table Offset Register. */ + ldr r0, =__ramVectors + ldr r1, =CY_CPU_VTOR_ADDR + str r0, [r1] + dsb 0xF + +#ifndef __NO_SYSTEM_INIT + bl SystemInit +#endif + + bl main + + /* Should never get here */ + b . + + .pool + .size Reset_Handler, . - Reset_Handler + + .align 1 + .thumb_func + .weak Default_Handler + .type Default_Handler, %function +Default_Handler: + b . + .size Default_Handler, . - Default_Handler + .weak Cy_SysLib_FaultHandler + .type Cy_SysLib_FaultHandler, %function + +Cy_SysLib_FaultHandler: + b . + .size Cy_SysLib_FaultHandler, . - Cy_SysLib_FaultHandler + .type Fault_Handler, %function + +Fault_Handler: + /* Storing LR content for Creator call stack trace */ + push {LR} + movs r0, #4 + mov r1, LR + tst r0, r1 + beq .L_MSP + mrs r0, PSP + b .L_API_call +.L_MSP: + mrs r0, MSP +.L_API_call: + /* Compensation of stack pointer address due to pushing 4 bytes of LR */ + adds r0, r0, #4 + bl Cy_SysLib_FaultHandler + b . + .size Fault_Handler, . - Fault_Handler + +.macro def_fault_Handler fault_handler_name + .weak \fault_handler_name + .set \fault_handler_name, Fault_Handler + .endm + +/* Macro to define default handlers. Default handler + * will be weak symbol and just dead loops. They can be + * overwritten by other handlers */ + .macro def_irq_handler handler_name + .weak \handler_name + .set \handler_name, Default_Handler + .endm + + def_irq_handler NMI_Handler + + def_fault_Handler HardFault_Handler + + def_irq_handler SVC_Handler + def_irq_handler PendSV_Handler + def_irq_handler SysTick_Handler + + def_irq_handler NvicMux0_IRQHandler /* CPU User Interrupt #0 */ + def_irq_handler NvicMux1_IRQHandler /* CPU User Interrupt #1 */ + def_irq_handler NvicMux2_IRQHandler /* CPU User Interrupt #2 */ + def_irq_handler NvicMux3_IRQHandler /* CPU User Interrupt #3 */ + def_irq_handler NvicMux4_IRQHandler /* CPU User Interrupt #4 */ + def_irq_handler NvicMux5_IRQHandler /* CPU User Interrupt #5 */ + def_irq_handler NvicMux6_IRQHandler /* CPU User Interrupt #6 */ + def_irq_handler NvicMux7_IRQHandler /* CPU User Interrupt #7 */ + def_irq_handler Internal0_IRQHandler /* Internal SW Interrupt #0 */ + def_irq_handler Internal1_IRQHandler /* Internal SW Interrupt #1 */ + def_irq_handler Internal2_IRQHandler /* Internal SW Interrupt #2 */ + def_irq_handler Internal3_IRQHandler /* Internal SW Interrupt #3 */ + def_irq_handler Internal4_IRQHandler /* Internal SW Interrupt #4 */ + def_irq_handler Internal5_IRQHandler /* Internal SW Interrupt #5 */ + def_irq_handler Internal6_IRQHandler /* Internal SW Interrupt #6 */ + def_irq_handler Internal7_IRQHandler /* Internal SW Interrupt #7 */ + + .end + + +/* [] END OF FILE */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/cy8c6xxa_cm4_dual.ld b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/cy8c6xxa_cm4_dual.ld new file mode 100644 index 0000000..ec70309 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/cy8c6xxa_cm4_dual.ld @@ -0,0 +1,436 @@ +/***************************************************************************//** +* \file cy8c6xxa_cm4_dual.ld +* \version 2.70 +* +* Linker file for the GNU C compiler. +* +* The main purpose of the linker script is to describe how the sections in the +* input files should be mapped into the output file, and to control the memory +* layout of the output file. +* +* \note The entry point location is fixed and starts at 0x10000000. The valid +* application image should be placed there. +* +* \note The linker files included with the PDL template projects must be generic +* and handle all common use cases. Your project may not use every section +* defined in the linker files. In that case you may see warnings during the +* build process. In your project, you can simply comment out or remove the +* relevant code in the linker file. +* +******************************************************************************** +* \copyright +* Copyright 2016-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +SEARCH_DIR(.) +GROUP(-lgcc -lc -lnosys) +ENTRY(Reset_Handler) + +/* The size of the stack section at the end of CM4 SRAM */ +STACK_SIZE = 0x1000; + +/* The size of the Cortex-M0+ application image at the start of FLASH */ +FLASH_CM0P_SIZE = 0x2000; + +/* Force symbol to be entered in the output file as an undefined symbol. Doing +* this may, for example, trigger linking of additional modules from standard +* libraries. You may list several symbols for each EXTERN, and you may use +* EXTERN multiple times. This command has the same effect as the -u command-line +* option. +*/ +EXTERN(Reset_Handler) + +/* The MEMORY section below describes the location and size of blocks of memory in the target. +* Use this section to specify the memory regions available for allocation. +*/ +MEMORY +{ + /* The ram and flash regions control RAM and flash memory allocation for the CM4 core. + * You can change the memory allocation by editing the 'ram' and 'flash' regions. + * Note that 2 KB of RAM (at the end of the SRAM) are reserved for system use. + * Using this memory region for other purposes will lead to unexpected behavior. + * Your changes must be aligned with the corresponding memory regions for CM0+ core in 'xx_cm0plus.ld', + * where 'xx' is the device group; for example, 'cy8c6xx7_cm0plus.ld'. + */ + ram (rwx) : ORIGIN = 0x08002000, LENGTH = 0xFD800 + flash (rx) : ORIGIN = 0x10000000, LENGTH = 0x200000 + + /* This is a 32K flash region used for EEPROM emulation. This region can also be used as the general purpose flash. + * You can assign sections to this memory region for only one of the cores. + * Note some middleware (e.g. BLE, Emulated EEPROM) can place their data into this memory region. + * Therefore, repurposing this memory region will prevent such middleware from operation. + */ + em_eeprom (rx) : ORIGIN = 0x14000000, LENGTH = 0x8000 /* 32 KB */ + + /* The following regions define device specific memory regions and must not be changed. */ + sflash_user_data (rx) : ORIGIN = 0x16000800, LENGTH = 0x800 /* Supervisory flash: User data */ + sflash_nar (rx) : ORIGIN = 0x16001A00, LENGTH = 0x200 /* Supervisory flash: Normal Access Restrictions (NAR) */ + sflash_public_key (rx) : ORIGIN = 0x16005A00, LENGTH = 0xC00 /* Supervisory flash: Public Key */ + sflash_toc_2 (rx) : ORIGIN = 0x16007C00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 */ + sflash_rtoc_2 (rx) : ORIGIN = 0x16007E00, LENGTH = 0x200 /* Supervisory flash: Table of Content # 2 Copy */ + xip (rx) : ORIGIN = 0x18000000, LENGTH = 0x8000000 /* 128 MB */ + efuse (r) : ORIGIN = 0x90700000, LENGTH = 0x100000 /* 1 MB */ +} + +/* Library configurations */ +GROUP(libgcc.a libc.a libm.a libnosys.a) + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + * __Vectors_End + * __Vectors_Size + */ + + +SECTIONS +{ + /* Cortex-M0+ application flash image area */ + .cy_m0p_image ORIGIN(flash) : + { + . = ALIGN(4); + __cy_m0p_code_start = . ; + KEEP(*(.cy_m0p_image)) + __cy_m0p_code_end = . ; + } > flash + + /* Check if .cy_m0p_image size exceeds FLASH_CM0P_SIZE */ + ASSERT(__cy_m0p_code_end <= ORIGIN(flash) + FLASH_CM0P_SIZE, "CM0+ flash image overflows with CM4, increase FLASH_CM0P_SIZE") + + /* Cortex-M4 application flash area */ + .text ORIGIN(flash) + FLASH_CM0P_SIZE : + { + . = ALIGN(4); + __Vectors = . ; + KEEP(*(.vectors)) + . = ALIGN(4); + __Vectors_End = .; + __Vectors_Size = __Vectors_End - __Vectors; + __end__ = .; + + . = ALIGN(4); + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + /* Read-only code (constants). */ + *(.rodata .rodata.* .constdata .constdata.* .conststring .conststring.*) + + KEEP(*(.eh_frame*)) + } > flash + + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > flash + + __exidx_start = .; + + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > flash + __exidx_end = .; + + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_psoc6_02_cm4.S */ + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + + /* Copy interrupt vectors from flash to RAM */ + LONG (__Vectors) /* From */ + LONG (__ram_vectors_start__) /* To */ + LONG (__Vectors_End - __Vectors) /* Size */ + + /* Copy data section to RAM */ + LONG (__etext) /* From */ + LONG (__data_start__) /* To */ + LONG (__data_end__ - __data_start__) /* Size */ + + __copy_table_end__ = .; + } > flash + + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_psoc6_02_cm4.S */ + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + __zero_table_end__ = .; + } > flash + + __etext = . ; + + + .ramVectors (NOLOAD) : ALIGN(8) + { + __ram_vectors_start__ = .; + KEEP(*(.ram_vectors)) + __ram_vectors_end__ = .; + } > ram + + + .data __ram_vectors_end__ : AT (__etext) + { + __data_start__ = .; + + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + + KEEP(*(.cy_ramfunc*)) + . = ALIGN(4); + + __data_end__ = .; + + } > ram + + + /* Place variables in the section that should not be initialized during the + * device startup. + */ + .noinit (NOLOAD) : ALIGN(8) + { + KEEP(*(.noinit)) + } > ram + + + /* The uninitialized global or static variables are placed in this section. + * + * The NOLOAD attribute tells linker that .bss section does not consume + * any space in the image. The NOLOAD attribute changes the .bss type to + * NOBITS, and that makes linker to A) not allocate section in memory, and + * A) put information to clear the section with all zeros during application + * loading. + * + * Without the NOLOAD attribute, the .bss section might get PROGBITS type. + * This makes linker to A) allocate zeroed section in memory, and B) copy + * this section to RAM during application loading. + */ + .bss (NOLOAD): + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > ram + + + .heap (NOLOAD): + { + __HeapBase = .; + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + . = ORIGIN(ram) + LENGTH(ram) - STACK_SIZE; + __HeapLimit = .; + } > ram + + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > ram + + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(ram) + LENGTH(ram); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") + + + /* Used for the digital signature of the secure application and the Bootloader SDK application. + * The size of the section depends on the required data size. */ + .cy_app_signature ORIGIN(flash) + LENGTH(flash) - 256 : + { + KEEP(*(.cy_app_signature)) + } > flash + + + /* Emulated EEPROM Flash area */ + .cy_em_eeprom : + { + KEEP(*(.cy_em_eeprom)) + } > em_eeprom + + + /* Supervisory Flash: User data */ + .cy_sflash_user_data : + { + KEEP(*(.cy_sflash_user_data)) + } > sflash_user_data + + + /* Supervisory Flash: Normal Access Restrictions (NAR) */ + .cy_sflash_nar : + { + KEEP(*(.cy_sflash_nar)) + } > sflash_nar + + + /* Supervisory Flash: Public Key */ + .cy_sflash_public_key : + { + KEEP(*(.cy_sflash_public_key)) + } > sflash_public_key + + + /* Supervisory Flash: Table of Content # 2 */ + .cy_toc_part2 : + { + KEEP(*(.cy_toc_part2)) + } > sflash_toc_2 + + + /* Supervisory Flash: Table of Content # 2 Copy */ + .cy_rtoc_part2 : + { + KEEP(*(.cy_rtoc_part2)) + } > sflash_rtoc_2 + + + /* Places the code in the Execute in Place (XIP) section. See the smif driver + * documentation for details. + */ + .cy_xip : + { + KEEP(*(.cy_xip)) + } > xip + + + /* eFuse */ + .cy_efuse : + { + KEEP(*(.cy_efuse)) + } > efuse + + + /* These sections are used for additional metadata (silicon revision, + * Silicon/JTAG ID, etc.) storage. + */ + .cymeta 0x90500000 : { KEEP(*(.cymeta)) } :NONE +} + + +/* The following symbols used by the cymcuelftool. */ +/* Flash */ +__cy_memory_0_start = 0x10000000; +__cy_memory_0_length = 0x00200000; +__cy_memory_0_row_size = 0x200; + +/* Emulated EEPROM Flash area */ +__cy_memory_1_start = 0x14000000; +__cy_memory_1_length = 0x8000; +__cy_memory_1_row_size = 0x200; + +/* Supervisory Flash */ +__cy_memory_2_start = 0x16000000; +__cy_memory_2_length = 0x8000; +__cy_memory_2_row_size = 0x200; + +/* XIP */ +__cy_memory_3_start = 0x18000000; +__cy_memory_3_length = 0x08000000; +__cy_memory_3_row_size = 0x200; + +/* eFuse */ +__cy_memory_4_start = 0x90700000; +__cy_memory_4_length = 0x100000; +__cy_memory_4_row_size = 1; + +/* EOF */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/startup_psoc6_02_cm4.S b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/startup_psoc6_02_cm4.S new file mode 100644 index 0000000..3eb9dab --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/PSOC_062_2M/CM4/GCC_ARM/startup_psoc6_02_cm4.S @@ -0,0 +1,697 @@ +/**************************************************************************//** + * @file startup_psoc6_02_cm4.S + * @brief CMSIS Core Device Startup File for + * ARMCM4 Device Series + * @version V5.00 + * @date 02. March 2016 + ******************************************************************************/ +/* + * Copyright (c) 2009-2016 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /* Address of the NMI handler */ + #define CY_NMI_HANLDER_ADDR 0x0000000D + + /* The CPU VTOR register */ + #define CY_CPU_VTOR_ADDR 0xE000ED08 + + /* Copy flash vectors and data section to RAM */ + #define __STARTUP_COPY_MULTIPLE + + /* Clear single BSS section */ + #define __STARTUP_CLEAR_BSS + + .syntax unified + .arch armv7-m + + .section .stack + .align 3 +#ifdef __STACK_SIZE + .equ Stack_Size, __STACK_SIZE +#else + .equ Stack_Size, 0x00001000 +#endif + .globl __StackTop + .globl __StackLimit +__StackLimit: + .space Stack_Size + .size __StackLimit, . - __StackLimit +__StackTop: + .size __StackTop, . - __StackTop + + .section .heap + .align 3 +#ifdef __HEAP_SIZE + .equ Heap_Size, __HEAP_SIZE +#else + .equ Heap_Size, 0x00000400 +#endif + .globl __HeapBase + .globl __HeapLimit +__HeapBase: + .if Heap_Size + .space Heap_Size + .endif + .size __HeapBase, . - __HeapBase +__HeapLimit: + .size __HeapLimit, . - __HeapLimit + + .section .vectors + .align 2 + .globl __Vectors +__Vectors: + .long __StackTop /* Top of Stack */ + .long Reset_Handler /* Reset Handler */ + .long CY_NMI_HANLDER_ADDR /* NMI Handler */ + .long HardFault_Handler /* Hard Fault Handler */ + .long MemManage_Handler /* MPU Fault Handler */ + .long BusFault_Handler /* Bus Fault Handler */ + .long UsageFault_Handler /* Usage Fault Handler */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long 0 /* Reserved */ + .long SVC_Handler /* SVCall Handler */ + .long DebugMon_Handler /* Debug Monitor Handler */ + .long 0 /* Reserved */ + .long PendSV_Handler /* PendSV Handler */ + .long SysTick_Handler /* SysTick Handler */ + + /* External interrupts Description */ + .long ioss_interrupts_gpio_0_IRQHandler /* GPIO Port Interrupt #0 */ + .long ioss_interrupts_gpio_1_IRQHandler /* GPIO Port Interrupt #1 */ + .long ioss_interrupts_gpio_2_IRQHandler /* GPIO Port Interrupt #2 */ + .long ioss_interrupts_gpio_3_IRQHandler /* GPIO Port Interrupt #3 */ + .long ioss_interrupts_gpio_4_IRQHandler /* GPIO Port Interrupt #4 */ + .long ioss_interrupts_gpio_5_IRQHandler /* GPIO Port Interrupt #5 */ + .long ioss_interrupts_gpio_6_IRQHandler /* GPIO Port Interrupt #6 */ + .long ioss_interrupts_gpio_7_IRQHandler /* GPIO Port Interrupt #7 */ + .long ioss_interrupts_gpio_8_IRQHandler /* GPIO Port Interrupt #8 */ + .long ioss_interrupts_gpio_9_IRQHandler /* GPIO Port Interrupt #9 */ + .long ioss_interrupts_gpio_10_IRQHandler /* GPIO Port Interrupt #10 */ + .long ioss_interrupts_gpio_11_IRQHandler /* GPIO Port Interrupt #11 */ + .long ioss_interrupts_gpio_12_IRQHandler /* GPIO Port Interrupt #12 */ + .long ioss_interrupts_gpio_13_IRQHandler /* GPIO Port Interrupt #13 */ + .long ioss_interrupts_gpio_14_IRQHandler /* GPIO Port Interrupt #14 */ + .long ioss_interrupt_gpio_IRQHandler /* GPIO All Ports */ + .long ioss_interrupt_vdd_IRQHandler /* GPIO Supply Detect Interrupt */ + .long lpcomp_interrupt_IRQHandler /* Low Power Comparator Interrupt */ + .long scb_8_interrupt_IRQHandler /* Serial Communication Block #8 (DeepSleep capable) */ + .long srss_interrupt_mcwdt_0_IRQHandler /* Multi Counter Watchdog Timer interrupt */ + .long srss_interrupt_mcwdt_1_IRQHandler /* Multi Counter Watchdog Timer interrupt */ + .long srss_interrupt_backup_IRQHandler /* Backup domain interrupt */ + .long srss_interrupt_IRQHandler /* Other combined Interrupts for SRSS (LVD, WDT, CLKCAL) */ + .long cpuss_interrupts_ipc_0_IRQHandler /* CPUSS Inter Process Communication Interrupt #0 */ + .long cpuss_interrupts_ipc_1_IRQHandler /* CPUSS Inter Process Communication Interrupt #1 */ + .long cpuss_interrupts_ipc_2_IRQHandler /* CPUSS Inter Process Communication Interrupt #2 */ + .long cpuss_interrupts_ipc_3_IRQHandler /* CPUSS Inter Process Communication Interrupt #3 */ + .long cpuss_interrupts_ipc_4_IRQHandler /* CPUSS Inter Process Communication Interrupt #4 */ + .long cpuss_interrupts_ipc_5_IRQHandler /* CPUSS Inter Process Communication Interrupt #5 */ + .long cpuss_interrupts_ipc_6_IRQHandler /* CPUSS Inter Process Communication Interrupt #6 */ + .long cpuss_interrupts_ipc_7_IRQHandler /* CPUSS Inter Process Communication Interrupt #7 */ + .long cpuss_interrupts_ipc_8_IRQHandler /* CPUSS Inter Process Communication Interrupt #8 */ + .long cpuss_interrupts_ipc_9_IRQHandler /* CPUSS Inter Process Communication Interrupt #9 */ + .long cpuss_interrupts_ipc_10_IRQHandler /* CPUSS Inter Process Communication Interrupt #10 */ + .long cpuss_interrupts_ipc_11_IRQHandler /* CPUSS Inter Process Communication Interrupt #11 */ + .long cpuss_interrupts_ipc_12_IRQHandler /* CPUSS Inter Process Communication Interrupt #12 */ + .long cpuss_interrupts_ipc_13_IRQHandler /* CPUSS Inter Process Communication Interrupt #13 */ + .long cpuss_interrupts_ipc_14_IRQHandler /* CPUSS Inter Process Communication Interrupt #14 */ + .long cpuss_interrupts_ipc_15_IRQHandler /* CPUSS Inter Process Communication Interrupt #15 */ + .long scb_0_interrupt_IRQHandler /* Serial Communication Block #0 */ + .long scb_1_interrupt_IRQHandler /* Serial Communication Block #1 */ + .long scb_2_interrupt_IRQHandler /* Serial Communication Block #2 */ + .long scb_3_interrupt_IRQHandler /* Serial Communication Block #3 */ + .long scb_4_interrupt_IRQHandler /* Serial Communication Block #4 */ + .long scb_5_interrupt_IRQHandler /* Serial Communication Block #5 */ + .long scb_6_interrupt_IRQHandler /* Serial Communication Block #6 */ + .long scb_7_interrupt_IRQHandler /* Serial Communication Block #7 */ + .long scb_9_interrupt_IRQHandler /* Serial Communication Block #9 */ + .long scb_10_interrupt_IRQHandler /* Serial Communication Block #10 */ + .long scb_11_interrupt_IRQHandler /* Serial Communication Block #11 */ + .long scb_12_interrupt_IRQHandler /* Serial Communication Block #12 */ + .long csd_interrupt_IRQHandler /* CSD (Capsense) interrupt */ + .long cpuss_interrupts_dmac_0_IRQHandler /* CPUSS DMAC, Channel #0 */ + .long cpuss_interrupts_dmac_1_IRQHandler /* CPUSS DMAC, Channel #1 */ + .long cpuss_interrupts_dmac_2_IRQHandler /* CPUSS DMAC, Channel #2 */ + .long cpuss_interrupts_dmac_3_IRQHandler /* CPUSS DMAC, Channel #3 */ + .long cpuss_interrupts_dw0_0_IRQHandler /* CPUSS DataWire #0, Channel #0 */ + .long cpuss_interrupts_dw0_1_IRQHandler /* CPUSS DataWire #0, Channel #1 */ + .long cpuss_interrupts_dw0_2_IRQHandler /* CPUSS DataWire #0, Channel #2 */ + .long cpuss_interrupts_dw0_3_IRQHandler /* CPUSS DataWire #0, Channel #3 */ + .long cpuss_interrupts_dw0_4_IRQHandler /* CPUSS DataWire #0, Channel #4 */ + .long cpuss_interrupts_dw0_5_IRQHandler /* CPUSS DataWire #0, Channel #5 */ + .long cpuss_interrupts_dw0_6_IRQHandler /* CPUSS DataWire #0, Channel #6 */ + .long cpuss_interrupts_dw0_7_IRQHandler /* CPUSS DataWire #0, Channel #7 */ + .long cpuss_interrupts_dw0_8_IRQHandler /* CPUSS DataWire #0, Channel #8 */ + .long cpuss_interrupts_dw0_9_IRQHandler /* CPUSS DataWire #0, Channel #9 */ + .long cpuss_interrupts_dw0_10_IRQHandler /* CPUSS DataWire #0, Channel #10 */ + .long cpuss_interrupts_dw0_11_IRQHandler /* CPUSS DataWire #0, Channel #11 */ + .long cpuss_interrupts_dw0_12_IRQHandler /* CPUSS DataWire #0, Channel #12 */ + .long cpuss_interrupts_dw0_13_IRQHandler /* CPUSS DataWire #0, Channel #13 */ + .long cpuss_interrupts_dw0_14_IRQHandler /* CPUSS DataWire #0, Channel #14 */ + .long cpuss_interrupts_dw0_15_IRQHandler /* CPUSS DataWire #0, Channel #15 */ + .long cpuss_interrupts_dw0_16_IRQHandler /* CPUSS DataWire #0, Channel #16 */ + .long cpuss_interrupts_dw0_17_IRQHandler /* CPUSS DataWire #0, Channel #17 */ + .long cpuss_interrupts_dw0_18_IRQHandler /* CPUSS DataWire #0, Channel #18 */ + .long cpuss_interrupts_dw0_19_IRQHandler /* CPUSS DataWire #0, Channel #19 */ + .long cpuss_interrupts_dw0_20_IRQHandler /* CPUSS DataWire #0, Channel #20 */ + .long cpuss_interrupts_dw0_21_IRQHandler /* CPUSS DataWire #0, Channel #21 */ + .long cpuss_interrupts_dw0_22_IRQHandler /* CPUSS DataWire #0, Channel #22 */ + .long cpuss_interrupts_dw0_23_IRQHandler /* CPUSS DataWire #0, Channel #23 */ + .long cpuss_interrupts_dw0_24_IRQHandler /* CPUSS DataWire #0, Channel #24 */ + .long cpuss_interrupts_dw0_25_IRQHandler /* CPUSS DataWire #0, Channel #25 */ + .long cpuss_interrupts_dw0_26_IRQHandler /* CPUSS DataWire #0, Channel #26 */ + .long cpuss_interrupts_dw0_27_IRQHandler /* CPUSS DataWire #0, Channel #27 */ + .long cpuss_interrupts_dw0_28_IRQHandler /* CPUSS DataWire #0, Channel #28 */ + .long cpuss_interrupts_dw1_0_IRQHandler /* CPUSS DataWire #1, Channel #0 */ + .long cpuss_interrupts_dw1_1_IRQHandler /* CPUSS DataWire #1, Channel #1 */ + .long cpuss_interrupts_dw1_2_IRQHandler /* CPUSS DataWire #1, Channel #2 */ + .long cpuss_interrupts_dw1_3_IRQHandler /* CPUSS DataWire #1, Channel #3 */ + .long cpuss_interrupts_dw1_4_IRQHandler /* CPUSS DataWire #1, Channel #4 */ + .long cpuss_interrupts_dw1_5_IRQHandler /* CPUSS DataWire #1, Channel #5 */ + .long cpuss_interrupts_dw1_6_IRQHandler /* CPUSS DataWire #1, Channel #6 */ + .long cpuss_interrupts_dw1_7_IRQHandler /* CPUSS DataWire #1, Channel #7 */ + .long cpuss_interrupts_dw1_8_IRQHandler /* CPUSS DataWire #1, Channel #8 */ + .long cpuss_interrupts_dw1_9_IRQHandler /* CPUSS DataWire #1, Channel #9 */ + .long cpuss_interrupts_dw1_10_IRQHandler /* CPUSS DataWire #1, Channel #10 */ + .long cpuss_interrupts_dw1_11_IRQHandler /* CPUSS DataWire #1, Channel #11 */ + .long cpuss_interrupts_dw1_12_IRQHandler /* CPUSS DataWire #1, Channel #12 */ + .long cpuss_interrupts_dw1_13_IRQHandler /* CPUSS DataWire #1, Channel #13 */ + .long cpuss_interrupts_dw1_14_IRQHandler /* CPUSS DataWire #1, Channel #14 */ + .long cpuss_interrupts_dw1_15_IRQHandler /* CPUSS DataWire #1, Channel #15 */ + .long cpuss_interrupts_dw1_16_IRQHandler /* CPUSS DataWire #1, Channel #16 */ + .long cpuss_interrupts_dw1_17_IRQHandler /* CPUSS DataWire #1, Channel #17 */ + .long cpuss_interrupts_dw1_18_IRQHandler /* CPUSS DataWire #1, Channel #18 */ + .long cpuss_interrupts_dw1_19_IRQHandler /* CPUSS DataWire #1, Channel #19 */ + .long cpuss_interrupts_dw1_20_IRQHandler /* CPUSS DataWire #1, Channel #20 */ + .long cpuss_interrupts_dw1_21_IRQHandler /* CPUSS DataWire #1, Channel #21 */ + .long cpuss_interrupts_dw1_22_IRQHandler /* CPUSS DataWire #1, Channel #22 */ + .long cpuss_interrupts_dw1_23_IRQHandler /* CPUSS DataWire #1, Channel #23 */ + .long cpuss_interrupts_dw1_24_IRQHandler /* CPUSS DataWire #1, Channel #24 */ + .long cpuss_interrupts_dw1_25_IRQHandler /* CPUSS DataWire #1, Channel #25 */ + .long cpuss_interrupts_dw1_26_IRQHandler /* CPUSS DataWire #1, Channel #26 */ + .long cpuss_interrupts_dw1_27_IRQHandler /* CPUSS DataWire #1, Channel #27 */ + .long cpuss_interrupts_dw1_28_IRQHandler /* CPUSS DataWire #1, Channel #28 */ + .long cpuss_interrupts_fault_0_IRQHandler /* CPUSS Fault Structure Interrupt #0 */ + .long cpuss_interrupts_fault_1_IRQHandler /* CPUSS Fault Structure Interrupt #1 */ + .long cpuss_interrupt_crypto_IRQHandler /* CRYPTO Accelerator Interrupt */ + .long cpuss_interrupt_fm_IRQHandler /* FLASH Macro Interrupt */ + .long cpuss_interrupts_cm4_fp_IRQHandler /* Floating Point operation fault */ + .long cpuss_interrupts_cm0_cti_0_IRQHandler /* CM0+ CTI #0 */ + .long cpuss_interrupts_cm0_cti_1_IRQHandler /* CM0+ CTI #1 */ + .long cpuss_interrupts_cm4_cti_0_IRQHandler /* CM4 CTI #0 */ + .long cpuss_interrupts_cm4_cti_1_IRQHandler /* CM4 CTI #1 */ + .long tcpwm_0_interrupts_0_IRQHandler /* TCPWM #0, Counter #0 */ + .long tcpwm_0_interrupts_1_IRQHandler /* TCPWM #0, Counter #1 */ + .long tcpwm_0_interrupts_2_IRQHandler /* TCPWM #0, Counter #2 */ + .long tcpwm_0_interrupts_3_IRQHandler /* TCPWM #0, Counter #3 */ + .long tcpwm_0_interrupts_4_IRQHandler /* TCPWM #0, Counter #4 */ + .long tcpwm_0_interrupts_5_IRQHandler /* TCPWM #0, Counter #5 */ + .long tcpwm_0_interrupts_6_IRQHandler /* TCPWM #0, Counter #6 */ + .long tcpwm_0_interrupts_7_IRQHandler /* TCPWM #0, Counter #7 */ + .long tcpwm_1_interrupts_0_IRQHandler /* TCPWM #1, Counter #0 */ + .long tcpwm_1_interrupts_1_IRQHandler /* TCPWM #1, Counter #1 */ + .long tcpwm_1_interrupts_2_IRQHandler /* TCPWM #1, Counter #2 */ + .long tcpwm_1_interrupts_3_IRQHandler /* TCPWM #1, Counter #3 */ + .long tcpwm_1_interrupts_4_IRQHandler /* TCPWM #1, Counter #4 */ + .long tcpwm_1_interrupts_5_IRQHandler /* TCPWM #1, Counter #5 */ + .long tcpwm_1_interrupts_6_IRQHandler /* TCPWM #1, Counter #6 */ + .long tcpwm_1_interrupts_7_IRQHandler /* TCPWM #1, Counter #7 */ + .long tcpwm_1_interrupts_8_IRQHandler /* TCPWM #1, Counter #8 */ + .long tcpwm_1_interrupts_9_IRQHandler /* TCPWM #1, Counter #9 */ + .long tcpwm_1_interrupts_10_IRQHandler /* TCPWM #1, Counter #10 */ + .long tcpwm_1_interrupts_11_IRQHandler /* TCPWM #1, Counter #11 */ + .long tcpwm_1_interrupts_12_IRQHandler /* TCPWM #1, Counter #12 */ + .long tcpwm_1_interrupts_13_IRQHandler /* TCPWM #1, Counter #13 */ + .long tcpwm_1_interrupts_14_IRQHandler /* TCPWM #1, Counter #14 */ + .long tcpwm_1_interrupts_15_IRQHandler /* TCPWM #1, Counter #15 */ + .long tcpwm_1_interrupts_16_IRQHandler /* TCPWM #1, Counter #16 */ + .long tcpwm_1_interrupts_17_IRQHandler /* TCPWM #1, Counter #17 */ + .long tcpwm_1_interrupts_18_IRQHandler /* TCPWM #1, Counter #18 */ + .long tcpwm_1_interrupts_19_IRQHandler /* TCPWM #1, Counter #19 */ + .long tcpwm_1_interrupts_20_IRQHandler /* TCPWM #1, Counter #20 */ + .long tcpwm_1_interrupts_21_IRQHandler /* TCPWM #1, Counter #21 */ + .long tcpwm_1_interrupts_22_IRQHandler /* TCPWM #1, Counter #22 */ + .long tcpwm_1_interrupts_23_IRQHandler /* TCPWM #1, Counter #23 */ + .long pass_interrupt_sar_IRQHandler /* SAR ADC interrupt */ + .long audioss_0_interrupt_i2s_IRQHandler /* I2S0 Audio interrupt */ + .long audioss_0_interrupt_pdm_IRQHandler /* PDM0/PCM0 Audio interrupt */ + .long audioss_1_interrupt_i2s_IRQHandler /* I2S1 Audio interrupt */ + .long profile_interrupt_IRQHandler /* Energy Profiler interrupt */ + .long smif_interrupt_IRQHandler /* Serial Memory Interface interrupt */ + .long usb_interrupt_hi_IRQHandler /* USB Interrupt */ + .long usb_interrupt_med_IRQHandler /* USB Interrupt */ + .long usb_interrupt_lo_IRQHandler /* USB Interrupt */ + .long sdhc_0_interrupt_wakeup_IRQHandler /* SDIO wakeup interrupt for mxsdhc */ + .long sdhc_0_interrupt_general_IRQHandler /* Consolidated interrupt for mxsdhc for everything else */ + .long sdhc_1_interrupt_wakeup_IRQHandler /* EEMC wakeup interrupt for mxsdhc, not used */ + .long sdhc_1_interrupt_general_IRQHandler /* Consolidated interrupt for mxsdhc for everything else */ + + + .size __Vectors, . - __Vectors + .equ __VectorsSize, . - __Vectors + + .section .ram_vectors + .align 2 + .globl __ramVectors +__ramVectors: + .space __VectorsSize + .size __ramVectors, . - __ramVectors + + + .text + .thumb + .thumb_func + .align 2 + + /* + * Device startup customization + * + * Note. The global resources are not yet initialized (for example global variables, peripherals, clocks) + * because this function is executed as the first instruction in the ResetHandler. + * The PDL is also not initialized to use the proper register offsets. + * The user of this function is responsible for initializing the PDL and resources before using them. + */ + .weak Cy_OnResetUser + .func Cy_OnResetUser, Cy_OnResetUser + .type Cy_OnResetUser, %function + +Cy_OnResetUser: + bx lr + .size Cy_OnResetUser, . - Cy_OnResetUser + .endfunc + + /* OS-specific low-level initialization */ + .weak cy_toolchain_init + .func cy_toolchain_init, cy_toolchain_init + .type cy_toolchain_init, %function + +cy_toolchain_init: + bx lr + .size cy_toolchain_init, . - cy_toolchain_init + .endfunc + + /* Reset handler */ + .weak Reset_Handler + .type Reset_Handler, %function + +Reset_Handler: + bl Cy_OnResetUser + cpsid i + +/* Firstly it copies data from read only memory to RAM. There are two schemes + * to copy. One can copy more than one sections. Another can only copy + * one section. The former scheme needs more instructions and read-only + * data to implement than the latter. + * Macro __STARTUP_COPY_MULTIPLE is used to choose between two schemes. */ + +#ifdef __STARTUP_COPY_MULTIPLE +/* Multiple sections scheme. + * + * Between symbol address __copy_table_start__ and __copy_table_end__, + * there are array of triplets, each of which specify: + * offset 0: LMA of start of a section to copy from + * offset 4: VMA of start of a section to copy to + * offset 8: size of the section to copy. Must be multiply of 4 + * + * All addresses must be aligned to 4 bytes boundary. + */ + ldr r4, =__copy_table_start__ + ldr r5, =__copy_table_end__ + +.L_loop0: + cmp r4, r5 + bge .L_loop0_done + ldr r1, [r4] + ldr r2, [r4, #4] + ldr r3, [r4, #8] + +.L_loop0_0: + subs r3, #4 + ittt ge + ldrge r0, [r1, r3] + strge r0, [r2, r3] + bge .L_loop0_0 + + adds r4, #12 + b .L_loop0 + +.L_loop0_done: +#else +/* Single section scheme. + * + * The ranges of copy from/to are specified by following symbols + * __etext: LMA of start of the section to copy from. Usually end of text + * __data_start__: VMA of start of the section to copy to + * __data_end__: VMA of end of the section to copy to + * + * All addresses must be aligned to 4 bytes boundary. + */ + ldr r1, =__etext + ldr r2, =__data_start__ + ldr r3, =__data_end__ + +.L_loop1: + cmp r2, r3 + ittt lt + ldrlt r0, [r1], #4 + strlt r0, [r2], #4 + blt .L_loop1 +#endif /*__STARTUP_COPY_MULTIPLE */ + +/* This part of work usually is done in C library startup code. Otherwise, + * define this macro to enable it in this startup. + * + * There are two schemes too. One can clear multiple BSS sections. Another + * can only clear one section. The former is more size expensive than the + * latter. + * + * Define macro __STARTUP_CLEAR_BSS_MULTIPLE to choose the former. + * Otherwise define macro __STARTUP_CLEAR_BSS to choose the later. + */ +#ifdef __STARTUP_CLEAR_BSS_MULTIPLE +/* Multiple sections scheme. + * + * Between symbol address __copy_table_start__ and __copy_table_end__, + * there are array of tuples specifying: + * offset 0: Start of a BSS section + * offset 4: Size of this BSS section. Must be multiply of 4 + */ + ldr r3, =__zero_table_start__ + ldr r4, =__zero_table_end__ + +.L_loop2: + cmp r3, r4 + bge .L_loop2_done + ldr r1, [r3] + ldr r2, [r3, #4] + movs r0, 0 + +.L_loop2_0: + subs r2, #4 + itt ge + strge r0, [r1, r2] + bge .L_loop2_0 + + adds r3, #8 + b .L_loop2 +.L_loop2_done: +#elif defined (__STARTUP_CLEAR_BSS) +/* Single BSS section scheme. + * + * The BSS section is specified by following symbols + * __bss_start__: start of the BSS section. + * __bss_end__: end of the BSS section. + * + * Both addresses must be aligned to 4 bytes boundary. + */ + ldr r1, =__bss_start__ + ldr r2, =__bss_end__ + + movs r0, 0 +.L_loop3: + cmp r1, r2 + itt lt + strlt r0, [r1], #4 + blt .L_loop3 +#endif /* __STARTUP_CLEAR_BSS_MULTIPLE || __STARTUP_CLEAR_BSS */ + + /* Update Vector Table Offset Register. */ + ldr r0, =__ramVectors + ldr r1, =CY_CPU_VTOR_ADDR + str r0, [r1] + dsb 0xF + + /* Enable the FPU if used */ + bl Cy_SystemInitFpuEnable + +#ifndef __NO_SYSTEM_INIT + bl SystemInit +#endif + + /* OS-specific low-level initialization */ + bl cy_toolchain_init + + /* Call C/C++ static constructors */ + bl __libc_init_array + + /* Execute main application */ + bl main + + /* Call C/C++ static destructors */ + bl __libc_fini_array + + /* Should never get here */ + b . + + .pool + .size Reset_Handler, . - Reset_Handler + + .align 1 + .thumb_func + .weak Default_Handler + .type Default_Handler, %function + +Default_Handler: + b . + .size Default_Handler, . - Default_Handler + + + .weak Cy_SysLib_FaultHandler + .type Cy_SysLib_FaultHandler, %function + +Cy_SysLib_FaultHandler: + b . + .size Cy_SysLib_FaultHandler, . - Cy_SysLib_FaultHandler + .type Fault_Handler, %function + +Fault_Handler: + /* Storing LR content for Creator call stack trace */ + push {LR} + movs r0, #4 + mov r1, LR + tst r0, r1 + beq .L_MSP + mrs r0, PSP + b .L_API_call +.L_MSP: + mrs r0, MSP +.L_API_call: + /* Compensation of stack pointer address due to pushing 4 bytes of LR */ + adds r0, r0, #4 + bl Cy_SysLib_FaultHandler + b . + .size Fault_Handler, . - Fault_Handler + +.macro def_fault_Handler fault_handler_name + .weak \fault_handler_name + .set \fault_handler_name, Fault_Handler + .endm + +/* Macro to define default handlers. Default handler + * will be weak symbol and just dead loops. They can be + * overwritten by other handlers */ + .macro def_irq_handler handler_name + .weak \handler_name + .set \handler_name, Default_Handler + .endm + + def_irq_handler NMI_Handler + + def_fault_Handler HardFault_Handler + def_fault_Handler MemManage_Handler + def_fault_Handler BusFault_Handler + def_fault_Handler UsageFault_Handler + + def_irq_handler SVC_Handler + def_irq_handler DebugMon_Handler + def_irq_handler PendSV_Handler + def_irq_handler SysTick_Handler + + def_irq_handler ioss_interrupts_gpio_0_IRQHandler /* GPIO Port Interrupt #0 */ + def_irq_handler ioss_interrupts_gpio_1_IRQHandler /* GPIO Port Interrupt #1 */ + def_irq_handler ioss_interrupts_gpio_2_IRQHandler /* GPIO Port Interrupt #2 */ + def_irq_handler ioss_interrupts_gpio_3_IRQHandler /* GPIO Port Interrupt #3 */ + def_irq_handler ioss_interrupts_gpio_4_IRQHandler /* GPIO Port Interrupt #4 */ + def_irq_handler ioss_interrupts_gpio_5_IRQHandler /* GPIO Port Interrupt #5 */ + def_irq_handler ioss_interrupts_gpio_6_IRQHandler /* GPIO Port Interrupt #6 */ + def_irq_handler ioss_interrupts_gpio_7_IRQHandler /* GPIO Port Interrupt #7 */ + def_irq_handler ioss_interrupts_gpio_8_IRQHandler /* GPIO Port Interrupt #8 */ + def_irq_handler ioss_interrupts_gpio_9_IRQHandler /* GPIO Port Interrupt #9 */ + def_irq_handler ioss_interrupts_gpio_10_IRQHandler /* GPIO Port Interrupt #10 */ + def_irq_handler ioss_interrupts_gpio_11_IRQHandler /* GPIO Port Interrupt #11 */ + def_irq_handler ioss_interrupts_gpio_12_IRQHandler /* GPIO Port Interrupt #12 */ + def_irq_handler ioss_interrupts_gpio_13_IRQHandler /* GPIO Port Interrupt #13 */ + def_irq_handler ioss_interrupts_gpio_14_IRQHandler /* GPIO Port Interrupt #14 */ + def_irq_handler ioss_interrupt_gpio_IRQHandler /* GPIO All Ports */ + def_irq_handler ioss_interrupt_vdd_IRQHandler /* GPIO Supply Detect Interrupt */ + def_irq_handler lpcomp_interrupt_IRQHandler /* Low Power Comparator Interrupt */ + def_irq_handler scb_8_interrupt_IRQHandler /* Serial Communication Block #8 (DeepSleep capable) */ + def_irq_handler srss_interrupt_mcwdt_0_IRQHandler /* Multi Counter Watchdog Timer interrupt */ + def_irq_handler srss_interrupt_mcwdt_1_IRQHandler /* Multi Counter Watchdog Timer interrupt */ + def_irq_handler srss_interrupt_backup_IRQHandler /* Backup domain interrupt */ + def_irq_handler srss_interrupt_IRQHandler /* Other combined Interrupts for SRSS (LVD, WDT, CLKCAL) */ + def_irq_handler cpuss_interrupts_ipc_0_IRQHandler /* CPUSS Inter Process Communication Interrupt #0 */ + def_irq_handler cpuss_interrupts_ipc_1_IRQHandler /* CPUSS Inter Process Communication Interrupt #1 */ + def_irq_handler cpuss_interrupts_ipc_2_IRQHandler /* CPUSS Inter Process Communication Interrupt #2 */ + def_irq_handler cpuss_interrupts_ipc_3_IRQHandler /* CPUSS Inter Process Communication Interrupt #3 */ + def_irq_handler cpuss_interrupts_ipc_4_IRQHandler /* CPUSS Inter Process Communication Interrupt #4 */ + def_irq_handler cpuss_interrupts_ipc_5_IRQHandler /* CPUSS Inter Process Communication Interrupt #5 */ + def_irq_handler cpuss_interrupts_ipc_6_IRQHandler /* CPUSS Inter Process Communication Interrupt #6 */ + def_irq_handler cpuss_interrupts_ipc_7_IRQHandler /* CPUSS Inter Process Communication Interrupt #7 */ + def_irq_handler cpuss_interrupts_ipc_8_IRQHandler /* CPUSS Inter Process Communication Interrupt #8 */ + def_irq_handler cpuss_interrupts_ipc_9_IRQHandler /* CPUSS Inter Process Communication Interrupt #9 */ + def_irq_handler cpuss_interrupts_ipc_10_IRQHandler /* CPUSS Inter Process Communication Interrupt #10 */ + def_irq_handler cpuss_interrupts_ipc_11_IRQHandler /* CPUSS Inter Process Communication Interrupt #11 */ + def_irq_handler cpuss_interrupts_ipc_12_IRQHandler /* CPUSS Inter Process Communication Interrupt #12 */ + def_irq_handler cpuss_interrupts_ipc_13_IRQHandler /* CPUSS Inter Process Communication Interrupt #13 */ + def_irq_handler cpuss_interrupts_ipc_14_IRQHandler /* CPUSS Inter Process Communication Interrupt #14 */ + def_irq_handler cpuss_interrupts_ipc_15_IRQHandler /* CPUSS Inter Process Communication Interrupt #15 */ + def_irq_handler scb_0_interrupt_IRQHandler /* Serial Communication Block #0 */ + def_irq_handler scb_1_interrupt_IRQHandler /* Serial Communication Block #1 */ + def_irq_handler scb_2_interrupt_IRQHandler /* Serial Communication Block #2 */ + def_irq_handler scb_3_interrupt_IRQHandler /* Serial Communication Block #3 */ + def_irq_handler scb_4_interrupt_IRQHandler /* Serial Communication Block #4 */ + def_irq_handler scb_5_interrupt_IRQHandler /* Serial Communication Block #5 */ + def_irq_handler scb_6_interrupt_IRQHandler /* Serial Communication Block #6 */ + def_irq_handler scb_7_interrupt_IRQHandler /* Serial Communication Block #7 */ + def_irq_handler scb_9_interrupt_IRQHandler /* Serial Communication Block #9 */ + def_irq_handler scb_10_interrupt_IRQHandler /* Serial Communication Block #10 */ + def_irq_handler scb_11_interrupt_IRQHandler /* Serial Communication Block #11 */ + def_irq_handler scb_12_interrupt_IRQHandler /* Serial Communication Block #12 */ + def_irq_handler csd_interrupt_IRQHandler /* CSD (Capsense) interrupt */ + def_irq_handler cpuss_interrupts_dmac_0_IRQHandler /* CPUSS DMAC, Channel #0 */ + def_irq_handler cpuss_interrupts_dmac_1_IRQHandler /* CPUSS DMAC, Channel #1 */ + def_irq_handler cpuss_interrupts_dmac_2_IRQHandler /* CPUSS DMAC, Channel #2 */ + def_irq_handler cpuss_interrupts_dmac_3_IRQHandler /* CPUSS DMAC, Channel #3 */ + def_irq_handler cpuss_interrupts_dw0_0_IRQHandler /* CPUSS DataWire #0, Channel #0 */ + def_irq_handler cpuss_interrupts_dw0_1_IRQHandler /* CPUSS DataWire #0, Channel #1 */ + def_irq_handler cpuss_interrupts_dw0_2_IRQHandler /* CPUSS DataWire #0, Channel #2 */ + def_irq_handler cpuss_interrupts_dw0_3_IRQHandler /* CPUSS DataWire #0, Channel #3 */ + def_irq_handler cpuss_interrupts_dw0_4_IRQHandler /* CPUSS DataWire #0, Channel #4 */ + def_irq_handler cpuss_interrupts_dw0_5_IRQHandler /* CPUSS DataWire #0, Channel #5 */ + def_irq_handler cpuss_interrupts_dw0_6_IRQHandler /* CPUSS DataWire #0, Channel #6 */ + def_irq_handler cpuss_interrupts_dw0_7_IRQHandler /* CPUSS DataWire #0, Channel #7 */ + def_irq_handler cpuss_interrupts_dw0_8_IRQHandler /* CPUSS DataWire #0, Channel #8 */ + def_irq_handler cpuss_interrupts_dw0_9_IRQHandler /* CPUSS DataWire #0, Channel #9 */ + def_irq_handler cpuss_interrupts_dw0_10_IRQHandler /* CPUSS DataWire #0, Channel #10 */ + def_irq_handler cpuss_interrupts_dw0_11_IRQHandler /* CPUSS DataWire #0, Channel #11 */ + def_irq_handler cpuss_interrupts_dw0_12_IRQHandler /* CPUSS DataWire #0, Channel #12 */ + def_irq_handler cpuss_interrupts_dw0_13_IRQHandler /* CPUSS DataWire #0, Channel #13 */ + def_irq_handler cpuss_interrupts_dw0_14_IRQHandler /* CPUSS DataWire #0, Channel #14 */ + def_irq_handler cpuss_interrupts_dw0_15_IRQHandler /* CPUSS DataWire #0, Channel #15 */ + def_irq_handler cpuss_interrupts_dw0_16_IRQHandler /* CPUSS DataWire #0, Channel #16 */ + def_irq_handler cpuss_interrupts_dw0_17_IRQHandler /* CPUSS DataWire #0, Channel #17 */ + def_irq_handler cpuss_interrupts_dw0_18_IRQHandler /* CPUSS DataWire #0, Channel #18 */ + def_irq_handler cpuss_interrupts_dw0_19_IRQHandler /* CPUSS DataWire #0, Channel #19 */ + def_irq_handler cpuss_interrupts_dw0_20_IRQHandler /* CPUSS DataWire #0, Channel #20 */ + def_irq_handler cpuss_interrupts_dw0_21_IRQHandler /* CPUSS DataWire #0, Channel #21 */ + def_irq_handler cpuss_interrupts_dw0_22_IRQHandler /* CPUSS DataWire #0, Channel #22 */ + def_irq_handler cpuss_interrupts_dw0_23_IRQHandler /* CPUSS DataWire #0, Channel #23 */ + def_irq_handler cpuss_interrupts_dw0_24_IRQHandler /* CPUSS DataWire #0, Channel #24 */ + def_irq_handler cpuss_interrupts_dw0_25_IRQHandler /* CPUSS DataWire #0, Channel #25 */ + def_irq_handler cpuss_interrupts_dw0_26_IRQHandler /* CPUSS DataWire #0, Channel #26 */ + def_irq_handler cpuss_interrupts_dw0_27_IRQHandler /* CPUSS DataWire #0, Channel #27 */ + def_irq_handler cpuss_interrupts_dw0_28_IRQHandler /* CPUSS DataWire #0, Channel #28 */ + def_irq_handler cpuss_interrupts_dw1_0_IRQHandler /* CPUSS DataWire #1, Channel #0 */ + def_irq_handler cpuss_interrupts_dw1_1_IRQHandler /* CPUSS DataWire #1, Channel #1 */ + def_irq_handler cpuss_interrupts_dw1_2_IRQHandler /* CPUSS DataWire #1, Channel #2 */ + def_irq_handler cpuss_interrupts_dw1_3_IRQHandler /* CPUSS DataWire #1, Channel #3 */ + def_irq_handler cpuss_interrupts_dw1_4_IRQHandler /* CPUSS DataWire #1, Channel #4 */ + def_irq_handler cpuss_interrupts_dw1_5_IRQHandler /* CPUSS DataWire #1, Channel #5 */ + def_irq_handler cpuss_interrupts_dw1_6_IRQHandler /* CPUSS DataWire #1, Channel #6 */ + def_irq_handler cpuss_interrupts_dw1_7_IRQHandler /* CPUSS DataWire #1, Channel #7 */ + def_irq_handler cpuss_interrupts_dw1_8_IRQHandler /* CPUSS DataWire #1, Channel #8 */ + def_irq_handler cpuss_interrupts_dw1_9_IRQHandler /* CPUSS DataWire #1, Channel #9 */ + def_irq_handler cpuss_interrupts_dw1_10_IRQHandler /* CPUSS DataWire #1, Channel #10 */ + def_irq_handler cpuss_interrupts_dw1_11_IRQHandler /* CPUSS DataWire #1, Channel #11 */ + def_irq_handler cpuss_interrupts_dw1_12_IRQHandler /* CPUSS DataWire #1, Channel #12 */ + def_irq_handler cpuss_interrupts_dw1_13_IRQHandler /* CPUSS DataWire #1, Channel #13 */ + def_irq_handler cpuss_interrupts_dw1_14_IRQHandler /* CPUSS DataWire #1, Channel #14 */ + def_irq_handler cpuss_interrupts_dw1_15_IRQHandler /* CPUSS DataWire #1, Channel #15 */ + def_irq_handler cpuss_interrupts_dw1_16_IRQHandler /* CPUSS DataWire #1, Channel #16 */ + def_irq_handler cpuss_interrupts_dw1_17_IRQHandler /* CPUSS DataWire #1, Channel #17 */ + def_irq_handler cpuss_interrupts_dw1_18_IRQHandler /* CPUSS DataWire #1, Channel #18 */ + def_irq_handler cpuss_interrupts_dw1_19_IRQHandler /* CPUSS DataWire #1, Channel #19 */ + def_irq_handler cpuss_interrupts_dw1_20_IRQHandler /* CPUSS DataWire #1, Channel #20 */ + def_irq_handler cpuss_interrupts_dw1_21_IRQHandler /* CPUSS DataWire #1, Channel #21 */ + def_irq_handler cpuss_interrupts_dw1_22_IRQHandler /* CPUSS DataWire #1, Channel #22 */ + def_irq_handler cpuss_interrupts_dw1_23_IRQHandler /* CPUSS DataWire #1, Channel #23 */ + def_irq_handler cpuss_interrupts_dw1_24_IRQHandler /* CPUSS DataWire #1, Channel #24 */ + def_irq_handler cpuss_interrupts_dw1_25_IRQHandler /* CPUSS DataWire #1, Channel #25 */ + def_irq_handler cpuss_interrupts_dw1_26_IRQHandler /* CPUSS DataWire #1, Channel #26 */ + def_irq_handler cpuss_interrupts_dw1_27_IRQHandler /* CPUSS DataWire #1, Channel #27 */ + def_irq_handler cpuss_interrupts_dw1_28_IRQHandler /* CPUSS DataWire #1, Channel #28 */ + def_irq_handler cpuss_interrupts_fault_0_IRQHandler /* CPUSS Fault Structure Interrupt #0 */ + def_irq_handler cpuss_interrupts_fault_1_IRQHandler /* CPUSS Fault Structure Interrupt #1 */ + def_irq_handler cpuss_interrupt_crypto_IRQHandler /* CRYPTO Accelerator Interrupt */ + def_irq_handler cpuss_interrupt_fm_IRQHandler /* FLASH Macro Interrupt */ + def_irq_handler cpuss_interrupts_cm4_fp_IRQHandler /* Floating Point operation fault */ + def_irq_handler cpuss_interrupts_cm0_cti_0_IRQHandler /* CM0+ CTI #0 */ + def_irq_handler cpuss_interrupts_cm0_cti_1_IRQHandler /* CM0+ CTI #1 */ + def_irq_handler cpuss_interrupts_cm4_cti_0_IRQHandler /* CM4 CTI #0 */ + def_irq_handler cpuss_interrupts_cm4_cti_1_IRQHandler /* CM4 CTI #1 */ + def_irq_handler tcpwm_0_interrupts_0_IRQHandler /* TCPWM #0, Counter #0 */ + def_irq_handler tcpwm_0_interrupts_1_IRQHandler /* TCPWM #0, Counter #1 */ + def_irq_handler tcpwm_0_interrupts_2_IRQHandler /* TCPWM #0, Counter #2 */ + def_irq_handler tcpwm_0_interrupts_3_IRQHandler /* TCPWM #0, Counter #3 */ + def_irq_handler tcpwm_0_interrupts_4_IRQHandler /* TCPWM #0, Counter #4 */ + def_irq_handler tcpwm_0_interrupts_5_IRQHandler /* TCPWM #0, Counter #5 */ + def_irq_handler tcpwm_0_interrupts_6_IRQHandler /* TCPWM #0, Counter #6 */ + def_irq_handler tcpwm_0_interrupts_7_IRQHandler /* TCPWM #0, Counter #7 */ + def_irq_handler tcpwm_1_interrupts_0_IRQHandler /* TCPWM #1, Counter #0 */ + def_irq_handler tcpwm_1_interrupts_1_IRQHandler /* TCPWM #1, Counter #1 */ + def_irq_handler tcpwm_1_interrupts_2_IRQHandler /* TCPWM #1, Counter #2 */ + def_irq_handler tcpwm_1_interrupts_3_IRQHandler /* TCPWM #1, Counter #3 */ + def_irq_handler tcpwm_1_interrupts_4_IRQHandler /* TCPWM #1, Counter #4 */ + def_irq_handler tcpwm_1_interrupts_5_IRQHandler /* TCPWM #1, Counter #5 */ + def_irq_handler tcpwm_1_interrupts_6_IRQHandler /* TCPWM #1, Counter #6 */ + def_irq_handler tcpwm_1_interrupts_7_IRQHandler /* TCPWM #1, Counter #7 */ + def_irq_handler tcpwm_1_interrupts_8_IRQHandler /* TCPWM #1, Counter #8 */ + def_irq_handler tcpwm_1_interrupts_9_IRQHandler /* TCPWM #1, Counter #9 */ + def_irq_handler tcpwm_1_interrupts_10_IRQHandler /* TCPWM #1, Counter #10 */ + def_irq_handler tcpwm_1_interrupts_11_IRQHandler /* TCPWM #1, Counter #11 */ + def_irq_handler tcpwm_1_interrupts_12_IRQHandler /* TCPWM #1, Counter #12 */ + def_irq_handler tcpwm_1_interrupts_13_IRQHandler /* TCPWM #1, Counter #13 */ + def_irq_handler tcpwm_1_interrupts_14_IRQHandler /* TCPWM #1, Counter #14 */ + def_irq_handler tcpwm_1_interrupts_15_IRQHandler /* TCPWM #1, Counter #15 */ + def_irq_handler tcpwm_1_interrupts_16_IRQHandler /* TCPWM #1, Counter #16 */ + def_irq_handler tcpwm_1_interrupts_17_IRQHandler /* TCPWM #1, Counter #17 */ + def_irq_handler tcpwm_1_interrupts_18_IRQHandler /* TCPWM #1, Counter #18 */ + def_irq_handler tcpwm_1_interrupts_19_IRQHandler /* TCPWM #1, Counter #19 */ + def_irq_handler tcpwm_1_interrupts_20_IRQHandler /* TCPWM #1, Counter #20 */ + def_irq_handler tcpwm_1_interrupts_21_IRQHandler /* TCPWM #1, Counter #21 */ + def_irq_handler tcpwm_1_interrupts_22_IRQHandler /* TCPWM #1, Counter #22 */ + def_irq_handler tcpwm_1_interrupts_23_IRQHandler /* TCPWM #1, Counter #23 */ + def_irq_handler pass_interrupt_sar_IRQHandler /* SAR ADC interrupt */ + def_irq_handler audioss_0_interrupt_i2s_IRQHandler /* I2S0 Audio interrupt */ + def_irq_handler audioss_0_interrupt_pdm_IRQHandler /* PDM0/PCM0 Audio interrupt */ + def_irq_handler audioss_1_interrupt_i2s_IRQHandler /* I2S1 Audio interrupt */ + def_irq_handler profile_interrupt_IRQHandler /* Energy Profiler interrupt */ + def_irq_handler smif_interrupt_IRQHandler /* Serial Memory Interface interrupt */ + def_irq_handler usb_interrupt_hi_IRQHandler /* USB Interrupt */ + def_irq_handler usb_interrupt_med_IRQHandler /* USB Interrupt */ + def_irq_handler usb_interrupt_lo_IRQHandler /* USB Interrupt */ + def_irq_handler sdhc_0_interrupt_wakeup_IRQHandler /* SDIO wakeup interrupt for mxsdhc */ + def_irq_handler sdhc_0_interrupt_general_IRQHandler /* Consolidated interrupt for mxsdhc for everything else */ + def_irq_handler sdhc_1_interrupt_wakeup_IRQHandler /* EEMC wakeup interrupt for mxsdhc, not used */ + def_irq_handler sdhc_1_interrupt_general_IRQHandler /* Consolidated interrupt for mxsdhc for everything else */ + + .end + + +/* [] END OF FILE */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg.c b/bootloader/mcuboot/boot/cypress/platforms/cycfg.c new file mode 100644 index 0000000..ede3da0 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg.c @@ -0,0 +1,33 @@ +/******************************************************************************* +* File Name: cycfg.c +* +* Description: +* Wrapper function to initialize all generated code. +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../../output/libs/COMPONENT_PSOC6/psoc6pdl): 1.5.0.1837 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "cycfg.h" + +void init_cycfg_all(void) +{ + init_cycfg_system(); + init_cycfg_clocks(); +} diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg.h b/bootloader/mcuboot/boot/cypress/platforms/cycfg.h new file mode 100644 index 0000000..9b74187 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg.h @@ -0,0 +1,45 @@ +/******************************************************************************* +* File Name: cycfg.h +* +* Description: +* Simple wrapper header containing all generated files. +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../../output/libs/COMPONENT_PSOC6/psoc6pdl): 1.5.0.1837 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#if !defined(CYCFG_H) +#define CYCFG_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include "cycfg_system.h" +#include "cycfg_clocks.h" + +void init_cycfg_all(void); + + +#if defined(__cplusplus) +} +#endif + + +#endif /* CYCFG_H */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.c b/bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.c new file mode 100644 index 0000000..7f720f7 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.c @@ -0,0 +1,47 @@ +/******************************************************************************* +* File Name: cycfg_clocks.c +* +* Description: +* Clock configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../../output/libs/COMPONENT_PSOC6/psoc6pdl): 1.5.0.1837 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "cycfg_clocks.h" + +#if defined (CY_USING_HAL) + const cyhal_resource_inst_t CYBSP_CSD_CLK_DIV_obj = + { + .type = CYHAL_RSC_CLOCK, + .block_num = CYBSP_CSD_CLK_DIV_HW, + .channel_num = CYBSP_CSD_CLK_DIV_NUM, + }; +#endif //defined (CY_USING_HAL) + + +void init_cycfg_clocks(void) +{ + Cy_SysClk_PeriphDisableDivider(CY_SYSCLK_DIV_8_BIT, 0U); + Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_8_BIT, 0U, 0U); + Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_8_BIT, 0U); +#if defined (CY_USING_HAL) + cyhal_hwmgr_reserve(&CYBSP_CSD_CLK_DIV_obj); +#endif //defined (CY_USING_HAL) +} diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.h b/bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.h new file mode 100644 index 0000000..5766ab5 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_clocks.h @@ -0,0 +1,54 @@ +/******************************************************************************* +* File Name: cycfg_clocks.h +* +* Description: +* Clock configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../../output/libs/COMPONENT_PSOC6/psoc6pdl): 1.5.0.1837 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#if !defined(CYCFG_CLOCKS_H) +#define CYCFG_CLOCKS_H + +#include "cy_sysclk.h" +#if defined (CY_USING_HAL) + #include "cyhal_hwmgr.h" +#endif //defined (CY_USING_HAL) + +#if defined(__cplusplus) +extern "C" { +#endif + +#define CYBSP_CSD_CLK_DIV_ENABLED 1U +#define CYBSP_CSD_CLK_DIV_HW CY_SYSCLK_DIV_8_BIT +#define CYBSP_CSD_CLK_DIV_NUM 0U + +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t CYBSP_CSD_CLK_DIV_obj; +#endif //defined (CY_USING_HAL) + +void init_cycfg_clocks(void); + +#if defined(__cplusplus) +} +#endif + + +#endif /* CYCFG_CLOCKS_H */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.c b/bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.c new file mode 100644 index 0000000..1c55938 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.c @@ -0,0 +1,73 @@ +/******************************************************************************* +* File Name: cycfg_peripherals.c +* +* Description: +* Peripheral Hardware Block configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../psoc6pdl): 1.3.1.1499 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "cycfg_peripherals.h" + +const cy_stc_scb_uart_config_t CYBSP_UART_config = +{ + .uartMode = CY_SCB_UART_STANDARD, + .enableMutliProcessorMode = false, + .smartCardRetryOnNack = false, + .irdaInvertRx = false, + .irdaEnableLowPowerReceiver = false, + .oversample = 8, + .enableMsbFirst = false, + .dataWidth = 8UL, + .parity = CY_SCB_UART_PARITY_NONE, + .stopBits = CY_SCB_UART_STOP_BITS_1, + .enableInputFilter = false, + .breakWidth = 11UL, + .dropOnFrameError = false, + .dropOnParityError = false, + .receiverAddress = 0x0UL, + .receiverAddressMask = 0x0UL, + .acceptAddrInFifo = false, + .enableCts = false, + .ctsPolarity = CY_SCB_UART_ACTIVE_LOW, + .rtsRxFifoLevel = 0UL, + .rtsPolarity = CY_SCB_UART_ACTIVE_LOW, + .rxFifoTriggerLevel = 0UL, + .rxFifoIntEnableMask = 0UL, + .txFifoTriggerLevel = 63UL, + .txFifoIntEnableMask = 0UL, +}; +#if defined (CY_USING_HAL) + const cyhal_resource_inst_t CYBSP_UART_obj = + { + .type = CYHAL_RSC_SCB, + .block_num = 5U, + .channel_num = 0U, + }; +#endif //defined (CY_USING_HAL) + + +void init_cycfg_peripherals(void) +{ + Cy_SysClk_PeriphAssignDivider(PCLK_SCB5_CLOCK, CY_SYSCLK_DIV_16_BIT, 0U); +#if defined (CY_USING_HAL) + cyhal_hwmgr_reserve(&CYBSP_UART_obj); +#endif //defined (CY_USING_HAL) +} diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.h b/bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.h new file mode 100644 index 0000000..41a0383 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_peripherals.h @@ -0,0 +1,57 @@ +/******************************************************************************* +* File Name: cycfg_peripherals.h +* +* Description: +* Peripheral Hardware Block configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../psoc6pdl): 1.3.1.1499 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#if !defined(CYCFG_PERIPHERALS_H) +#define CYCFG_PERIPHERALS_H + +// #include "cycfg_notices.h" +#include "cy_scb_uart.h" +#include "cy_sysclk.h" +#if defined (CY_USING_HAL) + #include "cyhal_hwmgr.h" +#endif //defined (CY_USING_HAL) + +#if defined(__cplusplus) +extern "C" { +#endif + +#define CYBSP_UART_ENABLED 1U +#define CYBSP_UART_HW SCB5 +#define CYBSP_UART_IRQ scb_5_interrupt_IRQn + +extern const cy_stc_scb_uart_config_t CYBSP_UART_config; +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t CYBSP_UART_obj; +#endif //defined (CY_USING_HAL) + +void init_cycfg_peripherals(void); + +#if defined(__cplusplus) +} +#endif + + +#endif /* CYCFG_PERIPHERALS_H */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.c b/bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.c new file mode 100644 index 0000000..771d772 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.c @@ -0,0 +1,89 @@ +/******************************************************************************* +* File Name: cycfg_pins.c +* +* Description: +* Pin configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../psoc6pdl): 1.3.1.1499 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "cycfg_pins.h" + +const cy_stc_gpio_pin_config_t CYBSP_UART_RX_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_HIGHZ, + .hsiom = CYBSP_UART_RX_HSIOM, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; +#if defined (CY_USING_HAL) + const cyhal_resource_inst_t CYBSP_UART_RX_obj = + { + .type = CYHAL_RSC_GPIO, + .block_num = CYBSP_UART_RX_PORT_NUM, + .channel_num = CYBSP_UART_RX_PIN, + }; +#endif //defined (CY_USING_HAL) +const cy_stc_gpio_pin_config_t CYBSP_UART_TX_config = +{ + .outVal = 1, + .driveMode = CY_GPIO_DM_STRONG_IN_OFF, + .hsiom = CYBSP_UART_TX_HSIOM, + .intEdge = CY_GPIO_INTR_DISABLE, + .intMask = 0UL, + .vtrip = CY_GPIO_VTRIP_CMOS, + .slewRate = CY_GPIO_SLEW_FAST, + .driveSel = CY_GPIO_DRIVE_1_2, + .vregEn = 0UL, + .ibufMode = 0UL, + .vtripSel = 0UL, + .vrefSel = 0UL, + .vohSel = 0UL, +}; +#if defined (CY_USING_HAL) + const cyhal_resource_inst_t CYBSP_UART_TX_obj = + { + .type = CYHAL_RSC_GPIO, + .block_num = CYBSP_UART_TX_PORT_NUM, + .channel_num = CYBSP_UART_TX_PIN, + }; +#endif //defined (CY_USING_HAL) + +void init_cycfg_pins(void) +{ + Cy_GPIO_Pin_Init(CYBSP_UART_RX_PORT, CYBSP_UART_RX_PIN, &CYBSP_UART_RX_config); +#if defined (CY_USING_HAL) + cyhal_hwmgr_reserve(&CYBSP_UART_RX_obj); +#endif //defined (CY_USING_HAL) + + Cy_GPIO_Pin_Init(CYBSP_UART_TX_PORT, CYBSP_UART_TX_PIN, &CYBSP_UART_TX_config); +#if defined (CY_USING_HAL) + cyhal_hwmgr_reserve(&CYBSP_UART_TX_obj); +#endif //defined (CY_USING_HAL) +} diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.h b/bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.h new file mode 100644 index 0000000..c214826 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_pins.h @@ -0,0 +1,113 @@ +/******************************************************************************* +* File Name: cycfg_pins.h +* +* Description: +* Pin configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../psoc6pdl): 1.3.1.1499 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#if !defined(CYCFG_PINS_H) +#define CYCFG_PINS_H + +#include "cy_gpio.h" +#if defined (CY_USING_HAL) + #include "cyhal_hwmgr.h" +#endif //defined (CY_USING_HAL) +#include "cycfg_routing.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define CYBSP_UART_RX_ENABLED 1U +#define CYBSP_UART_RX_PORT GPIO_PRT5 +#define CYBSP_UART_RX_PORT_NUM 5U +#define CYBSP_UART_RX_PIN 0U +#define CYBSP_UART_RX_NUM 0U +#define CYBSP_UART_RX_DRIVEMODE CY_GPIO_DM_HIGHZ +#define CYBSP_UART_RX_INIT_DRIVESTATE 1 +#ifndef ioss_0_port_5_pin_0_HSIOM + #define ioss_0_port_5_pin_0_HSIOM HSIOM_SEL_GPIO +#endif +#define CYBSP_UART_RX_HSIOM ioss_0_port_5_pin_0_HSIOM +#define CYBSP_UART_RX_IRQ ioss_interrupts_gpio_5_IRQn +#if defined (CY_USING_HAL) + #define CYBSP_UART_RX_HAL_PORT_PIN P5_0 +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + #define CYBSP_UART_RX_HAL_IRQ CYHAL_GPIO_IRQ_NONE +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + #define CYBSP_UART_RX_HAL_DIR CYHAL_GPIO_DIR_INPUT +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + #define CYBSP_UART_RX_HAL_DRIVEMODE CYHAL_GPIO_DRIVE_NONE +#endif //defined (CY_USING_HAL) +#define CYBSP_UART_TX_ENABLED 1U +#define CYBSP_UART_TX_PORT GPIO_PRT5 +#define CYBSP_UART_TX_PORT_NUM 5U +#define CYBSP_UART_TX_PIN 1U +#define CYBSP_UART_TX_NUM 1U +#define CYBSP_UART_TX_DRIVEMODE CY_GPIO_DM_STRONG_IN_OFF +#define CYBSP_UART_TX_INIT_DRIVESTATE 1 +#ifndef ioss_0_port_5_pin_1_HSIOM + #define ioss_0_port_5_pin_1_HSIOM HSIOM_SEL_GPIO +#endif +#define CYBSP_UART_TX_HSIOM ioss_0_port_5_pin_1_HSIOM +#define CYBSP_UART_TX_IRQ ioss_interrupts_gpio_5_IRQn +#if defined (CY_USING_HAL) + #define CYBSP_UART_TX_HAL_PORT_PIN P5_1 +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + #define CYBSP_UART_TX_HAL_IRQ CYHAL_GPIO_IRQ_NONE +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + #define CYBSP_UART_TX_HAL_DIR CYHAL_GPIO_DIR_OUTPUT +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + #define CYBSP_UART_TX_HAL_DRIVEMODE CYHAL_GPIO_DRIVE_STRONG +#endif //defined (CY_USING_HAL) + +extern const cy_stc_gpio_pin_config_t CYBSP_WCO_IN_config; +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t CYBSP_WCO_IN_obj; +#endif //defined (CY_USING_HAL) +extern const cy_stc_gpio_pin_config_t CYBSP_WCO_OUT_config; +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t CYBSP_WCO_OUT_obj; +#endif //defined (CY_USING_HAL) +extern const cy_stc_gpio_pin_config_t CYBSP_UART_RX_config; +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t CYBSP_UART_RX_obj; +#endif //defined (CY_USING_HAL) +extern const cy_stc_gpio_pin_config_t CYBSP_UART_TX_config; +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t CYBSP_UART_TX_obj; +#endif //defined (CY_USING_HAL) + +void init_cycfg_pins(void); + +#if defined(__cplusplus) +} +#endif + + +#endif /* CYCFG_PINS_H */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.c b/bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.c new file mode 100644 index 0000000..063e736 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.c @@ -0,0 +1,31 @@ +/******************************************************************************* +* File Name: cycfg_routing.c +* +* Description: +* Establishes all necessary connections between hardware elements. +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../psoc6pdl): 1.4.0.1889 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "cycfg_routing.h" + +void init_cycfg_routing(void) +{ +} diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.h b/bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.h new file mode 100644 index 0000000..bfb2998 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_routing.h @@ -0,0 +1,46 @@ +/******************************************************************************* +* File Name: cycfg_routing.h +* +* Description: +* Establishes all necessary connections between hardware elements. +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../psoc6pdl): 1.4.0.1889 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#if !defined(CYCFG_ROUTING_H) +#define CYCFG_ROUTING_H + +#if defined(__cplusplus) +extern "C" { +#endif + +void init_cycfg_routing(void); + +#define init_cycfg_connectivity() init_cycfg_routing() + +#define ioss_0_port_5_pin_0_HSIOM P5_0_SCB5_UART_RX +#define ioss_0_port_5_pin_1_HSIOM P5_1_SCB5_UART_TX + +#if defined(__cplusplus) +} +#endif + + +#endif /* CYCFG_ROUTING_H */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_system.c b/bootloader/mcuboot/boot/cypress/platforms/cycfg_system.c new file mode 100644 index 0000000..57ab013 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_system.c @@ -0,0 +1,537 @@ +/******************************************************************************* +* File Name: cycfg_system.c +* +* Description: +* System configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../../output/libs/COMPONENT_PSOC6/psoc6pdl): 1.5.0.1837 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include "cycfg_system.h" + +#define CY_CFG_SYSCLK_ECO_ERROR 1 +#define CY_CFG_SYSCLK_ALTHF_ERROR 2 +#define CY_CFG_SYSCLK_PLL_ERROR 3 +#define CY_CFG_SYSCLK_FLL_ERROR 4 +#define CY_CFG_SYSCLK_WCO_ERROR 5 +#define CY_CFG_SYSCLK_CLKALTSYSTICK_ENABLED 1 +#define CY_CFG_SYSCLK_CLKBAK_ENABLED 1 +#define CY_CFG_SYSCLK_CLKFAST_ENABLED 1 +#define CY_CFG_SYSCLK_FLL_ENABLED 1 +#define CY_CFG_SYSCLK_CLKHF0_ENABLED 1 +#define CY_CFG_SYSCLK_CLKHF0_FREQ_MHZ 100UL +#define CY_CFG_SYSCLK_CLKHF0_CLKPATH CY_SYSCLK_CLKHF_IN_CLKPATH0 +#define CY_CFG_SYSCLK_CLKHF2_ENABLED 1 +#define CY_CFG_SYSCLK_CLKHF2_FREQ_MHZ 50UL +#define CY_CFG_SYSCLK_CLKHF2_CLKPATH CY_SYSCLK_CLKHF_IN_CLKPATH0 +#define CY_CFG_SYSCLK_CLKHF3_ENABLED 1 +#define CY_CFG_SYSCLK_CLKHF3_FREQ_MHZ 100UL +#define CY_CFG_SYSCLK_CLKHF3_CLKPATH CY_SYSCLK_CLKHF_IN_CLKPATH0 +#define CY_CFG_SYSCLK_CLKHF4_ENABLED 1 +#define CY_CFG_SYSCLK_CLKHF4_FREQ_MHZ 100UL +#define CY_CFG_SYSCLK_CLKHF4_CLKPATH CY_SYSCLK_CLKHF_IN_CLKPATH0 +#define CY_CFG_SYSCLK_ILO_ENABLED 1 +#define CY_CFG_SYSCLK_IMO_ENABLED 1 +#define CY_CFG_SYSCLK_CLKLF_ENABLED 1 +#define CY_CFG_SYSCLK_CLKPATH0_ENABLED 1 +#define CY_CFG_SYSCLK_CLKPATH0_SOURCE CY_SYSCLK_CLKPATH_IN_IMO +#define CY_CFG_SYSCLK_CLKPATH1_ENABLED 1 +#define CY_CFG_SYSCLK_CLKPATH1_SOURCE CY_SYSCLK_CLKPATH_IN_IMO +#define CY_CFG_SYSCLK_CLKPATH2_ENABLED 1 +#define CY_CFG_SYSCLK_CLKPATH2_SOURCE CY_SYSCLK_CLKPATH_IN_IMO +#define CY_CFG_SYSCLK_CLKPERI_ENABLED 1 +#define CY_CFG_SYSCLK_PLL0_ENABLED 1 +#define CY_CFG_SYSCLK_PLL1_ENABLED 1 +#define CY_CFG_SYSCLK_CLKSLOW_ENABLED 1 +#define CY_CFG_SYSCLK_CLKTIMER_ENABLED 1 +#define CY_CFG_SYSCLK_WCO_ENABLED 1 + +static const cy_stc_fll_manual_config_t srss_0_clock_0_fll_0_fllConfig = +{ + .fllMult = 500U, + .refDiv = 20U, + .ccoRange = CY_SYSCLK_FLL_CCO_RANGE4, + .enableOutputDiv = true, + .lockTolerance = 10U, + .igain = 9U, + .pgain = 5U, + .settlingCount = 8U, + .outputMode = CY_SYSCLK_FLLPLL_OUTPUT_OUTPUT, + .cco_Freq = 355U, +}; +#if defined (CY_USING_HAL) + const cyhal_resource_inst_t srss_0_clock_0_pathmux_0_obj = + { + .type = CYHAL_RSC_CLKPATH, + .block_num = 0U, + .channel_num = 0U, + }; +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + const cyhal_resource_inst_t srss_0_clock_0_pathmux_1_obj = + { + .type = CYHAL_RSC_CLKPATH, + .block_num = 1U, + .channel_num = 0U, + }; +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + const cyhal_resource_inst_t srss_0_clock_0_pathmux_2_obj = + { + .type = CYHAL_RSC_CLKPATH, + .block_num = 2U, + .channel_num = 0U, + }; +#endif //defined (CY_USING_HAL) +static const cy_stc_pll_manual_config_t srss_0_clock_0_pll_0_pllConfig = +{ + .feedbackDiv = 36, + .referenceDiv = 1, + .outputDiv = 2, + .lfMode = false, + .outputMode = CY_SYSCLK_FLLPLL_OUTPUT_AUTO, +}; +static const cy_stc_pll_manual_config_t srss_0_clock_0_pll_1_pllConfig = +{ + .feedbackDiv = 30, + .referenceDiv = 1, + .outputDiv = 5, + .lfMode = false, + .outputMode = CY_SYSCLK_FLLPLL_OUTPUT_AUTO, +}; + +__WEAK void cycfg_ClockStartupError(uint32_t error) +{ + (void)error; /* Suppress the compiler warning */ + while(1); +} +__STATIC_INLINE void Cy_SysClk_ClkAltSysTickInit() +{ + Cy_SysTick_SetClockSource(CY_SYSTICK_CLOCK_SOURCE_CLK_LF); +} +__STATIC_INLINE void Cy_SysClk_ClkBakInit() +{ + Cy_SysClk_ClkBakSetSource(CY_SYSCLK_BAK_IN_CLKLF); +} +__STATIC_INLINE void Cy_SysClk_ClkFastInit() +{ + Cy_SysClk_ClkFastSetDivider(0U); +} +__STATIC_INLINE void Cy_SysClk_FllInit() +{ + if (CY_SYSCLK_SUCCESS != Cy_SysClk_FllManualConfigure(&srss_0_clock_0_fll_0_fllConfig)) + { + cycfg_ClockStartupError(CY_CFG_SYSCLK_FLL_ERROR); + } + if (CY_SYSCLK_SUCCESS != Cy_SysClk_FllEnable(200000UL)) + { + cycfg_ClockStartupError(CY_CFG_SYSCLK_FLL_ERROR); + } +} +__STATIC_INLINE void Cy_SysClk_ClkHf0Init() +{ + Cy_SysClk_ClkHfSetSource(0U, CY_CFG_SYSCLK_CLKHF0_CLKPATH); + Cy_SysClk_ClkHfSetDivider(0U, CY_SYSCLK_CLKHF_NO_DIVIDE); +} +__STATIC_INLINE void Cy_SysClk_ClkHf2Init() +{ + Cy_SysClk_ClkHfSetSource(CY_CFG_SYSCLK_CLKHF2, CY_CFG_SYSCLK_CLKHF2_CLKPATH); + Cy_SysClk_ClkHfSetDivider(CY_CFG_SYSCLK_CLKHF2, CY_SYSCLK_CLKHF_DIVIDE_BY_2); + Cy_SysClk_ClkHfEnable(CY_CFG_SYSCLK_CLKHF2); +} +__STATIC_INLINE void Cy_SysClk_ClkHf3Init() +{ + Cy_SysClk_ClkHfSetSource(CY_CFG_SYSCLK_CLKHF3, CY_CFG_SYSCLK_CLKHF3_CLKPATH); + Cy_SysClk_ClkHfSetDivider(CY_CFG_SYSCLK_CLKHF3, CY_SYSCLK_CLKHF_NO_DIVIDE); + Cy_SysClk_ClkHfEnable(CY_CFG_SYSCLK_CLKHF3); +} +__STATIC_INLINE void Cy_SysClk_ClkHf4Init() +{ + Cy_SysClk_ClkHfSetSource(CY_CFG_SYSCLK_CLKHF4, CY_CFG_SYSCLK_CLKHF4_CLKPATH); + Cy_SysClk_ClkHfSetDivider(CY_CFG_SYSCLK_CLKHF4, CY_SYSCLK_CLKHF_NO_DIVIDE); + Cy_SysClk_ClkHfEnable(CY_CFG_SYSCLK_CLKHF4); +} +__STATIC_INLINE void Cy_SysClk_IloInit() +{ + /* The WDT is unlocked in the default startup code */ + Cy_SysClk_IloEnable(); + Cy_SysClk_IloHibernateOn(true); +} +__STATIC_INLINE void Cy_SysClk_ClkLfInit() +{ + /* The WDT is unlocked in the default startup code */ + Cy_SysClk_ClkLfSetSource(CY_SYSCLK_CLKLF_IN_WCO); +} +__STATIC_INLINE void Cy_SysClk_ClkPath0Init() +{ + Cy_SysClk_ClkPathSetSource(0U, CY_CFG_SYSCLK_CLKPATH0_SOURCE); +} +__STATIC_INLINE void Cy_SysClk_ClkPath1Init() +{ + Cy_SysClk_ClkPathSetSource(1U, CY_CFG_SYSCLK_CLKPATH1_SOURCE); +} +__STATIC_INLINE void Cy_SysClk_ClkPath2Init() +{ + Cy_SysClk_ClkPathSetSource(2U, CY_CFG_SYSCLK_CLKPATH2_SOURCE); +} +__STATIC_INLINE void Cy_SysClk_ClkPeriInit() +{ + Cy_SysClk_ClkPeriSetDivider(1U); +} +__STATIC_INLINE void Cy_SysClk_Pll0Init() +{ + if (CY_SYSCLK_SUCCESS != Cy_SysClk_PllManualConfigure(1U, &srss_0_clock_0_pll_0_pllConfig)) + { + cycfg_ClockStartupError(CY_CFG_SYSCLK_PLL_ERROR); + } + if (CY_SYSCLK_SUCCESS != Cy_SysClk_PllEnable(1U, 10000u)) + { + cycfg_ClockStartupError(CY_CFG_SYSCLK_PLL_ERROR); + } +} +__STATIC_INLINE void Cy_SysClk_Pll1Init() +{ + if (CY_SYSCLK_SUCCESS != Cy_SysClk_PllManualConfigure(2U, &srss_0_clock_0_pll_1_pllConfig)) + { + cycfg_ClockStartupError(CY_CFG_SYSCLK_PLL_ERROR); + } + if (CY_SYSCLK_SUCCESS != Cy_SysClk_PllEnable(2U, 10000u)) + { + cycfg_ClockStartupError(CY_CFG_SYSCLK_PLL_ERROR); + } +} +__STATIC_INLINE void Cy_SysClk_ClkSlowInit() +{ + Cy_SysClk_ClkSlowSetDivider(0U); +} +__STATIC_INLINE void Cy_SysClk_ClkTimerInit() +{ + Cy_SysClk_ClkTimerDisable(); + Cy_SysClk_ClkTimerSetSource(CY_SYSCLK_CLKTIMER_IN_IMO); + Cy_SysClk_ClkTimerSetDivider(0U); + Cy_SysClk_ClkTimerEnable(); +} +__STATIC_INLINE void Cy_SysClk_WcoInit() +{ + (void)Cy_GPIO_Pin_FastInit(GPIO_PRT0, 0U, 0x00U, 0x00U, HSIOM_SEL_GPIO); + (void)Cy_GPIO_Pin_FastInit(GPIO_PRT0, 1U, 0x00U, 0x00U, HSIOM_SEL_GPIO); + if (CY_SYSCLK_SUCCESS != Cy_SysClk_WcoEnable(1000000UL)) + { + cycfg_ClockStartupError(CY_CFG_SYSCLK_WCO_ERROR); + } +} + + +void init_cycfg_system(void) +{ + /* Set worst case memory wait states (! ultra low power, 150 MHz), will update at the end */ + Cy_SysLib_SetWaitStates(false, 150UL); + #ifdef CY_CFG_PWR_ENABLED + #ifdef CY_CFG_PWR_INIT + init_cycfg_power(); + #else + #warning Power system will not be configured. Update power personality to v1.20 or later. + #endif /* CY_CFG_PWR_INIT */ + #endif /* CY_CFG_PWR_ENABLED */ + + /* Reset the core clock path to default and disable all the FLLs/PLLs */ + Cy_SysClk_ClkHfSetDivider(0U, CY_SYSCLK_CLKHF_NO_DIVIDE); + Cy_SysClk_ClkFastSetDivider(0U); + Cy_SysClk_ClkPeriSetDivider(1U); + Cy_SysClk_ClkSlowSetDivider(0U); + for (uint32_t pll = CY_SRSS_NUM_PLL; pll > 0UL; --pll) /* PLL 1 is the first PLL. 0 is invalid. */ + { + (void)Cy_SysClk_PllDisable(pll); + } + Cy_SysClk_ClkPathSetSource(CY_SYSCLK_CLKHF_IN_CLKPATH1, CY_SYSCLK_CLKPATH_IN_IMO); + + if ((CY_SYSCLK_CLKHF_IN_CLKPATH0 == Cy_SysClk_ClkHfGetSource(0UL)) && + (CY_SYSCLK_CLKPATH_IN_WCO == Cy_SysClk_ClkPathGetSource(CY_SYSCLK_CLKHF_IN_CLKPATH0))) + { + Cy_SysClk_ClkHfSetSource(0U, CY_SYSCLK_CLKHF_IN_CLKPATH1); + } + + Cy_SysClk_FllDisable(); + Cy_SysClk_ClkPathSetSource(CY_SYSCLK_CLKHF_IN_CLKPATH0, CY_SYSCLK_CLKPATH_IN_IMO); + Cy_SysClk_ClkHfSetSource(0UL, CY_SYSCLK_CLKHF_IN_CLKPATH0); + #ifdef CY_IP_MXBLESS + (void)Cy_BLE_EcoReset(); + #endif + + + /* Enable all source clocks */ + #ifdef CY_CFG_SYSCLK_PILO_ENABLED + Cy_SysClk_PiloInit(); + #endif + + #ifdef CY_CFG_SYSCLK_WCO_ENABLED + Cy_SysClk_WcoInit(); + #endif + + #ifdef CY_CFG_SYSCLK_CLKLF_ENABLED + Cy_SysClk_ClkLfInit(); + #endif + + #ifdef CY_CFG_SYSCLK_ALTHF_ENABLED + Cy_SysClk_AltHfInit(); + #endif + + #ifdef CY_CFG_SYSCLK_ECO_ENABLED + Cy_SysClk_EcoInit(); + #endif + + #ifdef CY_CFG_SYSCLK_EXTCLK_ENABLED + Cy_SysClk_ExtClkInit(); + #endif + + /* Configure CPU clock dividers */ + #ifdef CY_CFG_SYSCLK_CLKFAST_ENABLED + Cy_SysClk_ClkFastInit(); + #endif + + #ifdef CY_CFG_SYSCLK_CLKPERI_ENABLED + Cy_SysClk_ClkPeriInit(); + #endif + + #ifdef CY_CFG_SYSCLK_CLKSLOW_ENABLED + Cy_SysClk_ClkSlowInit(); + #endif + + #if ((CY_CFG_SYSCLK_CLKPATH0_SOURCE == CY_SYSCLK_CLKPATH_IN_WCO) && (CY_CFG_SYSCLK_CLKHF0_CLKPATH == CY_SYSCLK_CLKHF_IN_CLKPATH0)) + /* Configure HFCLK0 to temporarily run from IMO to initialize other clocks */ + Cy_SysClk_ClkPathSetSource(1UL, CY_SYSCLK_CLKPATH_IN_IMO); + Cy_SysClk_ClkHfSetSource(0UL, CY_SYSCLK_CLKHF_IN_CLKPATH1); + #else + #ifdef CY_CFG_SYSCLK_CLKPATH1_ENABLED + Cy_SysClk_ClkPath1Init(); + #endif + #endif + + /* Configure Path Clocks */ + #ifdef CY_CFG_SYSCLK_CLKPATH0_ENABLED + Cy_SysClk_ClkPath0Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH2_ENABLED + Cy_SysClk_ClkPath2Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH3_ENABLED + Cy_SysClk_ClkPath3Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH4_ENABLED + Cy_SysClk_ClkPath4Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH5_ENABLED + Cy_SysClk_ClkPath5Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH6_ENABLED + Cy_SysClk_ClkPath6Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH7_ENABLED + Cy_SysClk_ClkPath7Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH8_ENABLED + Cy_SysClk_ClkPath8Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH9_ENABLED + Cy_SysClk_ClkPath9Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH10_ENABLED + Cy_SysClk_ClkPath10Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH11_ENABLED + Cy_SysClk_ClkPath11Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH12_ENABLED + Cy_SysClk_ClkPath12Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH13_ENABLED + Cy_SysClk_ClkPath13Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH14_ENABLED + Cy_SysClk_ClkPath14Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKPATH15_ENABLED + Cy_SysClk_ClkPath15Init(); + #endif + + /* Configure and enable FLL */ + #ifdef CY_CFG_SYSCLK_FLL_ENABLED + Cy_SysClk_FllInit(); + #endif + + Cy_SysClk_ClkHf0Init(); + + #if ((CY_CFG_SYSCLK_CLKPATH0_SOURCE == CY_SYSCLK_CLKPATH_IN_WCO) && (CY_CFG_SYSCLK_CLKHF0_CLKPATH == CY_SYSCLK_CLKHF_IN_CLKPATH0)) + #ifdef CY_CFG_SYSCLK_CLKPATH1_ENABLED + /* Apply the ClkPath1 user setting */ + Cy_SysClk_ClkPath1Init(); + #endif + #endif + + /* Configure and enable PLLs */ + #ifdef CY_CFG_SYSCLK_PLL0_ENABLED + Cy_SysClk_Pll0Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL1_ENABLED + Cy_SysClk_Pll1Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL2_ENABLED + Cy_SysClk_Pll2Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL3_ENABLED + Cy_SysClk_Pll3Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL4_ENABLED + Cy_SysClk_Pll4Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL5_ENABLED + Cy_SysClk_Pll5Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL6_ENABLED + Cy_SysClk_Pll6Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL7_ENABLED + Cy_SysClk_Pll7Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL8_ENABLED + Cy_SysClk_Pll8Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL9_ENABLED + Cy_SysClk_Pll9Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL10_ENABLED + Cy_SysClk_Pll10Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL11_ENABLED + Cy_SysClk_Pll11Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL12_ENABLED + Cy_SysClk_Pll12Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL13_ENABLED + Cy_SysClk_Pll13Init(); + #endif + #ifdef CY_CFG_SYSCLK_PLL14_ENABLED + Cy_SysClk_Pll14Init(); + #endif + + /* Configure HF clocks */ + #ifdef CY_CFG_SYSCLK_CLKHF1_ENABLED + Cy_SysClk_ClkHf1Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF2_ENABLED + Cy_SysClk_ClkHf2Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF3_ENABLED + Cy_SysClk_ClkHf3Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF4_ENABLED + Cy_SysClk_ClkHf4Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF5_ENABLED + Cy_SysClk_ClkHf5Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF6_ENABLED + Cy_SysClk_ClkHf6Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF7_ENABLED + Cy_SysClk_ClkHf7Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF8_ENABLED + Cy_SysClk_ClkHf8Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF9_ENABLED + Cy_SysClk_ClkHf9Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF10_ENABLED + Cy_SysClk_ClkHf10Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF11_ENABLED + Cy_SysClk_ClkHf11Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF12_ENABLED + Cy_SysClk_ClkHf12Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF13_ENABLED + Cy_SysClk_ClkHf13Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF14_ENABLED + Cy_SysClk_ClkHf14Init(); + #endif + #ifdef CY_CFG_SYSCLK_CLKHF15_ENABLED + Cy_SysClk_ClkHf15Init(); + #endif + + /* Configure miscellaneous clocks */ + #ifdef CY_CFG_SYSCLK_CLKTIMER_ENABLED + Cy_SysClk_ClkTimerInit(); + #endif + + #ifdef CY_CFG_SYSCLK_CLKALTSYSTICK_ENABLED + Cy_SysClk_ClkAltSysTickInit(); + #endif + + #ifdef CY_CFG_SYSCLK_CLKPUMP_ENABLED + Cy_SysClk_ClkPumpInit(); + #endif + + #ifdef CY_CFG_SYSCLK_CLKBAK_ENABLED + Cy_SysClk_ClkBakInit(); + #endif + + /* Configure default enabled clocks */ + #ifdef CY_CFG_SYSCLK_ILO_ENABLED + Cy_SysClk_IloInit(); + #else + Cy_SysClk_IloDisable(); + #endif + + #ifndef CY_CFG_SYSCLK_IMO_ENABLED + #error the IMO must be enabled for proper chip operation + #endif + + #ifdef CY_CFG_SYSCLK_MFO_ENABLED + Cy_SysClk_MfoInit(); + #endif + + #ifdef CY_CFG_SYSCLK_CLKMF_ENABLED + Cy_SysClk_ClkMfInit(); + #endif + + /* Set accurate flash wait states */ + #if (defined (CY_CFG_PWR_ENABLED) && defined (CY_CFG_SYSCLK_CLKHF0_ENABLED)) + Cy_SysLib_SetWaitStates(CY_CFG_PWR_USING_ULP != 0, CY_CFG_SYSCLK_CLKHF0_FREQ_MHZ); + #endif + + /* Update System Core Clock values for correct Cy_SysLib_Delay functioning */ + SystemCoreClockUpdate(); + +#if defined (CY_USING_HAL) + cyhal_hwmgr_reserve(&srss_0_clock_0_pathmux_0_obj); +#endif //defined (CY_USING_HAL) + +#if defined (CY_USING_HAL) + cyhal_hwmgr_reserve(&srss_0_clock_0_pathmux_1_obj); +#endif //defined (CY_USING_HAL) + +#if defined (CY_USING_HAL) + cyhal_hwmgr_reserve(&srss_0_clock_0_pathmux_2_obj); +#endif //defined (CY_USING_HAL) +} diff --git a/bootloader/mcuboot/boot/cypress/platforms/cycfg_system.h b/bootloader/mcuboot/boot/cypress/platforms/cycfg_system.h new file mode 100644 index 0000000..581aeda --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/cycfg_system.h @@ -0,0 +1,86 @@ +/******************************************************************************* +* File Name: cycfg_system.h +* +* Description: +* System configuration +* This file was automatically generated and should not be modified. +* Device Configurator: 2.0.0.1483 +* Device Support Library (../../../../output/libs/COMPONENT_PSOC6/psoc6pdl): 1.5.0.1837 +* +******************************************************************************** +* Copyright 2017-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#if !defined(CYCFG_SYSTEM_H) +#define CYCFG_SYSTEM_H + +#include "cy_sysclk.h" +#include "cy_systick.h" +#if defined (CY_USING_HAL) + #include "cyhal_hwmgr.h" +#endif //defined (CY_USING_HAL) +#include "cy_gpio.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define cpuss_0_dap_0_ENABLED 1U +#define srss_0_clock_0_ENABLED 1U +#define srss_0_clock_0_altsystickclk_0_ENABLED 1U +#define srss_0_clock_0_bakclk_0_ENABLED 1U +#define srss_0_clock_0_fastclk_0_ENABLED 1U +#define srss_0_clock_0_fll_0_ENABLED 1U +#define srss_0_clock_0_hfclk_0_ENABLED 1U +#define CY_CFG_SYSCLK_CLKHF0 0UL +#define srss_0_clock_0_hfclk_2_ENABLED 1U +#define CY_CFG_SYSCLK_CLKHF2 2UL +#define srss_0_clock_0_hfclk_3_ENABLED 1U +#define CY_CFG_SYSCLK_CLKHF3 3UL +#define srss_0_clock_0_hfclk_4_ENABLED 1U +#define CY_CFG_SYSCLK_CLKHF4 4UL +#define srss_0_clock_0_ilo_0_ENABLED 1U +#define srss_0_clock_0_imo_0_ENABLED 1U +#define srss_0_clock_0_lfclk_0_ENABLED 1U +#define CY_CFG_SYSCLK_CLKLF_FREQ_HZ 32768 +#define srss_0_clock_0_pathmux_0_ENABLED 1U +#define srss_0_clock_0_pathmux_1_ENABLED 1U +#define srss_0_clock_0_pathmux_2_ENABLED 1U +#define srss_0_clock_0_periclk_0_ENABLED 1U +#define srss_0_clock_0_pll_0_ENABLED 1U +#define srss_0_clock_0_pll_1_ENABLED 1U +#define srss_0_clock_0_slowclk_0_ENABLED 1U +#define srss_0_clock_0_timerclk_0_ENABLED 1U +#define srss_0_clock_0_wco_0_ENABLED 1U + +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t srss_0_clock_0_pathmux_0_obj; +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t srss_0_clock_0_pathmux_1_obj; +#endif //defined (CY_USING_HAL) +#if defined (CY_USING_HAL) + extern const cyhal_resource_inst_t srss_0_clock_0_pathmux_2_obj; +#endif //defined (CY_USING_HAL) + +void init_cycfg_system(void); + +#if defined(__cplusplus) +} +#endif + + +#endif /* CYCFG_SYSTEM_H */ diff --git a/bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.c b/bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.c new file mode 100644 index 0000000..0472f3e --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.c @@ -0,0 +1,270 @@ +/***************************************************************************//** +* \file cy_retarget_io.c +* +* \brief +* Provides APIs for retargeting stdio to UART hardware contained on the Cypress +* kits. +* +******************************************************************************** +* \copyright +* Copyright 2018-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#include "cy_retarget_io_pdl.h" + +#include "cycfg_peripherals.h" + +#include "cy_sysint.h" +#include "cy_scb_uart.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Tracks the previous character sent to output stream */ +#ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF +static char cy_retarget_io_stdout_prev_char = 0; +#endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + +cy_stc_scb_uart_context_t CYBSP_UART_context; + +static uint8_t cy_retarget_io_getchar(void); +static void cy_retarget_io_putchar(char c); + +#if defined(__ARMCC_VERSION) /* ARM-MDK */ + /*************************************************************************** + * Function Name: fputc + ***************************************************************************/ + __attribute__((weak)) int fputc(int ch, FILE *f) + { + (void)f; + #ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF + if ((char)ch == '\n' && cy_retarget_io_stdout_prev_char != '\r') + { + cy_retarget_io_putchar('\r'); + } + + cy_retarget_io_stdout_prev_char = (char)ch; + #endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + cy_retarget_io_putchar(ch); + return (ch); + } +#elif defined (__ICCARM__) /* IAR */ + #include + + /*************************************************************************** + * Function Name: __write + ***************************************************************************/ + __weak size_t __write(int handle, const unsigned char * buffer, size_t size) + { + size_t nChars = 0; + /* This template only writes to "standard out", for all other file + * handles it returns failure. */ + if (handle != _LLIO_STDOUT) + { + return (_LLIO_ERROR); + } + if (buffer != NULL) + { + for (/* Empty */; nChars < size; ++nChars) + { + #ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF + if (*buffer == '\n' && cy_retarget_io_stdout_prev_char != '\r') + { + cy_retarget_io_putchar('\r'); + } + + cy_retarget_io_stdout_prev_char = *buffer; + #endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + cy_retarget_io_putchar(*buffer); + ++buffer; + } + } + return (nChars); + } +#else /* (__GNUC__) GCC */ + /* Add an explicit reference to the floating point printf library to allow + the usage of floating point conversion specifier. */ + __asm (".global _printf_float"); + /*************************************************************************** + * Function Name: _write + ***************************************************************************/ + __attribute__((weak)) int _write (int fd, const char *ptr, int len) + { + int nChars = 0; + (void)fd; + if (ptr != NULL) + { + for (/* Empty */; nChars < len; ++nChars) + { + #ifdef CY_RETARGET_IO_CONVERT_LF_TO_CRLF + if (*ptr == '\n' && cy_retarget_io_stdout_prev_char != '\r') + { + cy_retarget_io_putchar('\r'); + } + + cy_retarget_io_stdout_prev_char = *ptr; + #endif /* CY_RETARGET_IO_CONVERT_LF_TO_CRLF */ + cy_retarget_io_putchar((uint32_t)*ptr); + ++ptr; + } + } + return (nChars); + } +#endif + + +#if defined(__ARMCC_VERSION) /* ARM-MDK */ + /*************************************************************************** + * Function Name: fgetc + ***************************************************************************/ + __attribute__((weak)) int fgetc(FILE *f) + { + (void)f; + return (cy_retarget_io_getchar()); + } +#elif defined (__ICCARM__) /* IAR */ + __weak size_t __read(int handle, unsigned char * buffer, size_t size) + { + /* This template only reads from "standard in", for all other file + handles it returns failure. */ + if ((handle != _LLIO_STDIN) || (buffer == NULL)) + { + return (_LLIO_ERROR); + } + else + { + *buffer = cy_retarget_io_getchar(); + return (1); + } + } +#else /* (__GNUC__) GCC */ + /* Add an explicit reference to the floating point scanf library to allow + the usage of floating point conversion specifier. */ + __asm (".global _scanf_float"); + __attribute__((weak)) int _read (int fd, char *ptr, int len) + { + int nChars = 0; + (void)fd; + if (ptr != NULL) + { + for(/* Empty */;nChars < len;++ptr) + { + *ptr = (char)cy_retarget_io_getchar(); + ++nChars; + if((*ptr == '\n') || (*ptr == '\r')) + { + break; + } + } + } + return (nChars); + } +#endif + +static uint8_t cy_retarget_io_getchar(void) +{ + uint32_t read_value = Cy_SCB_UART_Get(CYBSP_UART_HW); + while (read_value == CY_SCB_UART_RX_NO_DATA) + { + read_value = Cy_SCB_UART_Get(CYBSP_UART_HW); + } + + return (uint8_t)read_value; +} + +static void cy_retarget_io_putchar(char c) +{ + uint32_t count = 0; + while (count == 0) + { + count = Cy_SCB_UART_Put(CYBSP_UART_HW, c); + } +} + +static cy_rslt_t cy_retarget_io_pdl_setbaud(CySCB_Type *base, uint32_t baudrate) +{ + cy_rslt_t result = CY_RSLT_TYPE_ERROR; + + uint8_t oversample_value = 8u; + uint8_t frac_bits = 0u; + uint32_t divider; + + Cy_SCB_UART_Disable(base, NULL); + + result = (cy_rslt_t) Cy_SysClk_PeriphDisableDivider(CY_SYSCLK_DIV_16_BIT, 0); + + divider = ((Cy_SysClk_ClkPeriGetFrequency() * (1 << frac_bits)) + ((baudrate * oversample_value) / 2)) / (baudrate * oversample_value) - 1; + + if (result == CY_RSLT_SUCCESS) + { + result = (cy_rslt_t) Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_16_BIT, 0u, divider); + } + + if (result == CY_RSLT_SUCCESS) + { + result = Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_16_BIT, 0u); + } + + Cy_SCB_UART_Enable(base); + + return result; +} + +cy_rslt_t cy_retarget_io_pdl_init(uint32_t baudrate) +{ + cy_rslt_t result = CY_RSLT_TYPE_ERROR; + + result = (cy_rslt_t)Cy_SCB_UART_Init(CYBSP_UART_HW, &CYBSP_UART_config, &CYBSP_UART_context); + + if (result == CY_RSLT_SUCCESS) + { + result = cy_retarget_io_pdl_setbaud(CYBSP_UART_HW, baudrate); + } + + if (result == CY_RSLT_SUCCESS) + { + Cy_SCB_UART_Enable(CYBSP_UART_HW); + } + + return result; +} + +/** + * @brief Wait while UART completes transfer. Try for tries_count times - + * once each 10 millisecons. + */ +void cy_retarget_io_wait_tx_complete(CySCB_Type *base, uint32_t tries_count) +{ + while(tries_count > 0) + { + if (!Cy_SCB_UART_IsTxComplete(base)) { + Cy_SysLib_DelayCycles(10 * cy_delayFreqKhz); + tries_count -= 1; + } else { + return; + } + } +} + +void cy_retarget_io_pdl_deinit() +{ + Cy_SCB_UART_DeInit(CYBSP_UART_HW); +} + +#if defined(__cplusplus) +} +#endif diff --git a/bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.h b/bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.h new file mode 100644 index 0000000..f0c8733 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/platforms/retarget_io_pdl/cy_retarget_io_pdl.h @@ -0,0 +1,62 @@ +/***************************************************************************//** +* \file cy_retarget_io.h +* +* \brief +* Provides APIs for transmitting messages to or from the board via standard +* printf/scanf functions. Messages are transmitted over a UART connection which +* is generally connected to a host machine. Transmission is done at 115200 baud +* using the tx and rx pins provided by the user of this library. The UART +* instance is made available via cy_retarget_io_uart_obj in case any changes +* to the default configuration are desired. +* NOTE: If the application is built using newlib-nano, by default, floating +* point format strings (%f) are not supported. To enable this support you must +* add '-u _printf_float' to the linker command line. +* +******************************************************************************** +* \copyright +* Copyright 2018-2019 Cypress Semiconductor Corporation +* SPDX-License-Identifier: Apache-2.0 +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*******************************************************************************/ + +#pragma once + +#include +#include "cy_result.h" +#include "cy_pdl.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/** UART baud rate */ +#define CY_RETARGET_IO_BAUDRATE (115200) + +/** Defining this macro enables conversion of line feed (LF) into carriage + * return followed by line feed (CR & LF) on the output direction (STDOUT). You + * can define this macro through the DEFINES variable in the application + * Makefile. + */ +#define CY_RETARGET_IO_CONVERT_LF_TO_CRLF + +cy_rslt_t cy_retarget_io_pdl_init(uint32_t baudrate); + +void cy_retarget_io_wait_tx_complete(CySCB_Type *base, uint32_t tries_count); + +void cy_retarget_io_pdl_deinit(void); + +#if defined(__cplusplus) +} +#endif + diff --git a/bootloader/mcuboot/boot/cypress/toolchains.mk b/bootloader/mcuboot/boot/cypress/toolchains.mk new file mode 100644 index 0000000..11c1413 --- /dev/null +++ b/bootloader/mcuboot/boot/cypress/toolchains.mk @@ -0,0 +1,107 @@ +################################################################################ +# \file toolchains.mk +# \version 1.0 +# +# \brief +# Makefile to describe supported toolchains for Cypress MCUBoot based applications. +# +################################################################################ +# \copyright +# Copyright 2018-2019 Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ + +include common_libs.mk + +# Compilers +GCC_ARM := 1 +IAR := 2 +ARM := 3 +OTHER := 4 + +ifeq ($(MAKEINFO), 1) +$(info $(COMPILER)) +endif +# Detect host OS to make resolving compiler pathes easier +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S), Darwin) + HOST_OS = osx +else + ifeq ($(UNAME_S), Linux) + HOST_OS = linux + else + HOST_OS = win + endif +endif + +# Path to the compiler installation +# NOTE: Absolute pathes for now for the sake of development +ifeq ($(HOST_OS), win) + ifeq ($(COMPILER), GCC_ARM) + TOOLCHAIN_PATH ?= c:/Users/$(USERNAME)/ModusToolbox/tools_2.2/gcc + MY_TOOLCHAIN_PATH:=$(subst \,/,$(TOOLCHAIN_PATH)) + TOOLCHAIN_PATH := $(MY_TOOLCHAIN_PATH) + GCC_PATH := $(TOOLCHAIN_PATH) + # executables + CC := "$(GCC_PATH)/bin/arm-none-eabi-gcc" + LD := $(CC) + endif + +else ifeq ($(HOST_OS), osx) + TOOLCHAIN_PATH ?= /opt/gcc-arm-none-eabi + GCC_PATH := $(TOOLCHAIN_PATH) + + CC := "$(GCC_PATH)/bin/arm-none-eabi-gcc" + LD := $(CC) + +else ifeq ($(HOST_OS), linux) + TOOLCHAIN_PATH ?= /opt/gcc-arm-none-eabi + GCC_PATH := $(TOOLCHAIN_PATH) + # executables + CC := "$(GCC_PATH)/bin/arm-none-eabi-gcc" + LD := $(CC) +endif + +PDL_ELFTOOL := "hal/tools/$(HOST_OS)/elf/cymcuelftool" + +OBJDUMP := "$(GCC_PATH)/bin/arm-none-eabi-objdump" +OBJCOPY := "$(GCC_PATH)/bin/arm-none-eabi-objcopy" + +# Set flags for toolchain executables +ifeq ($(COMPILER), GCC_ARM) + # set build-in compiler flags + CFLAGS_COMMON := -mcpu=cortex-$(CORE_SIFFX) -mthumb -mfloat-abi=soft -fno-stack-protector -ffunction-sections -fdata-sections -ffat-lto-objects -fstrict-aliasing -g -Wall -Wextra + ifeq ($(BUILDCFG), Debug) + CFLAGS_COMMON += -Og -g3 + else ifeq ($(BUILDCFG), Release) + CFLAGS_COMMON += -Os -g + else +$(error BUILDCFG : '$(BUILDCFG)' is not supported) + endif + # add defines and includes + CFLAGS := $(CFLAGS_COMMON) $(INCLUDES) + CC_DEPEND = -MD -MP -MF + + LDFLAGS_COMMON := -mcpu=cortex-$(CORE_SIFFX) -mthumb -specs=nano.specs -ffunction-sections -fdata-sections -Wl,--gc-sections -L "$(GCC_PATH)/lib/gcc/arm-none-eabi/7.2.1/thumb/v6-m" -ffat-lto-objects -g --enable-objc-gc + ifeq ($(BUILDCFG), Debug) + LDFLAGS_COMMON += -Og + else ifeq ($(BUILDCFG), Release) + LDFLAGS_COMMON += -Os + else +$(error BUILDCFG : '$(BUILDCFG)' is not supported) + endif + LDFLAGS_NANO := -L "$(GCC_PATH)/arm-none-eabi/lib/thumb/v6-m" + LDFLAGS := $(LDFLAGS_COMMON) $(LDFLAGS_NANO) +endif diff --git a/bootloader/mcuboot/boot/espressif/CMakeLists.txt b/bootloader/mcuboot/boot/espressif/CMakeLists.txt new file mode 100644 index 0000000..bc703fc --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/CMakeLists.txt @@ -0,0 +1,409 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13) +cmake_policy(SET CMP0109 NEW) + +include(${CMAKE_CURRENT_LIST_DIR}/tools/utils.cmake) + +if (NOT DEFINED MCUBOOT_TARGET) + message(FATAL_ERROR "MCUBOOT_TARGET not defined. Please pass -DMCUBOOT_TARGET flag.") +endif() + +add_definitions(-DMCUBOOT_TARGET=${MCUBOOT_TARGET}) +add_definitions(-D__ESPRESSIF__=1) + +if ("${MCUBOOT_TARGET}" STREQUAL "esp32" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32s2" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32s3") + set(MCUBOOT_ARCH "xtensa") +elseif("${MCUBOOT_TARGET}" STREQUAL "esp32c3" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32c6" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32c2" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32h2") + set(MCUBOOT_ARCH "riscv") +endif() + +if (NOT DEFINED CMAKE_TOOLCHAIN_FILE) + if (DEFINED TOOLCHAIN_BIN_DIR) + message("CMAKE_TOOLCHAIN_FILE not defined, searching for toolchain compiler in TOOLCHAIN_BIN_DIR: ${TOOLCHAIN_BIN_DIR}") + set(CMAKE_SYSTEM_NAME Generic) + + file(GLOB C_COMPILER_BIN "${TOOLCHAIN_BIN_DIR}/*${MCUBOOT_ARCH}*elf-gcc") + if (NOT C_COMPILER_BIN) + message(FATAL_ERROR "No C compiler found. Please ensure that TOOLCHAIN_BIN_DIR directory contains a set of C compiling tools compatible with the target") + endif() + set(CMAKE_C_COMPILER ${C_COMPILER_BIN}) + set(CMAKE_ASM_COMPILER ${C_COMPILER_BIN}) + message("C compiler found: ${CMAKE_C_COMPILER}") + + file(GLOB CXX_COMPILER_BIN "${TOOLCHAIN_BIN_DIR}/*${MCUBOOT_ARCH}*elf-g++") + if (NOT CXX_COMPILER_BIN) + message(FATAL_ERROR "No C++ compiler found. Please ensure that TOOLCHAIN_BIN_DIR directory contains a set of C++ compiling tools compatible with the target") + endif() + set(CMAKE_CXX_COMPILER ${CXX_COMPILER_BIN}) + message("CXX compiler found: ${CMAKE_CXX_COMPILER}") + else() + # Set toolchain file that expect the same toolchain as IDF sets on PATH + set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/tools/toolchain-${MCUBOOT_TARGET}.cmake) + message("No user-defined toolchain, setting default toolchain file: ${CMAKE_TOOLCHAIN_FILE}") + endif() + + # This flag is needed when redefining a different compiler toolchain at this point + # on CMakeLists, the reason is that CMake does a compiler testing prior to building + # that may fail due to cross-compilation + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") +else() + message("CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}") +endif() + +project(mcuboot_${MCUBOOT_TARGET}) + +# Set the minimum revision for each supported chip +if ("${MCUBOOT_TARGET}" STREQUAL "esp32") + set(ESP_MIN_REVISION 3) +elseif("${MCUBOOT_TARGET}" STREQUAL "esp32s2") + set(ESP_MIN_REVISION 0) +elseif("${MCUBOOT_TARGET}" STREQUAL "esp32s3") + set(ESP_MIN_REVISION 0) +elseif("${MCUBOOT_TARGET}" STREQUAL "esp32c3") + set(ESP_MIN_REVISION 3) +elseif("${MCUBOOT_TARGET}" STREQUAL "esp32c6") + set(ESP_MIN_REVISION 0) +elseif("${MCUBOOT_TARGET}" STREQUAL "esp32c2") + set(ESP_MIN_REVISION 0) +elseif("${MCUBOOT_TARGET}" STREQUAL "esp32h2") + set(ESP_MIN_REVISION 0) +else() + message(FATAL_ERROR "Unsupported target ${MCUBOOT_TARGET}") +endif() + +if (NOT DEFINED ESP_HAL_PATH) + if (DEFINED ENV{ESP_HAL_PATH}) + set(ESP_HAL_PATH $ENV{ESP_HAL_PATH}) + else() + message(WARNING "ESP_HAL_PATH not defined, checking if IDF_PATH exists.") + if (DEFINED ENV{IDF_PATH}) + set(ESP_HAL_PATH $ENV{IDF_PATH}) + message("IDF installation found in the system, using IDF_PATH as ESP_HAL_PATH.") + else () + message(FATAL_ERROR "Please set -DESP_HAL_PATH parameter or define ESP_HAL_PATH environment variable.") + endif() + endif() +endif() + +execute_process( + COMMAND git describe --tags + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + OUTPUT_VARIABLE MCUBOOT_VER + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +add_definitions(-DMCUBOOT_VER=\"${MCUBOOT_VER}\") + +if (NOT DEFINED MCUBOOT_CONFIG_FILE) + set(MCUBOOT_CONFIG_FILE "${CMAKE_CURRENT_LIST_DIR}/port/${MCUBOOT_TARGET}/bootloader.conf") +endif() + +string(REPLACE " " ";" MCUBOOT_CONFIG_FILE_LIST "${MCUBOOT_CONFIG_FILE}") +foreach(CONFIG_FILE ${MCUBOOT_CONFIG_FILE_LIST}) + if (NOT EXISTS "${CONFIG_FILE}") + message(FATAL_ERROR "MCUboot configuration file does not exist at ${CONFIG_FILE}") + endif() + parse_and_set_config_file(${CONFIG_FILE}) +endforeach() + +set(APP_NAME mcuboot_${MCUBOOT_TARGET}) +set(APP_EXECUTABLE ${APP_NAME}.elf) + +set(MCUBOOT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set(BOOTUTIL_DIR ${MCUBOOT_ROOT_DIR}/boot/bootutil) +set(BOOT_SERIAL_DIR ${MCUBOOT_ROOT_DIR}/boot/boot_serial) +set(ZCBOR_DIR ${MCUBOOT_ROOT_DIR}/boot/zcbor) +set(ESPRESSIF_PORT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# Find imgtool. +# Go with an explicitly installed imgtool first, falling +# back to mcuboot/scripts/imgtool.py. +find_program(IMGTOOL_COMMAND + NAMES imgtool imgtool.py + ) +if ("${IMGTOOL_COMMAND}" MATCHES "IMGTOOL_COMMAND-NOTFOUND") + set(imgtool_path "${MCUBOOT_ROOT_DIR}/scripts/imgtool.py") +else() + set(imgtool_path "${IMGTOOL_COMMAND}") +endif() + +# Find installed esptool, if not found falls to IDF's +find_program(ESPTOOL_COMMAND + NAMES esptool esptool.py + ) +if ("${ESPTOOL_COMMAND}" MATCHES "ESPTOOL_COMMAND-NOTFOUND") + if (DEFINED ENV{IDF_PATH}) + set(esptool_path "${IDF_PATH}/components/esptool_py/esptool/esptool.py") + else() + message(FATAL_ERROR "esptool.py not found. Please install it using \'pip install esptool\'.") + endif() +else() + set(esptool_path "${ESPTOOL_COMMAND}") +endif() + +# Flash frequency parameter for esptool.py, for more information, check `esptool.py -h` +if (NOT DEFINED ESP_FLASH_FREQ) + if ("${MCUBOOT_TARGET}" STREQUAL "esp32" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32s2" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32s3" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32c3" OR + "${MCUBOOT_TARGET}" STREQUAL "esp32c6") + set(ESP_FLASH_FREQ "40m") + elseif("${MCUBOOT_TARGET}" STREQUAL "esp32c2") + set(ESP_FLASH_FREQ "60m") + elseif("${MCUBOOT_TARGET}" STREQUAL "esp32h2") + set(ESP_FLASH_FREQ "24m") + endif() +endif() + +# Flash mode parameter for esptool.py, for more information, check `esptool.py -h` +if (NOT DEFINED ESP_FLASH_MODE) + set(ESP_FLASH_MODE "dio") +endif() + +# Serial baud rate parameter for esptool.py flash use, for more information, check `esptool.py -h` +if (NOT DEFINED ESP_BAUD_RATE) + set(ESP_BAUD_RATE 115200) +endif() + +if (DEFINED CONFIG_ESP_SIGN_RSA) + include(${CMAKE_CURRENT_LIST_DIR}/include/crypto_config/rsa.cmake) +elseif (DEFINED CONFIG_ESP_SIGN_EC256) + include(${CMAKE_CURRENT_LIST_DIR}/include/crypto_config/ec256.cmake) +elseif (DEFINED CONFIG_ESP_SIGN_ED25519) + include(${CMAKE_CURRENT_LIST_DIR}/include/crypto_config/ed25519.cmake) +else() + # No signature verification + set(TINYCRYPT_DIR ${MCUBOOT_ROOT_DIR}/ext/tinycrypt/lib) + set(CRYPTO_INC + ${TINYCRYPT_DIR}/include + ) + set(crypto_srcs + ${TINYCRYPT_DIR}/source/sha256.c + ${TINYCRYPT_DIR}/source/utils.c + ) +endif() + +if(DEFINED CONFIG_ESP_SIGN_KEY_FILE) + if(IS_ABSOLUTE ${CONFIG_ESP_SIGN_KEY_FILE}) + set(KEY_FILE ${CONFIG_ESP_SIGN_KEY_FILE}) + else() + set(KEY_FILE ${MCUBOOT_ROOT_DIR}/${CONFIG_ESP_SIGN_KEY_FILE}) + endif() + message("MCUBoot bootloader key file: ${KEY_FILE}") + + set(GENERATED_PUBKEY ${CMAKE_CURRENT_BINARY_DIR}/autogen-pubkey.c) + add_custom_command( + OUTPUT ${GENERATED_PUBKEY} + COMMAND + ${imgtool_path} + getpub + -k + ${KEY_FILE} + > ${GENERATED_PUBKEY} + DEPENDS ${KEY_FILE} + ) + list(APPEND crypto_srcs ${GENERATED_PUBKEY}) +endif() + +set(bootutil_srcs + ${BOOTUTIL_DIR}/src/boot_record.c + ${BOOTUTIL_DIR}/src/bootutil_misc.c + ${BOOTUTIL_DIR}/src/bootutil_public.c + ${BOOTUTIL_DIR}/src/caps.c + ${BOOTUTIL_DIR}/src/encrypted.c + ${BOOTUTIL_DIR}/src/fault_injection_hardening.c + ${BOOTUTIL_DIR}/src/fault_injection_hardening_delay_rng_mbedtls.c + ${BOOTUTIL_DIR}/src/image_ecdsa.c + ${BOOTUTIL_DIR}/src/image_ed25519.c + ${BOOTUTIL_DIR}/src/image_rsa.c + ${BOOTUTIL_DIR}/src/image_validate.c + ${BOOTUTIL_DIR}/src/loader.c + ${BOOTUTIL_DIR}/src/swap_misc.c + ${BOOTUTIL_DIR}/src/swap_move.c + ${BOOTUTIL_DIR}/src/swap_scratch.c + ${BOOTUTIL_DIR}/src/tlv.c + ) +set(bootutil_paths) + +set(CFLAGS + "-Wno-frame-address" + "-Wall" + "-Wextra" + "-W" + "-Wdeclaration-after-statement" + "-Wwrite-strings" + "-Wlogical-op" + "-Wshadow" + "-ffunction-sections" + "-fdata-sections" + "-fstrict-volatile-bitfields" + "-Werror=all" + "-Wno-error=unused-function" + "-Wno-error=unused-but-set-variable" + "-Wno-error=unused-variable" + "-Wno-error=deprecated-declarations" + "-Wno-unused-parameter" + "-Wno-sign-compare" + "-ggdb" + "-Os" + "-D_GNU_SOURCE" + "-std=gnu17" + "-Wno-old-style-declaration" + "-Wno-implicit-int" + "-Wno-declaration-after-statement" + ) + +set(LDFLAGS + "-nostdlib" + "-Wno-frame-address" + "-Wl,--cref" + "-Wl,--Map=${APP_NAME}.map" + "-fno-rtti" + "-fno-lto" + "-Wl,--gc-sections" + "-Wl,--undefined=uxTopUsedPriority" + "-lm" + "-lgcc" + "-lgcov" + ) + +if ("${MCUBOOT_ARCH}" STREQUAL "xtensa") + list(APPEND CFLAGS + "-mlongcalls" + ) + list(APPEND LDFLAGS + "-mlongcalls" + ) +endif() + +add_subdirectory(hal) +add_executable( + ${APP_EXECUTABLE} + ${CMAKE_CURRENT_LIST_DIR}/main.c + ) + +target_compile_options( + ${APP_EXECUTABLE} + PUBLIC + ${CFLAGS} + ) + +set(port_srcs + ${CMAKE_CURRENT_LIST_DIR}/port/esp_mcuboot.c + ${CMAKE_CURRENT_LIST_DIR}/port/esp_loader.c + ${CMAKE_CURRENT_LIST_DIR}/os.c + ) + +if(CONFIG_ESP_MCUBOOT_SERIAL) + set(MBEDTLS_DIR "${MCUBOOT_ROOT_DIR}/ext/mbedtls") + + list(APPEND bootutil_srcs + ${BOOT_SERIAL_DIR}/src/boot_serial.c + ${BOOT_SERIAL_DIR}/src/zcbor_bulk.c + ${ZCBOR_DIR}/src/zcbor_decode.c + ${ZCBOR_DIR}/src/zcbor_encode.c + ${ZCBOR_DIR}/src/zcbor_common.c + ) + list(APPEND bootutil_paths + ${ZCBOR_DIR}/include + ) + list(APPEND port_srcs + ${CMAKE_CURRENT_LIST_DIR}/port/${MCUBOOT_TARGET}/serial_adapter.c + ${MBEDTLS_DIR}/library/base64.c + ) + list(APPEND CRYPTO_INC + ${MBEDTLS_DIR}/include + ) +endif() + +target_sources( + ${APP_EXECUTABLE} + PUBLIC + ${bootutil_srcs} + ${crypto_srcs} + ${port_srcs} + ) + +target_include_directories( + ${APP_EXECUTABLE} + PUBLIC + ${BOOTUTIL_DIR}/include + ${BOOTUTIL_DIR}/src + ${BOOT_SERIAL_DIR}/include + ${CRYPTO_INC} + ${CMAKE_CURRENT_LIST_DIR}/include + ${bootutil_paths} + ) + +set(ld_input ${CMAKE_CURRENT_LIST_DIR}/port/${MCUBOOT_TARGET}/ld/bootloader.ld) +set(ld_output ${CMAKE_CURRENT_BINARY_DIR}/ld/bootloader.ld) + +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/ld") + +get_directory_property(configs COMPILE_DEFINITIONS) +foreach(c ${configs}) + list(APPEND conf_defines "-D${c}") +endforeach() + +# Preprocess linker script +add_custom_command( + TARGET ${APP_EXECUTABLE} PRE_LINK + COMMAND ${CMAKE_C_COMPILER} -x c -E -P -o ${ld_output} ${conf_defines} ${ld_input} + MAIN_DEPENDENCY ${ld_input} + COMMENT "Preprocessing bootloader.ld linker script..." + ) + +target_link_libraries( + ${APP_EXECUTABLE} + PUBLIC + -T${ld_output} + ${LDFLAGS} + ) + +target_link_libraries( + ${APP_EXECUTABLE} + PUBLIC + hal + ) + +# This step uses esptool.py for generating the final bootloader binary in +# Espressif compatible format. +# Note: Both binary generation and flash steps still have some default arguments +add_custom_command(TARGET ${APP_EXECUTABLE} POST_BUILD + COMMAND + ${esptool_path} + --chip ${MCUBOOT_TARGET} elf2image --min-rev ${ESP_MIN_REVISION} + --flash_mode ${ESP_FLASH_MODE} --flash_freq ${ESP_FLASH_FREQ} --flash_size ${CONFIG_ESP_FLASH_SIZE} + -o ${APP_NAME}.bin ${APP_NAME}.elf + ) + +if (DEFINED MCUBOOT_FLASH_PORT) + set(FLASH_PORT ${MCUBOOT_FLASH_PORT}) +else() + # Defaults to the first USB serial port + set(FLASH_PORT "/dev/ttyUSB0") +endif() + +if (NOT EXISTS "${FLASH_PORT}") + message(WARNING "Could not open ${FLASH_PORT}, serial port does not exist") +endif() + +add_custom_target(flash DEPENDS ${APP_NAME}.bin) +add_custom_command(TARGET flash + USES_TERMINAL + COMMAND + ${esptool_path} + -p ${FLASH_PORT} -b ${ESP_BAUD_RATE} --before default_reset --after no_reset + --chip ${MCUBOOT_TARGET} write_flash + --flash_mode ${ESP_FLASH_MODE} --flash_size ${CONFIG_ESP_FLASH_SIZE} + --flash_freq ${ESP_FLASH_FREQ} ${CONFIG_ESP_BOOTLOADER_OFFSET} + ${APP_NAME}.bin + ) diff --git a/bootloader/mcuboot/boot/espressif/ci_configs/multi-boot.conf b/bootloader/mcuboot/boot/espressif/ci_configs/multi-boot.conf new file mode 100644 index 0000000..4adf253 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/ci_configs/multi-boot.conf @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# ATTENTION: +# This configuration file targets the building for CI environment and contains +# a set of definitions to resemble a bootloader image for RELEASE environment. + +CONFIG_ESP_IMAGE_NUMBER=2 +CONFIG_ESP_MULTI_PROCESSOR_BOOT=y diff --git a/bootloader/mcuboot/boot/espressif/ci_configs/multi-image.conf b/bootloader/mcuboot/boot/espressif/ci_configs/multi-image.conf new file mode 100644 index 0000000..895a865 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/ci_configs/multi-image.conf @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# ATTENTION: +# This configuration file targets the building for CI environment and contains +# a set of definitions to resemble a bootloader image for RELEASE environment. + +CONFIG_ESP_IMAGE_NUMBER=2 diff --git a/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ec256.conf b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ec256.conf new file mode 100644 index 0000000..17b032b --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ec256.conf @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# ATTENTION: +# This configuration file targets the building for CI environment and contains +# a set of definitions to resemble a bootloader image for RELEASE environment. +# Running the generated firmware image may result in irreversible operations +# to the chip! + +CONFIG_SECURE_SIGNED_ON_BOOT=1 +CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +CONFIG_SECURE_BOOT=1 +CONFIG_SECURE_BOOT_V2_ENABLED=1 +CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 +CONFIG_SECURE_FLASH_ENC_ENABLED=1 +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=1 +CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem +CONFIG_ESP_USE_TINYCRYPT=1 +CONFIG_ESP_SIGN_EC256=1 +CONFIG_ESP_DOWNGRADE_PREVENTION=1 +CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=1 diff --git a/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ed25519.conf b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ed25519.conf new file mode 100644 index 0000000..9cbdcd6 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-ed25519.conf @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# ATTENTION: +# This configuration file targets the building for CI environment and contains +# a set of definitions to resemble a bootloader image for RELEASE environment. +# Running the generated firmware image may result in irreversible operations +# to the chip! + +CONFIG_SECURE_SIGNED_ON_BOOT=1 +CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +CONFIG_SECURE_BOOT=1 +CONFIG_SECURE_BOOT_V2_ENABLED=1 +CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 +CONFIG_SECURE_FLASH_ENC_ENABLED=1 +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=1 +CONFIG_ESP_SIGN_KEY_FILE=root-ed25519.pem +CONFIG_ESP_USE_TINYCRYPT=1 +CONFIG_ESP_SIGN_ED25519=1 +CONFIG_ESP_DOWNGRADE_PREVENTION=1 +CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=1 diff --git a/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa2048.conf b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa2048.conf new file mode 100644 index 0000000..2b22150 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa2048.conf @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# ATTENTION: +# This configuration file targets the building for CI environment and contains +# a set of definitions to resemble a bootloader image for RELEASE environment. +# Running the generated firmware image may result in irreversible operations +# to the chip! + +CONFIG_SECURE_SIGNED_ON_BOOT=1 +CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +CONFIG_SECURE_BOOT=1 +CONFIG_SECURE_BOOT_V2_ENABLED=1 +CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 +CONFIG_SECURE_FLASH_ENC_ENABLED=1 +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=1 +CONFIG_ESP_SIGN_KEY_FILE=root-rsa-2048.pem +CONFIG_ESP_USE_MBEDTLS=1 +CONFIG_ESP_SIGN_RSA=1 +CONFIG_ESP_SIGN_RSA_LEN=2048 +CONFIG_ESP_DOWNGRADE_PREVENTION=1 +CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=1 diff --git a/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa3072.conf b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa3072.conf new file mode 100644 index 0000000..9f13785 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/ci_configs/secureboot-sign-rsa3072.conf @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# ATTENTION: +# This configuration file targets the building for CI environment and contains +# a set of definitions to resemble a bootloader image for RELEASE environment. +# Running the generated firmware image may result in irreversible operations +# to the chip! + +CONFIG_SECURE_SIGNED_ON_BOOT=1 +CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +CONFIG_SECURE_BOOT=1 +CONFIG_SECURE_BOOT_V2_ENABLED=1 +CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 +CONFIG_SECURE_FLASH_ENC_ENABLED=1 +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=1 +CONFIG_ESP_SIGN_KEY_FILE=root-rsa-3072.pem +CONFIG_ESP_USE_MBEDTLS=1 +CONFIG_ESP_SIGN_RSA=1 +CONFIG_ESP_SIGN_RSA_LEN=3072 +CONFIG_ESP_DOWNGRADE_PREVENTION=1 +CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=1 diff --git a/bootloader/mcuboot/boot/espressif/ci_configs/serialrecovery.conf b/bootloader/mcuboot/boot/espressif/ci_configs/serialrecovery.conf new file mode 100644 index 0000000..7e81bde --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/ci_configs/serialrecovery.conf @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# ATTENTION: +# This configuration file targets the building for CI environment and contains +# a set of definitions to resemble a bootloader image for RELEASE environment. + +CONFIG_ESP_MCUBOOT_SERIAL=y +CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y diff --git a/bootloader/mcuboot/boot/espressif/hal/CMakeLists.txt b/bootloader/mcuboot/boot/espressif/hal/CMakeLists.txt new file mode 100644 index 0000000..d248c26 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/CMakeLists.txt @@ -0,0 +1,213 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13) + +project(hal) + +set(esp_hal_dir ${ESP_HAL_PATH}) +set(src_dir ${CMAKE_CURRENT_LIST_DIR}/src) +set(include_dirs + ${CMAKE_CURRENT_LIST_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}/include/${MCUBOOT_TARGET} + ) + +list(APPEND include_dirs + ${esp_hal_dir}/components/bootloader_support/include + ${esp_hal_dir}/components/bootloader_support/private_include + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/include + ${esp_hal_dir}/components/spi_flash/include + ${esp_hal_dir}/components/spi_flash/include/spi_flash + ${esp_hal_dir}/components/esp_app_format/include + ${esp_hal_dir}/components/newlib/platform_include + ${esp_hal_dir}/components/esp_common/include + ${esp_hal_dir}/components/${MCUBOOT_ARCH}/include + ${esp_hal_dir}/components/esp_rom/include + ${esp_hal_dir}/components/esp_rom/include/${MCUBOOT_TARGET} + ${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET} + ${esp_hal_dir}/components/soc/include + ${esp_hal_dir}/components/soc/${MCUBOOT_TARGET} + ${esp_hal_dir}/components/soc/${MCUBOOT_TARGET}/include + ${esp_hal_dir}/components/efuse/include + ${esp_hal_dir}/components/efuse/${MCUBOOT_TARGET}/include + ${esp_hal_dir}/components/efuse/private_include + ${esp_hal_dir}/components/efuse/${MCUBOOT_TARGET}/private_include + ${esp_hal_dir}/components/esp_hw_support/include + ${esp_hal_dir}/components/esp_hw_support/include/soc + ${esp_hal_dir}/components/esp_hw_support/include/soc/${MCUBOOT_TARGET} + ${esp_hal_dir}/components/esp_hw_support/port/include + ${esp_hal_dir}/components/esp_hw_support/include/esp_private + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET} + ${esp_hal_dir}/components/hal/${MCUBOOT_TARGET}/include + ${esp_hal_dir}/components/hal/include + ${esp_hal_dir}/components/hal/platform_port/include + ${esp_hal_dir}/components/esp_system/include + ${esp_hal_dir}/components/log/include + ) + +if("${MCUBOOT_ARCH}" STREQUAL "xtensa") + list(APPEND include_dirs + ${esp_hal_dir}/components/${MCUBOOT_ARCH}/${MCUBOOT_TARGET}/include + ${esp_hal_dir}/components/${MCUBOOT_ARCH}/include + ) +endif() + +set(hal_srcs + ${esp_hal_dir}/components/bootloader_support/src/${MCUBOOT_TARGET}/bootloader_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_init.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_common.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_common_loader.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_console.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_console_loader.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash_config_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_clock_init.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_clock_loader.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_efuse.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_panic.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_mem.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_random.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_random_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/bootloader_support/src/bootloader_utility.c + ${esp_hal_dir}/components/bootloader_support/src/esp_image_format.c + ${esp_hal_dir}/components/bootloader_support/src/${MCUBOOT_TARGET}/bootloader_soc.c + ${esp_hal_dir}/components/bootloader_support/src/${MCUBOOT_TARGET}/bootloader_sha.c + ${esp_hal_dir}/components/hal/mpu_hal.c + ${esp_hal_dir}/components/hal/efuse_hal.c + ${esp_hal_dir}/components/hal/mmu_hal.c + ${esp_hal_dir}/components/hal/wdt_hal_iram.c + ${esp_hal_dir}/components/hal/${MCUBOOT_TARGET}/efuse_hal.c + ${esp_hal_dir}/components/soc/${MCUBOOT_TARGET}/uart_periph.c + ${esp_hal_dir}/components/soc/${MCUBOOT_TARGET}/gpio_periph.c + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_time.c + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_clk.c + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_clk_init.c + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_uart.c + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_sys.c + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_spiflash.c + ${esp_hal_dir}/components/efuse/${MCUBOOT_TARGET}/esp_efuse_table.c + ${esp_hal_dir}/components/efuse/src/esp_efuse_fields.c + ${esp_hal_dir}/components/efuse/${MCUBOOT_TARGET}/esp_efuse_fields.c + ${esp_hal_dir}/components/efuse/src/esp_efuse_api.c + ${esp_hal_dir}/components/efuse/src/esp_efuse_utility.c + ${esp_hal_dir}/components/efuse/${MCUBOOT_TARGET}/esp_efuse_utility.c + ${esp_hal_dir}/components/log/log_noos.c + ${src_dir}/bootloader_banner.c + ${src_dir}/bootloader_wdt.c + ) + +if(DEFINED CONFIG_SECURE_BOOT_V2_ENABLED) + list(APPEND hal_srcs + ${src_dir}/secure_boot.c + ${esp_hal_dir}/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_bootloader.c + ${esp_hal_dir}/components/bootloader_support/src/${MCUBOOT_TARGET}/secure_boot_secure_features.c + ) + list(APPEND include_dirs + ${esp_hal_dir}/components/bootloader_support/src/secure_boot_v2 + ) +endif() + +if(DEFINED CONFIG_SECURE_FLASH_ENC_ENABLED) + list(APPEND hal_srcs + ${src_dir}/flash_encrypt.c + ${esp_hal_dir}/components/bootloader_support/src/${MCUBOOT_TARGET}/flash_encryption_secure_features.c + ) + set_source_files_properties( + ${src_dir}/flash_encrypt.c + PROPERTIES COMPILE_FLAGS + "-Wno-unused-variable" + ) +endif() + +if("${MCUBOOT_ARCH}" STREQUAL "xtensa") + list(APPEND hal_srcs + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_longjmp.S + ) +endif() + +set(CFLAGS + "-nostdlib" + "-Wno-frame-address" + "-Wall" + "-Wextra" + "-W" + "-Wwrite-strings" + "-Wlogical-op" + "-Wshadow" + "-ffunction-sections" + "-fdata-sections" + "-fstrict-volatile-bitfields" + "-Werror=all" + "-Wno-error=unused-function" + "-Wno-error=unused-but-set-variable" + "-Wno-error=unused-variable" + "-Wno-error=deprecated-declarations" + "-Wno-unused-parameter" + "-Wno-sign-compare" + "-ggdb" + "-Os" + "-D_GNU_SOURCE" + "-std=gnu17" + "-Wno-old-style-declaration" + "-Wno-implicit-int" + ) + +set(LDFLAGS + "-Wno-frame-address" + "-Wl,--cref" + "-Wl,--Map=${APP_NAME}.map" + "-fno-rtti" + "-fno-lto" + "-Wl,--gc-sections" + "-Wl,--undefined=uxTopUsedPriority" + "-lm" + "-lgcc" + "-lgcov" + ) + +if("${MCUBOOT_ARCH}" STREQUAL "xtensa") + list(APPEND CFLAGS + "-mlongcalls" + ) + list(APPEND LDFLAGS + "-mlongcalls" + ) +endif() + +set(LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.ld + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.libgcc.ld + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.api.ld + -T${esp_hal_dir}/components/soc/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.peripherals.ld + ) + +include(${CMAKE_CURRENT_LIST_DIR}/include/${MCUBOOT_TARGET}/${MCUBOOT_TARGET}.cmake) + +add_library(hal STATIC ${hal_srcs} ${include_dirs}) + +# Wrap for overriding the print banner function from bootloader_support +add_definitions(-DIDF_VER=0) +target_link_libraries( + hal + INTERFACE + "-Wl,--wrap=bootloader_print_banner") + +target_include_directories( + hal + PUBLIC + ${include_dirs} + ) + +target_compile_options( + hal + PUBLIC + ${CFLAGS} + ) + +target_link_libraries( + hal + PUBLIC + ${LDFLAGS} + ${LINKER_SCRIPTS} + ) diff --git a/bootloader/mcuboot/boot/espressif/hal/include/app_cpu_start.h b/bootloader/mcuboot/boot/espressif/hal/include/app_cpu_start.h new file mode 100644 index 0000000..03c5c77 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/app_cpu_start.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +void appcpu_start(uint32_t entry_addr); diff --git a/bootloader/mcuboot/boot/espressif/hal/include/bootloader_wdt.h b/bootloader/mcuboot/boot/espressif/hal/include/bootloader_wdt.h new file mode 100644 index 0000000..e5ac551 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/bootloader_wdt.h @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +void bootloader_wdt_feed(void); diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32/esp32.cmake b/bootloader/mcuboot/boot/espressif/hal/include/esp32/esp32.cmake new file mode 100644 index 0000000..6e72ced --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32/esp32.cmake @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND hal_srcs + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_init.c + ${esp_hal_dir}/components/efuse/src/efuse_controller/keys/without_key_purposes/three_key_blocks/esp_efuse_api_key.c + ) + +if (DEFINED CONFIG_ESP_MULTI_PROCESSOR_BOOT) + list(APPEND hal_srcs + ${src_dir}/${MCUBOOT_TARGET}/app_cpu_start.c + ${esp_hal_dir}/components/esp_hw_support/cpu.c + ) +endif() + +if (DEFINED CONFIG_ESP_CONSOLE_UART_CUSTOM) + list(APPEND hal_srcs + ${src_dir}/${MCUBOOT_TARGET}/console_uart_custom.c + ) +endif() + +list(APPEND LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.newlib-funcs.ld + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.eco3.ld + ) + +set_source_files_properties( + ${esp_hal_dir}/components/bootloader_support/src/esp32/bootloader_esp32.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash.c + PROPERTIES COMPILE_FLAGS + "-Wno-unused-variable -Wno-unused-but-set-variable") diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32/sdkconfig.h b/bootloader/mcuboot/boot/espressif/hal/include/esp32/sdkconfig.h new file mode 100644 index 0000000..7431e9b --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32/sdkconfig.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BOOTLOADER_BUILD 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x0000 +#define CONFIG_IDF_TARGET_ESP32 1 +#define CONFIG_ESP32_REV_MIN_3 1 +#define CONFIG_ESP32_REV_MIN_FULL 300 +#define CONFIG_ESP_REV_MIN_FULL CONFIG_ESP32_REV_MIN_FULL +#define CONFIG_ESP32_REV_MIN 3 +#define CONFIG_ESP32_REV_MAX_FULL 399 +#define CONFIG_ESP_REV_MAX_FULL CONFIG_ESP32_REV_MAX_FULL +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_MMU_PAGE_SIZE 0x10000 +#define CONFIG_ESP32_XTAL_FREQ 40 +#define CONFIG_XTAL_FREQ 40 +#define CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 1 +#define CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ 160 +#define CONFIG_MCUBOOT 1 +#define NDEBUG 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_BOOTLOADER_OFFSET_IN_FLASH 0x1000 +#define CONFIG_PARTITION_TABLE_OFFSET 0x10000 +#define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000 +#define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000 +#define CONFIG_EFUSE_MAX_BLK_LEN 192 +#define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1 diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32c2/esp32c2.cmake b/bootloader/mcuboot/boot/espressif/hal/include/esp32c2/esp32c2.cmake new file mode 100644 index 0000000..b859f9f --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32c2/esp32c2.cmake @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND hal_srcs + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_init.c + ${esp_hal_dir}/components/hal/cache_hal.c + ${esp_hal_dir}/components/efuse/${MCUBOOT_TARGET}/esp_efuse_table.c + ${esp_hal_dir}/components/efuse/src/efuse_controller/keys/without_key_purposes/one_key_block/esp_efuse_api_key.c +) + +if (DEFINED CONFIG_ESP_CONSOLE_UART_CUSTOM) + list(APPEND hal_srcs + ${src_dir}/${MCUBOOT_TARGET}/console_uart_custom.c + ) +endif() + +list(APPEND LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.newlib.ld +) + +set_source_files_properties( + ${esp_hal_dir}/components/bootloader_support/src/esp_image_format.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash_config_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/hal/mmu_hal.c + ${esp_hal_dir}/components/hal/cache_hal.c + PROPERTIES COMPILE_FLAGS + "-Wno-logical-op") diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32c2/sdkconfig.h b/bootloader/mcuboot/boot/espressif/hal/include/esp32c2/sdkconfig.h new file mode 100644 index 0000000..f71490b --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32c2/sdkconfig.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BOOTLOADER_BUILD 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x000C +#define CONFIG_IDF_TARGET_ESP32C2 1 +#define CONFIG_ESP32C2_REV_MIN_0 1 +#define CONFIG_ESP32C2_REV_MIN_FULL 3 +#define CONFIG_ESP_REV_MIN_FULL CONFIG_ESP32C2_REV_MIN_FULL +#define CONFIG_ESP32C2_REV_MIN 3 +#define CONFIG_ESP32C2_REV_MAX_FULL 99 +#define CONFIG_ESP_REV_MAX_FULL CONFIG_ESP32C2_REV_MAX_FULL +#define CONFIG_IDF_TARGET_ARCH_RISCV 1 +#define CONFIG_MMU_PAGE_SIZE 0x10000 +#define CONFIG_XTAL_FREQ_26 1 +#define CONFIG_XTAL_FREQ 26 +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_MCUBOOT 1 +#define NDEBUG 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_BOOTLOADER_OFFSET_IN_FLASH 0x0000 +#define CONFIG_PARTITION_TABLE_OFFSET 0x10000 +#define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000 +#define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000 +#define CONFIG_EFUSE_MAX_BLK_LEN 256 +#define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1 diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32c3/esp32c3.cmake b/bootloader/mcuboot/boot/espressif/hal/include/esp32c3/esp32c3.cmake new file mode 100644 index 0000000..3d4525a --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32c3/esp32c3.cmake @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND hal_srcs + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_init.c + ${esp_hal_dir}/components/hal/cache_hal.c + ${esp_hal_dir}/components/efuse/src/efuse_controller/keys/with_key_purposes/esp_efuse_api_key.c +) + +if (DEFINED CONFIG_ESP_CONSOLE_UART_CUSTOM) + list(APPEND hal_srcs + ${src_dir}/${MCUBOOT_TARGET}/console_uart_custom.c + ) +endif() + +list(APPEND LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.newlib.ld + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.eco3.ld +) diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32c3/sdkconfig.h b/bootloader/mcuboot/boot/espressif/hal/include/esp32c3/sdkconfig.h new file mode 100644 index 0000000..08c616a --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32c3/sdkconfig.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BOOTLOADER_BUILD 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x0005 +#define CONFIG_IDF_TARGET_ESP32C3 1 +#define CONFIG_ESP32C3_REV_MIN_3 1 +#define CONFIG_ESP32C3_REV_MIN_FULL 3 +#define CONFIG_ESP_REV_MIN_FULL CONFIG_ESP32C3_REV_MIN_FULL +#define CONFIG_ESP32C3_REV_MIN 3 +#define CONFIG_ESP32C3_REV_MAX_FULL 99 +#define CONFIG_ESP_REV_MAX_FULL CONFIG_ESP32C3_REV_MAX_FULL +#define CONFIG_IDF_TARGET_ARCH_RISCV 1 +#define CONFIG_MMU_PAGE_SIZE 0x10000 +#define CONFIG_XTAL_FREQ 40 +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_MCUBOOT 1 +#define NDEBUG 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_BOOTLOADER_OFFSET_IN_FLASH 0x0000 +#define CONFIG_PARTITION_TABLE_OFFSET 0x10000 +#define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000 +#define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000 +#define CONFIG_EFUSE_MAX_BLK_LEN 256 +#define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1 diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32c6/esp32c6.cmake b/bootloader/mcuboot/boot/espressif/hal/include/esp32c6/esp32c6.cmake new file mode 100644 index 0000000..f1b0a49 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32c6/esp32c6.cmake @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND include_dirs + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/private_include +) + +list(APPEND hal_srcs + ${esp_hal_dir}/components/hal/cache_hal.c + ${esp_hal_dir}/components/hal/lp_timer_hal.c + ${esp_hal_dir}/components/efuse/src/efuse_controller/keys/with_key_purposes/esp_efuse_api_key.c + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_hp_regi2c_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/pmu_param.c +) + +if (DEFINED CONFIG_ESP_CONSOLE_UART_CUSTOM) + list(APPEND hal_srcs + ${src_dir}/${MCUBOOT_TARGET}/console_uart_custom.c + ) +endif() + +list(APPEND LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.newlib.ld +) + +set_source_files_properties( + ${esp_hal_dir}/components/bootloader_support/src/esp_image_format.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash_config_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/hal/mmu_hal.c + ${esp_hal_dir}/components/hal/cache_hal.c + PROPERTIES COMPILE_FLAGS + "-Wno-logical-op") diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32c6/sdkconfig.h b/bootloader/mcuboot/boot/espressif/hal/include/esp32c6/sdkconfig.h new file mode 100644 index 0000000..2d79f57 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32c6/sdkconfig.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BOOTLOADER_BUILD 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x000D +#define CONFIG_IDF_TARGET_ESP32C6 1 +#define CONFIG_ESP32C6_REV_MIN_0 1 +#define CONFIG_ESP32C6_REV_MIN_FULL 0 +#define CONFIG_ESP_REV_MIN_FULL CONFIG_ESP32C6_REV_MIN_FULL +#define CONFIG_ESP32C6_REV_MIN 0 +#define CONFIG_ESP32C6_REV_MAX_FULL 99 +#define CONFIG_ESP_REV_MAX_FULL CONFIG_ESP32C6_REV_MAX_FULL +#define CONFIG_IDF_TARGET_ARCH_RISCV 1 +#define CONFIG_MMU_PAGE_SIZE 0x10000 +#define SOC_MMU_PAGE_SIZE CONFIG_MMU_PAGE_SIZE /* from soc/CMakeLists */ +#define CONFIG_XTAL_FREQ 40 +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_MCUBOOT 1 +#define NDEBUG 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_BOOTLOADER_OFFSET_IN_FLASH 0x0000 +#define CONFIG_PARTITION_TABLE_OFFSET 0x10000 +#define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000 +#define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000 +#define CONFIG_EFUSE_MAX_BLK_LEN 256 +#define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1 diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32h2/esp32h2.cmake b/bootloader/mcuboot/boot/espressif/hal/include/esp32h2/esp32h2.cmake new file mode 100644 index 0000000..2507e65 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32h2/esp32h2.cmake @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND include_dirs + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/private_include +) + +list(APPEND hal_srcs + ${esp_hal_dir}/components/hal/cache_hal.c + ${esp_hal_dir}/components/hal/lp_timer_hal.c + ${esp_hal_dir}/components/efuse/src/efuse_controller/keys/with_key_purposes/esp_efuse_api_key.c + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_regi2c_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/pmu_param.c +) + +if (DEFINED CONFIG_ESP_CONSOLE_UART_CUSTOM) + list(APPEND hal_srcs + ${src_dir}/${MCUBOOT_TARGET}/console_uart_custom.c + ) +endif() + +list(APPEND LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.newlib.ld +) + +set_source_files_properties( + ${esp_hal_dir}/components/bootloader_support/src/esp_image_format.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash.c + ${esp_hal_dir}/components/bootloader_support/bootloader_flash/src/bootloader_flash_config_${MCUBOOT_TARGET}.c + ${esp_hal_dir}/components/hal/mmu_hal.c + ${esp_hal_dir}/components/hal/cache_hal.c + PROPERTIES COMPILE_FLAGS + "-Wno-logical-op") diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32h2/sdkconfig.h b/bootloader/mcuboot/boot/espressif/hal/include/esp32h2/sdkconfig.h new file mode 100644 index 0000000..84cbc5b --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32h2/sdkconfig.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BOOTLOADER_BUILD 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x0010 +#define CONFIG_IDF_TARGET_ESP32H2 1 +#define CONFIG_ESP32H2_REV_MIN_0 1 +#define CONFIG_ESP32H2_REV_MIN_FULL 0 +#define CONFIG_ESP_REV_MIN_FULL CONFIG_ESP32H2_REV_MIN_FULL +#define CONFIG_ESP32H2_REV_MIN 0 +#define CONFIG_ESP32H2_REV_MAX_FULL 99 +#define CONFIG_ESP_REV_MAX_FULL CONFIG_ESP32H2_REV_MAX_FULL +#define CONFIG_IDF_TARGET_ARCH_RISCV 1 +#define CONFIG_MMU_PAGE_SIZE 0x10000 +#define SOC_MMU_PAGE_SIZE CONFIG_MMU_PAGE_SIZE /* from soc/CMakeLists */ +#define CONFIG_XTAL_FREQ_32 1 +#define CONFIG_XTAL_FREQ 32 +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_MCUBOOT 1 +#define NDEBUG 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_BOOTLOADER_OFFSET_IN_FLASH 0x0000 +#define CONFIG_PARTITION_TABLE_OFFSET 0x10000 +#define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000 +#define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000 +#define CONFIG_EFUSE_MAX_BLK_LEN 256 +#define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1 diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32s2/esp32s2.cmake b/bootloader/mcuboot/boot/espressif/hal/include/esp32s2/esp32s2.cmake new file mode 100644 index 0000000..4f78e42 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32s2/esp32s2.cmake @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND hal_srcs + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_init.c + ${esp_hal_dir}/components/hal/cache_hal.c + ${esp_hal_dir}/components/efuse/src/efuse_controller/keys/with_key_purposes/esp_efuse_api_key.c + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_crc.c + ${esp_hal_dir}/components/esp_rom/patches/esp_rom_regi2c_esp32s2.c + + ) + +list(APPEND LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.newlib-funcs.ld + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.spiflash.ld + ) + +set_source_files_properties( + ${esp_hal_dir}/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c + PROPERTIES COMPILE_FLAGS + "-Wno-unused-but-set-variable") diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32s2/sdkconfig.h b/bootloader/mcuboot/boot/espressif/hal/include/esp32s2/sdkconfig.h new file mode 100644 index 0000000..d041f96 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32s2/sdkconfig.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BOOTLOADER_BUILD 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x0002 +#define CONFIG_IDF_TARGET_ESP32S2 1 +#define CONFIG_ESP32S2_REV_MIN_0 1 +#define CONFIG_ESP32S2_REV_MIN_FULL 0 +#define CONFIG_ESP_REV_MIN_FULL CONFIG_ESP32S2_REV_MIN_FULL +#define CONFIG_ESP32S2_REV_MIN 0 +#define CONFIG_ESP32S2_REV_MAX_FULL 99 +#define CONFIG_ESP_REV_MAX_FULL CONFIG_ESP32S2_REV_MAX_FULL +#define CONFIG_MMU_PAGE_SIZE 0x10000 +#define CONFIG_XTAL_FREQ 40 +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_ESP32S2_XTAL_FREQ 40 +#define CONFIG_MCUBOOT 1 +#define NDEBUG 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_BOOTLOADER_OFFSET_IN_FLASH 0x1000 +#define CONFIG_PARTITION_TABLE_OFFSET 0x10000 +#define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000 +#define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000 +#define CONFIG_EFUSE_MAX_BLK_LEN 256 +#define CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT 1 diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32s3/esp32s3.cmake b/bootloader/mcuboot/boot/espressif/hal/include/esp32s3/esp32s3.cmake new file mode 100644 index 0000000..b894c07 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32s3/esp32s3.cmake @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND hal_srcs + ${esp_hal_dir}/components/esp_hw_support/port/${MCUBOOT_TARGET}/rtc_init.c + ${esp_hal_dir}/components/hal/cache_hal.c + ${esp_hal_dir}/components/efuse/src/efuse_controller/keys/with_key_purposes/esp_efuse_api_key.c +) + +if (DEFINED CONFIG_ESP_MULTI_PROCESSOR_BOOT) + list(APPEND hal_srcs + ${src_dir}/${MCUBOOT_TARGET}/app_cpu_start.c + ${esp_hal_dir}/components/esp_hw_support/cpu.c + ) +endif() + +list(APPEND LINKER_SCRIPTS + -T${esp_hal_dir}/components/esp_rom/${MCUBOOT_TARGET}/ld/${MCUBOOT_TARGET}.rom.newlib.ld + ) + +set_source_files_properties( + ${esp_hal_dir}/components/bootloader_support/src/esp32s3/bootloader_esp32s3.c + PROPERTIES COMPILE_FLAGS + "-Wno-unused-variable -Wno-unused-but-set-variable") diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp32s3/sdkconfig.h b/bootloader/mcuboot/boot/espressif/hal/include/esp32s3/sdkconfig.h new file mode 100644 index 0000000..25581c8 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp32s3/sdkconfig.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BOOTLOADER_BUILD 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x0009 +#define CONFIG_IDF_TARGET_ESP32S3 1 +#define CONFIG_ESP32S3_REV_MIN_0 1 +#define CONFIG_ESP32S3_REV_MIN_FULL 0 +#define CONFIG_ESP_REV_MIN_FULL CONFIG_ESP32S3_REV_MIN_FULL +#define CONFIG_ESP32S3_REV_MIN 0 +#define CONFIG_ESP32S3_REV_MAX_FULL 99 +#define CONFIG_ESP_REV_MAX_FULL CONFIG_ESP32S3_REV_MAX_FULL +#define CONFIG_MMU_PAGE_SIZE 0x10000 +#define CONFIG_XTAL_FREQ 40 +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_MCUBOOT 1 +#define NDEBUG 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_BOOTLOADER_OFFSET_IN_FLASH 0x0000 +#define CONFIG_PARTITION_TABLE_OFFSET 0x10000 +#define CONFIG_EFUSE_VIRTUAL_OFFSET 0x250000 +#define CONFIG_EFUSE_VIRTUAL_SIZE 0x2000 +#define CONFIG_EFUSE_MAX_BLK_LEN 256 diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp_log.h b/bootloader/mcuboot/boot/espressif/hal/include/esp_log.h new file mode 100644 index 0000000..ad6270d --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp_log.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include + +/* Log levels from IDF are similar to MCUboot's */ + +#ifndef CONFIG_BOOTLOADER_LOG_LEVEL +#define CONFIG_BOOTLOADER_LOG_LEVEL MCUBOOT_LOG_LEVEL +#endif + +#define ESP_LOGE(tag, fmt, ...) MCUBOOT_LOG_ERR("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_LOGW(tag, fmt, ...) MCUBOOT_LOG_WRN("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_LOGI(tag, fmt, ...) MCUBOOT_LOG_INF("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_LOGD(tag, fmt, ...) MCUBOOT_LOG_DBG("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_LOGV(tag, fmt, ...) MCUBOOT_LOG_DBG("[%s] " fmt, tag, ##__VA_ARGS__) + +#define ESP_EARLY_LOGE(tag, fmt, ...) MCUBOOT_LOG_ERR("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGW(tag, fmt, ...) MCUBOOT_LOG_WRN("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGI(tag, fmt, ...) MCUBOOT_LOG_INF("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGD(tag, fmt, ...) MCUBOOT_LOG_DBG("[%s] " fmt, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGV(tag, fmt, ...) MCUBOOT_LOG_DBG("[%s] " fmt, tag, ##__VA_ARGS__) + +uint32_t esp_log_early_timestamp(void); diff --git a/bootloader/mcuboot/boot/espressif/hal/include/esp_mcuboot_image.h b/bootloader/mcuboot/boot/espressif/hal/include/esp_mcuboot_image.h new file mode 100644 index 0000000..baccf08 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/esp_mcuboot_image.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/* Magic is derived from sha256sum of the string "espmcuboot" + * The application header magic must match this number + */ +#define ESP_LOAD_HEADER_MAGIC 0xace637d3 + +/* Load header that should be a part of application image + * for MCUboot-Espressif port booting. + */ +typedef struct esp_image_load_header { + uint32_t header_magic; /* Magic for load header */ + uint32_t entry_addr; /* Application entry address */ + uint32_t iram_dest_addr; /* Destination address(VMA) for IRAM region */ + uint32_t iram_flash_offset; /* Flash offset(LMA) for start of IRAM region */ + uint32_t iram_size; /* Size of IRAM region */ + uint32_t dram_dest_addr; /* Destination address(VMA) for DRAM region */ + uint32_t dram_flash_offset; /* Flash offset(LMA) for start of DRAM region */ + uint32_t dram_size; /* Size of DRAM region */ +} esp_image_load_header_t; diff --git a/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_assert.h b/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_assert.h new file mode 100644 index 0000000..fc59909 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_assert.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +extern void mcuboot_assert_handler(const char *file, int line, const char *func); + +#ifdef assert +#undef assert +#endif +#define assert(arg) \ + do { \ + if (!(arg)) { \ + mcuboot_assert_handler(__FILE__, __LINE__, __func__); \ + } \ + } while(0) diff --git a/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h b/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000..a7058e7 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_CONFIG_H__ +#define __MCUBOOT_CONFIG_H__ + +/* + * Signature types + * + * You must choose exactly one signature type - check bootloader.conf + * configuration file + */ + +/* Uncomment for RSA signature support */ +#if defined(CONFIG_ESP_SIGN_RSA) +#define MCUBOOT_SIGN_RSA +# if (CONFIG_ESP_SIGN_RSA_LEN != 2048 && \ + CONFIG_ESP_SIGN_RSA_LEN != 3072) +# error "Invalid RSA key size (must be 2048 or 3072)" +# else +# define MCUBOOT_SIGN_RSA_LEN CONFIG_ESP_SIGN_RSA_LEN +# endif +#elif defined(CONFIG_ESP_SIGN_EC256) +#define MCUBOOT_SIGN_EC256 +#elif defined(CONFIG_ESP_SIGN_ED25519) +#define MCUBOOT_SIGN_ED25519 +#endif + +#if defined(CONFIG_SECURE_FLASH_ENC_ENABLED) +#define MCUBOOT_BOOT_MAX_ALIGN 32 +#endif + +/* + * Upgrade mode + * + * The default is to support A/B image swapping with rollback. Other modes + * with simpler code path, which only supports overwriting the existing image + * with the update image or running the newest image directly from its flash + * partition, are also available. + * + * You can enable only one mode at a time from the list below to override + * the default upgrade mode. + */ + +/* Uncomment to enable the overwrite-only code path. */ +/* #define MCUBOOT_OVERWRITE_ONLY */ + +#ifdef MCUBOOT_OVERWRITE_ONLY +/* Uncomment to only erase and overwrite those primary slot sectors needed + * to install the new image, rather than the entire image slot. */ +/* #define MCUBOOT_OVERWRITE_ONLY_FAST */ +#endif + +/* Uncomment to enable the direct-xip code path. */ +/* #define MCUBOOT_DIRECT_XIP */ + +/* Uncomment to enable the ram-load code path. */ +/* #define MCUBOOT_RAM_LOAD */ + +/* + * Cryptographic settings + * + * You must choose between Mbed TLS and Tinycrypt as source of + * cryptographic primitives. Other cryptographic settings are also + * available. + */ + +/* Uncomment to use Mbed TLS cryptographic primitives */ +#if defined(CONFIG_ESP_USE_MBEDTLS) +#define MCUBOOT_USE_MBED_TLS +#else +/* MCUboot requires the definition of a crypto lib, + * using Tinycrypt as default */ +#define MCUBOOT_USE_TINYCRYPT +#endif + +/* + * Always check the signature of the image in the primary slot before booting, + * even if no upgrade was performed. This is recommended if the boot + * time penalty is acceptable. + */ +#define MCUBOOT_VALIDATE_PRIMARY_SLOT + +#ifdef CONFIG_ESP_DOWNGRADE_PREVENTION +#define MCUBOOT_DOWNGRADE_PREVENTION 1 +/* MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER is used later as bool value so it is + * always defined, (unlike MCUBOOT_DOWNGRADE_PREVENTION which is only used in + * preprocessor condition and my be not defined) */ +# ifdef CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER +# define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 1 +# else +# define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 0 +# endif +#endif + +/* + * Flash abstraction + */ + +/* Uncomment if your flash map API supports flash_area_get_sectors(). + * See the flash APIs for more details. */ +#define MCUBOOT_USE_FLASH_AREA_GET_SECTORS + +/* Default maximum number of flash sectors per image slot; change + * as desirable. */ +#define MCUBOOT_MAX_IMG_SECTORS 512 + +/* Default number of separately updateable images; change in case of + * multiple images. */ +#if defined(CONFIG_ESP_IMAGE_NUMBER) +#define MCUBOOT_IMAGE_NUMBER CONFIG_ESP_IMAGE_NUMBER +#else +#define MCUBOOT_IMAGE_NUMBER 1 +#endif + +/* + * Logging + */ + +/* + * If logging is enabled the following functions must be defined by the + * platform: + * + * MCUBOOT_LOG_MODULE_REGISTER(domain) + * Register a new log module and add the current C file to it. + * + * MCUBOOT_LOG_MODULE_DECLARE(domain) + * Add the current C file to an existing log module. + * + * MCUBOOT_LOG_ERR(...) + * MCUBOOT_LOG_WRN(...) + * MCUBOOT_LOG_INF(...) + * MCUBOOT_LOG_DBG(...) + * + * The function priority is: + * + * MCUBOOT_LOG_ERR > MCUBOOT_LOG_WRN > MCUBOOT_LOG_INF > MCUBOOT_LOG_DBG + */ +#define MCUBOOT_HAVE_LOGGING 1 +/* #define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_INFO */ + +/* + * Assertions + */ + +/* Uncomment if your platform has its own mcuboot_config/mcuboot_assert.h. + * If so, it must provide an ASSERT macro for use by bootutil. Otherwise, + * "assert" is used. */ +#define MCUBOOT_HAVE_ASSERT_H 1 + +#ifdef CONFIG_ESP_MCUBOOT_SERIAL +#define CONFIG_MCUBOOT_SERIAL +#endif + +/* + * When a serial recovery process is receiving the image data, this option + * enables it to erase flash progressively (by sectors) instead of the + * default behavior that is erasing whole image size of flash area after + * receiving first frame. + * Enabling this options prevents stalling the beginning of transfer + * for the time needed to erase large chunk of flash. + */ +#ifdef CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY +#define MCUBOOT_ERASE_PROGRESSIVELY +#endif + +/* Serial extensions are not implemented + */ +#define MCUBOOT_PERUSER_MGMT_GROUP_ENABLED 0 + +/* + * Watchdog feeding + */ + +/* This macro might be implemented if the OS / HW watchdog is enabled while + * doing a swap upgrade and the time it takes for a swapping is long enough + * to cause an unwanted reset. If implementing this, the OS main.c must also + * enable the watchdog (if required)! + */ +#include + #define MCUBOOT_WATCHDOG_FEED() \ + do { \ + bootloader_wdt_feed(); \ + } while (0) + +#define MCUBOOT_CPU_IDLE() \ + do { \ + } while (0) + +#endif /* __MCUBOOT_CONFIG_H__ */ diff --git a/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_logging.h b/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000..d619772 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" +#include "mcuboot_config.h" + +extern int ets_printf(const char *fmt, ...); + +#define MCUBOOT_LOG_LEVEL_OFF 0 +#define MCUBOOT_LOG_LEVEL_ERROR 1 +#define MCUBOOT_LOG_LEVEL_WARNING 2 +#define MCUBOOT_LOG_LEVEL_INFO 3 +#define MCUBOOT_LOG_LEVEL_DEBUG 4 + +#if CONFIG_IDF_TARGET_ESP32 +#define TARGET "[esp32]" +#elif CONFIG_IDF_TARGET_ESP32S2 +#define TARGET "[esp32s2]" +#elif CONFIG_IDF_TARGET_ESP32S3 +#define TARGET "[esp32s3]" +#elif CONFIG_IDF_TARGET_ESP32C3 +#define TARGET "[esp32c3]" +#elif CONFIG_IDF_TARGET_ESP32C6 +#define TARGET "[esp32c6]" +#elif CONFIG_IDF_TARGET_ESP32C2 +#define TARGET "[esp32c2]" +#elif CONFIG_IDF_TARGET_ESP32H2 +#define TARGET "[esp32h2]" +#else +#error "Selected target not supported." +#endif + +#ifndef MCUBOOT_LOG_LEVEL +#define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_INFO +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_ERROR +#define MCUBOOT_LOG_ERR(_fmt, ...) \ + do { \ + ets_printf(TARGET " [ERR] " _fmt "\n\r", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_ERR(_fmt, ...) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_WARNING +#define MCUBOOT_LOG_WRN(_fmt, ...) \ + do { \ + ets_printf(TARGET " [WRN] " _fmt "\n\r", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_WRN(_fmt, ...) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_INFO +#define MCUBOOT_LOG_INF(_fmt, ...) \ + do { \ + ets_printf(TARGET " [INF] " _fmt "\n\r", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_INF(_fmt, ...) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_DEBUG +#define MCUBOOT_LOG_DBG(_fmt, ...) \ + do { \ + ets_printf(TARGET " [DBG] " _fmt "\n\r", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_DBG(_fmt, ...) +#endif + +#define MCUBOOT_LOG_MODULE_DECLARE(...) +#define MCUBOOT_LOG_MODULE_REGISTER(...) diff --git a/bootloader/mcuboot/boot/espressif/hal/include/soc_log.h b/bootloader/mcuboot/boot/espressif/hal/include/soc_log.h new file mode 100644 index 0000000..3e8f231 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/include/soc_log.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#define SOC_LOGE(tag, fmt, ...) MCUBOOT_LOG_ERR("[%s] " fmt, tag, ##__VA_ARGS__) +#define SOC_LOGW(tag, fmt, ...) MCUBOOT_LOG_WRN("[%s] " fmt, tag, ##__VA_ARGS__) +#define SOC_LOGI(tag, fmt, ...) MCUBOOT_LOG_INF("[%s] " fmt, tag, ##__VA_ARGS__) +#define SOC_LOGD(tag, fmt, ...) MCUBOOT_LOG_DBG("[%s] " fmt, tag, ##__VA_ARGS__) diff --git a/bootloader/mcuboot/boot/espressif/hal/src/bootloader_banner.c b/bootloader/mcuboot/boot/espressif/hal/src/bootloader_banner.c new file mode 100644 index 0000000..8e7d7fc --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/bootloader_banner.c @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/** + * Override the bootloader's print banner function from IDF. + */ +void __wrap_bootloader_print_banner(void) +{ + MCUBOOT_LOG_INF("*** Booting MCUboot build %s ***", MCUBOOT_VER); +} diff --git a/bootloader/mcuboot/boot/espressif/hal/src/bootloader_wdt.c b/bootloader/mcuboot/boot/espressif/hal/src/bootloader_wdt.c new file mode 100644 index 0000000..b9bcc6e --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/bootloader_wdt.c @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "soc/rtc.h" + +void bootloader_wdt_feed(void) +{ + wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_feed(&rtc_wdt_ctx); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); +} diff --git a/bootloader/mcuboot/boot/espressif/hal/src/esp32/app_cpu_start.c b/bootloader/mcuboot/boot/espressif/hal/src/esp32/app_cpu_start.c new file mode 100644 index 0000000..c465f7d --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/esp32/app_cpu_start.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "app_cpu_start.h" + +#include "soc/dport_reg.h" +#include "soc/gpio_periph.h" +#include "soc/rtc_periph.h" +#include "soc/rtc_cntl_reg.h" +#include "esp32/rom/cache.h" +#include "esp32/rom/uart.h" +#include "esp_cpu.h" +#include "esp_log.h" + +static const char *TAG = "app_cpu_start"; + +void appcpu_start(uint32_t entry_addr) +{ + ESP_LOGI(TAG, "Starting APPCPU"); + + Cache_Flush(1); + Cache_Read_Enable(1); + + esp_cpu_unstall(1); + + DPORT_SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_C_REG, DPORT_APPCPU_RUNSTALL); + DPORT_SET_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_A_REG, DPORT_APPCPU_RESETTING); + + ets_set_appcpu_boot_addr(entry_addr); + ets_delay_us(10000); + uart_tx_wait_idle(0); + ESP_LOGI(TAG, "APPCPU start sequence complete"); +} diff --git a/bootloader/mcuboot/boot/espressif/hal/src/esp32/console_uart_custom.c b/bootloader/mcuboot/boot/espressif/hal/src/esp32/console_uart_custom.c new file mode 100644 index 0000000..f004be9 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/esp32/console_uart_custom.c @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#if CONFIG_ESP_CONSOLE_UART_CUSTOM +static uart_dev_t *alt_console_uart_dev = (CONFIG_ESP_CONSOLE_UART_NUM == 0) ? + &UART0 : + (CONFIG_ESP_CONSOLE_UART_NUM == 1) ? + &UART1 : + &UART2; + +void IRAM_ATTR esp_rom_uart_putc(char c) +{ + while (uart_ll_get_txfifo_len(alt_console_uart_dev) == 0); + uart_ll_write_txfifo(alt_console_uart_dev, (const uint8_t *) &c, 1); +} +#endif + diff --git a/bootloader/mcuboot/boot/espressif/hal/src/esp32c2/console_uart_custom.c b/bootloader/mcuboot/boot/espressif/hal/src/esp32c2/console_uart_custom.c new file mode 100644 index 0000000..e006709 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/esp32c2/console_uart_custom.c @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#if CONFIG_ESP_CONSOLE_UART_CUSTOM +static uart_dev_t *alt_console_uart_dev = (CONFIG_ESP_CONSOLE_UART_NUM == 0) ? + &UART0 : + &UART1; + +void IRAM_ATTR esp_rom_uart_putc(char c) +{ + while (uart_ll_get_txfifo_len(alt_console_uart_dev) == 0); + uart_ll_write_txfifo(alt_console_uart_dev, (const uint8_t *) &c, 1); +} +#endif diff --git a/bootloader/mcuboot/boot/espressif/hal/src/esp32c3/console_uart_custom.c b/bootloader/mcuboot/boot/espressif/hal/src/esp32c3/console_uart_custom.c new file mode 100644 index 0000000..214c60f --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/esp32c3/console_uart_custom.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#if CONFIG_ESP_CONSOLE_UART_CUSTOM +static uart_dev_t *alt_console_uart_dev = (CONFIG_ESP_CONSOLE_UART_NUM == 0) ? + &UART0 : + &UART1; + +void IRAM_ATTR esp_rom_uart_putc(char c) +{ + while (uart_ll_get_txfifo_len(alt_console_uart_dev) == 0); + uart_ll_write_txfifo(alt_console_uart_dev, (const uint8_t *) &c, 1); +} +#endif + diff --git a/bootloader/mcuboot/boot/espressif/hal/src/esp32c6/console_uart_custom.c b/bootloader/mcuboot/boot/espressif/hal/src/esp32c6/console_uart_custom.c new file mode 100644 index 0000000..da5c1ab --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/esp32c6/console_uart_custom.c @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#if CONFIG_ESP_CONSOLE_UART_CUSTOM +static uart_dev_t *alt_console_uart_dev = (CONFIG_ESP_CONSOLE_UART_NUM == 0) ? + &UART0 : + &UART1; + +void IRAM_ATTR esp_rom_uart_putc(char c) +{ + while (uart_ll_get_txfifo_len(alt_console_uart_dev) == 0); + uart_ll_write_txfifo(alt_console_uart_dev, (const uint8_t *) &c, 1); +} +#endif + diff --git a/bootloader/mcuboot/boot/espressif/hal/src/esp32h2/console_uart_custom.c b/bootloader/mcuboot/boot/espressif/hal/src/esp32h2/console_uart_custom.c new file mode 100644 index 0000000..ee80d6b --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/esp32h2/console_uart_custom.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#if CONFIG_ESP_CONSOLE_UART_CUSTOM +static uart_dev_t *alt_console_uart_dev = (CONFIG_ESP_CONSOLE_UART_NUM == 0) ? + &UART0 : + &UART1; + +void IRAM_ATTR esp_rom_uart_putc(char c) +{ + while (uart_ll_get_txfifo_len(alt_console_uart_dev) == 0); + uart_ll_write_txfifo(alt_console_uart_dev, (const uint8_t *) &c, 1); +} +#endif diff --git a/bootloader/mcuboot/boot/espressif/hal/src/esp32s3/app_cpu_start.c b/bootloader/mcuboot/boot/espressif/hal/src/esp32s3/app_cpu_start.c new file mode 100644 index 0000000..2241457 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/esp32s3/app_cpu_start.c @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "app_cpu_start.h" + +#include "esp_rom_sys.h" +#include "soc/dport_reg.h" +#include "soc/gpio_periph.h" +#include "soc/rtc_periph.h" +#include "soc/rtc_cntl_reg.h" +#include "esp32s3/rom/cache.h" +#include "esp32s3/rom/uart.h" +#include "esp_cpu.h" +#include "esp_log.h" + +static const char *TAG = "app_cpu_start"; + +void appcpu_start(uint32_t entry_addr) +{ + ESP_LOGI(TAG, "Starting APPCPU"); + + esp_cpu_unstall(1); + + // Enable clock and reset APP CPU. Note that OpenOCD may have already + // enabled clock and taken APP CPU out of reset. In this case don't reset + // APP CPU again, as that will clear the breakpoints which may have already + // been set. + if (!REG_GET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_CLKGATE_EN)) { + REG_SET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_CLKGATE_EN); + REG_CLR_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RUNSTALL); + REG_SET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RESETING); + REG_CLR_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RESETING); + } + + ets_set_appcpu_boot_addr(entry_addr); + esp_rom_delay_us(10000); + uart_tx_wait_idle(0); + ESP_LOGI(TAG, "APPCPU start sequence complete"); +} diff --git a/bootloader/mcuboot/boot/espressif/hal/src/flash_encrypt.c b/bootloader/mcuboot/boot/espressif/hal/src/flash_encrypt.c new file mode 100644 index 0000000..d064d8b --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/flash_encrypt.c @@ -0,0 +1,482 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "bootloader_flash_priv.h" +#include "bootloader_random.h" +#include "esp_image_format.h" +#include "esp_flash_encrypt.h" +#include "esp_flash_partitions.h" +#include "esp_secure_boot.h" +#include "esp_efuse.h" +#include "esp_efuse_table.h" +#include "esp_log.h" +#include "hal/wdt_hal.h" +#include "hal/efuse_hal.h" +#include "soc/soc_caps.h" +#ifdef CONFIG_SOC_EFUSE_CONSISTS_OF_ONE_KEY_BLOCK +#include "soc/sensitive_reg.h" +#endif + +#include "esp_mcuboot_image.h" + +#if CONFIG_IDF_TARGET_ESP32 +#define CRYPT_CNT ESP_EFUSE_FLASH_CRYPT_CNT +#define WR_DIS_CRYPT_CNT ESP_EFUSE_WR_DIS_FLASH_CRYPT_CNT +#else +#define CRYPT_CNT ESP_EFUSE_SPI_BOOT_CRYPT_CNT +#define WR_DIS_CRYPT_CNT ESP_EFUSE_WR_DIS_SPI_BOOT_CRYPT_CNT +#endif + +#define FLASH_ENC_CNT_MAX (CRYPT_CNT[0]->bit_count) + +/* This file implements FLASH ENCRYPTION related APIs to perform + * various operations such as programming necessary flash encryption + * eFuses, detect whether flash encryption is enabled (by reading eFuse) + * and if required encrypt the partitions in flash memory + */ + +static const char *TAG = "flash_encrypt"; + +/* Static functions for stages of flash encryption */ +static esp_err_t encrypt_bootloader(void); +static esp_err_t encrypt_primary_slot(void); +static size_t get_flash_encrypt_cnt_value(void); + +/** + * This former inlined function must not be defined in the header file anymore. + * As it depends on efuse component, any use of it outside of `bootloader_support`, + * would require the caller component to include `efuse` as part of its `REQUIRES` or + * `PRIV_REQUIRES` entries. + * Attribute IRAM_ATTR must be specified for the app build. + */ +bool IRAM_ATTR esp_flash_encryption_enabled(void) +{ +#ifndef CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH + return efuse_hal_flash_encryption_enabled(); +#else + uint32_t flash_crypt_cnt = 0; +#if CONFIG_IDF_TARGET_ESP32 + esp_efuse_read_field_blob(ESP_EFUSE_FLASH_CRYPT_CNT, &flash_crypt_cnt, ESP_EFUSE_FLASH_CRYPT_CNT[0]->bit_count); +#else + esp_efuse_read_field_blob(ESP_EFUSE_SPI_BOOT_CRYPT_CNT, &flash_crypt_cnt, ESP_EFUSE_SPI_BOOT_CRYPT_CNT[0]->bit_count); +#endif + /* __builtin_parity is in flash, so we calculate parity inline */ + bool enabled = false; + while (flash_crypt_cnt) { + if (flash_crypt_cnt & 1) { + enabled = !enabled; + } + flash_crypt_cnt >>= 1; + } + return enabled; +#endif // CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH +} + +static size_t get_flash_encrypt_cnt_value(void) +{ + size_t flash_crypt_cnt = 0; + esp_efuse_read_field_cnt(CRYPT_CNT, &flash_crypt_cnt); + return flash_crypt_cnt; +} + +bool esp_flash_encrypt_initialized_once(void) +{ + return get_flash_encrypt_cnt_value() != 0; +} + +bool esp_flash_encrypt_is_write_protected(bool print_error) +{ + if (esp_efuse_read_field_bit(WR_DIS_CRYPT_CNT)) { + if (print_error) { + ESP_LOGE(TAG, "Flash Encryption cannot be enabled (CRYPT_CNT (%d) is write protected)", get_flash_encrypt_cnt_value()); + } + return true; + } + return false; +} + +bool esp_flash_encrypt_state(void) +{ + size_t flash_crypt_cnt = get_flash_encrypt_cnt_value(); + bool flash_crypt_wr_dis = esp_flash_encrypt_is_write_protected(false); + + ESP_LOGV(TAG, "CRYPT_CNT %d, write protection %d", flash_crypt_cnt, flash_crypt_wr_dis); + + if (flash_crypt_cnt % 2 == 1) { + /* Flash is already encrypted */ + int left = (FLASH_ENC_CNT_MAX - flash_crypt_cnt) / 2; + if (flash_crypt_wr_dis) { + left = 0; /* can't update FLASH_CRYPT_CNT, no more flashes */ + } + ESP_LOGI(TAG, "flash encryption is enabled (%d plaintext flashes left)", left); + return true; + } + return false; +} + +esp_err_t esp_flash_encrypt_check_and_update(void) +{ + bool flash_encryption_enabled = esp_flash_encrypt_state(); + if (!flash_encryption_enabled) { +#ifndef CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED + if (esp_flash_encrypt_is_write_protected(true)) { + return ESP_FAIL; + } + + esp_err_t err = esp_flash_encrypt_init(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Initialization of Flash encryption key failed (%d)", err); + return err; + } + + err = esp_flash_encrypt_contents(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Encryption flash contents failed (%d)", err); + return err; + } + + err = esp_flash_encrypt_enable(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Enabling of Flash encryption failed (%d)", err); + return err; + } +#else + ESP_LOGE(TAG, "flash encryption is not enabled, and SECURE_FLASH_REQUIRE_ALREADY_ENABLED " + "is set, refusing to boot."); + return ESP_ERR_INVALID_STATE; +#endif // CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED + } + return ESP_OK; +} + +static esp_err_t check_and_generate_encryption_keys(void) +{ + size_t key_size = 32; +#ifdef CONFIG_IDF_TARGET_ESP32 + enum { BLOCKS_NEEDED = 1 }; + esp_efuse_purpose_t purposes[BLOCKS_NEEDED] = { + ESP_EFUSE_KEY_PURPOSE_FLASH_ENCRYPTION, + }; + esp_efuse_coding_scheme_t coding_scheme = esp_efuse_get_coding_scheme(EFUSE_BLK_ENCRYPT_FLASH); + if (coding_scheme != EFUSE_CODING_SCHEME_NONE && coding_scheme != EFUSE_CODING_SCHEME_3_4) { + ESP_LOGE(TAG, "Unknown/unsupported CODING_SCHEME value 0x%x", coding_scheme); + return ESP_ERR_NOT_SUPPORTED; + } + if (coding_scheme == EFUSE_CODING_SCHEME_3_4) { + key_size = 24; + } +#else +#ifdef CONFIG_SECURE_FLASH_ENCRYPTION_AES256 + enum { BLOCKS_NEEDED = 2 }; + esp_efuse_purpose_t purposes[BLOCKS_NEEDED] = { + ESP_EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_1, + ESP_EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_2, + }; + if (esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY, NULL)) { + ESP_LOGE(TAG, "XTS_AES_128_KEY is already in use, XTS_AES_256_KEY_1/2 can not be used"); + return ESP_ERR_INVALID_STATE; + } +#else +#ifdef CONFIG_SECURE_FLASH_ENCRYPTION_AES128_DERIVED + enum { BLOCKS_NEEDED = 1 }; + esp_efuse_purpose_t purposes[BLOCKS_NEEDED] = { + ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS, + }; + key_size = 16; +#else + enum { BLOCKS_NEEDED = 1 }; + esp_efuse_purpose_t purposes[BLOCKS_NEEDED] = { + ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY, + }; +#endif // CONFIG_SECURE_FLASH_ENCRYPTION_AES128_DERIVED +#endif // CONFIG_SECURE_FLASH_ENCRYPTION_AES256 +#endif // CONFIG_IDF_TARGET_ESP32 + + /* Initialize all efuse block entries to invalid (max) value */ + esp_efuse_block_t blocks[BLOCKS_NEEDED] = {[0 ... BLOCKS_NEEDED-1] = EFUSE_BLK_KEY_MAX}; + bool has_key = true; + for (unsigned i = 0; i < BLOCKS_NEEDED; i++) { + bool tmp_has_key = esp_efuse_find_purpose(purposes[i], &blocks[i]); + if (tmp_has_key) { // For ESP32: esp_efuse_find_purpose() always returns True, need to check whether the key block is used or not. + tmp_has_key &= !esp_efuse_key_block_unused(blocks[i]); + } + if (i == 1 && tmp_has_key != has_key) { + ESP_LOGE(TAG, "Invalid efuse key blocks: Both AES-256 key blocks must be set."); + return ESP_ERR_INVALID_STATE; + } + has_key &= tmp_has_key; + } + + if (!has_key) { + /* Generate key */ + uint8_t keys[BLOCKS_NEEDED][32] = { 0 }; + ESP_LOGI(TAG, "Generating new flash encryption key..."); + for (unsigned i = 0; i < BLOCKS_NEEDED; ++i) { + bootloader_fill_random(keys[i], key_size); + } + ESP_LOGD(TAG, "Key generation complete"); + + esp_err_t err = esp_efuse_write_keys(purposes, keys, BLOCKS_NEEDED); + if (err != ESP_OK) { + if (err == ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS) { + ESP_LOGE(TAG, "Not enough free efuse key blocks (need %d) to continue", BLOCKS_NEEDED); + } else { + ESP_LOGE(TAG, "Failed to write efuse block with purpose (err=0x%x). Can't continue.", err); + } + return err; + } + } else { + for (unsigned i = 0; i < BLOCKS_NEEDED; i++) { + if (!esp_efuse_get_key_dis_write(blocks[i]) + || !esp_efuse_get_key_dis_read(blocks[i]) + || !esp_efuse_get_keypurpose_dis_write(blocks[i])) { // For ESP32: no keypurpose, it returns always True. + ESP_LOGE(TAG, "Invalid key state, check read&write protection for key and keypurpose(if exists)"); + return ESP_ERR_INVALID_STATE; + } + } + ESP_LOGI(TAG, "Using pre-loaded flash encryption key in efuse"); + } + return ESP_OK; +} + +esp_err_t esp_flash_encrypt_init(void) +{ + if (esp_flash_encryption_enabled() || esp_flash_encrypt_initialized_once()) { + return ESP_OK; + } + + /* Very first flash encryption pass: generate keys, etc. */ + + esp_efuse_batch_write_begin(); /* Batch all efuse writes at the end of this function */ + + /* Before first flash encryption pass, need to initialise key & crypto config */ + esp_err_t err = check_and_generate_encryption_keys(); + if (err != ESP_OK) { + esp_efuse_batch_write_cancel(); + return err; + } + + err = esp_flash_encryption_enable_secure_features(); + if (err != ESP_OK) { + esp_efuse_batch_write_cancel(); + return err; + } + + err = esp_efuse_batch_write_commit(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error programming security eFuses (err=0x%x).", err); + return err; + } + + return ESP_OK; +} + +/* Encrypt all flash data that should be encrypted */ +esp_err_t esp_flash_encrypt_contents(void) +{ + esp_err_t err; + +#ifdef CONFIG_SOC_EFUSE_CONSISTS_OF_ONE_KEY_BLOCK + REG_WRITE(SENSITIVE_XTS_AES_KEY_UPDATE_REG, 1); +#endif + + err = encrypt_bootloader(); + if (err != ESP_OK) { + return err; + } + + /* If the primary slot executable application is not encrypted, + * then encrypt it + */ + err = encrypt_primary_slot(); + if (err != ESP_OK) { + return err; + } + + /* Unconditionally encrypts remaining regions + * This will need changes when implementing multi-slot support + */ + ESP_LOGI(TAG, "Encrypting remaining flash..."); + uint32_t region_addr = CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS; + size_t region_size = CONFIG_ESP_APPLICATION_SIZE; + err = esp_flash_encrypt_region(region_addr, region_size); + if (err != ESP_OK) { + return err; + } + region_addr = CONFIG_ESP_SCRATCH_OFFSET; + region_size = CONFIG_ESP_SCRATCH_SIZE; + err = esp_flash_encrypt_region(region_addr, region_size); + if (err != ESP_OK) { + return err; + } + +#if defined(CONFIG_ESP_IMAGE_NUMBER) && (CONFIG_ESP_IMAGE_NUMBER == 2) + region_addr = CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS; + region_size = CONFIG_ESP_APPLICATION_SIZE; + err = esp_flash_encrypt_region(region_addr, region_size); + if (err != ESP_OK) { + return err; + } + region_addr = CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS; + region_size = CONFIG_ESP_APPLICATION_SIZE; + err = esp_flash_encrypt_region(region_addr, region_size); + if (err != ESP_OK) { + return err; + } +#endif + + ESP_LOGI(TAG, "Flash encryption completed"); + + return ESP_OK; +} + +esp_err_t esp_flash_encrypt_enable(void) +{ + esp_err_t err = ESP_OK; + if (!esp_flash_encryption_enabled()) { + + if (esp_flash_encrypt_is_write_protected(true)) { + return ESP_FAIL; + } + + size_t flash_crypt_cnt = get_flash_encrypt_cnt_value(); + +#ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE + // Go straight to max, permanently enabled + ESP_LOGI(TAG, "Setting CRYPT_CNT for permanent encryption"); + size_t new_flash_crypt_cnt = FLASH_ENC_CNT_MAX - flash_crypt_cnt; +#else + /* Set least significant 0-bit in flash_crypt_cnt */ + size_t new_flash_crypt_cnt = 1; +#endif + ESP_LOGD(TAG, "CRYPT_CNT %d -> %d", flash_crypt_cnt, new_flash_crypt_cnt); + err = esp_efuse_write_field_cnt(CRYPT_CNT, new_flash_crypt_cnt); + +#if defined(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE) && defined(CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_128_DERIVED) + // For AES128_DERIVED, FE key is 16 bytes and XTS_KEY_LENGTH_256 is 0. + // It is important to protect XTS_KEY_LENGTH_256 from further changing it to 1. Set write protection for this bit. + // Burning WR_DIS_CRYPT_CNT, blocks further changing of eFuses: DOWNLOAD_DIS_MANUAL_ENCRYPT, SPI_BOOT_CRYPT_CNT, [XTS_KEY_LENGTH_256], SECURE_BOOT_EN. + esp_efuse_write_field_bit(WR_DIS_CRYPT_CNT); +#endif + } + + ESP_LOGI(TAG, "Flash encryption completed"); + +#ifdef CONFIG_EFUSE_VIRTUAL + ESP_LOGW(TAG, "Flash encryption not really completed. Must disable virtual efuses"); +#endif + + return err; +} + +static esp_err_t encrypt_bootloader(void) +{ + esp_err_t err; + uint32_t image_length; + /* Check for plaintext bootloader (verification will fail if it's already encrypted) */ + if (esp_image_verify_bootloader(&image_length) == ESP_OK) { + ESP_LOGI(TAG, "Encrypting bootloader..."); + + err = esp_flash_encrypt_region(ESP_BOOTLOADER_OFFSET, CONFIG_ESP_BOOTLOADER_SIZE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to encrypt bootloader in place: 0x%x", err); + return err; + } + ESP_LOGI(TAG, "Bootloader encrypted successfully"); + } else { + ESP_LOGW(TAG, "No valid bootloader was found"); + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +static esp_err_t verify_img_header(uint32_t addr, const esp_image_load_header_t *image, bool silent) +{ + esp_err_t err = ESP_OK; + + if (image->header_magic != ESP_LOAD_HEADER_MAGIC) { + if (!silent) { + ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", + addr); + } + err = ESP_ERR_IMAGE_INVALID; + } + + return err; +} + +static esp_err_t encrypt_primary_slot(void) +{ + esp_err_t err; + + esp_image_load_header_t img_header; + + /* Check if the slot is plaintext or encrypted, 0x20 offset is for skipping + * MCUboot header + */ + err = bootloader_flash_read(CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS + 0x20, + &img_header, sizeof(esp_image_load_header_t), true); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to read slot img header"); + return err; + } else { + err = verify_img_header(CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS, + &img_header, true); + } + + if (err == ESP_OK) { + ESP_LOGI(TAG, "Encrypting primary slot..."); + + err = esp_flash_encrypt_region(CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS, + CONFIG_ESP_APPLICATION_SIZE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to encrypt slot in place: 0x%x", err); + return err; + } + } else { + ESP_LOGW(TAG, "Slot already encrypted or no valid image was found"); + } + + return ESP_OK; +} + +esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length) +{ + esp_err_t err; + uint32_t buf[FLASH_SECTOR_SIZE / sizeof(uint32_t)]; + + if (src_addr % FLASH_SECTOR_SIZE != 0) { + ESP_LOGE(TAG, "esp_flash_encrypt_region bad src_addr 0x%x", src_addr); + return ESP_FAIL; + } + + wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); + for (size_t i = 0; i < data_length; i += FLASH_SECTOR_SIZE) { + wdt_hal_write_protect_disable(&rtc_wdt_ctx); + wdt_hal_feed(&rtc_wdt_ctx); + wdt_hal_write_protect_enable(&rtc_wdt_ctx); + uint32_t sec_start = i + src_addr; + err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, true); + if (err != ESP_OK) { + goto flash_failed; + } + err = bootloader_flash_erase_sector(sec_start / FLASH_SECTOR_SIZE); + if (err != ESP_OK) { + goto flash_failed; + } + err = bootloader_flash_write(sec_start, buf, FLASH_SECTOR_SIZE, true); + if (err != ESP_OK) { + goto flash_failed; + } + } + return ESP_OK; + +flash_failed: + ESP_LOGE(TAG, "flash operation failed: 0x%x", err); + return err; +} diff --git a/bootloader/mcuboot/boot/espressif/hal/src/secure_boot.c b/bootloader/mcuboot/boot/espressif/hal/src/secure_boot.c new file mode 100644 index 0000000..8ad29ae --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/hal/src/secure_boot.c @@ -0,0 +1,266 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_secure_boot.h" +#include "bootloader_flash_priv.h" +#include "bootloader_sha.h" +#include "bootloader_utility.h" +#include "esp_image_format.h" +#include "esp_efuse.h" +#include "esp_efuse_table.h" +#include "secure_boot_signature_priv.h" + + +/* The following API implementations are used only when called + * from the bootloader code. + */ + +#ifdef CONFIG_SECURE_BOOT_V2_ENABLED + +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +static const char *TAG = "secure_boot_v2"; + +/* A signature block is valid when it has correct magic byte, crc and image digest. */ +static esp_err_t validate_signature_block(const ets_secure_boot_sig_block_t *block, int block_num, const uint8_t *image_digest) +{ + if (block->magic_byte != ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC) { + // All signature blocks have been parsed, no new signature block present. + ESP_LOGD(TAG, "Signature block(%d) invalid/absent.", block_num); + return ESP_FAIL; + } + if (block->block_crc != esp_rom_crc32_le(0, (uint8_t *)block, CRC_SIGN_BLOCK_LEN)) { + ESP_LOGE(TAG, "Magic byte correct but incorrect crc."); + return ESP_FAIL; + } + if (memcmp(image_digest, block->image_digest, ESP_SECURE_BOOT_DIGEST_LEN)) { + ESP_LOGE(TAG, "Magic byte & CRC correct but incorrect image digest."); + return ESP_FAIL; + } else { + ESP_LOGD(TAG, "valid signature block(%d) found", block_num); + return ESP_OK; + } + return ESP_FAIL; +} + +/* Generates the public key digests of the valid public keys in an image's + signature block, verifies each signature, and stores the key digests in the + public_key_digests structure. + + @param flash_offset Image offset in flash + @param flash_size Image size in flash (not including signature block) + @param[out] public_key_digests Pointer to structure to hold the key digests for valid sig blocks + + + Note that this function doesn't read any eFuses, so it doesn't know if the + keys are ultimately trusted by the hardware or not + + @return - ESP_OK if no signatures failed to verify, or if no valid signature blocks are found at all. + - ESP_FAIL if there's a valid signature block that doesn't verify using the included public key (unexpected!) +*/ +static esp_err_t s_calculate_image_public_key_digests(uint32_t flash_offset, uint32_t flash_size, esp_image_sig_public_key_digests_t *public_key_digests) +{ + esp_err_t ret; + uint8_t image_digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0}; + uint8_t __attribute__((aligned(4))) key_digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0}; + size_t sig_block_addr = flash_offset + ALIGN_UP(flash_size, FLASH_SECTOR_SIZE); + + ESP_LOGD(TAG, "calculating public key digests for sig blocks of image offset 0x%x (sig block offset 0x%x)", flash_offset, sig_block_addr); + + bzero(public_key_digests, sizeof(esp_image_sig_public_key_digests_t)); + + ret = bootloader_sha256_flash_contents(flash_offset, sig_block_addr - flash_offset, image_digest); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "error generating image digest, %d", ret); + return ret; + } + + ESP_LOGD(TAG, "reading signature(s)"); + const ets_secure_boot_signature_t *signatures = bootloader_mmap(sig_block_addr, sizeof(ets_secure_boot_signature_t)); + if (signatures == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", sig_block_addr, sizeof(ets_secure_boot_signature_t)); + return ESP_FAIL; + } + + /* Validating Signature block */ + for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + const ets_secure_boot_sig_block_t *block = &signatures->block[i]; + + ret = validate_signature_block(block, i, image_digest); + if (ret != ESP_OK) { + ret = ESP_OK; // past the last valid signature block + break; + } + + /* Generating the SHA of the public key components in the signature block */ + bootloader_sha256_handle_t sig_block_sha; + sig_block_sha = bootloader_sha256_start(); +#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME + bootloader_sha256_data(sig_block_sha, &block->key, sizeof(block->key)); +#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME + bootloader_sha256_data(sig_block_sha, &block->ecdsa.key, sizeof(block->ecdsa.key)); +#endif + bootloader_sha256_finish(sig_block_sha, key_digest); + + // Check we can verify the image using this signature and this key + uint8_t temp_verified_digest[ESP_SECURE_BOOT_DIGEST_LEN]; +#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME + bool verified = ets_rsa_pss_verify(&block->key, block->signature, image_digest, temp_verified_digest); +#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME + bool verified = ets_ecdsa_verify(&block->ecdsa.key.point[0], block->ecdsa.signature, block->ecdsa.key.curve_id, image_digest, temp_verified_digest); +#endif + + if (!verified) { + /* We don't expect this: the signature blocks before we enable secure boot should all be verifiable or invalid, + so this is a fatal error + */ + ret = ESP_FAIL; + ESP_LOGE(TAG, "Secure boot key (%d) verification failed.", i); + break; + } + ESP_LOGD(TAG, "Signature block (%d) is verified", i); + /* Copy the key digest to the buffer provided by the caller */ + memcpy((void *)public_key_digests->key_digests[i], key_digest, ESP_SECURE_BOOT_DIGEST_LEN); + public_key_digests->num_digests++; + } + + if (ret == ESP_OK && public_key_digests->num_digests > 0) { + ESP_LOGI(TAG, "Digests successfully calculated, %d valid signatures (image offset 0x%x)", + public_key_digests->num_digests, flash_offset); + } + + bootloader_munmap(signatures); + return ret; +} + +esp_err_t check_and_generate_secure_boot_keys(void) +{ + esp_err_t ret; +#ifdef CONFIG_IDF_TARGET_ESP32 + esp_efuse_coding_scheme_t coding_scheme = esp_efuse_get_coding_scheme(EFUSE_BLK_SECURE_BOOT); + if (coding_scheme != EFUSE_CODING_SCHEME_NONE) { + ESP_LOGE(TAG, "No coding schemes are supported in secure boot v2.(Detected scheme: 0x%x)", coding_scheme); + return ESP_ERR_NOT_SUPPORTED; + } +#endif // CONFIG_IDF_TARGET_ESP32 + + esp_efuse_purpose_t secure_boot_key_purpose[SECURE_BOOT_NUM_BLOCKS] = { +#if SECURE_BOOT_NUM_BLOCKS == 1 + ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_V2, +#else + ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0, + ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1, + ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2, +#endif + }; + + /* Verify the bootloader */ + esp_image_metadata_t bootloader_data = { 0 }; + ret = esp_image_verify_bootloader_data(&bootloader_data); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "bootloader image appears invalid! error %d", ret); + return ret; + } + + /* Initialize all efuse block entries to invalid (max) value */ + esp_efuse_block_t blocks[SECURE_BOOT_NUM_BLOCKS] = {[0 ... SECURE_BOOT_NUM_BLOCKS-1] = EFUSE_BLK_KEY_MAX}; + /* Check if secure boot digests are present */ + bool has_secure_boot_digest = false; + for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + bool tmp_has_key = esp_efuse_find_purpose(secure_boot_key_purpose[i], &blocks[i]); + if (tmp_has_key) { // For ESP32: esp_efuse_find_purpose() always returns True, need to check whether the key block is used or not. + tmp_has_key &= !esp_efuse_key_block_unused(blocks[i]); + } + has_secure_boot_digest |= tmp_has_key; + } + + esp_image_sig_public_key_digests_t boot_key_digests = {0}; + ESP_LOGI(TAG, "Secure boot digests %s", has_secure_boot_digest ? "already present":"absent, generating.."); + + if (!has_secure_boot_digest) { + /* Generate the bootloader public key digests */ + ret = s_calculate_image_public_key_digests(bootloader_data.start_addr, bootloader_data.image_len - SIG_BLOCK_PADDING, &boot_key_digests); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Bootloader signature block is invalid"); + return ret; + } + + if (boot_key_digests.num_digests == 0) { + ESP_LOGE(TAG, "No valid bootloader signature blocks found."); + return ESP_FAIL; + } + ESP_LOGI(TAG, "%d signature block(s) found appended to the bootloader.", boot_key_digests.num_digests); + + ESP_LOGI(TAG, "Burning public key hash to eFuse"); + ret = esp_efuse_write_keys(secure_boot_key_purpose, boot_key_digests.key_digests, boot_key_digests.num_digests); + if (ret != ESP_OK) { + if (ret == ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS) { + ESP_LOGE(TAG, "Bootloader signatures(%d) more than available key slots.", boot_key_digests.num_digests); + } else { + ESP_LOGE(TAG, "Failed to write efuse block with purpose (err=0x%x). Can't continue.", ret); + } + return ret; + } + } else { + for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + /* Check if corresponding digest slot is used or not */ + if (blocks[i] == EFUSE_BLK_KEY_MAX) { + ESP_LOGD(TAG, "SECURE_BOOT_DIGEST%d slot is not used", i); + continue; + } + +#if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS + if (esp_efuse_get_digest_revoke(i)) { + continue; + } +#endif +#ifndef CONFIG_SOC_EFUSE_CONSISTS_OF_ONE_KEY_BLOCK + if (esp_efuse_get_key_dis_read(blocks[i])) { + ESP_LOGE(TAG, "Key digest (BLK%d) read protected, aborting...", blocks[i]); + return ESP_FAIL; + } +#endif + if (esp_efuse_block_is_empty(blocks[i])) { + ESP_LOGE(TAG, "%d eFuse block is empty, aborting...", blocks[i]); + return ESP_FAIL; + } + esp_efuse_set_key_dis_write(blocks[i]); +#ifdef CONFIG_SOC_EFUSE_CONSISTS_OF_ONE_KEY_BLOCK + size_t offset = 128; +#else + size_t offset = 0; +#endif + ret = esp_efuse_read_block(blocks[i], boot_key_digests.key_digests[boot_key_digests.num_digests], offset, + ESP_SECURE_BOOT_KEY_DIGEST_LEN * 8); + if (ret) { + ESP_LOGE(TAG, "Error during reading %d eFuse block (err=0x%x)", blocks[i], ret); + return ret; + } + boot_key_digests.num_digests++; + } + if (boot_key_digests.num_digests == 0) { + ESP_LOGE(TAG, "No valid pre-loaded public key digest in eFuse"); + return ESP_FAIL; + } + ESP_LOGW(TAG, "Using pre-loaded public key digest in eFuse"); + } + +#if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS + /* Revoke the empty signature blocks */ + if (boot_key_digests.num_digests < SECURE_BOOT_NUM_BLOCKS) { + /* The revocation index can be 0, 1, 2. Bootloader count can be 1,2,3. */ + for (unsigned i = boot_key_digests.num_digests; i < SECURE_BOOT_NUM_BLOCKS; i++) { + ESP_LOGI(TAG, "Revoking empty key digest slot (%d)...", i); + esp_efuse_set_digest_revoke(i); + } + } +#endif // SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS + return ESP_OK; +} + +#endif // CONFIG_SECURE_BOOT_V2_ENABLED diff --git a/bootloader/mcuboot/boot/espressif/include/crypto_config/ec256.cmake b/bootloader/mcuboot/boot/espressif/include/crypto_config/ec256.cmake new file mode 100644 index 0000000..36e3e01 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/crypto_config/ec256.cmake @@ -0,0 +1,29 @@ +# Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. +# +# SPDX-License-Identifier: Apache-2.0 + +set(MBEDTLS_ASN1_DIR "${MCUBOOT_ROOT_DIR}/ext/mbedtls-asn1") +set(CRYPTO_INC + ${MBEDTLS_ASN1_DIR}/include + ) +set(crypto_srcs + # Additionally pull in just the ASN.1 parser from Mbed TLS. + ${MBEDTLS_ASN1_DIR}/src/asn1parse.c + ${MBEDTLS_ASN1_DIR}/src/platform_util.c + ) + +if (DEFINED CONFIG_ESP_USE_MBEDTLS) + message(FATAL_ERROR "EC256 signature verification using Mbed TLS lib is not supported") +elseif (DEFINED CONFIG_ESP_USE_TINYCRYPT) + set(TINYCRYPT_DIR ${MCUBOOT_ROOT_DIR}/ext/tinycrypt/lib) + list(APPEND CRYPTO_INC + ${TINYCRYPT_DIR}/include + ) + list(APPEND crypto_srcs + ${ESPRESSIF_PORT_DIR}/keys.c + ${TINYCRYPT_DIR}/source/utils.c + ${TINYCRYPT_DIR}/source/sha256.c + ${TINYCRYPT_DIR}/source/ecc.c + ${TINYCRYPT_DIR}/source/ecc_dsa.c + ) +endif() \ No newline at end of file diff --git a/bootloader/mcuboot/boot/espressif/include/crypto_config/ed25519.cmake b/bootloader/mcuboot/boot/espressif/include/crypto_config/ed25519.cmake new file mode 100644 index 0000000..011aca6 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/crypto_config/ed25519.cmake @@ -0,0 +1,31 @@ +# Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. +# +# SPDX-License-Identifier: Apache-2.0 + +set(MBEDTLS_ASN1_DIR "${MCUBOOT_ROOT_DIR}/ext/mbedtls-asn1") +set(CRYPTO_INC + ${MBEDTLS_ASN1_DIR}/include + ) +set(crypto_srcs + # Additionally pull in just the ASN.1 parser from Mbed TLS. + ${MBEDTLS_ASN1_DIR}/src/asn1parse.c + ${MBEDTLS_ASN1_DIR}/src/platform_util.c + ) + +if (DEFINED CONFIG_ESP_USE_MBEDTLS) + message(FATAL_ERROR "ED25519 image signing using Mbed TLS lib is not supported") +elseif (DEFINED CONFIG_ESP_USE_TINYCRYPT) + set(TINYCRYPT_DIR ${MCUBOOT_ROOT_DIR}/ext/tinycrypt/lib) + set(TINYCRYPT512_DIR ${MCUBOOT_ROOT_DIR}/ext/tinycrypt-sha512/lib) + list(APPEND CRYPTO_INC + ${TINYCRYPT_DIR}/include + ${TINYCRYPT512_DIR}/include + ) + list(APPEND crypto_srcs + ${ESPRESSIF_PORT_DIR}/keys.c + ${TINYCRYPT_DIR}/source/utils.c + ${TINYCRYPT_DIR}/source/sha256.c + ${TINYCRYPT512_DIR}/source/sha512.c + ${MCUBOOT_ROOT_DIR}/ext/fiat/src/curve25519.c + ) +endif() \ No newline at end of file diff --git a/bootloader/mcuboot/boot/espressif/include/crypto_config/mbedtls_custom_config.h b/bootloader/mcuboot/boot/espressif/include/crypto_config/mbedtls_custom_config.h new file mode 100644 index 0000000..ed3c0ef --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/crypto_config/mbedtls_custom_config.h @@ -0,0 +1,3265 @@ +/** + * \file mbedtls_config.h + * + * \brief Configuration options (set of defines) + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +/** + * This is an optional version symbol that enables comatibility handling of + * config files. + * + * It is equal to the #MBEDTLS_VERSION_NUMBER of the Mbed TLS version that + * introduced the config format we want to be compatible with. + */ +//#define MBEDTLS_CONFIG_VERSION 0x03000000 + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def MBEDTLS_HAVE_ASM + * + * The compiler has support for asm(). + * + * Requires support for asm() in compiler. + * + * Used in: + * library/aria.c + * library/bn_mul.h + * + * Required by: + * MBEDTLS_AESNI_C + * MBEDTLS_PADLOCK_C + * + * Comment to disable the use of assembly code. + */ +#define MBEDTLS_HAVE_ASM + +/** + * \def MBEDTLS_NO_UDBL_DIVISION + * + * The platform lacks support for double-width integer division (64-bit + * division on a 32-bit platform, 128-bit division on a 64-bit platform). + * + * Used in: + * include/mbedtls/bignum.h + * library/bignum.c + * + * The bignum code uses double-width division to speed up some operations. + * Double-width division is often implemented in software that needs to + * be linked with the program. The presence of a double-width integer + * type is usually detected automatically through preprocessor macros, + * but the automatic detection cannot know whether the code needs to + * and can be linked with an implementation of division for that type. + * By default division is assumed to be usable if the type is present. + * Uncomment this option to prevent the use of double-width division. + * + * Note that division for the native integer type is always required. + * Furthermore, a 64-bit type is always required even on a 32-bit + * platform, but it need not support multiplication or division. In some + * cases it is also desirable to disable some double-width operations. For + * example, if double-width division is implemented in software, disabling + * it can reduce code size in some embedded targets. + */ +//#define MBEDTLS_NO_UDBL_DIVISION + +/** + * \def MBEDTLS_NO_64BIT_MULTIPLICATION + * + * The platform lacks support for 32x32 -> 64-bit multiplication. + * + * Used in: + * library/poly1305.c + * + * Some parts of the library may use multiplication of two unsigned 32-bit + * operands with a 64-bit result in order to speed up computations. On some + * platforms, this is not available in hardware and has to be implemented in + * software, usually in a library provided by the toolchain. + * + * Sometimes it is not desirable to have to link to that library. This option + * removes the dependency of that library on platforms that lack a hardware + * 64-bit multiplier by embedding a software implementation in Mbed TLS. + * + * Note that depending on the compiler, this may decrease performance compared + * to using the library function provided by the toolchain. + */ +//#define MBEDTLS_NO_64BIT_MULTIPLICATION + +/** + * \def MBEDTLS_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + */ +//#define MBEDTLS_HAVE_SSE2 + +/** + * \def MBEDTLS_HAVE_TIME + * + * System has time.h and time(). + * The time does not need to be correct, only time differences are used, + * by contrast with MBEDTLS_HAVE_TIME_DATE + * + * Defining MBEDTLS_HAVE_TIME allows you to specify MBEDTLS_PLATFORM_TIME_ALT, + * MBEDTLS_PLATFORM_TIME_MACRO, MBEDTLS_PLATFORM_TIME_TYPE_MACRO and + * MBEDTLS_PLATFORM_STD_TIME. + * + * Comment if your system does not support time functions + */ +#define MBEDTLS_HAVE_TIME + +/** + * \def MBEDTLS_HAVE_TIME_DATE + * + * System has time.h, time(), and an implementation for + * mbedtls_platform_gmtime_r() (see below). + * The time needs to be correct (not necessarily very accurate, but at least + * the date should be correct). This is used to verify the validity period of + * X.509 certificates. + * + * Comment if your system does not have a correct clock. + * + * \note mbedtls_platform_gmtime_r() is an abstraction in platform_util.h that + * behaves similarly to the gmtime_r() function from the C standard. Refer to + * the documentation for mbedtls_platform_gmtime_r() for more information. + * + * \note It is possible to configure an implementation for + * mbedtls_platform_gmtime_r() at compile-time by using the macro + * MBEDTLS_PLATFORM_GMTIME_R_ALT. + */ +#define MBEDTLS_HAVE_TIME_DATE + +/** + * \def MBEDTLS_PLATFORM_MEMORY + * + * Enable the memory allocation layer. + * + * By default mbed TLS uses the system-provided calloc() and free(). + * This allows different allocators (self-implemented or provided) to be + * provided to the platform abstraction layer. + * + * Enabling MBEDTLS_PLATFORM_MEMORY without the + * MBEDTLS_PLATFORM_{FREE,CALLOC}_MACROs will provide + * "mbedtls_platform_set_calloc_free()" allowing you to set an alternative calloc() and + * free() function pointer at runtime. + * + * Enabling MBEDTLS_PLATFORM_MEMORY and specifying + * MBEDTLS_PLATFORM_{CALLOC,FREE}_MACROs will allow you to specify the + * alternate function at compile time. + * + * Requires: MBEDTLS_PLATFORM_C + * + * Enable this layer to allow use of alternative memory allocators. + */ +#define MBEDTLS_PLATFORM_MEMORY + +/** + * \def MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + * + * Do not assign standard functions in the platform layer (e.g. calloc() to + * MBEDTLS_PLATFORM_STD_CALLOC and printf() to MBEDTLS_PLATFORM_STD_PRINTF) + * + * This makes sure there are no linking errors on platforms that do not support + * these functions. You will HAVE to provide alternatives, either at runtime + * via the platform_set_xxx() functions or at compile time by setting + * the MBEDTLS_PLATFORM_STD_XXX defines, or enabling a + * MBEDTLS_PLATFORM_XXX_MACRO. + * + * Requires: MBEDTLS_PLATFORM_C + * + * Uncomment to prevent default assignment of standard functions in the + * platform layer. + */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +/** + * \def MBEDTLS_PLATFORM_EXIT_ALT + * + * MBEDTLS_PLATFORM_XXX_ALT: Uncomment a macro to let mbed TLS support the + * function in the platform abstraction layer. + * + * Example: In case you uncomment MBEDTLS_PLATFORM_PRINTF_ALT, mbed TLS will + * provide a function "mbedtls_platform_set_printf()" that allows you to set an + * alternative printf function pointer. + * + * All these define require MBEDTLS_PLATFORM_C to be defined! + * + * \note MBEDTLS_PLATFORM_SNPRINTF_ALT is required on Windows; + * it will be enabled automatically by check_config.h + * + * \warning MBEDTLS_PLATFORM_XXX_ALT cannot be defined at the same time as + * MBEDTLS_PLATFORM_XXX_MACRO! + * + * Requires: MBEDTLS_PLATFORM_TIME_ALT requires MBEDTLS_HAVE_TIME + * + * Uncomment a macro to enable alternate implementation of specific base + * platform function + */ +#define MBEDTLS_PLATFORM_EXIT_ALT +//#define MBEDTLS_PLATFORM_TIME_ALT +//#define MBEDTLS_PLATFORM_FPRINTF_ALT +//#define MBEDTLS_PLATFORM_PRINTF_ALT +//#define MBEDTLS_PLATFORM_SNPRINTF_ALT +//#define MBEDTLS_PLATFORM_VSNPRINTF_ALT +//#define MBEDTLS_PLATFORM_NV_SEED_ALT +//#define MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT + +/** + * \def MBEDTLS_DEPRECATED_WARNING + * + * Mark deprecated functions and features so that they generate a warning if + * used. Functionality deprecated in one version will usually be removed in the + * next version. You can enable this to help you prepare the transition to a + * new major version by making sure your code is not using this functionality. + * + * This only works with GCC and Clang. With other compilers, you may want to + * use MBEDTLS_DEPRECATED_REMOVED + * + * Uncomment to get warnings on using deprecated functions and features. + */ +//#define MBEDTLS_DEPRECATED_WARNING + +/** + * \def MBEDTLS_DEPRECATED_REMOVED + * + * Remove deprecated functions and features so that they generate an error if + * used. Functionality deprecated in one version will usually be removed in the + * next version. You can enable this to help you prepare the transition to a + * new major version by making sure your code is not using this functionality. + * + * Uncomment to get errors on using deprecated functions and features. + */ +//#define MBEDTLS_DEPRECATED_REMOVED + +/* \} name SECTION: System support */ + +/** + * \name SECTION: mbed TLS feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def MBEDTLS_TIMING_ALT + * + * Uncomment to provide your own alternate implementation for + * mbedtls_timing_get_timer(), mbedtls_set_alarm(), mbedtls_set/get_delay() + * + * Only works if you have MBEDTLS_TIMING_C enabled. + * + * You will need to provide a header "timing_alt.h" and an implementation at + * compile time. + */ +//#define MBEDTLS_TIMING_ALT + +/** + * \def MBEDTLS_AES_ALT + * + * MBEDTLS__MODULE_NAME__ALT: Uncomment a macro to let mbed TLS use your + * alternate core implementation of a symmetric crypto, an arithmetic or hash + * module (e.g. platform specific assembly optimized implementations). Keep + * in mind that the function prototypes should remain the same. + * + * This replaces the whole module. If you only want to replace one of the + * functions, use one of the MBEDTLS__FUNCTION_NAME__ALT flags. + * + * Example: In case you uncomment MBEDTLS_AES_ALT, mbed TLS will no longer + * provide the "struct mbedtls_aes_context" definition and omit the base + * function declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * module. + * + * \warning MD5, DES and SHA-1 are considered weak and their + * use constitutes a security risk. If possible, we recommend + * avoiding dependencies on them, and considering stronger message + * digests and ciphers instead. + * + */ +//#define MBEDTLS_AES_ALT +//#define MBEDTLS_ARIA_ALT +//#define MBEDTLS_CAMELLIA_ALT +//#define MBEDTLS_CCM_ALT +//#define MBEDTLS_CHACHA20_ALT +//#define MBEDTLS_CHACHAPOLY_ALT +//#define MBEDTLS_CMAC_ALT +//#define MBEDTLS_DES_ALT +//#define MBEDTLS_DHM_ALT +//#define MBEDTLS_ECJPAKE_ALT +//#define MBEDTLS_GCM_ALT +//#define MBEDTLS_NIST_KW_ALT +//#define MBEDTLS_MD5_ALT +//#define MBEDTLS_POLY1305_ALT +//#define MBEDTLS_RIPEMD160_ALT +//#define MBEDTLS_RSA_ALT +//#define MBEDTLS_SHA1_ALT +//#define MBEDTLS_SHA256_ALT +//#define MBEDTLS_SHA512_ALT + +/* + * When replacing the elliptic curve module, pleace consider, that it is + * implemented with two .c files: + * - ecp.c + * - ecp_curves.c + * You can replace them very much like all the other MBEDTLS__MODULE_NAME__ALT + * macros as described above. The only difference is that you have to make sure + * that you provide functionality for both .c files. + */ +//#define MBEDTLS_ECP_ALT + +/** + * \def MBEDTLS_SHA256_PROCESS_ALT + * + * MBEDTLS__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use you + * alternate core implementation of symmetric crypto or hash function. Keep in + * mind that function prototypes should remain the same. + * + * This replaces only one function. The header file from mbed TLS is still + * used, in contrast to the MBEDTLS__MODULE_NAME__ALT flags. + * + * Example: In case you uncomment MBEDTLS_SHA256_PROCESS_ALT, mbed TLS will + * no longer provide the mbedtls_sha1_process() function, but it will still provide + * the other function (using your mbedtls_sha1_process() function) and the definition + * of mbedtls_sha1_context, so your implementation of mbedtls_sha1_process must be compatible + * with this definition. + * + * \note If you use the AES_xxx_ALT macros, then it is recommended to also set + * MBEDTLS_AES_ROM_TABLES in order to help the linker garbage-collect the AES + * tables. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * function. + * + * \warning MD5, DES and SHA-1 are considered weak and their use + * constitutes a security risk. If possible, we recommend avoiding + * dependencies on them, and considering stronger message digests + * and ciphers instead. + * + * \warning If both MBEDTLS_ECDSA_SIGN_ALT and MBEDTLS_ECDSA_DETERMINISTIC are + * enabled, then the deterministic ECDH signature functions pass the + * the static HMAC-DRBG as RNG to mbedtls_ecdsa_sign(). Therefore + * alternative implementations should use the RNG only for generating + * the ephemeral key and nothing else. If this is not possible, then + * MBEDTLS_ECDSA_DETERMINISTIC should be disabled and an alternative + * implementation should be provided for mbedtls_ecdsa_sign_det_ext(). + * + */ +//#define MBEDTLS_MD5_PROCESS_ALT +//#define MBEDTLS_RIPEMD160_PROCESS_ALT +//#define MBEDTLS_SHA1_PROCESS_ALT +//#define MBEDTLS_SHA256_PROCESS_ALT +//#define MBEDTLS_SHA512_PROCESS_ALT +//#define MBEDTLS_DES_SETKEY_ALT +//#define MBEDTLS_DES_CRYPT_ECB_ALT +//#define MBEDTLS_DES3_CRYPT_ECB_ALT +//#define MBEDTLS_AES_SETKEY_ENC_ALT +//#define MBEDTLS_AES_SETKEY_DEC_ALT +//#define MBEDTLS_AES_ENCRYPT_ALT +//#define MBEDTLS_AES_DECRYPT_ALT +//#define MBEDTLS_ECDH_GEN_PUBLIC_ALT +//#define MBEDTLS_ECDH_COMPUTE_SHARED_ALT +//#define MBEDTLS_ECDSA_VERIFY_ALT +//#define MBEDTLS_ECDSA_SIGN_ALT +//#define MBEDTLS_ECDSA_GENKEY_ALT + +/** + * \def MBEDTLS_ECP_INTERNAL_ALT + * + * Expose a part of the internal interface of the Elliptic Curve Point module. + * + * MBEDTLS_ECP__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use your + * alternative core implementation of elliptic curve arithmetic. Keep in mind + * that function prototypes should remain the same. + * + * This partially replaces one function. The header file from mbed TLS is still + * used, in contrast to the MBEDTLS_ECP_ALT flag. The original implementation + * is still present and it is used for group structures not supported by the + * alternative. + * + * The original implementation can in addition be removed by setting the + * MBEDTLS_ECP_NO_FALLBACK option, in which case any function for which the + * corresponding MBEDTLS_ECP__FUNCTION_NAME__ALT macro is defined will not be + * able to fallback to curves not supported by the alternative implementation. + * + * Any of these options become available by defining MBEDTLS_ECP_INTERNAL_ALT + * and implementing the following functions: + * unsigned char mbedtls_internal_ecp_grp_capable( + * const mbedtls_ecp_group *grp ) + * int mbedtls_internal_ecp_init( const mbedtls_ecp_group *grp ) + * void mbedtls_internal_ecp_free( const mbedtls_ecp_group *grp ) + * The mbedtls_internal_ecp_grp_capable function should return 1 if the + * replacement functions implement arithmetic for the given group and 0 + * otherwise. + * The functions mbedtls_internal_ecp_init and mbedtls_internal_ecp_free are + * called before and after each point operation and provide an opportunity to + * implement optimized set up and tear down instructions. + * + * Example: In case you set MBEDTLS_ECP_INTERNAL_ALT and + * MBEDTLS_ECP_DOUBLE_JAC_ALT, mbed TLS will still provide the ecp_double_jac() + * function, but will use your mbedtls_internal_ecp_double_jac() if the group + * for the operation is supported by your implementation (i.e. your + * mbedtls_internal_ecp_grp_capable() function returns 1 for this group). If the + * group is not supported by your implementation, then the original mbed TLS + * implementation of ecp_double_jac() is used instead, unless this fallback + * behaviour is disabled by setting MBEDTLS_ECP_NO_FALLBACK (in which case + * ecp_double_jac() will return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE). + * + * The function prototypes and the definition of mbedtls_ecp_group and + * mbedtls_ecp_point will not change based on MBEDTLS_ECP_INTERNAL_ALT, so your + * implementation of mbedtls_internal_ecp__function_name__ must be compatible + * with their definitions. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * function. + */ +/* Required for all the functions in this section */ +//#define MBEDTLS_ECP_INTERNAL_ALT +/* Turn off software fallback for curves not supported in hardware */ +//#define MBEDTLS_ECP_NO_FALLBACK +/* Support for Weierstrass curves with Jacobi representation */ +//#define MBEDTLS_ECP_RANDOMIZE_JAC_ALT +//#define MBEDTLS_ECP_ADD_MIXED_ALT +//#define MBEDTLS_ECP_DOUBLE_JAC_ALT +//#define MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT +//#define MBEDTLS_ECP_NORMALIZE_JAC_ALT +/* Support for curves with Montgomery arithmetic */ +//#define MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT +//#define MBEDTLS_ECP_RANDOMIZE_MXZ_ALT +//#define MBEDTLS_ECP_NORMALIZE_MXZ_ALT + +/** + * \def MBEDTLS_ENTROPY_HARDWARE_ALT + * + * Uncomment this macro to let mbed TLS use your own implementation of a + * hardware entropy collector. + * + * Your function must be called \c mbedtls_hardware_poll(), have the same + * prototype as declared in library/entropy_poll.h, and accept NULL as first + * argument. + * + * Uncomment to use your own hardware entropy collector. + */ +//#define MBEDTLS_ENTROPY_HARDWARE_ALT + +/** + * \def MBEDTLS_AES_ROM_TABLES + * + * Use precomputed AES tables stored in ROM. + * + * Uncomment this macro to use precomputed AES tables stored in ROM. + * Comment this macro to generate AES tables in RAM at runtime. + * + * Tradeoff: Using precomputed ROM tables reduces RAM usage by ~8kb + * (or ~2kb if \c MBEDTLS_AES_FEWER_TABLES is used) and reduces the + * initialization time before the first AES operation can be performed. + * It comes at the cost of additional ~8kb ROM use (resp. ~2kb if \c + * MBEDTLS_AES_FEWER_TABLES below is used), and potentially degraded + * performance if ROM access is slower than RAM access. + * + * This option is independent of \c MBEDTLS_AES_FEWER_TABLES. + * + */ +//#define MBEDTLS_AES_ROM_TABLES + +/** + * \def MBEDTLS_AES_FEWER_TABLES + * + * Use less ROM/RAM for AES tables. + * + * Uncommenting this macro omits 75% of the AES tables from + * ROM / RAM (depending on the value of \c MBEDTLS_AES_ROM_TABLES) + * by computing their values on the fly during operations + * (the tables are entry-wise rotations of one another). + * + * Tradeoff: Uncommenting this reduces the RAM / ROM footprint + * by ~6kb but at the cost of more arithmetic operations during + * runtime. Specifically, one has to compare 4 accesses within + * different tables to 4 accesses with additional arithmetic + * operations within the same table. The performance gain/loss + * depends on the system and memory details. + * + * This option is independent of \c MBEDTLS_AES_ROM_TABLES. + * + */ +//#define MBEDTLS_AES_FEWER_TABLES + +/** + * \def MBEDTLS_CAMELLIA_SMALL_MEMORY + * + * Use less ROM for the Camellia implementation (saves about 768 bytes). + * + * Uncomment this macro to use less memory for Camellia. + */ +//#define MBEDTLS_CAMELLIA_SMALL_MEMORY + +/** + * \def MBEDTLS_CIPHER_MODE_CBC + * + * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CBC + +/** + * \def MBEDTLS_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CFB + +/** + * \def MBEDTLS_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CTR + +/** + * \def MBEDTLS_CIPHER_MODE_OFB + * + * Enable Output Feedback mode (OFB) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_OFB + +/** + * \def MBEDTLS_CIPHER_MODE_XTS + * + * Enable Xor-encrypt-xor with ciphertext stealing mode (XTS) for AES. + */ +#define MBEDTLS_CIPHER_MODE_XTS + +/** + * \def MBEDTLS_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * To enable the following ciphersuites: + * MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_RSA_WITH_NULL_SHA256 + * MBEDTLS_TLS_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_RSA_WITH_NULL_MD5 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_PSK_WITH_NULL_SHA + * + * Uncomment this macro to enable the NULL cipher and ciphersuites + */ +//#define MBEDTLS_CIPHER_NULL_CIPHER + +/** + * \def MBEDTLS_CIPHER_PADDING_PKCS7 + * + * MBEDTLS_CIPHER_PADDING_XXX: Uncomment or comment macros to add support for + * specific padding modes in the cipher layer with cipher modes that support + * padding (e.g. CBC) + * + * If you disable all padding modes, only full blocks can be used with CBC. + * + * Enable padding modes in the cipher layer. + */ +#define MBEDTLS_CIPHER_PADDING_PKCS7 +#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS +#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN +#define MBEDTLS_CIPHER_PADDING_ZEROS + +/** \def MBEDTLS_CTR_DRBG_USE_128_BIT_KEY + * + * Uncomment this macro to use a 128-bit key in the CTR_DRBG module. + * By default, CTR_DRBG uses a 256-bit key. + */ +//#define MBEDTLS_CTR_DRBG_USE_128_BIT_KEY + +/** + * \def MBEDTLS_ECP_DP_SECP192R1_ENABLED + * + * MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve + * module. By default all supported curves are enabled. + * + * Comment macros to disable the curve and functions for it + */ +/* Short Weierstrass curves (supporting ECP, ECDH, ECDSA) */ +#define MBEDTLS_ECP_DP_SECP192R1_ENABLED +#define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_SECP521R1_ENABLED +#define MBEDTLS_ECP_DP_SECP192K1_ENABLED +#define MBEDTLS_ECP_DP_SECP224K1_ENABLED +#define MBEDTLS_ECP_DP_SECP256K1_ENABLED +#define MBEDTLS_ECP_DP_BP256R1_ENABLED +#define MBEDTLS_ECP_DP_BP384R1_ENABLED +#define MBEDTLS_ECP_DP_BP512R1_ENABLED +/* Montgomery curves (supporting ECP) */ +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_ECP_DP_CURVE448_ENABLED + +/** + * \def MBEDTLS_ECP_NIST_OPTIM + * + * Enable specific 'modulo p' routines for each NIST prime. + * Depending on the prime and architecture, makes operations 4 to 8 times + * faster on the corresponding curve. + * + * Comment this macro to disable NIST curves optimisation. + */ +#define MBEDTLS_ECP_NIST_OPTIM + +/** + * \def MBEDTLS_ECP_RESTARTABLE + * + * Enable "non-blocking" ECC operations that can return early and be resumed. + * + * This allows various functions to pause by returning + * #MBEDTLS_ERR_ECP_IN_PROGRESS (or, for functions in the SSL module, + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) and then be called later again in + * order to further progress and eventually complete their operation. This is + * controlled through mbedtls_ecp_set_max_ops() which limits the maximum + * number of ECC operations a function may perform before pausing; see + * mbedtls_ecp_set_max_ops() for more information. + * + * This is useful in non-threaded environments if you want to avoid blocking + * for too long on ECC (and, hence, X.509 or SSL/TLS) operations. + * + * Uncomment this macro to enable restartable ECC computations. + * + * \note This option only works with the default software implementation of + * elliptic curve functionality. It is incompatible with + * MBEDTLS_ECP_ALT, MBEDTLS_ECDH_XXX_ALT, MBEDTLS_ECDSA_XXX_ALT. + */ +//#define MBEDTLS_ECP_RESTARTABLE + +/** + * \def MBEDTLS_ECDSA_DETERMINISTIC + * + * Enable deterministic ECDSA (RFC 6979). + * Standard ECDSA is "fragile" in the sense that lack of entropy when signing + * may result in a compromise of the long-term signing key. This is avoided by + * the deterministic variant. + * + * Requires: MBEDTLS_HMAC_DRBG_C, MBEDTLS_ECDSA_C + * + * Comment this macro to disable deterministic ECDSA. + */ +#define MBEDTLS_ECDSA_DETERMINISTIC + +/** + * \def MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + * + * Enable the PSK based ciphersuite modes in SSL / TLS. + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED + * + * Enable the DHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_DHM_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED + * + * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED + * + * Enable the RSA-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_RSA_ENABLED + * + * Enable the RSA-only based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + */ +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED + * + * Enable the DHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_DHM_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED + * + * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + * + * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_ECDSA_C, MBEDTLS_X509_CRT_PARSE_C, + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + * + * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_ECDSA_C, MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED + * + * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_RSA_C, MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED + * + * Enable the ECJPAKE based ciphersuite modes in SSL / TLS. + * + * \warning This is currently experimental. EC J-PAKE support is based on the + * Thread v1.0.0 specification; incompatible changes to the specification + * might still happen. For this reason, this is disabled by default. + * + * Requires: MBEDTLS_ECJPAKE_C + * MBEDTLS_SHA256_C + * MBEDTLS_ECP_DP_SECP256R1_ENABLED + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + */ +//#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED + +/** + * \def MBEDTLS_PK_PARSE_EC_EXTENDED + * + * Enhance support for reading EC keys using variants of SEC1 not allowed by + * RFC 5915 and RFC 5480. + * + * Currently this means parsing the SpecifiedECDomain choice of EC + * parameters (only known groups are supported, not arbitrary domains, to + * avoid validation issues). + * + * Disable if you only need to support RFC 5915 + 5480 key formats. + */ +#define MBEDTLS_PK_PARSE_EC_EXTENDED + +/** + * \def MBEDTLS_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of mbedtls_strerror() in + * third party libraries easier when MBEDTLS_ERROR_C is disabled + * (no effect when MBEDTLS_ERROR_C is enabled). + * + * You can safely disable this if MBEDTLS_ERROR_C is enabled, or if you're + * not using mbedtls_strerror() or error_strerror() in your application. + * + * Disable if you run into name conflicts and want to really remove the + * mbedtls_strerror() + */ +#define MBEDTLS_ERROR_STRERROR_DUMMY + +/** + * \def MBEDTLS_GENPRIME + * + * Enable the prime-number generation code. + * + * Requires: MBEDTLS_BIGNUM_C + */ +#define MBEDTLS_GENPRIME + +/** + * \def MBEDTLS_FS_IO + * + * Enable functions that use the filesystem. + */ +#define MBEDTLS_FS_IO + +/** + * \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources in mbedtls_entropy_init(). + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. + */ +//#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/** + * \def MBEDTLS_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. + */ +//#define MBEDTLS_NO_PLATFORM_ENTROPY + +/** + * \def MBEDTLS_ENTROPY_FORCE_SHA256 + * + * Force the entropy accumulator to use a SHA-256 accumulator instead of the + * default SHA-512 based one (if both are available). + * + * Requires: MBEDTLS_SHA256_C + * + * On 32-bit systems SHA-256 can be much faster than SHA-512. Use this option + * if you have performance concerns. + * + * This option is only useful if both MBEDTLS_SHA256_C and + * MBEDTLS_SHA512_C are defined. Otherwise the available hash module is used. + */ +//#define MBEDTLS_ENTROPY_FORCE_SHA256 + +/** + * \def MBEDTLS_ENTROPY_NV_SEED + * + * Enable the non-volatile (NV) seed file-based entropy source. + * (Also enables the NV seed read/write functions in the platform layer) + * + * This is crucial (if not required) on systems that do not have a + * cryptographic entropy source (in hardware or kernel) available. + * + * Requires: MBEDTLS_ENTROPY_C, MBEDTLS_PLATFORM_C + * + * \note The read/write functions that are used by the entropy source are + * determined in the platform layer, and can be modified at runtime and/or + * compile-time depending on the flags (MBEDTLS_PLATFORM_NV_SEED_*) used. + * + * \note If you use the default implementation functions that read a seedfile + * with regular fopen(), please make sure you make a seedfile with the + * proper name (defined in MBEDTLS_PLATFORM_STD_NV_SEED_FILE) and at + * least MBEDTLS_ENTROPY_BLOCK_SIZE bytes in size that can be read from + * and written to or you will get an entropy source error! The default + * implementation will only use the first MBEDTLS_ENTROPY_BLOCK_SIZE + * bytes from the file. + * + * \note The entropy collector will write to the seed file before entropy is + * given to an external source, to update it. + */ +//#define MBEDTLS_ENTROPY_NV_SEED + +/* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER + * + * Enable key identifiers that encode a key owner identifier. + * + * The owner of a key is identified by a value of type ::mbedtls_key_owner_id_t + * which is currently hard-coded to be int32_t. + * + * Note that this option is meant for internal use only and may be removed + * without notice. It is incompatible with MBEDTLS_USE_PSA_CRYPTO. + */ +//#define MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER + +/** + * \def MBEDTLS_MEMORY_DEBUG + * + * Enable debugging of buffer allocator memory issues. Automatically prints + * (to stderr) all (fatal) messages on memory allocation issues. Enables + * function for 'debug output' of allocated memory. + * + * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C + * + * Uncomment this macro to let the buffer allocator print out error messages. + */ +//#define MBEDTLS_MEMORY_DEBUG + +/** + * \def MBEDTLS_MEMORY_BACKTRACE + * + * Include backtrace information with each allocated block. + * + * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C + * GLIBC-compatible backtrace() an backtrace_symbols() support + * + * Uncomment this macro to include backtrace information + */ +//#define MBEDTLS_MEMORY_BACKTRACE + +/** + * \def MBEDTLS_PK_RSA_ALT_SUPPORT + * + * Support external private RSA keys (eg from a HSM) in the PK layer. + * + * Comment this macro to disable support for external private RSA keys. + */ +#define MBEDTLS_PK_RSA_ALT_SUPPORT + +/** + * \def MBEDTLS_PKCS1_V15 + * + * Enable support for PKCS#1 v1.5 encoding. + * + * Requires: MBEDTLS_RSA_C + * + * This enables support for PKCS#1 v1.5 operations. + */ +#define MBEDTLS_PKCS1_V15 + +/** + * \def MBEDTLS_PKCS1_V21 + * + * Enable support for PKCS#1 v2.1 encoding. + * + * Requires: MBEDTLS_MD_C, MBEDTLS_RSA_C + * + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + */ +#define MBEDTLS_PKCS1_V21 + +/** \def MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS + * + * Enable support for platform built-in keys. If you enable this feature, + * you must implement the function mbedtls_psa_platform_get_builtin_key(). + * See the documentation of that function for more information. + * + * Built-in keys are typically derived from a hardware unique key or + * stored in a secure element. + * + * Requires: MBEDTLS_PSA_CRYPTO_C. + * + * \warning This interface is experimental and may change or be removed + * without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS + +/** \def MBEDTLS_PSA_CRYPTO_CLIENT + * + * Enable support for PSA crypto client. + * + * \note This option allows to include the code necessary for a PSA + * crypto client when the PSA crypto implementation is not included in + * the library (MBEDTLS_PSA_CRYPTO_C disabled). The code included is the + * code to set and get PSA key attributes. + * The development of PSA drivers partially relying on the library to + * fulfill the hardware gaps is another possible usage of this option. + * + * \warning This interface is experimental and may change or be removed + * without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_CLIENT + +/** \def MBEDTLS_PSA_CRYPTO_DRIVERS + * + * Enable support for the experimental PSA crypto driver interface. + * + * Requires: MBEDTLS_PSA_CRYPTO_C + * + * \warning This interface is experimental and may change or be removed + * without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_DRIVERS + +/** \def MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + * + * Make the PSA Crypto module use an external random generator provided + * by a driver, instead of Mbed TLS's entropy and DRBG modules. + * + * \note This random generator must deliver random numbers with cryptographic + * quality and high performance. It must supply unpredictable numbers + * with a uniform distribution. The implementation of this function + * is responsible for ensuring that the random generator is seeded + * with sufficient entropy. If you have a hardware TRNG which is slow + * or delivers non-uniform output, declare it as an entropy source + * with mbedtls_entropy_add_source() instead of enabling this option. + * + * If you enable this option, you must configure the type + * ::mbedtls_psa_external_random_context_t in psa/crypto_platform.h + * and define a function called mbedtls_psa_external_get_random() + * with the following prototype: + * ``` + * psa_status_t mbedtls_psa_external_get_random( + * mbedtls_psa_external_random_context_t *context, + * uint8_t *output, size_t output_size, size_t *output_length); + * ); + * ``` + * The \c context value is initialized to 0 before the first call. + * The function must fill the \c output buffer with \p output_size bytes + * of random data and set \c *output_length to \p output_size. + * + * Requires: MBEDTLS_PSA_CRYPTO_C + * + * \warning If you enable this option, code that uses the PSA cryptography + * interface will not use any of the entropy sources set up for + * the entropy module, nor the NV seed that MBEDTLS_ENTROPY_NV_SEED + * enables. + * + * \note This option is experimental and may be removed without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + +/** + * \def MBEDTLS_PSA_CRYPTO_SPM + * + * When MBEDTLS_PSA_CRYPTO_SPM is defined, the code is built for SPM (Secure + * Partition Manager) integration which separates the code into two parts: a + * NSPE (Non-Secure Process Environment) and an SPE (Secure Process + * Environment). + * + * Module: library/psa_crypto.c + * Requires: MBEDTLS_PSA_CRYPTO_C + * + */ +//#define MBEDTLS_PSA_CRYPTO_SPM + +/** + * \def MBEDTLS_PSA_INJECT_ENTROPY + * + * Enable support for entropy injection at first boot. This feature is + * required on systems that do not have a built-in entropy source (TRNG). + * This feature is currently not supported on systems that have a built-in + * entropy source. + * + * Requires: MBEDTLS_PSA_CRYPTO_STORAGE_C, MBEDTLS_ENTROPY_NV_SEED + * + */ +//#define MBEDTLS_PSA_INJECT_ENTROPY + +/** + * \def MBEDTLS_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem + * for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * + */ +//#define MBEDTLS_RSA_NO_CRT + +/** + * \def MBEDTLS_SELF_TEST + * + * Enable the checkup functions (*_self_test). + */ +#define MBEDTLS_SELF_TEST + +/** + * \def MBEDTLS_SHA256_SMALLER + * + * Enable an implementation of SHA-256 that has lower ROM footprint but also + * lower performance. + * + * The default implementation is meant to be a reasonnable compromise between + * performance and size. This version optimizes more aggressively for size at + * the expense of performance. Eg on Cortex-M4 it reduces the size of + * mbedtls_sha256_process() from ~2KB to ~0.5KB for a performance hit of about + * 30%. + * + * Uncomment to enable the smaller implementation of SHA256. + */ +//#define MBEDTLS_SHA256_SMALLER + +/** + * \def MBEDTLS_SHA512_SMALLER + * + * Enable an implementation of SHA-512 that has lower ROM footprint but also + * lower performance. + * + * Uncomment to enable the smaller implementation of SHA512. + */ +//#define MBEDTLS_SHA512_SMALLER + +/** + * \def MBEDTLS_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, mbed TLS can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define MBEDTLS_SSL_ALL_ALERT_MESSAGES + +/** + * \def MBEDTLS_SSL_DTLS_CONNECTION_ID + * + * Enable support for the DTLS Connection ID extension + * (version draft-ietf-tls-dtls-connection-id-05, + * https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05) + * which allows to identify DTLS connections across changes + * in the underlying transport. + * + * Setting this option enables the SSL APIs `mbedtls_ssl_set_cid()`, + * `mbedtls_ssl_get_peer_cid()` and `mbedtls_ssl_conf_cid()`. + * See the corresponding documentation for more information. + * + * \warning The Connection ID extension is still in draft state. + * We make no stability promises for the availability + * or the shape of the API controlled by this option. + * + * The maximum lengths of outgoing and incoming CIDs can be configured + * through the options + * - MBEDTLS_SSL_CID_OUT_LEN_MAX + * - MBEDTLS_SSL_CID_IN_LEN_MAX. + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Uncomment to enable the Connection ID extension. + */ +//#define MBEDTLS_SSL_DTLS_CONNECTION_ID + +/** + * \def MBEDTLS_SSL_ASYNC_PRIVATE + * + * Enable asynchronous external private key operations in SSL. This allows + * you to configure an SSL connection to call an external cryptographic + * module to perform private key operations instead of performing the + * operation inside the library. + * + */ +//#define MBEDTLS_SSL_ASYNC_PRIVATE + +/** + * \def MBEDTLS_SSL_CONTEXT_SERIALIZATION + * + * Enable serialization of the TLS context structures, through use of the + * functions mbedtls_ssl_context_save() and mbedtls_ssl_context_load(). + * + * This pair of functions allows one side of a connection to serialize the + * context associated with the connection, then free or re-use that context + * while the serialized state is persisted elsewhere, and finally deserialize + * that state to a live context for resuming read/write operations on the + * connection. From a protocol perspective, the state of the connection is + * unaffected, in particular this is entirely transparent to the peer. + * + * Note: this is distinct from TLS session resumption, which is part of the + * protocol and fully visible by the peer. TLS session resumption enables + * establishing new connections associated to a saved session with shorter, + * lighter handshakes, while context serialization is a local optimization in + * handling a single, potentially long-lived connection. + * + * Enabling these APIs makes some SSL structures larger, as 64 extra bytes are + * saved after the handshake to allow for more efficient serialization, so if + * you don't need this feature you'll save RAM by disabling it. + * + * Comment to disable the context serialization APIs. + */ +#define MBEDTLS_SSL_CONTEXT_SERIALIZATION + +/** + * \def MBEDTLS_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * + */ +//#define MBEDTLS_SSL_DEBUG_ALL + +/** \def MBEDTLS_SSL_ENCRYPT_THEN_MAC + * + * Enable support for Encrypt-then-MAC, RFC 7366. + * + * This allows peers that both support it to use a more robust protection for + * ciphersuites using CBC, providing deep resistance against timing attacks + * on the padding or underlying cipher. + * + * This only affects CBC ciphersuites, and is useless if none is defined. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for Encrypt-then-MAC + */ +#define MBEDTLS_SSL_ENCRYPT_THEN_MAC + +/** \def MBEDTLS_SSL_EXTENDED_MASTER_SECRET + * + * Enable support for RFC 7627: Session Hash and Extended Master Secret + * Extension. + * + * This was introduced as "the proper fix" to the Triple Handshake familiy of + * attacks, but it is recommended to always use it (even if you disable + * renegotiation), since it actually fixes a more fundamental issue in the + * original SSL/TLS design, and has implications beyond Triple Handshake. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for Extended Master Secret. + */ +#define MBEDTLS_SSL_EXTENDED_MASTER_SECRET + +/** + * \def MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + * + * This option controls the availability of the API mbedtls_ssl_get_peer_cert() + * giving access to the peer's certificate after completion of the handshake. + * + * Unless you need mbedtls_ssl_peer_cert() in your application, it is + * recommended to disable this option for reduced RAM usage. + * + * \note If this option is disabled, mbedtls_ssl_get_peer_cert() is still + * defined, but always returns \c NULL. + * + * \note This option has no influence on the protection against the + * triple handshake attack. Even if it is disabled, Mbed TLS will + * still ensure that certificates do not change during renegotiation, + * for exaple by keeping a hash of the peer's certificate. + * + * Comment this macro to disable storing the peer's certificate + * after the handshake. + */ +#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + +/** + * \def MBEDTLS_SSL_RENEGOTIATION + * + * Enable support for TLS renegotiation. + * + * The two main uses of renegotiation are (1) refresh keys on long-lived + * connections and (2) client authentication after the initial handshake. + * If you don't need renegotiation, it's probably better to disable it, since + * it has been associated with security issues in the past and is easy to + * misuse/misunderstand. + * + * Comment this to disable support for renegotiation. + * + * \note Even if this option is disabled, both client and server are aware + * of the Renegotiation Indication Extension (RFC 5746) used to + * prevent the SSL renegotiation attack (see RFC 5746 Sect. 1). + * (See \c mbedtls_ssl_conf_legacy_renegotiation for the + * configuration of this extension). + * + */ +#define MBEDTLS_SSL_RENEGOTIATION + +/** + * \def MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + * + * Enable support for RFC 6066 max_fragment_length extension in SSL. + * + * Comment this macro to disable support for the max_fragment_length extension + */ +#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + +/** + * \def MBEDTLS_SSL_PROTO_TLS1_2 + * + * Enable support for TLS 1.2 (and DTLS 1.2 if DTLS is enabled). + * + * Requires: MBEDTLS_SHA1_C or MBEDTLS_SHA256_C or MBEDTLS_SHA512_C + * (Depends on ciphersuites) + * + * Comment this macro to disable support for TLS 1.2 / DTLS 1.2 + */ +#define MBEDTLS_SSL_PROTO_TLS1_2 + +/** + * \def MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL + * + * This macro is used to selectively enable experimental parts + * of the code that contribute to the ongoing development of + * the prototype TLS 1.3 and DTLS 1.3 implementation, and provide + * no other purpose. + * + * \warning TLS 1.3 and DTLS 1.3 aren't yet supported in Mbed TLS, + * and no feature exposed through this macro is part of the + * public API. In particular, features under the control + * of this macro are experimental and don't come with any + * stability guarantees. + * + * Uncomment this macro to enable experimental and partial + * functionality specific to TLS 1.3. + */ +//#define MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL + +/** + * \def MBEDTLS_SSL_PROTO_DTLS + * + * Enable support for DTLS (all available versions). + * + * Enable this and MBEDTLS_SSL_PROTO_TLS1_2 to enable DTLS 1.2. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for DTLS + */ +#define MBEDTLS_SSL_PROTO_DTLS + +/** + * \def MBEDTLS_SSL_ALPN + * + * Enable support for RFC 7301 Application Layer Protocol Negotiation. + * + * Comment this macro to disable support for ALPN. + */ +#define MBEDTLS_SSL_ALPN + +/** + * \def MBEDTLS_SSL_DTLS_ANTI_REPLAY + * + * Enable support for the anti-replay mechanism in DTLS. + * + * Requires: MBEDTLS_SSL_TLS_C + * MBEDTLS_SSL_PROTO_DTLS + * + * \warning Disabling this is often a security risk! + * See mbedtls_ssl_conf_dtls_anti_replay() for details. + * + * Comment this to disable anti-replay in DTLS. + */ +#define MBEDTLS_SSL_DTLS_ANTI_REPLAY + +/** + * \def MBEDTLS_SSL_DTLS_HELLO_VERIFY + * + * Enable support for HelloVerifyRequest on DTLS servers. + * + * This feature is highly recommended to prevent DTLS servers being used as + * amplifiers in DoS attacks against other hosts. It should always be enabled + * unless you know for sure amplification cannot be a problem in the + * environment in which your server operates. + * + * \warning Disabling this can ba a security risk! (see above) + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Comment this to disable support for HelloVerifyRequest. + */ +#define MBEDTLS_SSL_DTLS_HELLO_VERIFY + +/** + * \def MBEDTLS_SSL_DTLS_SRTP + * + * Enable support for negotiation of DTLS-SRTP (RFC 5764) + * through the use_srtp extension. + * + * \note This feature provides the minimum functionality required + * to negotiate the use of DTLS-SRTP and to allow the derivation of + * the associated SRTP packet protection key material. + * In particular, the SRTP packet protection itself, as well as the + * demultiplexing of RTP and DTLS packets at the datagram layer + * (see Section 5 of RFC 5764), are not handled by this feature. + * Instead, after successful completion of a handshake negotiating + * the use of DTLS-SRTP, the extended key exporter API + * mbedtls_ssl_conf_export_keys_cb() should be used to implement + * the key exporter described in Section 4.2 of RFC 5764 and RFC 5705 + * (this is implemented in the SSL example programs). + * The resulting key should then be passed to an SRTP stack. + * + * Setting this option enables the runtime API + * mbedtls_ssl_conf_dtls_srtp_protection_profiles() + * through which the supported DTLS-SRTP protection + * profiles can be configured. You must call this API at + * runtime if you wish to negotiate the use of DTLS-SRTP. + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Uncomment this to enable support for use_srtp extension. + */ +//#define MBEDTLS_SSL_DTLS_SRTP + +/** + * \def MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + * + * Enable server-side support for clients that reconnect from the same port. + * + * Some clients unexpectedly close the connection and try to reconnect using the + * same source port. This needs special support from the server to handle the + * new connection securely, as described in section 4.2.8 of RFC 6347. This + * flag enables that support. + * + * Requires: MBEDTLS_SSL_DTLS_HELLO_VERIFY + * + * Comment this to disable support for clients reusing the source port. + */ +#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + +/** + * \def MBEDTLS_SSL_SESSION_TICKETS + * + * Enable support for RFC 5077 session tickets in SSL. + * Client-side, provides full support for session tickets (maintenance of a + * session store remains the responsibility of the application, though). + * Server-side, you also need to provide callbacks for writing and parsing + * tickets, including authenticated encryption and key management. Example + * callbacks are provided by MBEDTLS_SSL_TICKET_C. + * + * Comment this macro to disable support for SSL session tickets + */ +#define MBEDTLS_SSL_SESSION_TICKETS + +/** + * \def MBEDTLS_SSL_EXPORT_KEYS + * + * Enable support for exporting key block and master secret. + * This is required for certain users of TLS, e.g. EAP-TLS. + * + * Comment this macro to disable support for key export + */ +#define MBEDTLS_SSL_EXPORT_KEYS + +/** + * \def MBEDTLS_SSL_SERVER_NAME_INDICATION + * + * Enable support for RFC 6066 server name indication (SNI) in SSL. + * + * Requires: MBEDTLS_X509_CRT_PARSE_C + * + * Comment this macro to disable support for server name indication in SSL + */ +#define MBEDTLS_SSL_SERVER_NAME_INDICATION + +/** + * \def MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH + * + * When this option is enabled, the SSL buffer will be resized automatically + * based on the negotiated maximum fragment length in each direction. + * + * Requires: MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + */ +//#define MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH + +/** + * \def MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN + * + * Enable testing of the constant-flow nature of some sensitive functions with + * clang's MemorySanitizer. This causes some existing tests to also test + * this non-functional property of the code under test. + * + * This setting requires compiling with clang -fsanitize=memory. The test + * suites can then be run normally. + * + * \warning This macro is only used for extended testing; it is not considered + * part of the library's API, so it may change or disappear at any time. + * + * Uncomment to enable testing of the constant-flow nature of selected code. + */ +//#define MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN + +/** + * \def MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND + * + * Enable testing of the constant-flow nature of some sensitive functions with + * valgrind's memcheck tool. This causes some existing tests to also test + * this non-functional property of the code under test. + * + * This setting requires valgrind headers for building, and is only useful for + * testing if the tests suites are run with valgrind's memcheck. This can be + * done for an individual test suite with 'valgrind ./test_suite_xxx', or when + * using CMake, this can be done for all test suites with 'make memcheck'. + * + * \warning This macro is only used for extended testing; it is not considered + * part of the library's API, so it may change or disappear at any time. + * + * Uncomment to enable testing of the constant-flow nature of selected code. + */ +//#define MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND + +/** + * \def MBEDTLS_TEST_HOOKS + * + * Enable features for invasive testing such as introspection functions and + * hooks for fault injection. This enables additional unit tests. + * + * Merely enabling this feature should not change the behavior of the product. + * It only adds new code, and new branching points where the default behavior + * is the same as when this feature is disabled. + * However, this feature increases the attack surface: there is an added + * risk of vulnerabilities, and more gadgets that can make exploits easier. + * Therefore this feature must never be enabled in production. + * + * See `docs/architecture/testing/mbed-crypto-invasive-testing.md` for more + * information. + * + * Uncomment to enable invasive tests. + */ +//#define MBEDTLS_TEST_HOOKS + +/** + * \def MBEDTLS_THREADING_ALT + * + * Provide your own alternate threading implementation. + * + * Requires: MBEDTLS_THREADING_C + * + * Uncomment this to allow your own alternate threading implementation. + */ +//#define MBEDTLS_THREADING_ALT + +/** + * \def MBEDTLS_THREADING_PTHREAD + * + * Enable the pthread wrapper layer for the threading layer. + * + * Requires: MBEDTLS_THREADING_C + * + * Uncomment this to enable pthread mutexes. + */ +//#define MBEDTLS_THREADING_PTHREAD + +/** + * \def MBEDTLS_USE_PSA_CRYPTO + * + * Make the X.509 and TLS library use PSA for cryptographic operations, and + * enable new APIs for using keys handled by PSA Crypto. + * + * \note Development of this option is currently in progress, and parts of Mbed + * TLS's X.509 and TLS modules are not ported to PSA yet. However, these parts + * will still continue to work as usual, so enabling this option should not + * break backwards compatibility. + * + * \warning The PSA Crypto API is in beta stage. While you're welcome to + * experiment using it, incompatible API changes are still possible, and some + * parts may not have reached the same quality as the rest of Mbed TLS yet. + * + * \warning This option enables new Mbed TLS APIs that are dependent on the + * PSA Crypto API, so can't come with the same stability guarantees as the + * rest of the Mbed TLS APIs. You're welcome to experiment with them, but for + * now, access to these APIs is opt-in (via enabling the present option), in + * order to clearly differentiate them from the stable Mbed TLS APIs. + * + * Requires: MBEDTLS_PSA_CRYPTO_C. + * + * Uncomment this to enable internal use of PSA Crypto and new associated APIs. + */ +//#define MBEDTLS_USE_PSA_CRYPTO + +/** + * \def MBEDTLS_PSA_CRYPTO_CONFIG + * + * This setting allows support for cryptographic mechanisms through the PSA + * API to be configured separately from support through the mbedtls API. + * + * Uncomment this to enable use of PSA Crypto configuration settings which + * can be found in include/psa/crypto_config.h. + * + * This feature is still experimental and is not ready for production since + * it is not completed. + */ +//#define MBEDTLS_PSA_CRYPTO_CONFIG + +/** + * \def MBEDTLS_VERSION_FEATURES + * + * Allow run-time checking of compile-time enabled features. Thus allowing users + * to check at run-time if the library is for instance compiled with threading + * support via mbedtls_version_check_feature(). + * + * Requires: MBEDTLS_VERSION_C + * + * Comment this to disable run-time checking and save ROM space + */ +#define MBEDTLS_VERSION_FEATURES + +/** + * \def MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK + * + * If set, this enables the X.509 API `mbedtls_x509_crt_verify_with_ca_cb()` + * and the SSL API `mbedtls_ssl_conf_ca_cb()` which allow users to configure + * the set of trusted certificates through a callback instead of a linked + * list. + * + * This is useful for example in environments where a large number of trusted + * certificates is present and storing them in a linked list isn't efficient + * enough, or when the set of trusted certificates changes frequently. + * + * See the documentation of `mbedtls_x509_crt_verify_with_ca_cb()` and + * `mbedtls_ssl_conf_ca_cb()` for more information. + * + * Uncomment to enable trusted certificate callbacks. + */ +//#define MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK + +/** + * \def MBEDTLS_X509_REMOVE_INFO + * + * Disable mbedtls_x509_*_info() and related APIs. + * + * Uncomment to omit mbedtls_x509_*_info(), as well as mbedtls_debug_print_crt() + * and other functions/constants only used by these functions, thus reducing + * the code footprint by several KB. + */ +//#define MBEDTLS_X509_REMOVE_INFO + +/** + * \def MBEDTLS_X509_RSASSA_PSS_SUPPORT + * + * Enable parsing and verification of X.509 certificates, CRLs and CSRS + * signed with RSASSA-PSS (aka PKCS#1 v2.1). + * + * Comment this macro to disallow using RSASSA-PSS in certificates. + */ +#define MBEDTLS_X509_RSASSA_PSS_SUPPORT +/* \} name SECTION: mbed TLS feature support */ + +/** + * \name SECTION: mbed TLS modules + * + * This section enables or disables entire modules in mbed TLS + * \{ + */ + +/** + * \def MBEDTLS_AESNI_C + * + * Enable AES-NI support on x86-64. + * + * Module: library/aesni.c + * Caller: library/aes.c + * + * Requires: MBEDTLS_HAVE_ASM + * + * This modules adds support for the AES-NI instructions on x86-64 + */ +#define MBEDTLS_AESNI_C + +/** + * \def MBEDTLS_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/cipher.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA + * + * PEM_PARSE uses AES for decrypting encrypted keys. + */ +#define MBEDTLS_AES_C + +/** + * \def MBEDTLS_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509.c + * library/dhm.c + * library/pkcs12.c + * library/pkcs5.c + * library/pkparse.c + */ +#define MBEDTLS_ASN1_PARSE_C + +/** + * \def MBEDTLS_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + * Caller: library/ecdsa.c + * library/pkwrite.c + * library/x509_create.c + * library/x509write_crt.c + * library/x509write_csr.c + */ +#define MBEDTLS_ASN1_WRITE_C + +/** + * \def MBEDTLS_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +#define MBEDTLS_BASE64_C + +/** + * \def MBEDTLS_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * Caller: library/dhm.c + * library/ecp.c + * library/ecdsa.c + * library/rsa.c + * library/rsa_alt_helpers.c + * library/ssl_tls.c + * + * This module is required for RSA, DHM and ECC (ECDH, ECDSA) support. + */ +#define MBEDTLS_BIGNUM_C + +/** + * \def MBEDTLS_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_CAMELLIA_C + +/** + * \def MBEDTLS_ARIA_C + * + * Enable the ARIA block cipher. + * + * Module: library/aria.c + * Caller: library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * + * MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 + */ +#define MBEDTLS_ARIA_C + +/** + * \def MBEDTLS_CCM_C + * + * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher. + * + * Module: library/ccm.c + * + * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C + * + * This module enables the AES-CCM ciphersuites, if other requisites are + * enabled as well. + */ +#define MBEDTLS_CCM_C + +/** + * \def MBEDTLS_CHACHA20_C + * + * Enable the ChaCha20 stream cipher. + * + * Module: library/chacha20.c + */ +#define MBEDTLS_CHACHA20_C + +/** + * \def MBEDTLS_CHACHAPOLY_C + * + * Enable the ChaCha20-Poly1305 AEAD algorithm. + * + * Module: library/chachapoly.c + * + * This module requires: MBEDTLS_CHACHA20_C, MBEDTLS_POLY1305_C + */ +#define MBEDTLS_CHACHAPOLY_C + +/** + * \def MBEDTLS_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: library/ssl_tls.c + * + * Uncomment to enable generic cipher wrappers. + */ +#define MBEDTLS_CIPHER_C + +/** + * \def MBEDTLS_CMAC_C + * + * Enable the CMAC (Cipher-based Message Authentication Code) mode for block + * ciphers. + * + * \note When #MBEDTLS_CMAC_ALT is active, meaning that the underlying + * implementation of the CMAC algorithm is provided by an alternate + * implementation, that alternate implementation may opt to not support + * AES-192 or 3DES as underlying block ciphers for the CMAC operation. + * + * Module: library/cmac.c + * + * Requires: MBEDTLS_AES_C or MBEDTLS_DES_C + * + */ +#define MBEDTLS_CMAC_C + +/** + * \def MBEDTLS_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-based random generator. + * The CTR_DRBG generator uses AES-256 by default. + * To use AES-128 instead, enable \c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY above. + * + * \note To achieve a 256-bit security strength with CTR_DRBG, + * you must use AES-256 *and* use sufficient entropy. + * See ctr_drbg.h for more details. + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: MBEDTLS_AES_C + * + * This module provides the CTR_DRBG AES random number generator. + */ +#define MBEDTLS_CTR_DRBG_C + +/** + * \def MBEDTLS_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +#define MBEDTLS_DEBUG_C + +/** + * \def MBEDTLS_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/cipher.c + * + * PEM_PARSE uses DES/3DES for decrypting encrypted keys. + * + * \warning DES is considered a weak cipher and its use constitutes a + * security risk. We recommend considering stronger ciphers instead. + */ +#define MBEDTLS_DES_C + +/** + * \def MBEDTLS_DHM_C + * + * Enable the Diffie-Hellman-Merkle module. + * + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * DHE-RSA, DHE-PSK + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_DHM_C + +/** + * \def MBEDTLS_ECDH_C + * + * Enable the elliptic curve Diffie-Hellman library. + * + * Module: library/ecdh.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA, ECDHE-RSA, DHE-PSK + * + * Requires: MBEDTLS_ECP_C + */ +#define MBEDTLS_ECDH_C + +/** + * \def MBEDTLS_ECDSA_C + * + * Enable the elliptic curve DSA library. + * + * Module: library/ecdsa.c + * Caller: + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA + * + * Requires: MBEDTLS_ECP_C, MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C, + * and at least one MBEDTLS_ECP_DP_XXX_ENABLED for a + * short Weierstrass curve. + */ +#define MBEDTLS_ECDSA_C + +/** + * \def MBEDTLS_ECJPAKE_C + * + * Enable the elliptic curve J-PAKE library. + * + * \note EC J-PAKE support is based on the Thread v1.0.0 specification. + * It has not been reviewed for compliance with newer standards such as + * Thread v1.1 or RFC 8236. + * + * Module: library/ecjpake.c + * Caller: + * + * This module is used by the following key exchanges: + * ECJPAKE + * + * Requires: MBEDTLS_ECP_C, MBEDTLS_MD_C + */ +#define MBEDTLS_ECJPAKE_C + +/** + * \def MBEDTLS_ECP_C + * + * Enable the elliptic curve over GF(p) library. + * + * Module: library/ecp.c + * Caller: library/ecdh.c + * library/ecdsa.c + * library/ecjpake.c + * + * Requires: MBEDTLS_BIGNUM_C and at least one MBEDTLS_ECP_DP_XXX_ENABLED + */ +#define MBEDTLS_ECP_C + +/** + * \def MBEDTLS_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: MBEDTLS_SHA512_C or MBEDTLS_SHA256_C + * + * This module provides a generic entropy pool + */ +#define MBEDTLS_ENTROPY_C + +/** + * \def MBEDTLS_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables mbedtls_strerror(). + */ +#define MBEDTLS_ERROR_C + +/** + * \def MBEDTLS_GCM_C + * + * Enable the Galois/Counter Mode (GCM). + * + * Module: library/gcm.c + * + * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C or MBEDTLS_ARIA_C + * + * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other + * requisites are enabled as well. + */ +#define MBEDTLS_GCM_C + +/** + * \def MBEDTLS_HKDF_C + * + * Enable the HKDF algorithm (RFC 5869). + * + * Module: library/hkdf.c + * Caller: + * + * Requires: MBEDTLS_MD_C + * + * This module adds support for the Hashed Message Authentication Code + * (HMAC)-based key derivation function (HKDF). + */ +#define MBEDTLS_HKDF_C + +/** + * \def MBEDTLS_HMAC_DRBG_C + * + * Enable the HMAC_DRBG random generator. + * + * Module: library/hmac_drbg.c + * Caller: + * + * Requires: MBEDTLS_MD_C + * + * Uncomment to enable the HMAC_DRBG random number geerator. + */ +#define MBEDTLS_HMAC_DRBG_C + +/** + * \def MBEDTLS_NIST_KW_C + * + * Enable the Key Wrapping mode for 128-bit block ciphers, + * as defined in NIST SP 800-38F. Only KW and KWP modes + * are supported. At the moment, only AES is approved by NIST. + * + * Module: library/nist_kw.c + * + * Requires: MBEDTLS_AES_C and MBEDTLS_CIPHER_C + */ +#define MBEDTLS_NIST_KW_C + +/** + * \def MBEDTLS_MD_C + * + * Enable the generic message digest layer. + * + * Module: library/md.c + * Caller: + * + * Uncomment to enable generic message digest wrappers. + */ +#define MBEDTLS_MD_C + +/** + * \def MBEDTLS_MD5_C + * + * Enable the MD5 hash algorithm. + * + * Module: library/md5.c + * Caller: library/md.c + * library/pem.c + * library/ssl_tls.c + * + * This module is required for TLS 1.2 depending on the handshake parameters. + * Further, it is used for checking MD5-signed certificates, and for PBKDF1 + * when decrypting PEM-encoded encrypted keys. + * + * \warning MD5 is considered a weak message digest and its use constitutes a + * security risk. If possible, we recommend avoiding dependencies on + * it, and considering stronger message digests instead. + * + */ +#define MBEDTLS_MD5_C + +/** + * \def MBEDTLS_MEMORY_BUFFER_ALLOC_C + * + * Enable the buffer allocator implementation that makes use of a (stack) + * based buffer to 'allocate' dynamic memory. (replaces calloc() and free() + * calls) + * + * Module: library/memory_buffer_alloc.c + * + * Requires: MBEDTLS_PLATFORM_C + * MBEDTLS_PLATFORM_MEMORY (to use it within mbed TLS) + * + * Enable this module to enable the buffer memory allocator. + */ +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C + +/** + * \def MBEDTLS_NET_C + * + * Enable the TCP and UDP over IPv6/IPv4 networking routines. + * + * \note This module only works on POSIX/Unix (including Linux, BSD and OS X) + * and Windows. For other platforms, you'll want to disable it, and write your + * own networking callbacks to be passed to \c mbedtls_ssl_set_bio(). + * + * \note See also our Knowledge Base article about porting to a new + * environment: + * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * + * Module: library/net_sockets.c + * + * This module provides networking routines. + */ +#define MBEDTLS_NET_C + +/** + * \def MBEDTLS_OID_C + * + * Enable the OID database. + * + * Module: library/oid.c + * Caller: library/asn1write.c + * library/pkcs5.c + * library/pkparse.c + * library/pkwrite.c + * library/rsa.c + * library/x509.c + * library/x509_create.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * This modules translates between OIDs and internal values. + */ +#define MBEDTLS_OID_C + +/** + * \def MBEDTLS_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * Requires: MBEDTLS_HAVE_ASM + * + * This modules adds support for the VIA PadLock on x86. + */ +#define MBEDTLS_PADLOCK_C + +/** + * \def MBEDTLS_PEM_PARSE_C + * + * Enable PEM decoding / parsing. + * + * Module: library/pem.c + * Caller: library/dhm.c + * library/pkparse.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_BASE64_C + * + * This modules adds support for decoding / parsing PEM files. + */ +#define MBEDTLS_PEM_PARSE_C + +/** + * \def MBEDTLS_PEM_WRITE_C + * + * Enable PEM encoding / writing. + * + * Module: library/pem.c + * Caller: library/pkwrite.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * Requires: MBEDTLS_BASE64_C + * + * This modules adds support for encoding / writing PEM files. + */ +#define MBEDTLS_PEM_WRITE_C + +/** + * \def MBEDTLS_PK_C + * + * Enable the generic public (asymetric) key layer. + * + * Module: library/pk.c + * Caller: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: MBEDTLS_RSA_C or MBEDTLS_ECP_C + * + * Uncomment to enable generic public key wrappers. + */ +#define MBEDTLS_PK_C + +/** + * \def MBEDTLS_PK_PARSE_C + * + * Enable the generic public (asymetric) key parser. + * + * Module: library/pkparse.c + * Caller: library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_PK_C + * + * Uncomment to enable generic public key parse functions. + */ +#define MBEDTLS_PK_PARSE_C + +/** + * \def MBEDTLS_PK_WRITE_C + * + * Enable the generic public (asymetric) key writer. + * + * Module: library/pkwrite.c + * Caller: library/x509write.c + * + * Requires: MBEDTLS_PK_C + * + * Uncomment to enable generic public key write functions. + */ +#define MBEDTLS_PK_WRITE_C + +/** + * \def MBEDTLS_PKCS5_C + * + * Enable PKCS#5 functions. + * + * Module: library/pkcs5.c + * + * Requires: MBEDTLS_MD_C + * + * This module adds support for the PKCS#5 functions. + */ +#define MBEDTLS_PKCS5_C + +/** + * \def MBEDTLS_PKCS12_C + * + * Enable PKCS#12 PBE functions. + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/pkparse.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_CIPHER_C, MBEDTLS_MD_C + * + * This module enables PKCS#12 functions. + */ +#define MBEDTLS_PKCS12_C + +/** + * \def MBEDTLS_PLATFORM_C + * + * Enable the platform abstraction layer that allows you to re-assign + * functions like calloc(), free(), snprintf(), printf(), fprintf(), exit(). + * + * Enabling MBEDTLS_PLATFORM_C enables to use of MBEDTLS_PLATFORM_XXX_ALT + * or MBEDTLS_PLATFORM_XXX_MACRO directives, allowing the functions mentioned + * above to be specified at runtime or compile time respectively. + * + * \note This abstraction layer must be enabled on Windows (including MSYS2) + * as other module rely on it for a fixed snprintf implementation. + * + * Module: library/platform.c + * Caller: Most other .c files + * + * This module enables abstraction of common (libc) functions. + */ +#define MBEDTLS_PLATFORM_C + +/** + * \def MBEDTLS_POLY1305_C + * + * Enable the Poly1305 MAC algorithm. + * + * Module: library/poly1305.c + * Caller: library/chachapoly.c + */ +#define MBEDTLS_POLY1305_C + +/** + * \def MBEDTLS_PSA_CRYPTO_C + * + * Enable the Platform Security Architecture cryptography API. + * + * \warning The PSA Crypto API is still beta status. While you're welcome to + * experiment using it, incompatible API changes are still possible, and some + * parts may not have reached the same quality as the rest of Mbed TLS yet. + * + * Module: library/psa_crypto.c + * + * Requires: either MBEDTLS_CTR_DRBG_C and MBEDTLS_ENTROPY_C, + * or MBEDTLS_HMAC_DRBG_C and MBEDTLS_ENTROPY_C, + * or MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG. + * + */ +#define MBEDTLS_PSA_CRYPTO_C + +/** + * \def MBEDTLS_PSA_CRYPTO_SE_C + * + * Enable secure element support in the Platform Security Architecture + * cryptography API. + * + * \warning This feature is not yet suitable for production. It is provided + * for API evaluation and testing purposes only. + * + * Module: library/psa_crypto_se.c + * + * Requires: MBEDTLS_PSA_CRYPTO_C, MBEDTLS_PSA_CRYPTO_STORAGE_C + * + */ +//#define MBEDTLS_PSA_CRYPTO_SE_C + +/** + * \def MBEDTLS_PSA_CRYPTO_STORAGE_C + * + * Enable the Platform Security Architecture persistent key storage. + * + * Module: library/psa_crypto_storage.c + * + * Requires: MBEDTLS_PSA_CRYPTO_C, + * either MBEDTLS_PSA_ITS_FILE_C or a native implementation of + * the PSA ITS interface + */ +#define MBEDTLS_PSA_CRYPTO_STORAGE_C + +/** + * \def MBEDTLS_PSA_ITS_FILE_C + * + * Enable the emulation of the Platform Security Architecture + * Internal Trusted Storage (PSA ITS) over files. + * + * Module: library/psa_its_file.c + * + * Requires: MBEDTLS_FS_IO + */ +#define MBEDTLS_PSA_ITS_FILE_C + +/** + * \def MBEDTLS_RIPEMD160_C + * + * Enable the RIPEMD-160 hash algorithm. + * + * Module: library/ripemd160.c + * Caller: library/md.c + * + */ +#define MBEDTLS_RIPEMD160_C + +/** + * \def MBEDTLS_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * library/rsa_alt_helpers.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * This module is used by the following key exchanges: + * RSA, DHE-RSA, ECDHE-RSA, RSA-PSK + * + * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C + */ +#define MBEDTLS_RSA_C + +/** + * \def MBEDTLS_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509write_crt.c + * + * This module is required for TLS 1.2 depending on the handshake parameters, + * and for SHA1-signed certificates. + * + * \warning SHA-1 is considered a weak message digest and its use constitutes + * a security risk. If possible, we recommend avoiding dependencies + * on it, and considering stronger message digests instead. + * + */ +#define MBEDTLS_SHA1_C + +/** + * \def MBEDTLS_SHA224_C + * + * Enable the SHA-224 cryptographic hash algorithm. + * + * Requires: MBEDTLS_SHA256_C. The library does not currently support enabling + * SHA-224 without SHA-256. + * + * Module: library/sha256.c + * Caller: library/md.c + * library/ssl_cookie.c + * + * This module adds support for SHA-224. + */ +#define MBEDTLS_SHA224_C + +/** + * \def MBEDTLS_SHA256_C + * + * Enable the SHA-256 cryptographic hash algorithm. + * + * Requires: MBEDTLS_SHA224_C. The library does not currently support enabling + * SHA-256 without SHA-224. + * + * Module: library/sha256.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module adds support for SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define MBEDTLS_SHA256_C + +/** + * \def MBEDTLS_SHA384_C + * + * Enable the SHA-384 cryptographic hash algorithm. + * + * Requires: MBEDTLS_SHA512_C + * + * Module: library/sha512.c + * Caller: library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * Comment to disable SHA-384 + */ +#define MBEDTLS_SHA384_C + +/** + * \def MBEDTLS_SHA512_C + * + * Enable SHA-512 cryptographic hash algorithms. + * + * Module: library/sha512.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_tls.c + * library/ssl_cookie.c + * + * This module adds support for SHA-512. + */ +#define MBEDTLS_SHA512_C + +/** + * \def MBEDTLS_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: MBEDTLS_SSL_CACHE_C + */ +#define MBEDTLS_SSL_CACHE_C + +/** + * \def MBEDTLS_SSL_COOKIE_C + * + * Enable basic implementation of DTLS cookies for hello verification. + * + * Module: library/ssl_cookie.c + * Caller: + */ +#define MBEDTLS_SSL_COOKIE_C + +/** + * \def MBEDTLS_SSL_TICKET_C + * + * Enable an implementation of TLS server-side callbacks for session tickets. + * + * Module: library/ssl_ticket.c + * Caller: + * + * Requires: MBEDTLS_CIPHER_C + */ +#define MBEDTLS_SSL_TICKET_C + +/** + * \def MBEDTLS_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl_cli.c + * Caller: + * + * Requires: MBEDTLS_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define MBEDTLS_SSL_CLI_C + +/** + * \def MBEDTLS_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl_srv.c + * Caller: + * + * Requires: MBEDTLS_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +#define MBEDTLS_SSL_SRV_C + +/** + * \def MBEDTLS_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: MBEDTLS_CIPHER_C, MBEDTLS_MD_C + * and at least one of the MBEDTLS_SSL_PROTO_XXX defines + * + * This module is required for SSL/TLS. + */ +#define MBEDTLS_SSL_TLS_C + +/** + * \def MBEDTLS_THREADING_C + * + * Enable the threading abstraction layer. + * By default mbed TLS assumes it is used in a non-threaded environment or that + * contexts are not shared between threads. If you do intend to use contexts + * between threads, you will need to enable this layer to prevent race + * conditions. See also our Knowledge Base article about threading: + * https://tls.mbed.org/kb/development/thread-safety-and-multi-threading + * + * Module: library/threading.c + * + * This allows different threading implementations (self-implemented or + * provided). + * + * You will have to enable either MBEDTLS_THREADING_ALT or + * MBEDTLS_THREADING_PTHREAD. + * + * Enable this layer to allow use of mutexes within mbed TLS + */ +//#define MBEDTLS_THREADING_C + +/** + * \def MBEDTLS_TIMING_C + * + * Enable the semi-portable timing interface. + * + * \note The provided implementation only works on POSIX/Unix (including Linux, + * BSD and OS X) and Windows. On other platforms, you can either disable that + * module and provide your own implementations of the callbacks needed by + * \c mbedtls_ssl_set_timer_cb() for DTLS, or leave it enabled and provide + * your own implementation of the whole module by setting + * \c MBEDTLS_TIMING_ALT in the current file. + * + * \note See also our Knowledge Base article about porting to a new + * environment: + * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * + * Module: library/timing.c + */ +#define MBEDTLS_TIMING_C + +/** + * \def MBEDTLS_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +#define MBEDTLS_VERSION_C + +/** + * \def MBEDTLS_X509_USE_C + * + * Enable X.509 core for using certificates. + * + * Module: library/x509.c + * Caller: library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, + * MBEDTLS_PK_PARSE_C + * + * This module is required for the X.509 parsing modules. + */ +#define MBEDTLS_X509_USE_C + +/** + * \def MBEDTLS_X509_CRT_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509_crt.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is required for X.509 certificate parsing. + */ +#define MBEDTLS_X509_CRT_PARSE_C + +/** + * \def MBEDTLS_X509_CRL_PARSE_C + * + * Enable X.509 CRL parsing. + * + * Module: library/x509_crl.c + * Caller: library/x509_crt.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is required for X.509 CRL parsing. + */ +#define MBEDTLS_X509_CRL_PARSE_C + +/** + * \def MBEDTLS_X509_CSR_PARSE_C + * + * Enable X.509 Certificate Signing Request (CSR) parsing. + * + * Module: library/x509_csr.c + * Caller: library/x509_crt_write.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is used for reading X.509 certificate request. + */ +#define MBEDTLS_X509_CSR_PARSE_C + +/** + * \def MBEDTLS_X509_CREATE_C + * + * Enable X.509 core for creating certificates. + * + * Module: library/x509_create.c + * + * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, MBEDTLS_PK_WRITE_C + * + * This module is the basis for creating X.509 certificates and CSRs. + */ +#define MBEDTLS_X509_CREATE_C + +/** + * \def MBEDTLS_X509_CRT_WRITE_C + * + * Enable creating X.509 certificates. + * + * Module: library/x509_crt_write.c + * + * Requires: MBEDTLS_X509_CREATE_C + * + * This module is required for X.509 certificate creation. + */ +#define MBEDTLS_X509_CRT_WRITE_C + +/** + * \def MBEDTLS_X509_CSR_WRITE_C + * + * Enable creating X.509 Certificate Signing Requests (CSR). + * + * Module: library/x509_csr_write.c + * + * Requires: MBEDTLS_X509_CREATE_C + * + * This module is required for X.509 certificate request writing. + */ +#define MBEDTLS_X509_CSR_WRITE_C + +/* \} name SECTION: mbed TLS modules */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * + * Our advice is to enable options and change their values here + * only if you have a good reason and know the consequences. + * + * Please check the respective header file for documentation on these + * parameters (to prevent duplicate documentation). + * \{ + */ + +/* MPI / BIGNUM options */ +//#define MBEDTLS_MPI_WINDOW_SIZE 6 /**< Maximum window size used. */ +//#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */ + +/* CTR_DRBG options */ +//#define MBEDTLS_CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +//#define MBEDTLS_CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define MBEDTLS_CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define MBEDTLS_CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define MBEDTLS_CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* HMAC_DRBG options */ +//#define MBEDTLS_HMAC_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define MBEDTLS_HMAC_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define MBEDTLS_HMAC_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* ECP options */ +//#define MBEDTLS_ECP_WINDOW_SIZE 4 /**< Maximum window size used */ +//#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */ + +/* Entropy options */ +//#define MBEDTLS_ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +//#define MBEDTLS_ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ +//#define MBEDTLS_ENTROPY_MIN_HARDWARE 32 /**< Default minimum number of bytes required for the hardware entropy source mbedtls_hardware_poll() before entropy is released */ + +/* Memory buffer allocator options */ +//#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ + +/* Platform options */ +//#define MBEDTLS_PLATFORM_STD_MEM_HDR /**< Header to include if MBEDTLS_PLATFORM_NO_STD_FUNCTIONS is defined. Don't define if no header is needed. */ +//#define MBEDTLS_PLATFORM_STD_CALLOC calloc /**< Default allocator to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_FREE free /**< Default free to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT exit /**< Default exit to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_TIME time /**< Default time to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_STD_FPRINTF fprintf /**< Default fprintf to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_PRINTF printf /**< Default printf to use, can be undefined */ +/* Note: your snprintf must correctly zero-terminate the buffer! */ +//#define MBEDTLS_PLATFORM_STD_SNPRINTF snprintf /**< Default snprintf to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT_SUCCESS 0 /**< Default exit value to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT_FAILURE 1 /**< Default exit value to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_READ mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_WRITE mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_FILE "seedfile" /**< Seed file to read/write with default implementation */ + +/* To Use Function Macros MBEDTLS_PLATFORM_C must be enabled */ +/* MBEDTLS_PLATFORM_XXX_MACRO and MBEDTLS_PLATFORM_XXX_ALT cannot both be defined */ +//#define MBEDTLS_PLATFORM_CALLOC_MACRO calloc /**< Default allocator macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_FREE_MACRO free /**< Default free macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_EXIT_MACRO exit /**< Default exit macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_TIME_MACRO time /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_TIME_TYPE_MACRO time_t /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_FPRINTF_MACRO fprintf /**< Default fprintf macro to use, can be undefined */ +#define MBEDTLS_PLATFORM_PRINTF_MACRO ets_printf /**< Default printf macro to use, can be undefined */ +/* Note: your snprintf must correctly zero-terminate the buffer! */ +//#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf /**< Default snprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_VSNPRINTF_MACRO vsnprintf /**< Default vsnprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_NV_SEED_READ_MACRO mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */ + +/* PSA options */ +/** + * Use HMAC_DRBG with the specified hash algorithm for HMAC_DRBG for the + * PSA crypto subsystem. + * + * If this option is unset: + * - If CTR_DRBG is available, the PSA subsystem uses it rather than HMAC_DRBG. + * - Otherwise, the PSA subsystem uses HMAC_DRBG with either + * #MBEDTLS_MD_SHA512 or #MBEDTLS_MD_SHA256 based on availability and + * on unspecified heuristics. + */ +//#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA256 + +/** \def MBEDTLS_PSA_KEY_SLOT_COUNT + * Restrict the PSA library to supporting a maximum amount of simultaneously + * loaded keys. A loaded key is a key stored by the PSA Crypto core as a + * volatile key, or a persistent key which is loaded temporarily by the + * library as part of a crypto operation in flight. + * + * If this option is unset, the library will fall back to a default value of + * 32 keys. + */ +//#define MBEDTLS_PSA_KEY_SLOT_COUNT 32 + +/* SSL Cache options */ +//#define MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +//#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +/* SSL options */ + +/** \def MBEDTLS_SSL_IN_CONTENT_LEN + * + * Maximum length (in bytes) of incoming plaintext fragments. + * + * This determines the size of the incoming TLS I/O buffer in such a way + * that it is capable of holding the specified amount of plaintext data, + * regardless of the protection mechanism used. + * + * \note When using a value less than the default of 16KB on the client, it is + * recommended to use the Maximum Fragment Length (MFL) extension to + * inform the server about this limitation. On the server, there + * is no supported, standardized way of informing the client about + * restriction on the maximum size of incoming messages, and unless + * the limitation has been communicated by other means, it is recommended + * to only change the outgoing buffer size #MBEDTLS_SSL_OUT_CONTENT_LEN + * while keeping the default value of 16KB for the incoming buffer. + * + * Uncomment to set the maximum plaintext size of the incoming I/O buffer. + */ +//#define MBEDTLS_SSL_IN_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_CID_IN_LEN_MAX + * + * The maximum length of CIDs used for incoming DTLS messages. + * + */ +//#define MBEDTLS_SSL_CID_IN_LEN_MAX 32 + +/** \def MBEDTLS_SSL_CID_OUT_LEN_MAX + * + * The maximum length of CIDs used for outgoing DTLS messages. + * + */ +//#define MBEDTLS_SSL_CID_OUT_LEN_MAX 32 + +/** \def MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY + * + * This option controls the use of record plaintext padding + * in TLS 1.3 and when using the Connection ID extension in DTLS 1.2. + * + * The padding will always be chosen so that the length of the + * padded plaintext is a multiple of the value of this option. + * + * Note: A value of \c 1 means that no padding will be used + * for outgoing records. + * + * Note: On systems lacking division instructions, + * a power of two should be preferred. + */ +//#define MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY 16 + +/** \def MBEDTLS_SSL_OUT_CONTENT_LEN + * + * Maximum length (in bytes) of outgoing plaintext fragments. + * + * This determines the size of the outgoing TLS I/O buffer in such a way + * that it is capable of holding the specified amount of plaintext data, + * regardless of the protection mechanism used. + * + * It is possible to save RAM by setting a smaller outward buffer, while keeping + * the default inward 16384 byte buffer to conform to the TLS specification. + * + * The minimum required outward buffer size is determined by the handshake + * protocol's usage. Handshaking will fail if the outward buffer is too small. + * The specific size requirement depends on the configured ciphers and any + * certificate data which is sent during the handshake. + * + * Uncomment to set the maximum plaintext size of the outgoing I/O buffer. + */ +//#define MBEDTLS_SSL_OUT_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_DTLS_MAX_BUFFERING + * + * Maximum number of heap-allocated bytes for the purpose of + * DTLS handshake message reassembly and future message buffering. + * + * This should be at least 9/8 * MBEDTLSSL_IN_CONTENT_LEN + * to account for a reassembled handshake message of maximum size, + * together with its reassembly bitmap. + * + * A value of 2 * MBEDTLS_SSL_IN_CONTENT_LEN (32768 by default) + * should be sufficient for all practical situations as it allows + * to reassembly a large handshake message (such as a certificate) + * while buffering multiple smaller handshake messages. + * + */ +//#define MBEDTLS_SSL_DTLS_MAX_BUFFERING 32768 + +//#define MBEDTLS_PSK_MAX_LEN 32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */ +//#define MBEDTLS_SSL_COOKIE_TIMEOUT 60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */ + +/** + * Complete list of ciphersuites to use, in order of preference. + * + * \warning No dependency checking is done on that field! This option can only + * be used to restrict the set of available ciphersuites. It is your + * responsibility to make sure the needed modules are active. + * + * Use this to save a few hundred bytes of ROM (default ordering of all + * available ciphersuites) and a few to a few hundred bytes of RAM. + * + * The value below is only an example, not the default. + */ +//#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + +/* X509 options */ +//#define MBEDTLS_X509_MAX_INTERMEDIATE_CA 8 /**< Maximum number of intermediate CAs in a verification chain. */ +//#define MBEDTLS_X509_MAX_FILE_PATH_LEN 512 /**< Maximum length of a path/filename string in bytes including the null terminator character ('\0'). */ + +/** + * Uncomment the macro to let mbed TLS use your alternate implementation of + * mbedtls_platform_zeroize(). This replaces the default implementation in + * platform_util.c. + * + * mbedtls_platform_zeroize() is a widely used function across the library to + * zero a block of memory. The implementation is expected to be secure in the + * sense that it has been written to prevent the compiler from removing calls + * to mbedtls_platform_zeroize() as part of redundant code elimination + * optimizations. However, it is difficult to guarantee that calls to + * mbedtls_platform_zeroize() will not be optimized by the compiler as older + * versions of the C language standards do not provide a secure implementation + * of memset(). Therefore, MBEDTLS_PLATFORM_ZEROIZE_ALT enables users to + * configure their own implementation of mbedtls_platform_zeroize(), for + * example by using directives specific to their compiler, features from newer + * C standards (e.g using memset_s() in C11) or calling a secure memset() from + * their system (e.g explicit_bzero() in BSD). + */ +//#define MBEDTLS_PLATFORM_ZEROIZE_ALT + +/** + * Uncomment the macro to let Mbed TLS use your alternate implementation of + * mbedtls_platform_gmtime_r(). This replaces the default implementation in + * platform_util.c. + * + * gmtime() is not a thread-safe function as defined in the C standard. The + * library will try to use safer implementations of this function, such as + * gmtime_r() when available. However, if Mbed TLS cannot identify the target + * system, the implementation of mbedtls_platform_gmtime_r() will default to + * using the standard gmtime(). In this case, calls from the library to + * gmtime() will be guarded by the global mutex mbedtls_threading_gmtime_mutex + * if MBEDTLS_THREADING_C is enabled. We recommend that calls from outside the + * library are also guarded with this mutex to avoid race conditions. However, + * if the macro MBEDTLS_PLATFORM_GMTIME_R_ALT is defined, Mbed TLS will + * unconditionally use the implementation for mbedtls_platform_gmtime_r() + * supplied at compile time. + */ +//#define MBEDTLS_PLATFORM_GMTIME_R_ALT + +/** + * Enable the verified implementations of ECDH primitives from Project Everest + * (currently only Curve25519). This feature changes the layout of ECDH + * contexts and therefore is a compatibility break for applications that access + * fields of a mbedtls_ecdh_context structure directly. See also + * MBEDTLS_ECDH_LEGACY_CONTEXT in include/mbedtls/ecdh.h. + */ +//#define MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED + +/* \} name SECTION: Customisation configuration options */ diff --git a/bootloader/mcuboot/boot/espressif/include/crypto_config/rsa.cmake b/bootloader/mcuboot/boot/espressif/include/crypto_config/rsa.cmake new file mode 100644 index 0000000..ee146cd --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/crypto_config/rsa.cmake @@ -0,0 +1,28 @@ +# Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. +# +# SPDX-License-Identifier: Apache-2.0 + +if (DEFINED CONFIG_ESP_USE_MBEDTLS) + set(MBEDTLS_DIR ${MCUBOOT_ROOT_DIR}/ext/mbedtls) + set(CRYPTO_INC + ${MBEDTLS_DIR}/include + ) + set(crypto_srcs + ${ESPRESSIF_PORT_DIR}/keys.c + ${MBEDTLS_DIR}/library/platform.c + ${MBEDTLS_DIR}/library/platform_util.c + ${MBEDTLS_DIR}/library/sha256.c + ${MBEDTLS_DIR}/library/rsa.c + ${MBEDTLS_DIR}/library/bignum.c + ${MBEDTLS_DIR}/library/asn1parse.c + ${MBEDTLS_DIR}/library/md.c + ${MBEDTLS_DIR}/library/memory_buffer_alloc.c + ) + if (DEFINED MBEDTLS_CONFIG_FILE) + add_definitions(-DMBEDTLS_CONFIG_FILE=\"${MBEDTLS_CONFIG_FILE}\") + else() + add_definitions(-DMBEDTLS_CONFIG_FILE=\"${ESPRESSIF_PORT_DIR}/include/crypto_config/mbedtls_custom_config.h\") + endif() +elseif (DEFINED CONFIG_ESP_USE_TINYCRYPT) + message(FATAL_ERROR "RSA signature verification using Tinycrypt lib is not supported") +endif() \ No newline at end of file diff --git a/bootloader/mcuboot/boot/espressif/include/esp_loader.h b/bootloader/mcuboot/boot/espressif/include/esp_loader.h new file mode 100644 index 0000000..480022c --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/esp_loader.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +void start_cpu0_image(int image_index, int slot, unsigned int hdr_offset); +#ifdef CONFIG_ESP_MULTI_PROCESSOR_BOOT +void start_cpu1_image(int image_index, int slot, unsigned int hdr_offset); +#endif + +void esp_app_image_load(int image_index, int slot, unsigned int hdr_offset, unsigned int *entry_addr); diff --git a/bootloader/mcuboot/boot/espressif/include/flash_map_backend/flash_map_backend.h b/bootloader/mcuboot/boot/espressif/include/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000..4bd3d2e --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/flash_map_backend/flash_map_backend.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +struct flash_area { + uint8_t fa_id; /** The slot/scratch identification */ + uint8_t fa_device_id; /** The device id (usually there's only one) */ + uint16_t pad16; + uint32_t fa_off; /** The flash offset from the beginning */ + uint32_t fa_size; /** The size of this sector */ +}; + +//! Structure describing a sector within a flash area. +struct flash_sector { + //! Offset of this sector, from the start of its flash area (not device). + uint32_t fs_off; + + //! Size of this sector, in bytes. + uint32_t fs_size; +}; + +static inline uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + return (uint8_t)fa->fa_device_id; +} +static inline uint32_t flash_area_get_off(const struct flash_area *fa) +{ + return (uint32_t)fa->fa_off; +} + +static inline uint32_t flash_area_get_size(const struct flash_area *fa) +{ + return (uint32_t)fa->fa_size; +} + +static inline uint8_t flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +static inline uint32_t flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +static inline uint32_t flash_sector_get_size(const struct flash_sector *fs) +{ + return fs->fs_size; +} + +//! Opens the area for use. id is one of the `fa_id`s */ +int flash_area_open(uint8_t id, const struct flash_area **area_outp); +void flash_area_close(const struct flash_area *fa); + +//! Reads `len` bytes of flash memory at `off` to the buffer at `dst` +int flash_area_read(const struct flash_area *fa, uint32_t off, + void *dst, uint32_t len); +//! Writes `len` bytes of flash memory at `off` from the buffer at `src` +int flash_area_write(const struct flash_area *fa, uint32_t off, + const void *src, uint32_t len); +//! Erases `len` bytes of flash memory at `off` +int flash_area_erase(const struct flash_area *fa, + uint32_t off, uint32_t len); + +//! Returns this `flash_area`s alignment +uint32_t flash_area_align(const struct flash_area *area); +//! Returns the value read from an erased flash area byte +uint8_t flash_area_erased_val(const struct flash_area *area); + +//! Given flash area ID, return info about sectors within the area +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors); + +//! Retrieve the flash sector a given offset belongs to. +int flash_area_sector_from_off(uint32_t off, struct flash_sector *sector); + +//! Retrieve the flash sector a given offset belongs to. +int flash_area_get_sector(const struct flash_area *area, uint32_t off, + struct flash_sector *sector); + +//! Returns the `fa_id` for slot, where slot is 0 (primary) or 1 (secondary). +//! +//! `image_index` (0 or 1) is the index of the image. Image index is +//! relevant only when multi-image support support is enabled +int flash_area_id_from_multi_image_slot(int image_index, int slot); +int flash_area_id_from_image_slot(int slot); +int flash_area_to_sectors(int idx, int *cnt, struct flash_area *fa); diff --git a/bootloader/mcuboot/boot/espressif/include/os/os.h b/bootloader/mcuboot/boot/espressif/include/os/os.h new file mode 100644 index 0000000..e69de29 diff --git a/bootloader/mcuboot/boot/espressif/include/os/os_malloc.h b/bootloader/mcuboot/boot/espressif/include/os/os_malloc.h new file mode 100644 index 0000000..6ca5c46 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/os/os_malloc.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +void os_heap_init(void); diff --git a/bootloader/mcuboot/boot/espressif/include/serial_adapter/serial_adapter.h b/bootloader/mcuboot/boot/espressif/include/serial_adapter/serial_adapter.h new file mode 100644 index 0000000..1502ec3 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/serial_adapter/serial_adapter.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/** + * Serial write implementation used by MCUboot boot serial structure + * in boot_serial.h + */ +void console_write(const char *str, int cnt); + +/** + * Serial read implementation used by MCUboot boot serial structure + * in boot_serial.h + */ +int console_read(char *str, int str_cnt, int *newline); + +/** + * Initialize GPIOs used by console serial read/write + */ +void boot_console_init(void); + +/** + * Check if serial recovery detection pin is active + */ +bool boot_serial_detect_pin(void); diff --git a/bootloader/mcuboot/boot/espressif/include/sysflash/sysflash.h b/bootloader/mcuboot/boot/espressif/include/sysflash/sysflash.h new file mode 100644 index 0000000..5419853 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/include/sysflash/sysflash.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +//! A user-defined identifier for different storage mediums +//! (i.e internal flash, external NOR flash, eMMC, etc) +#define FLASH_DEVICE_INTERNAL_FLASH 0 + +//! An arbitrarily high slot ID we will use to indicate that +//! there is not slot +#define FLASH_SLOT_DOES_NOT_EXIST 255 + +//! The slot we will use to track the bootloader allocation +#define FLASH_AREA_BOOTLOADER 0 + +#define FLASH_AREA_IMAGE_0_PRIMARY 1 +#define FLASH_AREA_IMAGE_0_SECONDARY 2 +#define FLASH_AREA_IMAGE_SCRATCH 3 +#define FLASH_AREA_IMAGE_1_PRIMARY 4 +#define FLASH_AREA_IMAGE_1_SECONDARY 5 + +#if (MCUBOOT_IMAGE_NUMBER == 1) +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0_PRIMARY : \ + FLASH_SLOT_DOES_NOT_EXIST) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0_SECONDARY : \ + FLASH_SLOT_DOES_NOT_EXIST) + +#elif (MCUBOOT_IMAGE_NUMBER == 2) +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0_PRIMARY : \ + ((x) == 1) ? \ + FLASH_AREA_IMAGE_1_PRIMARY : \ + FLASH_SLOT_DOES_NOT_EXIST) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0_SECONDARY : \ + ((x) == 1) ? \ + FLASH_AREA_IMAGE_1_SECONDARY : \ + FLASH_SLOT_DOES_NOT_EXIST) + +#else +#warning "Image slot and flash area mapping is not defined" +#endif diff --git a/bootloader/mcuboot/boot/espressif/keys.c b/bootloader/mcuboot/boot/espressif/keys.c new file mode 100644 index 0000000..1b62a6b --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/keys.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#if !defined(MCUBOOT_HW_KEY) +#if defined(MCUBOOT_SIGN_RSA) +#define HAVE_KEYS +extern const unsigned char rsa_pub_key[]; +extern const unsigned int rsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_EC256) +#define HAVE_KEYS +extern const unsigned char ecdsa_pub_key[]; +extern const unsigned int ecdsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_ED25519) +#define HAVE_KEYS +extern const unsigned char ed25519_pub_key[]; +extern const unsigned int ed25519_pub_key_len; +#endif + +/* + * NOTE: *_pub_key and *_pub_key_len are autogenerated based on the provided + * key file. If no key file was configured, the array and length must be + * provided and added to the build manually. + */ +#if defined(HAVE_KEYS) +const struct bootutil_key bootutil_keys[] = { + { +#if defined(MCUBOOT_SIGN_RSA) + .key = rsa_pub_key, + .len = &rsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_EC256) + .key = ecdsa_pub_key, + .len = &ecdsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_ED25519) + .key = ed25519_pub_key, + .len = &ed25519_pub_key_len, +#endif + }, +}; +const int bootutil_key_cnt = 1; +#endif /* HAVE_KEYS */ +#else +unsigned int pub_key_len; +struct bootutil_key bootutil_keys[1] = { + { + .key = 0, + .len = &pub_key_len, + } +}; +const int bootutil_key_cnt = 1; +#endif /* !MCUBOOT_HW_KEY */ diff --git a/bootloader/mcuboot/boot/espressif/main.c b/bootloader/mcuboot/boot/espressif/main.c new file mode 100644 index 0000000..3f4d5a0 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/main.c @@ -0,0 +1,298 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "bootloader_init.h" +#include "bootloader_utility.h" +#include "bootloader_random.h" +#include "bootloader_soc.h" + +#include "esp_assert.h" + +#ifdef CONFIG_MCUBOOT_SERIAL +#include "boot_serial/boot_serial.h" +#include "serial_adapter/serial_adapter.h" + +const struct boot_uart_funcs boot_funcs = { + .read = console_read, + .write = console_write +}; +#endif + +#if defined(CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH) || defined(CONFIG_SECURE_BOOT) +#include "esp_efuse.h" +#endif +#ifdef CONFIG_SECURE_BOOT +#include "esp_secure_boot.h" +#endif +#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED +#include "esp_flash_encrypt.h" +#endif + +#include "esp_loader.h" +#include "os/os_malloc.h" + +#define IMAGE_INDEX_0 0 +#define IMAGE_INDEX_1 1 + +#define PRIMARY_SLOT 0 +#define SECONDARY_SLOT 1 + +#ifdef CONFIG_SECURE_BOOT +extern esp_err_t check_and_generate_secure_boot_keys(void); +#endif + +void do_boot(struct boot_rsp *rsp) +{ + BOOT_LOG_INF("br_image_off = 0x%x", rsp->br_image_off); + BOOT_LOG_INF("ih_hdr_size = 0x%x", rsp->br_hdr->ih_hdr_size); + int slot = (rsp->br_image_off == CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS) ? PRIMARY_SLOT : SECONDARY_SLOT; + start_cpu0_image(IMAGE_INDEX_0, slot, rsp->br_hdr->ih_hdr_size); +} + +#ifdef CONFIG_ESP_MULTI_PROCESSOR_BOOT +int read_image_header(uint32_t img_index, uint32_t slot, struct image_header *img_header) +{ + const struct flash_area *fap; + int area_id; + int rc = 0; + + area_id = flash_area_id_from_multi_image_slot(img_index, slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + if (flash_area_read(fap, 0, img_header, sizeof(struct image_header))) { + rc = BOOT_EFLASH; + goto done; + } + + BOOT_LOG_INF("Image offset = 0x%x", fap->fa_off); + BOOT_LOG_INF("Image header size = 0x%x", img_header->ih_hdr_size); + +done: + flash_area_close(fap); + return rc; +} + +void do_boot_appcpu(uint32_t img_index, uint32_t slot) +{ + struct image_header img_header; + + if (read_image_header(img_index, slot, &img_header) != 0) { + FIH_PANIC; + } + + start_cpu1_image(img_index, slot, img_header.ih_hdr_size); +} +#endif + +int main() +{ + if (bootloader_init() != ESP_OK) { + FIH_PANIC; + } + + /* Rough steps for a first boot when Secure Boot and/or Flash Encryption are still disabled on device: + * Secure Boot: + * 1) Calculate the SHA-256 hash digest of the public key and write to EFUSE. + * 2) Validate the application images and prepare the booting process. + * 3) Burn EFUSE to enable Secure Boot V2 (ABS_DONE_0). + * Flash Encryption: + * 4) Generate Flash Encryption key and write to EFUSE. + * 5) Encrypt flash in-place including bootloader, image primary/secondary slot and scratch. + * 6) Burn EFUSE to enable Flash Encryption. + * 7) Reset system to ensure Flash Encryption cache resets properly. + */ + +#ifdef CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH + BOOT_LOG_WRN("eFuse virtual mode is enabled. If Secure boot or Flash encryption is enabled then it does not provide any security. FOR TESTING ONLY!"); + esp_efuse_init_virtual_mode_in_flash(CONFIG_EFUSE_VIRTUAL_OFFSET, CONFIG_EFUSE_VIRTUAL_SIZE); +#endif + +#if defined(CONFIG_SECURE_BOOT) || defined(CONFIG_SECURE_FLASH_ENC_ENABLED) + esp_err_t err; +#endif + +#ifdef CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER + if (esp_secure_boot_enabled() ^ esp_flash_encrypt_initialized_once()) { + BOOT_LOG_ERR("Secure Boot and Flash Encryption cannot be enabled separately, only together (their keys go into one eFuse key block)"); + FIH_PANIC; + } + + if (!esp_secure_boot_enabled() || !esp_flash_encryption_enabled()) { + esp_efuse_batch_write_begin(); + } +#endif // CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER + +#ifdef CONFIG_SECURE_BOOT + /* Steps 1 (see above for full description): + * 1) Compute digest of the public key. + */ + + BOOT_LOG_INF("enabling secure boot v2..."); + + bool sb_hw_enabled = esp_secure_boot_enabled(); + + if (sb_hw_enabled) { + BOOT_LOG_INF("secure boot v2 is already enabled, continuing.."); + } else { + esp_efuse_batch_write_begin(); /* Batch all efuse writes at the end of this function */ + + err = check_and_generate_secure_boot_keys(); + if (err != ESP_OK) { + esp_efuse_batch_write_cancel(); + FIH_PANIC; + } + } +#endif + + os_heap_init(); + + struct boot_rsp rsp; + + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#ifdef CONFIG_MCUBOOT_SERIAL + boot_console_init(); + if (boot_serial_detect_pin()) { + BOOT_LOG_INF("Enter the serial recovery mode"); + boot_serial_start(&boot_funcs); + } +#endif + + /* Step 2 (see above for full description): + * 2) MCUboot validates the application images and prepares the booting process. + */ + + /* MCUboot's boot_go validates and checks all images for update and returns + * the load information for booting the main image + */ + FIH_CALL(boot_go, fih_rc, &rsp); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_LOG_ERR("Unable to find bootable image"); +#ifdef CONFIG_SECURE_BOOT + esp_efuse_batch_write_cancel(); +#endif + FIH_PANIC; + } + +#ifdef CONFIG_SECURE_BOOT + /* Step 3 (see above for full description): + * 3) Burn EFUSE to enable Secure Boot V2. + */ + + if (!sb_hw_enabled) { + BOOT_LOG_INF("blowing secure boot efuse..."); + err = esp_secure_boot_enable_secure_features(); + if (err != ESP_OK) { + esp_efuse_batch_write_cancel(); + FIH_PANIC; + } + + err = esp_efuse_batch_write_commit(); + if (err != ESP_OK) { + BOOT_LOG_ERR("Error programming security eFuses (err=0x%x).", err); + FIH_PANIC; + } + +#ifdef CONFIG_SECURE_BOOT_ENABLE_AGGRESSIVE_KEY_REVOKE + assert(esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_AGGRESSIVE_REVOKE)); +#endif + +#ifndef CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER + assert(esp_secure_boot_enabled()); + BOOT_LOG_INF("Secure boot permanently enabled"); +#endif + } +#endif + +#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED + /* Step 4, 5 & 6 (see above for full description): + * 4) Generate Flash Encryption key and write to EFUSE. + * 5) Encrypt flash in-place including bootloader, image primary/secondary slot and scratch. + * 6) Burn EFUSE to enable flash encryption + */ + BOOT_LOG_INF("Checking flash encryption..."); + bool flash_encryption_enabled = esp_flash_encrypt_state(); + if (!flash_encryption_enabled) { +#ifdef CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED + BOOT_LOG_ERR("flash encryption is not enabled, and SECURE_FLASH_REQUIRE_ALREADY_ENABLED is set, refusing to boot."); + FIH_PANIC; +#endif // CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED + + if (esp_flash_encrypt_is_write_protected(true)) { + FIH_PANIC; + } + + err = esp_flash_encrypt_init(); + if (err != ESP_OK) { + BOOT_LOG_ERR("Initialization of Flash Encryption key failed (%d)", err); + FIH_PANIC; + } + } + + if (!flash_encryption_enabled) { + err = esp_flash_encrypt_contents(); + if (err != ESP_OK) { + BOOT_LOG_ERR("Encryption flash contents failed (%d)", err); + FIH_PANIC; + } + + err = esp_flash_encrypt_enable(); + if (err != ESP_OK) { + BOOT_LOG_ERR("Enabling of Flash encryption failed (%d)", err); + FIH_PANIC; + } + } + +#ifdef CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER + if (!esp_secure_boot_enabled() || !flash_encryption_enabled) { + err = esp_efuse_batch_write_commit(); + if (err != ESP_OK) { + BOOT_LOG_ERR("Error programming eFuses (err=0x%x).", err); + FIH_PANIC; + } + assert(esp_secure_boot_enabled()); + BOOT_LOG_INF("Secure boot permanently enabled"); + } +#endif // CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER + + /* Step 7 (see above for full description): + * 7) Reset system to ensure flash encryption cache resets properly. + */ + if (!flash_encryption_enabled && esp_flash_encryption_enabled()) { + BOOT_LOG_INF("Resetting with flash encryption enabled..."); + bootloader_reset(); + } +#endif + + BOOT_LOG_INF("Disabling RNG early entropy source..."); + bootloader_random_disable(); + + /* Disable glitch reset after all the security checks are completed. + * Glitch detection can be falsely triggered by EMI interference (high RF TX power, etc) + * and to avoid such false alarms, disable it. + */ + bootloader_ana_clock_glitch_reset_config(false); + +#ifdef CONFIG_ESP_MULTI_PROCESSOR_BOOT + /* Multi image independent boot + * Boot on the second processor happens before the image0 boot + */ + do_boot_appcpu(IMAGE_INDEX_1, PRIMARY_SLOT); +#endif + + do_boot(&rsp); + + while(1); +} diff --git a/bootloader/mcuboot/boot/espressif/os.c b/bootloader/mcuboot/boot/espressif/os.c new file mode 100644 index 0000000..2a2e9ca --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/os.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifdef CONFIG_ESP_USE_MBEDTLS + +#include +#include + +#define CRYPTO_HEAP_SIZE 8192 + +static unsigned char memory_buf[CRYPTO_HEAP_SIZE]; + +/* + * Initialize Mbed TLS to be able to use the local heap. + */ +void os_heap_init(void) +{ + mbedtls_memory_buffer_alloc_init(memory_buf, sizeof(memory_buf)); +} +#else + +void os_heap_init(void) +{ +} + +#endif diff --git a/bootloader/mcuboot/boot/espressif/port/esp32/bootloader-multi.conf b/bootloader/mcuboot/boot/espressif/port/esp32/bootloader-multi.conf new file mode 100644 index 0000000..ad3355e --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32/bootloader-multi.conf @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x1000 +# Example of values to be used when multi image is enabled +# Notice that the OS layer and update agent must be aware +# of these regions +CONFIG_ESP_APPLICATION_SIZE=0x80000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x90000 +CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS=0x110000 +CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS=0x190000 +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y + +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=26 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=25 + +# Enables multi image, if it is not defined, it is assumed +# only one updatable image +# CONFIG_ESP_IMAGE_NUMBER=2 + +# Enables multi image boot on independent processors +# (main host OS is not responsible for booting the second image) +# Use only with CONFIG_ESP_IMAGE_NUMBER=2 +# CONFIG_ESP_MULTI_PROCESSOR_BOOT=y diff --git a/bootloader/mcuboot/boot/espressif/port/esp32/bootloader.conf b/bootloader/mcuboot/boot/espressif/port/esp32/bootloader.conf new file mode 100644 index 0000000..8f555ec --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32/bootloader.conf @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x1000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_APPLICATION_SIZE=0x100000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x110000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# When enabled, prevents updating image to an older version +# CONFIG_ESP_DOWNGRADE_PREVENTION=y +# This option makes downgrade prevention rely also on security +# counter (defined using imgtool) instead of only image version +# CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y + +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +# CONFIG_ESP_MCUBOOT_SERIAL=y +# Use sector erasing instead of entire image size erasing +# when uploading through Serial Recovery +# CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y +# GPIO used to boot on Serial Recovery +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=32 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +# CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +# CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication +# CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_RX=25 +# GPIO for Serial TX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_TX=26 + +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=26 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=25 + +# Enables multi image, if it is not defined, it is assumed +# only one updatable image +# CONFIG_ESP_IMAGE_NUMBER=2 + +# Enables multi image boot on independent processors +# (main host OS is not responsible for booting the second image) +# Use only with CONFIG_ESP_IMAGE_NUMBER=2 +# CONFIG_ESP_MULTI_PROCESSOR_BOOT=y + +# Example of values to be used when multi image is enabled +# Notice that the OS layer and update agent must be aware +# of these regions +# CONFIG_ESP_APPLICATION_SIZE=0x80000 +# CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +# CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x90000 +# CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS=0x110000 +# CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS=0x190000 +# CONFIG_ESP_SCRATCH_OFFSET=0x210000 +# CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# CONFIG_ESP_SIGN_EC256=y +# CONFIG_ESP_SIGN_ED25519=n +# CONFIG_ESP_SIGN_RSA=n +# CONFIG_ESP_SIGN_RSA_LEN=2048 + +# Use Tinycrypt lib for EC256 or ED25519 signing +# CONFIG_ESP_USE_TINYCRYPT=y +# Use Mbed TLS lib for RSA image signing +# CONFIG_ESP_USE_MBEDTLS=n + +# It is strongly recommended to generate a new signing key +# using imgtool instead of use the existent sample +# CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem + +# Hardware Secure Boot related options +# CONFIG_SECURE_SIGNED_ON_BOOT=1 +# CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +# CONFIG_SECURE_BOOT=1 +# CONFIG_SECURE_BOOT_V2_ENABLED=1 +# CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 + +# Hardware Flash Encryption related options +# CONFIG_SECURE_FLASH_ENC_ENABLED=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +# CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +# CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=1 + +# Options for enabling eFuse emulation in Flash +# CONFIG_EFUSE_VIRTUAL=1 +# CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 diff --git a/bootloader/mcuboot/boot/espressif/port/esp32/ld/bootloader.ld b/bootloader/mcuboot/boot/espressif/port/esp32/ld/bootloader.ld new file mode 100644 index 0000000..5ceac18 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32/ld/bootloader.ld @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Simplified memory map for the bootloader. + * + * The main purpose is to make sure the bootloader can load into main memory + * without overwriting itself. + */ + +MEMORY +{ +#ifdef CONFIG_ESP_MULTI_PROCESSOR_BOOT + iram_loader_seg (RWX) : org = 0x400AB900, len = 0x6500 +#else + /* iram_loader_seg is currently placed on APP_CPU cache IRAM address range */ + iram_loader_seg (RWX) : org = 0x40078000, len = 0x6500 +#endif + iram_seg (RWX) : org = 0x40090000, len = 0x9000 + dram_seg (RW) : org = 0x3FFF4700, len = 0xB900 +} + +/* Default entry point: */ +ENTRY(main); + +SECTIONS +{ + .iram_loader.text : + { + . = ALIGN (16); + _loader_text_start = ABSOLUTE(.); + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ + *libhal.a:*.*(.literal .text .literal.* .text.*) + *esp_mcuboot.*(.literal .text .literal.* .text.*) + *esp_loader.*(.literal .text .literal.* .text.*) + *main.*(.literal .text .literal.* .text.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _loader_text_end = ABSOLUTE(.); + } > iram_loader_seg + .iram.text : + { + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + } > iram_seg + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _dram_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } >dram_seg + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram_seg + .dram0.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.*(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.*(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _dram_end = ABSOLUTE(.); + } >dram_seg + .iram.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram .iram.*) /* catch stray IRAM_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } > iram_seg +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32/serial_adapter.c b/bootloader/mcuboot/boot/espressif/port/esp32/serial_adapter.c new file mode 100644 index 0000000..af32723 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32/serial_adapter.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#define SERIAL_BOOT_GPIO_DETECT CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#else +#define SERIAL_BOOT_GPIO_DETECT GPIO_NUM_5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#define SERIAL_BOOT_GPIO_DETECT_VAL CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#else +#define SERIAL_BOOT_GPIO_DETECT_VAL 1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#define SERIAL_BOOT_DETECT_DELAY_S CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#else +#define SERIAL_BOOT_DETECT_DELAY_S 5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#define SERIAL_BOOT_GPIO_INPUT_TYPE CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#else +// pull-down +#define SERIAL_BOOT_GPIO_INPUT_TYPE 0 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_UART_NUM +#define SERIAL_BOOT_UART_NUM CONFIG_ESP_SERIAL_BOOT_UART_NUM +#else +#define SERIAL_BOOT_UART_NUM ESP_ROM_UART_1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#define SERIAL_BOOT_GPIO_RX CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#else +#define SERIAL_BOOT_GPIO_RX GPIO_NUM_8 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#define SERIAL_BOOT_GPIO_TX CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#else +#define SERIAL_BOOT_GPIO_TX GPIO_NUM_9 +#endif + +static uart_dev_t *serial_boot_uart_dev = (SERIAL_BOOT_UART_NUM == 0) ? + &UART0 : + &UART1; + +void console_write(const char *str, int cnt) +{ + uint32_t tx_len; + + do { + tx_len = uart_ll_get_txfifo_len(serial_boot_uart_dev); + } while (tx_len < cnt); + + uart_ll_write_txfifo(serial_boot_uart_dev, (const uint8_t *)str, cnt); +} + +int console_read(char *str, int cnt, int *newline) +{ + volatile uint32_t len = 0; + volatile uint32_t read_len = 0; + volatile bool stop = false; + do { + len = uart_ll_get_rxfifo_len(serial_boot_uart_dev); + + if (len) { + for (uint32_t i = 0; i < len; i++) { + /* Read the character from the RX FIFO */ + uart_ll_read_rxfifo(serial_boot_uart_dev, (uint8_t *)&str[read_len], 1); + read_len++; + if (read_len == cnt || str[read_len - 1] == '\n') { + stop = true; + break; + } + } + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!stop); + + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + return read_len; +} + +int boot_console_init(void) +{ + BOOT_LOG_INF("Initializing serial boot pins"); + + /* Enable GPIO for UART RX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_connect_in_signal(SERIAL_BOOT_GPIO_RX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_RX_PIN_IDX), + 0); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_RX); + + /* Enable GPIO for UART TX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_TX); + esp_rom_gpio_connect_out_signal(SERIAL_BOOT_GPIO_TX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_TX_PIN_IDX), + 0, 0); + gpio_ll_output_enable(&GPIO, SERIAL_BOOT_GPIO_TX); + + uart_ll_set_sclk(serial_boot_uart_dev, UART_SCLK_APB); + uart_ll_set_mode_normal(serial_boot_uart_dev); + uart_ll_set_baudrate(serial_boot_uart_dev, 115200, UART_SCLK_APB); + uart_ll_set_stop_bits(serial_boot_uart_dev, 1u); + uart_ll_set_parity(serial_boot_uart_dev, UART_PARITY_DISABLE); + uart_ll_set_rx_tout(serial_boot_uart_dev, 16); + uart_ll_set_data_bit_num(serial_boot_uart_dev, UART_DATA_8_BITS); + uart_ll_set_tx_idle_num(serial_boot_uart_dev, 0); + uart_ll_set_hw_flow_ctrl(serial_boot_uart_dev, UART_HW_FLOWCTRL_DISABLE, 100); + periph_ll_enable_clk_clear_rst(PERIPH_UART0_MODULE + SERIAL_BOOT_UART_NUM); + + uart_ll_txfifo_rst(serial_boot_uart_dev); + uart_ll_rxfifo_rst(serial_boot_uart_dev); + esp_rom_delay_us(50000); + + return 0; +} + +bool boot_serial_detect_pin(void) +{ + bool detected = false; + int pin_value = 0; + + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_DETECT); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_DETECT); + switch (SERIAL_BOOT_GPIO_INPUT_TYPE) { + // Pull-down + case 0: + gpio_ll_pulldown_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + // Pull-up + case 1: + gpio_ll_pullup_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + } + esp_rom_delay_us(50000); + + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + esp_rom_delay_us(50000); + + if (detected) { + if (SERIAL_BOOT_DETECT_DELAY_S > 0) { + /* The delay time is an approximation */ + for (int i = 0; i < (SERIAL_BOOT_DETECT_DELAY_S * 100); i++) { + esp_rom_delay_us(10000); + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + if (!detected) { + break; + } + } + } + } + return detected; +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c2/bootloader.conf b/bootloader/mcuboot/boot/espressif/port/esp32c2/bootloader.conf new file mode 100644 index 0000000..54f797e --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c2/bootloader.conf @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x0000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_APPLICATION_SIZE=0x100000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x110000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# When enabled, prevents updating image to an older version +# CONFIG_ESP_DOWNGRADE_PREVENTION=y +# This option makes downgrade prevention rely also on security +# counter (defined using imgtool) instead of only image version +# CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y + +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +# CONFIG_ESP_MCUBOOT_SERIAL=y +# Use sector erasing (recommended) instead of entire image size +# erasing when uploading through Serial Recovery +# CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y + +# GPIO used to boot on Serial Recovery +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=18 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +# CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +# CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication (not needed when using USB) +# CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_RX=2 +# GPIO for Serial TX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_TX=3 + +# Use UART0 for console printing (use either UART or USB alone) +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# (UART_NUM=0 must not be changed) +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=3 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=2 + +# CONFIG_ESP_SIGN_EC256=y +# CONFIG_ESP_SIGN_ED25519=n +# CONFIG_ESP_SIGN_RSA=n +# CONFIG_ESP_SIGN_RSA_LEN=2048 + +# Use Tinycrypt lib for EC256 or ED25519 signing +# CONFIG_ESP_USE_TINYCRYPT=y +# Use Mbed TLS lib for RSA image signing +# CONFIG_ESP_USE_MBEDTLS=n + +# It is strongly recommended to generate a new signing key +# using imgtool instead of use the existent sample +# CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem + +# Hardware Secure Boot related options +# CONFIG_SECURE_SIGNED_ON_BOOT=1 +# CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME=1 +# CONFIG_SECURE_BOOT=1 +# CONFIG_SECURE_BOOT_V2_ENABLED=1 + +# Hardware Flash Encryption related options +# CONFIG_SECURE_FLASH_ENC_ENABLED=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +# CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +# CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=1 + +# This option must be also enabled when enabling both Secure Boot +# and Flash Encryption at same time +# CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER=1 + +# Options for enabling eFuse emulation in Flash +# CONFIG_EFUSE_VIRTUAL=1 +# CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c2/ld/bootloader.ld b/bootloader/mcuboot/boot/espressif/port/esp32c2/ld/bootloader.ld new file mode 100644 index 0000000..b24fabb --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c2/ld/bootloader.ld @@ -0,0 +1,181 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Simplified memory map for the bootloader. + * Make sure the bootloader can load into main memory without overwriting itself. + * + * ESP32-C2 ROM static data usage is as follows: + * - 0x3fccb264 - 0x3fcdcb70: Shared buffers, used in UART/USB/SPI download mode only + * - 0x3fcdcb70 - 0x3fcdeb70: PRO CPU stack, can be reclaimed as heap after RTOS startup + * - 0x3fcdeb70 - 0x3fce0000: ROM .bss and .data (not easily reclaimable) + * + * The 2nd stage bootloader can take space up to the end of ROM shared + * buffers area (0x3fcdcb70). + */ + +/* The offset between Dbus and Ibus. Used to convert between 0x403xxxxx and 0x3fcxxxxx addresses. */ +iram_dram_offset = 0x6e0000; + +/* We consider 0x3fcdcb70 to be the last usable address for 2nd stage bootloader stack overhead, dram_seg, + * and work out iram_seg and iram_loader_seg addresses from there, backwards. + */ + +/* These lengths can be adjusted, if necessary: */ +bootloader_usable_dram_end = 0x3fcdcb70; +bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ +bootloader_dram_seg_len = 0xA000; +bootloader_iram_loader_seg_len = 0x7000; +bootloader_iram_seg_len = 0x8800; + +/* Start of the lower region is determined by region size and the end of the higher region */ +bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; +bootloader_dram_seg_start = bootloader_dram_seg_end - bootloader_dram_seg_len; +bootloader_iram_loader_seg_start = bootloader_dram_seg_start - bootloader_iram_loader_seg_len + iram_dram_offset; +bootloader_iram_seg_start = bootloader_iram_loader_seg_start - bootloader_iram_seg_len; + +MEMORY +{ + iram_seg (RWX) : org = bootloader_iram_seg_start, len = bootloader_iram_seg_len + iram_loader_seg (RWX) : org = bootloader_iram_loader_seg_start, len = bootloader_iram_loader_seg_len + dram_seg (RW) : org = bootloader_dram_seg_start, len = bootloader_dram_seg_len +} + +/* The app may use RAM for static allocations up to the start of iram_loader_seg. + * If you have changed something above and this assert fails: + * 1. Check what the new value of bootloader_iram_loader_seg start is. + * 2. Update the value in this assert. + * 3. Update (SRAM_DRAM_END + I_D_SRAM_OFFSET) in components/esp_system/ld/esp32c2/memory.ld.in to the same value. + */ +ASSERT(bootloader_iram_loader_seg_start == 0x403a9b70, "bootloader_iram_loader_seg_start inconsistent with SRAM_DRAM_END"); + +/* Default entry point: */ +ENTRY(main); + +SECTIONS +{ + .iram_loader.text : + { + . = ALIGN (16); + _loader_text_start = ABSOLUTE(.); + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ + *libhal.a:*.*(.literal .text .literal.* .text.*) + *esp_mcuboot.*(.literal .text .literal.* .text.*) + *esp_loader.*(.literal .text .literal.* .text.*) + *main.*(.literal .text .literal.* .text.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _loader_text_end = ABSOLUTE(.); + } > iram_loader_seg + + .iram.text : + { + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + } > iram_seg + + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _dram_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } >dram_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram_seg + + .dram0.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.*(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.*(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _dram_end = ABSOLUTE(.); + } >dram_seg + + .iram.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram .iram.*) /* catch stray IRAM_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } > iram_seg + +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c2/serial_adapter.c b/bootloader/mcuboot/boot/espressif/port/esp32c2/serial_adapter.c new file mode 100644 index 0000000..d10c0d6 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c2/serial_adapter.c @@ -0,0 +1,190 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#define SERIAL_BOOT_GPIO_DETECT CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#else +#define SERIAL_BOOT_GPIO_DETECT GPIO_NUM_18 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#define SERIAL_BOOT_GPIO_DETECT_VAL CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#else +#define SERIAL_BOOT_GPIO_DETECT_VAL 1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#define SERIAL_BOOT_DETECT_DELAY_S CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#else +#define SERIAL_BOOT_DETECT_DELAY_S 5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#define SERIAL_BOOT_GPIO_INPUT_TYPE CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#else +// pull-down +#define SERIAL_BOOT_GPIO_INPUT_TYPE 0 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_UART_NUM +#define SERIAL_BOOT_UART_NUM CONFIG_ESP_SERIAL_BOOT_UART_NUM +#else +#define SERIAL_BOOT_UART_NUM ESP_ROM_UART_1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#define SERIAL_BOOT_GPIO_RX CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#else +#define SERIAL_BOOT_GPIO_RX GPIO_NUM_2 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#define SERIAL_BOOT_GPIO_TX CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#else +#define SERIAL_BOOT_GPIO_TX GPIO_NUM_3 +#endif + +static uart_dev_t *serial_boot_uart_dev = (SERIAL_BOOT_UART_NUM == 0) ? + &UART0 : + &UART1; + +void console_write(const char *str, int cnt) +{ + uint32_t tx_len; + uint32_t write_len; + + do { + tx_len = uart_ll_get_txfifo_len(serial_boot_uart_dev); + if (tx_len > 0) { + write_len = tx_len < cnt ? tx_len : cnt; + uart_ll_write_txfifo(serial_boot_uart_dev, (const uint8_t *)str, write_len); + cnt -= write_len; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (cnt > 0); +} + +int console_read(char *str, int cnt, int *newline) +{ + uint32_t len = 0; + uint32_t read_len = 0; + bool stop = false; + + do { + len = uart_ll_get_rxfifo_len(serial_boot_uart_dev); + + if (len > 0) { + for (uint32_t i = 0; i < len; i++) { + /* Read the character from the RX FIFO */ + uart_ll_read_rxfifo(serial_boot_uart_dev, (uint8_t *)&str[read_len], 1); + read_len++; + if (read_len == cnt - 1|| str[read_len - 1] == '\n') { + stop = true; + break; + } + } + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!stop); + + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + return read_len; +} + +int boot_console_init(void) +{ + BOOT_LOG_INF("Initializing serial boot pins"); + + /* Enable GPIO for UART RX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_connect_in_signal(SERIAL_BOOT_GPIO_RX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_RX_PIN_IDX), + 0); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_pad_pullup_only(SERIAL_BOOT_GPIO_RX); + + + /* Enable GPIO for UART TX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_TX); + esp_rom_gpio_connect_out_signal(SERIAL_BOOT_GPIO_TX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_TX_PIN_IDX), + 0, 0); + gpio_ll_output_enable(&GPIO, SERIAL_BOOT_GPIO_TX); + + uart_ll_set_sclk(serial_boot_uart_dev, UART_SCLK_DEFAULT); + uart_ll_set_mode_normal(serial_boot_uart_dev); + uart_ll_set_baudrate(serial_boot_uart_dev, 115200, UART_SCLK_DEFAULT); + uart_ll_set_stop_bits(serial_boot_uart_dev, 1u); + uart_ll_set_parity(serial_boot_uart_dev, UART_PARITY_DISABLE); + uart_ll_set_rx_tout(serial_boot_uart_dev, 16); + uart_ll_set_data_bit_num(serial_boot_uart_dev, UART_DATA_8_BITS); + uart_ll_set_tx_idle_num(serial_boot_uart_dev, 0); + uart_ll_set_hw_flow_ctrl(serial_boot_uart_dev, UART_HW_FLOWCTRL_DISABLE, 100); + periph_ll_enable_clk_clear_rst(PERIPH_UART0_MODULE + SERIAL_BOOT_UART_NUM); + + uart_ll_txfifo_rst(serial_boot_uart_dev); + uart_ll_rxfifo_rst(serial_boot_uart_dev); + esp_rom_delay_us(50000); + + return 0; +} + +bool boot_serial_detect_pin(void) +{ + bool detected = false; + int pin_value = 0; + + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_DETECT); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_DETECT); + switch (SERIAL_BOOT_GPIO_INPUT_TYPE) { + // Pull-down + case 0: + gpio_ll_pulldown_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + // Pull-up + case 1: + gpio_ll_pullup_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + } + esp_rom_delay_us(50000); + + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + esp_rom_delay_us(50000); + + if (detected) { + if (SERIAL_BOOT_DETECT_DELAY_S > 0) { + /* The delay time is an approximation */ + for (int i = 0; i < (SERIAL_BOOT_DETECT_DELAY_S * 100); i++) { + esp_rom_delay_us(10000); + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + if (!detected) { + break; + } + } + } + } + return detected; +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c3/bootloader.conf b/bootloader/mcuboot/boot/espressif/port/esp32c3/bootloader.conf new file mode 100644 index 0000000..88954ee --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c3/bootloader.conf @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x0000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_APPLICATION_SIZE=0x100000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x110000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# When enabled, prevents updating image to an older version +# CONFIG_ESP_DOWNGRADE_PREVENTION=y +# This option makes downgrade prevention rely also on security +# counter (defined using imgtool) instead of only image version +# CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y + +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +# CONFIG_ESP_MCUBOOT_SERIAL=y +# Use Serial through USB JTAG Serial port for Serial Recovery +# CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG=y +# Use sector erasing (recommended) instead of entire image size +# erasing when uploading through Serial Recovery +# CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y + +# GPIO used to boot on Serial Recovery +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=5 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +# CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +# CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication (not needed when using USB) +# CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_RX=8 +# GPIO for Serial TX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_TX=9 + +# Use UART0 for console printing (use either UART or USB alone) +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# (UART_NUM=0 must not be changed) +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=9 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=8 +# Use USB JTAG Serial for console printing +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# CONFIG_ESP_SIGN_EC256=y +# CONFIG_ESP_SIGN_ED25519=n +# CONFIG_ESP_SIGN_RSA=n +# CONFIG_ESP_SIGN_RSA_LEN=2048 + +# Use Tinycrypt lib for EC256 or ED25519 signing +# CONFIG_ESP_USE_TINYCRYPT=y +# Use Mbed TLS lib for RSA image signing +# CONFIG_ESP_USE_MBEDTLS=n + +# It is strongly recommended to generate a new signing key +# using imgtool instead of use the existent sample +# CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem + +# Hardware Secure Boot related options +# CONFIG_SECURE_SIGNED_ON_BOOT=1 +# CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +# CONFIG_SECURE_BOOT=1 +# CONFIG_SECURE_BOOT_V2_ENABLED=1 +# CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 + +# Hardware Flash Encryption related options +# CONFIG_SECURE_FLASH_ENC_ENABLED=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +# CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +# CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=1 + +# Options for enabling eFuse emulation in Flash +# CONFIG_EFUSE_VIRTUAL=1 +# CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c3/ld/bootloader.ld b/bootloader/mcuboot/boot/espressif/port/esp32c3/ld/bootloader.ld new file mode 100644 index 0000000..747b549 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c3/ld/bootloader.ld @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Simplified memory map for the bootloader. + * + * The main purpose is to make sure the bootloader can load into main memory + * without overwriting itself. + */ + +MEMORY +{ + iram_seg (RWX) : org = 0x403C7000, len = 0x9000 + iram_loader_seg (RWX) : org = 0x403D0000, len = 0x5400 + dram_seg (RW) : org = 0x3FCD5400, len = 0xA000 +} + +/* Default entry point: */ +ENTRY(main); + +SECTIONS +{ + .iram_loader.text : + { + . = ALIGN (16); + _loader_text_start = ABSOLUTE(.); + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ + *libhal.a:*.*(.literal .text .literal.* .text.*) + *esp_mcuboot.*(.literal .text .literal.* .text.*) + *esp_loader.*(.literal .text .literal.* .text.*) + *main.*(.literal .text .literal.* .text.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _loader_text_end = ABSOLUTE(.); + } > iram_loader_seg + + .iram.text : + { + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + } > iram_seg + + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _dram_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } >dram_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram_seg + + .dram0.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.*(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.*(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _dram_end = ABSOLUTE(.); + } >dram_seg + + .iram.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram .iram.*) /* catch stray IRAM_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } > iram_seg + +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c3/serial_adapter.c b/bootloader/mcuboot/boot/espressif/port/esp32c3/serial_adapter.c new file mode 100644 index 0000000..09643a1 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c3/serial_adapter.c @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#define SERIAL_BOOT_GPIO_DETECT CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#else +#define SERIAL_BOOT_GPIO_DETECT GPIO_NUM_5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#define SERIAL_BOOT_GPIO_DETECT_VAL CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#else +#define SERIAL_BOOT_GPIO_DETECT_VAL 1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#define SERIAL_BOOT_DETECT_DELAY_S CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#else +#define SERIAL_BOOT_DETECT_DELAY_S 5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#define SERIAL_BOOT_GPIO_INPUT_TYPE CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#else +// pull-down +#define SERIAL_BOOT_GPIO_INPUT_TYPE 0 +#endif + +#ifndef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + +#ifdef CONFIG_ESP_SERIAL_BOOT_UART_NUM +#define SERIAL_BOOT_UART_NUM CONFIG_ESP_SERIAL_BOOT_UART_NUM +#else +#define SERIAL_BOOT_UART_NUM ESP_ROM_UART_1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#define SERIAL_BOOT_GPIO_RX CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#else +#define SERIAL_BOOT_GPIO_RX GPIO_NUM_8 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#define SERIAL_BOOT_GPIO_TX CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#else +#define SERIAL_BOOT_GPIO_TX GPIO_NUM_9 +#endif + +static uart_dev_t *serial_boot_uart_dev = (SERIAL_BOOT_UART_NUM == 0) ? + &UART0 : + &UART1; + +#endif + +void console_write(const char *str, int cnt) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + while (!usb_serial_jtag_ll_txfifo_writable()) { + MCUBOOT_WATCHDOG_FEED(); + } + usb_serial_jtag_ll_write_txfifo((const uint8_t *)str, cnt); + usb_serial_jtag_ll_txfifo_flush(); +#else + uint32_t tx_len; + uint32_t write_len; + + do { + tx_len = uart_ll_get_txfifo_len(serial_boot_uart_dev); + if (tx_len > 0) { + write_len = tx_len < cnt ? tx_len : cnt; + uart_ll_write_txfifo(serial_boot_uart_dev, (const uint8_t *)str, write_len); + cnt -= write_len; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (cnt > 0); +#endif +} + +int console_read(char *str, int cnt, int *newline) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + uint32_t read_len = 0; + + esp_rom_delay_us(1000); + do { + if (usb_serial_jtag_ll_rxfifo_data_available()) { + usb_serial_jtag_ll_read_rxfifo((uint8_t *)&str[read_len], 1); + read_len++; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!(read_len == cnt || str[read_len - 1] == '\n')); + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + + return read_len; +#else + uint32_t len = 0; + uint32_t read_len = 0; + bool stop = false; + + do { + len = uart_ll_get_rxfifo_len(serial_boot_uart_dev); + + if (len > 0) { + for (uint32_t i = 0; i < len; i++) { + /* Read the character from the RX FIFO */ + uart_ll_read_rxfifo(serial_boot_uart_dev, (uint8_t *)&str[read_len], 1); + read_len++; + if (read_len == cnt - 1|| str[read_len - 1] == '\n') { + stop = true; + break; + } + } + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!stop); + + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + return read_len; +#endif +} + +int boot_console_init(void) +{ + BOOT_LOG_INF("Initializing serial boot pins"); + +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + esp_rom_uart_tx_wait_idle(0); +#else + /* Enable GPIO for UART RX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_connect_in_signal(SERIAL_BOOT_GPIO_RX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_RX_PIN_IDX), + 0); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_pad_pullup_only(SERIAL_BOOT_GPIO_RX); + + + /* Enable GPIO for UART TX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_TX); + esp_rom_gpio_connect_out_signal(SERIAL_BOOT_GPIO_TX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_TX_PIN_IDX), + 0, 0); + gpio_ll_output_enable(&GPIO, SERIAL_BOOT_GPIO_TX); + + uart_ll_set_sclk(serial_boot_uart_dev, UART_SCLK_APB); + uart_ll_set_mode_normal(serial_boot_uart_dev); + uart_ll_set_baudrate(serial_boot_uart_dev, 115200, UART_SCLK_APB); + uart_ll_set_stop_bits(serial_boot_uart_dev, 1u); + uart_ll_set_parity(serial_boot_uart_dev, UART_PARITY_DISABLE); + uart_ll_set_rx_tout(serial_boot_uart_dev, 16); + uart_ll_set_data_bit_num(serial_boot_uart_dev, UART_DATA_8_BITS); + uart_ll_set_tx_idle_num(serial_boot_uart_dev, 0); + uart_ll_set_hw_flow_ctrl(serial_boot_uart_dev, UART_HW_FLOWCTRL_DISABLE, 100); + periph_ll_enable_clk_clear_rst(PERIPH_UART0_MODULE + SERIAL_BOOT_UART_NUM); + + uart_ll_txfifo_rst(serial_boot_uart_dev); + uart_ll_rxfifo_rst(serial_boot_uart_dev); + esp_rom_delay_us(50000); +#endif + + return 0; +} + +bool boot_serial_detect_pin(void) +{ + bool detected = false; + int pin_value = 0; + + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_DETECT); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_DETECT); + switch (SERIAL_BOOT_GPIO_INPUT_TYPE) { + // Pull-down + case 0: + gpio_ll_pulldown_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + // Pull-up + case 1: + gpio_ll_pullup_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + } + esp_rom_delay_us(50000); + + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + esp_rom_delay_us(50000); + + if (detected) { + if (SERIAL_BOOT_DETECT_DELAY_S > 0) { + /* The delay time is an approximation */ + for (int i = 0; i < (SERIAL_BOOT_DETECT_DELAY_S * 100); i++) { + esp_rom_delay_us(10000); + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + if (!detected) { + break; + } + } + } + } + return detected; +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c6/bootloader.conf b/bootloader/mcuboot/boot/espressif/port/esp32c6/bootloader.conf new file mode 100644 index 0000000..5c5307c --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c6/bootloader.conf @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x0000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_APPLICATION_SIZE=0x100000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x110000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# When enabled, prevents updating image to an older version +# CONFIG_ESP_DOWNGRADE_PREVENTION=y +# This option makes downgrade prevention rely also on security +# counter (defined using imgtool) instead of only image version +# CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y + +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +# CONFIG_ESP_MCUBOOT_SERIAL=y +# Use Serial through USB JTAG Serial port for Serial Recovery +# CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG=y +# Use sector erasing (recommended) instead of entire image size +# erasing when uploading through Serial Recovery +# CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y + +# GPIO used to boot on Serial Recovery +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=5 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +# CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +# CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication (not needed when using USB) +# CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_RX=8 +# GPIO for Serial TX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_TX=9 + +# Use UART0 for console printing (use either UART or USB alone) +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# (UART_NUM=0 must not be changed) +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=9 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=8 +# Use USB JTAG Serial for console printing +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# CONFIG_ESP_SIGN_EC256=y +# CONFIG_ESP_SIGN_ED25519=n +# CONFIG_ESP_SIGN_RSA=n +# CONFIG_ESP_SIGN_RSA_LEN=2048 + +# Use Tinycrypt lib for EC256 or ED25519 signing +# CONFIG_ESP_USE_TINYCRYPT=y +# Use Mbed TLS lib for RSA image signing +# CONFIG_ESP_USE_MBEDTLS=n + +# It is strongly recommended to generate a new signing key +# using imgtool instead of use the existent sample +# CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem + +# Hardware Secure Boot related options +# CONFIG_SECURE_SIGNED_ON_BOOT=1 +# CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +# CONFIG_SECURE_BOOT=1 +# CONFIG_SECURE_BOOT_V2_ENABLED=1 +# CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 + +# Hardware Flash Encryption related options +# CONFIG_SECURE_FLASH_ENC_ENABLED=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +# CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +# CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=1 + +# Options for enabling eFuse emulation in Flash +# CONFIG_EFUSE_VIRTUAL=1 +# CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c6/ld/bootloader.ld b/bootloader/mcuboot/boot/espressif/port/esp32c6/ld/bootloader.ld new file mode 100644 index 0000000..06b6e91 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c6/ld/bootloader.ld @@ -0,0 +1,177 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Simplified memory map for the bootloader. + * Make sure the bootloader can load into main memory without overwriting itself. + * + * ESP32-C6 ROM static data usage is as follows: + * - 0x4086ad08 - 0x4087c610: Shared buffers, used in UART/USB/SPI download mode only + * - 0x4087c610 - 0x4087e610: PRO CPU stack, can be reclaimed as heap after RTOS startup + * - 0x4087e610 - 0x40880000: ROM .bss and .data (not easily reclaimable) + * + * The 2nd stage bootloader can take space up to the end of ROM shared + * buffers area (0x4087c610). + */ + +/* We consider 0x4087c610 to be the last usable address for 2nd stage bootloader stack overhead, dram_seg, + * and work out iram_seg and iram_loader_seg addresses from there, backwards. + */ + +/* These lengths can be adjusted, if necessary: */ +bootloader_usable_dram_end = 0x4087c610; +bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ +bootloader_dram_seg_len = 0xA000; +bootloader_iram_loader_seg_len = 0x7000; +bootloader_iram_seg_len = 0x9000; + +/* Start of the lower region is determined by region size and the end of the higher region */ +bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; /* 0x4087c610 - 0x2000 = 0x4087a610 */ +bootloader_dram_seg_start = bootloader_dram_seg_end - bootloader_dram_seg_len; /* 0x4087a610 - 0x5000 = 0x40875610 */ +bootloader_iram_loader_seg_start = bootloader_dram_seg_start - bootloader_iram_loader_seg_len; /* 0x40875610 - 0x7000 = 0x4086e610 */ +bootloader_iram_seg_start = bootloader_iram_loader_seg_start - bootloader_iram_seg_len; /* 0x4086e610 - 0x3000 = 0x4086b610 */ + +MEMORY +{ + iram_seg (RWX) : org = bootloader_iram_seg_start, len = bootloader_iram_seg_len + iram_loader_seg (RWX) : org = bootloader_iram_loader_seg_start, len = bootloader_iram_loader_seg_len + dram_seg (RW) : org = bootloader_dram_seg_start, len = bootloader_dram_seg_len +} + +/* The app may use RAM for static allocations up to the start of iram_loader_seg. + * If you have changed something above and this assert fails: + * 1. Check what the new value of bootloader_iram_loader_seg start is. + * 2. Update the value in this assert. + * 3. Update SRAM_DRAM_END in components/esp_system/ld/esp32c6/memory.ld.in to the same value. + */ +ASSERT(bootloader_iram_loader_seg_start == 0x40869610, "bootloader_iram_loader_seg_start inconsistent with SRAM_DRAM_END"); + +/* Default entry point: */ +ENTRY(main); + +SECTIONS +{ + .iram_loader.text : + { + . = ALIGN (16); + _loader_text_start = ABSOLUTE(.); + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ + *libhal.a:*.*(.literal .text .literal.* .text.*) + *esp_mcuboot.*(.literal .text .literal.* .text.*) + *esp_loader.*(.literal .text .literal.* .text.*) + *main.*(.literal .text .literal.* .text.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _loader_text_end = ABSOLUTE(.); + } > iram_loader_seg + + .iram.text : + { + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + } > iram_seg + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _dram_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } >dram_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram_seg + + .dram0.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.*(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.*(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _dram_end = ABSOLUTE(.); + } >dram_seg + + .iram.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram .iram.*) /* catch stray IRAM_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } > iram_seg + +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32c6/serial_adapter.c b/bootloader/mcuboot/boot/espressif/port/esp32c6/serial_adapter.c new file mode 100644 index 0000000..5e6bb59 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32c6/serial_adapter.c @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#define SERIAL_BOOT_GPIO_DETECT CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#else +#define SERIAL_BOOT_GPIO_DETECT GPIO_NUM_3 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#define SERIAL_BOOT_GPIO_DETECT_VAL CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#else +#define SERIAL_BOOT_GPIO_DETECT_VAL 1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#define SERIAL_BOOT_DETECT_DELAY_S CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#else +#define SERIAL_BOOT_DETECT_DELAY_S 5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#define SERIAL_BOOT_GPIO_INPUT_TYPE CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#else +// pull-down +#define SERIAL_BOOT_GPIO_INPUT_TYPE 0 +#endif + +#ifndef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + +#ifdef CONFIG_ESP_SERIAL_BOOT_UART_NUM +#define SERIAL_BOOT_UART_NUM CONFIG_ESP_SERIAL_BOOT_UART_NUM +#else +#define SERIAL_BOOT_UART_NUM ESP_ROM_UART_1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#define SERIAL_BOOT_GPIO_RX CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#else +#define SERIAL_BOOT_GPIO_RX GPIO_NUM_10 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#define SERIAL_BOOT_GPIO_TX CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#else +#define SERIAL_BOOT_GPIO_TX GPIO_NUM_11 +#endif + +static uart_dev_t *serial_boot_uart_dev = (SERIAL_BOOT_UART_NUM == 0) ? + &UART0 : + &UART1; + +#endif + +void console_write(const char *str, int cnt) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + while (!usb_serial_jtag_ll_txfifo_writable()) { + MCUBOOT_WATCHDOG_FEED(); + } + usb_serial_jtag_ll_write_txfifo((const uint8_t *)str, cnt); + usb_serial_jtag_ll_txfifo_flush(); +#else + uint32_t tx_len; + uint32_t write_len; + + do { + tx_len = uart_ll_get_txfifo_len(serial_boot_uart_dev); + if (tx_len > 0) { + write_len = tx_len < cnt ? tx_len : cnt; + uart_ll_write_txfifo(serial_boot_uart_dev, (const uint8_t *)str, write_len); + cnt -= write_len; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (cnt > 0); +#endif +} + +int console_read(char *str, int cnt, int *newline) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + uint32_t read_len = 0; + + esp_rom_delay_us(1000); + do { + if (usb_serial_jtag_ll_rxfifo_data_available()) { + usb_serial_jtag_ll_read_rxfifo((uint8_t *)&str[read_len], 1); + read_len++; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!(read_len == cnt || str[read_len - 1] == '\n')); + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + + return read_len; +#else + uint32_t len = 0; + uint32_t read_len = 0; + bool stop = false; + + do { + len = uart_ll_get_rxfifo_len(serial_boot_uart_dev); + + if (len > 0) { + for (uint32_t i = 0; i < len; i++) { + /* Read the character from the RX FIFO */ + uart_ll_read_rxfifo(serial_boot_uart_dev, (uint8_t *)&str[read_len], 1); + read_len++; + if (read_len == cnt - 1|| str[read_len - 1] == '\n') { + stop = true; + break; + } + } + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!stop); + + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + return read_len; +#endif +} + +int boot_console_init(void) +{ + BOOT_LOG_INF("Initializing serial boot pins"); + +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + esp_rom_uart_tx_wait_idle(0); +#else + /* Enable GPIO for UART RX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_connect_in_signal(SERIAL_BOOT_GPIO_RX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_RX_PIN_IDX), + 0); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_pad_pullup_only(SERIAL_BOOT_GPIO_RX); + + + /* Enable GPIO for UART TX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_TX); + esp_rom_gpio_connect_out_signal(SERIAL_BOOT_GPIO_TX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_TX_PIN_IDX), + 0, 0); + gpio_ll_output_enable(&GPIO, SERIAL_BOOT_GPIO_TX); + + uart_ll_set_sclk(serial_boot_uart_dev, UART_SCLK_DEFAULT); + uart_ll_set_mode_normal(serial_boot_uart_dev); + uart_ll_set_baudrate(serial_boot_uart_dev, 115200, UART_SCLK_DEFAULT); + uart_ll_set_stop_bits(serial_boot_uart_dev, 1u); + uart_ll_set_parity(serial_boot_uart_dev, UART_PARITY_DISABLE); + uart_ll_set_rx_tout(serial_boot_uart_dev, 16); + uart_ll_set_data_bit_num(serial_boot_uart_dev, UART_DATA_8_BITS); + uart_ll_set_tx_idle_num(serial_boot_uart_dev, 0); + uart_ll_set_hw_flow_ctrl(serial_boot_uart_dev, UART_HW_FLOWCTRL_DISABLE, 100); + periph_ll_enable_clk_clear_rst(PERIPH_UART0_MODULE + SERIAL_BOOT_UART_NUM); + + uart_ll_txfifo_rst(serial_boot_uart_dev); + uart_ll_rxfifo_rst(serial_boot_uart_dev); + esp_rom_delay_us(50000); +#endif + + return 0; +} + +bool boot_serial_detect_pin(void) +{ + bool detected = false; + int pin_value = 0; + + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_DETECT); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_DETECT); + switch (SERIAL_BOOT_GPIO_INPUT_TYPE) { + // Pull-down + case 0: + gpio_ll_pulldown_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + // Pull-up + case 1: + gpio_ll_pullup_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + } + esp_rom_delay_us(50000); + + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + esp_rom_delay_us(50000); + + if (detected) { + if (SERIAL_BOOT_DETECT_DELAY_S > 0) { + /* The delay time is an approximation */ + for (int i = 0; i < (SERIAL_BOOT_DETECT_DELAY_S * 100); i++) { + esp_rom_delay_us(10000); + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + if (!detected) { + break; + } + } + } + } + return detected; +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32h2/bootloader.conf b/bootloader/mcuboot/boot/espressif/port/esp32h2/bootloader.conf new file mode 100644 index 0000000..5c5307c --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32h2/bootloader.conf @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x0000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_APPLICATION_SIZE=0x100000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x110000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# When enabled, prevents updating image to an older version +# CONFIG_ESP_DOWNGRADE_PREVENTION=y +# This option makes downgrade prevention rely also on security +# counter (defined using imgtool) instead of only image version +# CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y + +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +# CONFIG_ESP_MCUBOOT_SERIAL=y +# Use Serial through USB JTAG Serial port for Serial Recovery +# CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG=y +# Use sector erasing (recommended) instead of entire image size +# erasing when uploading through Serial Recovery +# CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y + +# GPIO used to boot on Serial Recovery +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=5 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +# CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +# CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication (not needed when using USB) +# CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_RX=8 +# GPIO for Serial TX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_TX=9 + +# Use UART0 for console printing (use either UART or USB alone) +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# (UART_NUM=0 must not be changed) +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=9 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=8 +# Use USB JTAG Serial for console printing +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# CONFIG_ESP_SIGN_EC256=y +# CONFIG_ESP_SIGN_ED25519=n +# CONFIG_ESP_SIGN_RSA=n +# CONFIG_ESP_SIGN_RSA_LEN=2048 + +# Use Tinycrypt lib for EC256 or ED25519 signing +# CONFIG_ESP_USE_TINYCRYPT=y +# Use Mbed TLS lib for RSA image signing +# CONFIG_ESP_USE_MBEDTLS=n + +# It is strongly recommended to generate a new signing key +# using imgtool instead of use the existent sample +# CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem + +# Hardware Secure Boot related options +# CONFIG_SECURE_SIGNED_ON_BOOT=1 +# CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +# CONFIG_SECURE_BOOT=1 +# CONFIG_SECURE_BOOT_V2_ENABLED=1 +# CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 + +# Hardware Flash Encryption related options +# CONFIG_SECURE_FLASH_ENC_ENABLED=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +# CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +# CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=1 + +# Options for enabling eFuse emulation in Flash +# CONFIG_EFUSE_VIRTUAL=1 +# CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 diff --git a/bootloader/mcuboot/boot/espressif/port/esp32h2/ld/bootloader.ld b/bootloader/mcuboot/boot/espressif/port/esp32h2/ld/bootloader.ld new file mode 100644 index 0000000..b4330bf --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32h2/ld/bootloader.ld @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Simplified memory map for the bootloader. + * Make sure the bootloader can load into main memory without overwriting itself. + * + * ESP32-H2 ROM static data usage is as follows: + * - 0x4083ba78 - 0x4084d380: Shared buffers, used in UART/USB/SPI download mode only + * - 0x4084d380 - 0x4084f380: PRO CPU stack, can be reclaimed as heap after RTOS startup + * - 0x4084f380 - 0x4084fee0: ROM .bss and .data used in startup code or nonos/early boot (can be freed when IDF runs) + * - 0x4084fee0 - 0x40850000: ROM .bss and .data used in startup code and when IDF runs (cannot be freed) + * + * The 2nd stage bootloader can take space up to the end of ROM shared + * buffers area (0x4084d380). + */ + +/* We consider 0x3fcdc710 to be the last usable address for 2nd stage bootloader stack overhead, dram_seg, + * and work out iram_seg and iram_loader_seg addresses from there, backwards. + */ + +/* These lengths can be adjusted, if necessary: */ +bootloader_usable_dram_end = 0x4084cfd0; +bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */ +bootloader_dram_seg_len = 0xA000; +bootloader_iram_loader_seg_len = 0x7000; +bootloader_iram_seg_len = 0x8800; + +/* Start of the lower region is determined by region size and the end of the higher region */ +bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead; +bootloader_dram_seg_start = bootloader_dram_seg_end - bootloader_dram_seg_len; +bootloader_iram_loader_seg_start = bootloader_dram_seg_start - bootloader_iram_loader_seg_len; +bootloader_iram_seg_start = bootloader_iram_loader_seg_start - bootloader_iram_seg_len; + +MEMORY +{ + iram_seg (RWX) : org = bootloader_iram_seg_start, len = bootloader_iram_seg_len + iram_loader_seg (RWX) : org = bootloader_iram_loader_seg_start, len = bootloader_iram_loader_seg_len + dram_seg (RW) : org = bootloader_dram_seg_start, len = bootloader_dram_seg_len +} + +/* The app may use RAM for static allocations up to the start of iram_loader_seg. + * If you have changed something above and this assert fails: + * 1. Check what the new value of bootloader_iram_loader_seg start is. + * 2. Update the value in this assert. + * 3. Update SRAM_DRAM_END in components/esp_system/ld/esp32h2/memory.ld.in to the same value. + */ +ASSERT(bootloader_iram_loader_seg_start == 0x40839FD0, "bootloader_iram_loader_seg_start inconsistent with SRAM_DRAM_END"); + +/* Default entry point: */ +ENTRY(main); + +SECTIONS +{ + .iram_loader.text : + { + . = ALIGN (16); + _loader_text_start = ABSOLUTE(.); + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ + *libhal.a:*.*(.literal .text .literal.* .text.*) + *esp_mcuboot.*(.literal .text .literal.* .text.*) + *esp_loader.*(.literal .text .literal.* .text.*) + *main.*(.literal .text .literal.* .text.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _loader_text_end = ABSOLUTE(.); + } > iram_loader_seg + + .iram.text : + { + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + } > iram_seg + + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _dram_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } >dram_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram_seg + + .dram0.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.*(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.*(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _dram_end = ABSOLUTE(.); + } >dram_seg + + .iram.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram .iram.*) /* catch stray IRAM_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } > iram_seg + +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32h2/serial_adapter.c b/bootloader/mcuboot/boot/espressif/port/esp32h2/serial_adapter.c new file mode 100644 index 0000000..acd76b0 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32h2/serial_adapter.c @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#define SERIAL_BOOT_GPIO_DETECT CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#else +#define SERIAL_BOOT_GPIO_DETECT GPIO_NUM_12 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#define SERIAL_BOOT_GPIO_DETECT_VAL CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#else +#define SERIAL_BOOT_GPIO_DETECT_VAL 1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#define SERIAL_BOOT_DETECT_DELAY_S CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#else +#define SERIAL_BOOT_DETECT_DELAY_S 5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#define SERIAL_BOOT_GPIO_INPUT_TYPE CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#else +// pull-down +#define SERIAL_BOOT_GPIO_INPUT_TYPE 0 +#endif + +#ifndef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + +#ifdef CONFIG_ESP_SERIAL_BOOT_UART_NUM +#define SERIAL_BOOT_UART_NUM CONFIG_ESP_SERIAL_BOOT_UART_NUM +#else +#define SERIAL_BOOT_UART_NUM ESP_ROM_UART_1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#define SERIAL_BOOT_GPIO_RX CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#else +#define SERIAL_BOOT_GPIO_RX GPIO_NUM_10 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#define SERIAL_BOOT_GPIO_TX CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#else +#define SERIAL_BOOT_GPIO_TX GPIO_NUM_11 +#endif + +static uart_dev_t *serial_boot_uart_dev = (SERIAL_BOOT_UART_NUM == 0) ? + &UART0 : + &UART1; + +#endif + +void console_write(const char *str, int cnt) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + while (!usb_serial_jtag_ll_txfifo_writable()) { + MCUBOOT_WATCHDOG_FEED(); + } + usb_serial_jtag_ll_write_txfifo((const uint8_t *)str, cnt); + usb_serial_jtag_ll_txfifo_flush(); +#else + uint32_t tx_len; + uint32_t write_len; + + do { + tx_len = uart_ll_get_txfifo_len(serial_boot_uart_dev); + if (tx_len > 0) { + write_len = tx_len < cnt ? tx_len : cnt; + uart_ll_write_txfifo(serial_boot_uart_dev, (const uint8_t *)str, write_len); + cnt -= write_len; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (cnt > 0); +#endif +} + +int console_read(char *str, int cnt, int *newline) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + uint32_t read_len = 0; + + esp_rom_delay_us(1000); + do { + if (usb_serial_jtag_ll_rxfifo_data_available()) { + usb_serial_jtag_ll_read_rxfifo((uint8_t *)&str[read_len], 1); + read_len++; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!(read_len == cnt || str[read_len - 1] == '\n')); + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + + return read_len; +#else + uint32_t len = 0; + uint32_t read_len = 0; + bool stop = false; + + do { + len = uart_ll_get_rxfifo_len(serial_boot_uart_dev); + + if (len > 0) { + for (uint32_t i = 0; i < len; i++) { + /* Read the character from the RX FIFO */ + uart_ll_read_rxfifo(serial_boot_uart_dev, (uint8_t *)&str[read_len], 1); + read_len++; + if (read_len == cnt - 1|| str[read_len - 1] == '\n') { + stop = true; + break; + } + } + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!stop); + + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + return read_len; +#endif +} + +int boot_console_init(void) +{ + BOOT_LOG_INF("Initializing serial boot pins"); + +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + esp_rom_uart_tx_wait_idle(0); +#else + /* Enable GPIO for UART RX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_connect_in_signal(SERIAL_BOOT_GPIO_RX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_RX_PIN_IDX), + 0); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_pad_pullup_only(SERIAL_BOOT_GPIO_RX); + + + /* Enable GPIO for UART TX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_TX); + esp_rom_gpio_connect_out_signal(SERIAL_BOOT_GPIO_TX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_TX_PIN_IDX), + 0, 0); + gpio_ll_output_enable(&GPIO, SERIAL_BOOT_GPIO_TX); + + uart_ll_set_sclk(serial_boot_uart_dev, UART_SCLK_DEFAULT); + uart_ll_set_mode_normal(serial_boot_uart_dev); + uart_ll_set_baudrate(serial_boot_uart_dev, 115200, UART_SCLK_DEFAULT); + uart_ll_set_stop_bits(serial_boot_uart_dev, 1u); + uart_ll_set_parity(serial_boot_uart_dev, UART_PARITY_DISABLE); + uart_ll_set_rx_tout(serial_boot_uart_dev, 16); + uart_ll_set_data_bit_num(serial_boot_uart_dev, UART_DATA_8_BITS); + uart_ll_set_tx_idle_num(serial_boot_uart_dev, 0); + uart_ll_set_hw_flow_ctrl(serial_boot_uart_dev, UART_HW_FLOWCTRL_DISABLE, 100); + periph_ll_enable_clk_clear_rst(PERIPH_UART0_MODULE + SERIAL_BOOT_UART_NUM); + + uart_ll_txfifo_rst(serial_boot_uart_dev); + uart_ll_rxfifo_rst(serial_boot_uart_dev); + esp_rom_delay_us(50000); +#endif + + return 0; +} + +bool boot_serial_detect_pin(void) +{ + bool detected = false; + int pin_value = 0; + + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_DETECT); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_DETECT); + switch (SERIAL_BOOT_GPIO_INPUT_TYPE) { + // Pull-down + case 0: + gpio_ll_pulldown_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + // Pull-up + case 1: + gpio_ll_pullup_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + } + esp_rom_delay_us(50000); + + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + esp_rom_delay_us(50000); + + if (detected) { + if (SERIAL_BOOT_DETECT_DELAY_S > 0) { + /* The delay time is an approximation */ + for (int i = 0; i < (SERIAL_BOOT_DETECT_DELAY_S * 100); i++) { + esp_rom_delay_us(10000); + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + if (!detected) { + break; + } + } + } + } + return detected; +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32s2/bootloader.conf b/bootloader/mcuboot/boot/espressif/port/esp32s2/bootloader.conf new file mode 100644 index 0000000..485ba77 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32s2/bootloader.conf @@ -0,0 +1,84 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x1000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_APPLICATION_SIZE=0x100000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x110000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# When enabled, prevents updating image to an older version +# CONFIG_ESP_DOWNGRADE_PREVENTION=y +# This option makes downgrade prevention rely also on security +# counter (defined using imgtool) instead of only image version +# CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y + +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +# CONFIG_ESP_MCUBOOT_SERIAL=y +# Use sector erasing (recommended) instead of entire image size +# erasing when uploading through Serial Recovery +# CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y + +# GPIO used to boot on Serial Recovery +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=5 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +# CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +# CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication (not needed when using USB) +# CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_RX=18 +# GPIO for Serial TX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_TX=17 + +# Use UART0 for console printing (use either UART or USB alone) +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# (UART_NUM=0 must not be changed) +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=17 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=18 + +# CONFIG_ESP_SIGN_EC256=y +# CONFIG_ESP_SIGN_ED25519=n +# CONFIG_ESP_SIGN_RSA=n +# CONFIG_ESP_SIGN_RSA_LEN=2048 + +# Use Tinycrypt lib for EC256 or ED25519 signing +# CONFIG_ESP_USE_TINYCRYPT=y +# Use Mbed TLS lib for RSA image signing +# CONFIG_ESP_USE_MBEDTLS=n + +# It is strongly recommended to generate a new signing key +# using imgtool instead of use the existent sample +# CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem + +# Hardware Secure Boot related options +# CONFIG_SECURE_SIGNED_ON_BOOT=1 +# CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +# CONFIG_SECURE_BOOT=1 +# CONFIG_SECURE_BOOT_V2_ENABLED=1 +# CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 + +# Hardware Flash Encryption related options +# CONFIG_SECURE_FLASH_ENC_ENABLED=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +# CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +# CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=1 + +# Options for enabling eFuse emulation in Flash +# CONFIG_EFUSE_VIRTUAL=1 +# CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 diff --git a/bootloader/mcuboot/boot/espressif/port/esp32s2/ld/bootloader.ld b/bootloader/mcuboot/boot/espressif/port/esp32s2/ld/bootloader.ld new file mode 100644 index 0000000..cf159c3 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32s2/ld/bootloader.ld @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Simplified memory map for the bootloader. + * + * The main purpose is to make sure the bootloader can load into main memory + * without overwriting itself. + */ + +MEMORY +{ + iram_seg (RWX) : org = 0x40047000, len = 0x9000 + iram_loader_seg (RWX) : org = 0x40050000, len = 0x6000 + dram_seg (RW) : org = 0x3FFE6000, len = 0x9000 +} + +/* Default entry point: */ +ENTRY(main); + +SECTIONS +{ + .iram_loader.text : + { + . = ALIGN (16); + _loader_text_start = ABSOLUTE(.); + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ + *libhal.a:*.*(.literal .text .literal.* .text.*) + *esp_mcuboot.*(.literal .text .literal.* .text.*) + *esp_loader.*(.literal .text .literal.* .text.*) + *main.*(.literal .text .literal.* .text.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _loader_text_end = ABSOLUTE(.); + } > iram_loader_seg + + .iram.text : + { + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + } > iram_seg + + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _dram_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } >dram_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram_seg + + .dram0.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.*(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.*(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _dram_end = ABSOLUTE(.); + } >dram_seg + + .iram.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram .iram.*) /* catch stray IRAM_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } > iram_seg + +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32s2/serial_adapter.c b/bootloader/mcuboot/boot/espressif/port/esp32s2/serial_adapter.c new file mode 100644 index 0000000..e22a6ba --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32s2/serial_adapter.c @@ -0,0 +1,191 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#define SERIAL_BOOT_GPIO_DETECT CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#else +#define SERIAL_BOOT_GPIO_DETECT GPIO_NUM_5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#define SERIAL_BOOT_GPIO_DETECT_VAL CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#else +#define SERIAL_BOOT_GPIO_DETECT_VAL 1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#define SERIAL_BOOT_DETECT_DELAY_S CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#else +#define SERIAL_BOOT_DETECT_DELAY_S 5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#define SERIAL_BOOT_GPIO_INPUT_TYPE CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#else +// pull-down +#define SERIAL_BOOT_GPIO_INPUT_TYPE 0 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_UART_NUM +#define SERIAL_BOOT_UART_NUM CONFIG_ESP_SERIAL_BOOT_UART_NUM +#else +#define SERIAL_BOOT_UART_NUM ESP_ROM_UART_1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#define SERIAL_BOOT_GPIO_RX CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#else +#define SERIAL_BOOT_GPIO_RX GPIO_NUM_18 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#define SERIAL_BOOT_GPIO_TX CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#else +#define SERIAL_BOOT_GPIO_TX GPIO_NUM_17 +#endif + +static uart_dev_t *serial_boot_uart_dev = (SERIAL_BOOT_UART_NUM == 0) ? + &UART0 : + &UART1; + +void console_write(const char *str, int cnt) +{ + uint32_t tx_len; + uint32_t write_len; + + do { + tx_len = uart_ll_get_txfifo_len(serial_boot_uart_dev); + if (tx_len > 0) { + write_len = tx_len < cnt ? tx_len : cnt; + uart_ll_write_txfifo(serial_boot_uart_dev, (const uint8_t *)str, write_len); + cnt -= write_len; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (cnt > 0); +} + +int console_read(char *str, int cnt, int *newline) +{ + uint32_t len = 0; + uint32_t read_len = 0; + bool stop = false; + + do { + len = uart_ll_get_rxfifo_len(serial_boot_uart_dev); + + if (len > 0) { + for (uint32_t i = 0; i < len; i++) { + /* Read the character from the RX FIFO */ + uart_ll_read_rxfifo(serial_boot_uart_dev, (uint8_t *)&str[read_len], 1); + read_len++; + if (read_len == cnt - 1|| str[read_len - 1] == '\n') { + stop = true; + break; + } + } + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!stop); + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + return read_len; +} + +int boot_console_init(void) +{ + BOOT_LOG_INF("Initializing serial boot pins"); + + /* Enable GPIO for UART RX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_connect_in_signal(SERIAL_BOOT_GPIO_RX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_RX_PIN_IDX), + 0); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_pad_pullup_only(SERIAL_BOOT_GPIO_RX); + + /* Enable GPIO for UART TX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_TX); + esp_rom_gpio_connect_out_signal(SERIAL_BOOT_GPIO_TX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_TX_PIN_IDX), + 0, 0); + gpio_ll_output_enable(&GPIO, SERIAL_BOOT_GPIO_TX); + + uart_ll_set_sclk(serial_boot_uart_dev, UART_SCLK_APB); + uart_ll_set_mode_normal(serial_boot_uart_dev); + uart_ll_set_baudrate(serial_boot_uart_dev, 115200, UART_SCLK_APB); + uart_ll_set_stop_bits(serial_boot_uart_dev, 1u); + uart_ll_set_parity(serial_boot_uart_dev, UART_PARITY_DISABLE); + uart_ll_set_rx_tout(serial_boot_uart_dev, 16); + uart_ll_set_data_bit_num(serial_boot_uart_dev, UART_DATA_8_BITS); + uart_ll_set_tx_idle_num(serial_boot_uart_dev, 0); + uart_ll_set_hw_flow_ctrl(serial_boot_uart_dev, UART_HW_FLOWCTRL_DISABLE, 100); + periph_ll_enable_clk_clear_rst(PERIPH_UART0_MODULE + SERIAL_BOOT_UART_NUM); + + uart_ll_txfifo_rst(serial_boot_uart_dev); + uart_ll_rxfifo_rst(serial_boot_uart_dev); + esp_rom_delay_us(50000); + + return 0; +} + +bool boot_serial_detect_pin(void) +{ + bool detected = false; + int pin_value = 0; + + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_DETECT); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_DETECT); + switch (SERIAL_BOOT_GPIO_INPUT_TYPE) { + // Pull-down + case 0: + gpio_ll_pulldown_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + // Pull-up + case 1: + gpio_ll_pullup_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + } + esp_rom_delay_us(50000); + + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + esp_rom_delay_us(50000); + + if (detected) { + if (SERIAL_BOOT_DETECT_DELAY_S > 0) { + /* The delay time is an approximation */ + for (int i = 0; i < (SERIAL_BOOT_DETECT_DELAY_S * 100); i++) { + esp_rom_delay_us(10000); + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + if (!detected) { + break; + } + } + } + } + return detected; +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader-multi.conf b/bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader-multi.conf new file mode 100644 index 0000000..21c3457 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader-multi.conf @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x0000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +# Example of values to be used when multi image is enabled +# Notice that the OS layer and update agent must be aware +# of these regions +CONFIG_ESP_APPLICATION_SIZE=0x80000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x90000 +CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS=0x110000 +CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS=0x190000 +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# Use UART0 for console printing (use either UART or USB alone) +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# (UART_NUM=0 must not be changed) +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=17 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=18 +# Use USB JTAG Serial for console printing +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# Enables multi image, if it is not defined, it is assumed +# only one updatable image +# CONFIG_ESP_IMAGE_NUMBER=2 + +# Enables multi image boot on independent processors +# (main host OS is not responsible for booting the second image) +# Use only with CONFIG_ESP_IMAGE_NUMBER=2 +# CONFIG_ESP_MULTI_PROCESSOR_BOOT=y diff --git a/bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader.conf b/bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader.conf new file mode 100644 index 0000000..138737d --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32s3/bootloader.conf @@ -0,0 +1,108 @@ +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ESP_FLASH_SIZE=4MB +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_BOOTLOADER_OFFSET=0x0000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_APPLICATION_SIZE=0x100000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x110000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y +CONFIG_ESP_SCRATCH_OFFSET=0x210000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# When enabled, prevents updating image to an older version +# CONFIG_ESP_DOWNGRADE_PREVENTION=y +# This option makes downgrade prevention rely also on security +# counter (defined using imgtool) instead of only image version +# CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y + +# Enables multi image, if it is not defined, it is assumed +# only one updatable image +# CONFIG_ESP_IMAGE_NUMBER=2 + +# Enables multi image boot on independent processors +# (main host OS is not responsible for booting the second image) +# Use only with CONFIG_ESP_IMAGE_NUMBER=2 +# CONFIG_ESP_MULTI_PROCESSOR_BOOT=y + +# Example of values to be used when multi image is enabled +# Notice that the OS layer and update agent must be aware +# of these regions +# CONFIG_ESP_APPLICATION_SIZE=0x80000 +# CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +# CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x90000 +# CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS=0x110000 +# CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS=0x190000 +# CONFIG_ESP_SCRATCH_OFFSET=0x210000 +# CONFIG_ESP_SCRATCH_SIZE=0x40000 + +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +# CONFIG_ESP_MCUBOOT_SERIAL=y +# Use Serial through USB JTAG Serial port for Serial Recovery +# CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG=y +# Use sector erasing (recommended) instead of entire image size +# erasing when uploading through Serial Recovery +# CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y + +# GPIO used to boot on Serial Recovery +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=5 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +# CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +# CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +# CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication (not needed when using USB) +# CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_RX=18 +# GPIO for Serial TX signal +# CONFIG_ESP_SERIAL_BOOT_GPIO_TX=17 + +# Use UART0 for console printing (use either UART or USB alone) +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +# Configures alternative UART port for console printing +# (UART_NUM=0 must not be changed) +# CONFIG_ESP_CONSOLE_UART_CUSTOM=y +# CONFIG_ESP_CONSOLE_UART_TX_GPIO=17 +# CONFIG_ESP_CONSOLE_UART_RX_GPIO=18 +# Use USB JTAG Serial for console printing +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# CONFIG_ESP_SIGN_EC256=y +# CONFIG_ESP_SIGN_ED25519=n +# CONFIG_ESP_SIGN_RSA=n +# CONFIG_ESP_SIGN_RSA_LEN=2048 + +# Use Tinycrypt lib for EC256 or ED25519 signing +# CONFIG_ESP_USE_TINYCRYPT=y +# Use Mbed TLS lib for RSA image signing +# CONFIG_ESP_USE_MBEDTLS=n + +# It is strongly recommended to generate a new signing key +# using imgtool instead of use the existent sample +# CONFIG_ESP_SIGN_KEY_FILE=root-ec-p256.pem + +# Hardware Secure Boot related options +# CONFIG_SECURE_SIGNED_ON_BOOT=1 +# CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +# CONFIG_SECURE_BOOT=1 +# CONFIG_SECURE_BOOT_V2_ENABLED=1 +# CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 + +# Hardware Flash Encryption related options +# CONFIG_SECURE_FLASH_ENC_ENABLED=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +# CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +# CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +# CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=1 + +# Options for enabling eFuse emulation in Flash +# CONFIG_EFUSE_VIRTUAL=1 +# CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 diff --git a/bootloader/mcuboot/boot/espressif/port/esp32s3/ld/bootloader.ld b/bootloader/mcuboot/boot/espressif/port/esp32s3/ld/bootloader.ld new file mode 100644 index 0000000..082081e --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32s3/ld/bootloader.ld @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Simplified memory map for the bootloader. + * + * The main purpose is to make sure the bootloader can load into main memory + * without overwriting itself. + */ + +MEMORY +{ + iram_seg (RWX) : org = 0x403B0000, len = 0xA000 + iram_loader_seg (RWX) : org = 0x403BA000, len = 0x6000 + dram_seg (RW) : org = 0x3FCD0000, len = 0x9A00 +} + +/* Default entry point: */ +ENTRY(main); + +SECTIONS +{ + .iram_loader.text : + { + . = ALIGN (16); + _loader_text_start = ABSOLUTE(.); + *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram1 .iram1.*) /* catch stray IRAM_ATTR */ + *libhal.a:*.*(.literal .text .literal.* .text.*) + *esp_mcuboot.*(.literal .text .literal.* .text.*) + *esp_loader.*(.literal .text .literal.* .text.*) + *main.*(.literal .text .literal.* .text.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _loader_text_end = ABSOLUTE(.); + } > iram_loader_seg + + .iram.text : + { + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + } > iram_seg + + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _dram_start = ABSOLUTE(.); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } >dram_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + _data_end = ABSOLUTE(.); + } >dram_seg + + .dram0.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + *(.eh_frame) + . = (. + 3) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.*(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.*(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.*) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _dram_end = ABSOLUTE(.); + } >dram_seg + + .iram.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.iram .iram.*) /* catch stray IRAM_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + } > iram_seg + +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp32s3/serial_adapter.c b/bootloader/mcuboot/boot/espressif/port/esp32s3/serial_adapter.c new file mode 100644 index 0000000..7b244a6 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp32s3/serial_adapter.c @@ -0,0 +1,228 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#define SERIAL_BOOT_GPIO_DETECT CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT +#else +#define SERIAL_BOOT_GPIO_DETECT GPIO_NUM_5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#define SERIAL_BOOT_GPIO_DETECT_VAL CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL +#else +#define SERIAL_BOOT_GPIO_DETECT_VAL 1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#define SERIAL_BOOT_DETECT_DELAY_S CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S +#else +#define SERIAL_BOOT_DETECT_DELAY_S 5 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#define SERIAL_BOOT_GPIO_INPUT_TYPE CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE +#else +// pull-down +#define SERIAL_BOOT_GPIO_INPUT_TYPE 0 +#endif + +#ifndef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + +#ifdef CONFIG_ESP_SERIAL_BOOT_UART_NUM +#define SERIAL_BOOT_UART_NUM CONFIG_ESP_SERIAL_BOOT_UART_NUM +#else +#define SERIAL_BOOT_UART_NUM ESP_ROM_UART_1 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#define SERIAL_BOOT_GPIO_RX CONFIG_ESP_SERIAL_BOOT_GPIO_RX +#else +#define SERIAL_BOOT_GPIO_RX GPIO_NUM_18 +#endif + +#ifdef CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#define SERIAL_BOOT_GPIO_TX CONFIG_ESP_SERIAL_BOOT_GPIO_TX +#else +#define SERIAL_BOOT_GPIO_TX GPIO_NUM_17 +#endif + +static uart_dev_t *serial_boot_uart_dev = (SERIAL_BOOT_UART_NUM == 0) ? + &UART0 : + &UART1; + +#endif + +void console_write(const char *str, int cnt) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + while (!usb_serial_jtag_ll_txfifo_writable()) { + MCUBOOT_WATCHDOG_FEED(); + } + usb_serial_jtag_ll_write_txfifo((const uint8_t *)str, cnt); + usb_serial_jtag_ll_txfifo_flush(); +#else + uint32_t tx_len; + uint32_t write_len; + + do { + tx_len = uart_ll_get_txfifo_len(serial_boot_uart_dev); + if (tx_len > 0) { + write_len = tx_len < cnt ? tx_len : cnt; + uart_ll_write_txfifo(serial_boot_uart_dev, (const uint8_t *)str, write_len); + cnt -= write_len; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (cnt > 0); +#endif +} + +int console_read(char *str, int cnt, int *newline) +{ +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + + uint32_t read_len = 0; + + esp_rom_delay_us(1000); + do { + if (usb_serial_jtag_ll_rxfifo_data_available()) { + usb_serial_jtag_ll_read_rxfifo((uint8_t *)&str[read_len], 1); + read_len++; + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!(read_len == cnt || str[read_len - 1] == '\n')); + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + + return read_len; +#else + uint32_t len = 0; + uint32_t read_len = 0; + bool stop = false; + + do { + len = uart_ll_get_rxfifo_len(serial_boot_uart_dev); + + if (len > 0) { + for (uint32_t i = 0; i < len; i++) { + /* Read the character from the RX FIFO */ + uart_ll_read_rxfifo(serial_boot_uart_dev, (uint8_t *)&str[read_len], 1); + read_len++; + if (read_len == cnt - 1|| str[read_len - 1] == '\n') { + stop = true; + break; + } + } + } + MCUBOOT_WATCHDOG_FEED(); + esp_rom_delay_us(1000); + } while (!stop); + + *newline = (str[read_len - 1] == '\n') ? 1 : 0; + return read_len; +#endif +} + +int boot_console_init(void) +{ + BOOT_LOG_INF("Initializing serial boot pins"); + +#ifdef CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG + usb_serial_jtag_ll_txfifo_flush(); + esp_rom_uart_tx_wait_idle(0); +#else + /* Enable GPIO for UART RX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_connect_in_signal(SERIAL_BOOT_GPIO_RX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_RX_PIN_IDX), + 0); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_RX); + esp_rom_gpio_pad_pullup_only(SERIAL_BOOT_GPIO_RX); + + /* Enable GPIO for UART TX */ + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_TX); + esp_rom_gpio_connect_out_signal(SERIAL_BOOT_GPIO_TX, + UART_PERIPH_SIGNAL(SERIAL_BOOT_UART_NUM, SOC_UART_TX_PIN_IDX), + 0, 0); + gpio_ll_output_enable(&GPIO, SERIAL_BOOT_GPIO_TX); + + uart_ll_set_sclk(serial_boot_uart_dev, UART_SCLK_APB); + uart_ll_set_mode_normal(serial_boot_uart_dev); + uart_ll_set_baudrate(serial_boot_uart_dev, 115200, UART_SCLK_APB); + uart_ll_set_stop_bits(serial_boot_uart_dev, 1u); + uart_ll_set_parity(serial_boot_uart_dev, UART_PARITY_DISABLE); + uart_ll_set_rx_tout(serial_boot_uart_dev, 16); + uart_ll_set_data_bit_num(serial_boot_uart_dev, UART_DATA_8_BITS); + uart_ll_set_tx_idle_num(serial_boot_uart_dev, 0); + uart_ll_set_hw_flow_ctrl(serial_boot_uart_dev, UART_HW_FLOWCTRL_DISABLE, 100); + periph_ll_enable_clk_clear_rst(PERIPH_UART0_MODULE + SERIAL_BOOT_UART_NUM); + + uart_ll_txfifo_rst(serial_boot_uart_dev); + uart_ll_rxfifo_rst(serial_boot_uart_dev); + esp_rom_delay_us(50000); +#endif + return 0; +} + +bool boot_serial_detect_pin(void) +{ + bool detected = false; + int pin_value = 0; + + esp_rom_gpio_pad_select_gpio(SERIAL_BOOT_GPIO_DETECT); + gpio_ll_input_enable(&GPIO, SERIAL_BOOT_GPIO_DETECT); + switch (SERIAL_BOOT_GPIO_INPUT_TYPE) { + // Pull-down + case 0: + gpio_ll_pulldown_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + // Pull-up + case 1: + gpio_ll_pullup_en(&GPIO, SERIAL_BOOT_GPIO_DETECT); + break; + } + esp_rom_delay_us(50000); + + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + esp_rom_delay_us(50000); + + if (detected) { + if (SERIAL_BOOT_DETECT_DELAY_S > 0) { + /* The delay time is an approximation */ + for (int i = 0; i < (SERIAL_BOOT_DETECT_DELAY_S * 100); i++) { + esp_rom_delay_us(10000); + pin_value = gpio_ll_get_level(&GPIO, SERIAL_BOOT_GPIO_DETECT); + detected = (pin_value == SERIAL_BOOT_GPIO_DETECT_VAL); + if (!detected) { + break; + } + } + } + } + return detected; +} diff --git a/bootloader/mcuboot/boot/espressif/port/esp_loader.c b/bootloader/mcuboot/boot/espressif/port/esp_loader.c new file mode 100644 index 0000000..9074665 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp_loader.c @@ -0,0 +1,105 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include "bootloader_memory_utils.h" +#include "bootloader_flash_priv.h" +#include "esp_flash_encrypt.h" + +#include "rom/uart.h" + +#include "esp_mcuboot_image.h" +#include "esp_loader.h" +#include "flash_map_backend/flash_map_backend.h" + +#ifdef CONFIG_ESP_MULTI_PROCESSOR_BOOT +#include "app_cpu_start.h" +#endif + +static int load_segment(const struct flash_area *fap, uint32_t data_addr, uint32_t data_len, uint32_t load_addr) +{ + const uint32_t *data = (const uint32_t *)bootloader_mmap((fap->fa_off + data_addr), data_len); + if (!data) { + BOOT_LOG_ERR("%s: Bootloader mmap failed", __func__); + return -1; + } + memcpy((void *)load_addr, data, data_len); + bootloader_munmap(data); + return 0; +} + +void esp_app_image_load(int image_index, int slot, unsigned int hdr_offset, unsigned int *entry_addr) +{ + const struct flash_area *fap; + int area_id; + int rc; + + area_id = flash_area_id_from_multi_image_slot(image_index, slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + BOOT_LOG_ERR("%s: flash_area_open failed with %d", __func__, rc); + } + + BOOT_LOG_INF("Loading image %d - slot %d from flash, area id: %d", image_index, slot, area_id); + + const uint32_t *data = (const uint32_t *)bootloader_mmap((fap->fa_off + hdr_offset), sizeof(esp_image_load_header_t)); + esp_image_load_header_t load_header = {0}; + memcpy((void *)&load_header, data, sizeof(esp_image_load_header_t)); + bootloader_munmap(data); + + if (load_header.header_magic != ESP_LOAD_HEADER_MAGIC) { + BOOT_LOG_ERR("Load header magic verification failed. Aborting"); + FIH_PANIC; + } + + if (!esp_ptr_in_iram((void *)load_header.iram_dest_addr) || !esp_ptr_in_iram((void *)(load_header.iram_dest_addr + load_header.iram_size))) { + BOOT_LOG_ERR("IRAM region in load header is not valid. Aborting"); + FIH_PANIC; + } + + if (!esp_ptr_in_dram((void *)load_header.dram_dest_addr) || !esp_ptr_in_dram((void *)(load_header.dram_dest_addr + load_header.dram_size))) { + BOOT_LOG_ERR("DRAM region in load header is not valid. Aborting"); + FIH_PANIC; + } + + if (!esp_ptr_in_iram((void *)load_header.entry_addr)) { + BOOT_LOG_ERR("Application entry point (0x%x) is not in IRAM. Aborting", load_header.entry_addr); + FIH_PANIC; + } + + BOOT_LOG_INF("DRAM segment: start=0x%x, size=0x%x, vaddr=0x%x", fap->fa_off + load_header.dram_flash_offset, load_header.dram_size, load_header.dram_dest_addr); + load_segment(fap, load_header.dram_flash_offset, load_header.dram_size, load_header.dram_dest_addr); + + BOOT_LOG_INF("IRAM segment: start=0x%x, size=0x%x, vaddr=0x%x", fap->fa_off + load_header.iram_flash_offset, load_header.iram_size, load_header.iram_dest_addr); + load_segment(fap, load_header.iram_flash_offset, load_header.iram_size, load_header.iram_dest_addr); + + BOOT_LOG_INF("start=0x%x", load_header.entry_addr); + uart_tx_wait_idle(0); + + assert(entry_addr != NULL); + *entry_addr = load_header.entry_addr; +} + +void start_cpu0_image(int image_index, int slot, unsigned int hdr_offset) +{ + unsigned int entry_addr; + esp_app_image_load(image_index, slot, hdr_offset, &entry_addr); + ((void (*)(void))entry_addr)(); /* Call to application entry address should not return */ + FIH_PANIC; /* It should not get here */ +} + +#ifdef CONFIG_ESP_MULTI_PROCESSOR_BOOT +void start_cpu1_image(int image_index, int slot, unsigned int hdr_offset) +{ + unsigned int entry_addr; + esp_app_image_load(image_index, slot, hdr_offset, &entry_addr); + appcpu_start(entry_addr); +} +#endif diff --git a/bootloader/mcuboot/boot/espressif/port/esp_mcuboot.c b/bootloader/mcuboot/boot/espressif/port/esp_mcuboot.c new file mode 100644 index 0000000..0ee9e38 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/port/esp_mcuboot.c @@ -0,0 +1,420 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +#include "sdkconfig.h" +#include "esp_err.h" +#include "bootloader_flash_priv.h" +#include "esp_flash_encrypt.h" + +#include "flash_map_backend/flash_map_backend.h" +#include "sysflash/sysflash.h" + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef ALIGN_UP +# define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#endif + +#ifndef ALIGN_DOWN +# define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) +#endif + +#ifndef ALIGN_OFFSET +# define ALIGN_OFFSET(num, align) ((num) & ((align) - 1)) +#endif + +#ifndef IS_ALIGNED +# define IS_ALIGNED(num, align) (ALIGN_OFFSET((num), (align)) == 0) +#endif + +#define FLASH_BUFFER_SIZE 256 /* SPI Flash block size */ + +_Static_assert(IS_ALIGNED(FLASH_BUFFER_SIZE, 4), "Buffer size for SPI Flash operations must be 4-byte aligned."); + +#define BOOTLOADER_START_ADDRESS CONFIG_BOOTLOADER_OFFSET_IN_FLASH +#define BOOTLOADER_SIZE CONFIG_ESP_BOOTLOADER_SIZE +#define IMAGE0_PRIMARY_START_ADDRESS CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS +#define IMAGE0_SECONDARY_START_ADDRESS CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS +#define SCRATCH_OFFSET CONFIG_ESP_SCRATCH_OFFSET +#if (MCUBOOT_IMAGE_NUMBER == 2) +#define IMAGE1_PRIMARY_START_ADDRESS CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS +#define IMAGE1_SECONDARY_START_ADDRESS CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS +#endif + +#define APPLICATION_SIZE CONFIG_ESP_APPLICATION_SIZE +#define SCRATCH_SIZE CONFIG_ESP_SCRATCH_SIZE + +extern int ets_printf(const char *fmt, ...); + +static const struct flash_area bootloader = { + .fa_id = FLASH_AREA_BOOTLOADER, + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = BOOTLOADER_START_ADDRESS, + .fa_size = BOOTLOADER_SIZE, +}; + +static const struct flash_area primary_img0 = { + .fa_id = FLASH_AREA_IMAGE_PRIMARY(0), + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = IMAGE0_PRIMARY_START_ADDRESS, + .fa_size = APPLICATION_SIZE, +}; + +static const struct flash_area secondary_img0 = { + .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = IMAGE0_SECONDARY_START_ADDRESS, + .fa_size = APPLICATION_SIZE, +}; + +#if (MCUBOOT_IMAGE_NUMBER == 2) +static const struct flash_area primary_img1 = { + .fa_id = FLASH_AREA_IMAGE_PRIMARY(1), + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = IMAGE1_PRIMARY_START_ADDRESS, + .fa_size = APPLICATION_SIZE, +}; + +static const struct flash_area secondary_img1 = { + .fa_id = FLASH_AREA_IMAGE_SECONDARY(1), + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = IMAGE1_SECONDARY_START_ADDRESS, + .fa_size = APPLICATION_SIZE, +}; +#endif + +static const struct flash_area scratch_img0 = { + .fa_id = FLASH_AREA_IMAGE_SCRATCH, + .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, + .fa_off = SCRATCH_OFFSET, + .fa_size = SCRATCH_SIZE, +}; + +static const struct flash_area *s_flash_areas[] = { + &bootloader, + &primary_img0, + &secondary_img0, +#if (MCUBOOT_IMAGE_NUMBER == 2) + &primary_img1, + &secondary_img1, +#endif + &scratch_img0, +}; + +static const struct flash_area *prv_lookup_flash_area(uint8_t id) { + for (size_t i = 0; i < ARRAY_SIZE(s_flash_areas); i++) { + const struct flash_area *area = s_flash_areas[i]; + if (id == area->fa_id) { + return area; + } + } + return NULL; +} + +int flash_area_open(uint8_t id, const struct flash_area **area_outp) +{ + BOOT_LOG_DBG("%s: ID=%d", __func__, (int)id); + const struct flash_area *area = prv_lookup_flash_area(id); + *area_outp = area; + return area != NULL ? 0 : -1; +} + +void flash_area_close(const struct flash_area *area) +{ + +} + +static bool aligned_flash_read(uintptr_t addr, void *dest, size_t size) +{ + if (IS_ALIGNED(addr, 4) && IS_ALIGNED((uintptr_t)dest, 4) && IS_ALIGNED(size, 4)) { + /* A single read operation is enough when when all parameters are aligned */ + + return bootloader_flash_read(addr, dest, size, true) == ESP_OK; + } + + const uint32_t aligned_addr = ALIGN_DOWN(addr, 4); + const uint32_t addr_offset = ALIGN_OFFSET(addr, 4); + uint32_t bytes_remaining = size; + uint8_t read_data[FLASH_BUFFER_SIZE] = {0}; + + /* Align the read address to 4-byte boundary and ensure read size is a multiple of 4 bytes */ + + uint32_t bytes = MIN(bytes_remaining + addr_offset, sizeof(read_data)); + if (bootloader_flash_read(aligned_addr, read_data, ALIGN_UP(bytes, 4), true) != ESP_OK) { + return false; + } + + /* Skip non-useful data which may have been read for adjusting the alignment */ + + uint32_t bytes_read = bytes - addr_offset; + memcpy(dest, &read_data[addr_offset], bytes_read); + + bytes_remaining -= bytes_read; + + /* Read remaining data from Flash in case requested size is greater than buffer size */ + + uint32_t offset = bytes; + + while (bytes_remaining != 0) { + bytes = MIN(bytes_remaining, sizeof(read_data)); + if (bootloader_flash_read(aligned_addr + offset, read_data, ALIGN_UP(bytes, 4), true) != ESP_OK) { + return false; + } + + memcpy(&((uint8_t *)dest)[bytes_read], read_data, bytes); + + offset += bytes; + bytes_read += bytes; + bytes_remaining -= bytes; + } + + return true; +} + +int flash_area_read(const struct flash_area *fa, uint32_t off, void *dst, + uint32_t len) +{ + if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { + return -1; + } + + const uint32_t end_offset = off + len; + if (end_offset > fa->fa_size) { + BOOT_LOG_ERR("%s: Out of Bounds (0x%x vs 0x%x)", __func__, end_offset, fa->fa_size); + return -1; + } + + bool success = aligned_flash_read(fa->fa_off + off, dst, len); + if (!success) { + BOOT_LOG_ERR("%s: Flash read failed", __func__); + + return -1; + } + + return 0; +} + +static bool aligned_flash_write(size_t dest_addr, const void *src, size_t size) +{ +#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED + bool flash_encryption_enabled = esp_flash_encryption_enabled(); +#else + bool flash_encryption_enabled = false; +#endif + + if (IS_ALIGNED(dest_addr, 4) && IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED(size, 4)) { + /* A single write operation is enough when all parameters are aligned */ + + return bootloader_flash_write(dest_addr, (void *)src, size, flash_encryption_enabled) == ESP_OK; + } + + const uint32_t aligned_addr = ALIGN_DOWN(dest_addr, 4); + const uint32_t addr_offset = ALIGN_OFFSET(dest_addr, 4); + uint32_t bytes_remaining = size; + uint8_t write_data[FLASH_BUFFER_SIZE] = {0}; + + /* Perform a read operation considering an offset not aligned to 4-byte boundary */ + + uint32_t bytes = MIN(bytes_remaining + addr_offset, sizeof(write_data)); + if (bootloader_flash_read(aligned_addr, write_data, ALIGN_UP(bytes, 4), true) != ESP_OK) { + return false; + } + + uint32_t bytes_written = bytes - addr_offset; + memcpy(&write_data[addr_offset], src, bytes_written); + + if (bootloader_flash_write(aligned_addr, write_data, ALIGN_UP(bytes, 4), flash_encryption_enabled) != ESP_OK) { + return false; + } + + bytes_remaining -= bytes_written; + + /* Write remaining data to Flash if any */ + + uint32_t offset = bytes; + + while (bytes_remaining != 0) { + bytes = MIN(bytes_remaining, sizeof(write_data)); + if (bootloader_flash_read(aligned_addr + offset, write_data, ALIGN_UP(bytes, 4), true) != ESP_OK) { + return false; + } + + memcpy(write_data, &((uint8_t *)src)[bytes_written], bytes); + + if (bootloader_flash_write(aligned_addr + offset, write_data, ALIGN_UP(bytes, 4), flash_encryption_enabled) != ESP_OK) { + return false; + } + + offset += bytes; + bytes_written += bytes; + bytes_remaining -= bytes; + } + + return true; +} + +int flash_area_write(const struct flash_area *fa, uint32_t off, const void *src, + uint32_t len) +{ + if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { + return -1; + } + + const uint32_t end_offset = off + len; + if (end_offset > fa->fa_size) { + BOOT_LOG_ERR("%s: Out of Bounds (0x%x vs 0x%x)", __func__, end_offset, fa->fa_size); + return -1; + } + + const uint32_t start_addr = fa->fa_off + off; + BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len); + + bool success = aligned_flash_write(start_addr, src, len); + if (!success) { + BOOT_LOG_ERR("%s: Flash write failed", __func__); + return -1; + } + + return 0; +} + +int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len) +{ + if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { + return -1; + } + + if ((len % FLASH_SECTOR_SIZE) != 0 || (off % FLASH_SECTOR_SIZE) != 0) { + BOOT_LOG_ERR("%s: Not aligned on sector Offset: 0x%x Length: 0x%x", + __func__, (int)off, (int)len); + return -1; + } + + const uint32_t start_addr = fa->fa_off + off; + BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len); + + if (bootloader_flash_erase_range(start_addr, len) != ESP_OK) { + BOOT_LOG_ERR("%s: Flash erase failed", __func__); + return -1; + } +#if VALIDATE_PROGRAM_OP + for (size_t i = 0; i < len; i++) { + uint8_t *val = (void *)(start_addr + i); + if (*val != 0xff) { + BOOT_LOG_ERR("%s: Erase at 0x%x Failed", __func__, (int)val); + assert(0); + } + } +#endif + + return 0; +} + +uint32_t flash_area_align(const struct flash_area *area) +{ + static size_t align = 0; + + if (align == 0) { +#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED + bool flash_encryption_enabled = esp_flash_encryption_enabled(); +#else + bool flash_encryption_enabled = false; +#endif + + if (flash_encryption_enabled) { + align = 32; + } else { + align = 4; + } + } + return align; +} + +uint8_t flash_area_erased_val(const struct flash_area *area) +{ + return 0xff; +} + +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors) +{ + const struct flash_area *fa = prv_lookup_flash_area(fa_id); + if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { + return -1; + } + + const size_t sector_size = FLASH_SECTOR_SIZE; + uint32_t total_count = 0; + for (size_t off = 0; off < fa->fa_size; off += sector_size) { + // Note: Offset here is relative to flash area, not device + sectors[total_count].fs_off = off; + sectors[total_count].fs_size = sector_size; + total_count++; + } + + *count = total_count; + return 0; +} + +int flash_area_sector_from_off(uint32_t off, struct flash_sector *sector) +{ + sector->fs_off = (off / FLASH_SECTOR_SIZE) * FLASH_SECTOR_SIZE; + sector->fs_size = FLASH_SECTOR_SIZE; + + return 0; +} + +int flash_area_get_sector(const struct flash_area *fa, uint32_t off, + struct flash_sector *sector) +{ + sector->fs_off = (off / FLASH_SECTOR_SIZE) * FLASH_SECTOR_SIZE; + sector->fs_size = FLASH_SECTOR_SIZE; + + return 0; +} + +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + BOOT_LOG_DBG("%s", __func__); + switch (slot) { + case 0: + return FLASH_AREA_IMAGE_PRIMARY(image_index); + case 1: + return FLASH_AREA_IMAGE_SECONDARY(image_index); + } + + BOOT_LOG_ERR("Unexpected Request: image_index=%d, slot=%d", image_index, slot); + return -1; /* flash_area_open will fail on that */ +} + +int flash_area_id_from_image_slot(int slot) +{ + return flash_area_id_from_multi_image_slot(0, slot); +} + +int flash_area_to_sectors(int idx, int *cnt, struct flash_area *fa) +{ + return -1; +} + +void mcuboot_assert_handler(const char *file, int line, const char *func) +{ + ets_printf("assertion failed: file \"%s\", line %d, func: %s\n", file, line, func); + abort(); +} diff --git a/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32.cmake b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32.cmake new file mode 100644 index 0000000..88f6ea3 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER xtensa-esp32-elf-gcc) +set(CMAKE_CXX_COMPILER xtensa-esp32-elf-g++) +set(CMAKE_ASM_COMPILER xtensa-esp32-elf-gcc) +set(_CMAKE_TOOLCHAIN_PREFIX xtensa-esp32-elf-) + +set(CMAKE_C_FLAGS "${UNIQ_CMAKE_C_FLAGS}" CACHE STRING "C Compiler Base Flags" FORCE) +set(CMAKE_CXX_FLAGS "${UNIQ_CMAKE_CXX_FLAGS}" CACHE STRING "C++ Compiler Base Flags" FORCE) +set(CMAKE_ASM_FLAGS "${UNIQ_CMAKE_ASM_FLAGS}" CACHE STRING "ASM Compiler Base Flags" FORCE) + +set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections" CACHE STRING "Linker Base Flags") diff --git a/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c2.cmake b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c2.cmake new file mode 100644 index 0000000..0982a85 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c2.cmake @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER riscv32-esp-elf-gcc) +set(CMAKE_CXX_COMPILER riscv32-esp-elf-g++) +set(CMAKE_ASM_COMPILER riscv32-esp-elf-gcc) + +set(CMAKE_C_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C++ Compiler Base Flags") +set(CMAKE_EXE_LINKER_FLAGS "-nostartfiles -march=rv32imc_zicsr_zifencei --specs=nosys.specs" CACHE STRING "Linker Base Flags") diff --git a/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c3.cmake b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c3.cmake new file mode 100644 index 0000000..5493620 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c3.cmake @@ -0,0 +1,9 @@ +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER riscv32-esp-elf-gcc) +set(CMAKE_CXX_COMPILER riscv32-esp-elf-g++) +set(CMAKE_ASM_COMPILER riscv32-esp-elf-gcc) + +set(CMAKE_C_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C++ Compiler Base Flags") +set(CMAKE_EXE_LINKER_FLAGS "-nostartfiles -march=rv32imc_zicsr_zifencei --specs=nosys.specs" CACHE STRING "Linker Base Flags") diff --git a/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c6.cmake b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c6.cmake new file mode 100644 index 0000000..0982a85 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32c6.cmake @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER riscv32-esp-elf-gcc) +set(CMAKE_CXX_COMPILER riscv32-esp-elf-g++) +set(CMAKE_ASM_COMPILER riscv32-esp-elf-gcc) + +set(CMAKE_C_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C++ Compiler Base Flags") +set(CMAKE_EXE_LINKER_FLAGS "-nostartfiles -march=rv32imc_zicsr_zifencei --specs=nosys.specs" CACHE STRING "Linker Base Flags") diff --git a/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32h2.cmake b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32h2.cmake new file mode 100644 index 0000000..0982a85 --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32h2.cmake @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER riscv32-esp-elf-gcc) +set(CMAKE_CXX_COMPILER riscv32-esp-elf-g++) +set(CMAKE_ASM_COMPILER riscv32-esp-elf-gcc) + +set(CMAKE_C_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "-march=rv32imc_zicsr_zifencei" CACHE STRING "C++ Compiler Base Flags") +set(CMAKE_EXE_LINKER_FLAGS "-nostartfiles -march=rv32imc_zicsr_zifencei --specs=nosys.specs" CACHE STRING "Linker Base Flags") diff --git a/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s2.cmake b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s2.cmake new file mode 100644 index 0000000..77ea65c --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s2.cmake @@ -0,0 +1,10 @@ +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER xtensa-esp32s2-elf-gcc) +set(CMAKE_CXX_COMPILER xtensa-esp32s2-elf-g++) +set(CMAKE_ASM_COMPILER xtensa-esp32s2-elf-gcc) + +set(CMAKE_C_FLAGS "-mlongcalls" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "-mlongcalls" CACHE STRING "C++ Compiler Base Flags") + +set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections" CACHE STRING "Linker Base Flags") diff --git a/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s3.cmake b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s3.cmake new file mode 100644 index 0000000..de3e3da --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/toolchain-esp32s3.cmake @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +set(CMAKE_SYSTEM_NAME Generic) + +set(CMAKE_C_COMPILER xtensa-esp32s3-elf-gcc) +set(CMAKE_CXX_COMPILER xtensa-esp32s3-elf-g++) +set(CMAKE_ASM_COMPILER xtensa-esp32s3-elf-gcc) +set(_CMAKE_TOOLCHAIN_PREFIX xtensa-esp32s3-elf-) + +set(CMAKE_C_FLAGS "-mlongcalls" CACHE STRING "C Compiler Base Flags" FORCE) +set(CMAKE_CXX_FLAGS "-mlongcalls" CACHE STRING "C++ Compiler Base Flags" FORCE) +set(CMAKE_ASM_FLAGS "${UNIQ_CMAKE_ASM_FLAGS}" CACHE STRING "ASM Compiler Base Flags" FORCE) + +set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections" CACHE STRING "Linker Base Flags") diff --git a/bootloader/mcuboot/boot/espressif/tools/utils.cmake b/bootloader/mcuboot/boot/espressif/tools/utils.cmake new file mode 100644 index 0000000..238b30e --- /dev/null +++ b/bootloader/mcuboot/boot/espressif/tools/utils.cmake @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +# Parse config files (.conf, format: =) and set each as +# definitions and variables +function(parse_and_set_config_file CONFIG_FILE) + file(STRINGS ${CONFIG_FILE} BOOTLOADER_CONF) + foreach(config ${BOOTLOADER_CONF}) + if (NOT (${config} MATCHES "#")) + string(REGEX REPLACE "^[ ]+" "" config ${config}) + string(REGEX MATCH "^[^=]+" CONFIG_NAME ${config}) + string(REPLACE "${CONFIG_NAME}=" "" CONFIG_VALUE ${config}) + # Overrides if already defined (definitions from latter file prevails over the former) + if (DEFINED ${CONFIG_NAME}) + set(CONFIG_OLD "${CONFIG_NAME}") + remove_definitions(-D${CONFIG_NAME}=${${CONFIG_OLD}}) + endif() + if (NOT ("${CONFIG_VALUE}" STREQUAL "n" + OR "${CONFIG_VALUE}" STREQUAL "N")) + + if (("${CONFIG_VALUE}" STREQUAL "y") + OR ("${CONFIG_VALUE}" STREQUAL "Y")) + set(CONFIG_VALUE 1) + endif() + add_compile_definitions(${CONFIG_NAME}=${CONFIG_VALUE}) + set(${CONFIG_NAME} ${CONFIG_VALUE} PARENT_SCOPE) + endif() + endif() + endforeach() +endfunction() diff --git a/bootloader/mcuboot/boot/mbed/CMakeLists.txt b/bootloader/mcuboot/boot/mbed/CMakeLists.txt new file mode 100644 index 0000000..8baa262 --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright (c) 2021 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Mbed-MCUboot Port + +cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR) + +get_filename_component(BOOT_UTIL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../bootutil REALPATH) + +set(LIB_TARGET mbed-mcuboot) +set(LIB_BOOTUTIL bootutil) + +add_library(${LIB_TARGET} STATIC) + +target_include_directories(${LIB_TARGET} + PUBLIC + include + ${BOOT_UTIL_DIR}/include + ${BOOT_UTIL_DIR}/src +) + +target_sources(${LIB_TARGET} + PRIVATE + mcuboot_main.cpp + app_enc_keys.c + src/flash_map_backend.cpp + src/secondary_bd.cpp +) + +target_link_libraries(${LIB_TARGET} + PUBLIC + bootutil # Cross-dependency + mbed-mbedtls + mbed-storage-flashiap + mbed-storage-blockdevice +) + +if("_RTE_" IN_LIST MBED_CONFIG_DEFINITIONS) + target_link_libraries(${LIB_TARGET} + PUBLIC + mbed-os + ) +else() + target_link_libraries(${LIB_TARGET} + PUBLIC + mbed-baremetal + ) +endif() + +# The cross-dependency requires that bootutil have access to the mbed port's +# include directory and is linked with the appropriate mbed-specific libraries. +target_include_directories(${LIB_BOOTUTIL} + PUBLIC + include +) + +target_link_libraries(${LIB_BOOTUTIL} + PUBLIC + mbed-mcuboot + mbed-mbedtls +) diff --git a/bootloader/mcuboot/boot/mbed/app_enc_keys.c b/bootloader/mcuboot/boot/mbed/app_enc_keys.c new file mode 100644 index 0000000..9bed4d8 --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/app_enc_keys.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include +#include + +#if defined(MCUBOOT_SIGN_RSA) +#define HAVE_KEYS +extern const unsigned char rsa_pub_key[]; +extern unsigned int rsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_EC256) +#define HAVE_KEYS +extern const unsigned char ecdsa_pub_key[]; +extern unsigned int ecdsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_ED25519) +#define HAVE_KEYS +extern const unsigned char ed25519_pub_key[]; +extern unsigned int ed25519_pub_key_len; +#endif + +/* + * Note: Keys for both signing and encryption must be provided by the application. + * mcuboot's imgtool utility can be used to generate these keys and convert them into compatible C code. + * See imgtool's documentation, specifically the section: "Incorporating the public key into the code" which can be found here: + * https://github.com/JuulLabs-OSS/mcuboot/blob/master/docs/imgtool.md#incorporating-the-public-key-into-the-code + */ +#if defined(HAVE_KEYS) +const struct bootutil_key bootutil_keys[] = { + { +#if defined(MCUBOOT_SIGN_RSA) + .key = rsa_pub_key, + .len = &rsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_EC256) + .key = ecdsa_pub_key, + .len = &ecdsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_ED25519) + .key = ed25519_pub_key, + .len = &ed25519_pub_key_len, +#endif + }, +}; +const int bootutil_key_cnt = 1; + +#if defined(MCUBOOT_ENCRYPT_RSA) + +extern const unsigned char enc_priv_key[]; +extern const unsigned int enc_priv_key_len; + +const struct bootutil_key bootutil_enc_key = { + .key = enc_priv_key, + .len = &enc_priv_key_len, +}; +#elif defined(MCUBOOT_ENCRYPT_KW) +#error "Encrypted images with AES-KW is not implemented yet." +#endif + +#endif diff --git a/bootloader/mcuboot/boot/mbed/include/flash_map_backend/flash_map_backend.h b/bootloader/mcuboot/boot/mbed/include/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000..d526c5c --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/flash_map_backend/flash_map_backend.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens + */ + +#ifndef H_UTIL_FLASH_MAP_ +#define H_UTIL_FLASH_MAP_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * Provides abstraction of flash regions for type of use. + * I.e. dude where's my image? + * + * System will contain a map which contains flash areas. Every + * region will contain flash identifier, offset within flash and length. + * + * 1. This system map could be in a file within filesystem (Initializer + * must know/figure out where the filesystem is at). + * 2. Map could be at fixed location for project (compiled to code) + * 3. Map could be at specific place in flash (put in place at mfg time). + * + * Note that the map you use must be valid for BSP it's for, + * match the linker scripts when platform executes from flash, + * and match the target offset specified in download script. + */ +#include + +/** + * @brief Structure describing an area on a flash device. + * + * Multiple flash devices may be available in the system, each of + * which may have its own areas. For this reason, flash areas track + * which flash device they are part of. + */ +struct flash_area { + /** + * This flash area's ID; unique in the system. + */ + uint8_t fa_id; + + /** + * ID of the flash device this area is a part of. + */ + uint8_t fa_device_id; + + uint16_t pad16; + + /** + * This area's offset, relative to the beginning of its flash + * device's storage. + */ + uint32_t fa_off; + + /** + * This area's size, in bytes. + */ + uint32_t fa_size; +}; + +static inline uint8_t flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +static inline uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + return fa->fa_device_id; +} + +static inline uint32_t flash_area_get_off(const struct flash_area *fa) +{ + return fa->fa_off; +} + +static inline uint32_t flash_area_get_size(const struct flash_area *fa) +{ + return fa->fa_size; +} + +/** + * @brief Structure describing a sector within a flash area. + * + * Each sector has an offset relative to the start of its flash area + * (NOT relative to the start of its flash device), and a size. A + * flash area may contain sectors with different sizes. + */ +struct flash_sector { + /** + * Offset of this sector, from the start of its flash area (not device). + */ + uint32_t fs_off; + + /** + * Size of this sector, in bytes. + */ + uint32_t fs_size; +}; + +static inline uint32_t flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +static inline uint32_t flash_sector_get_size(const struct flash_sector *fs) +{ + return fs->fs_size; +} + +/* + * Start using flash area. + */ +int flash_area_open(uint8_t id, const struct flash_area ** fapp); + +void flash_area_close(const struct flash_area * fap); + +/* + * Read/write/erase. Offset is relative from beginning of flash area. + */ +int flash_area_read(const struct flash_area * fap, uint32_t off, void *dst, + uint32_t len); +int flash_area_write(const struct flash_area * fap, uint32_t off, const void *src, + uint32_t len); +int flash_area_erase(const struct flash_area * fap, uint32_t off, uint32_t len); + +/* + * Alignment restriction for flash writes. + */ +uint32_t flash_area_align(const struct flash_area * fap); + +/* + * What is value is read from erased flash bytes. + */ +uint8_t flash_area_erased_val(const struct flash_area * fap); + +/* + * Given flash area ID, return info about sectors within the area. + */ +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors); + + +int flash_area_id_from_image_slot(int slot); +int flash_area_id_from_multi_image_slot(int image_index, int slot); + + +/** + * Converts the specified flash area ID and image index (in multi-image setup) + * to an image slot index. + * + * Returns image slot index (0 or 1), or -1 if ID doesn't correspond to an image + * slot. + */ +int flash_area_id_to_multi_image_slot(int image_index, int area_id); + +#ifdef __cplusplus +} +#endif + +#endif /* H_UTIL_FLASH_MAP_ */ diff --git a/bootloader/mcuboot/boot/mbed/include/flash_map_backend/secondary_bd.h b/bootloader/mcuboot/boot/mbed/include/flash_map_backend/secondary_bd.h new file mode 100644 index 0000000..c14d3ce --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/flash_map_backend/secondary_bd.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens + * + * Created on: Jul 30, 2020 + * Author: gdbeckstein + */ + +#ifndef MCUBOOT_BOOT_MBED_INCLUDE_FLASH_MAP_BACKEND_SECONDARY_BD_H_ +#define MCUBOOT_BOOT_MBED_INCLUDE_FLASH_MAP_BACKEND_SECONDARY_BD_H_ + +#include "blockdevice/BlockDevice.h" + +/** + * This is implemented as a weak function and may be redefined + * by the application. The default case is to return the + * BlockDevice object returned by BlockDevice::get_default_instance(); + * + * @retval secondary_bd Secondary BlockDevice where update candidates are stored + */ +mbed::BlockDevice* get_secondary_bd(void); + +#endif /* MCUBOOT_BOOT_MBED_INCLUDE_FLASH_MAP_BACKEND_SECONDARY_BD_H_ */ diff --git a/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_assert.h b/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_assert.h new file mode 100644 index 0000000..8c8d52d --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_assert.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 Open Source Foundries Limited + * + * Copyright (c) 2020 Embedded Planet + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens + */ + +#include "platform/mbed_assert.h" diff --git a/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_config.h b/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000..b57e8d6 --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_config.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018 Open Source Foundries Limited + * Copyright (c) 2019-2020 Arm Limited + * Copyright (c) 2019-2020 Linaro Limited + * Copyright (c) 2020 Embedded Planet + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_CONFIG_H__ +#define __MCUBOOT_CONFIG_H__ + +/* + * For available configurations and their explanations, + * see mbed_lib.json. + */ + +#define SIGNATURE_TYPE_RSA 0 +#define SIGNATURE_TYPE_EC256 1 +#define SIGNATURE_TYPE_ED25519 2 +#define SIGNATURE_TYPE_NONE 3 + +/* + * Signature algorithm + */ +#if (MCUBOOT_SIGNATURE_ALGORITHM == SIGNATURE_TYPE_RSA) +#define MCUBOOT_SIGN_RSA +# if (MCUBOOT_RSA_SIGNATURE_LENGTH != 2048 && \ + MCUBOOT_RSA_SIGNATURE_LENGTH != 3072) +# error "Invalid RSA key size (must be 2048 or 3072)" +# else +# define MCUBOOT_SIGN_RSA_LEN MCUBOOT_RSA_SIGNATURE_LENGTH +# endif +#elif (MCUBOOT_SIGNATURE_ALGORITHM == SIGNATURE_TYPE_EC256) +#define MCUBOOT_SIGN_EC256 +#elif (MCUBOOT_SIGNATURE_ALGORITHM == SIGNATURE_TYPE_ED25519) +#define MCUBOOT_SIGN_ED25519 +#endif + +/* + * Crypto backend + */ +#define MBEDTLS 0 +#define TINYCRYPT 1 + +#if (MCUBOOT_CRYPTO_BACKEND == MBEDTLS) +#define MCUBOOT_USE_MBED_TLS +#elif (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) +/** + * XXX TinyCrypt is currently not supported by Mbed-OS due to build conflicts. + * See https://github.com/AGlass0fMilk/mbed-mcuboot-blinky/issues/2 + */ +#error TinyCrypt is currently not supported by Mbed-OS due to build conflicts. +#define MCUBOOT_USE_TINYCRYPT +#endif + +/* + * Only one image (two slots) supported for now + */ +#define MCUBOOT_IMAGE_NUMBER 1 + +/* + * Currently there is no configuration option, for this platform, + * that enables the system specific mcumgr commands in mcuboot + */ +#define MCUBOOT_PERUSER_MGMT_GROUP_ENABLED 0 + +/* + * Encrypted Images + */ +#if defined(MCUBOOT_ENCRYPT_RSA) || defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) +#define MCUBOOT_ENC_IMAGES +#endif + +/* + * Enabling this option uses newer flash map APIs. This saves RAM and + * avoids deprecated API usage. + */ +#define MCUBOOT_USE_FLASH_AREA_GET_SECTORS + +/* + * No watchdog integration for now + */ +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + } while (0) + +/* + * No direct idle call implemented + */ +#define MCUBOOT_CPU_IDLE() \ + do { \ + } while (0) + +#endif /* __MCUBOOT_CONFIG_H__ */ diff --git a/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_logging.h b/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000..c6a1cf7 --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * Copyright (c) 2020 Cypress Semiconductor Corporation + * Copyright (c) 2020 Embedded Planet + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the Licens. + */ + +#ifndef __MCUBOOT_LOGGING_H__ +#define __MCUBOOT_LOGGING_H__ + +#define MCUBOOT_LOG_LEVEL_OFF 0 +#define MCUBOOT_LOG_LEVEL_ERROR 1 +#define MCUBOOT_LOG_LEVEL_WARNING 2 +#define MCUBOOT_LOG_LEVEL_INFO 3 +#define MCUBOOT_LOG_LEVEL_DEBUG 4 + +/* + * The compiled log level determines the maximum level that can be + * printed. + */ +#ifndef MCUBOOT_LOG_LEVEL +#define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_OFF +#endif + +#if MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_ERROR +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_ERROR +#elif MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_WARNING +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_WARN +#elif MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_INFO +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_INFO +#elif MCUBOOT_LOG_LEVEL == MCUBOOT_LOG_LEVEL_DEBUG +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_DEBUG +#endif + +#define TRACE_GROUP "MCUb" +#include "mbed-trace/mbed_trace.h" +#include "bootutil/ignore.h" + +#define MCUBOOT_LOG_MODULE_DECLARE(domain) /* ignore */ +#define MCUBOOT_LOG_MODULE_REGISTER(domain) /* ignore */ + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_ERROR +#define MCUBOOT_LOG_ERR tr_error +#else +#define MCUBOOT_LOG_ERR(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_WARNING +#define MCUBOOT_LOG_WRN tr_warn +#else +#define MCUBOOT_LOG_WRN(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_INFO +#define MCUBOOT_LOG_INF tr_info +#else +#define MCUBOOT_LOG_INF(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_DEBUG +#define MCUBOOT_LOG_DBG tr_debug +#else +#define MCUBOOT_LOG_DBG(...) IGNORE(__VA_ARGS__) +#endif + +#endif /* __MCUBOOT_LOGGING_H__ */ diff --git a/bootloader/mcuboot/boot/mbed/include/os/os_malloc.h b/bootloader/mcuboot/boot/mbed/include/os/os_malloc.h new file mode 100644 index 0000000..3aa6f1e --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/os/os_malloc.h @@ -0,0 +1 @@ +/** Not required for Mbed -- malloc calls are retargeted by the platform */ diff --git a/bootloader/mcuboot/boot/mbed/include/sysflash/sysflash.h b/bootloader/mcuboot/boot/mbed/include/sysflash/sysflash.h new file mode 100644 index 0000000..e19945b --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/sysflash/sysflash.h @@ -0,0 +1,14 @@ +/* Manual version of auto-generated version. */ + +#ifndef __SYSFLASH_H__ +#define __SYSFLASH_H__ + +#define PRIMARY_ID 0 +#define SECONDARY_ID 1 +#define SCRATCH_ID 2 + +#define FLASH_AREA_IMAGE_PRIMARY(x) PRIMARY_ID +#define FLASH_AREA_IMAGE_SECONDARY(x) SECONDARY_ID +#define FLASH_AREA_IMAGE_SCRATCH SCRATCH_ID + +#endif /* __SYSFLASH_H__ */ diff --git a/bootloader/mcuboot/boot/mbed/include/utils/DataShare.cpp b/bootloader/mcuboot/boot/mbed/include/utils/DataShare.cpp new file mode 100644 index 0000000..a7d012b --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/utils/DataShare.cpp @@ -0,0 +1,97 @@ +/** + * + * Built with ARM Mbed-OS + * + * Copyright (c) 2019-2021 Embedded Planet, Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if MCUBOOT_DATA_SHARING && !MCUBOOT_BOOTLOADER_BUILD + +#include "DataShare.h" +#include "boot_status.h" + +#include + +#define PTR_TO_UINT8(x) ((volatile uint8_t *) x) +#define PTR_TO_UINT16(x) ((volatile uint16_t *) x) +#define PTR_TO_UINT32(x) ((volatile uint32_t *) x) + +#define SHARED_DATA_ENTRY_BASE MCUBOOT_SHARED_DATA_BASE+SHARED_DATA_HEADER_SIZE + +DataShare::DataShare(uint8_t *shared_base) : _shared_base(shared_base) { + volatile uint16_t *ptr = PTR_TO_UINT16(MCUBOOT_SHARED_DATA_BASE); + + /* Validate magic word */ + if(*ptr++ == SHARED_DATA_TLV_INFO_MAGIC) { + _is_valid = true; + } + + _total_size = *ptr; + +} + +bool DataShare::is_valid() { + return _is_valid; +} + +uint16_t DataShare::get_total_size() { + if(!is_valid()) { + return 0; + } + + return _total_size; +} + +int DataShare::get_next(uint16_t *type, mbed::Span buf, uint16_t *actual_size) { + + if(!is_valid()) { + return DATA_SHARE_ERROR_CORRUPT; + } + + if(_current_offset >= (_total_size - SHARED_DATA_HEADER_SIZE)) { + return DATA_SHARE_ERROR_EOF; + } + + uint16_t working_offset = _current_offset; + + /* Get the type and the length */ + *type = *PTR_TO_UINT16((SHARED_DATA_ENTRY_BASE + working_offset)); + working_offset += sizeof(uint16_t); + *actual_size = *PTR_TO_UINT16((SHARED_DATA_ENTRY_BASE + working_offset)); + working_offset += sizeof(uint16_t); + + /* Check if the output buffer is large enough */ + if((size_t)buf.size() < *actual_size) { + return DATA_SHARE_ERROR_OUT_OF_MEM; + } + + /* Copy data of TLV entry */ + memcpy(buf.data(), (const uint8_t*)PTR_TO_UINT8((SHARED_DATA_ENTRY_BASE + working_offset)), *actual_size); + + working_offset += *actual_size; + + /* Update state */ + _current_offset = working_offset; + + return DATA_SHARE_OK; + +} + +void DataShare::rewind() { + _current_offset = 0; +} + +#endif /* MCUBOOT_DATA_SHARING && !MCUBOOT_BOOTLOADER_BUILD */ diff --git a/bootloader/mcuboot/boot/mbed/include/utils/DataShare.h b/bootloader/mcuboot/boot/mbed/include/utils/DataShare.h new file mode 100644 index 0000000..fbb7e8e --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/include/utils/DataShare.h @@ -0,0 +1,92 @@ +/** + * + * Built with ARM Mbed-OS + * + * Copyright (c) 2019-2021 Embedded Planet, Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef DATASHARE_H_ +#define DATASHARE_H_ + +/** + * This is an utility intended to be used by the booted application rather + * than by the bootloader + */ +#if MCUBOOT_DATA_SHARING && !MCUBOOT_BOOTLOADER_BUILD + +#include +#include "platform/Span.h" + +/* Error codes */ +#define DATA_SHARE_OK 0 +#define DATA_SHARE_ERROR_EOF 1 /* No more TLV entries available */ +#define DATA_SHARE_ERROR_OUT_OF_MEM 2 /* The output buffer was not large enough to hold the contents of the TLV entry */ +#define DATA_SHARE_ERROR_CORRUPT 3 /* Data corruption has been detected */ + +/** + * Class enabling iterator-style access to the TLV-encoded data shared + * by an mcuboot-based bootloader. + */ +class DataShare +{ +public: + + /** + * Initializes a DataShare iterator at the given base address + * @note the configured MCUBOOT_SHARED_DATA_BASE address is used by default + */ + DataShare(uint8_t * shared_base = ((uint8_t *) MCUBOOT_SHARED_DATA_BASE)); + + /** + * Validates the magic number of the shared data section + * @return true if magic number is found, false otherwise + */ + bool is_valid(); + + /** + * Gets the total size of the shared data region + * @return 0 if shared data region is not valid, otherwise the size of the shared data region + */ + uint16_t get_total_size(); + + /** + * Attempts to get the next TLV entry in the shared data memory + * @param[put] type Type code of the data entry + * @param[out] out Output buffer span + * @param[out] actual_size Size of entry copied to output buffer in bytes + * @return err Zero if output buffer contents is valid (successful), non-zero on failure + */ + int get_next(uint16_t *type, mbed::Span buf, uint16_t *actual_size); + + /** + * Resets the iterator-like pointer to the first TLV element in the shared + * data region + */ + void rewind(); + +protected: + + uint8_t *_shared_base; + bool _is_valid = false; + uint16_t _total_size = 0; + uint16_t _current_offset = 0; + +}; + +#endif /* MCUBOOT_DATA_SHARING && !MCUBOOT_BOOTLOADER_BUILD */ + +#endif /* DATASHARE_H_ */ diff --git a/bootloader/mcuboot/boot/mbed/mbed_lib.json b/bootloader/mcuboot/boot/mbed/mbed_lib.json new file mode 100644 index 0000000..f5b7b67 --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/mbed_lib.json @@ -0,0 +1,194 @@ +{ + "name": "mcuboot", + "config": { + "bootloader-build": { + "help": "Build the bootloader, in addition to the MCUboot library.", + "macro_name": "MCUBOOT_BOOTLOADER_BUILD", + "accepted_values": [true, false], + "value": true + }, + "primary-slot-address": { + "help": "Start address of the primary (bootable) image slot. Target-dependent, please set on a per-target basis.", + "macro_name": "MCUBOOT_PRIMARY_SLOT_START_ADDR", + "required": true + }, + "slot-size": { + "help": "Size of the primary (bootable) image slot, in bytes. Target-dependent, please set on a per-target basis.", + "macro_name": "MCUBOOT_SLOT_SIZE", + "required": true + }, + "header-size": { + "help": "Size of the header info section, in bytes. Target-dependent, please set on a per-target basis.", + "macro_name": "MCUBOOT_HEADER_SIZE", + "value": "0x1000" + }, + "scratch-address": { + "help": "Start address of the scratch area. If needed, please set on a per-target basis.", + "macro_name": "MCUBOOT_SCRATCH_START_ADDR" + }, + "scratch-size": { + "help": "Size of the scratch area, in bytes. If needed, please set on a per-target basis.", + "macro_name": "MCUBOOT_SCRATCH_SIZE" + }, + "validate-primary-slot": { + "help": "Always check the signature of the image in the primary slot before booting, even if no upgrade was performed. This is recommended if the boot time penalty is acceptable.", + "macro_name": "MCUBOOT_VALIDATE_PRIMARY_SLOT", + "accepted_values": [true, null], + "value": true + }, + "signature-algorithm": { + "help": "The algorithm used for digital signing.", + "macro_name": "MCUBOOT_SIGNATURE_ALGORITHM", + "required": true, + "accepted_values": ["SIGNATURE_TYPE_RSA", "SIGNATURE_TYPE_EC256", "SIGNATURE_TYPE_ED25519", "SIGNATURE_TYPE_NONE"], + "value": "SIGNATURE_TYPE_RSA" + }, + "rsa-signature-length": { + "help": "If RSA is used for signature algorithm, this specifies the length.", + "macro_name": "MCUBOOT_RSA_SIGNATURE_LENGTH", + "required": true, + "accepted_values": [2048, 3072], + "value": 2048 + }, + "crypto-backend": { + "help": "The crypto library backend. NOTE: TinyCrypt is currently only supported with GCC for Mbed-OS builds.", + "macro_name": "MCUBOOT_CRYPTO_BACKEND", + "required": true, + "accepted_values": ["MBEDTLS", "TINYCRYPT"], + "value": "MBEDTLS" + }, + "overwrite-only": { + "help": "The default is to support A/B image swapping with rollback. A simpler code path, which only supports overwriting the existing image with the update image, is also available. (null to disable)", + "macro_name": "MCUBOOT_OVERWRITE_ONLY", + "accepted_values": [true, null], + "value": null + }, + "overwrite-only-fast": { + "help": "Only erase and overwrite those primary slot sectors needed to install the new image, rather than the entire image slot.", + "macro_name": "MCUBOOT_OVERWRITE_ONLY_FAST", + "accepted_values": [true, null], + "value": null + }, + "log-enable": { + "help": "Enable MCUboot logging. Must also enable mbed-trace", + "macro_name": "MCUBOOT_HAVE_LOGGING", + "accepted_values": [true, null], + "value": null + }, + "log-level": { + "help": "Verbosity of MCUboot logging.", + "macro_name": "MCUBOOT_LOG_LEVEL", + "accepted_values": ["MCUBOOT_LOG_LEVEL_OFF", "MCUBOOT_LOG_LEVEL_ERROR", "MCUBOOT_LOG_LEVEL_WARN", "MCUBOOT_LOG_LEVEL_INFO", "MCUBOOT_LOG_LEVEL_DEBUG"], + "value": "MCUBOOT_LOG_LEVEL_OFF" + }, + "log-bootloader-only": { + "help": "Exclude non-bootloader logs from Mbed OS (e.g. underlying storage).", + "macro_name": "MCUBOOT_LOG_BOOTLOADER_ONLY", + "accepted_values": [true, false], + "value": true + }, + "max-img-sectors": { + "help": "Maximum number of flash sectors per image slot. Target-dependent, please set on a per-target basis.", + "macro_name": "MCUBOOT_MAX_IMG_SECTORS", + "required": true + }, + "read-granularity": { + "help": "Granularity of read operations, in bytes. Enables a workaround if your block device does not support reading a single byte at a time. If this is used, it should be at least the value of your specific ->get_read_size() result.", + "macro_name": "MCUBOOT_READ_GRANULARITY", + "value": null + }, + "hardware-key": { + "help": "Use hardware key (NOT TESTED)", + "macro_name": "MCUBOOT_HW_KEY", + "accepted_values": [true, null], + "value": null + }, + "boot-swap-move": { + "help": "Boot swap using move (NOT TESTED)", + "macro_name": "MCUBOOT_SWAP_USING_MOVE", + "accepted_values": [true, null], + "value": null + }, + "updateable-image-number": { + "help": "Updateable image number (NOT TESTED)", + "macro_name": "MCUBOOT_IMAGE_NUMBER" + }, + "MCUBOOT_SWAP_SAVE_ENCTLV": { + "help": "Swap save enctlv (NOT TESTED)", + "macro_name": "MCUBOOT_IMAGE_NUMBER", + "value": null + }, + "encrypt-rsa": { + "help": "Encrypt images using RSA (NOT TESTED)", + "macro_name": "MCUBOOT_ENCRYPT_RSA", + "accepted_values": [true, null], + "value": null + }, + "encrypt-ec256": { + "help": "Encrypt images using EC256 (NOT TESTED)", + "macro_name": "MCUBOOT_ENCRYPT_EC256", + "accepted_values": [true, null], + "value": null + }, + "encrypt-x25519": { + "help": "Encrypt images using X25519 (NOT TESTED)", + "macro_name": "MCUBOOT_ENCRYPT_X25519", + "accepted_values": [true, null], + "value": null + }, + "bootstrap": { + "help": "Bootstrap (NOT TESTED)", + "macro_name": "MCUBOOT_BOOTSTRAP", + "value": null + }, + "use-bench": { + "help": "Use bench (NOT TESTED)", + "macro_name": "MCUBOOT_USE_BENCH", + "value": null + }, + "downgrade-prevention": { + "help": "Prevent downgrades (NOT TESTED)", + "macro_name": "MCUBOOT_DOWNGRADE_PREVENTION", + "value": null + }, + "hw-rollback-protection": { + "help": "Hardware rollback protection (NOT TESTED)", + "macro_name": "MCUBOOT_HW_ROLLBACK_PROT", + "value": null + }, + "measured-boot": { + "help": "Measured boot (NOT TESTED)", + "macro_name": "MCUBOOT_MEASURED_BOOT", + "value": null + }, + "share-data": { + "help": "Share data (NOT TESTED)", + "macro_name": "MCUBOOT_DATA_SHARING", + "value": null + }, + "share-data-base-address": { + "help": "Start of reserved RAM region for data shared between bootloader and application", + "macro_name": "MCUBOOT_SHARED_DATA_BASE", + "value": null + }, + "share-data-size": { + "help": "Size of reserved RAM region for data shared between bootloader and application", + "macro_name": "MCUBOOT_SHARED_DATA_SIZE", + "value": null + }, + "direct-xip": { + "help": "Enable ability to boot update candidates in-place.", + "macro_name": "MCUBOOT_DIRECT_XIP", + "value": null + }, + "direct-xip-revert": { + "help": "Enable XIP revert mechanism. Only valid if direct-xip is also enabled.", + "macro_name": "MCUBOOT_DIRECT_XIP_REVERT", + "value": null + }, + "xip-secondary-slot-address": { + "help": "Specify start address for secondary slot address in XIP-accessible memory. This is required if direct-xip is enabled.", + "value": null + } + } +} diff --git a/bootloader/mcuboot/boot/mbed/mcuboot_main.cpp b/bootloader/mcuboot/boot/mbed/mcuboot_main.cpp new file mode 100644 index 0000000..bf95e3a --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/mcuboot_main.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020 Embedded Planet + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#if MCUBOOT_BOOTLOADER_BUILD + +#include +#include "bootutil/bootutil.h" +#include "bootutil/image.h" +#include "hal/serial_api.h" +#include "platform/mbed_application.h" + +#if (MCUBOOT_CRYPTO_BACKEND == MBEDTLS) +#include "mbedtls/platform.h" +#elif (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) +#include "tinycrypt/ecc.h" +#endif + +#define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_DEBUG +#define TRACE_GROUP "BL" +#include "mbed-trace/mbed_trace.h" + +#if (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) +/* XXX add this global definition for linking only + * TinyCrypt is used for signature verification and ECIES using secp256r1 and AES encryption; + * RNG is not required. So here we provide a stub. + * See https://github.com/mcu-tools/mcuboot/pull/791#discussion_r514480098 + */ + +extern "C" { +int default_CSPRNG(uint8_t *dest, unsigned int size) { return 0; } +} +#endif + +int main() +{ + int rc; + +#ifdef MCUBOOT_HAVE_LOGGING + mbed_trace_init(); +#if MCUBOOT_LOG_BOOTLOADER_ONLY + mbed_trace_include_filters_set("MCUb,BL"); +#endif //MCUBOOT_LOG_BOOTLOADER_ONLY +#endif //MCUBOOT_HAVE_LOGGING + + tr_info("Starting MCUboot"); + +#if (MCUBOOT_CRYPTO_BACKEND == MBEDTLS) + // Initialize mbedtls crypto for use by MCUboot + mbedtls_platform_context unused_ctx; + rc = mbedtls_platform_setup(&unused_ctx); + if(rc != 0) { + tr_error("Failed to setup Mbed TLS, error: %d", rc); + exit(rc); + } +#elif (MCUBOOT_CRYPTO_BACKEND == TINYCRYPT) + uECC_set_rng(0); +#endif + + struct boot_rsp rsp; + rc = boot_go(&rsp); + if(rc != 0) { + tr_error("Failed to locate firmware image, error: %d", rc); + exit(rc); + } + + uint32_t address = rsp.br_image_off + rsp.br_hdr->ih_hdr_size; + + // Workaround: The extra \n ensures the last trace gets flushed + // before mbed_start_application() destroys the stack and jumps + // to the application + tr_info("Booting firmware image at 0x%x\n", address); + + // Run the application in the primary slot + // Add header size offset to calculate the actual start address of application + mbed_start_application(address); +} + +#endif // MCUBOOT_BOOTLOADER_BUILD diff --git a/bootloader/mcuboot/boot/mbed/src/flash_map_backend.cpp b/bootloader/mcuboot/boot/mbed/src/flash_map_backend.cpp new file mode 100644 index 0000000..955ac42 --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/src/flash_map_backend.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2020 Embedded Planet + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include +#include +#include "flash_map_backend/flash_map_backend.h" +#include "flash_map_backend/secondary_bd.h" +#include "sysflash/sysflash.h" + +#include "blockdevice/BlockDevice.h" +#include "FlashIAP/FlashIAPBlockDevice.h" + +#include "mcuboot_config/mcuboot_logging.h" + +#include "bootutil_priv.h" + +#define FLASH_DEVICE_INTERNAL_FLASH 0 +#define FLASH_AREAS 3 + +/** Application defined secondary block device */ +mbed::BlockDevice* mcuboot_secondary_bd = get_secondary_bd(); + +/** Internal application block device */ +static FlashIAPBlockDevice mcuboot_primary_bd(MCUBOOT_PRIMARY_SLOT_START_ADDR, MCUBOOT_SLOT_SIZE); + +#if MCUBOOT_SWAP_USING_SCRATCH +/** Scratch space is at the end of internal flash, after the main application */ +static FlashIAPBlockDevice mcuboot_scratch_bd(MCUBOOT_SCRATCH_START_ADDR, MCUBOOT_SCRATCH_SIZE); +#endif + +static mbed::BlockDevice* flash_map_bd[FLASH_AREAS] = { + (mbed::BlockDevice*) &mcuboot_primary_bd, /** Primary (loadable) image area */ + mcuboot_secondary_bd, /** Secondary (update candidate) image area */ +#if MCUBOOT_SWAP_USING_SCRATCH + (mbed::BlockDevice*) &mcuboot_scratch_bd /** Scratch space for swapping images */ +#else + nullptr +#endif +}; + +static struct flash_area flash_areas[FLASH_AREAS]; + +static unsigned int open_count[FLASH_AREAS] = {0}; + +int flash_area_open(uint8_t id, const struct flash_area** fapp) { + + *fapp = &flash_areas[id]; + struct flash_area* fap = (struct flash_area*)*fapp; + + // The offset of the slot is from the beginning of the flash device. + switch (id) { + case PRIMARY_ID: + fap->fa_off = MCUBOOT_PRIMARY_SLOT_START_ADDR; + break; + case SECONDARY_ID: +#if MCUBOOT_DIRECT_XIP + fap->fa_off = MBED_CONF_MCUBOOT_XIP_SECONDARY_SLOT_ADDRESS; +#else + fap->fa_off = 0; +#endif + break; +#if MCUBOOT_SWAP_USING_SCRATCH + case SCRATCH_ID: + fap->fa_off = MCUBOOT_SCRATCH_START_ADDR; + break; +#endif + default: + MCUBOOT_LOG_ERR("flash_area_open, unknown id %d", id); + return -1; + } + + open_count[id]++; + MCUBOOT_LOG_DBG("flash area %d open count: %d (+)", id, open_count[id]); + + fap->fa_id = id; + fap->fa_device_id = 0; // not relevant + + mbed::BlockDevice* bd = flash_map_bd[id]; + + /* Only initialize if this isn't a nested call to open the flash area */ + if (open_count[id] == 1) { + MCUBOOT_LOG_DBG("initializing flash area %d...", id); + int ret = bd->init(); + if (ret) + { + MCUBOOT_LOG_ERR("initializing flash area failed %d", ret); + return ret; + } + } else { + return 0; + } + + fap->fa_size = (uint32_t) bd->size(); + return 0; +} + +void flash_area_close(const struct flash_area* fap) { + uint8_t id = fap->fa_id; + /* No need to close an unopened flash area, avoid an overflow of the counter */ + if (!open_count[id]) { + return; + } + + open_count[id]--; + MCUBOOT_LOG_DBG("flash area %d open count: %d (-)", id, open_count[id]); + if (!open_count[id]) { + /* mcuboot is not currently consistent in opening/closing flash areas only once at a time + * so only deinitialize the BlockDevice if all callers have closed the flash area. */ + MCUBOOT_LOG_DBG("deinitializing flash area block device %d...", id); + mbed::BlockDevice* bd = flash_map_bd[id]; + bd->deinit(); + } +} + +/* + * Read/write/erase. Offset is relative from beginning of flash area. + */ +int flash_area_read(const struct flash_area* fap, uint32_t off, void* dst, uint32_t len) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + + /* Note: The address must be aligned to bd->get_read_size(). If MCUBOOT_READ_GRANULARITY + is defined, the length does not need to be aligned. */ +#ifdef MCUBOOT_READ_GRANULARITY + uint32_t read_size = bd->get_read_size(); + if (read_size == 0) { + MCUBOOT_LOG_ERR("Invalid read size: must be non-zero"); + return -1; + } + if (MCUBOOT_READ_GRANULARITY < read_size) { + MCUBOOT_LOG_ERR("Please increase MCUBOOT_READ_GRANULARITY (currently %u) to be at least %u", + MCUBOOT_READ_GRANULARITY, read_size); + return -1; + } + + uint32_t remainder = len % read_size; + len -= remainder; + if (len != 0) { +#endif + if (!bd->is_valid_read(off, len)) { + MCUBOOT_LOG_ERR("Invalid read: fa_id %d offset 0x%x len 0x%x", fap->fa_id, + (unsigned int) off, (unsigned int) len); + return -1; + } + else { + int ret = bd->read(dst, off, len); + if (ret != 0) { + MCUBOOT_LOG_ERR("Read failed: fa_id %d offset 0x%x len 0x%x", fap->fa_id, + (unsigned int) off, (unsigned int) len); + return ret; + } + } +#ifdef MCUBOOT_READ_GRANULARITY + } + + if (remainder) { + if (!bd->is_valid_read(off + len, read_size)) { + MCUBOOT_LOG_ERR("Invalid read: fa_id %d offset 0x%x len 0x%x", fap->fa_id, + (unsigned int) (off + len), (unsigned int) read_size); + return -1; + } + else { + uint8_t buffer[MCUBOOT_READ_GRANULARITY]; + int ret = bd->read(buffer, off + len, read_size); + if (ret != 0) { + MCUBOOT_LOG_ERR("Read failed: %d", ret); + return ret; + } + memcpy((uint8_t *)dst + len, buffer, remainder); + } + } +#endif + + return 0; +} + +int flash_area_write(const struct flash_area* fap, uint32_t off, const void* src, uint32_t len) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->program(src, off, len); +} + +int flash_area_erase(const struct flash_area* fap, uint32_t off, uint32_t len) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->erase(off, len); +} + +uint32_t flash_area_align(const struct flash_area* fap) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->get_program_size(); +} + +uint8_t flash_area_erased_val(const struct flash_area* fap) { + mbed::BlockDevice* bd = flash_map_bd[fap->fa_id]; + return bd->get_erase_value(); +} + +int flash_area_get_sectors(int fa_id, uint32_t* count, struct flash_sector* sectors) { + mbed::BlockDevice* bd = flash_map_bd[fa_id]; + + /* Loop through sectors and collect information on them */ + bd_addr_t offset = 0; + *count = 0; + while (*count < MCUBOOT_MAX_IMG_SECTORS && bd->is_valid_read(offset, bd->get_read_size())) { + + sectors[*count].fs_off = offset; + bd_size_t erase_size = bd->get_erase_size(offset); + sectors[*count].fs_size = erase_size; + + offset += erase_size; + *count += 1; + } + + return 0; +} + +int flash_area_id_from_image_slot(int slot) { + return slot; +} + +/** + * Multi images support not implemented yet + */ +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + assert(image_index == 0); + return slot; +} + +int flash_area_id_to_multi_image_slot(int image_index, int area_id) +{ + assert(image_index == 0); + return area_id; +} diff --git a/bootloader/mcuboot/boot/mbed/src/secondary_bd.cpp b/bootloader/mcuboot/boot/mbed/src/secondary_bd.cpp new file mode 100644 index 0000000..7550a61 --- /dev/null +++ b/bootloader/mcuboot/boot/mbed/src/secondary_bd.cpp @@ -0,0 +1,40 @@ +/* + * Mbed-OS Microcontroller Library + * Copyright (c) 2020 Embedded Planet + * Copyright (c) 2020 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#if MCUBOOT_DIRECT_XIP + +#include "flash_map_backend/secondary_bd.h" +#include "platform/mbed_toolchain.h" +#include "FlashIAP/FlashIAPBlockDevice.h" + +/** + * For an XIP build, the secondary BD is provided by mcuboot by default. + * + * This is a weak symbol so the user can override it. + */ +MBED_WEAK mbed::BlockDevice* get_secondary_bd(void) { + static FlashIAPBlockDevice secondary_bd(MBED_CONF_MCUBOOT_XIP_SECONDARY_SLOT_ADDRESS, + MCUBOOT_SLOT_SIZE); + + return &secondary_bd; +} + +#endif + + diff --git a/bootloader/mcuboot/boot/mynewt/README.md b/bootloader/mcuboot/boot/mynewt/README.md new file mode 100644 index 0000000..e482d6d --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/README.md @@ -0,0 +1,6 @@ +# MCUboot - apps/boot + +This sample app implements a bootloader for the Mynewt OS (apache.mynewt.org). +This app requires the following Mynewt repositories: + * @mcuboot (this one) + * @apache-mynewt-core diff --git a/bootloader/mcuboot/boot/mynewt/boot_uart/include/boot_uart/boot_uart.h b/bootloader/mcuboot/boot/mynewt/boot_uart/include/boot_uart/boot_uart.h new file mode 100644 index 0000000..6d69668 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/boot_uart/include/boot_uart/boot_uart.h @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _BOOT_UART_H_ +#define _BOOT_UART_H_ + +int boot_uart_open(void); +void boot_uart_close(void); +int boot_uart_read(char *str, int cnt, int *newline); +void boot_uart_write(const char *ptr, int cnt); + +#endif /* _BOOT_UART_H_ */ diff --git a/bootloader/mcuboot/boot/mynewt/boot_uart/pkg.yml b/bootloader/mcuboot/boot/mynewt/boot_uart/pkg.yml new file mode 100644 index 0000000..a100a20 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/boot_uart/pkg.yml @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: boot/mynewt/boot_uart +pkg.description: "For interfacing with uart from boot_serial" +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - loader + - boot + - bootloader + +pkg.deps: + - "@apache-mynewt-core/hw/hal" + + diff --git a/bootloader/mcuboot/boot/mynewt/boot_uart/src/boot_uart.c b/bootloader/mcuboot/boot/mynewt/boot_uart/src/boot_uart.c new file mode 100644 index 0000000..7593e33 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/boot_uart/src/boot_uart.c @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include +#include +#include +#include "os/mynewt.h" +#include + +/* + * RX is a ring buffer, which gets drained constantly. + * TX blocks until buffer has been completely transmitted. + */ +#define CONSOLE_HEAD_INC(cr) (((cr)->head + 1) & (sizeof((cr)->buf) - 1)) +#define CONSOLE_TAIL_INC(cr) (((cr)->tail + 1) & (sizeof((cr)->buf) - 1)) + +struct { + uint16_t head; + uint16_t tail; + uint8_t buf[MYNEWT_VAL(CONSOLE_UART_RX_BUF_SIZE)]; +} bs_uart_rx; + +struct { + uint8_t *ptr; + int cnt; +} volatile bs_uart_tx; + +static struct uart_dev *bs_uart; + +static int bs_rx_char(void *arg, uint8_t byte); +static int bs_tx_char(void *arg); + +int +boot_uart_open(void) +{ + struct uart_conf uc = { + .uc_speed = MYNEWT_VAL(CONSOLE_UART_BAUD), + .uc_databits = 8, + .uc_stopbits = 1, + .uc_parity = UART_PARITY_NONE, + .uc_flow_ctl = MYNEWT_VAL(CONSOLE_UART_FLOW_CONTROL), + .uc_tx_char = bs_tx_char, + .uc_rx_char = bs_rx_char, + }; + + bs_uart = (struct uart_dev *)os_dev_open(MYNEWT_VAL(CONSOLE_UART_DEV), + OS_TIMEOUT_NEVER, &uc); + if (!bs_uart) { + return -1; + } + return 0; +} + +void +boot_uart_close(void) +{ + os_dev_close(&bs_uart->ud_dev); + bs_uart_rx.head = 0; + bs_uart_rx.tail = 0; + bs_uart_tx.cnt = 0; +} + +static int +bs_rx_char(void *arg, uint8_t byte) +{ + if (CONSOLE_HEAD_INC(&bs_uart_rx) == bs_uart_rx.tail) { + /* + * RX queue full. Reader must drain this. + */ + return -1; + } + bs_uart_rx.buf[bs_uart_rx.head] = byte; + bs_uart_rx.head = CONSOLE_HEAD_INC(&bs_uart_rx); + return 0; +} + +static uint8_t +bs_pull_char(void) +{ + uint8_t ch; + + ch = bs_uart_rx.buf[bs_uart_rx.tail]; + bs_uart_rx.tail = CONSOLE_TAIL_INC(&bs_uart_rx); + return ch; +} + +int +boot_uart_read(char *str, int cnt, int *newline) +{ + int i; + int sr; + uint8_t ch; + + *newline = 0; + OS_ENTER_CRITICAL(sr); + for (i = 0; i < cnt; i++) { + if (bs_uart_rx.head == bs_uart_rx.tail) { + break; + } + + ch = bs_pull_char(); + if (ch == '\n') { + *str = '\0'; + *newline = 1; + break; + } + /* + * Unblock interrupts allowing more incoming data. + */ + OS_EXIT_CRITICAL(sr); + OS_ENTER_CRITICAL(sr); + *str++ = ch; + } + OS_EXIT_CRITICAL(sr); + if (i > 0 || *newline) { + uart_start_rx(bs_uart); + } + return i; +} + +static int +bs_tx_char(void *arg) +{ + uint8_t ch; + + if (!bs_uart_tx.cnt) { + return -1; + } + + bs_uart_tx.cnt--; + ch = *bs_uart_tx.ptr; + bs_uart_tx.ptr++; + return ch; +} + +#if MYNEWT_VAL(SELFTEST) +/* + * OS is not running, so native uart 'driver' cannot run either. + * At the moment unit tests don't check the responses to messages it + * sends, so we can drop the outgoing data here. + */ +void +boot_uart_write(const char *ptr, int cnt) +{ +} + +#else + +void +boot_uart_write(const char *ptr, int cnt) +{ + int sr; + + OS_ENTER_CRITICAL(sr); + bs_uart_tx.ptr = (uint8_t *)ptr; + bs_uart_tx.cnt = cnt; + OS_EXIT_CRITICAL(sr); + + uart_start_tx(bs_uart); + while (bs_uart_tx.cnt); +} + +#endif diff --git a/bootloader/mcuboot/boot/mynewt/boot_uart/syscfg.yml b/bootloader/mcuboot/boot/mynewt/boot_uart/syscfg.yml new file mode 100644 index 0000000..29f4b53 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/boot_uart/syscfg.yml @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + CONSOLE_UART_RX_BUF_SIZE: + description: > + UART console receive buffer size; must be power of 2. + value: 32 + diff --git a/bootloader/mcuboot/boot/mynewt/flash_map_backend/include/flash_map_backend/flash_map_backend.h b/bootloader/mcuboot/boot/mynewt/flash_map_backend/include/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000..3686510 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/flash_map_backend/include/flash_map_backend/flash_map_backend.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __FLASH_MAP_BACKEND_H__ +#define __FLASH_MAP_BACKEND_H__ + +#include +#include +#include +#include /* off_t */ + +#if (MCUBOOT_IMAGE_NUMBER == 1) + +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0 : \ + FLASH_AREA_IMAGE_0) +#if MYNEWT_VAL(BOOTUTIL_SINGLE_APPLICATION_SLOT) +#define FLASH_AREA_IMAGE_SECONDARY FLASH_AREA_IMAGE_PRIMARY +#else +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_1 : \ + FLASH_AREA_IMAGE_1) +#endif + +#elif (MCUBOOT_IMAGE_NUMBER == 2) + +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_0 : \ + ((x) == 1) ? \ + FLASH_AREA_IMAGE_2 : \ + 255) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_IMAGE_1 : \ + ((x) == 1) ? \ + FLASH_AREA_IMAGE_3 : \ + 255) + +#else +#error "Image slot and flash area mapping is not defined" +#endif + +int flash_area_id_from_multi_image_slot(int image_index, int slot); +int flash_area_id_to_multi_image_slot(int image_index, int area_id); + +struct flash_sector { + uint32_t fs_off; + uint32_t fs_size; +}; + +int flash_area_sector_from_off(off_t off, struct flash_sector *sector); + +static inline int flash_area_get_sector(const struct flash_area *fa, off_t off, + struct flash_sector *sector) +{ + return flash_area_sector_from_off(off, sector); +} + +static inline uint8_t flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +static inline uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + return fa->fa_device_id; +} + +static inline uint32_t flash_area_get_off(const struct flash_area *fa) +{ + return fa->fa_off; +} + +static inline uint32_t flash_area_get_size(const struct flash_area *fa) +{ + return fa->fa_size; +} + +static inline uint32_t flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +#endif /* __FLASH_MAP_BACKEND_H__ */ diff --git a/bootloader/mcuboot/boot/mynewt/flash_map_backend/pkg.yml b/bootloader/mcuboot/boot/mynewt/flash_map_backend/pkg.yml new file mode 100644 index 0000000..ba1bf5b --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/flash_map_backend/pkg.yml @@ -0,0 +1,14 @@ +# +# Copyright (c) 2018 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +pkg.name: boot/mynewt/flash_map_backend +pkg.description: Flash_map API helper reference. +pkg.author: "Nordic Semiconductor ASA" +pkg.homepage: "http://mcuboot.com" + +pkg.deps: + - "@apache-mynewt-core/sys/flash_map" + - "@mcuboot/boot/mynewt/mcuboot_config" diff --git a/bootloader/mcuboot/boot/mynewt/flash_map_backend/src/flash_map_extended.c b/bootloader/mcuboot/boot/mynewt/flash_map_backend/src/flash_map_extended.c new file mode 100644 index 0000000..e6952b3 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/flash_map_backend/src/flash_map_extended.c @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + switch (slot) { + case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); +#ifndef MCUBOOT_SINGLE_APPLICATION_SLOT + case 1: return FLASH_AREA_IMAGE_SECONDARY(image_index); +#endif +#if MCUBOOT_SWAP_USING_SCRATCH + case 2: return FLASH_AREA_IMAGE_SCRATCH; +#endif + } + return 255; +} + +int flash_area_id_to_multi_image_slot(int image_index, int area_id) +{ + if (area_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) { + return 0; + } +#ifndef MCUBOOT_SINGLE_APPLICATION_SLOT + if (area_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) { + return 1; + } +#endif + return 255; +} + +int flash_area_sector_from_off(off_t off, struct flash_sector *sector) +{ + const struct flash_area *fa; + const struct hal_flash *hf; + uint32_t start; + uint32_t size; + int rc; + int i; + + rc = flash_area_open(FLASH_AREA_IMAGE_0, &fa); + if (rc != 0) { + return -1; + } + + rc = -1; + hf = hal_bsp_flash_dev(fa->fa_device_id); + for (i = 0; i < hf->hf_sector_cnt; i++) { + hf->hf_itf->hff_sector_info(hf, i, &start, &size); + if (start < fa->fa_off) { + continue; + } + if (off >= start - fa->fa_off && off <= (start - fa->fa_off) + size) { + sector->fs_off = start - fa->fa_off; + sector->fs_size = size; + rc = 0; + break; + } + } + + flash_area_close(fa); + return rc; +} diff --git a/bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h b/bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000..1ed1628 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef __MCUBOOT_CONFIG_H__ +#define __MCUBOOT_CONFIG_H__ + +#include + +#if MYNEWT_VAL(BOOTUTIL_IMAGE_NUMBER) +#define MCUBOOT_IMAGE_NUMBER MYNEWT_VAL(BOOTUTIL_IMAGE_NUMBER) +#else +#define MCUBOOT_IMAGE_NUMBER 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_VERSION_CMP_USE_BUILD_NUMBER) +#define MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER +#endif +#if MYNEWT_VAL(BOOT_SERIAL) +#define MCUBOOT_SERIAL 1 +#endif +#if MYNEWT_VAL(BOOT_SERIAL_MGMT_ECHO) +#define MCUBOOT_BOOT_MGMT_ECHO 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_VALIDATE_SLOT0) +#define MCUBOOT_VALIDATE_PRIMARY_SLOT 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_USE_MBED_TLS) +#define MCUBOOT_USE_MBED_TLS 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_USE_TINYCRYPT) +#define MCUBOOT_USE_TINYCRYPT 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_SIGN_EC256) +#define MCUBOOT_SIGN_EC256 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_SIGN_RSA) +#define MCUBOOT_SIGN_RSA 1 +#define MCUBOOT_SIGN_RSA_LEN MYNEWT_VAL(BOOTUTIL_SIGN_RSA_LEN) +#endif +#if MYNEWT_VAL(BOOTUTIL_SIGN_ED25519) +#define MCUBOOT_SIGN_ED25519 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_ENCRYPT_RSA) +#define MCUBOOT_ENCRYPT_RSA 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_ENCRYPT_KW) +#define MCUBOOT_ENCRYPT_KW 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_ENCRYPT_EC256) +#define MCUBOOT_ENCRYPT_EC256 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_ENCRYPT_X25519) +#define MCUBOOT_ENCRYPT_X25519 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_ENCRYPT_RSA) || MYNEWT_VAL(BOOTUTIL_ENCRYPT_KW) || \ + MYNEWT_VAL(BOOTUTIL_ENCRYPT_EC256) || MYNEWT_VAL(BOOTUTIL_ENCRYPT_X25519) +#define MCUBOOT_ENC_IMAGES 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_SWAP_USING_MOVE) +#define MCUBOOT_SWAP_USING_MOVE 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_SWAP_SAVE_ENCTLV) +#define MCUBOOT_SWAP_SAVE_ENCTLV 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_OVERWRITE_ONLY) +#define MCUBOOT_OVERWRITE_ONLY 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_OVERWRITE_ONLY_FAST) +#define MCUBOOT_OVERWRITE_ONLY_FAST 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_SINGLE_APPLICATION_SLOT) +#define MCUBOOT_SINGLE_APPLICATION_SLOT 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_HAVE_LOGGING) +#define MCUBOOT_HAVE_LOGGING 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_BOOTSTRAP) +#define MCUBOOT_BOOTSTRAP 1 +#endif +#if MYNEWT_VAL_CHOICE(BOOTUTIL_DOWNGRADE_PREVENTION, version) +#define MCUBOOT_DOWNGRADE_PREVENTION 1 +/* MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER is used later as bool value so it is + * always defined, (unlike MCUBOOT_DOWNGRADE_PREVENTION which is only used in + * preprocessor condition and my be not defined) */ +#define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 0 +#elif MYNEWT_VAL_CHOICE(BOOTUTIL_DOWNGRADE_PREVENTION, security_counter) +#define MCUBOOT_DOWNGRADE_PREVENTION 1 +#define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 1 +#endif +#if MYNEWT_VAL(BOOTUTIL_HW_DOWNGRADE_PREVENTION) +#define MCUBOOT_HW_ROLLBACK_PROT 1 +#endif + +#if MYNEWT_VAL(MCUBOOT_MEASURED_BOOT) +#define MCUBOOT_MEASURED_BOOT 1 +#endif + +#if MYNEWT_VAL(MCUBOOT_MEASURED_BOOT_MAX_RECORD_SZ) +#define MAX_BOOT_RECORD_SZ MYNEWT_VAL(MCUBOOT_MEASURED_BOOT_MAX_RECORD_SZ) +#endif + +#if MYNEWT_VAL(MCUBOOT_DATA_SHARING) +#define MCUBOOT_DATA_SHARING 1 +#endif + +#if MYNEWT_VAL(MCUBOOT_SHARED_DATA_BASE) +#define MCUBOOT_SHARED_DATA_BASE MYNEWT_VAL(MCUBOOT_SHARED_DATA_BASE) +#endif + +#if MYNEWT_VAL(MCUBOOT_SHARED_DATA_SIZE) +#define MCUBOOT_SHARED_DATA_SIZE MYNEWT_VAL(MCUBOOT_SHARED_DATA_SIZE) +#endif + +/* + * Currently there is no configuration option, for this platform, + * that enables the system specific mcumgr commands in mcuboot + */ +#define MCUBOOT_PERUSER_MGMT_GROUP_ENABLED 0 + +#define MCUBOOT_MAX_IMG_SECTORS MYNEWT_VAL(BOOTUTIL_MAX_IMG_SECTORS) + +#if MYNEWT_VAL(MCU_FLASH_MIN_WRITE_SIZE) > 8 +#define MCUBOOT_BOOT_MAX_ALIGN MYNEWT_VAL(MCU_FLASH_MIN_WRITE_SIZE) +#endif + +#if MYNEWT_VAL(BOOTUTIL_FEED_WATCHDOG) && MYNEWT_VAL(WATCHDOG_INTERVAL) +#include +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + hal_watchdog_tickle(); \ + } while (0) +#else +#define MCUBOOT_WATCHDOG_FEED() do {} while (0) +#endif + +/* + * No direct idle call implemented + */ +#define MCUBOOT_CPU_IDLE() \ + do { \ + } while (0) + +#endif /* __MCUBOOT_CONFIG_H__ */ diff --git a/bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_logging.h b/bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000..a646faf --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifndef __MCUBOOT_LOGGING_H__ +#define __MCUBOOT_LOGGING_H__ + +#include +#include + +#define BOOTUTIL_LOG_LEVEL_OFF 1 +#define BOOTUTIL_LOG_LEVEL_ERROR 2 +#define BOOTUTIL_LOG_LEVEL_WARNING 3 +#define BOOTUTIL_LOG_LEVEL_INFO 4 +#define BOOTUTIL_LOG_LEVEL_DEBUG 5 + +#define MCUBOOT_LOG_LEVEL_OFF BOOTUTIL_LOG_LEVEL_OFF +#define MCUBOOT_LOG_LEVEL_ERROR BOOTUTIL_LOG_LEVEL_ERROR +#define MCUBOOT_LOG_LEVEL_WARNING BOOTUTIL_LOG_LEVEL_WARNING +#define MCUBOOT_LOG_LEVEL_INFO BOOTUTIL_LOG_LEVEL_INFO +#define MCUBOOT_LOG_LEVEL_DEBUG BOOTUTIL_LOG_LEVEL_DEBUG + +#ifndef MCUBOOT_LOG_LEVEL +#define MCUBOOT_LOG_LEVEL MYNEWT_VAL(BOOTUTIL_LOG_LEVEL) +#endif + +#define MCUBOOT_LOG_MODULE_DECLARE(domain) /* ignore */ +#define MCUBOOT_LOG_MODULE_REGISTER(domain) /* ignore */ + +#if !((MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_OFF) && \ + (MCUBOOT_LOG_LEVEL <= MCUBOOT_LOG_LEVEL_DEBUG)) +#error "Invalid MCUBOOT_LOG_LEVEL config." +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_ERROR +#define MCUBOOT_LOG_ERR(_fmt, ...) \ + do { \ + printf("[ERR] " _fmt "\n", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_ERR(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_WARNING +#define MCUBOOT_LOG_WRN(_fmt, ...) \ + do { \ + printf("[WRN] " _fmt "\n", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_WRN(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_INFO +#define MCUBOOT_LOG_INF(_fmt, ...) \ + do { \ + printf("[INF] " _fmt "\n", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_INF(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_DEBUG +#define MCUBOOT_LOG_DBG(_fmt, ...) \ + do { \ + printf("[DBG] " _fmt "\n", ##__VA_ARGS__); \ + } while (0) +#else +#define MCUBOOT_LOG_DBG(...) IGNORE(__VA_ARGS__) +#endif + +#define MCUBOOT_LOG_SIM(...) IGNORE(__VA_ARGS__) + +#endif diff --git a/bootloader/mcuboot/boot/mynewt/mcuboot_config/pkg.yml b/bootloader/mcuboot/boot/mynewt/mcuboot_config/pkg.yml new file mode 100644 index 0000000..33ba332 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/mcuboot_config/pkg.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: boot/mynewt/mcuboot_config +pkg.description: "Mynewt's mcuboot configuration" +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" diff --git a/bootloader/mcuboot/boot/mynewt/mcuboot_config/syscfg.yml b/bootloader/mcuboot/boot/mynewt/mcuboot_config/syscfg.yml new file mode 100644 index 0000000..7326005 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/mcuboot_config/syscfg.yml @@ -0,0 +1,159 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Package: boot/mynewt/mcuboot_config + +syscfg.defs: + BOOTUTIL_IMAGE_NUMBER: + description: 'Number of images for multi-image (0 and 1 mean single image).' + value: 0 + BOOTUTIL_VALIDATE_SLOT0: + description: 'Validate image at slot 0 on each boot.' + value: 0 + BOOTUTIL_SIGN_RSA: + description: 'Images are signed using RSA.' + value: 0 + BOOTUTIL_SIGN_RSA_LEN: + description: 'Key size for RSA keys (2048 or 3072).' + value: 2048 + BOOTUTIL_SIGN_EC256: + description: 'Images are signed using ECDSA NIST P-256.' + value: 0 + BOOTUTIL_SIGN_ED25519: + description: 'Images are signed using ED25519.' + value: 0 + BOOTUTIL_ENCRYPT_RSA: + description: 'Support for encrypted images using RSA-2048-OAEP.' + value: 0 + BOOTUTIL_ENCRYPT_KW: + description: 'Support for encrypted images using AES-128-Keywrap.' + value: 0 + BOOTUTIL_ENCRYPT_EC256: + description: 'Support for encrypted images using ECIES-P256.' + value: 0 + BOOTUTIL_ENCRYPT_X25519: + description: 'Support for encrypted images using ECIES-X25519.' + value: 0 + BOOTUTIL_USE_MBED_TLS: + description: 'Use mbed TLS for crypto operations.' + value: 1 + BOOTUTIL_USE_TINYCRYPT: + description: 'Use tinycrypt for crypto operations.' + value: 0 + BOOTUTIL_SWAP_USING_MOVE: + description: 'Perform swap without requiring scratch.' + value: 0 + BOOTUTIL_SWAP_SAVE_ENCTLV: + description: 'Save TLVs instead of plaintext encryption keys in swap status.' + value: 0 + BOOTUTIL_OVERWRITE_ONLY: + description: 'Non-swapping upgrades, copy from slot 1 to slot 0 only.' + value: 0 + BOOTUTIL_OVERWRITE_ONLY_FAST: + description: 'Use faster copy only upgrade.' + value: 1 + BOOTUTIL_SINGLE_APPLICATION_SLOT: + description: 'Set to one if there is only one slot.' + value: 0 + BOOTUTIL_IMAGE_FORMAT_V2: + description: 'Indicates that system is using v2 of image format.' + value: 1 + BOOTUTIL_MAX_IMG_SECTORS: + description: 'Maximum number of sectors that are swapped.' + value: 128 + BOOTUTIL_DOWNGRADE_PREVENTION: + description: > + Select downgrade prevention strategy. + - none downgrades are allowed + - version: + Prevent downgrades by enforcing incrementing version numbers. + When this option is set, any upgrade must have greater major version + or greater minor version with equal major version. This mechanism + only protects against some attacks against version downgrades (for + example, a JTAG could be used to write an older version). + - security_counter: + security counter is used for version eligibility check instead of pure + version. When this option is set, any upgrade must have greater or + equal security counter value. + Because of the acceptance of equal values it allows for software + downgrades to some extent. + choices: + - none + - version + - security_counter + value: none + BOOTUTIL_VERSION_CMP_USE_BUILD_NUMBER: + description: > + Use build number while comparing image version. + By default, the image version comparison relies only on version major, + minor and revision. Enable this option to take into account the build + number as well. + This only affect builds with BOOTUTIL_DOWNGRADE_PREVENTION set to version. + value: 0 + BOOTUTIL_HW_ROLLBACK_PROT: + description: > + Prevent undesirable/malicious software downgrades. When this option is + set, any upgrade must have greater or equal security counter value. + Because of the acceptance of equal values it allows for software + downgrade to some extent + value: 0 + BOOTUTIL_HAVE_LOGGING: + description: 'Enable serial logging' + value: 0 + restrictions: + - "!BOOTUTIL_NO_LOGGING" + BOOTUTIL_NO_LOGGING: + description: 'No serial logging' + value: 1 + restrictions: + - "!BOOTUTIL_HAVE_LOGGING" + BOOTUTIL_LOG_LEVEL: + description: > + Default console log level. Valid values are: + BOOTUTIL_LOG_LEVEL_OFF + BOOTUTIL_LOG_LEVEL_ERROR + BOOTUTIL_LOG_LEVEL_WARNING + BOOTUTIL_LOG_LEVEL_INFO + BOOTUTIL_LOG_LEVEL_DEBUG + value: 'BOOTUTIL_LOG_LEVEL_INFO' + BOOTUTIL_BOOTSTRAP: + description: 'Support bootstrapping slot0 from slot1, if slot0 is empty' + value: 0 + BOOTUTIL_FEED_WATCHDOG: + description: 'Enable watchdog feeding while performing a swap upgrade' + value: 0 + + MCUBOOT_MEASURED_BOOT: + description: > + Store the boot state/measurements in shared memory. + If enabled, the bootloader will store certain boot measurements such as + the hash of the firmware image in a shared memory area. This data can + be used later by runtime services (e.g. by a device attestation service). + value: + MCUBOOT_MEASURED_BOOT_MAX_RECORD_SZ: + description: the maximum size of the CBOR encoded boot record in bytes. + value: + MCUBOOT_DATA_SHARING: + description: Save application specific data in shared memory (RAM). + value: + MCUBOOT_SHARED_DATA_BASE: + description: RAM address of shared data + value: + MCUBOOT_SHARED_DATA_SIZE: + description: Shared data size. + value: diff --git a/bootloader/mcuboot/boot/mynewt/pkg.yml b/bootloader/mcuboot/boot/mynewt/pkg.yml new file mode 100644 index 0000000..e0b3132 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/pkg.yml @@ -0,0 +1,51 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: boot/mynewt +pkg.type: app +pkg.description: "Mynewt port of mcuboot" +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - loader + +pkg.cflags: + - "-DMCUBOOT_MYNEWT=1" + +pkg.deps: + - "@mcuboot/boot/mynewt/mcuboot_config" + - "@mcuboot/boot/bootutil" + - "@mcuboot/boot/mynewt/flash_map_backend" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/log/stub" + +pkg.ign_files.!BOOTUTIL_SINGLE_APPLICATION_SLOT: + - "single_loader.c" +pkg.ign_files.BOOTUTIL_SINGLE_APPLICATION_SLOT: + - "swap_scratch.c" + +pkg.deps.BOOTUTIL_NO_LOGGING: + - "@apache-mynewt-core/sys/console/stub" + +pkg.deps.BOOTUTIL_HAVE_LOGGING: + - "@apache-mynewt-core/sys/console/minimal" + +pkg.deps.BOOT_SERIAL: + - "@mcuboot/boot/boot_serial" + - "@mcuboot/boot/mynewt/boot_uart" diff --git a/bootloader/mcuboot/boot/mynewt/src/main.c b/bootloader/mcuboot/boot/mynewt/src/main.c new file mode 100644 index 0000000..7a3fa2e --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/src/main.c @@ -0,0 +1,278 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "mcuboot_config/mcuboot_config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef MCUBOOT_SERIAL +#include +#include +#include +#endif +#if defined(MCUBOOT_SERIAL) +#include +#endif +#include +#include "bootutil/image.h" +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/fault_injection_hardening.h" + +#if MYNEWT_VAL(BOOT_CUSTOM_START) +void boot_custom_start(uintptr_t flash_base, struct boot_rsp *rsp); +#endif + +#if MYNEWT_VAL(BOOT_PREBOOT) +void boot_preboot(void); +#endif + +#if defined(MCUBOOT_SERIAL) +#define BOOT_SERIAL_REPORT_DUR \ + (MYNEWT_VAL(OS_CPUTIME_FREQ) / MYNEWT_VAL(BOOT_SERIAL_REPORT_FREQ)) +#define BOOT_SERIAL_INPUT_MAX (512) + +static int boot_read(char *str, int cnt, int *newline); +static const struct boot_uart_funcs boot_uart_funcs = { + .read = boot_read, + .write = boot_uart_write +}; + +static int +boot_read(char *str, int cnt, int *newline) +{ +#if MYNEWT_VAL(BOOT_SERIAL_REPORT_PIN) != -1 + static uint32_t tick = 0; + + if (tick == 0) { + /* + * Configure GPIO line as output. This is a pin we toggle at the + * given frequency. + */ + hal_gpio_init_out(MYNEWT_VAL(BOOT_SERIAL_REPORT_PIN), 0); + tick = os_cputime_get32(); + } else { + if (os_cputime_get32() - tick > BOOT_SERIAL_REPORT_DUR) { + hal_gpio_toggle(MYNEWT_VAL(BOOT_SERIAL_REPORT_PIN)); + tick = os_cputime_get32(); + } + } +#endif + hal_watchdog_tickle(); + + return boot_uart_read(str, cnt, newline); +} + +#if MYNEWT_VAL(BOOT_SERIAL_DETECT_TIMEOUT) != 0 + +/** Don't include null-terminator in comparison. */ +#define BOOT_SERIAL_DETECT_STRING_LEN \ + (sizeof MYNEWT_VAL(BOOT_SERIAL_DETECT_STRING) - 1) + +/** + * Listens on the UART for the management string. Blocks for up to + * BOOT_SERIAL_DETECT_TIMEOUT milliseconds. + * + * @return true if the management string was received; + * false if the management string was not received + * before the UART listen timeout expired. + */ +static bool +serial_detect_uart_string(void) +{ + uint32_t start_tick; + char buf[BOOT_SERIAL_DETECT_STRING_LEN] = { 0 }; + char ch; + int newline; + int rc; + + /* Calculate the timeout duration in OS cputime ticks. */ + static const uint32_t timeout_dur = + MYNEWT_VAL(BOOT_SERIAL_DETECT_TIMEOUT) / + (1000.0 / MYNEWT_VAL(OS_CPUTIME_FREQ)); + + rc = boot_uart_open(); + assert(rc == 0); + + start_tick = os_cputime_get32(); + + while (1) { + /* Read a single character from the UART. */ + rc = boot_uart_read(&ch, 1, &newline); + if (rc > 0) { + /* Eliminate the oldest character in the buffer to make room for + * the new one. + */ + memmove(buf, buf + 1, BOOT_SERIAL_DETECT_STRING_LEN - 1); + buf[BOOT_SERIAL_DETECT_STRING_LEN - 1] = ch; + + /* If the full management string has been received, indicate that + * the serial boot loader should start. + */ + rc = memcmp(buf, + MYNEWT_VAL(BOOT_SERIAL_DETECT_STRING), + BOOT_SERIAL_DETECT_STRING_LEN); + if (rc == 0) { + boot_uart_close(); + return true; + } + } + + /* Abort the listen on timeout. */ + if (os_cputime_get32() >= start_tick + timeout_dur) { + boot_uart_close(); + return false; + } + } +} +#endif + +static void +serial_boot_detect(void) +{ + /* + * Read retained register and compare with expected magic value. + * If it matches, await for download commands from serial. + */ +#if MYNEWT_VAL(BOOT_SERIAL_NVREG_INDEX) != -1 + if (hal_nvreg_read(MYNEWT_VAL(BOOT_SERIAL_NVREG_INDEX)) == + MYNEWT_VAL(BOOT_SERIAL_NVREG_MAGIC)) { + + hal_nvreg_write(MYNEWT_VAL(BOOT_SERIAL_NVREG_INDEX), 0); + goto serial_boot; + } +#endif + + /* + * Configure a GPIO as input, and compare it against expected value. + * If it matches, await for download commands from serial. + */ +#if MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN) != -1 + hal_gpio_init_in(MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN), + MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN_CFG)); + if (hal_gpio_read(MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN)) == + MYNEWT_VAL(BOOT_SERIAL_DETECT_PIN_VAL)) { + goto serial_boot; + } +#endif + + /* + * Listen for management pattern in UART input. If detected, await for + * download commands from serial. + */ +#if MYNEWT_VAL(BOOT_SERIAL_DETECT_TIMEOUT) != 0 + if (serial_detect_uart_string()) { + goto serial_boot; + } +#endif + return; +serial_boot: + boot_uart_open(); + boot_serial_start(&boot_uart_funcs); + assert(0); +} +#endif + +/* + * Temporary flash_device_base() implementation. + * + * TODO: remove this when mynewt needs to support flash_device_base() + * for devices with nonzero base addresses. + */ +int flash_device_base(uint8_t fd_id, uintptr_t *ret) +{ + *ret = 0; + return 0; +} + +int +mynewt_main(void) +{ + struct boot_rsp rsp; + uintptr_t flash_base; + int rc; + fih_ret fih_rc = FIH_FAILURE; + + hal_bsp_init(); + +#if !MYNEWT_VAL(OS_SCHEDULING) && MYNEWT_VAL(WATCHDOG_INTERVAL) + rc = hal_watchdog_init(MYNEWT_VAL(WATCHDOG_INTERVAL)); + assert(rc == 0); +#endif + +#if defined(MCUBOOT_SERIAL) || defined(MCUBOOT_HAVE_LOGGING) || \ + MYNEWT_VAL(CRYPTO) || MYNEWT_VAL(HASH) || MYNEWT_VAL(BOOT_MYNEWT_SYSINIT) + /* initialize uart/crypto without os */ + os_dev_initialize_all(OS_DEV_INIT_PRIMARY); + os_dev_initialize_all(OS_DEV_INIT_SECONDARY); + sysinit(); + console_blocking_mode(); +#if defined(MCUBOOT_SERIAL) + serial_boot_detect(); + hal_timer_deinit(MYNEWT_VAL(OS_CPUTIME_TIMER_NUM)); +#endif +#else + flash_map_init(); +#endif + +#if MYNEWT_VAL(BOOT_PREBOOT) + boot_preboot(); +#endif + FIH_CALL(boot_go, fih_rc, &rsp); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + assert(fih_rc == FIH_SUCCESS); + FIH_PANIC; + } + + rc = flash_device_base(rsp.br_flash_dev_id, &flash_base); + assert(rc == 0); + +#if MYNEWT_VAL(BOOT_CUSTOM_START) + boot_custom_start(flash_base, &rsp); +#else + hal_bsp_deinit(); + hal_system_start((void *)(flash_base + rsp.br_image_off + + rsp.br_hdr->ih_hdr_size)); +#endif + + return 0; +} + +/* + * Mynewt startup code jump to mynewt_main() + * This function is here for compatibility with + * pre 1.12. mynewt-core that still wanted main() + */ +int +main(void) +{ + mynewt_main(); +} diff --git a/bootloader/mcuboot/boot/mynewt/src/single_loader.c b/bootloader/mcuboot/boot/mynewt/src/single_loader.c new file mode 100644 index 0000000..564d748 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/src/single_loader.c @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020 Nordic Semiconductor ASA + * Copyright (c) 2020 Arm Limited + */ + +#include +#include "bootutil/image.h" +#include "../../../bootutil/src/bootutil_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/fault_injection_hardening.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +/* Variables passed outside of unit via poiters. */ +static const struct flash_area *_fa_p; +static struct image_header _hdr = { 0 }; + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) +/** + * Validate hash of a primary boot image. + * + * @param[in] fa_p flash area pointer + * @param[in] hdr boot image header pointer + * + * @return FIH_SUCCESS on success, error code otherwise + */ +fih_ret +boot_image_validate(const struct flash_area *fa_p, + struct image_header *hdr) +{ + static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + /* NOTE: The first argument to boot_image_validate, for enc_state pointer, + * is allowed to be NULL only because the single image loader compiles + * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses + * the pointer from compilation. + */ + /* Validate hash */ + if (IS_ENCRYPTED(hdr)) + { + /* Clear the encrypted flag we didn't supply a key + * This flag could be set if there was a decryption in place + * was performed. We will try to validate the image, and if still + * encrypted the validation will fail, and go in panic mode + */ + hdr->ih_flags &= ~(ENCRYPTIONFLAGS); + } + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, hdr, fa_p, tmpbuf, + BOOT_TMPBUF_SZ, NULL, 0, NULL); + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/ + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) +inline static fih_ret +boot_image_validate_once(const struct flash_area *fa_p, + struct image_header *hdr) +{ + static struct boot_swap_state state; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + memset(&state, 0, sizeof(struct boot_swap_state)); + rc = boot_read_swap_state(fa_p, &state); + if (rc != 0) + FIH_RET(FIH_FAILURE); + if (state.magic != BOOT_MAGIC_GOOD + || state.image_ok != BOOT_FLAG_SET) { + /* At least validate the image once */ + FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(FIH_FAILURE); + } + if (state.magic != BOOT_MAGIC_GOOD) { + rc = boot_write_magic(fa_p); + if (rc != 0) + FIH_RET(FIH_FAILURE); + } + rc = boot_write_image_ok(fa_p); + if (rc != 0) + FIH_RET(FIH_FAILURE); + } + FIH_RET(FIH_SUCCESS); +} +#endif + +/** + * Gather information on image and prepare for booting. + * + * @parami[out] rsp Parameters for booting image, on success + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + int rc = -1; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p); + assert(rc == 0); + + rc = boot_image_load_header(_fa_p, &_hdr); + if (rc != 0) + goto out; + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT + FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto out; + } +#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) + FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto out; + } +#else + fih_rc = FIH_SUCCESS; +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ + + rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p); + rsp->br_image_off = flash_area_get_off(_fa_p); + rsp->br_hdr = &_hdr; + +out: + flash_area_close(_fa_p); + + FIH_RET(fih_rc); +} diff --git a/bootloader/mcuboot/boot/mynewt/syscfg.yml b/bootloader/mcuboot/boot/mynewt/syscfg.yml new file mode 100644 index 0000000..b4fdb77 --- /dev/null +++ b/bootloader/mcuboot/boot/mynewt/syscfg.yml @@ -0,0 +1,47 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Package: boot/mynewt + +syscfg.defs: + BOOT_LOADER: + description: 'Set to indicate that this app is a bootloader.' + value: 1 + BOOT_SERIAL: + description: 'Support image upgrade over serial within bootloader' + value: 0 + BOOT_CUSTOM_START: + description: 'Override hal_system_start with a custom start routine' + value: + BOOT_PREBOOT: + description: 'Call boot_preboot() function before booting application' + value: + BOOT_MYNEWT_SYSINIT: + description: > + When not 0 performs device initialization and calls newt + generated sysinit() function. + Note: this functionality is implicitly turned on when one of the + following settings are not 0: + MCUBOOT_SERIAL, MCUBOOT_HAVE_LOGGING, CRYPTO, HASH + value: 0 + +syscfg.vals: + SYSINIT_CONSTRAIN_INIT: 0 + OS_SCHEDULING: 0 + MSYS_1_BLOCK_COUNT: 0 + CONSOLE_COMPAT: 1 diff --git a/bootloader/mcuboot/boot/nuttx/include/flash_map_backend/flash_map_backend.h b/bootloader/mcuboot/boot/nuttx/include/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000..fb29f40 --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/include/flash_map_backend/flash_map_backend.h @@ -0,0 +1,449 @@ +/**************************************************************************** + * boot/nuttx/include/flash_map_backend/flash_map_backend.h + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __BOOT_NUTTX_INCLUDE_FLASH_MAP_BACKEND_FLASH_MAP_BACKEND_H +#define __BOOT_NUTTX_INCLUDE_FLASH_MAP_BACKEND_FLASH_MAP_BACKEND_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Structure describing a flash area. */ + +struct flash_area +{ + /* MCUboot-API fields */ + + uint8_t fa_id; /* The slot/scratch ID */ + uint8_t fa_device_id; /* The device ID (usually there's only one) */ + uint16_t pad16; /* Padding */ + uint32_t fa_off; /* The flash offset from the beginning */ + uint32_t fa_size; /* The size of this sector */ + + /* NuttX implementation-specific fields */ + + const char *fa_mtd_path; /* Path for the MTD partition */ +}; + +/* Structure describing a sector within a flash area. */ + +struct flash_sector +{ + /* Offset of this sector, from the start of its flash area (not device). */ + + uint32_t fs_off; + + /* Size of this sector, in bytes. */ + + uint32_t fs_size; +}; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: flash_area_get_id + * + * Description: + * Obtain the ID of a given flash area. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The ID of the requested flash area. + * + ****************************************************************************/ + +static inline uint8_t flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +/**************************************************************************** + * Name: flash_area_get_device_id + * + * Description: + * Obtain the ID of the device in which a given flash area resides on. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The device ID of the requested flash area. + * + ****************************************************************************/ + +static inline uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + return fa->fa_device_id; +} + +/**************************************************************************** + * Name: flash_area_get_sector + * + * Description: + * Retrieve the flash sector a given offset belongs to. + * + * Input Parameters: + * fap - flash area structure + * off - address offset. + * sector - flash sector + * + * Returned Value: + * Returns 0 on success, or an error code on failure. + * + ****************************************************************************/ + +int flash_area_get_sector(const struct flash_area *fap, off_t off, + struct flash_sector *fs); + +/**************************************************************************** + * Name: flash_area_get_off + * + * Description: + * Obtain the offset, from the beginning of a device, where a given flash + * area starts at. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The offset value of the requested flash area. + * + ****************************************************************************/ + +static inline uint32_t flash_area_get_off(const struct flash_area *fa) +{ + return fa->fa_off; +} + +/**************************************************************************** + * Name: flash_area_get_size + * + * Description: + * Obtain the size, from the offset, of a given flash area. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The size value of the requested flash area. + * + ****************************************************************************/ + +static inline uint32_t flash_area_get_size(const struct flash_area *fa) +{ + return fa->fa_size; +} + +/**************************************************************************** + * Name: flash_sector_get_off + * + * Description: + * Obtain the offset, from the beginning of its flash area, where a given + * flash sector starts at. + * + * Input Parameters: + * fs - Flash sector. + * + * Returned Value: + * The offset value of the requested flash sector. + * + ****************************************************************************/ + +static inline uint32_t flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +/**************************************************************************** + * Name: flash_sector_get_size + * + * Description: + * Obtain the size, from the offset, of a given flash sector. + * + * Input Parameters: + * fs - Flash sector. + * + * Returned Value: + * The size in bytes of the requested flash sector. + * + ****************************************************************************/ + +static inline uint32_t flash_sector_get_size(const struct flash_sector *fs) +{ + return fs->fs_size; +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: flash_area_open + * + * Description: + * Retrieve flash area from the flash map for a given partition. + * + * Input Parameters: + * id - ID of the flash partition. + * + * Output Parameters: + * fa - Pointer which will contain the reference to flash_area. + * If ID is unknown, it will be NULL on output. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_open(uint8_t id, const struct flash_area **fa); + +/**************************************************************************** + * Name: flash_area_close + * + * Description: + * Close a given flash area. + * + * Input Parameters: + * fa - Flash area to be closed. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void flash_area_close(const struct flash_area *fa); + +/**************************************************************************** + * Name: flash_area_read + * + * Description: + * Read data from flash area. + * Area readout boundaries are asserted before read request. API has the + * same limitation regarding read-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be read. + * off - Offset relative from beginning of flash area to be read. + * len - Number of bytes to read. + * + * Output Parameters: + * dst - Buffer to store read data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_read(const struct flash_area *fa, uint32_t off, + void *dst, uint32_t len); + +/**************************************************************************** + * Name: flash_area_write + * + * Description: + * Write data to flash area. + * Area write boundaries are asserted before write request. API has the + * same limitation regarding write-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be written. + * off - Offset relative from beginning of flash area to be written. + * src - Buffer with data to be written. + * len - Number of bytes to write. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_write(const struct flash_area *fa, uint32_t off, + const void *src, uint32_t len); + +/**************************************************************************** + * Name: flash_area_erase + * + * Description: + * Erase a given flash area range. + * Area boundaries are asserted before erase request. API has the same + * limitation regarding erase-block alignment and size as the underlying + * flash driver. + * + * Input Parameters: + * fa - Flash area to be erased. + * off - Offset relative from beginning of flash area to be erased. + * len - Number of bytes to be erase. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_erase(const struct flash_area *fa, uint32_t off, + uint32_t len); + +/**************************************************************************** + * Name: flash_area_align + * + * Description: + * Get write block size of the flash area. + * Write block size might be treated as read block size, although most + * drivers support unaligned readout. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Alignment restriction for flash writes in the given flash area. + * + ****************************************************************************/ + +uint32_t flash_area_align(const struct flash_area *fa); + +/**************************************************************************** + * Name: flash_area_erased_val + * + * Description: + * Get the value expected to be read when accessing any erased flash byte. + * This API is compatible with the MCUboot's porting layer. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Byte value of erased memory. + * + ****************************************************************************/ + +uint8_t flash_area_erased_val(const struct flash_area *fa); + +/**************************************************************************** + * Name: flash_area_get_sectors + * + * Description: + * Retrieve info about sectors within the area. + * + * Input Parameters: + * fa_id - ID of the flash area whose info will be retrieved. + * count - On input, represents the capacity of the sectors buffer. + * + * Output Parameters: + * count - On output, it shall contain the number of retrieved sectors. + * sectors - Buffer for sectors data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors); + +/**************************************************************************** + * Name: flash_area_id_from_multi_image_slot + * + * Description: + * Return the flash area ID for a given slot and a given image index + * (in case of a multi-image setup). + * + * Input Parameters: + * image_index - Index of the image. + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int flash_area_id_from_multi_image_slot(int image_index, int slot); + +/**************************************************************************** + * Name: flash_area_id_from_image_slot + * + * Description: + * Return the flash area ID for a given slot. + * + * Input Parameters: + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int flash_area_id_from_image_slot(int slot); + +/**************************************************************************** + * Name: flash_area_id_to_multi_image_slot + * + * Description: + * Convert the specified flash area ID and image index (in case of a + * multi-image setup) to an image slot index. + * + * Input Parameters: + * image_index - Index of the image. + * area_id - Unique identifier that is represented by fa_id in the + * flash_area struct. + * Returned Value: + * Image slot index (0 or 1), or negative value in case ID doesn't + * correspond to an image slot. + * + ****************************************************************************/ + +int flash_area_id_to_multi_image_slot(int image_index, int area_id); + +/**************************************************************************** + * Name: flash_area_id_from_image_offset + * + * Description: + * Return the flash area ID for a given image offset. + * + * Input Parameters: + * offset - Image offset. + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested offset + * is invalid. + * + ****************************************************************************/ + +int flash_area_id_from_image_offset(uint32_t offset); + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOT_NUTTX_INCLUDE_FLASH_MAP_BACKEND_FLASH_MAP_BACKEND_H */ diff --git a/bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_config.h b/bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000..41e7d67 --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_config.h @@ -0,0 +1,212 @@ +/**************************************************************************** + * boot/nuttx/include/mcuboot_config/mcuboot_config.h + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __BOOT_NUTTX_INCLUDE_MCUBOOT_CONFIG_MCUBOOT_CONFIG_H +#define __BOOT_NUTTX_INCLUDE_MCUBOOT_CONFIG_MCUBOOT_CONFIG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_MCUBOOT_WATCHDOG +# include "watchdog/watchdog.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Signature types + * + * You must choose exactly one signature type. + */ + +/* Uncomment for RSA signature support */ + +/* #define MCUBOOT_SIGN_RSA */ + +/* Uncomment for ECDSA signatures using curve P-256. */ + +/* #define MCUBOOT_SIGN_EC256 */ + +/* Upgrade mode + * + * The default is to support A/B image swapping with rollback. Other modes + * with simpler code path, which only supports overwriting the existing image + * with the update image or running the newest image directly from its flash + * partition, are also available. + * + * You can enable only one mode at a time from the list below to override + * the default upgrade mode. + */ + +/* Use image swap without using scratch area.*/ + +#ifdef CONFIG_MCUBOOT_SWAP_USING_MOVE +# define MCUBOOT_SWAP_USING_MOVE 1 +#endif + +/* Enable the overwrite-only code path. */ + +#ifdef CONFIG_MCUBOOT_OVERWRITE_ONLY +# define MCUBOOT_OVERWRITE_ONLY +#endif + +/* Only erase and overwrite those primary slot sectors needed + * to install the new image, rather than the entire image slot. + */ + +#ifdef CONFIG_MCUBOOT_OVERWRITE_ONLY_FAST +# define MCUBOOT_OVERWRITE_ONLY_FAST +#endif + +/* Enable the direct-xip code path. */ + +#ifdef CONFIG_MCUBOOT_DIRECT_XIP +# define MCUBOOT_DIRECT_XIP +#endif + +/* Enable the revert mechanism in direct-xip mode. */ + +#ifdef CONFIG_MCUBOOT_DIRECT_XIP_REVERT +# define MCUBOOT_DIRECT_XIP_REVERT +#endif + +/* Enable the ram-load code path. */ + +#ifdef CONFIG_MCUBOOT_RAM_LOAD +# define MCUBOOT_RAM_LOAD +#endif + +/* Enable bootstrapping the erased primary slot from the secondary slot */ + +#ifdef CONFIG_MCUBOOT_BOOTSTRAP +# define MCUBOOT_BOOTSTRAP +#endif + +/* Cryptographic settings + * + * You must choose between mbedTLS and Tinycrypt as source of + * cryptographic primitives. Other cryptographic settings are also + * available. + */ + +#ifdef CONFIG_MCUBOOT_USE_MBED_TLS +# define MCUBOOT_USE_MBED_TLS +#endif + +#ifdef CONFIG_MCUBOOT_USE_TINYCRYPT +# define MCUBOOT_USE_TINYCRYPT +#endif + +/* Always check the signature of the image in the primary slot before + * booting, even if no upgrade was performed. This is recommended if the boot + * time penalty is acceptable. + */ + +#define MCUBOOT_VALIDATE_PRIMARY_SLOT + +/* Flash abstraction */ + +/* Uncomment if your flash map API supports flash_area_get_sectors(). + * See the flash APIs for more details. + */ + +#define MCUBOOT_USE_FLASH_AREA_GET_SECTORS + +/* Default maximum number of flash sectors per image slot; change + * as desirable. + */ + +#define MCUBOOT_MAX_IMG_SECTORS 512 + +/* Default number of separately updateable images; change in case of + * multiple images. + */ + +#define MCUBOOT_IMAGE_NUMBER 1 + +/* Logging */ + +/* If logging is enabled the following functions must be defined by the + * platform: + * + * MCUBOOT_LOG_MODULE_REGISTER(domain) + * Register a new log module and add the current C file to it. + * + * MCUBOOT_LOG_MODULE_DECLARE(domain) + * Add the current C file to an existing log module. + * + * MCUBOOT_LOG_ERR(...) + * MCUBOOT_LOG_WRN(...) + * MCUBOOT_LOG_INF(...) + * MCUBOOT_LOG_DBG(...) + * + * The function priority is: + * + * MCUBOOT_LOG_ERR > MCUBOOT_LOG_WRN > MCUBOOT_LOG_INF > MCUBOOT_LOG_DBG + */ + +#ifdef CONFIG_MCUBOOT_ENABLE_LOGGING +# define MCUBOOT_HAVE_LOGGING +#endif + +/* Assertions */ + +/* Uncomment if your platform has its own mcuboot_config/mcuboot_assert.h. + * If so, it must provide an ASSERT macro for use by bootutil. Otherwise, + * "assert" is used. + */ + +/* #define MCUBOOT_HAVE_ASSERT_H 1 */ + +/* Watchdog feeding */ + +/* This macro might be implemented if the OS / HW watchdog is enabled while + * doing a swap upgrade and the time it takes for a swapping is long enough + * to cause an unwanted reset. If implementing this, the OS main.c must also + * enable the watchdog (if required)! + */ + +#ifdef CONFIG_MCUBOOT_WATCHDOG + +#ifndef CONFIG_MCUBOOT_WATCHDOG_DEVPATH +# define CONFIG_MCUBOOT_WATCHDOG_DEVPATH "/dev/watchdog0" +#endif + +#ifndef CONFIG_MCUBOOT_WATCHDOG_TIMEOUT +# define CONFIG_MCUBOOT_WATCHDOG_TIMEOUT 10000 /* Watchdog timeout in ms */ +#endif + +# define MCUBOOT_WATCHDOG_FEED() do \ + { \ + mcuboot_watchdog_feed(); \ + } \ + while (0) + +#else +# define MCUBOOT_WATCHDOG_FEED() do \ + { \ + } \ + while (0) +#endif + +#endif /* __BOOT_NUTTX_INCLUDE_MCUBOOT_CONFIG_MCUBOOT_CONFIG_H */ diff --git a/bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_logging.h b/bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000..a2ccce0 --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/include/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * boot/nuttx/include/mcuboot_config/mcuboot_logging.h + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __BOOT_NUTTX_INCLUDE_MCUBOOT_CONFIG_MCUBOOT_LOGGING_H +#define __BOOT_NUTTX_INCLUDE_MCUBOOT_CONFIG_MCUBOOT_LOGGING_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MCUBOOT_LOG_MODULE_DECLARE(...) +#define MCUBOOT_LOG_MODULE_REGISTER(...) + +#define MCUBOOT_LOG_ERR(format, ...) \ + syslog(LOG_ERR, "%s: " format "\n", __FUNCTION__, ##__VA_ARGS__) + +#define MCUBOOT_LOG_WRN(format, ...) \ + syslog(LOG_WARNING, "%s: " format "\n", __FUNCTION__, ##__VA_ARGS__) + +#define MCUBOOT_LOG_INF(format, ...) \ + syslog(LOG_INFO, "%s: " format "\n", __FUNCTION__, ##__VA_ARGS__) + +#define MCUBOOT_LOG_DBG(format, ...) \ + syslog(LOG_DEBUG, "%s: " format "\n", __FUNCTION__, ##__VA_ARGS__) + +#endif /* __BOOT_NUTTX_INCLUDE_MCUBOOT_CONFIG_MCUBOOT_LOGGING_H */ diff --git a/bootloader/mcuboot/boot/nuttx/include/os/os_malloc.h b/bootloader/mcuboot/boot/nuttx/include/os/os_malloc.h new file mode 100644 index 0000000..1fbefad --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/include/os/os_malloc.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * boot/nuttx/include/os/os_malloc.h + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __BOOT_NUTTX_INCLUDE_OS_OS_MALLOC_H +#define __BOOT_NUTTX_INCLUDE_OS_OS_MALLOC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#endif /* __BOOT_NUTTX_INCLUDE_OS_OS_MALLOC_H */ diff --git a/bootloader/mcuboot/boot/nuttx/include/sysflash/sysflash.h b/bootloader/mcuboot/boot/nuttx/include/sysflash/sysflash.h new file mode 100644 index 0000000..304ca0c --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/include/sysflash/sysflash.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * boot/nuttx/include/sysflash/sysflash.h + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __BOOT_NUTTX_INCLUDE_SYSFLASH_SYSFLASH_H +#define __BOOT_NUTTX_INCLUDE_SYSFLASH_SYSFLASH_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PRIMARY_ID 0 +#define SECONDARY_ID 1 +#define SCRATCH_ID 2 + +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + PRIMARY_ID : \ + PRIMARY_ID) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + SECONDARY_ID : \ + SECONDARY_ID) +#define FLASH_AREA_IMAGE_SCRATCH SCRATCH_ID + +#endif /* __BOOT_NUTTX_INCLUDE_SYSFLASH_SYSFLASH_H */ diff --git a/bootloader/mcuboot/boot/nuttx/include/watchdog/watchdog.h b/bootloader/mcuboot/boot/nuttx/include/watchdog/watchdog.h new file mode 100644 index 0000000..b75b7f6 --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/include/watchdog/watchdog.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * boot/nuttx/include/watchdog/watchdog.h + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +#ifndef __BOOT_NUTTX_INCLUDE_WATCHDOG_WATCHDOG_H +#define __BOOT_NUTTX_INCLUDE_WATCHDOG_WATCHDOG_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mcuboot_watchdog_feed + * + * Description: + * Reset the watchdog timer to the current timeout value, preventing any + * imminent watchdog timeouts. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void mcuboot_watchdog_feed(void); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mcuboot_watchdog_init + * + * Description: + * Initialize the watchdog timer by setting the trigger timeout and + * starting it. + * + * Input Parameters: + * None. + * + * Returned Value: + * OK on success, ERROR if not. + * + ****************************************************************************/ + +int mcuboot_watchdog_init(void); + +#endif /* __BOOT_NUTTX_INCLUDE_WATCHDOG_WATCHDOG_H */ diff --git a/bootloader/mcuboot/boot/nuttx/main.c b/bootloader/mcuboot/boot/nuttx/main.c new file mode 100644 index 0000000..4460546 --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/main.c @@ -0,0 +1,137 @@ +/**************************************************************************** + * boot/nuttx/main.c + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include +#include + +#include "flash_map_backend/flash_map_backend.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Should we perform board-specific driver initialization? There are two + * ways that board initialization can occur: 1) automatically via + * board_late_initialize() during bootupif CONFIG_BOARD_LATE_INITIALIZE + * or 2). + * via a call to boardctl() if the interface is enabled + * (CONFIG_BOARDCTL=y). + * If this task is running as an NSH built-in application, then that + * initialization has probably already been performed otherwise we do it + * here. + */ + +#undef NEED_BOARDINIT + +#if defined(CONFIG_BOARDCTL) && !defined(CONFIG_NSH_ARCHINIT) +# define NEED_BOARDINIT 1 +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * do_boot + ****************************************************************************/ + +static void do_boot(struct boot_rsp *rsp) +{ + const struct flash_area *flash_area; + struct boardioc_boot_info_s info; + int area_id; + int ret; + + area_id = flash_area_id_from_image_offset(rsp->br_image_off); + + ret = flash_area_open(area_id, &flash_area); + assert(ret == OK); + + syslog(LOG_INFO, "Booting from %s...\n", flash_area->fa_mtd_path); + + info.path = flash_area->fa_mtd_path; + info.header_size = rsp->br_hdr->ih_hdr_size; + + flash_area_close(flash_area); + + if (boardctl(BOARDIOC_BOOT_IMAGE, (uintptr_t)&info) != OK) + { + syslog(LOG_ERR, "Failed to load application image!\n"); + FIH_PANIC; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct boot_rsp rsp; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#ifdef NEED_BOARDINIT + /* Perform architecture-specific initialization (if configured) */ + + boardctl(BOARDIOC_INIT, 0); + +#ifdef CONFIG_BOARDCTL_FINALINIT + /* Perform architecture-specific final-initialization (if configured) */ + + boardctl(BOARDIOC_FINALINIT, 0); +#endif +#endif + + syslog(LOG_INFO, "*** Booting MCUboot build %s ***\n", CONFIG_MCUBOOT_VERSION); + +#ifdef CONFIG_MCUBOOT_WATCHDOG + int ret = mcuboot_watchdog_init(); + if (ret < 0) + { + syslog(LOG_ERR, "Unable to initialize the watchdog timer\n"); + FIH_PANIC; + } +#endif + + FIH_CALL(boot_go, fih_rc, &rsp); + + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) + { + syslog(LOG_ERR, "Unable to find bootable image\n"); + FIH_PANIC; + } + + do_boot(&rsp); + + while (1); +} diff --git a/bootloader/mcuboot/boot/nuttx/src/flash_map_backend/flash_map_backend.c b/bootloader/mcuboot/boot/nuttx/src/flash_map_backend/flash_map_backend.c new file mode 100644 index 0000000..ae3ec64 --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/src/flash_map_backend/flash_map_backend.c @@ -0,0 +1,844 @@ +/**************************************************************************** + * boot/nuttx/src/flash_map_backend/flash_map_backend.c + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "flash_map_backend/flash_map_backend.h" +#include "os/os_malloc.h" +#include "sysflash/sysflash.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0])) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct flash_device_s +{ + /* Reference to the flash area configuration parameters */ + + struct flash_area *fa_cfg; + + /* Geometry characteristics of the underlying MTD device */ + + struct mtd_geometry_s mtdgeo; + + /* Partition information */ + + struct partition_info_s partinfo; + + int fd; /* File descriptor for an open flash area */ + uint32_t refs; /* Reference counter */ + uint8_t erase_state; /* Byte value of the flash erased state */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct flash_area g_primary_img0 = +{ + .fa_id = FLASH_AREA_IMAGE_PRIMARY(0), + .fa_device_id = 0, + .fa_off = 0, + .fa_size = 0, + .fa_mtd_path = CONFIG_MCUBOOT_PRIMARY_SLOT_PATH +}; + +static struct flash_device_s g_primary_priv = +{ + .fa_cfg = &g_primary_img0, + .mtdgeo = + { + 0 + }, + .partinfo = + { + 0 + }, + .fd = -1, + .refs = 0, + .erase_state = CONFIG_MCUBOOT_DEFAULT_FLASH_ERASE_STATE +}; + +static struct flash_area g_secondary_img0 = +{ + .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), + .fa_device_id = 0, + .fa_off = 0, + .fa_size = 0, + .fa_mtd_path = CONFIG_MCUBOOT_SECONDARY_SLOT_PATH +}; + +static struct flash_device_s g_secondary_priv = +{ + .fa_cfg = &g_secondary_img0, + .mtdgeo = + { + 0 + }, + .partinfo = + { + 0 + }, + .fd = -1, + .refs = 0, + .erase_state = CONFIG_MCUBOOT_DEFAULT_FLASH_ERASE_STATE +}; + +static struct flash_area g_scratch_img0 = +{ + .fa_id = FLASH_AREA_IMAGE_SCRATCH, + .fa_device_id = 0, + .fa_off = 0, + .fa_size = 0, + .fa_mtd_path = CONFIG_MCUBOOT_SCRATCH_PATH +}; + +static struct flash_device_s g_scratch_priv = +{ + .fa_cfg = &g_scratch_img0, + .mtdgeo = + { + 0 + }, + .partinfo = + { + 0 + }, + .fd = -1, + .refs = 0, + .erase_state = CONFIG_MCUBOOT_DEFAULT_FLASH_ERASE_STATE +}; + +static struct flash_device_s *g_flash_devices[] = +{ + &g_primary_priv, + &g_secondary_priv, + &g_scratch_priv, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lookup_flash_device_by_id + * + * Description: + * Retrieve flash device from a given flash area ID. + * + * Input Parameters: + * fa_id - ID of the flash area. + * + * Returned Value: + * Reference to the found flash device, or NULL in case it does not exist. + * + ****************************************************************************/ + +static struct flash_device_s *lookup_flash_device_by_id(uint8_t fa_id) +{ + size_t i; + + for (i = 0; i < ARRAYSIZE(g_flash_devices); i++) + { + struct flash_device_s *dev = g_flash_devices[i]; + + if (fa_id == dev->fa_cfg->fa_id) + { + return dev; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: lookup_flash_device_by_offset + * + * Description: + * Retrieve flash device from a given flash area offset. + * + * Input Parameters: + * offset - Offset of the flash area. + * + * Returned Value: + * Reference to the found flash device, or NULL in case it does not exist. + * + ****************************************************************************/ + +static struct flash_device_s *lookup_flash_device_by_offset(uint32_t offset) +{ + size_t i; + + for (i = 0; i < ARRAYSIZE(g_flash_devices); i++) + { + struct flash_device_s *dev = g_flash_devices[i]; + + if (offset == dev->fa_cfg->fa_off) + { + return dev; + } + } + + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: flash_area_open + * + * Description: + * Retrieve flash area from the flash map for a given ID. + * + * Input Parameters: + * id - ID of the flash area. + * + * Output Parameters: + * fa - Pointer which will contain the reference to flash_area. + * If ID is unknown, it will be NULL on output. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_open(uint8_t id, const struct flash_area **fa) +{ + struct flash_device_s *dev; + int fd; + int ret; + + BOOT_LOG_INF("ID:%" PRIu8, id); + + dev = lookup_flash_device_by_id(id); + if (dev == NULL) + { + BOOT_LOG_ERR("Undefined flash area: %" PRIu8, id); + + return ERROR; + } + + *fa = dev->fa_cfg; + + if (dev->refs++ > 0) + { + BOOT_LOG_INF("Flash area ID %" PRIu8 " already open, count: %" PRIu32 " (+)", + id, dev->refs); + + return OK; + } + + fd = open(dev->fa_cfg->fa_mtd_path, O_RDWR); + if (fd < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Error opening MTD device: %d", errcode); + + goto errout; + } + + ret = ioctl(fd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&dev->mtdgeo)); + if (ret < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Error retrieving MTD device geometry: %d", errcode); + + goto errout_with_fd; + } + + ret = ioctl(fd, BIOC_PARTINFO, (unsigned long)((uintptr_t)&dev->partinfo)); + if (ret < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Error retrieving MTD partition info: %d", errcode); + + goto errout_with_fd; + } + + ret = ioctl(fd, MTDIOC_ERASESTATE, + (unsigned long)((uintptr_t)&dev->erase_state)); + if (ret < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Error retrieving MTD device erase state: %d", errcode); + + goto errout_with_fd; + } + + dev->fa_cfg->fa_off = dev->partinfo.startsector * dev->partinfo.sectorsize; + dev->fa_cfg->fa_size = dev->partinfo.numsectors * dev->partinfo.sectorsize; + + BOOT_LOG_INF("Flash area offset: 0x%" PRIx32, dev->fa_cfg->fa_off); + BOOT_LOG_INF("Flash area size: %" PRIu32, dev->fa_cfg->fa_size); + BOOT_LOG_INF("MTD erase state: 0x%" PRIx8, dev->erase_state); + + dev->fd = fd; + + BOOT_LOG_INF("Flash area %" PRIu8 " open, count: %" PRIu32 " (+)", id, dev->refs); + + return OK; + +errout_with_fd: + close(fd); + +errout: + --dev->refs; + + return ERROR; +} + +/**************************************************************************** + * Name: flash_area_close + * + * Description: + * Close a given flash area. + * + * Input Parameters: + * fa - Flash area to be closed. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void flash_area_close(const struct flash_area *fa) +{ + BOOT_LOG_INF("ID:%" PRIu8, fa->fa_id); + + struct flash_device_s *dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + if (dev->refs == 0) + { + /* No need to close an unopened flash area, avoid an overflow of the + * counter. + */ + + return; + } + + BOOT_LOG_INF("Close request for flash area %" PRIu8 ", count: %" PRIu32 " (-)", + fa->fa_id, dev->refs); + + if (--dev->refs == 0) + { + close(dev->fd); + dev->fd = -1; + + BOOT_LOG_INF("Flash area %" PRIu8 " closed", fa->fa_id); + } +} + +/**************************************************************************** + * Name: flash_area_read + * + * Description: + * Read data from flash area. + * Area readout boundaries are asserted before read request. API has the + * same limitation regarding read-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be read. + * off - Offset relative from beginning of flash area to be read. + * len - Number of bytes to read. + * + * Output Parameters: + * dst - Buffer to store read data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_read(const struct flash_area *fa, uint32_t off, + void *dst, uint32_t len) +{ + struct flash_device_s *dev; + off_t seekpos; + ssize_t nbytes; + + BOOT_LOG_INF("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32, + fa->fa_id, off, len); + + dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + if (off + len > fa->fa_size) + { + BOOT_LOG_ERR("Attempt to read out of flash area bounds"); + + return ERROR; + } + + /* Reposition the file offset from the beginning of the flash area */ + + seekpos = lseek(dev->fd, (off_t)off, SEEK_SET); + if (seekpos != (off_t)off) + { + int errcode = errno; + + BOOT_LOG_ERR("Seek to offset %" PRIu32 " failed: %d", off, errcode); + + return ERROR; + } + + /* Read the flash block into memory */ + + nbytes = read(dev->fd, dst, len); + if (nbytes < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Read from %s failed: %d", fa->fa_mtd_path, errcode); + + return ERROR; + } + + return OK; +} + +/**************************************************************************** + * Name: flash_area_write + * + * Description: + * Write data to flash area. + * Area write boundaries are asserted before write request. API has the + * same limitation regarding write-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be written. + * off - Offset relative from beginning of flash area to be written. + * src - Buffer with data to be written. + * len - Number of bytes to write. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_write(const struct flash_area *fa, uint32_t off, + const void *src, uint32_t len) +{ + struct flash_device_s *dev; + off_t seekpos; + ssize_t nbytes; + + BOOT_LOG_INF("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32, + fa->fa_id, off, len); + + dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + if (off + len > fa->fa_size) + { + BOOT_LOG_ERR("Attempt to write out of flash area bounds"); + + return ERROR; + } + + /* Reposition the file offset from the beginning of the flash area */ + + seekpos = lseek(dev->fd, (off_t)off, SEEK_SET); + if (seekpos != (off_t)off) + { + int errcode = errno; + + BOOT_LOG_ERR("Seek to offset %" PRIu32 " failed: %d", off, errcode); + + return ERROR; + } + + /* Write the buffer to the flash block */ + + nbytes = write(dev->fd, src, len); + if (nbytes < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Write to %s failed: %d", fa->fa_mtd_path, errcode); + + return ERROR; + } + + return OK; +} + +/**************************************************************************** + * Name: flash_area_erase + * + * Description: + * Erase a given flash area range. + * Area boundaries are asserted before erase request. API has the same + * limitation regarding erase-block alignment and size as the underlying + * flash driver. + * + * Input Parameters: + * fa - Flash area to be erased. + * off - Offset relative from beginning of flash area to be erased. + * len - Number of bytes to be erase. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len) +{ + int ret; + void *buffer; + size_t i; + struct flash_device_s *dev = lookup_flash_device_by_id(fa->fa_id); + const size_t sector_size = dev->mtdgeo.erasesize; + const uint8_t erase_val = dev->erase_state; + + BOOT_LOG_INF("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32, + fa->fa_id, off, len); + + buffer = malloc(sector_size); + if (buffer == NULL) + { + BOOT_LOG_ERR("Failed to allocate erase buffer"); + + return ERROR; + } + + memset(buffer, erase_val, sector_size); + + i = 0; + + do + { + BOOT_LOG_DBG("Erasing %zu bytes at offset %" PRIu32, + sector_size, off + i); + + ret = flash_area_write(fa, off + i, buffer, sector_size); + i += sector_size; + } + while (ret == OK && i < (len - sector_size)); + + if (ret == OK) + { + BOOT_LOG_DBG("Erasing %" PRIu32 " bytes at offset %" PRIu32, + len - i, off + i); + + ret = flash_area_write(fa, off + i, buffer, len - i); + } + + free(buffer); + + return ret; +} + +/**************************************************************************** + * Name: flash_area_align + * + * Description: + * Get write block size of the flash area. + * Write block size might be treated as read block size, although most + * drivers support unaligned readout. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Alignment restriction for flash writes in the given flash area. + * + ****************************************************************************/ + +uint32_t flash_area_align(const struct flash_area *fa) +{ + /* MTD access alignment is handled by the character and block device + * drivers. + */ + + const uint32_t minimum_write_length = 1; + + BOOT_LOG_INF("ID:%" PRIu8 " align:%" PRIu32, + fa->fa_id, minimum_write_length); + + return minimum_write_length; +} + +/**************************************************************************** + * Name: flash_area_erased_val + * + * Description: + * Get the value expected to be read when accessing any erased flash byte. + * This API is compatible with the MCUboot's porting layer. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Byte value of erased memory. + * + ****************************************************************************/ + +uint8_t flash_area_erased_val(const struct flash_area *fa) +{ + struct flash_device_s *dev; + uint8_t erased_val; + + dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + erased_val = dev->erase_state; + + BOOT_LOG_INF("ID:%" PRIu8 " erased_val:0x%" PRIx8, fa->fa_id, erased_val); + + return erased_val; +} + +/**************************************************************************** + * Name: flash_area_get_sectors + * + * Description: + * Retrieve info about sectors within the area. + * + * Input Parameters: + * fa_id - ID of the flash area whose info will be retrieved. + * count - On input, represents the capacity of the sectors buffer. + * + * Output Parameters: + * count - On output, it shall contain the number of retrieved sectors. + * sectors - Buffer for sectors data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors) +{ + size_t off; + uint32_t total_count = 0; + struct flash_device_s *dev = lookup_flash_device_by_id(fa_id); + const size_t sector_size = dev->mtdgeo.erasesize; + const struct flash_area *fa = fa = dev->fa_cfg; + + for (off = 0; off < fa->fa_size; off += sector_size) + { + /* Note: Offset here is relative to flash area, not device */ + + sectors[total_count].fs_off = off; + sectors[total_count].fs_size = sector_size; + total_count++; + } + + *count = total_count; + + DEBUGASSERT(total_count == dev->mtdgeo.neraseblocks); + + BOOT_LOG_INF("ID:%d count:%" PRIu32, fa_id, *count); + + return OK; +} + +/**************************************************************************** + * Name: flash_area_id_from_multi_image_slot + * + * Description: + * Return the flash area ID for a given slot and a given image index + * (in case of a multi-image setup). + * + * Input Parameters: + * image_index - Index of the image. + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + BOOT_LOG_INF("image_index:%d slot:%d", image_index, slot); + + switch (slot) + { + case 0: + return FLASH_AREA_IMAGE_PRIMARY(image_index); + case 1: + return FLASH_AREA_IMAGE_SECONDARY(image_index); + } + + BOOT_LOG_ERR("Unexpected Request: image_index:%d, slot:%d", + image_index, slot); + + return ERROR; /* flash_area_open will fail on that */ +} + +/**************************************************************************** + * Name: flash_area_id_from_image_slot + * + * Description: + * Return the flash area ID for a given slot. + * + * Input Parameters: + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int flash_area_id_from_image_slot(int slot) +{ + BOOT_LOG_INF("slot:%d", slot); + + return flash_area_id_from_multi_image_slot(0, slot); +} + +/**************************************************************************** + * Name: flash_area_id_to_multi_image_slot + * + * Description: + * Convert the specified flash area ID and image index (in case of a + * multi-image setup) to an image slot index. + * + * Input Parameters: + * image_index - Index of the image. + * area_id - Unique identifier that is represented by fa_id in the + * flash_area struct. + * Returned Value: + * Image slot index (0 or 1), or negative value in case ID doesn't + * correspond to an image slot. + * + ****************************************************************************/ + +int flash_area_id_to_multi_image_slot(int image_index, int area_id) +{ + BOOT_LOG_INF("image_index:%d area_id:%d", image_index, area_id); + + if (area_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) + { + return 0; + } + + if (area_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) + { + return 1; + } + + BOOT_LOG_ERR("Unexpected Request: image_index:%d, area_id:%d", + image_index, area_id); + + return ERROR; /* flash_area_open will fail on that */ +} + +/**************************************************************************** + * Name: flash_area_id_from_image_offset + * + * Description: + * Return the flash area ID for a given image offset. + * + * Input Parameters: + * offset - Image offset. + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested offset + * is invalid. + * + ****************************************************************************/ + +int flash_area_id_from_image_offset(uint32_t offset) +{ + struct flash_device_s *dev = lookup_flash_device_by_offset(offset); + + BOOT_LOG_INF("offset:%" PRIu32, offset); + + if (dev != NULL) + { + return dev->fa_cfg->fa_id; + } + + BOOT_LOG_ERR("Unexpected Request: offset:%" PRIu32, offset); + + return ERROR; /* flash_area_open will fail on that */ +} + +/**************************************************************************** + * Name: flash_area_get_sector + * + * Description: + * Retrieve the flash sector a given offset belongs to. + * + * Input Parameters: + * fap - flash area structure + * off - address offset. + * sector - flash sector + * + * Returned Value: + * Returns 0 on success, or an error code on failure. + * + ****************************************************************************/ + +int flash_area_get_sector(const struct flash_area *fap, off_t off, + struct flash_sector *fs) +{ + off_t offset = fap->fa_off + off; + struct flash_device_s *dev = lookup_flash_device_by_offset(offset); + if (dev == NULL) + { + return -errno; + } + + fs->fs_off = (offset / dev->mtdgeo.erasesize) * dev->mtdgeo.erasesize; + fs->fs_size = dev->mtdgeo.erasesize; + + return 0; +} diff --git a/bootloader/mcuboot/boot/nuttx/src/watchdog/watchdog.c b/bootloader/mcuboot/boot/nuttx/src/watchdog/watchdog.c new file mode 100644 index 0000000..59f4587 --- /dev/null +++ b/bootloader/mcuboot/boot/nuttx/src/watchdog/watchdog.c @@ -0,0 +1,130 @@ +/**************************************************************************** + * boot/nuttx/src/watchdog/watchdog.c + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "watchdog/watchdog.h" + +#include +#include +#include +#include + +#include + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mcuboot_watchdog_feed + * + * Description: + * Reset the watchdog timer to the current timeout value, preventing any + * imminent watchdog timeouts. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void mcuboot_watchdog_feed(void) +{ + int fd; + int ret; + + fd = open(CONFIG_MCUBOOT_WATCHDOG_DEVPATH, O_RDONLY); + if (fd < 0) + { + BOOT_LOG_ERR("Failed to open %s", CONFIG_MCUBOOT_WATCHDOG_DEVPATH); + + return; + } + + ret = ioctl(fd, WDIOC_KEEPALIVE, 0); + if (ret < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Failed to ping watchdog device: %d", errcode); + } + + close(fd); +} + +/**************************************************************************** + * Name: mcuboot_watchdog_init + * + * Description: + * Initialize the watchdog timer by setting the trigger timeout and + * starting it. + * + * Input Parameters: + * None. + * + * Returned Value: + * OK on success, ERROR if not. + * + ****************************************************************************/ + +int mcuboot_watchdog_init(void) +{ + int fd; + int ret; + + fd = open(CONFIG_MCUBOOT_WATCHDOG_DEVPATH, O_RDONLY); + if (fd < 0) + { + BOOT_LOG_ERR("Failed to open %s", CONFIG_MCUBOOT_WATCHDOG_DEVPATH); + goto errout; + } + + ret = ioctl(fd, WDIOC_SETTIMEOUT, (unsigned long)CONFIG_MCUBOOT_WATCHDOG_TIMEOUT); + if (ret < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Failed to set timeout in watchdog device: %d", errcode); + goto errout_with_dev; + } + + ret = ioctl(fd, WDIOC_START, 0); + if (ret < 0) + { + int errcode = errno; + + BOOT_LOG_ERR("Failed to start watchdog device: %d", errcode); + goto errout_with_dev; + } + + close(fd); + return OK; + +errout_with_dev: + close(fd); +errout: + return ERROR; +} diff --git a/bootloader/mcuboot/boot/zcbor/add_zcbor_copy_version.sh b/bootloader/mcuboot/boot/zcbor/add_zcbor_copy_version.sh new file mode 100644 index 0000000..a88ebf9 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/add_zcbor_copy_version.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ "$1" == "--help" ]; then + echo "Add header if the zcbor files are updated." + exit -1 +fi + +add_copy_notice() { +echo "$(printf '/* + * This file has been %s from the zcbor library. + * Commit %s + */ + +' "$2" "$(zcbor --version)"; cat $1;)" > $1 +} + +add_copy_notice src/zcbor_decode.c "copied" +add_copy_notice src/zcbor_encode.c "copied" +add_copy_notice src/zcbor_common.c "copied" +add_copy_notice include/zcbor_decode.h "copied" +add_copy_notice include/zcbor_encode.h "copied" +add_copy_notice include/zcbor_common.h "copied" +add_copy_notice include/zcbor_print.h "copied" +add_copy_notice include/zcbor_tags.h "copied" diff --git a/bootloader/mcuboot/boot/zcbor/include/zcbor_common.h b/bootloader/mcuboot/boot/zcbor/include/zcbor_common.h new file mode 100644 index 0000000..0583ab8 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/include/zcbor_common.h @@ -0,0 +1,514 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZCBOR_COMMON_H__ +#define ZCBOR_COMMON_H__ + +#include +#include +#include +#include +#include "zcbor_tags.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZCBOR_STRINGIFY_PRE(x) #x +#define ZCBOR_STRINGIFY(s) ZCBOR_STRINGIFY_PRE(s) + +#define ZCBOR_VERSION_MAJOR 0 +#define ZCBOR_VERSION_MINOR 8 +#define ZCBOR_VERSION_BUGFIX 1 + +/** The version string with dots and not prefix. */ +#define ZCBOR_VERSION_STR ZCBOR_STRINGIFY(ZCBOR_VERSION_MAJOR) \ + "." ZCBOR_STRINGIFY(ZCBOR_VERSION_MINOR) \ + "." ZCBOR_STRINGIFY(ZCBOR_VERSION_BUGFIX) + +/** Monotonically increasing integer representing the version. */ +#define ZCBOR_VERSION ((ZCBOR_VERSION_MAJOR << 24) \ + + (ZCBOR_VERSION_MINOR << 16) \ + + (ZCBOR_VERSION_BUGFIX << 8)) + +/** Convenience type that allows pointing to strings directly inside the payload + * without the need to copy out. + */ +struct zcbor_string { + const uint8_t *value; + size_t len; +}; + + +/** Type representing a string fragment. + * + * Don't modify any member variables, or subsequent calls may fail. +**/ +struct zcbor_string_fragment { + struct zcbor_string fragment; ///! Location and length of the fragment. + size_t offset; ///! The offset in the full string at which this fragment belongs. + size_t total_len; ///! The total length of the string this fragment is a part of. +}; + + +/** Size to use in struct zcbor_string_fragment when the real size is unknown. */ +#define ZCBOR_STRING_FRAGMENT_UNKNOWN_LENGTH SIZE_MAX + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) +#endif + +#ifndef ZCBOR_ARRAY_SIZE +#define ZCBOR_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +/* Endian-dependent offset of smaller integer in a bigger one. */ +#ifdef ZCBOR_BIG_ENDIAN +#define ZCBOR_ECPY_OFFS(dst_len, src_len) ((dst_len) - (src_len)) +#else +#define ZCBOR_ECPY_OFFS(dst_len, src_len) (0) +#endif /* ZCBOR_BIG_ENDIAN */ + +#if SIZE_MAX <= UINT64_MAX +/** The ZCBOR_SUPPORTS_SIZE_T will be defined if processing of size_t type variables directly + * with zcbor_size_ functions is supported. +**/ +#define ZCBOR_SUPPORTS_SIZE_T +#else +#warning "zcbor: Unsupported size_t encoding size" +#endif + +struct zcbor_state_constant; + +/** The zcbor_state_t structure is used for both encoding and decoding. */ +typedef struct { +union { + uint8_t *payload_mut; + uint8_t const *payload; /**< The current place in the payload. Will be + updated when an element is correctly + processed. */ +}; + uint8_t const *payload_bak; /**< Temporary backup of payload. */ + size_t elem_count; /**< The current element is part of a LIST or a MAP, + and this keeps count of how many elements are + expected. This will be checked before processing + and decremented if the element is correctly + processed. */ + uint8_t const *payload_end; /**< The end of the payload. This will be + checked against payload before + processing each element. */ + bool payload_moved; /**< Is set to true while the state is stored as a backup + if @ref zcbor_update_state is called, since that function + updates the payload_end of all backed-up states. */ + +/* This is the "decode state", the part of zcbor_state_t that is only used by zcbor_decode.c. */ +struct { + bool indefinite_length_array; /**< Is set to true if the decoder is currently + decoding the contents of an indefinite- + length array. */ + bool counting_map_elems; /**< Is set to true while the number of elements of the + current map are being counted. */ +#ifdef ZCBOR_MAP_SMART_SEARCH + uint8_t *map_search_elem_state; /**< Optional flags to use when searching unordered + maps. If this is not NULL and map_elem_count + is non-zero, this consists of one flag per element + in the current map. The n-th bit can be set to 0 + to indicate that the n-th element in the + map should not be searched. These are manipulated + via zcbor_elem_processed() or + zcbor_unordered_map_search(), and should not be + manipulated directly. */ +#else + size_t map_elems_processed; /**< The number of elements of an unordered map + that have been processed. */ +#endif + size_t map_elem_count; /**< Number of elements in the current unordered map. + This also serves as the number of bits (not bytes) + in the map_search_elem_state array (when applicable). */ +} decode_state; + struct zcbor_state_constant *constant_state; /**< The part of the state that is + not backed up and duplicated. */ +} zcbor_state_t; + +struct zcbor_state_constant { + zcbor_state_t *backup_list; + size_t current_backup; + size_t num_backups; + int error; +#ifdef ZCBOR_STOP_ON_ERROR + bool stop_on_error; +#endif + bool manually_process_elem; /**< Whether an (unordered map) element should be automatically + marked as processed when found via @ref zcbor_search_map_key. */ +#ifdef ZCBOR_MAP_SMART_SEARCH + uint8_t *map_search_elem_state_end; /**< The end of the @ref map_search_elem_state buffer. */ +#endif +}; + +/** Function pointer type used with zcbor_multi_decode. + * + * This type is compatible with all decoding functions here and in the generated + * code, except for zcbor_multi_decode. + */ +typedef bool(zcbor_encoder_t)(zcbor_state_t *, const void *); +typedef bool(zcbor_decoder_t)(zcbor_state_t *, void *); + +/** Enumeration representing the major types available in CBOR. + * + * The major type is represented in the 3 first bits of the header byte. + */ +typedef enum +{ + ZCBOR_MAJOR_TYPE_PINT = 0, ///! Positive Integer + ZCBOR_MAJOR_TYPE_NINT = 1, ///! Negative Integer + ZCBOR_MAJOR_TYPE_BSTR = 2, ///! Byte String + ZCBOR_MAJOR_TYPE_TSTR = 3, ///! Text String + ZCBOR_MAJOR_TYPE_LIST = 4, ///! List + ZCBOR_MAJOR_TYPE_MAP = 5, ///! Map + ZCBOR_MAJOR_TYPE_TAG = 6, ///! Semantic Tag + ZCBOR_MAJOR_TYPE_SIMPLE = 7, ///! Simple values and floats +} zcbor_major_type_t; + +/** Extract the major type, i.e. the first 3 bits of the header byte. */ +#define ZCBOR_MAJOR_TYPE(header_byte) ((zcbor_major_type_t)(((header_byte) >> 5) & 0x7)) + +/** Extract the additional info, i.e. the last 5 bits of the header byte. */ +#define ZCBOR_ADDITIONAL(header_byte) ((header_byte) & 0x1F) + +/** Convenience macro for failing out of a decoding/encoding function. +*/ +#define ZCBOR_FAIL() \ +do {\ + zcbor_log("ZCBOR_FAIL "); \ + zcbor_trace_file(state); \ + return false; \ +} while(0) + +#define ZCBOR_FAIL_IF(expr) \ +do {\ + if (expr) { \ + zcbor_log("ZCBOR_FAIL_IF(" #expr ") "); \ + ZCBOR_FAIL(); \ + } \ +} while(0) + +#define ZCBOR_ERR(err) \ +do { \ + zcbor_log("ZCBOR_ERR(%d) ", err); \ + zcbor_error(state, err); \ + ZCBOR_FAIL(); \ +} while(0) + +#define ZCBOR_ERR_IF(expr, err) \ +do {\ + if (expr) { \ + zcbor_log("ZCBOR_ERR_IF(" #expr ", %d) ", err); \ + ZCBOR_ERR(err); \ + } \ +} while(0) + +#define ZCBOR_CHECK_PAYLOAD() \ + ZCBOR_ERR_IF(state->payload >= state->payload_end, ZCBOR_ERR_NO_PAYLOAD) + +#ifdef ZCBOR_STOP_ON_ERROR +#define ZCBOR_CHECK_ERROR() \ +do { \ + if (!zcbor_check_error(state)) { \ + ZCBOR_FAIL(); \ + } \ +} while(0) +#else +#define ZCBOR_CHECK_ERROR() +#endif + +#define ZCBOR_VALUE_IN_HEADER 23 ///! Values below this are encoded directly in the header. +#define ZCBOR_VALUE_IS_1_BYTE 24 ///! The next 1 byte contains the value. +#define ZCBOR_VALUE_IS_2_BYTES 25 ///! The next 2 bytes contain the value. +#define ZCBOR_VALUE_IS_4_BYTES 26 ///! The next 4 bytes contain the value. +#define ZCBOR_VALUE_IS_8_BYTES 27 ///! The next 8 bytes contain the value. +#define ZCBOR_VALUE_IS_INDEFINITE_LENGTH 31 ///! The list or map has indefinite length, and will instead be terminated by a 0xFF token. + +#define ZCBOR_BOOL_TO_SIMPLE ((uint8_t)20) ///! In CBOR, false/true have the values 20/21 + +#define ZCBOR_FLAG_RESTORE 1UL ///! Restore from the backup. Overwrite the current state with the state from the backup. +#define ZCBOR_FLAG_CONSUME 2UL ///! Consume the backup. Remove the backup from the stack of backups. +#define ZCBOR_FLAG_KEEP_PAYLOAD 4UL ///! Keep the pre-restore payload after restoring. +#define ZCBOR_FLAG_KEEP_DECODE_STATE 8UL ///! Keep the pre-restore decode state (everything only used for decoding) + +#define ZCBOR_SUCCESS 0 +#define ZCBOR_ERR_NO_BACKUP_MEM 1 +#define ZCBOR_ERR_NO_BACKUP_ACTIVE 2 +#define ZCBOR_ERR_LOW_ELEM_COUNT 3 +#define ZCBOR_ERR_HIGH_ELEM_COUNT 4 +#define ZCBOR_ERR_INT_SIZE 5 +#define ZCBOR_ERR_FLOAT_SIZE 6 +#define ZCBOR_ERR_ADDITIONAL_INVAL 7 ///! > 27 +#define ZCBOR_ERR_NO_PAYLOAD 8 +#define ZCBOR_ERR_PAYLOAD_NOT_CONSUMED 9 +#define ZCBOR_ERR_WRONG_TYPE 10 +#define ZCBOR_ERR_WRONG_VALUE 11 +#define ZCBOR_ERR_WRONG_RANGE 12 +#define ZCBOR_ERR_ITERATIONS 13 +#define ZCBOR_ERR_ASSERTION 14 +#define ZCBOR_ERR_PAYLOAD_OUTDATED 15 ///! Because of a call to @ref zcbor_update_state +#define ZCBOR_ERR_ELEM_NOT_FOUND 16 +#define ZCBOR_ERR_MAP_MISALIGNED 17 +#define ZCBOR_ERR_ELEMS_NOT_PROCESSED 18 +#define ZCBOR_ERR_NOT_AT_END 19 +#define ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE 20 +#define ZCBOR_ERR_INVALID_VALUE_ENCODING 21 ///! When ZCBOR_CANONICAL is defined, and the incoming data is not encoded with minimal length. +#define ZCBOR_ERR_UNKNOWN 31 + +/** The largest possible elem_count. */ +#define ZCBOR_MAX_ELEM_COUNT SIZE_MAX + +/** Initial value for elem_count for when it just needs to be large. */ +#define ZCBOR_LARGE_ELEM_COUNT (ZCBOR_MAX_ELEM_COUNT - 15) + + +/** Take a backup of the current state. Overwrite the current elem_count. */ +bool zcbor_new_backup(zcbor_state_t *state, size_t new_elem_count); + +/** Consult the most recent backup. In doing so, check whether elem_count is + * less than or equal to max_elem_count. + * Also, take action based on the flags (See ZCBOR_FLAG_*). + */ +bool zcbor_process_backup(zcbor_state_t *state, uint32_t flags, size_t max_elem_count); + +/** Convenience function for starting encoding/decoding of a union. + * + * That is, for attempting to encode, or especially decode, multiple options. + * Makes a new backup. + */ +bool zcbor_union_start_code(zcbor_state_t *state); + +/** Convenience function before encoding/decoding one element of a union. + * + * Call this before attempting each option. + * Restores the backup, without consuming it. + */ +bool zcbor_union_elem_code(zcbor_state_t *state); + +/** Convenience function before encoding/decoding one element of a union. + * + * Consumes the backup without restoring it. + */ +bool zcbor_union_end_code(zcbor_state_t *state); + +/** Initialize a state with backups. + * As long as n_states is more than 1, one of the states in the array is used + * as a struct zcbor_state_constant object. + * If there is no struct zcbor_state_constant (n_states == 1), error codes are + * not available. + * This means that you get a state with (n_states - 2) backups. + * payload, payload_len, elem_count, and elem_state are used to initialize the first state. + * The elem_state is only needed for unordered maps, when ZCBOR_MAP_SMART_SEARCH is enabled. + * It is ignored otherwise. + */ +void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, + const uint8_t *payload, size_t payload_len, size_t elem_count, + uint8_t *elem_state, size_t elem_state_bytes); + +/** Do boilerplate entry function procedure. + * Initialize states, call function, and check the result. + */ +int zcbor_entry_function(const uint8_t *payload, size_t payload_len, + void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func, + size_t n_states, size_t elem_count); + +#ifdef ZCBOR_STOP_ON_ERROR +/** Check stored error and fail if present, but only if stop_on_error is true. + * + * @retval true No error found + * @retval false An error was found + */ +static inline bool zcbor_check_error(const zcbor_state_t *state) +{ + struct zcbor_state_constant *cs = state->constant_state; + return !(cs && cs->stop_on_error && cs->error); +} +#endif + +/** Return the current error state, replacing it with SUCCESS. */ +static inline int zcbor_pop_error(zcbor_state_t *state) +{ + if (!state->constant_state) { + return ZCBOR_SUCCESS; + } + int err = state->constant_state->error; + + state->constant_state->error = ZCBOR_SUCCESS; + return err; +} + +/** Look at current error state without altering it */ +static inline int zcbor_peek_error(const zcbor_state_t *state) +{ + if (!state->constant_state) { + return ZCBOR_SUCCESS; + } else { + return state->constant_state->error; + } +} + +/** Write the provided error to the error state. */ +static inline void zcbor_error(zcbor_state_t *state, int err) +{ +#ifdef ZCBOR_STOP_ON_ERROR + if (zcbor_check_error(state)) +#endif + { + if (state->constant_state) { + state->constant_state->error = err; + } + } +} + +/** Whether the current payload is exhausted. */ +static inline bool zcbor_payload_at_end(const zcbor_state_t *state) +{ + return (state->payload == state->payload_end); +} + +/** Update the current payload pointer (and payload_end). + * + * For use when the payload is divided into multiple chunks. + * + * This function also updates all backups to the new payload_end. + * This sets a flag so that @ref zcbor_process_backup fails if a backup is + * processed with the flag @ref ZCBOR_FLAG_RESTORE, but without the flag + * @ref ZCBOR_FLAG_KEEP_PAYLOAD since this would cause an invalid state. + * + * @param[inout] state The current state, will be updated with + * the new payload pointer. + * @param[in] payload The new payload chunk. + * @param[in] payload_len The length of the new payload chunk. + */ +void zcbor_update_state(zcbor_state_t *state, + const uint8_t *payload, size_t payload_len); + +/** Check that the provided fragments are complete and in the right order. + * + * If the total length is not known, the total_len can have the value + * @ref ZCBOR_STRING_FRAGMENT_UNKNOWN_LENGTH. If so, all fragments will be + * updated with the actual total length. + * + * @param[in] fragments An array of string fragments. Cannot be NULL. + * @param[in] num_fragments The number of fragments in @p fragments. + * + * @retval true If the fragments are in the right order, and there are no + * fragments missing. + * @retval false If not all fragments have the same total_len, or gaps are + * found, or if any fragment value is NULL. + */ +bool zcbor_validate_string_fragments(struct zcbor_string_fragment *fragments, + size_t num_fragments); + +/** Assemble the fragments into a single string. + * + * The fragments are copied in the order they appear, without regard for + * offset or total_len. To ensure that the fragments are correct, first + * validate with @ref zcbor_validate_string_fragments. + * + * @param[in] fragments An array of string fragments. Cannot be NULL. + * @param[in] num_fragments The number of fragments in @p fragments. + * @param[out] result The buffer to place the assembled string into. + * @param[inout] result_len In: The length of the @p result. + * Out: The length of the assembled string. + * + * @retval true On success. + * @retval false If the assembled string would be larger than the buffer. + * The buffer might still be written to. + */ +bool zcbor_splice_string_fragments(struct zcbor_string_fragment *fragments, + size_t num_fragments, uint8_t *result, size_t *result_len); + +/** Compare two struct zcbor_string instances. + * + * @param[in] str1 A string + * @param[in] str2 A string to compare to @p str1 + * + * @retval true if the strings are identical + * @retval false if length or contents don't match, or one one or both strings is NULL. + */ +bool zcbor_compare_strings(const struct zcbor_string *str1, + const struct zcbor_string *str2); + +/** Calculate the length of a CBOR string, list, or map header. + * + * This can be used to find the start of the CBOR object when you have a + * pointer to the start of the contents. The function assumes that the header + * will be the shortest it can be. + * + * @param[in] num_elems The number of elements in the string, list, or map. + * + * @return The length of the header in bytes (1-9). + */ +size_t zcbor_header_len(uint64_t value); + +/** Like @ref zcbor_header_len but for integer of any size <= 8. */ +size_t zcbor_header_len_ptr(const void *const value, size_t value_len); + +/** Convert a float16 value to float32. + * + * @param[in] input The float16 value stored in a uint16_t. + * + * @return The resulting float32 value. + */ +float zcbor_float16_to_32(uint16_t input); + +/** Convert a float32 value to float16. + * + * @param[in] input The float32 value. + * + * @return The resulting float16 value as a uint16_t. + */ +uint16_t zcbor_float32_to_16(float input); + +#ifdef ZCBOR_MAP_SMART_SEARCH +static inline size_t zcbor_round_up(size_t x, size_t align) +{ + return (((x) + (align) - 1) / (align) * (align)); +} + +#define ZCBOR_BITS_PER_BYTE 8 +/** Calculate the number of bytes needed to hold @p num_flags 1 bit flags + */ +static inline size_t zcbor_flags_to_bytes(size_t num_flags) +{ + return zcbor_round_up(num_flags, ZCBOR_BITS_PER_BYTE) / ZCBOR_BITS_PER_BYTE; +} + +/** Calculate the number of zcbor_state_t instances needed to hold @p num_flags 1 bit flags + */ +static inline size_t zcbor_flags_to_states(size_t num_flags) +{ + return zcbor_round_up(num_flags, sizeof(zcbor_state_t) * ZCBOR_BITS_PER_BYTE) + / (sizeof(zcbor_state_t) * ZCBOR_BITS_PER_BYTE); +} + +#define ZCBOR_FLAG_STATES(n_flags) zcbor_flags_to_states(n_flags) + +#else +#define ZCBOR_FLAG_STATES(n_flags) 0 +#endif + +size_t strnlen(const char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif /* ZCBOR_COMMON_H__ */ diff --git a/bootloader/mcuboot/boot/zcbor/include/zcbor_decode.h b/bootloader/mcuboot/boot/zcbor/include/zcbor_decode.h new file mode 100644 index 0000000..e5a58d8 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/include/zcbor_decode.h @@ -0,0 +1,452 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZCBOR_DECODE_H__ +#define ZCBOR_DECODE_H__ + +#include +#include +#include +#include "zcbor_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** The zcbor_decode library provides functions for decoding CBOR data elements. + * + * See The README for an introduction to CBOR, including the meaning of pint, + * nint, bstr etc. + */ + + +/** See @ref zcbor_new_state() */ +void zcbor_new_decode_state(zcbor_state_t *state_array, size_t n_states, + const uint8_t *payload, size_t payload_len, size_t elem_count, + uint8_t *elem_state, size_t elem_state_bytes); + +/** Convenience macro for declaring and initializing a decoding state with backups. + * + * This gives you a state variable named @p name. The variable functions like + * a pointer. + * + * @param[in] name The name of the new state variable. + * @param[in] num_backups The number of backup slots to keep in the state. + * @param[in] payload The payload to work on. + * @param[in] payload_size The size (in bytes) of @p payload. + * @param[in] elem_count The starting elem_count (typically 1). + * @param[in] n_flags For use if ZCBOR_MAP_SMART_SEARCH is enabled, ignored otherwise. + * The total number of unordered map search flags needed. + * I.e. the largest number of elements expected in an unordered map, + * including elements in nested unordered maps. + */ +#define ZCBOR_STATE_D(name, num_backups, payload, payload_size, elem_count, n_flags) \ +zcbor_state_t name[((num_backups) + 2 + ZCBOR_FLAG_STATES(n_flags))]; \ +do { \ + zcbor_new_decode_state(name, ZCBOR_ARRAY_SIZE(name), payload, payload_size, elem_count, \ + (uint8_t *)&name[(num_backups) + 1], ZCBOR_FLAG_STATES(n_flags) * sizeof(zcbor_state_t)); \ +} while(0) + + +/** The following applies to all _decode() functions listed directly below. + * + * @param[inout] state The current state of the decoding. + * @param[out] result Where to place the decoded value. + * @param[in] result_size (if present) Size in bytes of the memory at @p result + * + * @retval true If the value was decoded correctly. + * @retval false If the value has the wrong type, the payload overflowed, the + * element count was exhausted, or the value was larger than can + * fit in the result variable. + * Use zcbor_peek_error() to see the error code. + */ +bool zcbor_int32_decode(zcbor_state_t *state, int32_t *result); /* pint/nint */ +bool zcbor_int64_decode(zcbor_state_t *state, int64_t *result); /* pint/nint */ +bool zcbor_uint32_decode(zcbor_state_t *state, uint32_t *result); /* pint */ +bool zcbor_uint64_decode(zcbor_state_t *state, uint64_t *result); /* pint */ +bool zcbor_size_decode(zcbor_state_t *state, size_t *result); /* pint */ +bool zcbor_int_decode(zcbor_state_t *state, void *result, size_t result_size); /* pint/nint */ +bool zcbor_uint_decode(zcbor_state_t *state, void *result, size_t result_size); /* pint */ +bool zcbor_bstr_decode(zcbor_state_t *state, struct zcbor_string *result); /* bstr */ +bool zcbor_tstr_decode(zcbor_state_t *state, struct zcbor_string *result); /* tstr */ +bool zcbor_tag_decode(zcbor_state_t *state, uint32_t *result); /* CBOR tag */ +bool zcbor_simple_decode(zcbor_state_t *state, uint8_t *result); /* CBOR simple value */ +bool zcbor_bool_decode(zcbor_state_t *state, bool *result); /* boolean CBOR simple value */ +bool zcbor_float16_decode(zcbor_state_t *state, float *result); /* IEEE754 float16 */ +bool zcbor_float16_bytes_decode(zcbor_state_t *state, uint16_t *result); /* IEEE754 float16 raw bytes */ +bool zcbor_float16_32_decode(zcbor_state_t *state, float *result); /* IEEE754 float16 or float32 */ +bool zcbor_float32_decode(zcbor_state_t *state, float *result); /* IEEE754 float32 */ +bool zcbor_float32_64_decode(zcbor_state_t *state, double *result); /* IEEE754 float32 or float64 */ +bool zcbor_float64_decode(zcbor_state_t *state, double *result); /* IEEE754 float64 */ +bool zcbor_float_decode(zcbor_state_t *state, double *result); /* IEEE754 float16, float32, or float64 */ + +/** The following applies to all _expect() and _pexpect() functions listed directly below. + * + * @param[inout] state The current state of the decoding. + * @param[in] expected The expected value. + * + * @retval true If the result was decoded correctly and has the expected value. + * @retval false If the decoding failed or the result doesn't have the + * expected value. + * Use zcbor_peek_error() to see the error code. + */ +bool zcbor_int32_expect(zcbor_state_t *state, int32_t expected); /* pint/nint */ +bool zcbor_int64_expect(zcbor_state_t *state, int64_t expected); /* pint/nint */ +bool zcbor_uint32_expect(zcbor_state_t *state, uint32_t expected); /* pint */ +bool zcbor_uint64_expect(zcbor_state_t *state, uint64_t expected); /* pint */ +bool zcbor_size_expect(zcbor_state_t *state, size_t expected); /* pint */ +bool zcbor_bstr_expect(zcbor_state_t *state, struct zcbor_string *expected); /* bstr */ +bool zcbor_tstr_expect(zcbor_state_t *state, struct zcbor_string *expected); /* tstr */ +bool zcbor_tag_expect(zcbor_state_t *state, uint32_t expected); /* CBOR tag */ +bool zcbor_simple_expect(zcbor_state_t *state, uint8_t expected); /* CBOR simple value */ +bool zcbor_bool_expect(zcbor_state_t *state, bool expected); /* boolean CBOR simple value */ +bool zcbor_nil_expect(zcbor_state_t *state, void *unused); /* 'nil' CBOR simple value */ +bool zcbor_undefined_expect(zcbor_state_t *state, void *unused); /* 'undefined' CBOR simple value */ +bool zcbor_float16_expect(zcbor_state_t *state, float expected); /* IEEE754 float16 */ +bool zcbor_float16_bytes_expect(zcbor_state_t *state, uint16_t expected); /* IEEE754 float16 raw bytes */ +bool zcbor_float16_32_expect(zcbor_state_t *state, float expected); /* IEEE754 float16 or float32 */ +bool zcbor_float32_expect(zcbor_state_t *state, float expected); /* IEEE754 float32 */ +bool zcbor_float32_64_expect(zcbor_state_t *state, double expected); /* IEEE754 float32 or float64 */ +bool zcbor_float64_expect(zcbor_state_t *state, double expected); /* IEEE754 float64 */ +bool zcbor_float_expect(zcbor_state_t *state, double expected); /* IEEE754 float16, float32, or float64 */ + +/** Like the _expect() functions but the value is passed through a pointer. + * (for use as a zcbor_decoder_t function) */ +bool zcbor_int32_pexpect(zcbor_state_t *state, int32_t *expected); /* pint/nint */ +bool zcbor_int64_pexpect(zcbor_state_t *state, int64_t *expected); /* pint/nint */ +bool zcbor_uint32_pexpect(zcbor_state_t *state, uint32_t *expected); /* pint */ +bool zcbor_uint64_pexpect(zcbor_state_t *state, uint64_t *expected); /* pint */ +bool zcbor_size_pexpect(zcbor_state_t *state, size_t *expected); /* pint */ +bool zcbor_tag_pexpect(zcbor_state_t *state, uint32_t *expected); /* CBOR tag */ +bool zcbor_simple_pexpect(zcbor_state_t *state, uint8_t *expected); /* CBOR simple value */ +bool zcbor_bool_pexpect(zcbor_state_t *state, bool *expected); /* boolean CBOR simple value */ +bool zcbor_float16_pexpect(zcbor_state_t *state, float *expected); /* IEEE754 float16 */ +bool zcbor_float16_bytes_pexpect(zcbor_state_t *state, uint16_t *expected); /* IEEE754 float16 raw bytes */ +bool zcbor_float16_32_pexpect(zcbor_state_t *state, float *expected); /* IEEE754 float16 or float32 */ +bool zcbor_float32_pexpect(zcbor_state_t *state, float *expected); /* IEEE754 float32 */ +bool zcbor_float32_64_pexpect(zcbor_state_t *state, double *expected); /* IEEE754 float32 or float64 */ +bool zcbor_float64_pexpect(zcbor_state_t *state, double *expected); /* IEEE754 float64 */ +bool zcbor_float_pexpect(zcbor_state_t *state, double *expected); /* IEEE754 float16, float32, or float64 */ + +/** Consume and expect a pint/nint with a certain value, within a union. + * + * Calls @ref zcbor_union_elem_code then @ref zcbor_[u]int[32|64]_expect. + */ +bool zcbor_int32_expect_union(zcbor_state_t *state, int32_t expected); +bool zcbor_int64_expect_union(zcbor_state_t *state, int64_t expected); +bool zcbor_uint32_expect_union(zcbor_state_t *state, uint32_t expected); +bool zcbor_uint64_expect_union(zcbor_state_t *state, uint64_t expected); + +/** Decode and consume a list/map header. + * + * The contents of the list can be decoded via subsequent function calls. + * A state backup is created to keep track of the element count. + * Call @ref zcbor_list_end_decode / @ref zcbor_map_end_decode when done + * decoding the contents of the list/map + * + * @retval true Header decoded correctly + * @retval false Header decoded incorrectly, or backup failed. + */ +bool zcbor_list_start_decode(zcbor_state_t *state); +bool zcbor_map_start_decode(zcbor_state_t *state); +bool zcbor_unordered_map_start_decode(zcbor_state_t *state); + +/** Search for a key in a map. + * + * The CBOR spec allows elements (key-value pairs) in maps to appear in any order. + * This function should be used when the order of elements is unknown. + * + * This must only be used while inside a map that has been entered via + * @ref zcbor_unordered_map_start_decode. Use @ref zcbor_unordered_map_end_decode + * when leaving the map. + * + * This function searches for keys. When this function returns successfully, + * the @p state is pointing to the value corresponding to the found key. + * Therefore, to be able to call this function again, the value must first be + * decoded or skipped. + * + * When searching unordered maps, the found elements must be kept track of. + * By default, this function automatically keeps track, which means it keeps a + * running count of the number of found elements, which is checked when exiting + * the map. You can do this manually instead, see @ref zcbor_elem_processed and + * @ref manually_process_elem. If ZCBOR_MAP_SMART_SEARCH is defined, a flag is + * kept for each element, instead of a rolling count. + * + * @note Unless ZCBOR_MAP_SMART_SEARCH is defined, + * elements are not individually marked as processed, so they may + * be returned again in a subsequent call to this function, if it is + * matched by the @p key_decoder of that call. Because of this, you should + * only use this function when you know the @p key_decoder matches no more + * than one of the keys. Typically this means all keys are known strings + * or integers, i.e. the @p key_decoder is typically a _pexpect() function. + * + * When searching for strings, there are convenience functions available, + * see the zcbor_search_key_* functions. + * + * @param[in] key_decoder A decoding function that will be tried against all + * keys in the map until it returns true, at which point + * @ref zcbor_unordered_map_search will return true. + * For example, a zcbor_*_pexpect() function. + * @param[inout] state The current state of decoding. Must be currently decoding + * the contents of a map, and pointing to one (any) of the + * keys, not one of the values. If successful, the @p state + * will be pointing to the value corresponding to the + * matched key. If unsuccessful, the @p state will be + * unchanged. + * @param[inout] key_result This will be passed as the second argument to the + * @p key_decoder. + * + * @retval true If the key was found, i.e. @p key_decoder returned true. + * @retval false If the key was not found after searching all map elements. + * Or the map was pointing to a value (not a key). + * Or an unexpected error happened while skipping elements or + * jumping from the end of the map to the start. + */ +bool zcbor_unordered_map_search(zcbor_decoder_t key_decoder, zcbor_state_t *state, void *key_result); + +/** Find a specific bstr/tstr key as part of a map with unknown element order. + * + * Uses @ref zcbor_unordered_map_search under the hood. Please refer to those docs + * for the conditions under which this can be called. + * Refer to the docs for zcbor_(t|b)str_expect_* (e.g. @ref zcbor_bstr_expect_ptr) + * for parameter docs. + */ +bool zcbor_search_key_bstr_ptr(zcbor_state_t *state, char const *ptr, size_t len); +bool zcbor_search_key_tstr_ptr(zcbor_state_t *state, char const *ptr, size_t len); +bool zcbor_search_key_bstr_term(zcbor_state_t *state, char const *str, size_t maxlen); +bool zcbor_search_key_tstr_term(zcbor_state_t *state, char const *str, size_t maxlen); +#define zcbor_search_key_bstr_lit(state, str) zcbor_search_key_bstr_ptr(state, str, sizeof(str) - 1) +#define zcbor_search_key_tstr_lit(state, str) zcbor_search_key_tstr_ptr(state, str, sizeof(str) - 1) +#define zcbor_search_key_bstr_arr(state, str) zcbor_search_key_bstr_ptr(state, str, (sizeof(str))) +#define zcbor_search_key_tstr_arr(state, str) zcbor_search_key_tstr_ptr(state, str, (sizeof(str))) + +/** (Optional) Call this function to mark an (unordered map) element as processed. + * + * @note This should not be called unless the @ref manually_process_elem flag is set. + * By default, i.e. when @ref manually_process_elem is not set, this function is + * called internally by @ref zcbor_unordered_map_search whenever a key is found. + * + * By default, this function increments the internal count @ref map_elems_processed. + * + * If ZCBOR_MAP_SMART_SEARCH is defined, this function instead clears a flag for the + * element (key-value pair) that is currently being processed, or that has just been + * processed, meaning the element won't be found again via @ref zcbor_unordered_map_search. + * + * @ref zcbor_unordered_map_end_decode will fail if @ref map_elems_processed does not + * match the number of elements in the map, or if any of the map element's flag is set. + */ +bool zcbor_elem_processed(zcbor_state_t *state); + +/** Finalize decoding a list/map + * + * Check that the list/map had the correct number of elements, and restore the + * previous element count from the backup. + * + * Use @ref zcbor_list_map_end_force_decode to forcibly consume the backup if + * something has gone wrong. + * + * In all successful cases, the state is returned pointing to the byte/element + * after the list/map in the payload. + * + * @retval true Everything ok. + * @retval false Element count not correct. + */ +bool zcbor_list_end_decode(zcbor_state_t *state); +bool zcbor_map_end_decode(zcbor_state_t *state); +bool zcbor_unordered_map_end_decode(zcbor_state_t *state); +bool zcbor_list_map_end_force_decode(zcbor_state_t *state); + +/** Find whether the state is at the end of a list or map. + */ +bool zcbor_array_at_end(zcbor_state_t *state); + +/** Skip a single element, regardless of type and value. + * + * This means if the element is a map or list, this function will recursively + * skip all its contents. + * This function will also skip any tags preceeding the element. + * + * @param[inout] state The current state of the decoding. + * @param[in] unused Unused parameter to maintain signature parity with + * @ref zcbor_decoder_t. + */ +bool zcbor_any_skip(zcbor_state_t *state, void *unused); + +/** Decode 0 or more elements with the same type and constraints. + * + * The decoded values will appear consecutively in the @p result array. + * + * The following is an example of decoding a list containing 3 INTS followed by + * 0 to 2 bstrs: + * + * @code{c} + * uint32_t ints[3]; + * struct zcbor_string bstrs[2]; + * uint32_t num_decode; + * bool res; + * + * res = zcbor_list_start_decode(state); + * res = res && zcbor_multi_decode(3, 3, &num_decode, zcbor_uint32_decode, + * state, ints, sizeof(ints[0])); + * res = res && zcbor_multi_decode(0, 2, &num_decode, zcbor_bstr_decode, + * state, bstrs, sizeof(bstrs[0])); + * res = res && zcbor_list_end_decode(state); + * // check res + * @endcode + * + * The @ref zcbor_decoder_t type is designed to be compatible with all single- + * value decoder functions in this library, e.g. @ref zcbor_uint32_decode, + * @ref zcbor_tstr_expect, @ref zcbor_nil_expect, etc. For _expect() functions, + * @p result will be used as a value instead of an array/pointer, so + * @p result_len will determine how much the value changes for each call. + * To decode the same value multiple times, use a @p result_len of 0. + * This function can also be used with custom decoder functions, such as those + * generated by the zcbor.py script, which for example decodes larger chunks of + * the data at once. + * + * @param[in] min_decode The minimum acceptable number of elements. + * @param[in] max_decode The maximum acceptable number of elements. + * @param[out] num_decode The actual number of elements decoded. + * @param[in] decoder The decoder function to call under the hood. This + * function will be called with the provided arguments + * repeatedly until the function fails (returns false) + * or until it has been called @p max_decode times. + * The result pointer is moved @p result_len bytes for + * each call to @p decoder, i.e. @p result refers to + * an array of result variables. + * Should not be an _expect() function, use + * _pexpect() instead. + * @param[out] result Where to place the decoded values. Must be an array + * of at least @p max_decode elements. + * @param[in] result_len The length of each result variable. Must be the + * length of the individual elements of @p result. + * + * @retval true If at least @p min_decode variables were correctly decoded. + * @retval false If @p decoder failed before having decoded @p min_decode + * values. + */ +bool zcbor_multi_decode(size_t min_decode, size_t max_decode, size_t *num_decode, + zcbor_decoder_t decoder, zcbor_state_t *state, void *result, + size_t result_len); + +/** Attempt to decode a value that might not be present in the data. + * + * Works like @ref zcbor_multi_decode, with @p present as num_decode. + * Will return true, even if the data is not present. + * + * @param[out] present Whether or not the data was present and successfully decoded. + * @param[in] decoder The decoder to attempt. + * @param[out] result The result, if present. + * + * @return Should always return true. + */ +bool zcbor_present_decode(bool *present, + zcbor_decoder_t decoder, + zcbor_state_t *state, + void *result); + + +/** Supplementary string (bstr/tstr) decoding functions: */ + +/** Consume and expect a bstr/tstr with the value of the provided char/uint8_t array. + * + * @param[inout] state The current state of the decoding. + * @param[in] str The value to expect. A pointer to the string/array. + * _term() uses strnlen(), so @p str must be null-terminated. + * _lit() uses sizeof()-1, so @p str must be a (null-terminated) string literal. + * _arr() uses sizeof(), so @p str must be a uint8_t array (not null-terminated). + * @param[in] len (if present) The length of the string pointed to by @p str + * @param[in] maxlen (if present) The maximum length of the string pointed to by @p str. + * This value is passed to strnlen. + */ +bool zcbor_bstr_expect_ptr(zcbor_state_t *state, char const *ptr, size_t len); +bool zcbor_tstr_expect_ptr(zcbor_state_t *state, char const *ptr, size_t len); +bool zcbor_bstr_expect_term(zcbor_state_t *state, char const *str, size_t maxlen); +bool zcbor_tstr_expect_term(zcbor_state_t *state, char const *str, size_t maxlen); +#define zcbor_bstr_expect_lit(state, str) zcbor_bstr_expect_ptr(state, str, sizeof(str) - 1) +#define zcbor_tstr_expect_lit(state, str) zcbor_tstr_expect_ptr(state, str, sizeof(str) - 1) +#define zcbor_bstr_expect_arr(state, str) zcbor_bstr_expect_ptr(state, str, sizeof(str)) +#define zcbor_tstr_expect_arr(state, str) zcbor_tstr_expect_ptr(state, str, sizeof(str)) + +/** Decode and consume a bstr header. + * + * The rest of the string can be decoded as CBOR. + * A state backup is created to keep track of the element count. + * Call @ref zcbor_bstr_end_decode when done decoding the contents of the bstr. + * + * @param[inout] state The current state of the decoding. + * @param[out] result The resulting string, for reference. The string should be decoded via + * functions from this API since state is pointing to the start of the string, + * not the end. + * + * @retval true Header decoded correctly + * @retval false Header decoded incorrectly, or backup failed, or payload is not large enough + * to contain the contents of the string. Use @ref zcbor_bstr_start_decode_fragment + * for decoding fragmented payloads. + */ +bool zcbor_bstr_start_decode(zcbor_state_t *state, struct zcbor_string *result); + +/** Finalize decoding a CBOR-encoded bstr. + * + * Restore element count from backup. + */ +bool zcbor_bstr_end_decode(zcbor_state_t *state); + + +/** Supplementary string (bstr/tstr) decoding functions for fragmented payloads: */ + +/** Start decoding a bstr/tstr, even if the payload contains only part of it. + * + * This must be followed by a call to @ref zcbor_update_state, which can be + * followed by a call to @ref zcbor_next_fragment. Do not call this function + * again on subsequent fragments of the same string. + * + * This consumes the remaining payload as long as it belongs to the string. + */ +bool zcbor_bstr_decode_fragment(zcbor_state_t *state, struct zcbor_string_fragment *result); +bool zcbor_tstr_decode_fragment(zcbor_state_t *state, struct zcbor_string_fragment *result); + +/** Extract the next fragment of a string. + * + * Use this function to extract all but the first fragment. + */ +void zcbor_next_fragment(zcbor_state_t *state, + struct zcbor_string_fragment *prev_fragment, + struct zcbor_string_fragment *result); + +/** Decode and consume a bstr header, assuming the payload does not contain the whole bstr. + * + * The rest of the string can be decoded as CBOR. + * A state backup is created to keep track of the element count. + * Call @ref zcbor_update_state followed by @ref zcbor_bstr_next_fragment when + * the current payload has been exhausted. + * Call @ref zcbor_bstr_end_decode when done decoding the contents of the bstr. + */ +bool zcbor_bstr_start_decode_fragment(zcbor_state_t *state, + struct zcbor_string_fragment *result); + +/** Start decoding the next fragment of a string. + * + * Use this function to extract all but the first fragment of a CBOR-encoded + * bstr. + */ +void zcbor_bstr_next_fragment(zcbor_state_t *state, + struct zcbor_string_fragment *prev_fragment, + struct zcbor_string_fragment *result); + +/** Can be used on any fragment to tell if it is the final fragment of the string. */ +bool zcbor_is_last_fragment(const struct zcbor_string_fragment *fragment); + +#ifdef __cplusplus +} +#endif + +#endif /* ZCBOR_DECODE_H__ */ diff --git a/bootloader/mcuboot/boot/zcbor/include/zcbor_encode.h b/bootloader/mcuboot/boot/zcbor/include/zcbor_encode.h new file mode 100644 index 0000000..5139d3c --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/include/zcbor_encode.h @@ -0,0 +1,245 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZCBOR_ENCODE_H__ +#define ZCBOR_ENCODE_H__ + +#include +#include +#include +#include "zcbor_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** The zcbor_encode library provides functions for encoding CBOR data elements. + * + * See The README for an introduction to CBOR, including the meaning of pint, + * nint, bstr etc. + */ + + +/** See @ref zcbor_new_state() */ +void zcbor_new_encode_state(zcbor_state_t *state_array, size_t n_states, + uint8_t *payload, size_t payload_len, size_t elem_count); + +/** Convenience macro for declaring and initializing an encoding state with backups. + * + * This gives you a state variable named @p name. The variable functions like + * a pointer. + * + * @param[in] name The name of the new state variable. + * @param[in] num_backups The number of backup slots to keep in the state. + * @param[in] payload The payload to work on. + * @param[in] payload_size The size (in bytes) of @p payload. + * @param[in] elem_count The starting elem_count (typically 1). + */ +#define ZCBOR_STATE_E(name, num_backups, payload, payload_size, elem_count) \ +zcbor_state_t name[((num_backups) + 2)]; \ +do { \ + zcbor_new_encode_state(name, ZCBOR_ARRAY_SIZE(name), payload, payload_size, elem_count); \ +} while(0) + + +/** The following applies to all _put and _encode functions listed directly below. + * + * The difference between _put and _encode is only in the argument type, + * but when a @ref zcbor_encoder_t is needed, such as for @ref zcbor_multi_encode, + * the _encode variant must be used. + * + * @param[inout] state The current state of the encoding. + * @param[in] input The value to encode. + * + * @retval true Everything is ok. + * @retval false If the payload is exhausted. Or an unexpected error happened. + * Use zcbor_peek_error() to see the error code. + */ +bool zcbor_int32_put(zcbor_state_t *state, int32_t input); /* pint/nint */ +bool zcbor_int64_put(zcbor_state_t *state, int64_t input); /* pint/nint */ +bool zcbor_uint32_put(zcbor_state_t *state, uint32_t input); /* pint */ +bool zcbor_uint64_put(zcbor_state_t *state, uint64_t input); /* pint */ +bool zcbor_size_put(zcbor_state_t *state, size_t input); /* pint */ +bool zcbor_tag_put(zcbor_state_t *state, uint32_t tag); /* CBOR tag */ +bool zcbor_simple_put(zcbor_state_t *state, uint8_t input); /* CBOR simple value */ +bool zcbor_bool_put(zcbor_state_t *state, bool input); /* boolean CBOR simple value */ +bool zcbor_nil_put(zcbor_state_t *state, const void *unused); /* 'nil' CBOR simple value */ +bool zcbor_undefined_put(zcbor_state_t *state, const void *unused); /* 'undefined' CBOR simple value */ +bool zcbor_float16_put(zcbor_state_t *state, float input); /* IEEE754 float16 */ +bool zcbor_float16_bytes_put(zcbor_state_t *state, uint16_t input); /* IEEE754 float16 raw bytes */ +bool zcbor_float32_put(zcbor_state_t *state, float input); /* IEEE754 float32 */ +bool zcbor_float64_put(zcbor_state_t *state, double input); /* IEEE754 float64 */ + +bool zcbor_int32_encode(zcbor_state_t *state, const int32_t *input); /* pint/nint */ +bool zcbor_int64_encode(zcbor_state_t *state, const int64_t *input); /* pint/nint */ +bool zcbor_uint32_encode(zcbor_state_t *state, const uint32_t *input); /* pint */ +bool zcbor_uint64_encode(zcbor_state_t *state, const uint64_t *input); /* pint */ +bool zcbor_size_encode(zcbor_state_t *state, const size_t *input); /* pint */ +bool zcbor_int_encode(zcbor_state_t *state, const void *input_int, size_t int_size); +bool zcbor_uint_encode(zcbor_state_t *state, const void *input_uint, size_t uint_size); +bool zcbor_bstr_encode(zcbor_state_t *state, const struct zcbor_string *input); /* bstr */ +bool zcbor_tstr_encode(zcbor_state_t *state, const struct zcbor_string *input); /* tstr */ +bool zcbor_tag_encode(zcbor_state_t *state, uint32_t *tag); /* CBOR tag. Note that zcbor_tag_encode()'s argument was changed to be a pointer. See also zcbor_tag_put(). */ +bool zcbor_simple_encode(zcbor_state_t *state, uint8_t *input); /* CBOR simple value */ +bool zcbor_bool_encode(zcbor_state_t *state, const bool *input); /* boolean CBOR simple value */ +bool zcbor_float16_encode(zcbor_state_t *state, const float *input); /* IEEE754 float16 */ +bool zcbor_float16_bytes_encode(zcbor_state_t *state, const uint16_t *input); /* IEEE754 float16 raw bytes */ +bool zcbor_float32_encode(zcbor_state_t *state, const float *input); /* IEEE754 float32 */ +bool zcbor_float64_encode(zcbor_state_t *state, const double *input); /* IEEE754 float64 */ + +/** Encode a list/map header. + * + * The contents of the list/map can be encoded via subsequent function calls. + * If ZCBOR_CANONICAL is defined, a state backup is created to keep track of the + * element count. + * When all members have been encoded, call @ref zcbor_list_end_encode / + * @ref zcbor_map_end_encode to close the list/map. + * + * @param[inout] state The current state of the encoding. + * @param[in] max_num The maximum number of members in the list/map. + * This serves as a size hint for the header. Must be + * equal to the max_num provided to the corresponding + * @ref zcbor_list_end_encode / @ref zcbor_map_end_encode + * call. + * Only used when ZCBOR_CANONICAL is defined. + */ +bool zcbor_list_start_encode(zcbor_state_t *state, size_t max_num); +bool zcbor_map_start_encode(zcbor_state_t *state, size_t max_num); + +/** Encode the end of a list/map. Do some checks and deallocate backup. + * + * - Default: Adds a list terminator (0xFF) to mark the + * end of the list/map. + * - If ZCBOR_CANONICAL is defined: Instead encodes the number of members in + * the list/map header. If the header ends up a different size than expected, + * the list/map contents are moved using memmove(). + * + * Use @ref zcbor_list_map_end_force_encode to forcibly consume the backup if + * something has gone wrong. + * + * @param[inout] state The current state of the encoding. + * @param[in] max_num The maximum number of members in the list/map. Must be + * equal to the max_num provided to the corresponding + * @ref zcbor_list_start_encode call. + * Only used when ZCBOR_CANONICAL is defined. + */ +bool zcbor_list_end_encode(zcbor_state_t *state, size_t max_num); +bool zcbor_map_end_encode(zcbor_state_t *state, size_t max_num); +bool zcbor_list_map_end_force_encode(zcbor_state_t *state); + +/** Encode 0 or more elements with the same type and constraints. + * + * The encoded values are taken from the @p input array. + * + * The following is an example of encoding a list containing 3 INTS followed by + * 0 to 2 bstrs: + * + * @code{c} + * uint32_t ints[3] = ; + * struct zcbor_string bstrs[2] = ; + * bool res; + * + * res = zcbor_list_start_encode(state, 5); + * res = res && zcbor_multi_encode(3, zcbor_uint32_encode, state, + * ints, sizeof(uint32_t)); + * res = res && zcbor_multi_encode(2, zcbor_bstr_encode, state, + * bstrs, sizeof(struct zcbor_string)); + * res = res && zcbor_list_end_encode(state, 5); + * // check res + * @endcode + * + * The @ref zcbor_encoder_t type is designed to be compatible with all single- + * value encoder functions in this library, e.g. @ref zcbor_uint32_encode, + * @ref zcbor_tstr_put, @ref zcbor_nil_put, etc. For _put() functions, + * @p input will be used as a value instead of an array/pointer, so + * @p input_len will determine how much the value changes for each call. + * To encode the same value multiple times, use a @p input_len of 0. + * This function can also be used with custom decoder functions, such as those + * generated by the zcbor.py script, which for example encodes larger chunks of + * the data at once. + * + * @param[in] num_encode The actual number of elements. + * @param[in] encoder The encoder function to call under the hood. This + * function will be called with the provided arguments + * repeatedly until the function fails (returns false) + * or until it has been called @p max_encode times. + * The input pointer is moved @p input_len bytes for + * each call to @p encoder, i.e. @p input refers to an + * array of input variables. + * @param[in] input Source of the encoded values. Must be an array of + * at least @p max_encode elements. + * @param[in] input_len The length of the input variables. Must be the + * length of the individual elements in input. + * + * @retval true If at least @p min_encode variables were correctly encoded. + * @retval false If @p encoder failed before having encoded @p min_encode + * values. + */ +bool zcbor_multi_encode(size_t num_encode, zcbor_encoder_t encoder, + zcbor_state_t *state, const void *input, size_t result_len); + +/** Works like @ref zcbor_multi_encode + * + * But first checks that @p num_encode is between @p min_encode and @p max_encode. + */ +bool zcbor_multi_encode_minmax(size_t min_encode, size_t max_encode, + const size_t *num_encode, zcbor_encoder_t encoder, + zcbor_state_t *state, const void *input, size_t input_len); + + +/* Supplementary string (bstr/tstr) encoding functions: */ + +/** Encode a char/uint8_t pointer as a bstr/tstr. + * + * @param[inout] state The current state of the encoding. + * @param[in] str The value to encode. A pointer to the string/array. + * _term() uses strnlen(), so @p str must be null-terminated. + * _lit() uses sizeof()-1, so @p str must be a (null-terminated) string literal. + * _arr() uses sizeof(), so @p str must be a uint8_t array (not null-terminated). + * @param[in] len (if present) The length of the string pointed to by @p str + * @param[in] maxlen (if present) The maximum length of the string pointed to by @p str. + * This value is passed to strnlen. + */ +bool zcbor_bstr_encode_ptr(zcbor_state_t *state, const char *str, size_t len); +bool zcbor_tstr_encode_ptr(zcbor_state_t *state, const char *str, size_t len); +bool zcbor_bstr_put_term(zcbor_state_t *state, char const *str, size_t maxlen); +bool zcbor_tstr_put_term(zcbor_state_t *state, char const *str, size_t maxlen); +#define zcbor_bstr_put_lit(state, str) zcbor_bstr_encode_ptr(state, str, sizeof(str) - 1) +#define zcbor_tstr_put_lit(state, str) zcbor_tstr_encode_ptr(state, str, sizeof(str) - 1) +#define zcbor_bstr_put_arr(state, str) zcbor_bstr_encode_ptr(state, str, sizeof(str)) +#define zcbor_tstr_put_arr(state, str) zcbor_tstr_encode_ptr(state, str, sizeof(str)) + +/** Encode a bstr header. + * + * The rest of the string can be encoded as CBOR. + * A state backup is created to keep track of the element count. + * Call @ref zcbor_bstr_end_encode when done encoding the contents of the bstr. + * + * @param[inout] state The current state of the encoding. + * + * @retval true Header encoded correctly + * @retval false Header encoded incorrectly, or backup failed. + */ +bool zcbor_bstr_start_encode(zcbor_state_t *state); + +/** Finalize encoding a CBOR-encoded bstr. + * + * This writes the final size of the bstr to the header. + * Restore element count from backup. + */ +bool zcbor_bstr_end_encode(zcbor_state_t *state, struct zcbor_string *result); + +#ifdef __cplusplus +} +#endif + +#endif /* ZCBOR_ENCODE_H__ */ diff --git a/bootloader/mcuboot/boot/zcbor/include/zcbor_print.h b/bootloader/mcuboot/boot/zcbor/include/zcbor_print.h new file mode 100644 index 0000000..fc19667 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/include/zcbor_print.h @@ -0,0 +1,170 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZCBOR_PRINT_H__ +#define ZCBOR_PRINT_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ZCBOR_PRINT_FUNC +#include +#define zcbor_do_print(...) printf(__VA_ARGS__) +#else +#define zcbor_do_print(...) ZCBOR_PRINT_FUNC(__VA_ARGS__) +#endif + +#ifdef ZCBOR_VERBOSE +#define zcbor_trace_raw(state) (zcbor_do_print("rem: %zu, cur: 0x%x, ec: 0x%zx, err: %d",\ + (size_t)state->payload_end - (size_t)state->payload, *state->payload, state->elem_count, \ + state->constant_state ? state->constant_state->error : 0)) +#define zcbor_trace(state, appendix) do { \ + zcbor_trace_raw(state); \ + zcbor_do_print(", %s\n", appendix); \ +} while(0) +#define zcbor_trace_file(state) do { \ + zcbor_trace_raw(state); \ + zcbor_do_print(", %s:%d\n", __FILE__, __LINE__); \ +} while(0) + +#define zcbor_log_assert(expr, ...) \ +do { \ + zcbor_do_print("ASSERTION \n \"" #expr \ + "\"\nfailed at %s:%d with message:\n ", \ + __FILE__, __LINE__); \ + zcbor_do_print(__VA_ARGS__);\ +} while(0) +#define zcbor_log(...) zcbor_do_print(__VA_ARGS__) +#else +#define zcbor_trace(state, appendix) +#define zcbor_trace_file(state) ((void)state) +#define zcbor_log_assert(...) +#define zcbor_log(...) +#endif + +#ifdef ZCBOR_ASSERTS +#define zcbor_assert(expr, ...) \ +do { \ + if (!(expr)) { \ + zcbor_log_assert(expr, __VA_ARGS__); \ + ZCBOR_FAIL(); \ + } \ +} while(0) +#define zcbor_assert_state(expr, ...) \ +do { \ + if (!(expr)) { \ + zcbor_log_assert(expr, __VA_ARGS__); \ + ZCBOR_ERR(ZCBOR_ERR_ASSERTION); \ + } \ +} while(0) +#else +#define zcbor_assert(expr, ...) +#define zcbor_assert_state(expr, ...) +#endif + +__attribute__((used)) +static void zcbor_print_compare_lines(const uint8_t *str1, const uint8_t *str2, size_t size) +{ + for (size_t j = 0; j < size; j++) { + zcbor_do_print("%x ", str1[j]); + } + zcbor_do_print("\r\n"); + for (size_t j = 0; j < size; j++) { + zcbor_do_print("%x ", str2[j]); + } + zcbor_do_print("\r\n"); + for (size_t j = 0; j < size; j++) { + zcbor_do_print("%x ", str1[j] != str2[j]); + } + zcbor_do_print("\r\n"); + zcbor_do_print("\r\n"); +} + +__attribute__((used)) +static void zcbor_print_compare_strings(const uint8_t *str1, const uint8_t *str2, size_t size) +{ + const size_t col_width = 16; + + for (size_t i = 0; i <= size / col_width; i++) { + zcbor_do_print("line %zu (char %zu)\r\n", i, i*col_width); + zcbor_print_compare_lines(&str1[i*col_width], &str2[i*col_width], + MIN(col_width, (size - i*col_width))); + } + zcbor_do_print("\r\n"); +} + +__attribute__((used)) +static void zcbor_print_compare_strings_diff(const uint8_t *str1, const uint8_t *str2, size_t size) +{ + const size_t col_width = 16; + bool printed = false; + + for (size_t i = 0; i <= size / col_width; i++) { + if (memcmp(&str1[i*col_width], &str2[i*col_width], MIN(col_width, (size - i*col_width))) != 0) { + zcbor_do_print("line %zu (char %zu)\r\n", i, i*col_width); + zcbor_print_compare_lines(&str1[i*col_width], &str2[i*col_width], + MIN(col_width, (size - i*col_width))); + printed = true; + } + } + if (printed) { + zcbor_do_print("\r\n"); + } +} + +__attribute__((used)) +static const char *zcbor_error_str(int error) +{ + #define ZCBOR_ERR_CASE(err) case err: \ + return #err; /* The literal is static per C99 6.4.5 paragraph 5. */\ + + switch(error) { + ZCBOR_ERR_CASE(ZCBOR_SUCCESS) + ZCBOR_ERR_CASE(ZCBOR_ERR_NO_BACKUP_MEM) + ZCBOR_ERR_CASE(ZCBOR_ERR_NO_BACKUP_ACTIVE) + ZCBOR_ERR_CASE(ZCBOR_ERR_LOW_ELEM_COUNT) + ZCBOR_ERR_CASE(ZCBOR_ERR_HIGH_ELEM_COUNT) + ZCBOR_ERR_CASE(ZCBOR_ERR_INT_SIZE) + ZCBOR_ERR_CASE(ZCBOR_ERR_FLOAT_SIZE) + ZCBOR_ERR_CASE(ZCBOR_ERR_ADDITIONAL_INVAL) + ZCBOR_ERR_CASE(ZCBOR_ERR_NO_PAYLOAD) + ZCBOR_ERR_CASE(ZCBOR_ERR_PAYLOAD_NOT_CONSUMED) + ZCBOR_ERR_CASE(ZCBOR_ERR_WRONG_TYPE) + ZCBOR_ERR_CASE(ZCBOR_ERR_WRONG_VALUE) + ZCBOR_ERR_CASE(ZCBOR_ERR_WRONG_RANGE) + ZCBOR_ERR_CASE(ZCBOR_ERR_ITERATIONS) + ZCBOR_ERR_CASE(ZCBOR_ERR_ASSERTION) + ZCBOR_ERR_CASE(ZCBOR_ERR_PAYLOAD_OUTDATED) + ZCBOR_ERR_CASE(ZCBOR_ERR_ELEM_NOT_FOUND) + ZCBOR_ERR_CASE(ZCBOR_ERR_MAP_MISALIGNED) + ZCBOR_ERR_CASE(ZCBOR_ERR_ELEMS_NOT_PROCESSED) + ZCBOR_ERR_CASE(ZCBOR_ERR_NOT_AT_END) + ZCBOR_ERR_CASE(ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE) + ZCBOR_ERR_CASE(ZCBOR_ERR_INVALID_VALUE_ENCODING) + } + #undef ZCBOR_ERR_CASE + + return "ZCBOR_ERR_UNKNOWN"; +} + +__attribute__((used)) +static void zcbor_print_error(int error) +{ + zcbor_do_print("%s\r\n", zcbor_error_str(error)); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ZCBOR_PRINT_H__ */ diff --git a/bootloader/mcuboot/boot/zcbor/include/zcbor_tags.h b/bootloader/mcuboot/boot/zcbor/include/zcbor_tags.h new file mode 100644 index 0000000..d17e8b3 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/include/zcbor_tags.h @@ -0,0 +1,99 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZCBOR_TAGS_H__ +#define ZCBOR_TAGS_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Values defined by RFCs via www.iana.org/assignments/cbor-tags/cbor-tags.xhtml */ +enum zcbor_tag { + ZCBOR_TAG_TIME_TSTR = 0, ///! text string [RFC8949] Standard date/time string + ZCBOR_TAG_TIME_NUM = 1, ///! integer or float [RFC8949] Epoch-based date/time + ZCBOR_TAG_UBIGNUM_BSTR = 2, ///! byte string [RFC8949] Unsigned bignum + ZCBOR_TAG_BIGNUM_BSTR = 3, ///! byte string [RFC8949] Negative bignum + ZCBOR_TAG_DECFRAC_ARR = 4, ///! array [RFC8949] Decimal fraction + ZCBOR_TAG_BIGFLOAT_ARR = 5, ///! array [RFC8949] Bigfloat + ZCBOR_TAG_COSE_ENCRYPT0 = 16, ///! COSE_Encrypt0 [RFC9052] COSE Single Recipient Encrypted Data Object + ZCBOR_TAG_COSE_MAC0 = 17, ///! COSE_Mac0 [RFC9052] COSE MAC w/o Recipients Object + ZCBOR_TAG_COSE_SIGN1 = 18, ///! COSE_Sign1 [RFC9052] COSE Single Signer Data Object + ZCBOR_TAG_2BASE64URL = 21, ///! (any) [RFC8949] Expected conversion to base64url encoding + ZCBOR_TAG_2BASE64 = 22, ///! (any) [RFC8949] Expected conversion to base64 encoding + ZCBOR_TAG_2BASE16 = 23, ///! (any) [RFC8949] Expected conversion to base16 encoding + ZCBOR_TAG_BSTR = 24, ///! byte string [RFC8949] Encoded CBOR data item + ZCBOR_TAG_URI_TSTR = 32, ///! text string [RFC8949] URI + ZCBOR_TAG_BASE64URL_TSTR = 33, ///! text string [RFC8949] base64url + ZCBOR_TAG_BASE64_TSTR = 34, ///! text string [RFC8949] base64 + ZCBOR_TAG_REGEX = 35, ///! text string [RFC7049] Regular expression (UTF-8) + ZCBOR_TAG_MIME_TSTR = 36, ///! text string [RFC8949] MIME message + ZCBOR_TAG_LANG_TSTR = 38, ///! array [RFC9290] Text string with language tag + ZCBOR_TAG_MULTI_DIM_ARR_R = 40, ///! array of arrays [RFC8746] Multi-dimensional array, row-major order + ZCBOR_TAG_HOMOG_ARR = 41, ///! array [RFC8746] Homogeneous array + ZCBOR_TAG_YANG_BITS = 42, ///! text string [RFC9254] YANG bits datatype; see Section 6.7. + ZCBOR_TAG_YANG_ENUM = 43, ///! text string [RFC9254] YANG enumeration datatype; see Section 6.6. + ZCBOR_TAG_YANG_IDENTITYREF = 44, ///! uint/tstr [RFC9254] YANG identityref datatype; see Section 6.10. + ZCBOR_TAG_YANK_INSTANCE_ID = 45, ///! uint/tstr/array [RFC9254] YANG instance-identifier datatype; see Section 6.13. + ZCBOR_TAG_SID = 46, ///! uint [RFC9254] YANG Schema Item iDentifier (sid); see Section 3.2. + ZCBOR_TAG_IPV4 = 52, ///! bstr or array [RFC9164] IPv4 + ZCBOR_TAG_IPV6 = 54, ///! bstr or array [RFC9164] IPv6 + ZCBOR_TAG_CWT = 61, ///! CWT [RFC8392] CBOR Web Token + ZCBOR_TAG_TYPED_ARR_U8 = 64, ///! byte string [RFC8746] uint8 Typed Array + ZCBOR_TAG_TYPED_ARR_U16_BE = 65, ///! byte string [RFC8746] uint16, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_U32_BE = 66, ///! byte string [RFC8746] uint32, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_U64_BE = 67, ///! byte string [RFC8746] uint64, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_U8_CA = 68, ///! byte string [RFC8746] uint8 Typed Array, clamped arithmetic + ZCBOR_TAG_TYPED_ARR_U16_LE = 69, ///! byte string [RFC8746] uint16, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_U32_LE = 70, ///! byte string [RFC8746] uint32, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_U64_LE = 71, ///! byte string [RFC8746] uint64, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_S8 = 72, ///! byte string [RFC8746] sint8 Typed Array + ZCBOR_TAG_TYPED_ARR_S16_BE = 73, ///! byte string [RFC8746] sint16, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_S32_BE = 74, ///! byte string [RFC8746] sint32, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_S64_BE = 75, ///! byte string [RFC8746] sint64, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_S16_LE = 77, ///! byte string [RFC8746] sint16, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_S32_LE = 78, ///! byte string [RFC8746] sint32, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_S64_LE = 79, ///! byte string [RFC8746] sint64, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F16_BE = 80, ///! byte string [RFC8746] IEEE 754 binary16, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F32_BE = 81, ///! byte string [RFC8746] IEEE 754 binary32, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F64_BE = 82, ///! byte string [RFC8746] IEEE 754 binary64, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F128_BE = 83, ///! byte string [RFC8746] IEEE 754 binary128, big endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F16_LE = 84, ///! byte string [RFC8746] IEEE 754 binary16, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F32_LE = 85, ///! byte string [RFC8746] IEEE 754 binary32, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F64_LE = 86, ///! byte string [RFC8746] IEEE 754 binary64, little endian, Typed Array + ZCBOR_TAG_TYPED_ARR_F128_LE = 87, ///! byte string [RFC8746] IEEE 754 binary128, little endian, Typed Array + ZCBOR_TAG_COSE_ENCRYPT = 96, ///! COSE_Encrypt [RFC9052] COSE Encrypted Data Object + ZCBOR_TAG_COSE_MAC = 97, ///! COSE_Mac [RFC9052] COSE MACed Data Object + ZCBOR_TAG_COSE_SIGN = 98, ///! COSE_Sign [RFC9052] COSE Signed Data Object + ZCBOR_TAG_EPOCH_DAYS = 100, ///! integer [RFC8943] Number of days since the epoch date 1970-01-01 + ZCBOR_TAG_REL_OID_BER_SDNV = 110, ///! bstr/array/map [RFC9090] relative object identifier (BER encoding); SDNV [RFC6256] sequence + ZCBOR_TAG_OID_BER = 111, ///! bstr/array/map [RFC9090] object identifier (BER encoding) + ZCBOR_TAG_PEN_REL_OID_BER = 112, ///! bstr/array/map [RFC9090] object identifier (BER encoding), relative to 1.3.6.1.4.1 + ZCBOR_TAG_DOTS_SIG_CHAN_OBJ = 271, ///! DOTS sig chan obj [RFC9132] DDoS Open Threat Signaling (DOTS) signal channel object + ZCBOR_TAG_FULL_DATE_STR = 1004, ///! tstr (UTF-8) [RFC8943] Full-date string + ZCBOR_TAG_MULTI_DIM_ARR_C = 1040, ///! array of arrays [RFC8746] Multi-dimensional array, column-major order + ZCBOR_TAG_CBOR = 55799, ///! (any) [RFC8949] Self-described CBOR + ZCBOR_TAG_CBOR_SEQ_FILE = 55800, ///! tagged bstr [RFC9277] indicates that the file contains CBOR Sequences + ZCBOR_TAG_CBOR_FILE_LABEL = 55801, ///! tagged bstr [RFC9277] indicates that the file starts with a CBOR-Labeled Non-CBOR Data label. + ZCBOR_TAG_COAP_CT = 1668546817, ///! bstr or (any) [RFC9277] Start of range: the representation of content-format ct < 65025 is indicated by tag number TN(ct) = 0x63740101 + (ct / 255) * 256 + ct % 255 + ZCBOR_TAG_COAP_CT_END = 1668612095, ///! bstr or (any) [RFC9277] End of range: the representation of content-format ct < 65025 is indicated by tag number TN(ct) = 0x63740101 + (ct / 255) * 256 + ct % 255 +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* ZCBOR_TAGS_H__ */ diff --git a/bootloader/mcuboot/boot/zcbor/pkg.yml b/bootloader/mcuboot/boot/zcbor/pkg.yml new file mode 100644 index 0000000..f51acce --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: boot/zcbor +pkg.description: Library for encoding and decoding data to/from cbor. +pkg.author: "Nordic Semiconductor" +pkg.homepage: "https://github.com/NordicSemiconductor/zcbor" +pkg.keywords: + - zcbor + +#pkg.src_dirs: src diff --git a/bootloader/mcuboot/boot/zcbor/src/zcbor_common.c b/bootloader/mcuboot/boot/zcbor/src/zcbor_common.c new file mode 100644 index 0000000..3697983 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/src/zcbor_common.c @@ -0,0 +1,442 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "zcbor_common.h" +#include "zcbor_print.h" + +_Static_assert((sizeof(size_t) == sizeof(void *)), + "This code needs size_t to be the same length as pointers."); + +_Static_assert((sizeof(zcbor_state_t) >= sizeof(struct zcbor_state_constant)), + "This code needs zcbor_state_t to be at least as large as zcbor_backups_t."); + +bool zcbor_new_backup(zcbor_state_t *state, size_t new_elem_count) +{ + ZCBOR_CHECK_ERROR(); + + if ((state->constant_state->current_backup) + >= state->constant_state->num_backups) { + ZCBOR_ERR(ZCBOR_ERR_NO_BACKUP_MEM); + } + + state->payload_moved = false; + + (state->constant_state->current_backup)++; + + /* use the backup at current_backup - 1, since otherwise, the 0th + * backup would be unused. */ + size_t i = (state->constant_state->current_backup) - 1; + + memcpy(&state->constant_state->backup_list[i], state, + sizeof(zcbor_state_t)); + + state->elem_count = new_elem_count; + + zcbor_log("New backup (level %zu)\n", i); + + return true; +} + + +bool zcbor_process_backup(zcbor_state_t *state, uint32_t flags, + size_t max_elem_count) +{ + ZCBOR_CHECK_ERROR(); + zcbor_state_t local_copy = *state; + + if (state->constant_state->current_backup == 0) { + zcbor_log("No backups available.\r\n"); + ZCBOR_ERR(ZCBOR_ERR_NO_BACKUP_ACTIVE); + } + + /* use the backup at current_backup - 1, since otherwise, the + * 0th backup would be unused. */ + size_t i = state->constant_state->current_backup - 1; + + zcbor_log("Process backup (level %zu, flags 0x%x)\n", i, flags); + + if (flags & ZCBOR_FLAG_RESTORE) { + if (!(flags & ZCBOR_FLAG_KEEP_PAYLOAD)) { + if (state->constant_state->backup_list[i].payload_moved) { + zcbor_log("Payload pointer out of date.\r\n"); + ZCBOR_ERR(ZCBOR_ERR_PAYLOAD_OUTDATED); + } + } + memcpy(state, &state->constant_state->backup_list[i], + sizeof(zcbor_state_t)); + } + + if (flags & ZCBOR_FLAG_CONSUME) { + state->constant_state->current_backup--; + } + + if (local_copy.elem_count > max_elem_count) { + zcbor_log("elem_count: %zu (expected max %zu)\r\n", + local_copy.elem_count, max_elem_count); + ZCBOR_ERR(ZCBOR_ERR_HIGH_ELEM_COUNT); + } + + if (flags & ZCBOR_FLAG_KEEP_PAYLOAD) { + state->payload = local_copy.payload; + } + + if (flags & ZCBOR_FLAG_KEEP_DECODE_STATE) { + /* Copy decode state */ + state->decode_state = local_copy.decode_state; + } + + return true; +} + +static void update_backups(zcbor_state_t *state, uint8_t const *new_payload_end) +{ + if (state->constant_state) { + for (unsigned int i = 0; i < state->constant_state->current_backup; i++) { + state->constant_state->backup_list[i].payload_end = new_payload_end; + state->constant_state->backup_list[i].payload_moved = true; + } + } +} + + +bool zcbor_union_start_code(zcbor_state_t *state) +{ + if (!zcbor_new_backup(state, state->elem_count)) { + ZCBOR_FAIL(); + } + return true; +} + + +bool zcbor_union_elem_code(zcbor_state_t *state) +{ + if (!zcbor_process_backup(state, ZCBOR_FLAG_RESTORE, state->elem_count)) { + ZCBOR_FAIL(); + } + return true; +} + +bool zcbor_union_end_code(zcbor_state_t *state) +{ + if (!zcbor_process_backup(state, ZCBOR_FLAG_CONSUME, state->elem_count)) { + ZCBOR_FAIL(); + } + return true; +} + +void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, + const uint8_t *payload, size_t payload_len, size_t elem_count, + uint8_t *flags, size_t flags_bytes) +{ + state_array[0].payload = payload; + state_array[0].payload_end = payload + payload_len; + state_array[0].elem_count = elem_count; + state_array[0].payload_moved = false; + state_array[0].decode_state.indefinite_length_array = false; +#ifdef ZCBOR_MAP_SMART_SEARCH + state_array[0].decode_state.map_search_elem_state = flags; + state_array[0].decode_state.map_elem_count = 0; +#else + state_array[0].decode_state.map_elems_processed = 0; + (void)flags; + (void)flags_bytes; +#endif + state_array[0].constant_state = NULL; + + if (n_states < 2) { + return; + } + + /* Use the last state as a struct zcbor_state_constant object. */ + state_array[0].constant_state = (struct zcbor_state_constant *)&state_array[n_states - 1]; + state_array[0].constant_state->backup_list = NULL; + state_array[0].constant_state->num_backups = n_states - 2; + state_array[0].constant_state->current_backup = 0; + state_array[0].constant_state->error = ZCBOR_SUCCESS; +#ifdef ZCBOR_STOP_ON_ERROR + state_array[0].constant_state->stop_on_error = false; +#endif + state_array[0].constant_state->manually_process_elem = false; +#ifdef ZCBOR_MAP_SMART_SEARCH + state_array[0].constant_state->map_search_elem_state_end = flags + flags_bytes; +#endif + if (n_states > 2) { + state_array[0].constant_state->backup_list = &state_array[1]; + } +} + +void zcbor_update_state(zcbor_state_t *state, + const uint8_t *payload, size_t payload_len) +{ + state->payload = payload; + state->payload_end = payload + payload_len; + + update_backups(state, state->payload_end); +} + + +bool zcbor_validate_string_fragments(struct zcbor_string_fragment *fragments, + size_t num_fragments) +{ + size_t total_len = 0; + + if (fragments == NULL) { + return false; + } + + for (size_t i = 0; i < num_fragments; i++) { + if (fragments[i].offset != total_len) { + return false; + } + if (fragments[i].fragment.value == NULL) { + return false; + } + if (fragments[i].total_len != fragments[0].total_len) { + return false; + } + total_len += fragments[i].fragment.len; + if (total_len > fragments[0].total_len) { + return false; + } + } + + if (num_fragments && total_len != fragments[0].total_len) { + return false; + } + + if (num_fragments && (fragments[0].total_len == ZCBOR_STRING_FRAGMENT_UNKNOWN_LENGTH)) { + for (size_t i = 0; i < num_fragments; i++) { + fragments[i].total_len = total_len; + } + } + + return true; +} + +bool zcbor_splice_string_fragments(struct zcbor_string_fragment *fragments, + size_t num_fragments, uint8_t *result, size_t *result_len) +{ + size_t total_len = 0; + + if (!fragments) { + return false; + } + + for (size_t i = 0; i < num_fragments; i++) { + if ((total_len > *result_len) + || (fragments[i].fragment.len > (*result_len - total_len))) { + return false; + } + memcpy(&result[total_len], + fragments[i].fragment.value, fragments[i].fragment.len); + total_len += fragments[i].fragment.len; + } + + *result_len = total_len; + return true; +} + + +bool zcbor_compare_strings(const struct zcbor_string *str1, + const struct zcbor_string *str2) +{ + return (str1 != NULL) && (str2 != NULL) + && (str1->value != NULL) && (str2->value != NULL) && (str1->len == str2->len) + && (memcmp(str1->value, str2->value, str1->len) == 0); +} + + +size_t zcbor_header_len(uint64_t value) +{ + if (value <= ZCBOR_VALUE_IN_HEADER) { + return 1; + } else if (value <= 0xFF) { + return 2; + } else if (value <= 0xFFFF) { + return 3; + } else if (value <= 0xFFFFFFFF) { + return 5; + } else { + return 9; + } +} + + +size_t zcbor_header_len_ptr(const void *const value, size_t value_len) +{ + uint64_t val64 = 0; + + if (value_len > sizeof(val64)) { + return 0; + } + + memcpy(((uint8_t*)&val64) + ZCBOR_ECPY_OFFS(sizeof(val64), value_len), value, value_len); + return zcbor_header_len(val64); +} + + +int zcbor_entry_function(const uint8_t *payload, size_t payload_len, + void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func, + size_t n_states, size_t elem_count) +{ + zcbor_new_state(state, n_states, payload, payload_len, elem_count, NULL, 0); + + bool ret = func(state, result); + + if (!ret) { + int err = zcbor_pop_error(state); + + err = (err == ZCBOR_SUCCESS) ? ZCBOR_ERR_UNKNOWN : err; + return err; + } + + if (payload_len_out != NULL) { + *payload_len_out = MIN(payload_len, + (size_t)state[0].payload - (size_t)payload); + } + return ZCBOR_SUCCESS; +} + + +/* Float16: */ +#define F16_SIGN_OFFS 15 /* Bit offset of the sign bit. */ +#define F16_EXPO_OFFS 10 /* Bit offset of the exponent. */ +#define F16_EXPO_MSK 0x1F /* Bitmask for the exponent (right shifted by F16_EXPO_OFFS). */ +#define F16_MANTISSA_MSK 0x3FF /* Bitmask for the mantissa. */ +#define F16_MAX 65520 /* Lowest float32 value that rounds up to float16 infinity. + * (65519.996 rounds to 65504) */ +#define F16_MIN_EXPO 24 /* Negative exponent of the non-zero float16 value closest to 0 (2^-24) */ +#define F16_MIN (1.0f / (1 << F16_MIN_EXPO)) /* The non-zero float16 value closest to 0 (2^-24) */ +#define F16_MIN_NORM (1.0f / (1 << 14)) /* The normalized float16 value closest to 0 (2^-14) */ +#define F16_BIAS 15 /* The exponent bias of normalized float16 values. */ + +/* Float32: */ +#define F32_SIGN_OFFS 31 /* Bit offset of the sign bit. */ +#define F32_EXPO_OFFS 23 /* Bit offset of the exponent. */ +#define F32_EXPO_MSK 0xFF /* Bitmask for the exponent (right shifted by F32_EXPO_OFFS). */ +#define F32_MANTISSA_MSK 0x7FFFFF /* Bitmask for the mantissa. */ +#define F32_BIAS 127 /* The exponent bias of normalized float32 values. */ + +/* Rounding: */ +#define SUBNORM_ROUND_MSK (F32_MANTISSA_MSK | (1 << F32_EXPO_OFFS)) /* mantissa + lsb of expo for + * tiebreak. */ +#define SUBNORM_ROUND_BIT_MSK (1 << (F32_EXPO_OFFS - 1)) /* msb of mantissa (0x400000) */ +#define NORM_ROUND_MSK (F32_MANTISSA_MSK >> (F16_EXPO_OFFS - 1)) /* excess mantissa when going from + * float32 to float16 + 1 extra bit + * for tiebreak. */ +#define NORM_ROUND_BIT_MSK (1 << (F32_EXPO_OFFS - F16_EXPO_OFFS - 1)) /* bit 12 (0x1000) */ + + +float zcbor_float16_to_32(uint16_t input) +{ + uint32_t sign = input >> F16_SIGN_OFFS; + uint32_t expo = (input >> F16_EXPO_OFFS) & F16_EXPO_MSK; + uint32_t mantissa = input & F16_MANTISSA_MSK; + + if ((expo == 0) && (mantissa != 0)) { + /* Subnormal float16 - convert to normalized float32 */ + return ((float)mantissa * F16_MIN) * (sign ? -1 : 1); + } else { + /* Normalized / zero / Infinity / NaN */ + uint32_t new_expo = (expo == 0 /* zero */) ? 0 + : (expo == F16_EXPO_MSK /* inf/NaN */) ? F32_EXPO_MSK + : (expo + (F32_BIAS - F16_BIAS)); + uint32_t value32 = (sign << F32_SIGN_OFFS) | (new_expo << F32_EXPO_OFFS) + | (mantissa << (F32_EXPO_OFFS - F16_EXPO_OFFS)); + float result; + + memcpy(&result, &value32, sizeof(result)); + return result; + } +} + + +uint16_t zcbor_float32_to_16(float input) +{ + uint32_t value32; + + memcpy(&value32, &input, sizeof(value32)); + + uint32_t sign = value32 >> F32_SIGN_OFFS; + uint32_t expo = (value32 >> F32_EXPO_OFFS) & F32_EXPO_MSK; + uint32_t mantissa = value32 & F32_MANTISSA_MSK; + + uint16_t value16 = (uint16_t)(sign << F16_SIGN_OFFS); + + uint32_t abs_value32 = value32 & ~(1 << F32_SIGN_OFFS); + float abs_input; + + memcpy(&abs_input, &abs_value32, sizeof(abs_input)); + + if (abs_input <= (F16_MIN / 2)) { + /* 0 or too small for float16. Round down to 0. value16 is already correct. */ + } else if (abs_input < F16_MIN) { + /* Round up to 2^(-24) (F16_MIN), has other rounding rules than larger values. */ + value16 |= 0x0001; + } else if (abs_input < F16_MIN_NORM) { + /* Subnormal float16 (normal float32) */ + uint32_t adjusted_mantissa = + /* Adjust for the purposes of checking rounding. */ + /* The lsb of expo is needed for the cases where expo is 103 (minimum). */ + ((value32 << (expo - (F32_BIAS - F16_MIN_EXPO))) & SUBNORM_ROUND_MSK); + uint16_t rounding_bit = + /* "Round to nearest, ties to even". */ + /* 0x400000 means ties go down towards even. (0xC00000 means ties go up.) */ + (adjusted_mantissa & SUBNORM_ROUND_BIT_MSK) + && (adjusted_mantissa != SUBNORM_ROUND_BIT_MSK); + value16 |= ((uint16_t)(abs_input * (1 << 24)) + rounding_bit); /* expo is 0 */ + } else if (abs_input < F16_MAX) { + /* Normal float16 (normal float32) */ + uint16_t rounding_bit = + /* Bit 13 of the mantissa represents which way to round, except for the */ + /* special case where bits 0-12 and 14 are 0. */ + /* This is because of "Round to nearest, ties to even". */ + /* 0x1000 means ties go down towards even. (0x3000 means ties go up.) */ + ((mantissa & NORM_ROUND_BIT_MSK) + && ((mantissa & NORM_ROUND_MSK) != NORM_ROUND_BIT_MSK)); + value16 |= (uint16_t)((expo - (F32_BIAS - F16_BIAS)) << F16_EXPO_OFFS); + value16 |= (uint16_t)(mantissa >> (F32_EXPO_OFFS - F16_EXPO_OFFS)); + value16 += rounding_bit; /* Might propagate to exponent. */ + } else if (expo != F32_EXPO_MSK || !mantissa) { + /* Infinite, or finite normal float32 too large for float16. Round up to inf. */ + value16 |= (F16_EXPO_MSK << F16_EXPO_OFFS); + } else { + /* NaN */ + /* Preserve msbit of mantissa. */ + uint16_t new_mantissa = (uint16_t)(mantissa >> (F32_EXPO_OFFS - F16_EXPO_OFFS)); + value16 |= (F16_EXPO_MSK << F16_EXPO_OFFS) | (new_mantissa ? new_mantissa : 1); + } + + return value16; +} + + +/** Weak strnlen() implementation in case it is not available. + * + * This function is in the public domain, according to: + * https://github.com/arm-embedded/gcc-arm-none-eabi.debian/blob/master/src/libiberty/strnlen.c + */ +__attribute__((__weak__)) +size_t strnlen (const char *s, size_t maxlen) +{ + size_t i; + + for (i = 0; i < maxlen; ++i) { + if (s[i] == '\0') { + break; + } + } + return i; +} diff --git a/bootloader/mcuboot/boot/zcbor/src/zcbor_decode.c b/bootloader/mcuboot/boot/zcbor/src/zcbor_decode.c new file mode 100644 index 0000000..841c341 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/src/zcbor_decode.c @@ -0,0 +1,1572 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "zcbor_decode.h" +#include "zcbor_common.h" +#include "zcbor_print.h" + + +/** Return value length from additional value. + */ +static size_t additional_len(uint8_t additional) +{ + if (additional <= ZCBOR_VALUE_IN_HEADER) { + return 0; + } else if (ZCBOR_VALUE_IS_1_BYTE <= additional && additional <= ZCBOR_VALUE_IS_8_BYTES) { + /* 24 => 1 + * 25 => 2 + * 26 => 4 + * 27 => 8 + */ + return 1U << (additional - ZCBOR_VALUE_IS_1_BYTE); + } + return 0xF; +} + + +static bool initial_checks(zcbor_state_t *state) +{ + ZCBOR_CHECK_ERROR(); + ZCBOR_CHECK_PAYLOAD(); + return true; +} + + +static bool type_check(zcbor_state_t *state, zcbor_major_type_t exp_major_type) +{ + if (!initial_checks(state)) { + ZCBOR_FAIL(); + } + zcbor_major_type_t major_type = ZCBOR_MAJOR_TYPE(*state->payload); + + if (major_type != exp_major_type) { + ZCBOR_ERR(ZCBOR_ERR_WRONG_TYPE); + } + return true; +} + + +#define INITIAL_CHECKS() \ +do {\ + if (!initial_checks(state)) { \ + ZCBOR_FAIL(); \ + } \ +} while(0) + +#define INITIAL_CHECKS_WITH_TYPE(exp_major_type) \ +do {\ + if (!type_check(state, exp_major_type)) { \ + ZCBOR_FAIL(); \ + } \ +} while(0) + +static void err_restore(zcbor_state_t *state, int err) +{ + state->payload = state->payload_bak; + state->elem_count++; + zcbor_error(state, err); +} + +#define ERR_RESTORE(err) \ +do { \ + err_restore(state, err); \ + ZCBOR_FAIL(); \ +} while(0) + +#define FAIL_RESTORE() \ +do { \ + state->payload = state->payload_bak; \ + state->elem_count++; \ + ZCBOR_FAIL(); \ +} while(0) + +#define PRINT_FUNC() zcbor_log("%s ", __func__); + + +static void endian_copy(uint8_t *dst, const uint8_t *src, size_t src_len) +{ +#ifdef ZCBOR_BIG_ENDIAN + memcpy(dst, src, src_len); +#else + for (size_t i = 0; i < src_len; i++) { + dst[i] = src[src_len - 1 - i]; + } +#endif /* ZCBOR_BIG_ENDIAN */ +} + + +/** Get a single value. + * + * @details @p ppayload must point to the header byte. This function will + * retrieve the value (either from within the additional info, or from + * the subsequent bytes) and return it in the result. The result can + * have arbitrary length. + * + * The function will also validate + * - Min/max constraints on the value. + * - That @p payload doesn't overrun past @p payload_end. + * - That @p elem_count has not been exhausted. + * + * @p ppayload and @p elem_count are updated if the function + * succeeds. If not, they are left unchanged. + * + * CBOR values are always big-endian, so this function converts from + * big to little-endian if necessary (@ref ZCBOR_BIG_ENDIAN). + */ +static bool value_extract(zcbor_state_t *state, + void *const result, size_t result_len) +{ + zcbor_trace(state, "value_extract"); + zcbor_assert_state(result_len != 0, "0-length result not supported.\r\n"); + zcbor_assert_state(result_len <= 8, "result sizes above 8 bytes not supported.\r\n"); + zcbor_assert_state(result != NULL, "result cannot be NULL.\r\n"); + + INITIAL_CHECKS(); + ZCBOR_ERR_IF((state->elem_count == 0), ZCBOR_ERR_LOW_ELEM_COUNT); + + uint8_t additional = ZCBOR_ADDITIONAL(*state->payload); + size_t len = additional_len(additional); + uint8_t *result_offs = (uint8_t *)result + ZCBOR_ECPY_OFFS(result_len, MAX(1, len)); + + ZCBOR_ERR_IF(additional > ZCBOR_VALUE_IS_8_BYTES, ZCBOR_ERR_ADDITIONAL_INVAL); + ZCBOR_ERR_IF(len > result_len, ZCBOR_ERR_INT_SIZE); + ZCBOR_ERR_IF((state->payload + len + 1) > state->payload_end, + ZCBOR_ERR_NO_PAYLOAD); + + memset(result, 0, result_len); + + if (len == 0) { + *result_offs = additional; + } else { + endian_copy(result_offs, state->payload + 1, len); + +#ifdef ZCBOR_CANONICAL + ZCBOR_ERR_IF((zcbor_header_len_ptr(result, result_len) != (len + 1)), + ZCBOR_ERR_INVALID_VALUE_ENCODING); +#endif + } + + state->payload_bak = state->payload; + (state->payload) += len + 1; + (state->elem_count)--; + return true; +} + + +bool zcbor_int_decode(zcbor_state_t *state, void *result, size_t result_size) +{ + PRINT_FUNC(); + INITIAL_CHECKS(); + zcbor_major_type_t major_type = ZCBOR_MAJOR_TYPE(*state->payload); + uint8_t *result_uint8 = (uint8_t *)result; + int8_t *result_int8 = (int8_t *)result; + + if (major_type != ZCBOR_MAJOR_TYPE_PINT + && major_type != ZCBOR_MAJOR_TYPE_NINT) { + /* Value to be read doesn't have the right type. */ + ZCBOR_ERR(ZCBOR_ERR_WRONG_TYPE); + } + + if (!value_extract(state, result, result_size)) { + ZCBOR_FAIL(); + } + +#ifdef ZCBOR_BIG_ENDIAN + if (result_int8[0] < 0) { +#else + if (result_int8[result_size - 1] < 0) { +#endif + /* Value is too large to fit in a signed integer. */ + ERR_RESTORE(ZCBOR_ERR_INT_SIZE); + } + + if (major_type == ZCBOR_MAJOR_TYPE_NINT) { + /* Convert from CBOR's representation by flipping all bits. */ + for (unsigned int i = 0; i < result_size; i++) { + result_uint8[i] = (uint8_t)~result_uint8[i]; + } + } + + return true; +} + + +bool zcbor_int32_decode(zcbor_state_t *state, int32_t *result) +{ + PRINT_FUNC(); + return zcbor_int_decode(state, result, sizeof(*result)); +} + + +bool zcbor_int64_decode(zcbor_state_t *state, int64_t *result) +{ + PRINT_FUNC(); + return zcbor_int_decode(state, result, sizeof(*result)); +} + + +bool zcbor_uint_decode(zcbor_state_t *state, void *result, size_t result_size) +{ + PRINT_FUNC(); + INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_PINT); + + if (!value_extract(state, result, result_size)) { + zcbor_log("uint with size %zu failed.\r\n", result_size); + ZCBOR_FAIL(); + } + return true; +} + + +bool zcbor_uint32_decode(zcbor_state_t *state, uint32_t *result) +{ + PRINT_FUNC(); + return zcbor_uint_decode(state, result, sizeof(*result)); +} + + +bool zcbor_int32_expect_union(zcbor_state_t *state, int32_t result) +{ + PRINT_FUNC(); + if (!zcbor_union_elem_code(state)) { + ZCBOR_FAIL(); + } + return zcbor_int32_expect(state, result); +} + + +bool zcbor_int64_expect_union(zcbor_state_t *state, int64_t result) +{ + PRINT_FUNC(); + if (!zcbor_union_elem_code(state)) { + ZCBOR_FAIL(); + } + return zcbor_int64_expect(state, result); +} + + +bool zcbor_uint32_expect_union(zcbor_state_t *state, uint32_t result) +{ + PRINT_FUNC(); + if (!zcbor_union_elem_code(state)) { + ZCBOR_FAIL(); + } + return zcbor_uint32_expect(state, result); +} + + +bool zcbor_uint64_expect_union(zcbor_state_t *state, uint64_t result) +{ + PRINT_FUNC(); + if (!zcbor_union_elem_code(state)) { + ZCBOR_FAIL(); + } + return zcbor_uint64_expect(state, result); +} + + +bool zcbor_int32_expect(zcbor_state_t *state, int32_t expected) +{ + PRINT_FUNC(); + return zcbor_int64_expect(state, expected); +} + + +bool zcbor_int32_pexpect(zcbor_state_t *state, int32_t *expected) +{ + PRINT_FUNC(); + return zcbor_int32_expect(state, *expected); +} + + +bool zcbor_int64_expect(zcbor_state_t *state, int64_t expected) +{ + PRINT_FUNC(); + int64_t actual; + + if (!zcbor_int64_decode(state, &actual)) { + ZCBOR_FAIL(); + } + + if (actual != expected) { + zcbor_log("%" PRIi64 " != %" PRIi64 "\r\n", actual, expected); + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_int64_pexpect(zcbor_state_t *state, int64_t *expected) +{ + PRINT_FUNC(); + return zcbor_int64_expect(state, *expected); +} + + +bool zcbor_uint64_decode(zcbor_state_t *state, uint64_t *result) +{ + PRINT_FUNC(); + return zcbor_uint_decode(state, result, sizeof(*result)); +} + + +#ifdef ZCBOR_SUPPORTS_SIZE_T +bool zcbor_size_decode(zcbor_state_t *state, size_t *result) +{ + PRINT_FUNC(); + return zcbor_uint_decode(state, result, sizeof(*result)); +} +#endif + + +bool zcbor_uint32_expect(zcbor_state_t *state, uint32_t expected) +{ + PRINT_FUNC(); + return zcbor_uint64_expect(state, expected); +} + + +bool zcbor_uint32_pexpect(zcbor_state_t *state, uint32_t *expected) +{ + PRINT_FUNC(); + return zcbor_uint32_expect(state, *expected); +} + + +bool zcbor_uint64_expect(zcbor_state_t *state, uint64_t expected) +{ + PRINT_FUNC(); + uint64_t actual; + + if (!zcbor_uint64_decode(state, &actual)) { + ZCBOR_FAIL(); + } + if (actual != expected) { + zcbor_log("%" PRIu64 " != %" PRIu64 "\r\n", actual, expected); + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_uint64_pexpect(zcbor_state_t *state, uint64_t *expected) +{ + PRINT_FUNC(); + return zcbor_uint64_expect(state, *expected); +} + + +#ifdef ZCBOR_SUPPORTS_SIZE_T +bool zcbor_size_expect(zcbor_state_t *state, size_t expected) +{ + PRINT_FUNC(); + return zcbor_uint64_expect(state, expected); +} + + +bool zcbor_size_pexpect(zcbor_state_t *state, size_t *expected) +{ + PRINT_FUNC(); + return zcbor_size_expect(state, *expected); +} +#endif + + +static bool str_start_decode(zcbor_state_t *state, + struct zcbor_string *result, zcbor_major_type_t exp_major_type) +{ + INITIAL_CHECKS_WITH_TYPE(exp_major_type); + + if (!value_extract(state, &result->len, sizeof(result->len))) { + ZCBOR_FAIL(); + } + + result->value = state->payload; + return true; +} + +static bool str_start_decode_with_overflow_check(zcbor_state_t *state, + struct zcbor_string *result, zcbor_major_type_t exp_major_type) +{ + bool res = str_start_decode(state, result, exp_major_type); + + if (!res) { + ZCBOR_FAIL(); + } + + /* Casting to size_t is safe since str_start_decode() checks that + * payload_end is bigger that payload. */ + if (result->len > (size_t)(state->payload_end - state->payload)) { + zcbor_log("error: 0x%zu > 0x%zu\r\n", + result->len, + (state->payload_end - state->payload)); + ERR_RESTORE(ZCBOR_ERR_NO_PAYLOAD); + } + + return true; +} + + +bool zcbor_bstr_start_decode(zcbor_state_t *state, struct zcbor_string *result) +{ + PRINT_FUNC(); + struct zcbor_string dummy; + if (result == NULL) { + result = &dummy; + } + + if(!str_start_decode_with_overflow_check(state, result, ZCBOR_MAJOR_TYPE_BSTR)) { + ZCBOR_FAIL(); + } + + if (!zcbor_new_backup(state, ZCBOR_MAX_ELEM_COUNT)) { + FAIL_RESTORE(); + } + + state->payload_end = result->value + result->len; + return true; +} + + +bool zcbor_bstr_end_decode(zcbor_state_t *state) +{ + ZCBOR_ERR_IF(state->payload != state->payload_end, ZCBOR_ERR_PAYLOAD_NOT_CONSUMED); + + if (!zcbor_process_backup(state, + ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME | ZCBOR_FLAG_KEEP_PAYLOAD, + ZCBOR_MAX_ELEM_COUNT)) { + ZCBOR_FAIL(); + } + + return true; +} + + +static void partition_fragment(const zcbor_state_t *state, + struct zcbor_string_fragment *result) +{ + result->fragment.len = MIN(result->fragment.len, + (size_t)state->payload_end - (size_t)state->payload); +} + + +static bool start_decode_fragment(zcbor_state_t *state, + struct zcbor_string_fragment *result, + zcbor_major_type_t exp_major_type) +{ + PRINT_FUNC(); + if(!str_start_decode(state, &result->fragment, exp_major_type)) { + ZCBOR_FAIL(); + } + + result->offset = 0; + result->total_len = result->fragment.len; + partition_fragment(state, result); + state->payload_end = state->payload + result->fragment.len; + + return true; +} + +bool zcbor_bstr_start_decode_fragment(zcbor_state_t *state, + struct zcbor_string_fragment *result) +{ + PRINT_FUNC(); + if (!start_decode_fragment(state, result, ZCBOR_MAJOR_TYPE_BSTR)) { + ZCBOR_FAIL(); + } + if (!zcbor_new_backup(state, ZCBOR_MAX_ELEM_COUNT)) { + FAIL_RESTORE(); + } + return true; +} + + +void zcbor_next_fragment(zcbor_state_t *state, + struct zcbor_string_fragment *prev_fragment, + struct zcbor_string_fragment *result) +{ + memcpy(result, prev_fragment, sizeof(*result)); + result->fragment.value = state->payload_mut; + result->offset += prev_fragment->fragment.len; + result->fragment.len = result->total_len - result->offset; + + partition_fragment(state, result); + zcbor_log("New fragment length %zu\r\n", result->fragment.len); + + state->payload += result->fragment.len; +} + + +void zcbor_bstr_next_fragment(zcbor_state_t *state, + struct zcbor_string_fragment *prev_fragment, + struct zcbor_string_fragment *result) +{ + memcpy(result, prev_fragment, sizeof(*result)); + result->fragment.value = state->payload_mut; + result->offset += prev_fragment->fragment.len; + result->fragment.len = result->total_len - result->offset; + + partition_fragment(state, result); + zcbor_log("fragment length %zu\r\n", result->fragment.len); + state->payload_end = state->payload + result->fragment.len; +} + + +bool zcbor_is_last_fragment(const struct zcbor_string_fragment *fragment) +{ + return (fragment->total_len == (fragment->offset + fragment->fragment.len)); +} + + +static bool str_decode(zcbor_state_t *state, struct zcbor_string *result, + zcbor_major_type_t exp_major_type) +{ + if (!str_start_decode_with_overflow_check(state, result, exp_major_type)) { + ZCBOR_FAIL(); + } + + state->payload += result->len; + return true; +} + + +static bool str_decode_fragment(zcbor_state_t *state, struct zcbor_string_fragment *result, + zcbor_major_type_t exp_major_type) +{ + if (!start_decode_fragment(state, result, exp_major_type)) { + ZCBOR_FAIL(); + } + + (state->payload) += result->fragment.len; + return true; +} + + +static bool str_expect(zcbor_state_t *state, struct zcbor_string *result, + zcbor_major_type_t exp_major_type) +{ + struct zcbor_string tmp_result; + + if (!str_decode(state, &tmp_result, exp_major_type)) { + ZCBOR_FAIL(); + } + if (!zcbor_compare_strings(&tmp_result, result)) { + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_bstr_decode(zcbor_state_t *state, struct zcbor_string *result) +{ + PRINT_FUNC(); + return str_decode(state, result, ZCBOR_MAJOR_TYPE_BSTR); +} + + +bool zcbor_bstr_decode_fragment(zcbor_state_t *state, struct zcbor_string_fragment *result) +{ + PRINT_FUNC(); + return str_decode_fragment(state, result, ZCBOR_MAJOR_TYPE_BSTR); +} + + +bool zcbor_bstr_expect(zcbor_state_t *state, struct zcbor_string *expected) +{ + PRINT_FUNC(); + return str_expect(state, expected, ZCBOR_MAJOR_TYPE_BSTR); +} + + +bool zcbor_tstr_decode(zcbor_state_t *state, struct zcbor_string *result) +{ + PRINT_FUNC(); + return str_decode(state, result, ZCBOR_MAJOR_TYPE_TSTR); +} + + +bool zcbor_tstr_decode_fragment(zcbor_state_t *state, struct zcbor_string_fragment *result) +{ + PRINT_FUNC(); + return str_decode_fragment(state, result, ZCBOR_MAJOR_TYPE_TSTR); +} + + +bool zcbor_tstr_expect(zcbor_state_t *state, struct zcbor_string *expected) +{ + PRINT_FUNC(); + return str_expect(state, expected, ZCBOR_MAJOR_TYPE_TSTR); +} + + +bool zcbor_bstr_expect_ptr(zcbor_state_t *state, char const *ptr, size_t len) +{ + PRINT_FUNC(); + struct zcbor_string zs = { .value = (const uint8_t *)ptr, .len = len }; + + return zcbor_bstr_expect(state, &zs); +} + + +bool zcbor_tstr_expect_ptr(zcbor_state_t *state, char const *ptr, size_t len) +{ + PRINT_FUNC(); + struct zcbor_string zs = { .value = (const uint8_t *)ptr, .len = len }; + + return zcbor_tstr_expect(state, &zs); +} + + +bool zcbor_bstr_expect_term(zcbor_state_t *state, char const *string, size_t maxlen) +{ + PRINT_FUNC(); + return zcbor_bstr_expect_ptr(state, string, strnlen(string, maxlen)); +} + + +bool zcbor_tstr_expect_term(zcbor_state_t *state, char const *string, size_t maxlen) +{ + PRINT_FUNC(); + return zcbor_tstr_expect_ptr(state, string, strnlen(string, maxlen)); +} + + +static bool list_map_start_decode(zcbor_state_t *state, + zcbor_major_type_t exp_major_type) +{ + size_t new_elem_count; + bool indefinite_length_array = false; + + INITIAL_CHECKS_WITH_TYPE(exp_major_type); + +#ifndef ZCBOR_CANONICAL + if (ZCBOR_ADDITIONAL(*state->payload) == ZCBOR_VALUE_IS_INDEFINITE_LENGTH) { + /* Indefinite length array. */ + new_elem_count = ZCBOR_LARGE_ELEM_COUNT; + ZCBOR_ERR_IF(state->elem_count == 0, ZCBOR_ERR_LOW_ELEM_COUNT); + indefinite_length_array = true; + state->payload_bak = state->payload++; + state->elem_count--; + } else +#endif + { + if (!value_extract(state, &new_elem_count, sizeof(new_elem_count))) { + ZCBOR_FAIL(); + } + } + + if (!zcbor_new_backup(state, new_elem_count)) { + FAIL_RESTORE(); + } + + state->decode_state.indefinite_length_array = indefinite_length_array; + + return true; +} + + +bool zcbor_list_start_decode(zcbor_state_t *state) +{ + PRINT_FUNC(); + return list_map_start_decode(state, ZCBOR_MAJOR_TYPE_LIST); +} + + +bool zcbor_map_start_decode(zcbor_state_t *state) +{ + PRINT_FUNC(); + bool ret = list_map_start_decode(state, ZCBOR_MAJOR_TYPE_MAP); + + if (ret && !state->decode_state.indefinite_length_array) { + if (state->elem_count >= (ZCBOR_MAX_ELEM_COUNT / 2)) { + /* The new elem_count is too large. */ + ERR_RESTORE(ZCBOR_ERR_INT_SIZE); + } + state->elem_count *= 2; + } + return ret; +} + + +bool zcbor_array_at_end(zcbor_state_t *state) +{ +#ifdef ZCBOR_CANONICAL + const bool indefinite_length_array = false; +#else + const bool indefinite_length_array = state->decode_state.indefinite_length_array; +#endif + return ((!indefinite_length_array && (state->elem_count == 0)) + || (indefinite_length_array + && (state->payload < state->payload_end) + && (*state->payload == 0xFF))); +} + + +static size_t update_map_elem_count(zcbor_state_t *state, size_t elem_count); +#ifdef ZCBOR_MAP_SMART_SEARCH +static bool allocate_map_flags(zcbor_state_t *state, size_t elem_count); +#endif + + +bool zcbor_unordered_map_start_decode(zcbor_state_t *state) +{ + PRINT_FUNC(); + ZCBOR_FAIL_IF(!zcbor_map_start_decode(state)); + +#ifdef ZCBOR_MAP_SMART_SEARCH + state->decode_state.map_search_elem_state + += zcbor_flags_to_bytes(state->decode_state.map_elem_count); +#else + state->decode_state.map_elems_processed = 0; +#endif + state->decode_state.map_elem_count = 0; + state->decode_state.counting_map_elems = state->decode_state.indefinite_length_array; + + if (!state->decode_state.counting_map_elems) { + size_t old_flags = update_map_elem_count(state, state->elem_count); +#ifdef ZCBOR_MAP_SMART_SEARCH + ZCBOR_FAIL_IF(!allocate_map_flags(state, old_flags)); +#endif + (void)old_flags; + } + + return true; +} + + +/** Return the max (starting) elem_count of the current container. + * + * Should only be used for unordered maps (started with @ref zcbor_unordered_map_start_decode) + */ +static size_t zcbor_current_max_elem_count(zcbor_state_t *state) +{ + return (state->decode_state.indefinite_length_array ? \ + ZCBOR_LARGE_ELEM_COUNT : state->decode_state.map_elem_count * 2); +} + + +static bool map_restart(zcbor_state_t *state) +{ + if (!zcbor_process_backup(state, ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_KEEP_DECODE_STATE, + ZCBOR_MAX_ELEM_COUNT)) { + ZCBOR_FAIL(); + } + + state->elem_count = zcbor_current_max_elem_count(state); + return true; +} + + +__attribute__((used)) +static size_t get_current_index(zcbor_state_t *state, uint32_t index_offset) +{ + /* Subtract mode because for GET, you want the index you are pointing to, while for SET, + * you want the one you just processed. This only comes into play when elem_count is even. */ + return ((zcbor_current_max_elem_count(state) - state->elem_count - index_offset) / 2); +} + + +#ifdef ZCBOR_MAP_SMART_SEARCH +#define FLAG_MODE_GET_CURRENT 0 +#define FLAG_MODE_CLEAR_CURRENT 1 +#define FLAG_MODE_CLEAR_UNUSED 2 + +static bool manipulate_flags(zcbor_state_t *state, uint32_t mode) +{ + const size_t last_index = (state->decode_state.map_elem_count - 1); + size_t index = (mode == FLAG_MODE_CLEAR_UNUSED) ? last_index : get_current_index(state, mode); + + ZCBOR_ERR_IF((index >= state->decode_state.map_elem_count), + ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE); + uint8_t *flag_byte = &state->decode_state.map_search_elem_state[index >> 3]; + uint8_t flag_mask = (uint8_t)(1 << (index & 7)); + + switch(mode) { + case FLAG_MODE_GET_CURRENT: + return (!!(*flag_byte & flag_mask)); + case FLAG_MODE_CLEAR_CURRENT: + *flag_byte &= ~flag_mask; + return true; + case FLAG_MODE_CLEAR_UNUSED: + *flag_byte &= (uint8_t)((flag_mask << 1) - 1); + return true; + } + return false; +} + + +static bool should_try_key(zcbor_state_t *state) +{ + return manipulate_flags(state, FLAG_MODE_GET_CURRENT); +} + + +bool zcbor_elem_processed(zcbor_state_t *state) +{ + return manipulate_flags(state, FLAG_MODE_CLEAR_CURRENT); +} + + +static bool allocate_map_flags(zcbor_state_t *state, size_t old_flags) +{ + size_t new_bytes = zcbor_flags_to_bytes(state->decode_state.map_elem_count); + size_t old_bytes = zcbor_flags_to_bytes(old_flags); + size_t extra_bytes = new_bytes - old_bytes; + const uint8_t *flags_end = state->constant_state->map_search_elem_state_end; + + if (extra_bytes) { + if ((state->decode_state.map_search_elem_state + new_bytes) > flags_end) { + state->decode_state.map_elem_count + = 8 * (size_t)(flags_end - state->decode_state.map_search_elem_state); + ZCBOR_ERR(ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE); + } + + memset(&state->decode_state.map_search_elem_state[new_bytes - extra_bytes], 0xFF, extra_bytes); + } + return true; +} +#else + +static bool should_try_key(zcbor_state_t *state) +{ + return (state->decode_state.map_elems_processed < state->decode_state.map_elem_count); +} + + +bool zcbor_elem_processed(zcbor_state_t *state) +{ + if (should_try_key(state)) { + state->decode_state.map_elems_processed++; + } + return true; +} +#endif + + +static size_t update_map_elem_count(zcbor_state_t *state, size_t elem_count) +{ + size_t old_map_elem_count = state->decode_state.map_elem_count; + + state->decode_state.map_elem_count = MAX(old_map_elem_count, elem_count / 2); + return old_map_elem_count; +} + + +static bool handle_map_end(zcbor_state_t *state) +{ + state->decode_state.counting_map_elems = false; + return map_restart(state); +} + + +static bool try_key(zcbor_state_t *state, void *key_result, zcbor_decoder_t key_decoder) +{ + uint8_t const *payload_bak2 = state->payload; + size_t elem_count_bak = state->elem_count; + + if (!key_decoder(state, (uint8_t *)key_result)) { + state->payload = payload_bak2; + state->elem_count = elem_count_bak; + return false; + } + + zcbor_log("Found element at index %zu.\n", get_current_index(state, 1)); + return true; +} + + +bool zcbor_unordered_map_search(zcbor_decoder_t key_decoder, zcbor_state_t *state, void *key_result) +{ + PRINT_FUNC(); + /* elem_count cannot be odd since the map consists of key-value-pairs. + * This might mean that this function was called while pointing at a value (instead + * of a key). */ + ZCBOR_ERR_IF(state->elem_count & 1, ZCBOR_ERR_MAP_MISALIGNED); + + uint8_t const *payload_bak = state->payload; + size_t elem_count = state->elem_count; + + /* Loop once through all the elements of the map. */ + do { + if (zcbor_array_at_end(state)) { + if (!handle_map_end(state)) { + goto error; + } + continue; /* This continue is needed so the loop stops both if elem_count is + * at the very start or the very end of the map. */ + } + + if (state->decode_state.counting_map_elems) { + size_t m_elem_count = ZCBOR_LARGE_ELEM_COUNT - state->elem_count + 2; + size_t old_flags = update_map_elem_count(state, m_elem_count); +#ifdef ZCBOR_MAP_SMART_SEARCH + ZCBOR_FAIL_IF(!allocate_map_flags(state, old_flags)); +#endif + (void)old_flags; + } + + if (should_try_key(state) && try_key(state, key_result, key_decoder)) { + if (!state->constant_state->manually_process_elem) { + ZCBOR_FAIL_IF(!zcbor_elem_processed(state)); + } + return true; + } + + /* Skip over both the key and the value. */ + if (!zcbor_any_skip(state, NULL) || !zcbor_any_skip(state, NULL)) { + goto error; + } + } while (state->elem_count != elem_count); + + zcbor_error(state, ZCBOR_ERR_ELEM_NOT_FOUND); +error: + state->payload = payload_bak; + state->elem_count = elem_count; + ZCBOR_FAIL(); +} + + +bool zcbor_search_key_bstr_ptr(zcbor_state_t *state, char const *ptr, size_t len) +{ + struct zcbor_string zs = { .value = (const uint8_t *)ptr, .len = len }; + + return zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_bstr_expect, state, &zs); +} + + +bool zcbor_search_key_tstr_ptr(zcbor_state_t *state, char const *ptr, size_t len) +{ + struct zcbor_string zs = { .value = (const uint8_t *)ptr, .len = len }; + + return zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_tstr_expect, state, &zs); +} + + +bool zcbor_search_key_bstr_term(zcbor_state_t *state, char const *str, size_t maxlen) +{ + return zcbor_search_key_bstr_ptr(state, str, strnlen(str, maxlen)); +} + + +bool zcbor_search_key_tstr_term(zcbor_state_t *state, char const *str, size_t maxlen) +{ + return zcbor_search_key_tstr_ptr(state, str, strnlen(str, maxlen)); +} + + +static bool array_end_expect(zcbor_state_t *state) +{ + INITIAL_CHECKS(); + ZCBOR_ERR_IF(*state->payload != 0xFF, ZCBOR_ERR_WRONG_TYPE); + + state->payload++; + return true; +} + + +static bool list_map_end_decode(zcbor_state_t *state) +{ + size_t max_elem_count = 0; + +#ifndef ZCBOR_CANONICAL + if (state->decode_state.indefinite_length_array) { + if (!array_end_expect(state)) { + ZCBOR_FAIL(); + } + max_elem_count = ZCBOR_MAX_ELEM_COUNT; + state->decode_state.indefinite_length_array = false; + } +#endif + if (!zcbor_process_backup(state, + ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME | ZCBOR_FLAG_KEEP_PAYLOAD, + max_elem_count)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_list_end_decode(zcbor_state_t *state) +{ + PRINT_FUNC(); + return list_map_end_decode(state); +} + + +bool zcbor_map_end_decode(zcbor_state_t *state) +{ + PRINT_FUNC(); + return list_map_end_decode(state); +} + + +bool zcbor_unordered_map_end_decode(zcbor_state_t *state) +{ + /* Checking zcbor_array_at_end() ensures that check is valid. + * In case the map is at the end, but state->decode_state.counting_map_elems isn't updated.*/ + ZCBOR_ERR_IF(!zcbor_array_at_end(state) && state->decode_state.counting_map_elems, + ZCBOR_ERR_ELEMS_NOT_PROCESSED); + + if (state->decode_state.map_elem_count > 0) { +#ifdef ZCBOR_MAP_SMART_SEARCH + manipulate_flags(state, FLAG_MODE_CLEAR_UNUSED); + + for (size_t i = 0; i < zcbor_flags_to_bytes(state->decode_state.map_elem_count); i++) { + if (state->decode_state.map_search_elem_state[i] != 0) { + zcbor_log("unprocessed element(s) in map: [%zu] = 0x%02x\n", + i, state->decode_state.map_search_elem_state[i]); + ZCBOR_ERR(ZCBOR_ERR_ELEMS_NOT_PROCESSED); + } + } +#else + ZCBOR_ERR_IF(should_try_key(state), ZCBOR_ERR_ELEMS_NOT_PROCESSED); +#endif + } + while (!zcbor_array_at_end(state)) { + zcbor_any_skip(state, NULL); + } + return zcbor_map_end_decode(state); +} + + +bool zcbor_list_map_end_force_decode(zcbor_state_t *state) +{ + if (!zcbor_process_backup(state, + ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME | ZCBOR_FLAG_KEEP_PAYLOAD, + ZCBOR_MAX_ELEM_COUNT)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_simple_decode(zcbor_state_t *state, uint8_t *result) +{ + PRINT_FUNC(); + PRINT_FUNC(); + INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_SIMPLE); + + /* Simple values must be 0-23 (additional is 0-23) or 24-255 (additional is 24). + * Other additional values are not considered simple values. */ + ZCBOR_ERR_IF(ZCBOR_ADDITIONAL(*state->payload) > 24, ZCBOR_ERR_WRONG_TYPE); + + if (!value_extract(state, result, sizeof(*result))) { + ZCBOR_FAIL(); + } + return true; +} + + +bool zcbor_simple_expect(zcbor_state_t *state, uint8_t expected) +{ + PRINT_FUNC(); + uint8_t actual; + + if (!zcbor_simple_decode(state, &actual)) { + ZCBOR_FAIL(); + } + + if (actual != expected) { + zcbor_log("simple value %u != %u\r\n", actual, expected); + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + + return true; +} + + +bool zcbor_simple_pexpect(zcbor_state_t *state, uint8_t *expected) +{ + PRINT_FUNC(); + return zcbor_simple_expect(state, *expected); +} + + +bool zcbor_nil_expect(zcbor_state_t *state, void *unused) +{ + PRINT_FUNC(); + (void)unused; + return zcbor_simple_expect(state, 22); +} + + +bool zcbor_undefined_expect(zcbor_state_t *state, void *unused) +{ + PRINT_FUNC(); + (void)unused; + return zcbor_simple_expect(state, 23); +} + + +bool zcbor_bool_decode(zcbor_state_t *state, bool *result) +{ + PRINT_FUNC(); + uint8_t value; + + if (!zcbor_simple_decode(state, &value)) { + ZCBOR_FAIL(); + } + value -= ZCBOR_BOOL_TO_SIMPLE; + if (value > 1) { + ERR_RESTORE(ZCBOR_ERR_WRONG_TYPE); + } + *result = value; + + zcbor_log("boolval: %u\r\n", *result); + return true; +} + + +bool zcbor_bool_expect(zcbor_state_t *state, bool expected) +{ + PRINT_FUNC(); + return zcbor_simple_expect(state, (uint8_t)(!!expected) + ZCBOR_BOOL_TO_SIMPLE); +} + + +bool zcbor_bool_pexpect(zcbor_state_t *state, bool *expected) +{ + PRINT_FUNC(); + return zcbor_bool_expect(state, *expected); +} + + +static bool float_check(zcbor_state_t *state, uint8_t additional_val) +{ + INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_SIMPLE); + ZCBOR_ERR_IF(ZCBOR_ADDITIONAL(*state->payload) != additional_val, ZCBOR_ERR_FLOAT_SIZE); + return true; +} + + +bool zcbor_float16_bytes_decode(zcbor_state_t *state, uint16_t *result) +{ + PRINT_FUNC(); + ZCBOR_FAIL_IF(!float_check(state, ZCBOR_VALUE_IS_2_BYTES)); + + if (!value_extract(state, result, sizeof(*result))) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float16_bytes_expect(zcbor_state_t *state, uint16_t expected) +{ + PRINT_FUNC(); + uint16_t actual; + + if (!zcbor_float16_bytes_decode(state, &actual)) { + ZCBOR_FAIL(); + } + if (actual != expected) { + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_float16_bytes_pexpect(zcbor_state_t *state, uint16_t *expected) +{ + PRINT_FUNC(); + return zcbor_float16_bytes_expect(state, *expected); +} + + +bool zcbor_float16_decode(zcbor_state_t *state, float *result) +{ + PRINT_FUNC(); + uint16_t value16; + + if (!zcbor_float16_bytes_decode(state, &value16)) { + ZCBOR_FAIL(); + } + + *result = zcbor_float16_to_32(value16); + return true; +} + + +bool zcbor_float16_expect(zcbor_state_t *state, float expected) +{ + PRINT_FUNC(); + float actual; + + if (!zcbor_float16_decode(state, &actual)) { + ZCBOR_FAIL(); + } + if (actual != expected) { + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_float16_pexpect(zcbor_state_t *state, float *expected) +{ + PRINT_FUNC(); + return zcbor_float16_expect(state, *expected); +} + + +bool zcbor_float32_decode(zcbor_state_t *state, float *result) +{ + PRINT_FUNC(); + ZCBOR_FAIL_IF(!float_check(state, ZCBOR_VALUE_IS_4_BYTES)); + + if (!value_extract(state, result, sizeof(*result))) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float32_expect(zcbor_state_t *state, float expected) +{ + PRINT_FUNC(); + float actual; + + if (!zcbor_float32_decode(state, &actual)) { + ZCBOR_FAIL(); + } + if (actual != expected) { + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_float32_pexpect(zcbor_state_t *state, float *expected) +{ + PRINT_FUNC(); + return zcbor_float32_expect(state, *expected); +} + + +bool zcbor_float16_32_decode(zcbor_state_t *state, float *result) +{ + PRINT_FUNC(); + if (zcbor_float16_decode(state, result)) { + /* Do nothing */ + } else if (!zcbor_float32_decode(state, result)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float16_32_expect(zcbor_state_t *state, float expected) +{ + PRINT_FUNC(); + if (zcbor_float16_expect(state, expected)) { + /* Do nothing */ + } else if (!zcbor_float32_expect(state, expected)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float16_32_pexpect(zcbor_state_t *state, float *expected) +{ + PRINT_FUNC(); + return zcbor_float16_32_expect(state, *expected); +} + + +bool zcbor_float64_decode(zcbor_state_t *state, double *result) +{ + PRINT_FUNC(); + ZCBOR_FAIL_IF(!float_check(state, ZCBOR_VALUE_IS_8_BYTES)); + + if (!value_extract(state, result, sizeof(*result))) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float64_expect(zcbor_state_t *state, double expected) +{ + PRINT_FUNC(); + double actual; + + if (!zcbor_float64_decode(state, &actual)) { + ZCBOR_FAIL(); + } + if (actual != expected) { + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_float64_pexpect(zcbor_state_t *state, double *expected) +{ + PRINT_FUNC(); + return zcbor_float64_expect(state, *expected); +} + + +bool zcbor_float32_64_decode(zcbor_state_t *state, double *result) +{ + PRINT_FUNC(); + float float_result; + + if (zcbor_float32_decode(state, &float_result)) { + *result = (double)float_result; + } else if (!zcbor_float64_decode(state, result)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float32_64_expect(zcbor_state_t *state, double expected) +{ + PRINT_FUNC(); + if (zcbor_float64_expect(state, expected)) { + /* Do nothing */ + } else if (!zcbor_float32_expect(state, (float)expected)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float32_64_pexpect(zcbor_state_t *state, double *expected) +{ + PRINT_FUNC(); + return zcbor_float32_64_expect(state, *expected); +} + + +bool zcbor_float_decode(zcbor_state_t *state, double *result) +{ + PRINT_FUNC(); + float float_result; + + if (zcbor_float16_decode(state, &float_result)) { + *result = (double)float_result; + } else if (zcbor_float32_decode(state, &float_result)) { + *result = (double)float_result; + } else if (!zcbor_float64_decode(state, result)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float_expect(zcbor_state_t *state, double expected) +{ + PRINT_FUNC(); + if (zcbor_float16_expect(state, (float)expected)) { + /* Do nothing */ + } else if (zcbor_float32_expect(state, (float)expected)) { + /* Do nothing */ + } else if (!zcbor_float64_expect(state, expected)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float_pexpect(zcbor_state_t *state, double *expected) +{ + PRINT_FUNC(); + return zcbor_float_expect(state, *expected); +} + + +bool zcbor_any_skip(zcbor_state_t *state, void *result) +{ + PRINT_FUNC(); + zcbor_assert_state(result == NULL, + "'any' type cannot be returned, only skipped.\r\n"); + (void)result; + + INITIAL_CHECKS(); + zcbor_major_type_t major_type = ZCBOR_MAJOR_TYPE(*state->payload); + uint8_t additional = ZCBOR_ADDITIONAL(*state->payload); + uint64_t value = 0; /* In case of indefinite_length_array. */ + zcbor_state_t state_copy; + + memcpy(&state_copy, state, sizeof(zcbor_state_t)); + + while (major_type == ZCBOR_MAJOR_TYPE_TAG) { + uint32_t tag_dummy; + + if (!zcbor_tag_decode(&state_copy, &tag_dummy)) { + ZCBOR_FAIL(); + } + ZCBOR_ERR_IF(state_copy.payload >= state_copy.payload_end, ZCBOR_ERR_NO_PAYLOAD); + major_type = ZCBOR_MAJOR_TYPE(*state_copy.payload); + additional = ZCBOR_ADDITIONAL(*state_copy.payload); + } + +#ifdef ZCBOR_CANONICAL + const bool indefinite_length_array = false; +#else + const bool indefinite_length_array = ((additional == ZCBOR_VALUE_IS_INDEFINITE_LENGTH) + && ((major_type == ZCBOR_MAJOR_TYPE_LIST) || (major_type == ZCBOR_MAJOR_TYPE_MAP))); +#endif + + if (!indefinite_length_array && !value_extract(&state_copy, &value, sizeof(value))) { + /* Can happen because of elem_count (or payload_end) */ + ZCBOR_FAIL(); + } + + switch (major_type) { + case ZCBOR_MAJOR_TYPE_BSTR: + case ZCBOR_MAJOR_TYPE_TSTR: + /* 'value' is the length of the BSTR or TSTR. + * The subtraction is safe because value_extract() above + * checks that payload_end is greater than payload. */ + ZCBOR_ERR_IF( + value > (uint64_t)(state_copy.payload_end - state_copy.payload), + ZCBOR_ERR_NO_PAYLOAD); + (state_copy.payload) += value; + break; + case ZCBOR_MAJOR_TYPE_MAP: + ZCBOR_ERR_IF(value > (SIZE_MAX / 2), ZCBOR_ERR_INT_SIZE); + value *= 2; + /* fallthrough */ + case ZCBOR_MAJOR_TYPE_LIST: + if (indefinite_length_array) { + state_copy.payload++; + value = ZCBOR_LARGE_ELEM_COUNT; + } + state_copy.elem_count = (size_t)value; + state_copy.decode_state.indefinite_length_array = indefinite_length_array; + while (!zcbor_array_at_end(&state_copy)) { + if (!zcbor_any_skip(&state_copy, NULL)) { + ZCBOR_FAIL(); + } + } + if (indefinite_length_array && !array_end_expect(&state_copy)) { + ZCBOR_FAIL(); + } + break; + default: + /* Do nothing */ + break; + } + + state->payload = state_copy.payload; + state->elem_count--; + + return true; +} + + +bool zcbor_tag_decode(zcbor_state_t *state, uint32_t *result) +{ + PRINT_FUNC(); + INITIAL_CHECKS_WITH_TYPE(ZCBOR_MAJOR_TYPE_TAG); + + if (!value_extract(state, result, sizeof(*result))) { + ZCBOR_FAIL(); + } + state->elem_count++; + return true; +} + + +bool zcbor_tag_expect(zcbor_state_t *state, uint32_t expected) +{ + PRINT_FUNC(); + uint32_t actual; + + if (!zcbor_tag_decode(state, &actual)) { + ZCBOR_FAIL(); + } + if (actual != expected) { + ERR_RESTORE(ZCBOR_ERR_WRONG_VALUE); + } + return true; +} + + +bool zcbor_tag_pexpect(zcbor_state_t *state, uint32_t *expected) +{ + PRINT_FUNC(); + return zcbor_tag_expect(state, *expected); +} + + +bool zcbor_multi_decode(size_t min_decode, + size_t max_decode, + size_t *num_decode, + zcbor_decoder_t decoder, + zcbor_state_t *state, + void *result, + size_t result_len) +{ + PRINT_FUNC(); + ZCBOR_CHECK_ERROR(); + for (size_t i = 0; i < max_decode; i++) { + uint8_t const *payload_bak = state->payload; + size_t elem_count_bak = state->elem_count; + + if (!decoder(state, + (uint8_t *)result + i*result_len)) { + *num_decode = i; + state->payload = payload_bak; + state->elem_count = elem_count_bak; + ZCBOR_ERR_IF(i < min_decode, ZCBOR_ERR_ITERATIONS); + zcbor_log("Found %zu elements.\r\n", i); + return true; + } + } + zcbor_log("Found %zu elements.\r\n", max_decode); + *num_decode = max_decode; + return true; +} + + +bool zcbor_present_decode(bool *present, + zcbor_decoder_t decoder, + zcbor_state_t *state, + void *result) +{ + PRINT_FUNC(); + size_t num_decode = 0; + bool retval = zcbor_multi_decode(0, 1, &num_decode, decoder, state, result, 0); + + zcbor_assert_state(retval, "zcbor_multi_decode should not fail with these parameters.\r\n"); + + *present = !!num_decode; + return retval; +} + + +void zcbor_new_decode_state(zcbor_state_t *state_array, size_t n_states, + const uint8_t *payload, size_t payload_len, size_t elem_count, + uint8_t *flags, size_t flags_bytes) +{ + zcbor_new_state(state_array, n_states, payload, payload_len, elem_count, flags, flags_bytes); +} diff --git a/bootloader/mcuboot/boot/zcbor/src/zcbor_encode.c b/bootloader/mcuboot/boot/zcbor/src/zcbor_encode.c new file mode 100644 index 0000000..53d19c0 --- /dev/null +++ b/bootloader/mcuboot/boot/zcbor/src/zcbor_encode.c @@ -0,0 +1,614 @@ +/* + * This file has been copied from the zcbor library. + * Commit zcbor 0.8.1 + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "zcbor_encode.h" +#include "zcbor_common.h" +#include "zcbor_print.h" + +_Static_assert((sizeof(size_t) == sizeof(void *)), + "This code needs size_t to be the same length as pointers."); + + +static uint8_t log2ceil(size_t val) +{ + switch(val) { + case 1: return 0; + case 2: return 1; + case 3: return 2; + case 4: return 2; + case 5: return 3; + case 6: return 3; + case 7: return 3; + case 8: return 3; + } + + zcbor_log("Should not come here.\r\n"); + return 0; +} + + +static uint8_t get_additional(size_t len, uint8_t value0) +{ + return len == 0 ? value0 : (uint8_t)(24 + log2ceil(len)); +} + + +static bool encode_header_byte(zcbor_state_t *state, + zcbor_major_type_t major_type, uint8_t additional) +{ + ZCBOR_CHECK_ERROR(); + ZCBOR_CHECK_PAYLOAD(); + + zcbor_assert_state(additional < 32, "Unsupported additional value: %d\r\n", additional); + + *(state->payload_mut++) = (uint8_t)((major_type << 5) | (additional & 0x1F)); + return true; +} + + +/** Encode a single value. + */ +static bool value_encode_len(zcbor_state_t *state, zcbor_major_type_t major_type, + const void *const result, size_t result_len) +{ + uint8_t *u8_result = (uint8_t *)result; + + if ((state->payload + 1 + result_len) > state->payload_end) { + ZCBOR_ERR(ZCBOR_ERR_NO_PAYLOAD); + } + + if (!encode_header_byte(state, major_type, + get_additional(result_len, u8_result[0]))) { + ZCBOR_FAIL(); + } + state->payload_mut--; + zcbor_trace(state, "value_encode_len"); + state->payload_mut++; + +#ifdef ZCBOR_BIG_ENDIAN + memcpy(state->payload_mut, u8_result, result_len); + state->payload_mut += result_len; +#else + for (; result_len > 0; result_len--) { + *(state->payload_mut++) = u8_result[result_len - 1]; + } +#endif /* ZCBOR_BIG_ENDIAN */ + + state->elem_count++; + return true; +} + + +static bool value_encode(zcbor_state_t *state, zcbor_major_type_t major_type, + const void *const input, size_t max_result_len) +{ + zcbor_assert_state(max_result_len != 0, "0-length result not supported.\r\n"); + + size_t result_len = zcbor_header_len_ptr(input, max_result_len) - 1; + const void *result = input; + +#ifdef ZCBOR_BIG_ENDIAN + result = (uint8_t *)input + max_result_len - (result_len ? result_len : 1); +#endif + + return value_encode_len(state, major_type, result, result_len); +} + + +bool zcbor_int_encode(zcbor_state_t *state, const void *input_int, size_t int_size) +{ + zcbor_major_type_t major_type; + uint8_t input_buf[8]; + const uint8_t *input_uint8 = input_int; + const int8_t *input_int8 = input_int; + const uint8_t *input = input_int; + + if (int_size > sizeof(int64_t)) { + ZCBOR_ERR(ZCBOR_ERR_INT_SIZE); + } + +#ifdef ZCBOR_BIG_ENDIAN + if (input_int8[0] < 0) { +#else + if (input_int8[int_size - 1] < 0) { +#endif + major_type = ZCBOR_MAJOR_TYPE_NINT; + + /* Convert to CBOR's representation by flipping all bits. */ + for (unsigned int i = 0; i < int_size; i++) { + input_buf[i] = (uint8_t)~input_uint8[i]; + } + input = input_buf; + } else { + major_type = ZCBOR_MAJOR_TYPE_PINT; + } + + if (!value_encode(state, major_type, input, int_size)) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_uint_encode(zcbor_state_t *state, const void *input_uint, size_t uint_size) +{ + if (!value_encode(state, ZCBOR_MAJOR_TYPE_PINT, input_uint, uint_size)) { + zcbor_log("uint with size %zu failed.\r\n", uint_size); + ZCBOR_FAIL(); + } + return true; +} + + +bool zcbor_int32_encode(zcbor_state_t *state, const int32_t *input) +{ + return zcbor_int_encode(state, input, sizeof(*input)); +} + + +bool zcbor_int64_encode(zcbor_state_t *state, const int64_t *input) +{ + return zcbor_int_encode(state, input, sizeof(*input)); +} + + +bool zcbor_uint32_encode(zcbor_state_t *state, const uint32_t *input) +{ + return zcbor_uint_encode(state, input, sizeof(*input)); +} + + +bool zcbor_uint64_encode(zcbor_state_t *state, const uint64_t *input) +{ + return zcbor_uint_encode(state, input, sizeof(*input)); +} + + +bool zcbor_int32_put(zcbor_state_t *state, int32_t input) +{ + return zcbor_int_encode(state, &input, sizeof(input)); +} + + +bool zcbor_int64_put(zcbor_state_t *state, int64_t input) +{ + return zcbor_int_encode(state, &input, sizeof(input)); +} + + +bool zcbor_uint32_put(zcbor_state_t *state, uint32_t input) +{ + return zcbor_uint_encode(state, &input, sizeof(input)); +} + + +bool zcbor_uint64_put(zcbor_state_t *state, uint64_t input) +{ + return zcbor_uint_encode(state, &input, sizeof(input)); +} + + +#ifdef ZCBOR_SUPPORTS_SIZE_T +bool zcbor_size_put(zcbor_state_t *state, size_t input) +{ + return zcbor_uint_encode(state, &input, sizeof(input)); +} + + +bool zcbor_size_encode(zcbor_state_t *state, const size_t *input) +{ + return zcbor_uint_encode(state, input, sizeof(*input)); +} +#endif + +static bool str_start_encode(zcbor_state_t *state, + const struct zcbor_string *input, zcbor_major_type_t major_type) +{ + if (input->value && ((zcbor_header_len_ptr(&input->len, sizeof(input->len)) + + input->len + (size_t)state->payload) + > (size_t)state->payload_end)) { + ZCBOR_ERR(ZCBOR_ERR_NO_PAYLOAD); + } + if (!value_encode(state, major_type, &input->len, sizeof(input->len))) { + ZCBOR_FAIL(); + } + + return true; +} + + +static size_t remaining_str_len(zcbor_state_t *state) +{ + size_t max_len = (size_t)state->payload_end - (size_t)state->payload; + size_t result_len = zcbor_header_len_ptr(&max_len, sizeof(max_len)) - 1; + + return max_len - result_len - 1; +} + + +bool zcbor_bstr_start_encode(zcbor_state_t *state) +{ + if (!zcbor_new_backup(state, 0)) { + ZCBOR_FAIL(); + } + + uint64_t max_len = remaining_str_len(state); + + /* Encode a dummy header */ + if (!value_encode(state, ZCBOR_MAJOR_TYPE_BSTR, &max_len, sizeof(max_len))) { + ZCBOR_FAIL(); + } + return true; +} + + +bool zcbor_bstr_end_encode(zcbor_state_t *state, struct zcbor_string *result) +{ + const uint8_t *payload = state->payload; + struct zcbor_string dummy_value; + + if (result == NULL) { + /* Use a dummy value for the sake of the length calculation below. + * Will not be returned. + */ + result = &dummy_value; + } + + if (!zcbor_process_backup(state, ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME, 0xFFFFFFFF)) { + ZCBOR_FAIL(); + } + + result->value = state->payload_end - remaining_str_len(state); + result->len = (size_t)payload - (size_t)result->value; + + /* Reencode header of list now that we know the number of elements. */ + if (!zcbor_bstr_encode(state, result)) { + ZCBOR_FAIL(); + } + return true; +} + + +static bool str_encode(zcbor_state_t *state, + const struct zcbor_string *input, zcbor_major_type_t major_type) +{ + ZCBOR_CHECK_PAYLOAD(); /* To make the size_t cast below safe. */ + if (input->len > (size_t)(state->payload_end - state->payload)) { + ZCBOR_ERR(ZCBOR_ERR_NO_PAYLOAD); + } + if (!str_start_encode(state, input, major_type)) { + ZCBOR_FAIL(); + } + if (state->payload_mut != input->value) { + /* Use memmove since string might be encoded into the same space + * because of bstrx_cbor_start_encode/bstrx_cbor_end_encode. */ + memmove(state->payload_mut, input->value, input->len); + } + state->payload += input->len; + return true; +} + + +bool zcbor_bstr_encode(zcbor_state_t *state, const struct zcbor_string *input) +{ + return str_encode(state, input, ZCBOR_MAJOR_TYPE_BSTR); +} + + +bool zcbor_tstr_encode(zcbor_state_t *state, const struct zcbor_string *input) +{ + return str_encode(state, input, ZCBOR_MAJOR_TYPE_TSTR); +} + + +bool zcbor_bstr_encode_ptr(zcbor_state_t *state, const char *str, size_t len) +{ + const struct zcbor_string zs = { .value = (const uint8_t *)str, .len = len }; + + return zcbor_bstr_encode(state, &zs); +} + + +bool zcbor_tstr_encode_ptr(zcbor_state_t *state, const char *str, size_t len) +{ + const struct zcbor_string zs = { .value = (const uint8_t *)str, .len = len }; + + return zcbor_tstr_encode(state, &zs); +} + + +bool zcbor_bstr_put_term(zcbor_state_t *state, char const *str, size_t maxlen) +{ + return zcbor_bstr_encode_ptr(state, str, strnlen(str, maxlen)); +} + + +bool zcbor_tstr_put_term(zcbor_state_t *state, char const *str, size_t maxlen) +{ + return zcbor_tstr_encode_ptr(state, str, strnlen(str, maxlen)); +} + + +static bool list_map_start_encode(zcbor_state_t *state, size_t max_num, + zcbor_major_type_t major_type) +{ +#ifdef ZCBOR_CANONICAL + if (!zcbor_new_backup(state, 0)) { + ZCBOR_FAIL(); + } + + /* Encode dummy header with max number of elements. */ + if (!value_encode(state, major_type, &max_num, sizeof(max_num))) { + ZCBOR_FAIL(); + } + state->elem_count--; /* Because of dummy header. */ +#else + (void)max_num; + + if (!encode_header_byte(state, major_type, ZCBOR_VALUE_IS_INDEFINITE_LENGTH)) { + ZCBOR_FAIL(); + } +#endif + return true; +} + + +bool zcbor_list_start_encode(zcbor_state_t *state, size_t max_num) +{ + return list_map_start_encode(state, max_num, ZCBOR_MAJOR_TYPE_LIST); +} + + +bool zcbor_map_start_encode(zcbor_state_t *state, size_t max_num) +{ + return list_map_start_encode(state, max_num, ZCBOR_MAJOR_TYPE_MAP); +} + + +static bool list_map_end_encode(zcbor_state_t *state, size_t max_num, + zcbor_major_type_t major_type) +{ +#ifdef ZCBOR_CANONICAL + size_t list_count = ((major_type == ZCBOR_MAJOR_TYPE_LIST) ? + state->elem_count + : (state->elem_count / 2)); + + const uint8_t *payload = state->payload; + + size_t max_header_len = zcbor_header_len_ptr(&max_num, 4) - 1; + size_t header_len = zcbor_header_len_ptr(&list_count, 4) - 1; + + if (!zcbor_process_backup(state, ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME, 0xFFFFFFFF)) { + ZCBOR_FAIL(); + } + + zcbor_log("list_count: %zu\r\n", list_count); + + + /** If max_num is smaller than the actual number of encoded elements, + * the value_encode() below will corrupt the data if the encoded + * header is larger than the previously encoded header. */ + if (header_len > max_header_len) { + zcbor_log("max_num too small.\r\n"); + ZCBOR_ERR(ZCBOR_ERR_HIGH_ELEM_COUNT); + } + + /* Reencode header of list now that we know the number of elements. */ + if (!(value_encode(state, major_type, &list_count, sizeof(list_count)))) { + ZCBOR_FAIL(); + } + + if (max_header_len != header_len) { + const uint8_t *start = state->payload + max_header_len - header_len; + size_t body_size = (size_t)payload - (size_t)start; + + memmove(state->payload_mut, start, body_size); + /* Reset payload pointer to end of list */ + state->payload += body_size; + } else { + /* Reset payload pointer to end of list */ + state->payload = payload; + } +#else + (void)max_num; + (void)major_type; + if (!encode_header_byte(state, ZCBOR_MAJOR_TYPE_SIMPLE, ZCBOR_VALUE_IS_INDEFINITE_LENGTH)) { + ZCBOR_FAIL(); + } +#endif + return true; +} + + +bool zcbor_list_end_encode(zcbor_state_t *state, size_t max_num) +{ + return list_map_end_encode(state, max_num, ZCBOR_MAJOR_TYPE_LIST); +} + + +bool zcbor_map_end_encode(zcbor_state_t *state, size_t max_num) +{ + return list_map_end_encode(state, max_num, ZCBOR_MAJOR_TYPE_MAP); +} + + +bool zcbor_list_map_end_force_encode(zcbor_state_t *state) +{ +#ifdef ZCBOR_CANONICAL + if (!zcbor_process_backup(state, ZCBOR_FLAG_RESTORE | ZCBOR_FLAG_CONSUME, + ZCBOR_MAX_ELEM_COUNT)) { + ZCBOR_FAIL(); + } +#endif + (void)state; + return true; +} + + +bool zcbor_simple_encode(zcbor_state_t *state, uint8_t *input) +{ + if (!value_encode(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, sizeof(*input))) { + zcbor_log("Error encoding %u (0x%p)\r\n", *input, input); + ZCBOR_FAIL(); + } + return true; +} + + +bool zcbor_simple_put(zcbor_state_t *state, uint8_t input) +{ + return value_encode(state, ZCBOR_MAJOR_TYPE_SIMPLE, &input, sizeof(input)); +} + + +bool zcbor_nil_put(zcbor_state_t *state, const void *unused) +{ + (void)unused; + return zcbor_simple_put(state, 22); +} + + +bool zcbor_undefined_put(zcbor_state_t *state, const void *unused) +{ + (void)unused; + return zcbor_simple_put(state, 23); +} + + +bool zcbor_bool_encode(zcbor_state_t *state, const bool *input) +{ + return zcbor_bool_put(state, *input); +} + + +bool zcbor_bool_put(zcbor_state_t *state, bool input) +{ + return zcbor_simple_put(state, (!!input + ZCBOR_BOOL_TO_SIMPLE)); +} + + +bool zcbor_float64_encode(zcbor_state_t *state, const double *input) +{ + if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, + sizeof(*input))) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float64_put(zcbor_state_t *state, double input) +{ + return zcbor_float64_encode(state, &input); +} + + +bool zcbor_float32_encode(zcbor_state_t *state, const float *input) +{ + if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, + sizeof(*input))) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float32_put(zcbor_state_t *state, float input) +{ + return zcbor_float32_encode(state, &input); +} + + +bool zcbor_float16_encode(zcbor_state_t *state, const float *input) +{ + return zcbor_float16_put(state, *input); +} + + +bool zcbor_float16_put(zcbor_state_t *state, float input) +{ + return zcbor_float16_bytes_put(state, zcbor_float32_to_16(input)); +} + + +bool zcbor_float16_bytes_encode(zcbor_state_t *state, const uint16_t *input) +{ + if (!value_encode_len(state, ZCBOR_MAJOR_TYPE_SIMPLE, input, + sizeof(*input))) { + ZCBOR_FAIL(); + } + + return true; +} + + +bool zcbor_float16_bytes_put(zcbor_state_t *state, uint16_t input) +{ + return zcbor_float16_bytes_encode(state, &input); +} + + +bool zcbor_tag_put(zcbor_state_t *state, uint32_t tag) +{ + if (!value_encode(state, ZCBOR_MAJOR_TYPE_TAG, &tag, sizeof(tag))) { + ZCBOR_FAIL(); + } + state->elem_count--; + + return true; +} + + +bool zcbor_tag_encode(zcbor_state_t *state, uint32_t *tag) +{ + return zcbor_tag_put(state, *tag); +} + + +bool zcbor_multi_encode_minmax(size_t min_encode, size_t max_encode, + const size_t *num_encode, zcbor_encoder_t encoder, + zcbor_state_t *state, const void *input, size_t result_len) +{ + + if ((*num_encode >= min_encode) && (*num_encode <= max_encode)) { + return zcbor_multi_encode(*num_encode, encoder, state, input, result_len); + } else { + ZCBOR_ERR(ZCBOR_ERR_ITERATIONS); + } +} + + +bool zcbor_multi_encode(const size_t num_encode, zcbor_encoder_t encoder, + zcbor_state_t *state, const void *input, size_t result_len) +{ + ZCBOR_CHECK_ERROR(); + for (size_t i = 0; i < num_encode; i++) { + if (!encoder(state, (const uint8_t *)input + i*result_len)) { + ZCBOR_FAIL(); + } + } + zcbor_log("Encoded %zu elements.\n", num_encode); + return true; +} + + +void zcbor_new_encode_state(zcbor_state_t *state_array, size_t n_states, + uint8_t *payload, size_t payload_len, size_t elem_count) +{ + zcbor_new_state(state_array, n_states, payload, payload_len, elem_count, NULL, 0); +} diff --git a/bootloader/mcuboot/boot/zephyr/CMakeLists.txt b/bootloader/mcuboot/boot/zephyr/CMakeLists.txt new file mode 100644 index 0000000..3a34064 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/CMakeLists.txt @@ -0,0 +1,635 @@ +# CMakeLists.txt for building mcuboot as a Zephyr project +# +# Copyright (c) 2017 Open Source Foundries Limited +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +# find_package(Zephyr) in order to load application boilerplate: +# http://docs.zephyrproject.org/application/application.html +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(NONE) + +# Path to "boot" subdirectory of repository root. +get_filename_component(BOOT_DIR ${APPLICATION_SOURCE_DIR} DIRECTORY) +# Path to top-level repository root directory. +get_filename_component(MCUBOOT_DIR ${BOOT_DIR} DIRECTORY) +# Path to tinycrypt library source subdirectory of MCUBOOT_DIR. +set(TINYCRYPT_DIR "${MCUBOOT_DIR}/ext/tinycrypt/lib") +assert_exists(TINYCRYPT_DIR) +set(TINYCRYPT_SHA512_DIR "${MCUBOOT_DIR}/ext/tinycrypt-sha512/lib") +assert_exists(TINYCRYPT_SHA512_DIR) +# Path to crypto-fiat +set(FIAT_DIR "${MCUBOOT_DIR}/ext/fiat") +assert_exists(FIAT_DIR) +# Path to mbed-tls' asn1 parser library. +set(MBEDTLS_ASN1_DIR "${MCUBOOT_DIR}/ext/mbedtls-asn1") +assert_exists(MBEDTLS_ASN1_DIR) +set(MCUBOOT_NRF_EXT_DIR "${MCUBOOT_DIR}/ext/nrf") + +if(CONFIG_BOOT_USE_NRF_CC310_BL) + if(NOT EXISTS ${ZEPHYR_NRFXLIB_MODULE_DIR}) + message(FATAL_ERROR " + ------------------------------------------------------------------------ + No such file or directory: ${ZEPHYR_NRFXLIB_MODULE_DIR} + The current configuration enables nRF CC310 crypto accelerator hardware + with the `CONFIG_BOOT_USE_NRF_CC310_BL` option. Please follow + `ext/nrf/README.md` guide to fix your setup or use tinycrypt instead of + the HW accelerator. + To use the tinycrypt set `CONFIG_BOOT_ECDSA_TINYCRYPT` to y. + ------------------------------------------------------------------------") + endif() +endif() + +zephyr_library_include_directories( + include + targets + ) +if(EXISTS targets/${BOARD}.h) + zephyr_library_compile_definitions(MCUBOOT_TARGET_CONFIG="${BOARD}.h") +endif() + +if(DEFINED CONFIG_MBEDTLS) + zephyr_library_include_directories( + ${ZEPHYR_MBEDTLS_MODULE_DIR}/include + ) +endif() + +# Zephyr port-specific sources. +zephyr_library_sources( + main.c + io.c + flash_map_extended.c + os.c + keys.c + ) + +if(DEFINED CONFIG_ENABLE_MGMT_PERUSER) + zephyr_library_sources( + boot_serial_extensions.c + ) + + zephyr_linker_sources_ifdef( + CONFIG_ENABLE_MGMT_PERUSER + SECTIONS include/boot_serial/boot_serial.ld + ) + + if(DEFINED CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE OR DEFINED CONFIG_BOOT_MGMT_CUSTOM_IMG_LIST) + zephyr_library_sources( + boot_serial_extension_zephyr_basic.c + ) + endif() +endif() + +if(NOT DEFINED CONFIG_FLASH_PAGE_LAYOUT) + zephyr_library_sources( + flash_map_legacy.c + ) +endif() + +if(DEFINED CONFIG_BOOT_SHARE_BACKEND_RETENTION) + zephyr_library_sources( + shared_data.c + ) +endif() + +# Generic bootutil sources and includes. +zephyr_library_include_directories(${BOOT_DIR}/bootutil/include) +zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/image_validate.c + ${BOOT_DIR}/bootutil/src/tlv.c + ${BOOT_DIR}/bootutil/src/encrypted.c + ${BOOT_DIR}/bootutil/src/image_rsa.c + ${BOOT_DIR}/bootutil/src/image_ecdsa.c + ${BOOT_DIR}/bootutil/src/image_ed25519.c + ${BOOT_DIR}/bootutil/src/bootutil_misc.c + ${BOOT_DIR}/bootutil/src/fault_injection_hardening.c + ) + +if(DEFINED CONFIG_BOOT_ENCRYPT_X25519 AND DEFINED CONFIG_BOOT_ED25519_PSA) + zephyr_library_sources(${BOOT_DIR}/bootutil/src/encrypted_psa.c) +endif() + +if(DEFINED CONFIG_MEASURED_BOOT OR DEFINED CONFIG_BOOT_SHARE_DATA) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/boot_record.c + ) + + # Set a define for this file which will allow inclusion of the Zephyr version + # include file + set_source_files_properties( + ${BOOT_DIR}/bootutil/src/boot_record.c + PROPERTIES COMPILE_FLAGS -DZEPHYR_VER_INCLUDE=1 + ) +endif() + +# library which might be common source code for MCUBoot and an application +zephyr_link_libraries(MCUBOOT_BOOTUTIL) + +if(CONFIG_BOOT_FIH_PROFILE_HIGH) +zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/fault_injection_hardening_delay_rng_mbedtls.c + ) +endif() + +if(CONFIG_SINGLE_APPLICATION_SLOT) +zephyr_library_sources( + ${BOOT_DIR}/zephyr/single_loader.c + ) +zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) +elseif(CONFIG_BOOT_FIRMWARE_LOADER) +zephyr_library_sources( + ${BOOT_DIR}/zephyr/firmware_loader.c + ) +zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) +else() + if(NOT CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER EQUAL "-1") + # Sysbuild + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/loader.c + ) + else() + # Legacy child/parent image + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/loader_legacy_child_parent.c + ) + endif() +zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_misc.c + ${BOOT_DIR}/bootutil/src/swap_scratch.c + ${BOOT_DIR}/bootutil/src/swap_move.c + ${BOOT_DIR}/bootutil/src/caps.c + ) + + if(NOT CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER EQUAL "-1" AND NOT CONFIG_BOOT_UPGRADE_ONLY) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_nsib.c + ) + endif() +endif() + +if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256) + zephyr_library_include_directories( + ${MBEDTLS_ASN1_DIR}/include + ) + zephyr_library_sources( + # Additionally pull in just the ASN.1 parser from mbedTLS. + ${MBEDTLS_ASN1_DIR}/src/asn1parse.c + ${MBEDTLS_ASN1_DIR}/src/platform_util.c + ) + if(CONFIG_BOOT_USE_TINYCRYPT) + # When using ECDSA signatures, pull in our copy of the tinycrypt library. + zephyr_library_include_directories( + ${BOOT_DIR}/zephyr/include + ${TINYCRYPT_DIR}/include + ) + zephyr_include_directories(${TINYCRYPT_DIR}/include) + + zephyr_library_sources( + ${TINYCRYPT_DIR}/source/ecc.c + ${TINYCRYPT_DIR}/source/ecc_dsa.c + ${TINYCRYPT_DIR}/source/sha256.c + ${TINYCRYPT_DIR}/source/utils.c + ) + elseif(CONFIG_BOOT_USE_NRF_CC310_BL) + zephyr_library_sources(${MCUBOOT_NRF_EXT_DIR}/cc310_glue.c) + zephyr_library_include_directories(${MCUBOOT_NRF_EXT_DIR}) + zephyr_link_libraries(nrfxlib_crypto) + elseif(CONFIG_BOOT_USE_NRF_EXTERNAL_CRYPTO) + zephyr_include_directories(${BL_CRYPTO_DIR}/../include) + endif() + + # Since here we are not using Zephyr's mbedTLS but rather our own, we need + # to set MBEDTLS_CONFIG_FILE ourselves. When using Zephyr's copy, this + # variable is set by its Kconfig in the Zephyr codebase. + zephyr_library_compile_definitions( + MBEDTLS_CONFIG_FILE="${CMAKE_CURRENT_LIST_DIR}/include/mcuboot-mbedtls-cfg.h" + ) +elseif(CONFIG_BOOT_SIGNATURE_TYPE_NONE) + zephyr_library_include_directories( + ${BOOT_DIR}/zephyr/include + ${TINYCRYPT_DIR}/include + ) + + zephyr_library_sources( + ${TINYCRYPT_DIR}/source/sha256.c + ${TINYCRYPT_DIR}/source/utils.c + ) +elseif(CONFIG_BOOT_SIGNATURE_TYPE_RSA) + # Use mbedTLS provided by Zephyr for RSA signatures. (Its config file + # is set using Kconfig.) + zephyr_include_directories(include) + if(CONFIG_BOOT_ENCRYPT_RSA) + set_source_files_properties( + ${BOOT_DIR}/bootutil/src/encrypted.c + PROPERTIES + INCLUDE_DIRECTORIES ${ZEPHYR_MBEDTLS_MODULE_DIR}/library + ) + endif() +elseif(CONFIG_BOOT_SIGNATURE_TYPE_ED25519 OR CONFIG_BOOT_ENCRYPT_X25519) + if(CONFIG_BOOT_USE_TINYCRYPT) + zephyr_library_include_directories( + ${MBEDTLS_ASN1_DIR}/include + ${BOOT_DIR}/zephyr/include + ${TINYCRYPT_DIR}/include + ${TINYCRYPT_SHA512_DIR}/include + ) + zephyr_library_sources( + ${TINYCRYPT_DIR}/source/sha256.c + ${TINYCRYPT_DIR}/source/utils.c + ${TINYCRYPT_SHA512_DIR}/source/sha512.c + # Additionally pull in just the ASN.1 parser from mbedTLS. + ${MBEDTLS_ASN1_DIR}/src/asn1parse.c + ${MBEDTLS_ASN1_DIR}/src/platform_util.c + ) + zephyr_library_compile_definitions( + MBEDTLS_CONFIG_FILE="${CMAKE_CURRENT_LIST_DIR}/include/mcuboot-mbedtls-cfg.h" + ) + else() + zephyr_include_directories(include) + endif() + + zephyr_library_include_directories( + ${BOOT_DIR}/zephyr/include + ${FIAT_DIR}/include/ + ) + + if(NOT CONFIG_BOOT_ED25519_PSA) + zephyr_library_sources( + ${FIAT_DIR}/src/curve25519.c + ) + else() + zephyr_library_sources( + ${MBEDTLS_ASN1_DIR}/src/asn1parse.c + ${BOOT_DIR}/bootutil/src/ed25519_psa.c + ) + endif() +endif() + +if(NOT CONFIG_BOOT_ED25519_PSA) + if(CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_ENCRYPT_X25519) + zephyr_library_sources( + ${TINYCRYPT_DIR}/source/aes_encrypt.c + ${TINYCRYPT_DIR}/source/aes_decrypt.c + ${TINYCRYPT_DIR}/source/ctr_mode.c + ${TINYCRYPT_DIR}/source/hmac.c + ${TINYCRYPT_DIR}/source/ecc_dh.c + ) + endif() +endif() + +if(CONFIG_BOOT_ENCRYPT_EC256) + zephyr_library_sources( + ${TINYCRYPT_DIR}/source/ecc_dh.c + ) +endif() + +if(CONFIG_BOOT_DECOMPRESSION) + zephyr_library_sources( + decompression.c + ) +endif() + +if(CONFIG_MCUBOOT_SERIAL) + zephyr_sources(${BOOT_DIR}/zephyr/serial_adapter.c) + zephyr_sources(${BOOT_DIR}/boot_serial/src/boot_serial.c) + zephyr_sources(${BOOT_DIR}/boot_serial/src/zcbor_bulk.c) + + zephyr_include_directories(${BOOT_DIR}/bootutil/include) + zephyr_include_directories(${BOOT_DIR}/boot_serial/include) + zephyr_include_directories(include) + + zephyr_include_directories_ifdef( + CONFIG_BOOT_ERASE_PROGRESSIVELY + ${BOOT_DIR}/bootutil/src + ) + + if(CONFIG_BOOT_ENCRYPT_IMAGE) + zephyr_library_sources( + ${BOOT_DIR}/boot_serial/src/boot_serial_encryption.c + ) + endif() +endif() + +if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") + # CONF_FILE points to the KConfig configuration files of the bootloader. + foreach (filepath ${CONF_FILE}) + file(READ ${filepath} temp_text) + string(FIND "${temp_text}" ${CONFIG_BOOT_SIGNATURE_KEY_FILE} match) + if (${match} GREATER_EQUAL 0) + if (NOT DEFINED CONF_DIR) + get_filename_component(CONF_DIR ${filepath} DIRECTORY) + else() + message(FATAL_ERROR "Signature key file defined in multiple conf files") + endif() + endif() + endforeach() + + if(IS_ABSOLUTE ${CONFIG_BOOT_SIGNATURE_KEY_FILE}) + set(KEY_FILE ${CONFIG_BOOT_SIGNATURE_KEY_FILE}) + elseif((DEFINED CONF_DIR) AND + (EXISTS ${CONF_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE})) + set(KEY_FILE ${CONF_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE}) + else() + set(KEY_FILE ${MCUBOOT_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE}) + endif() + message("MCUBoot bootloader key file: ${KEY_FILE}") + + set_property( + GLOBAL + PROPERTY + KEY_FILE + ${KEY_FILE} + ) + + set(GENERATED_PUBKEY ${ZEPHYR_BINARY_DIR}/autogen-pubkey.c) + add_custom_command( + OUTPUT ${GENERATED_PUBKEY} + COMMAND + ${PYTHON_EXECUTABLE} + ${MCUBOOT_DIR}/scripts/imgtool.py + getpub + -k + ${KEY_FILE} + > ${GENERATED_PUBKEY} + DEPENDS ${KEY_FILE} + ) + zephyr_library_sources(${GENERATED_PUBKEY}) +endif() + +if(CONFIG_BOOT_ENCRYPTION_KEY_FILE AND NOT CONFIG_BOOT_ENCRYPTION_KEY_FILE STREQUAL "") + # CONF_FILE points to the KConfig configuration files of the bootloader. + unset(CONF_DIR) + foreach(filepath ${CONF_FILE}) + file(READ ${filepath} temp_text) + string(FIND "${temp_text}" ${CONFIG_BOOT_ENCRYPTION_KEY_FILE} match) + if(${match} GREATER_EQUAL 0) + if(NOT DEFINED CONF_DIR) + get_filename_component(CONF_DIR ${filepath} DIRECTORY) + else() + message(FATAL_ERROR "Encryption key file defined in multiple conf files") + endif() + endif() + endforeach() + + if(IS_ABSOLUTE ${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) + set(KEY_FILE ${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) + elseif((DEFINED CONF_DIR) AND + (EXISTS ${CONF_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE})) + set(KEY_FILE ${CONF_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) + else() + set(KEY_FILE ${MCUBOOT_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) + endif() + message("MCUBoot bootloader encryption key file: ${KEY_FILE}") + + set(GENERATED_ENCKEY ${ZEPHYR_BINARY_DIR}/autogen-enckey.c) + add_custom_command( + OUTPUT ${GENERATED_ENCKEY} + COMMAND + ${PYTHON_EXECUTABLE} + ${MCUBOOT_DIR}/scripts/imgtool.py + getpriv + -k + ${KEY_FILE} + > ${GENERATED_ENCKEY} + DEPENDS ${KEY_FILE} + ) + zephyr_library_sources(${GENERATED_ENCKEY}) +endif() + +if(CONFIG_MCUBOOT_CLEANUP_ARM_CORE) +zephyr_library_sources( + ${BOOT_DIR}/zephyr/arm_cleanup.c +) +endif() + +if(CONFIG_MCUBOOT_BOOT_BANNER) + # Replace Zephyr's boot banner with the MCUboot one + zephyr_sources(kernel/banner.c) +endif() + +function(align_up num align result) + math(EXPR out "(((${num}) + ((${align}) - 1)) & ~((${align}) - 1))") + set(${result} "${out}" PARENT_SCOPE) +endfunction() + +function(dt_get_parent node) + string(FIND "${${node}}" "/" pos REVERSE) + + if(pos EQUAL -1) + message(ERROR "Unable to get parent of node: ${${node}}") + endif() + + string(SUBSTRING "${${node}}" 0 ${pos} ${node}) + set(${node} "${${node}}" PARENT_SCOPE) +endfunction() + +if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO) + dt_nodelabel(slot0_flash NODELABEL "slot0_partition") + dt_prop(slot0_size PATH "${slot0_flash}" PROPERTY "reg" INDEX 1) + dt_get_parent(slot0_flash) + dt_get_parent(slot0_flash) + dt_prop(erase_size_slot0 PATH "${slot0_flash}" PROPERTY "erase-block-size") + + if(NOT DEFINED slot0_size) + message(WARNING "Unable to determine size of slot0 partition, cannot calculate minimum sector usage") + elseif(NOT DEFINED erase_size_slot0) + message(WARNING "Unable to determine erase size of slot0 partition, cannot calculate minimum sector usage") + else() + math(EXPR slot_min_sectors "${slot0_size} / ${erase_size_slot0}") + endif() + + if(NOT CONFIG_SINGLE_APPLICATION_SLOT) + dt_nodelabel(slot1_flash NODELABEL "slot1_partition") + dt_prop(slot1_size PATH "${slot1_flash}" PROPERTY "reg" INDEX 1) + dt_get_parent(slot1_flash) + dt_get_parent(slot1_flash) + dt_prop(erase_size_slot1 PATH "${slot1_flash}" PROPERTY "erase-block-size") + + if(NOT DEFINED slot1_size) + message(WARNING "Unable to determine size of slot1 partition, cannot calculate minimum sector usage") + elseif(NOT DEFINED erase_size_slot1) + message(WARNING "Unable to determine erase size of slot1 partition, cannot calculate minimum sector usage") + else() + math(EXPR slot1_min_sectors "${slot1_size} / ${erase_size_slot1}") + + if("${slot1_min_sectors}" GREATER "${slot_min_sectors}") + set(slot_min_sectors ${slot1_min_sectors}) + endif() + endif() + endif() + + if(DEFINED slot_min_sectors AND "${slot_min_sectors}" GREATER "0") + zephyr_compile_definitions("MIN_SECTOR_COUNT=${slot_min_sectors}") + message("Calculated maximum number of sectors: ${slot_min_sectors}") + else() + message(WARNING "Unable to calculate minimum number of sector sizes, falling back to 128 sector default. Please disable CONFIG_BOOT_MAX_IMG_SECTORS_AUTO and set CONFIG_BOOT_MAX_IMG_SECTORS to the required value") + endif() +endif() + +if(SYSBUILD) + if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER OR CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE OR CONFIG_BOOT_UPGRADE_ONLY OR CONFIG_BOOT_DIRECT_XIP OR CONFIG_BOOT_RAM_LOAD) + # TODO: RAM LOAD support + dt_nodelabel(slot0_flash NODELABEL "slot0_partition") + dt_get_parent(slot0_flash) + dt_get_parent(slot0_flash) + + if(NOT CONFIG_SINGLE_APPLICATION_SLOT) + dt_nodelabel(slot1_flash NODELABEL "slot1_partition") + dt_get_parent(slot1_flash) + dt_get_parent(slot1_flash) + + if(NOT "${slot0_flash}" STREQUAL "${slot1_flash}") + # Check both slots for the one with the largest write/erase block size + dt_prop(erase_size_slot0 PATH "${slot0_flash}" PROPERTY "erase-block-size") + dt_prop(write_size_slot0 PATH "${slot0_flash}" PROPERTY "write-block-size") + dt_prop(erase_size_slot1 PATH "${slot1_flash}" PROPERTY "erase-block-size") + dt_prop(write_size_slot1 PATH "${slot1_flash}" PROPERTY "write-block-size") + + if(DEFINED erase_size_slot0 AND DEFINED erase_size_slot1) + if(${erase_size_slot0} GREATER ${erase_size_slot1}) + set(erase_size ${erase_size_slot0}) + else() + set(erase_size ${erase_size_slot1}) + endif() + elseif(DEFINED erase_size_slot0) + set(erase_size ${erase_size_slot0}) + elseif(DEFINED erase_size_slot1) + set(erase_size ${erase_size_slot1}) + endif() + + if(DEFINED write_size_slot0 AND DEFINED write_size_slot1) + if(${write_size_slot0} GREATER ${write_size_slot1}) + set(write_size ${write_size_slot0}) + else() + set(write_size ${write_size_slot1}) + endif() + elseif(DEFINED write_size_slot0) + set(write_size ${write_size_slot0}) + elseif(DEFINED write_size_slot1) + set(write_size ${write_size_slot1}) + endif() + else() + dt_prop(erase_size PATH "${slot0_flash}" PROPERTY "erase-block-size") + dt_prop(write_size PATH "${slot0_flash}" PROPERTY "write-block-size") + endif() + else() + dt_prop(erase_size PATH "${slot0_flash}" PROPERTY "erase-block-size") + dt_prop(write_size PATH "${slot0_flash}" PROPERTY "write-block-size") + endif() + + if(NOT DEFINED erase_size) + message(WARNING "Unable to determine erase size of slot0 or slot1 partition, setting to 1 (this is probably wrong)") + set(erase_size 1) + endif() + + if(NOT DEFINED write_size) + message(WARNING "Unable to determine write size of slot0 or slot1 partition, setting to 8 (this is probably wrong)") + set(write_size 8) + endif() + + if(${write_size} LESS 8) + set(max_align_size 8) + else() + set(max_align_size ${write_size}) + endif() + + set(key_size 0) + + # Boot trailer magic size + set(boot_magic_size 16) + + # Estimates for trailer TLV data size, this was taken from hello world builds for nrf52840dk + if(CONFIG_BOOT_SIGNATURE_TYPE_RSA) + if(CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN EQUAL 3072) + set(boot_tlv_estimate 464) + else() + set(boot_tlv_estimate 336) + endif() + elseif(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) + set(boot_tlv_estimate 150) + elseif(CONFIG_BOOT_SIGNATURE_TYPE_ED25519) + set(boot_tlv_estimate 144) + else() + set(boot_tlv_estimate 40) + endif() + + if(CONFIG_BOOT_ENCRYPT_RSA OR CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_ENCRYPT_X25519) + # 128-bit AES key size + set(boot_enc_key_size 16) + + if(CONFIG_BOOT_SWAP_SAVE_ENCTLV) + if(CONFIG_BOOT_ENCRYPT_RSA) + set(key_size 256) + elseif(CONFIG_BOOT_ENCRYPT_EC256) + math(EXPR key_size "65 + 32 + ${boot_enc_key_size}") + elseif(CONFIG_BOOT_ENCRYPT_X25519) + math(EXPR key_size "32 + 32 + ${boot_enc_key_size}") + endif() + else() + set(key_size "${boot_enc_key_size}") + endif() + + align_up(${key_size} ${max_align_size} key_size) + math(EXPR key_size "${key_size} * 2") + endif() + + align_up(${boot_magic_size} ${write_size} boot_magic_size) + + if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER) + set(boot_swap_data_size 0) + else() + math(EXPR boot_swap_data_size "${max_align_size} * 4") + endif() + + if(CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE) + if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO AND DEFINED slot_min_sectors AND "${slot_min_sectors}" GREATER "0") + math(EXPR boot_status_data_size "${slot_min_sectors} * (3 * ${write_size})") + else() + math(EXPR boot_status_data_size "${CONFIG_BOOT_MAX_IMG_SECTORS} * (3 * ${write_size})") + endif() + else() + set(boot_status_data_size 0) + endif() + + math(EXPR required_size "${key_size} + ${boot_magic_size} + ${boot_swap_data_size} + ${boot_status_data_size} + ${boot_tlv_estimate}") + align_up(${required_size} ${erase_size} required_size) + + if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER) + set(required_upgrade_size "0") + else() + math(EXPR required_upgrade_size "${boot_magic_size} + ${boot_swap_data_size} + ${boot_status_data_size}") + align_up(${required_upgrade_size} ${erase_size} required_upgrade_size) + endif() + + if(CONFIG_BOOT_SWAP_USING_MOVE) + math(EXPR required_size "${required_size} + ${erase_size}") + math(EXPR required_upgrade_size "${required_upgrade_size} + ${erase_size}") + endif() + else() + set(required_size 0) + set(required_upgrade_size 0) + endif() + + set(mcuboot_image_footer_size ${required_size} CACHE INTERNAL "Estimated MCUboot image trailer size" FORCE) + set(mcuboot_image_upgrade_footer_size ${required_upgrade_size} CACHE INTERNAL "Estimated MCUboot update image trailer size" FORCE) +endif() + +if(CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL OR CONFIG_MCUBOOT_CLEANUP_NONSECURE_RAM) +zephyr_library_sources( + ${BOOT_DIR}/zephyr/nrf_cleanup.c +) +endif() + +if(SYSBUILD AND CONFIG_PCD_APP) + # Sysbuild requires details of the RAM flash device are stored to the cache of MCUboot so + # that they can be read when running partition manager + dt_nodelabel(ram_flash_dev NODELABEL flash_sim0) + dt_reg_addr(ram_flash_addr PATH ${ram_flash_dev}) + dt_reg_size(ram_flash_size PATH ${ram_flash_dev}) + + set(RAM_FLASH_ADDR "${ram_flash_addr}" CACHE STRING "" FORCE) + set(RAM_FLASH_SIZE "${ram_flash_size}" CACHE STRING "" FORCE) +endif() diff --git a/bootloader/mcuboot/boot/zephyr/Kconfig b/bootloader/mcuboot/boot/zephyr/Kconfig new file mode 100644 index 0000000..b681fb5 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/Kconfig @@ -0,0 +1,1040 @@ +# Copyright (c) 2017-2020 Linaro Limited +# Copyright (c) 2020 Arm Limited +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +mainmenu "MCUboot configuration" + +comment "MCUboot-specific configuration options" + +source "$(ZEPHYR_NRF_MODULE_DIR)/modules/mcuboot/boot/zephyr/Kconfig" + +# Hidden option to mark a project as MCUboot +config MCUBOOT + default y + bool + select MPU_ALLOW_FLASH_WRITE if ARM_MPU + select USE_DT_CODE_PARTITION if HAS_FLASH_LOAD_OFFSET + select MCUBOOT_BOOTUTIL_LIB + select REBOOT if SECURE_BOOT + +config BOOT_USE_MBEDTLS + bool + # Hidden option + default n + help + Use mbedTLS for crypto primitives. + +config BOOT_USE_PSA_CRYPTO + bool + default y if NRF_SECURITY + # This is counter intuitive but that is how PSA heap is enabled. + select MBEDTLS_ENABLE_HEAP + select MBEDTLS_PSA_CRYPTO_C + help + Hidden option set if using PSA crypt for cryptography functionality + +config BOOT_USE_TINYCRYPT + bool + # Hidden option + default n + # When building for ECDSA, we use our own copy of mbedTLS, so the + # Zephyr one must not be enabled or the MBEDTLS_CONFIG_FILE macros + # will collide. + select MBEDTLS_PROMPTLESS if ZEPHYR_MBEDTLS_MODULE + help + Use TinyCrypt for crypto primitives. + +config BOOT_USE_CC310 + bool + # Hidden option + default n + # When building for ECDSA, we use our own copy of mbedTLS, so the + # Zephyr one must not be enabled or the MBEDTLS_CONFIG_FILE macros + # will collide. + help + Use cc310 for crypto primitives. + +config BOOT_USE_NRF_CC310_BL + bool + default n + +config NRFXLIB_CRYPTO + bool + default n + +config NRF_CC310_BL + bool + default n + +if BOOT_USE_PSA_CRYPTO + +config BOOT_PSA_IMG_HASH_ALG_SHA256_DEPENDENCIES + bool + default y if BOOT_IMG_HASH_ALG_SHA256 + select PSA_WANT_ALG_SHA_256 + help + Dependencies for hashing with SHA256 + +config BOOT_ED25519_PSA_DEPENDENCIES + bool + select PSA_WANT_ALG_SHA_256 if BOOT_IMG_HASH_ALG_SHA256 + select PSA_WANT_ALG_SHA_512 + select PSA_WANT_ALG_PURE_EDDSA + select PSA_WANT_ECC_TWISTED_EDWARDS_255 + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + help + Dependencies for ed25519 signature + +if BOOT_ENCRYPT_IMAGE + +config BOOT_X25519_PSA_DEPENDENCIES + bool + select PSA_WANT_ALG_ECDH + select PSA_WANT_ALG_HMAC + select PSA_WANT_ALG_HKDF + select PSA_WANT_ALG_CTR + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + select PSA_WANT_KEY_TYPE_DERIVE + select PSA_WANT_KEY_TYPE_AES + select PSA_WANT_ECC_MONTGOMERY_255 + help + Dependencies for x25519 shared-random key encryption and AES + encryption. The PSA_WANT_ALG_CTR and PSA_WANT_KEY_TYPE_AES + enable Counter based block cipher and AES key, and algorithm support, + to use with it; the others are used for shared key decryption + and derivation. + +endif # BOOT_ENCRYPT_IMAGE + +if MBEDTLS_ENABLE_HEAP + +config MBEDTLS_HEAP_SIZE + default 2048 if BOOT_USE_PSA_CRYPTO + help + The PSA internals need to be able to allocate memory for operation + and it uses mbedTLS heap for that. + +endif # MBEDTLS_ENABLE_HEAP + +endif # BOOT_USE_PSA_CRYPTO + +menu "MCUBoot settings" + +config SINGLE_APPLICATION_SLOT + bool "Single slot bootloader" + default n + help + Single image area is used for application which means that + uploading a new application overwrites the one that previously + occupied the area. + +config BOOT_IMG_HASH_ALG_SHA256_ALLOW + bool + help + Hidden option set by configurations that allow SHA256 + +config BOOT_IMG_HASH_ALG_SHA384_ALLOW + bool + help + Hidden option set by configurations that allow SHA384 + +config BOOT_IMG_HASH_ALG_SHA512_ALLOW + bool + help + Hidden option set by configurations that allow SHA512 + +config BOOT_IMG_HASH_DIRECTLY_ON_STORAGE + bool "Hash calculation functions access storage through address space" + depends on !BOOT_ENCRYPT_IMAGE + help + When possible to map storage device, at least for read operations, + to address space or RAM area, enabling this option allows hash + calculation functions to directly access the storage through that address + space or using its own DMA. This reduces flash read overhead done + by the MCUboot. + Notes: + - not supported when encrypted images are in use, because calculating + SHA requires image to be decrypted first, which is done to RAM. + - currently only supported on internal storage of devices; this + option will not work with devices that use external storage for + either of image slots. + +choice BOOT_IMG_HASH_ALG + prompt "Selected image hash algorithm" + default BOOT_IMG_HASH_ALG_SHA256 if BOOT_IMG_HASH_ALG_SHA256_ALLOW + default BOOT_IMG_HASH_ALG_SHA384 if BOOT_IMG_HASH_ALG_SHA384_ALLOW + default BOOT_IMG_HASH_ALG_SHA512 if BOOT_IMG_HASH_ALG_SHA512_ALLOW + help + Hash algorithm used for image verification. Selection + here may be limited by other configurations, like for + example selected cryptographic signature. + +config BOOT_IMG_HASH_ALG_SHA256 + bool "SHA256" + depends on BOOT_IMG_HASH_ALG_SHA256_ALLOW + help + SHA256 algorithm + +config BOOT_IMG_HASH_ALG_SHA384 + bool "SHA384" + depends on BOOT_IMG_HASH_ALG_SHA384_ALLOW + help + SHA384 algorithm + +config BOOT_IMG_HASH_ALG_SHA512 + bool "SHA512" + depends on BOOT_IMG_HASH_ALG_SHA512_ALLOW + help + SHA512 algorithm + +endchoice # BOOT_IMG_HASH_ALG + +config BOOT_SIGNATURE_TYPE_PURE_ALLOW + bool + help + Hidden option set by configurations that allow Pure variant, + for example ed25519. The pure variant means that image + signature is calculated over entire image instead of hash + of an image. + +choice BOOT_SIGNATURE_TYPE + prompt "Signature type" + default BOOT_SIGNATURE_TYPE_ED25519 if SOC_NRF54L15_CPUAPP + default BOOT_SIGNATURE_TYPE_RSA + +config BOOT_SIGNATURE_TYPE_NONE + bool "No signature; use only hash check" + select BOOT_USE_TINYCRYPT + select BOOT_IMG_HASH_ALG_SHA256_ALLOW + +config BOOT_SIGNATURE_TYPE_RSA + bool "RSA signatures" + select BOOT_USE_MBEDTLS + select MBEDTLS + select BOOT_ENCRYPTION_SUPPORT + select BOOT_IMG_HASH_ALG_SHA256_ALLOW + +if BOOT_SIGNATURE_TYPE_RSA +config BOOT_SIGNATURE_TYPE_RSA_LEN + int "RSA signature length" + range 2048 3072 + default 2048 +endif + +config BOOT_SIGNATURE_TYPE_ECDSA_P256 + bool "Elliptic curve digital signatures with curve P-256" + select BOOT_ENCRYPTION_SUPPORT + select BOOT_IMG_HASH_ALG_SHA256_ALLOW + +if BOOT_SIGNATURE_TYPE_ECDSA_P256 +choice BOOT_ECDSA_IMPLEMENTATION + prompt "Ecdsa implementation" + default BOOT_ECDSA_TINYCRYPT + +config BOOT_ECDSA_TINYCRYPT + bool "Use tinycrypt" + select BOOT_USE_TINYCRYPT + +config BOOT_ECDSA_CC310 + bool "Use CC310" + depends on HAS_HW_NRF_CC310 + select BOOT_USE_NRF_CC310_BL + select NRF_CC310_BL + select NRFXLIB_CRYPTO + select BOOT_USE_CC310 +endchoice # Ecdsa implementation +endif + +config BOOT_SIGNATURE_TYPE_ED25519 + bool "Edwards curve digital signatures using ed25519" + select BOOT_ENCRYPTION_SUPPORT if !BOOT_SIGNATURE_TYPE_PURE + select BOOT_IMG_HASH_ALG_SHA256_ALLOW if !BOOT_SIGNATURE_TYPE_PURE + # The SHA is used only for key hashing, not for images. + select BOOT_IMG_HASH_ALG_SHA512_ALLOW if BOOT_USE_PSA_CRYPTO + select BOOT_SIGNATURE_TYPE_PURE_ALLOW + help + This is ed25519 signature calculated over SHA512 of SHA256 of application + image; that is not completely correct approach as the SHA512 should be + rather directly calculated over an image. + Select BOOT_SIGNATURE_TYPE_PURE to have a PureEdDSA calculating image + signature directly on image, rather than hash of the image. + +if BOOT_SIGNATURE_TYPE_ED25519 + +config BOOT_SIGNATURE_TYPE_PURE + bool "Use Pure signature of image" + depends on BOOT_SIGNATURE_TYPE_PURE_ALLOW + help + The Pure signature is calculated directly over image rather than + hash of an image. + This is more secure signature, specifically if hardware can do the + verification without need to share key. + Note that this requires that all slots for which signature is to be + verified need to be accessible through memory address space that + cryptography can access. + +choice BOOT_ED25519_IMPLEMENTATION + prompt "Ecdsa implementation" + default BOOT_ED25519_TINYCRYPT + +config BOOT_ED25519_TINYCRYPT + bool "Use tinycrypt" + select BOOT_USE_TINYCRYPT + depends on !NRF_SECURITY + +config BOOT_ED25519_MBEDTLS + bool "Use mbedTLS" + select BOOT_USE_MBEDTLS + select MBEDTLS + depends on !NRF_SECURITY + +config BOOT_ED25519_PSA + bool "Use PSA crypto" + depends on NRF_SECURITY + select BOOT_USE_PSA_CRYPTO + select BOOT_ED25519_PSA_DEPENDENCIES + select BOOT_X25519_PSA_DEPENDENCIES if BOOT_ENCRYPT_IMAGE + +endchoice +endif + +endchoice + +config BOOT_SIGNATURE_USING_KMU + bool "Use KMU stored keys for signature verification" + depends on NRF_SECURITY + depends on CRACEN_LIB_KMU + select PSA_WANT_ALG_GCM + select PSA_WANT_KEY_TYPE_AES + select PSA_WANT_AES_KEY_SIZE_256 + select PSA_WANT_ALG_SP800_108_COUNTER_CMAC + select PSA_WANT_ALG_CMAC + select PSA_WANT_ALG_ECB_NO_PADDING + help + MCUboot will use keys provisioned to the device key management unit for signature + verification instead of compiling in key data from a file. + +if !BOOT_SIGNATURE_USING_KMU + +config BOOT_SIGNATURE_KEY_FILE + string "PEM key file" + default "root-ec-p256.pem" if BOOT_SIGNATURE_TYPE_ECDSA_P256 + default "root-ed25519.pem" if BOOT_SIGNATURE_TYPE_ED25519 + default "root-rsa-3072.pem" if BOOT_SIGNATURE_TYPE_RSA && BOOT_SIGNATURE_TYPE_RSA_LEN=3072 + default "root-rsa-2048.pem" if BOOT_SIGNATURE_TYPE_RSA && BOOT_SIGNATURE_TYPE_RSA_LEN=2048 + default "" + help + You can use either absolute or relative path. + In case relative path is used, the build system assumes that it starts + from the directory where the MCUBoot KConfig configuration file is + located. If the key file is not there, the build system uses relative + path that starts from the MCUBoot repository root directory. + The key file will be parsed by imgtool's getpub command and a .c source + with the public key information will be written in a format expected by + MCUboot. + +endif + +config MCUBOOT_CLEANUP_ARM_CORE + bool "Perform core cleanup before chain-load the application" + depends on CPU_CORTEX_M + default y + help + This option instructs MCUboot to perform a clean-up of a set of + architecture core HW registers before jumping to the application + firmware. The clean-up sets these registers to their warm-reset + values as specified by the architecture. + + This option is enabled by default to prevent possible problems when + booting zephyr (or other) applications whereby e.g. a MPU stack guard + may be initialised in RAM which is then used by the application + start-up code which can cause a module fault and potentially make the + module irrecoverable. + +# Disable MBEDTLS from being selected if NRF_SECURITY is enabled, and use default NRF_SECURITY +# configuration file for MBEDTLS +config MBEDTLS + depends on !NRF_SECURITY + +config NRF_SECURITY + select MBEDTLS_PROMPTLESS + +if MBEDTLS + +config MBEDTLS_CFG_FILE + default "mcuboot-mbedtls-cfg.h" if !NRF_SECURITY + +endif + +config BOOT_HW_KEY + bool "Use HW key for image verification" + default n + help + Use HW key for image verification, otherwise the public key is embedded + in MCUBoot. If enabled the public key is appended to the signed image + and requires the hash of the public key to be provisioned to the device + beforehand. + +config BOOT_VALIDATE_SLOT0 + bool "Validate image in the primary slot on every boot" + default y + help + If y, the bootloader attempts to validate the signature of the + primary slot every boot. This adds the signature check time to + every boot, but can mitigate against some changes that are + able to modify the flash image itself. + +config BOOT_VALIDATE_SLOT0_ONCE + bool "Validate image in the primary slot just once after after upgrade" + depends on !BOOT_VALIDATE_SLOT0 && SINGLE_APPLICATION_SLOT + default n + help + If y, the bootloader attempts to validate the signature of the + primary slot only once after an upgrade of the main slot. + It caches the result in the magic area, which makes it an unsecure + method. This option is usefull for lowering the boot up time for + low end devices with as a compromise lowering the security level. + If unsure, leave at the default value. + +config BOOT_PREFER_SWAP_MOVE + bool "Prefer the newer swap move algorithm" + default y if SOC_FAMILY_NORDIC_NRF + default y if !$(dt_nodelabel_enabled,scratch_partition) + help + If y, the BOOT_IMAGE_UPGRADE_MODE will default to using + "move" instead of "scratch". This is a separate bool config + option, because Kconfig doesn't allow defaults to be + overridden in choice options. Most devices should be using + swap move. + +if !SINGLE_APPLICATION_SLOT +choice BOOT_IMAGE_UPGRADE_MODE + prompt "Image upgrade modes" + default BOOT_SWAP_USING_MOVE if BOOT_PREFER_SWAP_MOVE + default BOOT_SWAP_USING_SCRATCH + +config BOOT_SWAP_USING_SCRATCH + bool "Swap mode that run with the scratch partition" + help + This is the most conservative swap mode but it can work even on + devices with heterogeneous flash page layout. + +config BOOT_UPGRADE_ONLY + bool "Overwrite image updates instead of swapping" + help + If y, overwrite the primary slot with the upgrade image instead + of swapping them. This prevents the fallback recovery, but + uses a much simpler code path. + +config BOOT_SWAP_USING_MOVE + bool "Swap mode that can run without a scratch partition" + help + If y, the swap upgrade is done in two steps, where first every + sector of the primary slot is moved up one sector, then for + each sector X in the secondary slot, it is moved to index X in + the primary slot, then the sector at X+1 in the primary is + moved to index X in the secondary. + This allows a swap upgrade without using a scratch partition, + but is currently limited to all sectors in both slots being of + the same size. + +config BOOT_DIRECT_XIP + bool "Run the latest image directly from its slot" + help + If y, mcuboot selects the newest valid image based on the image version + numbers, thereafter the selected image can run directly from its slot + without having to move/copy it into the primary slot. For this reason the + images must be linked to be executed from the given image slot. Using this + mode results in a simpler code path and smaller code size. + +config BOOT_RAM_LOAD + bool "RAM load" + help + If y, mcuboot selects the newest valid image based on the image version + numbers, thereafter the selected image is copied to RAM and executed from + there. For this reason, the image has to be linked to be executed from RAM. + The address that the image is copied to is specified using the load-addr + argument to the imgtool.py script which writes it to the image header. + +config BOOT_FIRMWARE_LOADER + bool "Firmware loader" + help + If y, mcuboot will have a single application slot, and the secondary + slot will be for a non-upgradeable firmware loaded image (e.g. for + loading firmware via Bluetooth). The main application will boot by + default unless there is an error with it or the boot mode has been + forced to the firmware loader. + + Note: The firmware loader image must be signed with the same signing + key as the primary image. + +endchoice + +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_Z_SRAM := zephyr,sram + +if BOOT_RAM_LOAD +config BOOT_IMAGE_EXECUTABLE_RAM_START + hex "Boot image executable ram start" + default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_SRAM)) + +config BOOT_IMAGE_EXECUTABLE_RAM_SIZE + int "Boot image executable base size" + default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM),0) +endif + +config BOOT_DIRECT_XIP_REVERT + bool "Enable the revert mechanism in direct-xip mode" + depends on BOOT_DIRECT_XIP + default n + help + If y, enables the revert mechanism in direct-xip similar to the one in + swap mode. It requires the trailer magic to be added to the signed image. + When a reboot happens without the image being confirmed at runtime, the + bootloader considers the image faulty and erases it. After this it will + attempt to boot the previous image. The images can also be made permanent + (marked as confirmed in advance) just like in swap mode. + +config BOOT_BOOTSTRAP + bool "Bootstrap erased the primary slot from the secondary slot" + default n + help + If y, enables bootstraping support. Bootstrapping allows an erased + primary slot to be initialized from a valid image in the secondary slot. + If unsure, leave at the default value. + +config BOOT_SWAP_SAVE_ENCTLV + bool "Save encrypted key TLVs instead of plaintext keys in swap metadata" + default n + depends on BOOT_ENCRYPT_IMAGE + help + If y, instead of saving the encrypted image keys in plaintext in the + swap resume metadata, save the encrypted image TLVs. This should be used + when there is no security mechanism protecting the data in the primary + slot from being dumped. If n is selected (default), the keys are written + after being decrypted from the image TLVs and could be read by an + attacker who has access to the flash contents of the primary slot (eg + JTAG/SWD or primary slot in external flash). + If unsure, leave at the default value. + +endif # !SINGLE_APPLICATION_SLOT + +config BOOT_ENCRYPTION_SUPPORT + bool + help + Hidden option used to check if image encryption is supported. + +config BOOT_ENCRYPT_IMAGE + bool "Support for encrypted image updates" + depends on BOOT_ENCRYPTION_SUPPORT + select BOOT_ENCRYPT_RSA if BOOT_SIGNATURE_TYPE_RSA + select BOOT_ENCRYPT_EC256 if BOOT_SIGNATURE_TYPE_ECDSA_P256 + select BOOT_ENCRYPT_X25519 if BOOT_SIGNATURE_TYPE_ED25519 + depends on !SINGLE_APPLICATION_SLOT || MCUBOOT_SERIAL + help + If y, images in the secondary slot can be encrypted and are decrypted + on the fly when upgrading to the primary slot, as well as encrypted + back when swapping from the primary slot to the secondary slot. The + encryption mechanism must match the same type as the signature type, + supported types include: + - RSA-OAEP (2048 bits). + - ECIES using primitives described under "ECIES-P256 encryption" in + docs/encrypted_images.md. + - ECIES using primitives described under "ECIES-X25519 encryption" + in docs/encrypted_images.md. + + Note that for single slot operation, this can still be used to allow + loading encrypted images via serial recovery which are then + decrypted on-the-fly without needing a second slot. + +config BOOT_ENCRYPT_RSA + bool + help + Hidden option selecting RSA encryption. + +config BOOT_ENCRYPT_EC256 + bool + help + Hidden option selecting EC256 encryption. + +config BOOT_ENCRYPT_X25519 + bool + help + Hidden option selecting x25519 encryption. + +config BOOT_ENCRYPTION_KEY_FILE + string "Encryption key file" + depends on BOOT_ENCRYPT_IMAGE + default "enc-rsa2048-priv.pem" if BOOT_ENCRYPT_RSA + default "enc-ec256-priv.pem" if BOOT_ENCRYPT_EC256 + default "enc-x25519-priv.pem" if BOOT_ENCRYPT_X25519 + default "" + help + You can use either absolute or relative path. + In case relative path is used, the build system assumes that it starts + from the directory where the MCUBoot KConfig configuration file is + located. If the key file is not there, the build system uses relative + path that starts from the MCUBoot repository root directory. + The key file will be parsed by imgtool's getpriv command and a .c source + with the public key information will be written in a format expected by + MCUboot. + +config BOOT_MAX_IMG_SECTORS_AUTO + bool "Calculate maximum sectors automatically" + default y if !PARTITION_MANAGER_ENABLED + help + If this option is enabled then the maximum number of supported sectors per image will + be calculated automatically from the flash erase sizes and size of each partition for + the first image. + + If this information is not available, or multiple images are used, then this option + should be disabled and BOOT_MAX_IMG_SECTORS should be set instead + +config BOOT_MAX_IMG_SECTORS + int "Maximum number of sectors per image slot" + default 128 + depends on !BOOT_MAX_IMG_SECTORS_AUTO + help + This option controls the maximum number of sectors that each of + the two image areas can contain. Smaller values reduce MCUboot's + memory usage; larger values allow it to support larger images. + If unsure, leave at the default value. + +config BOOT_SHARE_BACKEND_AVAILABLE + bool + default n + help + Hidden open which indicates if there is a sharing backend available. + +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_BOOTLOADER_INFO := zephyr,bootloader-info + +config BOOT_SHARE_BACKEND_AVAILABLE + bool + default n + help + Hidden open which indicates if there is a sharing backend available. + +choice BOOT_SHARE_BACKEND + prompt "Shared data backend" + default BOOT_SHARE_BACKEND_DISABLED + +config BOOT_SHARE_BACKEND_DISABLED + bool "Disabled" + help + No data sharing support. + +config BOOT_SHARE_BACKEND_RETENTION + bool "Retention" + depends on RETENTION + depends on $(dt_chosen_enabled,$(DT_CHOSEN_BOOTLOADER_INFO)) + select BOOT_SHARE_BACKEND_AVAILABLE + help + Use retention to share data with application. Requires: + - Retained memory area + - Retention partition of retained memory area + - Chosen node "zephyr,bootloader-info" to be set to the retention + partition + +config BOOT_SHARE_BACKEND_EXTERNAL + bool "External (user-provided code)" + select BOOT_SHARE_BACKEND_AVAILABLE + help + Use a custom user-specified storage. + +endchoice + +menuconfig BOOT_SHARE_DATA + bool "Save application specific data" + default n + depends on BOOT_SHARE_BACKEND_AVAILABLE + help + This will allow data to be shared between MCUboot and an application, + it does not include any informatiom by default. + + Note: This requires a backend to function, see + BOOT_SHARE_BACKEND_RETENTION for details on using the retention + subsystem as a backend. + +config BOOT_SHARE_DATA_BOOTINFO + bool "Save boot information data" + default n + depends on BOOT_SHARE_DATA + help + This will place information about the MCUboot configuration and + running application into a shared memory area. + +menuconfig MEASURED_BOOT + bool "Store the boot state/measurements in shared memory area" + default n + depends on BOOT_SHARE_BACKEND_AVAILABLE + help + If enabled, the bootloader will store certain boot measurements such as + the hash of the firmware image in a shared memory area. This data can + be used later by runtime services (e.g. by a device attestation service). + + Note: This requires a backend to function, see + BOOT_SHARE_BACKEND_RETENTION for details on using the retention + subsystem as a backend. + +config MEASURED_BOOT_MAX_CBOR_SIZE + int "Maximum CBOR size of boot state/measurements" + default 64 + range 0 256 + depends on MEASURED_BOOT + help + The maximum size of the CBOR message which stores boot + state/measurements. + +choice BOOT_FAULT_INJECTION_HARDENING_PROFILE + prompt "Fault injection hardening profile" + default BOOT_FIH_PROFILE_OFF + +config BOOT_FIH_PROFILE_OFF + bool "No hardening against hardware level fault injection" + help + No hardening in SW against hardware level fault injection: power or + clock glitching, etc. + +config BOOT_FIH_PROFILE_LOW + bool "Moderate level hardening against hardware level fault injection" + help + Moderate level hardening: Long global fail loop to avoid break out, + control flow integrity check to discover discrepancy in expected code + flow. + +config BOOT_FIH_PROFILE_MEDIUM + bool "Medium level hardening against hardware level fault injection" + help + Medium level hardening: Long global fail loop to avoid break out, + control flow integrity check to discover discrepancy in expected code + flow, double variables to discover register or memory corruption. + +config BOOT_FIH_PROFILE_HIGH + bool "Maximum level hardening against hardware level fault injection" + select MBEDTLS + help + Maximum level hardening: Long global fail loop to avoid break out, + control flow integrity check to discover discrepancy in expected code + flow, double variables to discover register or memory corruption, random + delays to make code execution less predictable. Random delays requires an + entropy source. + +endchoice + +choice BOOT_USB_DFU + prompt "USB DFU" + default BOOT_USB_DFU_NO + +config BOOT_USB_DFU_NO + prompt "Disabled" + +config BOOT_USB_DFU_WAIT + bool "Wait for a prescribed duration to see if USB DFU is invoked" + select USB_DEVICE_STACK + select USB_DFU_CLASS + select IMG_MANAGER + select STREAM_FLASH + select MULTITHREADING + help + If y, MCUboot waits for a prescribed duration of time to allow + for USB DFU to be invoked. Please note DFU always updates the + slot1 image. + +config BOOT_USB_DFU_GPIO + bool "Use GPIO to detect whether to trigger DFU mode" + select USB_DEVICE_STACK + select USB_DFU_CLASS + select IMG_MANAGER + select STREAM_FLASH + select MULTITHREADING + help + If y, MCUboot uses GPIO to detect whether to invoke USB DFU. + +endchoice + +config BOOT_USB_DFU_WAIT_DELAY_MS + int "USB DFU wait duration" + depends on BOOT_USB_DFU_WAIT + default 12000 + help + Milliseconds to wait for USB DFU to be invoked. + +if BOOT_USB_DFU_GPIO + +config BOOT_USB_DFU_DETECT_DELAY + int "Serial detect pin detection delay time [ms]" + default 0 + help + Used to prevent the bootloader from loading on button press. + Useful for powering on when using the same button as + the one used to place the device in bootloader mode. + +endif # BOOT_USB_DFU_GPIO + +config BOOT_USB_DFU_NO_APPLICATION + bool "Stay in bootloader if no application" + help + Allows for entering USB DFU recovery mode if there is no bootable + application that the bootloader can jump to. + +config BOOT_USE_BENCH + bool "Enable benchmark code" + default n + help + If y, adds support for simple benchmarking that can record + time intervals between two calls. The time printed depends + on the particular Zephyr target, and is generally ticks of a + specific board-specific timer. + +module = MCUBOOT +module-str = MCUBoot bootloader +source "subsys/logging/Kconfig.template.log_config" + +config MCUBOOT_LOG_THREAD_STACK_SIZE + int "Stack size for the MCUBoot log processing thread" + depends on LOG && !LOG_IMMEDIATE + default 2048 if COVERAGE_GCOV + default 1024 if NO_OPTIMIZATIONS + default 1024 if XTENSA + default 4096 if (X86 && X86_64) + default 4096 if ARM64 + default 768 + help + Set the internal stack size for MCUBoot log processing thread. + +config MCUBOOT_INDICATION_LED + bool "Turns on LED indication when device is in DFU" + select GPIO + help + Device device activates the LED while in bootloader mode. + mcuboot-led0 alias must be set in the device's .dts + definitions for this to work. + +rsource "Kconfig.serial_recovery" + +rsource "Kconfig.firmware_loader" + +config BOOT_INTR_VEC_RELOC + bool "Relocate the interrupt vector to the application" + default n + depends on SW_VECTOR_RELAY || CPU_CORTEX_M_HAS_VTOR + help + Relocate the interrupt vector to the application before it is started. + Select this option if application requires vector relocation, + but it doesn't relocate vector in its reset handler. + +config UPDATEABLE_IMAGE_NUMBER + int "Number of updateable images" + default 1 + range 1 1 if SINGLE_APPLICATION_SLOT + help + Enables support of multi image update. + +config BOOT_VERSION_CMP_USE_BUILD_NUMBER + bool "Use build number while comparing image version" + depends on (UPDATEABLE_IMAGE_NUMBER > 1) || BOOT_DIRECT_XIP || \ + BOOT_RAM_LOAD || MCUBOOT_DOWNGRADE_PREVENTION + help + By default, the image version comparison relies only on version major, + minor and revision. Enable this option to take into account the build + number as well. + +choice BOOT_DOWNGRADE_PREVENTION_CHOICE + prompt "Downgrade prevention" + optional + +config MCUBOOT_DOWNGRADE_PREVENTION + bool "SW based downgrade prevention" + depends on !BOOT_DIRECT_XIP + help + Prevent downgrades by enforcing incrementing version numbers. + When this option is set, any upgrade must have greater major version + or greater minor version with equal major version. This mechanism + only protects against some attacks against version downgrades (for + example, a JTAG could be used to write an older version). + +config MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER + bool "Use image security counter instead of version number" + depends on MCUBOOT_DOWNGRADE_PREVENTION + depends on (BOOT_SWAP_USING_MOVE || BOOT_SWAP_USING_SCRATCH) + help + Security counter is used for version eligibility check instead of pure + version. When this option is set, any upgrade must have greater or + equal security counter value. + Because of the acceptance of equal values it allows for software + downgrades to some extent. + +config MCUBOOT_HW_DOWNGRADE_PREVENTION + bool "HW based downgrade prevention" + help + Prevent undesirable/malicious software downgrades. When this option is + set, any upgrade must have greater or equal security counter value. + Because of the acceptance of equal values it allows for software + downgrade to some extent. + +endchoice + +config BOOT_WATCHDOG_FEED + bool "Feed the watchdog while doing swap" + default y if WATCHDOG + default y if SOC_FAMILY_NORDIC_NRF + # for nRF nrfx based implementation is available + imply NRFX_WDT if SOC_FAMILY_NORDIC_NRF + imply NRFX_WDT0 if SOC_FAMILY_NORDIC_NRF + imply NRFX_WDT1 if SOC_FAMILY_NORDIC_NRF + imply NRFX_WDT30 if SOC_FAMILY_NORDIC_NRF + imply NRFX_WDT31 if SOC_FAMILY_NORDIC_NRF + help + Enables implementation of MCUBOOT_WATCHDOG_FEED() macro which is + used to feed watchdog while doing time consuming operations. + +config BOOT_IMAGE_ACCESS_HOOKS + bool "Enable hooks for overriding MCUboot's native routines" + help + Allow to provide procedures for override or extend native + MCUboot's routines required for access the image data and the image + update. It is up to the project customization to add required source + files to the build. + +config MCUBOOT_ACTION_HOOKS + bool "Enable hooks for responding to MCUboot status changes" + help + This will call a handler when the MCUboot status changes which allows + for some level of user feedback, for instance to change LED status to + indicate a failure, using the callback: + 'void mcuboot_status_change(mcuboot_status_type_t status)' where + 'mcuboot_status_type_t' is listed in + boot/bootutil/include/bootutil/mcuboot_status.h + +config BOOT_DISABLE_CACHES + bool "Disable I/D caches before chain-loading application" + depends on CPU_HAS_ICACHE || CPU_HAS_DCACHE + default y + help + Will flush and disable the instruction and data caches on the CPU prior to + booting an application, this is required on some ARM Cortex devices and + increases protection against data leakage from MCUboot to applications via + these caches. + +config MCUBOOT_BOOT_BANNER + bool "Use MCUboot boot banner" + depends on BOOT_BANNER + depends on !NCS_BOOT_BANNER + depends on "$(APP_VERSION_EXTENDED_STRING)" != "" + default y + help + Uses a MCUboot boot banner instead of the default zephyr one, which will output the + MCUboot name and version, followed by the zephyr name and version. + + For example: + + *** Booting MCUboot v2.0.0-72-g8c0e36c88663 *** + *** Using Zephyr OS build v3.6.0-2607-gd0be2010c31f *** + +config BOOT_BANNER_STRING + default "Using Zephyr OS build" if MCUBOOT_BOOT_BANNER + +config BOOT_DECOMPRESSION_SUPPORT + bool + depends on NRF_COMPRESS && NRF_COMPRESS_DECOMPRESSION && (NRF_COMPRESS_LZMA_VERSION_LZMA1 || NRF_COMPRESS_LZMA_VERSION_LZMA2) + depends on !SINGLE_APPLICATION_SLOT && !BOOT_ENCRYPT_IMAGE && BOOT_UPGRADE_ONLY + depends on UPDATEABLE_IMAGE_NUMBER = 1 + default y + help + Hidden symbol which should be selected if a system provided decompression support. + +if BOOT_DECOMPRESSION_SUPPORT + +menuconfig BOOT_DECOMPRESSION + bool "Decompression [EXPERIMENTAL]" + select NRF_COMPRESS_CLEANUP + select PM_USE_CONFIG_SRAM_SIZE if SOC_NRF54L15_CPUAPP + select EXPERIMENTAL + help + If enabled, will include support for compressed images being loaded to the secondary slot + which then get decompressed into the primary slot. This mode allows the secondary slot to + be smaller than primary slot which otherwise would not be allowed. + +if BOOT_DECOMPRESSION + +config BOOT_DECOMPRESSION_BUFFER_SIZE + int + range 16 16384 + default NRF_COMPRESS_CHUNK_SIZE + help + The size of a secondary buffer used for writing decompressed data to the storage device. + +endif # BOOT_DECOMPRESSION + +endif # BOOT_DECOMPRESSION_SUPPORT + +endmenu + +config MCUBOOT_DEVICE_SETTINGS + # Hidden selector for device-specific settings + bool + default y + # CPU options + select MCUBOOT_DEVICE_CPU_CORTEX_M0 if CPU_CORTEX_M0 + # Enable flash page layout if available + select FLASH_PAGE_LAYOUT if FLASH_HAS_PAGE_LAYOUT + # Enable flash_map module as flash I/O back-end + select FLASH_MAP + +config MCUBOOT_DEVICE_CPU_CORTEX_M0 + # Hidden selector for Cortex-M0 settings + bool + default n + select SW_VECTOR_RELAY if !CPU_CORTEX_M0_HAS_VECTOR_TABLE_REMAP + +comment "Zephyr configuration options" + +# Disabling MULTITHREADING provides a code size advantage, but +# it requires peripheral drivers (particularly a flash driver) +# that works properly with the option enabled. +# +# If you know for sure that your hardware will work, you can default +# it to n here. Otherwise, having it on by default makes the most +# hardware work. +config MULTITHREADING + default y if BOOT_SERIAL_CDC_ACM #usb driver requires MULTITHREADING + default y if BOOT_USB_DFU_GPIO || BOOT_USB_DFU_WAIT + default n if SOC_FAMILY_NORDIC_NRF + default n if SOC_FAMILY_ESPRESSIF_ESP32 && MCUBOOT + default y + +config LOG_PROCESS_THREAD + default n # mcuboot has its own log processing thread + +# override USB device name +config USB_DEVICE_PRODUCT + default "MCUBOOT" + +# use MCUboot's own log configuration +config MCUBOOT_BOOTUTIL_LIB_OWN_LOG + bool + default n + +config MCUBOOT_VERIFY_IMG_ADDRESS + bool "Verify reset address of image in secondary slot" + depends on UPDATEABLE_IMAGE_NUMBER > 1 + depends on !BOOT_ENCRYPT_IMAGE + depends on ARM + default y if BOOT_UPGRADE_ONLY + help + Verify that the reset address in the image located in the secondary slot + is contained within the corresponding primary slot. This is recommended + if swapping is not used (that is, BOOT_UPGRADE_ONLY is set). If a user + incorrectly uploads an update for image 1 to image 0's secondary slot + MCUboot will overwrite image 0's primary slot with this image even + though it will not boot. If swapping is enabled this will be handled + since the image will not confirm itself. If, however, swapping is not + enabled then the only mitigation is serial recovery. This feature can + also be useful when BOOT_DIRECT_XIP is enabled, to ensure that the image + linked at the correct address is loaded. + +source "Kconfig.zephyr" diff --git a/bootloader/mcuboot/boot/zephyr/Kconfig.firmware_loader b/bootloader/mcuboot/boot/zephyr/Kconfig.firmware_loader new file mode 100644 index 0000000..1ba2239 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/Kconfig.firmware_loader @@ -0,0 +1,47 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +if BOOT_FIRMWARE_LOADER + +menu "Firmware loader entrance methods" + +menuconfig BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO + bool "GPIO" + depends on GPIO + help + Use a GPIO to enter firmware loader mode. + +config BOOT_FIRMWARE_LOADER_DETECT_DELAY + int "Serial detect pin detection delay time [ms]" + default 0 + depends on BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO + help + Used to prevent the bootloader from loading on button press. + Useful for powering on when using the same button as + the one used to place the device in bootloader mode. + +config BOOT_FIRMWARE_LOADER_BOOT_MODE + bool "Check boot mode via retention subsystem" + depends on RETENTION_BOOT_MODE + help + Allows for entering firmware loader mode by using Zephyr's boot mode + retention system (i.e. an application must set the boot mode to stay + in firmware loader mode and reboot the module). + +config BOOT_FIRMWARE_LOADER_NO_APPLICATION + bool "Stay in bootloader if no application" + help + Allows for entering firmware loader mode if there is no bootable + application that the bootloader can jump to. + +config BOOT_FIRMWARE_LOADER_PIN_RESET + bool "Check for device reset by pin" + select HWINFO + help + Checks if the module reset was caused by the reset pin and will + remain in bootloader firmware loader mode if it was. + +endmenu + +endif diff --git a/bootloader/mcuboot/boot/zephyr/Kconfig.serial_recovery b/bootloader/mcuboot/boot/zephyr/Kconfig.serial_recovery new file mode 100644 index 0000000..7ed9a7d --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/Kconfig.serial_recovery @@ -0,0 +1,212 @@ +# Copyright (c) 2017-2020 Linaro Limited +# Copyright (c) 2020 Arm Limited +# Copyright (c) 2017-2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +menuconfig MCUBOOT_SERIAL + bool "MCUboot serial recovery" + default n + select REBOOT + select SERIAL + select UART_INTERRUPT_DRIVEN + select BASE64 + select CRC + select ZCBOR + depends on !BOOT_FIRMWARE_LOADER + help + If y, enables a serial-port based update mode. This allows + MCUboot itself to load update images into flash over a UART. + If unsure, leave at the default value. + +if MCUBOOT_SERIAL + +choice BOOT_SERIAL_DEVICE + prompt "Serial device" + default BOOT_SERIAL_UART if !BOARD_NRF52840DONGLE_NRF52840 + default BOOT_SERIAL_CDC_ACM if BOARD_NRF52840DONGLE_NRF52840 + +config BOOT_SERIAL_UART + bool "UART" + # SERIAL and UART_INTERRUPT_DRIVEN already selected + help + The serial device to use will be fist selected via chosen + node "zephyr,uart-mcumgr", when such node does not exist + the "zephyr,console" is used. In case when + the "zephyr,uart-mcumgr" points to the same device as + the "zephyr,console" compilation error will be triggered. + +config BOOT_SERIAL_CDC_ACM + bool "CDC ACM" + select USB_DEVICE_STACK + help + This setting will choose CDC ACM for serial recovery unless chosen + "zephyr,uart-mcumgr" is present, in which case the chosen takes + precedence and redirects serial recovery to uart pointed by + the chosen, leaving console on CDC ACM. + +endchoice + +config MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD + bool "Allow to select image number for DFU" + depends on !SINGLE_APPLICATION_SLOT + help + With the option enabled, the mcuboot serial recovery will + respect the "image" field in mcumgr image update frame + header. + The mapping of image number to partition is as follows: + 0 -> default behaviour, same as 1; + 1 -> image-0 (primary slot of the first image); + 2 -> image-1 (secondary slot of the first image); + 3 -> image-2; + 4 -> image-3. + Note that 0 is default upload target when no explicit + selection is done. + +config BOOT_SERIAL_UNALIGNED_BUFFER_SIZE + int "Stack buffer for unaligned memory writes" + default 64 + range 0 128 + help + Specifies the stack usage for a buffer which is used for unaligned + memory access when data is written to a device with memory alignment + requirements. Set to 0 to disable. + +config BOOT_MAX_LINE_INPUT_LEN + int "Maximum input line length" + default 128 + help + Maximum length of input serial port buffer (SMP serial transport uses + fragments of 128-bytes, this should not need to be changed unless a + different value is used for the transport). + +config BOOT_LINE_BUFS + int "Number of receive buffers" + range 2 128 + default 8 + help + Number of receive buffers for data received via the serial port. + +config BOOT_SERIAL_MAX_RECEIVE_SIZE + int "Maximum command line length" + default 1024 + help + Maximum length of received commands via the serial port (this should + be equal to the maximum line length, BOOT_MAX_LINE_INPUT_LEN times + by the number of receive buffers, BOOT_LINE_BUFS to allow for + optimal data transfer speeds). + +config BOOT_ERASE_PROGRESSIVELY + bool "Erase flash progressively when receiving new firmware" + default y if SOC_FAMILY_NORDIC_NRF + help + If enabled, flash is erased as necessary when receiving new firmware, + instead of erasing the whole image slot at once. This is necessary + on some hardware that has long erase times, to prevent long wait + times at the beginning of the DFU process. + +config BOOT_MGMT_ECHO + bool "Enable echo command" + help + if enabled, support for the mcumgr echo command is being added. + +menuconfig ENABLE_MGMT_PERUSER + bool "Enable system specific mcumgr commands" + help + The option enables processing of system specific mcumgr commands; + system specific commands are within group MGMT_GROUP_ID_PERUSER (64) + and above, as defined within mcumgr library. + These are system specific command and system specific implementation + function is required to process these commands. + +if ENABLE_MGMT_PERUSER + +config BOOT_MGMT_CUSTOM_STORAGE_ERASE + bool "Enable storage erase command" + help + The option enables mcumgr command that allows to erase storage + partition. + Note that the storage partition needs to be defined, in DTS, otherwise + enabling the option will cause a compilation to fail. + +endif # ENABLE_MGMT_PERUSER + +menu "Entrance methods" + +menuconfig BOOT_SERIAL_ENTRANCE_GPIO + bool "GPIO" + default y + depends on GPIO + help + Use a GPIO to enter serial recovery mode. + +config BOOT_SERIAL_DETECT_DELAY + int "Serial detect pin detection delay time [ms]" + default 0 + depends on BOOT_SERIAL_ENTRANCE_GPIO + help + Used to prevent the bootloader from loading on button press. + Useful for powering on when using the same button as + the one used to place the device in bootloader mode. + +menuconfig BOOT_SERIAL_WAIT_FOR_DFU + bool "Wait a prescribed duration to see if DFU is invoked by receiving a MCUmgr comand" + depends on BOOT_SERIAL_UART || BOOT_SERIAL_CDC_ACM + help + If y, MCUboot waits for a prescribed duration of time to allow + for DFU to be invoked. The serial recovery can be entered by receiving any + mcumgr command. + +config BOOT_SERIAL_WAIT_FOR_DFU_TIMEOUT + int "Duration to wait for the serial DFU timeout in ms" + default 500 + depends on BOOT_SERIAL_WAIT_FOR_DFU + help + Timeout in ms for MCUboot to wait to allow for DFU to be invoked. + +config BOOT_SERIAL_BOOT_MODE + bool "Check boot mode via retention subsystem" + depends on RETENTION_BOOT_MODE + help + Allows for entering serial recovery mode by using Zephyr's boot mode + retention system (i.e. an application must set the boot mode to stay + in serial recovery mode and reboot the module). + +config BOOT_SERIAL_NO_APPLICATION + bool "Stay in bootloader if no application" + help + Allows for entering serial recovery mode if there is no bootable + application that the bootloader can jump to. + +config BOOT_SERIAL_PIN_RESET + bool "Check for device reset by pin" + select HWINFO + help + Checks if the module reset was caused by the reset pin and will + remain in bootloader serial recovery mode if it was. + +endmenu + +config BOOT_SERIAL_IMG_GRP_HASH + bool "Image list hash support" + default y + help + If y, image list responses will include the image hash (adds ~100 + bytes of flash). + +config BOOT_SERIAL_IMG_GRP_IMAGE_STATE + bool "Image state support" + depends on !SINGLE_APPLICATION_SLOT + select BOOT_SERIAL_IMG_GRP_HASH if UPDATEABLE_IMAGE_NUMBER > 1 + help + If y, image states will be included with image lists and the set + state command can be used to mark an image as test/confirmed. + +config BOOT_SERIAL_IMG_GRP_SLOT_INFO + bool "Slot info" + default y if UPDATEABLE_IMAGE_NUMBER > 1 + help + If y, will include the slot info command which lists what available + slots there are in the system. + +endif # MCUBOOT_SERIAL diff --git a/bootloader/mcuboot/boot/zephyr/VERSION b/bootloader/mcuboot/boot/zephyr/VERSION new file mode 100644 index 0000000..428a355 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/VERSION @@ -0,0 +1,5 @@ +VERSION_MAJOR = 2 +VERSION_MINOR = 1 +PATCHLEVEL = 0 +VERSION_TWEAK = 0 +EXTRAVERSION = dev diff --git a/bootloader/mcuboot/boot/zephyr/app.overlay b/bootloader/mcuboot/boot/zephyr/app.overlay new file mode 100644 index 0000000..74d3dfb --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/app.overlay @@ -0,0 +1,5 @@ +/ { + chosen { + zephyr,code-partition = &boot_partition; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/arm_cleanup.c b/bootloader/mcuboot/boot/zephyr/arm_cleanup.c new file mode 100644 index 0000000..3b6c71a --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/arm_cleanup.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#if CONFIG_CPU_HAS_NXP_MPU +#include +#endif + +void cleanup_arm_nvic(void) { + /* Allow any pending interrupts to be recognized */ + __ISB(); + __disable_irq(); + + /* Disable NVIC interrupts */ + for (uint8_t i = 0; i < ARRAY_SIZE(NVIC->ICER); i++) { + NVIC->ICER[i] = 0xFFFFFFFF; + } + /* Clear pending NVIC interrupts */ + for (uint8_t i = 0; i < ARRAY_SIZE(NVIC->ICPR); i++) { + NVIC->ICPR[i] = 0xFFFFFFFF; + } +} + +#if CONFIG_CPU_HAS_ARM_MPU +__weak void z_arm_clear_arm_mpu_config(void) +{ + int i; + + int num_regions = + ((MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos); + + for (i = 0; i < num_regions; i++) { + ARM_MPU_ClrRegion(i); + } +} +#elif CONFIG_CPU_HAS_NXP_MPU +__weak void z_arm_clear_arm_mpu_config(void) +{ + int i; + + int num_regions = FSL_FEATURE_SYSMPU_DESCRIPTOR_COUNT; + + SYSMPU_Enable(SYSMPU, false); + + /* NXP MPU region 0 is reserved for the debugger */ + for (i = 1; i < num_regions; i++) { + SYSMPU_RegionEnable(SYSMPU, i, false); + } +} +#endif diff --git a/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_bee_nrf9160.conf b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_bee_nrf9160.conf new file mode 100644 index 0000000..2d4f009 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_bee_nrf9160.conf @@ -0,0 +1,10 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y + +CONFIG_MULTITHREADING=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_nrf9160.conf b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_nrf9160.conf new file mode 100644 index 0000000..2d4f009 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_nrf9160.conf @@ -0,0 +1,10 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y + +CONFIG_MULTITHREADING=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_dk_nrf9160.conf b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_dk_nrf9160.conf new file mode 100644 index 0000000..2d4f009 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_dk_nrf9160.conf @@ -0,0 +1,10 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y + +CONFIG_MULTITHREADING=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_nrf9160.conf b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_nrf9160.conf new file mode 100644 index 0000000..2d4f009 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/actinius_icarus_som_nrf9160.conf @@ -0,0 +1,10 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y + +CONFIG_MULTITHREADING=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/bl5340_dvk_nrf5340_cpuapp.conf b/bootloader/mcuboot/boot/zephyr/boards/bl5340_dvk_nrf5340_cpuapp.conf new file mode 100644 index 0000000..00a8dfb --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/bl5340_dvk_nrf5340_cpuapp.conf @@ -0,0 +1,6 @@ +CONFIG_MULTITHREADING=y +# Enable QSPI (MX25R64) - Slot 1 in QSPI +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=4 +CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/bootloader/mcuboot/boot/zephyr/boards/circuitdojo_feather_nrf9160.conf b/bootloader/mcuboot/boot/zephyr/boards/circuitdojo_feather_nrf9160.conf new file mode 100644 index 0000000..c23f92c --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/circuitdojo_feather_nrf9160.conf @@ -0,0 +1,13 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n + +# Multithreading +CONFIG_MULTITHREADING=y + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_DETECT_DELAY=450 +CONFIG_MCUBOOT_INDICATION_LED=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/conexio_stratus.conf b/bootloader/mcuboot/boot/zephyr/boards/conexio_stratus.conf new file mode 100644 index 0000000..79ba798 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/conexio_stratus.conf @@ -0,0 +1,15 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# Multithreading +CONFIG_MULTITHREADING=y + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_DETECT_DELAY=450 +CONFIG_MCUBOOT_INDICATION_LED=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/disco_l475_iot1_stm32l475xx.conf b/bootloader/mcuboot/boot/zephyr/boards/disco_l475_iot1_stm32l475xx.conf new file mode 100644 index 0000000..cbf3886 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/disco_l475_iot1_stm32l475xx.conf @@ -0,0 +1,2 @@ +CONFIG_BOOT_MAX_IMG_SECTORS=256 +CONFIG_WATCHDOG=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/flash_sim_driver.conf b/bootloader/mcuboot/boot/zephyr/boards/flash_sim_driver.conf new file mode 100644 index 0000000..7e5e58e --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/flash_sim_driver.conf @@ -0,0 +1,2 @@ +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_UNALIGNED_READ=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/frdm_k64f_mk64f12.conf b/bootloader/mcuboot/boot/zephyr/boards/frdm_k64f_mk64f12.conf new file mode 100644 index 0000000..80e211d --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/frdm_k64f_mk64f12.conf @@ -0,0 +1 @@ +CONFIG_WATCHDOG=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0.conf b/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0.conf new file mode 100644 index 0000000..801162f --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0.conf @@ -0,0 +1,5 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +#MCXN94x does not support the MCUBoot swap mode. +CONFIG_BOOT_UPGRADE_ONLY=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.conf b/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.conf new file mode 100644 index 0000000..3982570 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.conf @@ -0,0 +1,5 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 +CONFIG_BOOT_UPGRADE_ONLY=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.overlay b/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.overlay new file mode 100644 index 0000000..21d18e8 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/frdm_mcxn947_mcxn947_cpu0_qspi.overlay @@ -0,0 +1,12 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,flash = &flash; + }; +}; + diff --git a/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s06_lpc55s06.conf b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s06_lpc55s06.conf new file mode 100644 index 0000000..c0f9637 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s06_lpc55s06.conf @@ -0,0 +1,6 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +#LPC does not support the MCUBoot swap mode. +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s16_lpc55s16.conf b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s16_lpc55s16.conf new file mode 100644 index 0000000..c0f9637 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s16_lpc55s16.conf @@ -0,0 +1,6 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +#LPC does not support the MCUBoot swap mode. +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s28_lpc55s28.conf b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s28_lpc55s28.conf new file mode 100644 index 0000000..fbe7b67 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s28_lpc55s28.conf @@ -0,0 +1,6 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +#LPC does not support the MCUBoot swap mode. +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_MAX_IMG_SECTORS=512 diff --git a/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s36_lpc55s36.conf b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s36_lpc55s36.conf new file mode 100644 index 0000000..c0f9637 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s36_lpc55s36.conf @@ -0,0 +1,6 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +#LPC does not support the MCUBoot swap mode. +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s69_lpc55s69_cpu0.conf b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s69_lpc55s69_cpu0.conf new file mode 100644 index 0000000..fbe7b67 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/lpcxpresso55s69_lpc55s69_cpu0.conf @@ -0,0 +1,6 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +#LPC does not support the MCUBoot swap mode. +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_MAX_IMG_SECTORS=512 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1010_evk_mimxrt1011.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1010_evk_mimxrt1011.conf new file mode 100644 index 0000000..e7737af --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1010_evk_mimxrt1011.conf @@ -0,0 +1,4 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=2048 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1015_evk_mimxrt1015.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1015_evk_mimxrt1015.conf new file mode 100644 index 0000000..e7737af --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1015_evk_mimxrt1015.conf @@ -0,0 +1,4 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=2048 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1020_evk_mimxrt1021.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1020_evk_mimxrt1021.conf new file mode 100644 index 0000000..37ed5f8 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1020_evk_mimxrt1021.conf @@ -0,0 +1,4 @@ +# Copyright 2021 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1024_evk_mimxrt1024.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1024_evk_mimxrt1024.conf new file mode 100644 index 0000000..22e3320 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1024_evk_mimxrt1024.conf @@ -0,0 +1,4 @@ +# Copyright (c) 2021 Prevas A/S +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1040_evk_mimxrt1042.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1040_evk_mimxrt1042.conf new file mode 100644 index 0000000..35f90d4 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1040_evk_mimxrt1042.conf @@ -0,0 +1,4 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1050_evk_mimxrt1052.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1050_evk_mimxrt1052.conf new file mode 100644 index 0000000..dc4ea57 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1050_evk_mimxrt1052.conf @@ -0,0 +1,5 @@ +# Copyright 2021-2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062.conf new file mode 100644 index 0000000..7850953 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062.conf @@ -0,0 +1,5 @@ +# Copyright 2021-2022 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062_cm7.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062_cm7.conf new file mode 100644 index 0000000..480796d --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evk_mimxrt1062_cm7.conf @@ -0,0 +1,7 @@ +# Copyright 2022 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y +# Move swap provides better wear levelling, so use it by default +CONFIG_BOOT_SWAP_USING_MOVE=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evkb_mimxrt1062.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evkb_mimxrt1062.conf new file mode 100644 index 0000000..7850953 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1060_evkb_mimxrt1062.conf @@ -0,0 +1,5 @@ +# Copyright 2021-2022 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1062_fmurt6.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1062_fmurt6.conf new file mode 100644 index 0000000..c38a67f --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1062_fmurt6.conf @@ -0,0 +1,5 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1064_evk_mimxrt1064.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1064_evk_mimxrt1064.conf new file mode 100644 index 0000000..1c50bc9 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1064_evk_mimxrt1064.conf @@ -0,0 +1,4 @@ +# Copyright 2021 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1160_evk_mimxrt1166_cm7.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1160_evk_mimxrt1166_cm7.conf new file mode 100644 index 0000000..e8fc78e --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1160_evk_mimxrt1166_cm7.conf @@ -0,0 +1,5 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=2048 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt1170_evk_mimxrt1176_cm7.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1170_evk_mimxrt1176_cm7.conf new file mode 100644 index 0000000..9b299e1 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt1170_evk_mimxrt1176_cm7.conf @@ -0,0 +1,6 @@ +# Copyright 2022-2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=2048 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y + diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt595_evk_mimxrt595s_cm33.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt595_evk_mimxrt595s_cm33.conf new file mode 100644 index 0000000..957f990 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt595_evk_mimxrt595s_cm33.conf @@ -0,0 +1,3 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 +CONFIG_BOOT_MAX_IMG_SECTORS=8192 diff --git a/bootloader/mcuboot/boot/zephyr/boards/mimxrt685_evk_mimxrt685s_cm33.conf b/bootloader/mcuboot/boot/zephyr/boards/mimxrt685_evk_mimxrt685s_cm33.conf new file mode 100644 index 0000000..f93c663 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/mimxrt685_evk_mimxrt685s_cm33.conf @@ -0,0 +1,4 @@ +# Copyright 2021 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=8192 diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf51dk_nrf51822.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf51dk_nrf51822.conf new file mode 100644 index 0000000..b6d5ae5 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf51dk_nrf51822.conf @@ -0,0 +1,5 @@ +# Due the small boot partition, we can't enable logging or the debug +# optimization level out off the box. You need to increase the boot +# partition size with a zephyr DTS overlay to make MCUboot's debug +# builds fit. +CONFIG_LOG=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840_big.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf52840_big.overlay new file mode 100644 index 0000000..8ad19fc --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840_big.overlay @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00010000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x000010000 0x000074000>; + }; + slot1_partition: partition@75000 { + label = "image-1"; + reg = <0x00084000 0x000074000>; + }; + }; +}; + +&zephyr_udc0 { + cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840_single_slot.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf52840_single_slot.overlay new file mode 100644 index 0000000..8e7cc33 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840_single_slot.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00010000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x000010000 0x0000E8000>; + }; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_hooks_sample_overlay.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_hooks_sample_overlay.conf new file mode 100644 index 0000000..fd336db --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_hooks_sample_overlay.conf @@ -0,0 +1,6 @@ +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 + +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_UNALIGNED_READ=y + +CONFIG_BOOT_IMAGE_ACCESS_HOOKS=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 0000000..3bd75a2 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,2 @@ +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000..add9e2d --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,3 @@ +&uicr { + /delete-property/ gpio-as-nreset; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor.conf new file mode 100644 index 0000000..3205eaf --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor.conf @@ -0,0 +1,3 @@ +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor_secondary.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor_secondary.overlay new file mode 100644 index 0000000..948a530 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_nor_secondary.overlay @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +&flash0 { + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00010000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x000010000 0x0000e8000>; + }; + }; +}; + +&mx25r64 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot1_partition: partition@0 { + label = "image-1"; + reg = <0x000000000 0x0000e8000>; + }; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf new file mode 100644 index 0000000..9f984be --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf @@ -0,0 +1,2 @@ +CONFIG_MULTITHREADING=y +CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram.overlay new file mode 100644 index 0000000..d489e97 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram.overlay @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &slot1_partition; +/delete-node/ &slot0_partition; +/delete-node/ &boot_partition; + +&flash0 { + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00010000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x000010000 0x00000A000>; + }; + }; +}; + +/ { + soc { + flash_controller2: flash-controller@2 { + compatible = "zephyr,sim-flash"; + reg = <0x00000000 DT_SIZE_K(40)>; + + #address-cells = <1>; + #size-cells = <1>; + erase-value = <0xff>; + + flash_sim0: flash_sim@0 { + status = "okay"; + compatible = "soc-nv-flash"; + erase-block-size = <4096>; + write-block-size = <1>; + reg = <0x00000000 DT_SIZE_K(40)>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot1_partition: partition@0 { + label = "image-1"; + reg = <0x00000000 0x00000A000>; + }; + }; + }; + }; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram_multi.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram_multi.overlay new file mode 100644 index 0000000..aeb0b05 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dk_ram_multi.overlay @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/delete-node/ &slot1_partition; +/delete-node/ &slot0_partition; +/delete-node/ &boot_partition; + +&flash0 { + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00010000>; + }; + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x000010000 0x00000A000>; + }; + slot1_partition: partition@1A000 { + label = "image-1"; + reg = <0x00001A000 0x00000A000>; + }; + slot3_partition: partition@24000 { + label = "image-3"; + reg = <0x000024000 0x00000A000>; + }; + }; +}; + +/ { + soc { + flash_controller2: flash-controller@2 { + compatible = "zephyr,sim-flash"; + reg = <0x00000000 DT_SIZE_K(40)>; + + #address-cells = <1>; + #size-cells = <1>; + erase-value = <0xff>; + + flash_sim0: flash_sim@0 { + status = "okay"; + compatible = "soc-nv-flash"; + erase-block-size = <4096>; + write-block-size = <1>; + reg = <0x00000000 DT_SIZE_K(40)>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot2_partition: partition@0 { + label = "image-2"; + reg = <0x00000000 0x00000A000>; + }; + }; + }; + }; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52840dongle_nrf52840.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dongle_nrf52840.conf new file mode 100644 index 0000000..d219f35 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52840dongle_nrf52840.conf @@ -0,0 +1,26 @@ +# The UART is used for Serial Recovery, so logging requires +# an RTT console, which is not available out of the box on this board. +# Disable logging. +CONFIG_LOG=n + +# Serial +CONFIG_CONSOLE=n +CONFIG_SERIAL=y +CONFIG_UART_NRFX=n +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUBoot serial +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_REMOTE_WAKEUP=n +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" + +CONFIG_NORDIC_QSPI_NOR=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf52_minimal_footprint.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf52_minimal_footprint.conf new file mode 100644 index 0000000..a290312 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf52_minimal_footprint.conf @@ -0,0 +1,61 @@ +# Minimal MCUBoot flash footprint configuration +# for nRF52832 SoC targets +# This is not recomendet configuration because of security and reliability +# reasons. + + +# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y +CONFIG_BOOT_SIGNATURE_KEY_FILE="root-ec-p256.pem" + +# In any real project CONFIG_BOOT_VALIDATE_SLOT0 enabling is recommended +# by security reason. +# CONFIG_BOOT_VALIDATE_SLOT0 is not set + +# In most of projects CONFIG_BOOT_UPGRADE_ONLY disabling is recommended +# by reliability reason. +CONFIG_BOOT_UPGRADE_ONLY=y + +# CONFIG_BOARD_ENABLE_DCDC is not set +CONFIG_SOC_SERIES_NRF52X=y +CONFIG_SOC_NRF52832_QFAA=y +CONFIG_ARM=y +CONFIG_ARM_MPU=n +CONFIG_MAIN_STACK_SIZE=10240 +CONFIG_THREAD_STACK_INFO=n +# CONFIG_TICKLESS_KERNEL is not set +CONFIG_FLASH=y + +CONFIG_CONSOLE=n +CONFIG_DEBUG=n +CONFIG_EARLY_CONSOLE=n +CONFIG_PRINTK=n + +CONFIG_SYS_CLOCK_EXISTS=n + +# Drivers and peripherals +CONFIG_I2C=n +CONFIG_WATCHDOG=n +CONFIG_GPIO=n +CONFIG_PINMUX=n +CONFIG_SPI=n +CONFIG_SERIAL=n + +# Power management +CONFIG_PM=n + +# Interrupts +CONFIG_DYNAMIC_INTERRUPTS=n +CONFIG_IRQ_OFFLOAD=n + +# Memory protection +CONFIG_MEMORY_PROTECTION=n +CONFIG_THREAD_CUSTOM_DATA=n +CONFIG_FPU=n + +# Boot +CONFIG_BOOT_BANNER=n +CONFIG_BOOT_DELAY=0 + +# Console +CONFIG_STDOUT_CONSOLE=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf5340dk_nrf5340_cpuapp_minimal.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf5340dk_nrf5340_cpuapp_minimal.conf new file mode 100644 index 0000000..dd54681 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf5340dk_nrf5340_cpuapp_minimal.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# CC3xx is currently not used for nrf53 +CONFIG_HW_CC3XX=n +CONFIG_NRF_CC3XX_PLATFORM=n + +# Required for kernel operation +CONFIG_CLOCK_CONTROL=y +CONFIG_SYS_CLOCK_EXISTS=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf new file mode 100644 index 0000000..8d8eb84 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Ensure that the SPI NOR driver is disabled by default +CONFIG_SPI_NOR=n + +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf new file mode 100644 index 0000000..8fc12e0 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf @@ -0,0 +1,15 @@ +CONFIG_MULTITHREADING=y +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_FLASH=y +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x14000 +CONFIG_MAIN_STACK_SIZE=20480 +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n + +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay new file mode 100644 index 0000000..60ee6fe --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay @@ -0,0 +1,47 @@ +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + zephyr,code-partition = &boot_partition; + }; +}; + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +/delete-node/ &slot0_ns_partition; +/delete-node/ &slot1_ns_partition; + +/delete-node/ &storage_partition; + +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00014000>; + }; + slot0_partition: partition@14000 { + label = "image-0"; + reg = <0x000014000 0x0015A000>; + }; + storage_partition: partition@16E000 { + label = "storage"; + reg = < 0x16E000 0x9000 >; + }; + }; +}; + +&mx25r64 { + status = "okay"; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot1_partition: partition@0 { + label = "image-1"; + reg = <0x000000000 0x0015A000>; + }; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf new file mode 100644 index 0000000..8d8eb84 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Ensure that the SPI NOR driver is disabled by default +CONFIG_SPI_NOR=n + +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf new file mode 100644 index 0000000..8fc12e0 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf @@ -0,0 +1,15 @@ +CONFIG_MULTITHREADING=y +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_FLASH=y +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x14000 +CONFIG_MAIN_STACK_SIZE=20480 +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n + +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.overlay b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.overlay new file mode 100644 index 0000000..60ee6fe --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.overlay @@ -0,0 +1,47 @@ +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + zephyr,code-partition = &boot_partition; + }; +}; + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +/delete-node/ &slot0_ns_partition; +/delete-node/ &slot1_ns_partition; + +/delete-node/ &storage_partition; + +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00014000>; + }; + slot0_partition: partition@14000 { + label = "image-0"; + reg = <0x000014000 0x0015A000>; + }; + storage_partition: partition@16E000 { + label = "storage"; + reg = < 0x16E000 0x9000 >; + }; + }; +}; + +&mx25r64 { + status = "okay"; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot1_partition: partition@0 { + label = "image-1"; + reg = <0x000000000 0x0015A000>; + }; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/nrf9161dk_nrf9161_0_7_0.conf b/bootloader/mcuboot/boot/zephyr/boards/nrf9161dk_nrf9161_0_7_0.conf new file mode 100644 index 0000000..c4ce81a --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/nrf9161dk_nrf9161_0_7_0.conf @@ -0,0 +1 @@ +CONFIG_SPI_NOR=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/odroid_go_esp32_procpu.conf b/bootloader/mcuboot/boot/zephyr/boards/odroid_go_esp32_procpu.conf new file mode 100644 index 0000000..2d97ef6 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/odroid_go_esp32_procpu.conf @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_REGULATOR=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/pinnacle_100_dvk_nrf52840.conf b/bootloader/mcuboot/boot/zephyr/boards/pinnacle_100_dvk_nrf52840.conf new file mode 100644 index 0000000..00a8dfb --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/pinnacle_100_dvk_nrf52840.conf @@ -0,0 +1,6 @@ +CONFIG_MULTITHREADING=y +# Enable QSPI (MX25R64) - Slot 1 in QSPI +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=4 +CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/bootloader/mcuboot/boot/zephyr/boards/rd_rw612_bga_rw612.conf b/bootloader/mcuboot/boot/zephyr/boards/rd_rw612_bga_rw612.conf new file mode 100644 index 0000000..3a751f2 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/rd_rw612_bga_rw612.conf @@ -0,0 +1,4 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 diff --git a/bootloader/mcuboot/boot/zephyr/boards/sparkfun_thing_plus_nrf9160.conf b/bootloader/mcuboot/boot/zephyr/boards/sparkfun_thing_plus_nrf9160.conf new file mode 100644 index 0000000..c23f92c --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/sparkfun_thing_plus_nrf9160.conf @@ -0,0 +1,13 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n + +# Multithreading +CONFIG_MULTITHREADING=y + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_DETECT_DELAY=450 +CONFIG_MCUBOOT_INDICATION_LED=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/thingy52_nrf52832.conf b/bootloader/mcuboot/boot/zephyr/boards/thingy52_nrf52832.conf new file mode 100644 index 0000000..cf4c34c --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/thingy52_nrf52832.conf @@ -0,0 +1 @@ +CONFIG_GPIO_SX1509B=n diff --git a/bootloader/mcuboot/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf b/bootloader/mcuboot/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf new file mode 100644 index 0000000..e106566 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf @@ -0,0 +1,74 @@ +CONFIG_SIZE_OPTIMIZATIONS=y + +CONFIG_SYSTEM_CLOCK_NO_WAIT=y +CONFIG_PM=n + +CONFIG_MAIN_STACK_SIZE=10240 +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_BOOT_MAX_IMG_SECTORS=2048 +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y + +# Flash +CONFIG_FLASH=y +CONFIG_BOOT_ERASE_PROGRESSIVELY=y +CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS=y +CONFIG_FPROTECT=y + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y + +# MCUBoot serial +CONFIG_GPIO=y +CONFIG_GPIO_NRFX_INTERRUPT=n +CONFIG_MCUBOOT_SERIAL=y +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by QSPI +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 + +# Required by USB and QSPI +CONFIG_MULTITHREADING=y + +# USB +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n +CONFIG_USB_DEVICE_REMOTE_WAKEUP=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor ASA" +CONFIG_USB_DEVICE_PRODUCT="Bootloader Thingy:53" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x5300 +CONFIG_USB_CDC_ACM=y + +# Decrease memory footprint +CONFIG_CBPRINTF_NANO=y +CONFIG_TIMESLICING=n +CONFIG_BOOT_BANNER=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n +CONFIG_LOG=n +CONFIG_ERRNO=n +CONFIG_PRINTK=n +CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_SPI=n +CONFIG_I2C=n +CONFIG_UART_NRFX=n + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 +CONFIG_BOOT_UPGRADE_ONLY=y +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +# Enable custom command to erase settings partition. +CONFIG_ENABLE_MGMT_PERUSER=y +CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf52840.conf b/bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf52840.conf new file mode 100644 index 0000000..c0d1834 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf52840.conf @@ -0,0 +1,34 @@ +# Disable Zephyr console +CONFIG_LOG=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# The build won't fit on the partition allocated for it without size +# optimizations. +CONFIG_SIZE_OPTIMIZATIONS=y +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x12000 + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_NRFX=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUboot serial recovery +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_COMPOSITE_DEVICE=y +CONFIG_USB_MASS_STORAGE=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x520F diff --git a/bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf9160.conf b/bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf9160.conf new file mode 100644 index 0000000..1bf2e42 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/thingy91_nrf9160.conf @@ -0,0 +1,13 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# Disable Flash protection +CONFIG_FPROTECT=n + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y diff --git a/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf b/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf new file mode 100644 index 0000000..d3e253b --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf @@ -0,0 +1,63 @@ +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=110 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y + +# Disable Zephyr console +CONFIG_LOG=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_NRFX=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUboot serial recovery +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_COMPOSITE_DEVICE=y +CONFIG_USB_MASS_STORAGE=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x910A + +CONFIG_BOOT_SERIAL_BOOT_MODE=y + +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x13E00 + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 +CONFIG_BOOT_UPGRADE_ONLY=y +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +CONFIG_BOOT_IMAGE_ACCESS_HOOKS=y + +# Makes it possible to update the network core using the flash simulator +CONFIG_NRF53_RECOVERY_NETWORK_CORE=y + +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE=y + +# Skip checks on the secondary image to make it possible to update MCUBoot on S1/S0 +CONFIG_MCUBOOT_VERIFY_IMG_ADDRESS=n + +CONFIG_BOOT_SERIAL_NO_APPLICATION=y +CONFIG_FW_INFO_FIRMWARE_VERSION=2 diff --git a/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.conf b/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.conf new file mode 100644 index 0000000..7c2042d --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.conf @@ -0,0 +1,21 @@ +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=512 + +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_SPI_NOR_SFDP_DEVICETREE=y +CONFIG_MULTITHREADING=y + +# Disable Zephyr console and use UART for MCUboot serial recovery instead +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n +CONFIG_MCUBOOT_SERIAL=y +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE=y + +CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y +CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y + +CONFIG_FW_INFO_FIRMWARE_VERSION=2 diff --git a/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.overlay b/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.overlay new file mode 100644 index 0000000..7f2818c --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/thingy91x_nrf9151.overlay @@ -0,0 +1,4 @@ +&uart0 { + status = "okay"; + current-speed = < 1000000 >; +}; diff --git a/bootloader/mcuboot/boot/zephyr/boards/tlsr9518adk80d_tlsr9518.conf b/bootloader/mcuboot/boot/zephyr/boards/tlsr9518adk80d_tlsr9518.conf new file mode 100644 index 0000000..d0f5330 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/tlsr9518adk80d_tlsr9518.conf @@ -0,0 +1,4 @@ +# Copyright 2022 Telink Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=4096 diff --git a/bootloader/mcuboot/boot/zephyr/boards/vmu_rt1170_mimxrt1176_cm7.conf b/bootloader/mcuboot/boot/zephyr/boards/vmu_rt1170_mimxrt1176_cm7.conf new file mode 100644 index 0000000..c38a67f --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boards/vmu_rt1170_mimxrt1176_cm7.conf @@ -0,0 +1,5 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=1024 +CONFIG_BOOT_ERASE_PROGRESSIVELY=y diff --git a/bootloader/mcuboot/boot/zephyr/boot_serial_extension_zephyr_basic.c b/bootloader/mcuboot/boot/zephyr/boot_serial_extension_zephyr_basic.c new file mode 100644 index 0000000..b0c75f4 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boot_serial_extension_zephyr_basic.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include <../subsys/mgmt/mcumgr/transport/include/mgmt/mcumgr/transport/smp_internal.h> + +#include +#include + +#include "bootutil/bootutil_log.h" +#include "../boot_serial/src/boot_serial_priv.h" +#include + +#include "bootutil/image.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/boot_hooks.h" + +#include + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#ifdef CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE +static int bs_custom_storage_erase(const struct nmgr_hdr *hdr, + const char *buffer, int len, + zcbor_state_t *cs) +{ + int rc; + const struct flash_area *fa; + + (void)buffer; + (void)len; + + if (hdr->nh_group != ZEPHYR_MGMT_GRP_BASIC || hdr->nh_op != NMGR_OP_WRITE || + hdr->nh_id != ZEPHYR_MGMT_GRP_BASIC_CMD_ERASE_STORAGE) { + return MGMT_ERR_ENOTSUP; + } + + rc = flash_area_open(FIXED_PARTITION_ID(storage_partition), &fa); + + if (rc < 0) { + BOOT_LOG_ERR("failed to open flash area"); + } else { + rc = flash_area_erase(fa, 0, flash_area_get_size(fa)); + if (rc < 0) { + BOOT_LOG_ERR("failed to erase flash area"); + } + flash_area_close(fa); + } + if (rc == 0) { + rc = MGMT_ERR_OK; + } else { + rc = MGMT_ERR_EUNKNOWN; + } + + zcbor_map_start_encode(cs, 10); + zcbor_tstr_put_lit(cs, "rc"); + zcbor_uint32_put(cs, rc); + zcbor_map_end_encode(cs, 10); + + return rc; +} + +MCUMGR_HANDLER_DEFINE(storage_erase, bs_custom_storage_erase); +#endif diff --git a/bootloader/mcuboot/boot/zephyr/boot_serial_extensions.c b/bootloader/mcuboot/boot/zephyr/boot_serial_extensions.c new file mode 100644 index 0000000..abbb651 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/boot_serial_extensions.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "bootutil/bootutil_log.h" +#include "../boot_serial/src/boot_serial_priv.h" +#include +#include + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +int bs_peruser_system_specific(const struct nmgr_hdr *hdr, const char *buffer, + int len, zcbor_state_t *cs) +{ + int mgmt_rc = MGMT_ERR_ENOTSUP; + + STRUCT_SECTION_FOREACH(mcuboot_bs_custom_handlers, function) { + if (function->handler) { + mgmt_rc = function->handler(hdr, buffer, len, cs); + + if (mgmt_rc != MGMT_ERR_ENOTSUP) { + break; + } + } + } + + if (mgmt_rc == MGMT_ERR_ENOTSUP) { + zcbor_map_start_encode(cs, 10); + zcbor_tstr_put_lit(cs, "rc"); + zcbor_uint32_put(cs, mgmt_rc); + zcbor_map_end_encode(cs, 10); + } + + return MGMT_ERR_OK; +} diff --git a/bootloader/mcuboot/boot/zephyr/decompression.c b/bootloader/mcuboot/boot/zephyr/decompression.c new file mode 100644 index 0000000..35a1950 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/decompression.c @@ -0,0 +1,1278 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include "compression/decompression.h" +#include "bootutil/crypto/sha.h" +#include "bootutil/bootutil_log.h" + +#if !defined(__BOOTSIM__) +#define TARGET_STATIC static +#else +#define TARGET_STATIC +#endif + +#if defined(MCUBOOT_SIGN_RSA) +#if MCUBOOT_SIGN_RSA_LEN == 2048 +#define EXPECTED_SIG_TLV IMAGE_TLV_RSA2048_PSS +#elif MCUBOOT_SIGN_RSA_LEN == 3072 +#define EXPECTED_SIG_TLV IMAGE_TLV_RSA3072_PSS +#endif +#elif defined(MCUBOOT_SIGN_EC256) || \ + defined(MCUBOOT_SIGN_EC384) || \ + defined(MCUBOOT_SIGN_EC) +#define EXPECTED_SIG_TLV IMAGE_TLV_ECDSA_SIG +#elif defined(MCUBOOT_SIGN_ED25519) +#define EXPECTED_SIG_TLV IMAGE_TLV_ED25519 +#endif + +#define DECOMP_BUF_SIZE CONFIG_BOOT_DECOMPRESSION_BUFFER_SIZE +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) +#define DECOMP_BUF_EXTRA_SIZE 2 +#else +#define DECOMP_BUF_EXTRA_SIZE 0 +#endif +#define DECOMP_BUF_ALLOC_SIZE (DECOMP_BUF_SIZE + DECOMP_BUF_EXTRA_SIZE) + +/* Number of times that consumed data by decompression system can be 0 in a row before aborting */ +#define OFFSET_ZERO_CHECK_TIMES 3 + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static int boot_sha_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, uint32_t protected_size, + uint8_t *buf, size_t buf_size, bootutil_sha_context *sha_ctx); + +bool boot_is_compressed_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state) +{ + /* Image is compressed in secondary slot, need to check if fits into the primary slot */ + bool opened_flash_area = false; + int primary_fa_id; + int rc; + int size_check; + int size; + uint32_t protected_tlvs_size; + uint32_t decompressed_size; + + if (BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT) == NULL) { + opened_flash_area = true; + } + + primary_fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT); + rc = flash_area_open(primary_fa_id, &BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); + assert(rc == 0); + + size_check = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); + + if (opened_flash_area) { + (void)flash_area_close(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); + } + + rc = bootutil_get_img_decomp_size(hdr, fap, &decompressed_size); + + if (rc) { + return false; + } + + if (!boot_u32_safe_add(&size, decompressed_size, hdr->ih_hdr_size)) { + return false; + } + + rc = boot_size_protected_tlvs(hdr, fap, &protected_tlvs_size); + + if (rc) { + return false; + } + + if (!boot_u32_safe_add(&size, size, protected_tlvs_size)) { + return false; + } + + if (size >= size_check) { + BOOT_LOG_ERR("Compressed image too large, decompressed image size: 0x%x, slot size: 0x%x", + size, size_check); + return false; + } + + return true; +} + +static bool is_compression_object_valid(struct nrf_compress_implementation *compression) +{ + if (compression == NULL || compression->init == NULL || compression->deinit == NULL || + compression->decompress_bytes_needed == NULL || compression->decompress == NULL) { + return false; + } + + return true; +} + +int bootutil_img_hash_decompress(struct enc_key_data *enc_state, int image_index, + struct image_header *hdr, const struct flash_area *fap, + uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len) +{ + int rc; + uint32_t read_pos = 0; + uint32_t write_pos = 0; + uint32_t protected_tlv_size = 0; + uint32_t decompressed_image_size; + uint32_t output_size_total = 0; + struct nrf_compress_implementation *compression_lzma = NULL; + struct nrf_compress_implementation *compression_arm_thumb = NULL; + TARGET_STATIC struct image_header modified_hdr; + bootutil_sha_context sha_ctx; + uint8_t flash_erased_value; + + bootutil_sha_init(&sha_ctx); + + /* Setup decompression system */ +#if CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA1 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1)) { +#elif CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA2 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) { +#endif + /* Compressed image does not use the correct compression type which is supported by this + * build + */ + BOOT_LOG_ERR("Invalid image compression flags: no supported compression found"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + compression_lzma = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_LZMA); + compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); + + if (!is_compression_object_valid(compression_lzma) || + !is_compression_object_valid(compression_arm_thumb)) { + /* Compression library missing or missing required function pointer */ + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + rc = compression_lzma->init(NULL); + rc = compression_arm_thumb->init(NULL); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + /* We need a modified header which has the updated sizes, start with the original header */ + memcpy(&modified_hdr, hdr, sizeof(modified_hdr)); + + /* Extract the decompressed image size from the protected TLV, set it and remove the + * compressed image flags + */ + rc = bootutil_get_img_decomp_size(hdr, fap, &decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine decompressed size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + /* Calculate the protected TLV size, these will not include the decompressed + * sha/size/signature entries + */ + rc = boot_size_protected_tlvs(hdr, fap, &protected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine protected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_protect_tlv_size = protected_tlv_size; + bootutil_sha_update(&sha_ctx, &modified_hdr, sizeof(modified_hdr)); + read_pos = sizeof(modified_hdr); + flash_erased_value = flash_area_erased_val(fap); + memset(tmp_buf, flash_erased_value, tmp_buf_sz); + + while (read_pos < modified_hdr.ih_hdr_size) { + uint32_t copy_size = tmp_buf_sz; + + if ((read_pos + copy_size) > modified_hdr.ih_hdr_size) { + copy_size = modified_hdr.ih_hdr_size - read_pos; + } + + bootutil_sha_update(&sha_ctx, tmp_buf, copy_size); + read_pos += copy_size; + } + + /* Read in compressed data, decompress and add to hash calculation */ + read_pos = 0; + + while (read_pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - read_pos; + uint32_t tmp_off = 0; + uint8_t offset_zero_check = 0; + + if (copy_size > tmp_buf_sz) { + copy_size = tmp_buf_sz; + } + + rc = flash_area_read(fap, (hdr->ih_hdr_size + read_pos), tmp_buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (hdr->ih_hdr_size + read_pos), copy_size, fap->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + /* Decompress data in chunks, writing it back with a larger write offset of the primary + * slot than read size of the secondary slot + */ + while (tmp_off < copy_size) { + uint32_t offset = 0; + uint8_t *output = NULL; + uint32_t output_size = 0; + uint32_t chunk_size; + bool last_packet = false; + + chunk_size = compression_lzma->decompress_bytes_needed(NULL); + + if (chunk_size > (copy_size - tmp_off)) { + chunk_size = (copy_size - tmp_off); + } + + if ((read_pos + tmp_off + chunk_size) >= hdr->ih_img_size) { + last_packet = true; + } + + rc = compression_lzma->decompress(NULL, &tmp_buf[tmp_off], chunk_size, last_packet, + &offset, &output, &output_size); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + write_pos += output_size; + + if (write_pos > decompressed_image_size) { + BOOT_LOG_ERR("Decompressed image larger than claimed TLV size, at least: %d", + write_pos); + rc = BOOT_EBADIMAGE; + goto finish; + } + + /* Additional dry-run validity checks */ + if (last_packet == true && write_pos == 0) { + /* Last packet and we still have no output, this is a faulty update */ + BOOT_LOG_ERR("All compressed data consumed without any output, image not valid"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + if (offset == 0) { + /* If the decompression system continually consumes 0 bytes, then there is a + * problem with this update image, abort and mark image as bad + */ + if (offset_zero_check >= OFFSET_ZERO_CHECK_TIMES) { + BOOT_LOG_ERR("Decompression system returning no output data, image not valid"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + ++offset_zero_check; + + break; + } else { + offset_zero_check = 0; + } + + /* Copy data to secondary buffer for calculating hash */ + if (output_size > 0) { + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + /* Run this through the ARM thumb filter */ + uint32_t offset_arm_thumb = 0; + uint8_t *output_arm_thumb = NULL; + uint32_t processed_size = 0; + uint32_t output_size_arm_thumb = 0; + + while (processed_size < output_size) { + uint32_t current_size = output_size - processed_size; + bool arm_thumb_last_packet = false; + + if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) { + current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE; + } + + if (last_packet && (processed_size + current_size) == + output_size) { + arm_thumb_last_packet = true; + } + + rc = compression_arm_thumb->decompress(NULL, &output[processed_size], + current_size, arm_thumb_last_packet, + &offset_arm_thumb, + &output_arm_thumb, + &output_size_arm_thumb); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + bootutil_sha_update(&sha_ctx, output_arm_thumb, output_size_arm_thumb); + output_size_total += output_size_arm_thumb; + processed_size += current_size; + } + } else { + bootutil_sha_update(&sha_ctx, output, output_size); + output_size_total += output_size; + } + } + + tmp_off += offset; + } + + read_pos += copy_size; + } + + if (modified_hdr.ih_img_size != output_size_total) { + BOOT_LOG_ERR("Decompression expected output_size mismatch: %d vs %d", + modified_hdr.ih_img_size, output_size_total); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* If there are any protected TLVs present, add them after the main decompressed image */ + if (modified_hdr.ih_protect_tlv_size > 0) { + rc = boot_sha_protected_tlvs(hdr, fap, modified_hdr.ih_protect_tlv_size, tmp_buf, + tmp_buf_sz, &sha_ctx); + } + + bootutil_sha_finish(&sha_ctx, hash_result); + +finish: + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + +finish_without_clean: + bootutil_sha_drop(&sha_ctx); + + return rc; +} + +static int boot_copy_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_dst, + uint32_t protected_size, uint8_t *buf, size_t buf_size, + uint16_t *buf_pos, uint32_t *written) +{ + int rc; + uint32_t off; + uint32_t write_pos = 0; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_PROT_INFO_MAGIC, + .it_tlv_tot = protected_size, + }; + uint16_t info_size_left = sizeof(tlv_info_header); + + while (info_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (info_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_info_header_address = (uint8_t *)&tlv_info_header; + + if (single_copy_size > info_size_left) { + single_copy_size = info_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_info_header_address[sizeof(tlv_info_header) - + info_size_left], single_copy_size); + *buf_pos += single_copy_size; + info_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE) { + /* Skip these TLVs as they are not needed */ + continue; + } else { + uint16_t header_size_left = sizeof(tlv_header); + uint16_t data_size_left = len; + + tlv_header.it_type = type; + tlv_header.it_len = len; + + while (header_size_left > 0 || data_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + uint8_t *tlv_header_address = (uint8_t *)&tlv_header; + + if (header_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > header_size_left) { + single_copy_size = header_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_header_address[sizeof(tlv_header) - + header_size_left], + single_copy_size); + *buf_pos += single_copy_size; + copy_size -= single_copy_size; + header_size_left -= single_copy_size; + } + + if (data_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > data_size_left) { + single_copy_size = data_size_left; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + (len - data_size_left)), + &buf[*buf_pos], single_copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + (len - data_size_left)), single_copy_size, fap_src->fa_id, rc); + goto out; + } + + *buf_pos += single_copy_size; + data_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + } + } + + *written = write_pos; + +out: + return rc; +} + +static int boot_sha_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, uint32_t protected_size, + uint8_t *buf, size_t buf_size, bootutil_sha_context *sha_ctx) +{ + int rc; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_PROT_INFO_MAGIC, + .it_tlv_tot = protected_size, + }; + + bootutil_sha_update(sha_ctx, &tlv_info_header, sizeof(tlv_info_header)); + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, true); + if (rc) { + goto out; + } + + while (true) { + uint32_t read_off = 0; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE) { + /* Skip these TLVs as they are not needed */ + continue; + } + + tlv_header.it_type = type; + tlv_header.it_len = len; + + bootutil_sha_update(sha_ctx, &tlv_header, sizeof(tlv_header)); + + while (read_off < len) { + uint32_t copy_size = buf_size; + + if (copy_size > (len - read_off)) { + copy_size = len - read_off; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + read_off), buf, copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + read_off), copy_size, fap_src->fa_id, rc); + goto out; + } + + bootutil_sha_update(sha_ctx, buf, copy_size); + read_off += copy_size; + } + } + +out: + return rc; +} + +int boot_size_protected_tlvs(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *sz) +{ + int rc = 0; + uint32_t tlv_size; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + + *sz = 0; + tlv_size = hdr->ih_protect_tlv_size; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE) { + /* Exclude these TLVs as they will be copied to the unprotected area */ + tlv_size -= len + sizeof(struct image_tlv); + } + } + + if (!rc) { + if (tlv_size == sizeof(struct image_tlv_info)) { + /* If there are no entries then omit protected TLV section entirely */ + tlv_size = 0; + } + + *sz = tlv_size; + } + +out: + return rc; +} + +int boot_size_unprotected_tlvs(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *sz) +{ + int rc = 0; + uint32_t tlv_size; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + + *sz = 0; + tlv_size = sizeof(struct image_tlv_info); + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } else if (bootutil_tlv_iter_is_prot(&it, off) && type != IMAGE_TLV_DECOMP_SHA && + type != IMAGE_TLV_DECOMP_SIGNATURE) { + /* Include size of protected hash and signature as these will be replacing the + * original ones + */ + continue; + } else if (type == EXPECTED_HASH_TLV || type == EXPECTED_SIG_TLV) { + /* Exclude the original unprotected TLVs for signature and hash, the length of the + * signature of the compressed data might not be the same size as the signaute of the + * decompressed data, as is the case when using ECDSA-P256 + */ + continue; + } + + tlv_size += len + sizeof(struct image_tlv); + } + + if (!rc) { + if (tlv_size == sizeof(struct image_tlv_info)) { + /* If there are no entries in the unprotected TLV section then there is something wrong + * with this image + */ + BOOT_LOG_ERR("No unprotected TLVs in post-decompressed image output, image is invalid"); + rc = BOOT_EBADIMAGE; + goto out; + } + + *sz = tlv_size; + } + +out: + return rc; +} + +static int boot_copy_unprotected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_dst, + uint32_t unprotected_size, uint8_t *buf, size_t buf_size, + uint16_t *buf_pos, uint32_t *written) +{ + int rc; + uint32_t write_pos = 0; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv_iter it_protected; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_INFO_MAGIC, + .it_tlv_tot = unprotected_size, + }; + uint16_t info_size_left = sizeof(tlv_info_header); + + while (info_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (info_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_info_header_address = (uint8_t *)&tlv_info_header; + + if (single_copy_size > info_size_left) { + single_copy_size = info_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_info_header_address[sizeof(tlv_info_header) - + info_size_left], single_copy_size); + *buf_pos += single_copy_size; + info_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, false); + if (rc) { + goto out; + } + + while (true) { + uint16_t header_size_left = sizeof(tlv_header); + uint16_t data_size_left; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } else if (bootutil_tlv_iter_is_prot(&it, off)) { + /* Skip protected TLVs */ + continue; + } + + /* Change the values of these fields from having the data in the compressed image + * unprotected TLV (which is valid only for the compressed image data) to having the + * fields in the protected TLV section (which is valid for the decompressed image data). + * The compressed data is no longer needed + */ + if (type == EXPECTED_HASH_TLV || type == EXPECTED_SIG_TLV) { + rc = bootutil_tlv_iter_begin(&it_protected, hdr, fap_src, (type == EXPECTED_HASH_TLV ? + IMAGE_TLV_DECOMP_SHA : + IMAGE_TLV_DECOMP_SIGNATURE), + true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it_protected, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + } + + if (type == IMAGE_TLV_DECOMP_SHA) { + type = EXPECTED_HASH_TLV; + } else { + type = EXPECTED_SIG_TLV; + } + } + + data_size_left = len; + tlv_header.it_type = type; + tlv_header.it_len = len; + + while (header_size_left > 0 || data_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (header_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_header_address = (uint8_t *)&tlv_header; + + if (single_copy_size > header_size_left) { + single_copy_size = header_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_header_address[sizeof(tlv_header) - header_size_left], + single_copy_size); + *buf_pos += single_copy_size; + copy_size -= single_copy_size; + header_size_left -= single_copy_size; + } + + if (data_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > data_size_left) { + single_copy_size = data_size_left; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + len - data_size_left), + &buf[*buf_pos], single_copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + (len - data_size_left)), single_copy_size, fap_src->fa_id, rc); + goto out; + } + + *buf_pos += single_copy_size; + data_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + } + + *written = write_pos; + +out: + return rc; +} + +int boot_copy_region_decompress(struct boot_loader_state *state, const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_src, + uint32_t off_dst, uint32_t sz, uint8_t *buf, size_t buf_size) +{ + int rc; + uint32_t pos = 0; + uint16_t decomp_buf_size = 0; + uint16_t write_alignment; + uint32_t write_pos = 0; + uint32_t protected_tlv_size = 0; + uint32_t unprotected_tlv_size = 0; + uint32_t tlv_write_size = 0; + uint32_t decompressed_image_size; + struct nrf_compress_implementation *compression_lzma = NULL; + struct nrf_compress_implementation *compression_arm_thumb = NULL; + struct image_header *hdr; + TARGET_STATIC uint8_t decomp_buf[DECOMP_BUF_ALLOC_SIZE] __attribute__((aligned(4))); + TARGET_STATIC struct image_header modified_hdr; + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + uint8_t excess_data_buffer[DECOMP_BUF_EXTRA_SIZE]; + bool excess_data_buffer_full = false; +#endif + + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + + /* Setup decompression system */ +#if CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA1 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1)) { +#elif CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA2 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) { +#endif + /* Compressed image does not use the correct compression type which is supported by this + * build + */ + BOOT_LOG_ERR("Invalid image compression flags: no supported compression found"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + compression_lzma = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_LZMA); + compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); + + if (!is_compression_object_valid(compression_lzma) || + !is_compression_object_valid(compression_arm_thumb)) { + /* Compression library missing or missing required function pointer */ + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish; + } + + rc = compression_lzma->init(NULL); + rc = compression_arm_thumb->init(NULL); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish; + } + + write_alignment = flash_area_align(fap_dst); + + memcpy(&modified_hdr, hdr, sizeof(modified_hdr)); + + rc = bootutil_get_img_decomp_size(hdr, fap_src, &decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine decompressed size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + /* Calculate protected TLV size for target image once items are removed */ + rc = boot_size_protected_tlvs(hdr, fap_src, &protected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine protected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_protect_tlv_size = protected_tlv_size; + + rc = boot_size_unprotected_tlvs(hdr, fap_src, &unprotected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine unprotected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + /* Write out the image header first, this should be a multiple of the write size */ + rc = flash_area_write(fap_dst, off_dst, &modified_hdr, sizeof(modified_hdr)); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off_dst, sizeof(modified_hdr), fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + /* Read in, decompress and write out data */ + while (pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - pos; + uint32_t tmp_off = 0; + + if (copy_size > buf_size) { + copy_size = buf_size; + } + + rc = flash_area_read(fap_src, off_src + hdr->ih_hdr_size + pos, buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_src + hdr->ih_hdr_size + pos), copy_size, fap_src->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + /* Decompress data in chunks, writing it back with a larger write offset of the primary + * slot than read size of the secondary slot + */ + while (tmp_off < copy_size) { + uint32_t offset = 0; + uint32_t output_size = 0; + uint32_t chunk_size; + uint32_t compression_buffer_pos = 0; + uint8_t *output = NULL; + bool last_packet = false; + + chunk_size = compression_lzma->decompress_bytes_needed(NULL); + + if (chunk_size > (copy_size - tmp_off)) { + chunk_size = (copy_size - tmp_off); + } + + if ((pos + tmp_off + chunk_size) >= hdr->ih_img_size) { + last_packet = true; + } + + rc = compression_lzma->decompress(NULL, &buf[tmp_off], chunk_size, last_packet, + &offset, &output, &output_size); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* Copy data to secondary buffer for writing out */ + while (output_size > 0) { + uint32_t data_size = (DECOMP_BUF_SIZE - decomp_buf_size); + + if (data_size > output_size) { + data_size = output_size; + } + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + memcpy(&decomp_buf[decomp_buf_size + DECOMP_BUF_EXTRA_SIZE], + &output[compression_buffer_pos], data_size); + } else +#endif + { + memcpy(&decomp_buf[decomp_buf_size], &output[compression_buffer_pos], + data_size); + } + + compression_buffer_pos += data_size; + + decomp_buf_size += data_size; + output_size -= data_size; + + /* Write data out from secondary buffer when it is full */ + if (decomp_buf_size == DECOMP_BUF_SIZE) { +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + uint32_t filter_writeback_pos = 0; + uint32_t processed_size = 0; + + /* Run this through the ARM thumb filter */ + while (processed_size < DECOMP_BUF_SIZE) { + uint32_t offset_arm_thumb = 0; + uint32_t output_size_arm_thumb = 0; + uint8_t *output_arm_thumb = NULL; + uint32_t current_size = DECOMP_BUF_SIZE; + bool arm_thumb_last_packet = false; + + if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) { + current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE; + } + + if (last_packet && (processed_size + current_size) == DECOMP_BUF_SIZE + && output_size == 0) { + arm_thumb_last_packet = true; + } + + rc = compression_arm_thumb->decompress(NULL, + &decomp_buf[processed_size + + DECOMP_BUF_EXTRA_SIZE], + current_size, + arm_thumb_last_packet, + &offset_arm_thumb, + &output_arm_thumb, + &output_size_arm_thumb); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + memcpy(&decomp_buf[filter_writeback_pos], output_arm_thumb, + output_size_arm_thumb); + filter_writeback_pos += output_size_arm_thumb; + processed_size += current_size; + } + + if (excess_data_buffer_full == true) + { + /* Restore extra data removed from previous iteration to the write + * buffer + */ + memmove(&decomp_buf[DECOMP_BUF_EXTRA_SIZE], decomp_buf, + filter_writeback_pos); + memcpy(decomp_buf, excess_data_buffer, DECOMP_BUF_EXTRA_SIZE); + excess_data_buffer_full = false; + filter_writeback_pos += DECOMP_BUF_EXTRA_SIZE; + } + + if ((filter_writeback_pos % sizeof(uint32_t)) != 0) + { + /* Since there are an extra 2 bytes here, remove them and stash for + * later usage to prevent flash write issues with non-word boundary + * writes + */ + memcpy(excess_data_buffer, &decomp_buf[filter_writeback_pos - + DECOMP_BUF_EXTRA_SIZE], + DECOMP_BUF_EXTRA_SIZE); + excess_data_buffer_full = true; + filter_writeback_pos -= DECOMP_BUF_EXTRA_SIZE; + } + + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, filter_writeback_pos); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), DECOMP_BUF_SIZE, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += filter_writeback_pos; + decomp_buf_size = 0; + filter_writeback_pos = 0; + } else +#endif + { + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, DECOMP_BUF_SIZE); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), DECOMP_BUF_SIZE, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += DECOMP_BUF_SIZE; + decomp_buf_size = 0; + } + } + } + + tmp_off += offset; + } + + pos += copy_size; + } + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT && decomp_buf_size > 0) { + /* Extra data that has not been written out that needs ARM thumb filter applied */ + uint32_t offset_arm_thumb = 0; + uint32_t output_size_arm_thumb = 0; + uint8_t *output_arm_thumb = NULL; + + rc = compression_arm_thumb->decompress(NULL, &decomp_buf[DECOMP_BUF_EXTRA_SIZE], + decomp_buf_size, true, &offset_arm_thumb, + &output_arm_thumb, &output_size_arm_thumb); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + memcpy(decomp_buf, output_arm_thumb, output_size_arm_thumb); + } +#endif + + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + + if (protected_tlv_size > 0) { + rc = boot_copy_protected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size + + write_pos), protected_tlv_size, + decomp_buf, DECOMP_BUF_SIZE, &decomp_buf_size, + &tlv_write_size); + + if (rc) { + BOOT_LOG_ERR("Protected TLV copy failure: %d", rc); + goto finish; + } + + write_pos += tlv_write_size; + } + + tlv_write_size = 0; + rc = boot_copy_unprotected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size + + write_pos), unprotected_tlv_size, + decomp_buf, DECOMP_BUF_SIZE, &decomp_buf_size, + &tlv_write_size); + + if (rc) { + BOOT_LOG_ERR("Protected TLV copy failure: %d", rc); + goto finish; + } + + write_pos += tlv_write_size; + + /* Check if we have unwritten data buffered up and, if so, write it out */ + if (decomp_buf_size > 0) { + uint32_t write_padding_size = write_alignment - (decomp_buf_size % write_alignment); + + /* Check if additional write padding should be applied to meet the minimum write size */ + if (write_alignment > 1 && write_padding_size) { + uint8_t flash_erased_value; + + flash_erased_value = flash_area_erased_val(fap_dst); + memset(&decomp_buf[decomp_buf_size], flash_erased_value, write_padding_size); + decomp_buf_size += write_padding_size; + } + + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf, + decomp_buf_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_size, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += decomp_buf_size; + decomp_buf_size = 0; + } + +finish: + memset(decomp_buf, 0, sizeof(decomp_buf)); + + return rc; +} + +int bootutil_get_img_decomp_size(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *img_decomp_size) +{ + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + if (hdr == NULL || fap == NULL || img_decomp_size == NULL) { + return BOOT_EBADARGS; + } else if (hdr->ih_protect_tlv_size == 0) { + return BOOT_EBADIMAGE; + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SIZE, true); + + if (rc) { + return rc; + } + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + + if (rc != 0) { + return -1; + } + + if (len != sizeof(*img_decomp_size)) { + BOOT_LOG_ERR("Invalid decompressed image size TLV: %d", len); + return BOOT_EBADIMAGE; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, img_decomp_size, len); + + if (rc) { + BOOT_LOG_ERR("Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off, len, fap->fa_id, rc); + return BOOT_EFLASH; + } + + return 0; +} diff --git a/bootloader/mcuboot/boot/zephyr/external_crypto.conf b/bootloader/mcuboot/boot/zephyr/external_crypto.conf new file mode 100644 index 0000000..8181ad5 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/external_crypto.conf @@ -0,0 +1,20 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# These configurations should be used when using nrf/samples/bootloader +# as the immutable bootloader (B0), and MCUBoot as the second stage updateable +# bootloader. + +# Set ECDSA as signing mechanism +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +# Use crypto backend from B0 +CONFIG_BOOT_NRF_EXTERNAL_CRYPTO=y +CONFIG_SECURE_BOOT_CRYPTO=y +CONFIG_SB_CRYPTO_CLIENT_ECDSA_SECP256R1=y +CONFIG_SB_CRYPTO_CLIENT_SHA256=y +CONFIG_BL_SHA256_EXT_API_REQUIRED=y +CONFIG_BL_SECP256R1_EXT_API_REQUIRED=y diff --git a/bootloader/mcuboot/boot/zephyr/firmware_loader.c b/bootloader/mcuboot/boot/zephyr/firmware_loader.c new file mode 100644 index 0000000..11b461c --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/firmware_loader.c @@ -0,0 +1,196 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020 Arm Limited + * Copyright (c) 2020-2023 Nordic Semiconductor ASA + */ + +#include +#include +#include +#include +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/fault_injection_hardening.h" + +#include "io/io.h" +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +/* Variables passed outside of unit via poiters. */ +static const struct flash_area *_fa_p; +static struct image_header _hdr = { 0 }; + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) +/** + * Validate hash of a primary boot image. + * + * @param[in] fa_p flash area pointer + * @param[in] hdr boot image header pointer + * + * @return FIH_SUCCESS on success, error code otherwise + */ +fih_ret +boot_image_validate(const struct flash_area *fa_p, + struct image_header *hdr) +{ + static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + /* NOTE: The first argument to boot_image_validate, for enc_state pointer, + * is allowed to be NULL only because the single image loader compiles + * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses + * the pointer from compilation. + */ + /* Validate hash */ + if (IS_ENCRYPTED(hdr)) + { + /* Clear the encrypted flag we didn't supply a key + * This flag could be set if there was a decryption in place + * was performed. We will try to validate the image, and if still + * encrypted the validation will fail, and go in panic mode + */ + hdr->ih_flags &= ~(ENCRYPTIONFLAGS); + } + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, hdr, fa_p, tmpbuf, + BOOT_TMPBUF_SZ, NULL, 0, NULL); + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/ + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) +inline static fih_ret +boot_image_validate_once(const struct flash_area *fa_p, + struct image_header *hdr) +{ + static struct boot_swap_state state; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + memset(&state, 0, sizeof(struct boot_swap_state)); + rc = boot_read_swap_state(fa_p, &state); + if (rc != 0) + FIH_RET(FIH_FAILURE); + if (state.magic != BOOT_MAGIC_GOOD + || state.image_ok != BOOT_FLAG_SET) { + /* At least validate the image once */ + FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(FIH_FAILURE); + } + if (state.magic != BOOT_MAGIC_GOOD) { + rc = boot_write_magic(fa_p); + if (rc != 0) + FIH_RET(FIH_FAILURE); + } + rc = boot_write_image_ok(fa_p); + if (rc != 0) + FIH_RET(FIH_FAILURE); + } + FIH_RET(FIH_SUCCESS); +} +#endif + +/** + * Validates that an image in a slot is OK to boot. + * + * @param[in] slot Slot number to check + * @param[out] rsp Parameters for booting image, on success + * + * @return FIH_SUCCESS on success; non-zero on failure. + */ +static fih_ret validate_image_slot(int slot, struct boot_rsp *rsp) +{ + int rc = -1; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + rc = flash_area_open(slot, &_fa_p); + assert(rc == 0); + + rc = boot_image_load_header(_fa_p, &_hdr); + if (rc != 0) { + goto other; + } + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT + FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto other; + } +#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) + FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto other; + } +#else + fih_rc = FIH_SUCCESS; +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ + + rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p); + rsp->br_image_off = flash_area_get_off(_fa_p); + rsp->br_hdr = &_hdr; + +other: + flash_area_close(_fa_p); + + FIH_RET(fih_rc); +} + +/** + * Gather information on image and prepare for booting. Will boot from main + * image if none of the enabled entrance modes for the firmware loader are set, + * otherwise will boot the firmware loader. Note: firmware loader must be a + * valid signed image with the same signing key as the application image. + * + * @param[out] rsp Parameters for booting image, on success + * + * @return FIH_SUCCESS on success; non-zero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + bool boot_firmware_loader = false; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO + if (io_detect_pin() && + !io_boot_skip_serial_recovery()) { + boot_firmware_loader = true; + } +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET + if (io_detect_pin_reset()) { + boot_firmware_loader = true; + } +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE + if (io_detect_boot_mode()) { + boot_firmware_loader = true; + } +#endif + + /* Check if firmware loader button is pressed. TODO: check all entrance methods */ + if (boot_firmware_loader == true) { + FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp); + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(fih_rc); + } + } + + FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_PRIMARY(0), rsp); + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_CALL(validate_image_slot, fih_rc, FLASH_AREA_IMAGE_SECONDARY(0), rsp); + } +#endif + + FIH_RET(fih_rc); +} diff --git a/bootloader/mcuboot/boot/zephyr/flash_map_extended.c b/bootloader/mcuboot/boot/zephyr/flash_map_extended.c new file mode 100644 index 0000000..d0744af --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/flash_map_extended.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "target.h" + +#include +#include + +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#if (!defined(CONFIG_XTENSA) && DT_HAS_CHOSEN(zephyr_flash_controller)) +#define FLASH_DEVICE_ID SOC_FLASH_0_ID +#define FLASH_DEVICE_BASE CONFIG_FLASH_BASE_ADDRESS +#define FLASH_DEVICE_NODE DT_CHOSEN(zephyr_flash_controller) + +#elif (defined(CONFIG_XTENSA) && DT_NODE_EXISTS(DT_INST(0, jedec_spi_nor))) +#define FLASH_DEVICE_ID SPI_FLASH_0_ID +#define FLASH_DEVICE_BASE 0 +#define FLASH_DEVICE_NODE DT_INST(0, jedec_spi_nor) + +#elif defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32) + +#define FLASH_DEVICE_ID SPI_FLASH_0_ID +#define FLASH_DEVICE_BASE 0 +#define FLASH_DEVICE_NODE DT_CHOSEN(zephyr_flash_controller) + +#else +#error "FLASH_DEVICE_ID could not be determined" +#endif + +static const struct device *flash_dev = DEVICE_DT_GET(FLASH_DEVICE_NODE); + +int flash_device_base(uint8_t fd_id, uintptr_t *ret) +{ + if (fd_id != FLASH_DEVICE_ID) { + BOOT_LOG_ERR("invalid flash ID %d; expected %d", + fd_id, FLASH_DEVICE_ID); + return -EINVAL; + } + *ret = FLASH_DEVICE_BASE; + return 0; +} + +/* + * This depends on the mappings defined in sysflash.h. + * MCUBoot uses continuous numbering for the primary slot, the secondary slot, + * and the scratch while zephyr might number it differently. + */ +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + switch (slot) { + case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); +#if !defined(CONFIG_SINGLE_APPLICATION_SLOT) + case 1: return FLASH_AREA_IMAGE_SECONDARY(image_index); +#endif +#if defined(CONFIG_BOOT_SWAP_USING_SCRATCH) + case 2: return FLASH_AREA_IMAGE_SCRATCH; +#endif + } + + return -EINVAL; /* flash_area_open will fail on that */ +} + +int flash_area_id_from_image_slot(int slot) +{ + return flash_area_id_from_multi_image_slot(0, slot); +} + +int flash_area_id_to_multi_image_slot(int image_index, int area_id) +{ + if (area_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) { + return 0; + } +#if !defined(CONFIG_SINGLE_APPLICATION_SLOT) + if (area_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) { + return 1; + } +#endif + + BOOT_LOG_ERR("invalid flash area ID"); + return -1; +} + +#if defined(CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD) +int flash_area_id_from_direct_image(int image_id) +{ + switch (image_id) { + case 0: + case 1: + return FIXED_PARTITION_ID(slot0_partition); +#if FIXED_PARTITION_EXISTS(slot1_partition) + case 2: + return FIXED_PARTITION_ID(slot1_partition); +#endif +#if FIXED_PARTITION_EXISTS(slot2_partition) + case 3: + return FIXED_PARTITION_ID(slot2_partition); +#endif +#if FIXED_PARTITION_EXISTS(slot3_partition) + case 4: + return FIXED_PARTITION_ID(slot3_partition); +#endif +#if FIXED_PARTITION_EXISTS(slot4_partition) + case 5: + return FIXED_PARTITION_ID(slot4_partition); +#endif +#if FIXED_PARTITION_EXISTS(slot5_partition) + case 6: + return FIXED_PARTITION_ID(slot5_partition); +#endif + } + return -EINVAL; +} +#endif + +int flash_area_sector_from_off(off_t off, struct flash_sector *sector) +{ + int rc; + struct flash_pages_info page; + + rc = flash_get_page_info_by_offs(flash_dev, off, &page); + if (rc) { + return rc; + } + + sector->fs_off = page.start_offset; + sector->fs_size = page.size; + + return rc; +} + +uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + (void)fa; + return FLASH_DEVICE_ID; +} + +#define ERASED_VAL 0xff +__weak uint8_t flash_area_erased_val(const struct flash_area *fap) +{ + (void)fap; + return ERASED_VAL; +} + +int flash_area_get_sector(const struct flash_area *fap, off_t off, + struct flash_sector *fsp) +{ + struct flash_pages_info fpi; + int rc; + + if (off >= fap->fa_size) { + return -ERANGE; + } + + rc = flash_get_page_info_by_offs(fap->fa_dev, fap->fa_off + off, + &fpi); + + if (rc == 0) { + fsp->fs_off = fpi.start_offset - fap->fa_off; + fsp->fs_size = fpi.size; + } + + return rc; +} diff --git a/bootloader/mcuboot/boot/zephyr/flash_map_legacy.c b/bootloader/mcuboot/boot/zephyr/flash_map_legacy.c new file mode 100644 index 0000000..6a8e7ae --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/flash_map_legacy.c @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @file + * @brief Legacy flash fallbacks + * + * This file contains hacks for flash drivers without page layout + * support. They're hacks because they guess a page layout that may be + * incorrect, but is likely to "work". Needless to say, such guesswork + * is undesirable in trusted bootloader code. + * + * The behavior is: + * + * - If FLASH_AREA_IMAGE_SECTOR_SIZE is defined (this was used by + * older Zephyr ports), the image sectors have uniform sector sizes. + * We also assume that's the size of the scratch sectors. + * + * - Otherwise, we assume that the size of the entire scratch area is + * a least common multiple of all sector sizes, and use that as + * FLASH_AREA_IMAGE_SECTOR_SIZE. + */ + +#include "bootutil/bootutil_log.h" + +#include +#include +#include + +#warning "The flash driver lacks page layout support; falling back on hacks." + +#if !defined(FLASH_AREA_IMAGE_SECTOR_SIZE) +#define FLASH_AREA_IMAGE_SECTOR_SIZE FLASH_AREA_IMAGE_SCRATCH_SIZE +#endif + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +/* + * Lookup the sector map for a given flash area. This should fill in + * `ret` with all of the sectors in the area. `*cnt` will be set to + * the storage at `ret` and should be set to the final number of + * sectors in this area. + */ +int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret) +{ + const struct flash_area *fa; + uint32_t max_cnt = *cnt; + uint32_t rem_len; + int rc = -1; + + if (flash_area_open(idx, &fa)) { + goto out; + } + + BOOT_LOG_DBG("area %d: offset=0x%x, length=0x%x", idx, fa->fa_off, + fa->fa_size); + + if (*cnt < 1) { + goto fa_close_out; + } + + rem_len = fa->fa_size; + *cnt = 0; + while (rem_len > 0 && *cnt < max_cnt) { + if (rem_len < FLASH_AREA_IMAGE_SECTOR_SIZE) { + BOOT_LOG_ERR("area %d size 0x%x not divisible by sector size 0x%x", + idx, fa->fa_size, FLASH_AREA_IMAGE_SECTOR_SIZE); + goto fa_close_out; + } + + ret[*cnt].fs_off = FLASH_AREA_IMAGE_SECTOR_SIZE * (*cnt); + ret[*cnt].fs_size = FLASH_AREA_IMAGE_SECTOR_SIZE; + *cnt = *cnt + 1; + rem_len -= FLASH_AREA_IMAGE_SECTOR_SIZE; + } + + if (*cnt >= max_cnt) { + BOOT_LOG_ERR("flash area %d sector count overflow", idx); + goto fa_close_out; + } + + rc = 0; + +fa_close_out: + flash_area_close(fa); +out: + return rc; +} diff --git a/bootloader/mcuboot/boot/zephyr/hooks_sample.c b/bootloader/mcuboot/boot/zephyr/hooks_sample.c new file mode 100644 index 0000000..a5a7293 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/hooks_sample.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "bootutil/image.h" +#include "bootutil/bootutil.h" +#include "bootutil/fault_injection_hardening.h" +#include "flash_map_backend/flash_map_backend.h" + +/* @retval 0: header was read/populated + * FIH_FAILURE: image is invalid, + * BOOT_HOOK_REGULAR if hook not implemented for the image-slot, + * othervise an error-code value. + */ +int boot_read_image_header_hook(int img_index, int slot, + struct image_header *img_hed) +{ + if (img_index == 1 && slot == 0) { + img_hed->ih_magic = IMAGE_MAGIC; + return 0; + } + + return BOOT_HOOK_REGULAR; +} + +/* @retval FIH_SUCCESS: image is valid, + * FIH_FAILURE: image is invalid, + * fih encoded BOOT_HOOK_REGULAR if hook not implemented for + * the image-slot. + */ +fih_ret boot_image_check_hook(int img_index, int slot) +{ + if (img_index == 1 && slot == 0) { + FIH_RET(FIH_SUCCESS); + } + + FIH_RET(FIH_BOOT_HOOK_REGULAR); +} + +int boot_perform_update_hook(int img_index, struct image_header *img_head, + const struct flash_area *area) +{ + if (img_index == 1) { + return 0; + } + + return BOOT_HOOK_REGULAR; +} + +int boot_read_swap_state_primary_slot_hook(int image_index, + struct boot_swap_state *state) +{ + if (image_index == 1) { + state->magic = BOOT_MAGIC_UNSET; + state->swap_type = BOOT_SWAP_TYPE_NONE; + state->image_num = image_index ; // ? + state->copy_done = BOOT_FLAG_UNSET; + state->image_ok = BOOT_FLAG_UNSET; + + return 0; + } + + return BOOT_HOOK_REGULAR; +} + +int boot_copy_region_post_hook(int img_index, const struct flash_area *area, + size_t size) +{ + return 0; +} + +int boot_serial_uploaded_hook(int img_index, const struct flash_area *area, + size_t size) +{ + return 0; +} + +int boot_img_install_stat_hook(int image_index, int slot, int *img_install_stat) +{ + return BOOT_HOOK_REGULAR; +} diff --git a/bootloader/mcuboot/boot/zephyr/include/arm_cleanup.h b/bootloader/mcuboot/boot/zephyr/include/arm_cleanup.h new file mode 100644 index 0000000..565477c --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/arm_cleanup.h @@ -0,0 +1,23 @@ + +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_ARM_CLEANUP_ +#define H_ARM_CLEANUP_ + +/** + * Cleanup interrupt priority and interupt enable registers. + */ +void cleanup_arm_nvic(void); + +#if defined(CONFIG_CPU_HAS_ARM_MPU) || defined(CONFIG_CPU_HAS_NXP_MPU) +/** + * Cleanup all ARM MPU region configuration + */ +void z_arm_clear_arm_mpu_config(void); +#endif + +#endif diff --git a/bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial.ld b/bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial.ld new file mode 100644 index 0000000..c0e82ad --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial.ld @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +ITERABLE_SECTION_ROM(mcuboot_bs_custom_handlers, 4) diff --git a/bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial_extensions.h b/bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial_extensions.h new file mode 100644 index 0000000..6eea574 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/boot_serial/boot_serial_extensions.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_BOOT_SERIAL_EXTENTIONS_ +#define H_BOOT_SERIAL_EXTENTIONS_ + +#include +#include +#include + +/** + * Callback handler prototype for boot serial extensions. + * + * @param[in] hdr MCUmgr header + * @param[in] buffer Buffer with first MCUmgr message + * @param[in] len Length of data in buffer + * @param[out] cs Response + * + * @return MGMT_ERR_ENOTSUP to run other handlers, other MGMT_ERR_* value + * when expected handler has ran. + */ +typedef int (*bs_custom_handler_cb)(const struct nmgr_hdr *hdr, + const char *buffer, int len, + zcbor_state_t *cs); + +struct mcuboot_bs_custom_handlers { + const bs_custom_handler_cb handler; +}; + +/* Used to create an iterable section containing a boot serial handler + * function + */ +#define MCUMGR_HANDLER_DEFINE(name, _handler) \ + STRUCT_SECTION_ITERABLE(mcuboot_bs_custom_handlers, name) = { \ + .handler = _handler, \ + } + +#endif /* H_BOOT_SERIAL_EXTENTIONS_ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/compression/decompression.h b/bootloader/mcuboot/boot/zephyr/include/compression/decompression.h new file mode 100644 index 0000000..f8a676a --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/compression/decompression.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_DECOMPRESSION_ +#define H_DECOMPRESSION_ + +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "../src/bootutil_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Checks if a compressed image header is valid. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param state Bootloader state object. + * + * @return true if valid; false if invalid. + */ +bool boot_is_compressed_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state); + +/** + * Reads in compressed image data from a slot, decompresses it and writes it out to a destination + * slot, including corresponding image headers and TLVs. + * + * @param state Bootloader state object. + * @param fap_src Flash area of the source slot. + * @param fap_dst Flash area of the destination slot. + * @param off_src Offset of the source slot to read from (should be 0). + * @param off_dst Offset of the destination slot to write to (should be 0). + * @param sz Size of the source slot data. + * @param buf Temporary buffer for reading data from. + * @param buf_size Size of temporary buffer. + * + * @return 0 on success; nonzero on failure. + */ +int boot_copy_region_decompress(struct boot_loader_state *state, const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_src, + uint32_t off_dst, uint32_t sz, uint8_t *buf, size_t buf_size); + +/** + * Gets the total data size (excluding headers and TLVs) of a compressed image when it is + * decompressed. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param img_decomp_size Pointer to variable that will be updated with the decompressed image + * size. + * + * @return 0 on success; nonzero on failure. + */ +int bootutil_get_img_decomp_size(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *img_decomp_size); + +/** + * Calculate MCUboot-compatible image hash of compressed image slot. + * + * @param enc_state Not currently used, set to NULL. + * @param image_index Image number. + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param tmp_buf Temporary buffer for reading data from. + * @param tmp_buf_sz Size of temporary buffer. + * @param hash_result Pointer to a variable that will be updated with the image hash. + * @param seed Not currently used, set to NULL. + * @param seed_len Not currently used, set to 0. + * + * @return 0 on success; nonzero on failure. + */ +int bootutil_img_hash_decompress(struct enc_key_data *enc_state, int image_index, + struct image_header *hdr, const struct flash_area *fap, + uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len); + +/** + * Calculates the size that the compressed image protected TLV section will occupy once the image + * has been decompressed. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param sz Pointer to variable that will be updated with the protected TLV size. + * + * @return 0 on success; nonzero on failure. + */ +int boot_size_protected_tlvs(const struct image_header *hdr, const struct flash_area *fap_src, + uint32_t *sz); + +#ifdef __cplusplus +} +#endif + +#endif /* H_DECOMPRESSION_ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/config-asn1.h b/bootloader/mcuboot/boot/zephyr/include/config-asn1.h new file mode 100644 index 0000000..b9d70df --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/config-asn1.h @@ -0,0 +1,44 @@ +/* + * Configuration of mbedTLS containing only the ASN.1 parser. + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MBEDTLS_CONFIG_ASN1_H +#define MBEDTLS_CONFIG_ASN1_H + +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +// #define MBEDTLS_ASN1_WRITE_C +// #define MBEDTLS_BIGNUM_C +// #define MBEDTLS_MD_C +// #define MBEDTLS_OID_C +// #define MBEDTLS_SHA256_C + +#endif /* MBEDTLS_CONFIG_ASN1_H */ diff --git a/bootloader/mcuboot/boot/zephyr/include/config-ec.h b/bootloader/mcuboot/boot/zephyr/include/config-ec.h new file mode 100644 index 0000000..f6c690b --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/config-ec.h @@ -0,0 +1,94 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_ECDSA +#define MCUBOOT_MBEDTLS_CONFIG_ECDSA + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ECDH_C + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_NIST_OPTIM +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C + +/* Bring in support for x509. */ +#define MBEDTLS_X509_USE_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_X509_CRT_PARSE_C + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_MPI_MAX_SIZE 32 + +#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +/* If encryption is being used, also enable the features needed for + * that. */ +#if defined(MCUBOOT_ENC_IMAGES) +#define MBEDTLS_CIPHER_MODE_CTR +#define MBEDTLS_CIPHER_C +#define MBEDTLS_NIST_KW_C +#endif /* MCUBOOT_ENC_IMAGES */ + +#endif /* MCUBOOT_MBEDTLS_CONFIG_ECDSA */ diff --git a/bootloader/mcuboot/boot/zephyr/include/config-ed25519.h b/bootloader/mcuboot/boot/zephyr/include/config-ed25519.h new file mode 100644 index 0000000..2766ee2 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/config-ed25519.h @@ -0,0 +1,76 @@ +/* + * Configuration of mbedTLS containing only the ASN.1 parser. + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - ed25519 signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_ED25519 +#define MCUBOOT_MBEDTLS_CONFIG_ED25519 + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_CIPHER_MODE_CTR + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SHA224_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_AES_C + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_MPI_MAX_SIZE 64 + +//#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +#endif /* MCUBOOT_MBEDTLS_CONFIG_RSA */ diff --git a/bootloader/mcuboot/boot/zephyr/include/config-kw.h b/bootloader/mcuboot/boot/zephyr/include/config-kw.h new file mode 100644 index 0000000..1198fe5 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/config-kw.h @@ -0,0 +1,66 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_KW +#define MCUBOOT_MBEDTLS_CONFIG_KW + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#define MBEDTLS_ASN1_PARSE_C + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_CIPHER_MODE_CTR + +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_NIST_KW_C + +#endif /* MCUBOOT_MBEDTLS_CONFIG_KW */ diff --git a/bootloader/mcuboot/boot/zephyr/include/config-rsa-kw.h b/bootloader/mcuboot/boot/zephyr/include/config-rsa-kw.h new file mode 100644 index 0000000..ae67df9 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/config-rsa-kw.h @@ -0,0 +1,80 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA signature verification + NIST Keywrapping support + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_RSA_KW +#define MCUBOOT_MBEDTLS_CONFIG_RSA_KW + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_RSA_C +#define MBEDTLS_PKCS1_V21 + +#define MBEDTLS_CIPHER_MODE_CTR + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_NIST_KW_C + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_MPI_MAX_SIZE 256 + +#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +#endif /* MCUBOOT_MBEDTLS_CONFIG_RSA_KW */ diff --git a/bootloader/mcuboot/boot/zephyr/include/config-rsa.h b/bootloader/mcuboot/boot/zephyr/include/config-rsa.h new file mode 100644 index 0000000..4b7c75e --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/config-rsa.h @@ -0,0 +1,83 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_RSA +#define MCUBOOT_MBEDTLS_CONFIG_RSA + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_RSA_C +#define MBEDTLS_PKCS1_V21 + +#define MBEDTLS_CIPHER_MODE_CTR + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA256_SMALLER +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C + +/* Save RAM by adjusting to our exact needs */ +#if (CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN == 3072) +#define MBEDTLS_MPI_MAX_SIZE 384 +#else +#define MBEDTLS_MPI_MAX_SIZE 256 +#endif + +#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +#endif /* MCUBOOT_MBEDTLS_CONFIG_RSA */ diff --git a/bootloader/mcuboot/boot/zephyr/include/flash_map_backend/flash_map_backend.h b/bootloader/mcuboot/boot/zephyr/include/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000..e5408a1 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/flash_map_backend/flash_map_backend.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2015 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FLASH_MAP_BACKEND_H__ +#define __FLASH_MAP_BACKEND_H__ + +#include // the zephyr flash_map + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * Provides abstraction of flash regions for type of use. + * I.e. dude where's my image? + * + * System will contain a map which contains flash areas. Every + * region will contain flash identifier, offset within flash and length. + * + * 1. This system map could be in a file within filesystem (Initializer + * must know/figure out where the filesystem is at). + * 2. Map could be at fixed location for project (compiled to code) + * 3. Map could be at specific place in flash (put in place at mfg time). + * + * Note that the map you use must be valid for BSP it's for, + * match the linker scripts when platform executes from flash, + * and match the target offset specified in download script. + */ +#include +#include + +/* + * Retrieve a memory-mapped flash device's base address. + * + * On success, the address will be stored in the value pointed to by + * ret. + * + * Returns 0 on success, or an error code on failure. + */ +int flash_device_base(uint8_t fd_id, uintptr_t *ret); + +int flash_area_id_from_image_slot(int slot); +int flash_area_id_from_multi_image_slot(int image_index, int slot); + +/** + * Converts the specified flash area ID and image index (in multi-image setup) + * to an image slot index. + * + * Returns image slot index (0 or 1), or -1 if ID doesn't correspond to an image + * slot. + */ +int flash_area_id_to_multi_image_slot(int image_index, int area_id); + +/* Retrieve the flash sector a given offset belongs to. + * + * Returns 0 on success, or an error code on failure. + */ +int flash_area_sector_from_off(off_t off, struct flash_sector *sector); + +static inline uint32_t flash_area_get_off(const struct flash_area *fa) +{ + return (uint32_t)fa->fa_off; +} + +static inline uint32_t flash_area_get_size(const struct flash_area *fa) +{ + return (uint32_t)fa->fa_size; +} + +static inline uint8_t flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +uint8_t flash_area_get_device_id(const struct flash_area *fa); + +/* + * Returns the value expected to be read when accessing any erased + * flash byte. + */ +uint8_t flash_area_erased_val(const struct flash_area *fap); + +static inline uint32_t flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +static inline uint32_t flash_sector_get_size(const struct flash_sector *fs) +{ + return fs->fs_size; +} + +/* Retrieve the flash sector withing given flash area, at a given offset. + * + * @param fa flash area where the sector is taken from. + * @param off offset within flash area. + * @param sector structure of sector information. + * Returns 0 on success, -ERANGE if @p off is beyond flash area size, + * other negative errno code on failure. + */ +int flash_area_get_sector(const struct flash_area *fa, off_t off, + struct flash_sector *fs); + +#ifdef __cplusplus +} +#endif + +#endif /* __FLASH_MAP_BACKEND_H__ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/hal/hal_bsp.h b/bootloader/mcuboot/boot/zephyr/include/hal/hal_bsp.h new file mode 100644 index 0000000..8b52f27 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/hal/hal_bsp.h @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __HAL_BSP_H_ +#define __HAL_BSP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * Initializes BSP; registers flash_map with the system. + */ +void hal_bsp_init(void); + +/* + * Return pointer to flash device structure, given BSP specific + * flash id. + */ +struct hal_flash; +const struct hal_flash *hal_bsp_flash_dev(uint8_t flash_id); + +/* + * Grows heap by given amount. XXX giving space back not implemented. + */ +void *_sbrk(int incr); + +/* + * Report which memory areas should be included inside a coredump. + */ +struct hal_bsp_mem_dump { + void *hbmd_start; + uint32_t hbmd_size; +}; + +const struct hal_bsp_mem_dump *hal_bsp_core_dump(int *area_cnt); + +/* + * Get unique HW identifier/serial number for platform. + * Returns the number of bytes filled in. + */ +#define HAL_BSP_MAX_ID_LEN 32 +int hal_bsp_hw_id(uint8_t *id, int max_len); + +#define HAL_BSP_POWER_ON (1) +#define HAL_BSP_POWER_WFI (2) +#define HAL_BSP_POWER_SLEEP (3) +#define HAL_BSP_POWER_DEEP_SLEEP (4) +#define HAL_BSP_POWER_OFF (5) +#define HAL_BSP_POWER_PERUSER (128) + +int hal_bsp_power_state(int state); + +/* Returns priority of given interrupt number */ +uint32_t hal_bsp_get_nvic_priority(int irq_num, uint32_t pri); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/zephyr/include/hal/hal_flash.h b/bootloader/mcuboot/boot/zephyr/include/hal/hal_flash.h new file mode 100644 index 0000000..2895479 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/hal/hal_flash.h @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_HAL_FLASH_ +#define H_HAL_FLASH_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +int hal_flash_read(uint8_t flash_id, uint32_t address, void *dst, + uint32_t num_bytes); +int hal_flash_write(uint8_t flash_id, uint32_t address, const void *src, + uint32_t num_bytes); +int hal_flash_erase_sector(uint8_t flash_id, uint32_t sector_address); +int hal_flash_erase(uint8_t flash_id, uint32_t address, uint32_t num_bytes); +uint8_t hal_flash_align(uint8_t flash_id); +int hal_flash_init(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* H_HAL_FLASH_ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/io/io.h b/bootloader/mcuboot/boot/zephyr/include/io/io.h new file mode 100644 index 0000000..563281f --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/io/io.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012-2014 Wind River Systems, Inc. + * Copyright (c) 2020 Arm Limited + * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_IO_ +#define H_IO_ + +#include + +#ifdef CONFIG_SOC_FAMILY_NORDIC_NRF +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Initialises the configured LED. + */ +void io_led_init(void); + +/* + * Sets value of the configured LED. + */ +void io_led_set(int value); + +/* + * Checks if GPIO is set in the required way to remain in serial recovery mode + * + * @retval false for normal boot, true for serial recovery boot + */ +bool io_detect_pin(void); + +/* + * Checks if board was reset using reset pin and if device should stay in + * serial recovery mode + * + * @retval false for normal boot, true for serial recovery boot + */ +bool io_detect_pin_reset(void); + +/* + * Checks board boot mode via retention subsystem and if device should stay in + * serial recovery mode + * + * @retval false for normal boot, true for serial recovery boot + */ +bool io_detect_boot_mode(void); + +#ifdef CONFIG_SOC_FAMILY_NORDIC_NRF +static inline bool io_boot_skip_serial_recovery() +{ + uint32_t rr = nrfx_reset_reason_get(); + + return !(rr == 0 || (rr & NRFX_RESET_REASON_RESETPIN_MASK)); +} +#else +static inline bool io_boot_skip_serial_recovery() +{ + return false; +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/zephyr/include/mcuboot-mbedtls-cfg.h b/bootloader/mcuboot/boot/zephyr/include/mcuboot-mbedtls-cfg.h new file mode 100644 index 0000000..a46fbb0 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/mcuboot-mbedtls-cfg.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Open Source Foundries Limited + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _MCUBOOT_MBEDTLS_CONFIG_ +#define _MCUBOOT_MBEDTLS_CONFIG_ + +/** + * @file + * + * This is the top-level mbedTLS configuration file for MCUboot. The + * configuration depends on the signature type, so this file just + * pulls in the right header depending on that setting. + */ + +/* + * IMPORTANT: + * + * If you put any "generic" definitions in here, make sure to update + * the simulator build.rs accordingly. + */ + +#if defined(CONFIG_BOOT_SIGNATURE_TYPE_RSA) || defined(CONFIG_BOOT_ENCRYPT_RSA) +#include "config-rsa.h" +#elif defined(CONFIG_BOOT_USE_PSA_CRYPTO) || defined(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) || \ + defined(CONFIG_BOOT_ENCRYPT_EC256) || \ + (defined(CONFIG_BOOT_ENCRYPT_X25519) && !defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519)) +#include "config-asn1.h" +#elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519) +#include "config-ed25519.h" +#else +#error "Cannot configure mbedTLS; signature type is unknown." +#endif + +#endif diff --git a/bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000..7896e09 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2018 Open Source Foundries Limited + * Copyright (c) 2019-2020 Arm Limited + * Copyright (c) 2019-2020 Linaro Limited + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_CONFIG_H__ +#define __MCUBOOT_CONFIG_H__ + +#include + +#ifdef CONFIG_BOOT_SIGNATURE_TYPE_RSA +#define MCUBOOT_SIGN_RSA +# if (CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN != 2048 && \ + CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN != 3072) +# error "Invalid RSA key size (must be 2048 or 3072)" +# else +# define MCUBOOT_SIGN_RSA_LEN CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN +# endif +#elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) +#define MCUBOOT_SIGN_EC256 +#elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519) +#define MCUBOOT_SIGN_ED25519 +#endif + +#if defined(CONFIG_BOOT_USE_TINYCRYPT) +# if defined(CONFIG_MBEDTLS) || defined(CONFIG_BOOT_USE_CC310) +# error "One crypto library implementation allowed at a time." +# endif +#elif defined(CONFIG_MBEDTLS) && defined(CONFIG_BOOT_USE_CC310) +# error "One crypto library implementation allowed at a time." +#endif + +#ifdef CONFIG_BOOT_USE_MBEDTLS +#define MCUBOOT_USE_MBED_TLS +#elif defined(CONFIG_BOOT_USE_TINYCRYPT) +#define MCUBOOT_USE_TINYCRYPT +#elif defined(CONFIG_BOOT_USE_CC310) +#define MCUBOOT_USE_CC310 +#elif defined(CONFIG_MBEDTLS_PSA_CRYPTO_CLIENT) +#define MCUBOOT_USE_PSA_CRYPTO +#elif defined(CONFIG_BOOT_USE_NRF_EXTERNAL_CRYPTO) +#define MCUBOOT_USE_NRF_EXTERNAL_CRYPTO +#endif + +#ifdef CONFIG_BOOT_IMG_HASH_ALG_SHA512 +#define MCUBOOT_SHA512 +#endif + +#ifdef CONFIG_BOOT_IMG_HASH_ALG_SHA256 +#define MCUBOOT_SHA256 +#endif + +/* Zephyr, regardless of C library used, provides snprintf */ +#define MCUBOOT_USE_SNPRINTF 1 + +#ifdef CONFIG_BOOT_HW_KEY +#define MCUBOOT_HW_KEY +#endif + +#ifdef CONFIG_BOOT_VALIDATE_SLOT0 +#define MCUBOOT_VALIDATE_PRIMARY_SLOT +#endif + +#ifdef CONFIG_BOOT_VALIDATE_SLOT0_ONCE +#define MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE +#endif + +#ifdef CONFIG_BOOT_UPGRADE_ONLY +#define MCUBOOT_OVERWRITE_ONLY +#define MCUBOOT_OVERWRITE_ONLY_FAST +#endif + +#ifdef CONFIG_SINGLE_APPLICATION_SLOT +#define MCUBOOT_SINGLE_APPLICATION_SLOT 1 +#define MCUBOOT_IMAGE_NUMBER 1 +#else + +#ifdef CONFIG_BOOT_SWAP_USING_MOVE +#define MCUBOOT_SWAP_USING_MOVE 1 +#endif + +#ifdef CONFIG_BOOT_DIRECT_XIP +#define MCUBOOT_DIRECT_XIP +#endif + +#ifdef CONFIG_BOOT_DIRECT_XIP_REVERT +#define MCUBOOT_DIRECT_XIP_REVERT +#endif + +#ifdef CONFIG_BOOT_RAM_LOAD +#define MCUBOOT_RAM_LOAD 1 +#define IMAGE_EXECUTABLE_RAM_START CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_START +#define IMAGE_EXECUTABLE_RAM_SIZE CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_SIZE +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER +#define MCUBOOT_FIRMWARE_LOADER +#endif + +#ifdef CONFIG_UPDATEABLE_IMAGE_NUMBER +#define MCUBOOT_IMAGE_NUMBER CONFIG_UPDATEABLE_IMAGE_NUMBER +#else +#define MCUBOOT_IMAGE_NUMBER 1 +#endif + +#ifdef CONFIG_BOOT_VERSION_CMP_USE_BUILD_NUMBER +#define MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER +#endif + +#ifdef CONFIG_BOOT_SWAP_SAVE_ENCTLV +#define MCUBOOT_SWAP_SAVE_ENCTLV 1 +#endif + +#endif /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#ifdef CONFIG_LOG +#define MCUBOOT_HAVE_LOGGING 1 +#endif + +#ifdef CONFIG_BOOT_ENCRYPT_RSA +#define MCUBOOT_ENC_IMAGES +#define MCUBOOT_ENCRYPT_RSA +#endif + +#ifdef CONFIG_BOOT_ENCRYPT_EC256 +#define MCUBOOT_ENC_IMAGES +#define MCUBOOT_ENCRYPT_EC256 +#endif + +#ifdef CONFIG_BOOT_ENCRYPT_X25519 +#define MCUBOOT_ENC_IMAGES +#define MCUBOOT_ENCRYPT_X25519 +#endif + +#ifdef CONFIG_BOOT_DECOMPRESSION +#define MCUBOOT_DECOMPRESS_IMAGES +#endif + +/* Invoke hashing functions directly on storage. This requires for device + * to be able to map storage to address space or RAM. + */ +#ifdef CONFIG_BOOT_IMG_HASH_DIRECTLY_ON_STORAGE +#define MCUBOOT_HASH_STORAGE_DIRECTLY +#endif + +#ifdef CONFIG_BOOT_SIGNATURE_TYPE_PURE +#define MCUBOOT_SIGN_PURE +#endif + +#ifdef CONFIG_BOOT_BOOTSTRAP +#define MCUBOOT_BOOTSTRAP 1 +#endif + +#ifdef CONFIG_BOOT_USE_BENCH +#define MCUBOOT_USE_BENCH 1 +#endif + +#ifdef CONFIG_MCUBOOT_DOWNGRADE_PREVENTION +#define MCUBOOT_DOWNGRADE_PREVENTION 1 +/* MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER is used later as bool value so it is + * always defined, (unlike MCUBOOT_DOWNGRADE_PREVENTION which is only used in + * preprocessor condition and my be not defined) */ +# ifdef CONFIG_MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER +# define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 1 +# else +# define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 0 +# endif +#endif + +#ifdef CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION +#define MCUBOOT_HW_ROLLBACK_PROT +#endif + +#ifdef CONFIG_MEASURED_BOOT +#define MCUBOOT_MEASURED_BOOT +#endif + +#ifdef CONFIG_BOOT_SHARE_DATA +#define MCUBOOT_DATA_SHARING +#endif + +#ifdef CONFIG_BOOT_SHARE_BACKEND_RETENTION +#define MCUBOOT_CUSTOM_DATA_SHARING_FUNCTION +#endif + +#ifdef CONFIG_BOOT_SHARE_DATA_BOOTINFO +#define MCUBOOT_DATA_SHARING_BOOTINFO +#endif + +#ifdef CONFIG_MEASURED_BOOT_MAX_CBOR_SIZE +#define MAX_BOOT_RECORD_SZ CONFIG_MEASURED_BOOT_MAX_CBOR_SIZE +#endif + +#ifdef CONFIG_BOOT_FIH_PROFILE_OFF +#define MCUBOOT_FIH_PROFILE_OFF +#endif + +#ifdef CONFIG_BOOT_FIH_PROFILE_LOW +#define MCUBOOT_FIH_PROFILE_LOW +#endif + +#ifdef CONFIG_BOOT_FIH_PROFILE_MEDIUM +#define MCUBOOT_FIH_PROFILE_MEDIUM +#endif + +#ifdef CONFIG_BOOT_FIH_PROFILE_HIGH +#define MCUBOOT_FIH_PROFILE_HIGH +#endif + +#ifdef CONFIG_ENABLE_MGMT_PERUSER +#define MCUBOOT_PERUSER_MGMT_GROUP_ENABLED 1 +#else +#define MCUBOOT_PERUSER_MGMT_GROUP_ENABLED 0 +#endif + +#ifdef CONFIG_BOOT_MGMT_CUSTOM_IMG_LIST +#define MCUBOOT_MGMT_CUSTOM_IMG_LIST +#endif + +#ifdef CONFIG_BOOT_MGMT_ECHO +#define MCUBOOT_BOOT_MGMT_ECHO +#endif + +#ifdef CONFIG_BOOT_IMAGE_ACCESS_HOOKS +#define MCUBOOT_IMAGE_ACCESS_HOOKS +#endif + +#ifdef CONFIG_MCUBOOT_VERIFY_IMG_ADDRESS +#define MCUBOOT_VERIFY_IMG_ADDRESS +#endif + +#ifdef CONFIG_MCUBOOT_SERIAL +#define MCUBOOT_SERIAL +#endif + +/* + * The configuration option enables direct image upload with the + * serial recovery. + */ +#ifdef CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD +#define MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD +#endif + +#ifdef CONFIG_BOOT_SERIAL_WAIT_FOR_DFU +#define MCUBOOT_SERIAL_WAIT_FOR_DFU +#endif + +#ifdef CONFIG_BOOT_SERIAL_IMG_GRP_HASH +#define MCUBOOT_SERIAL_IMG_GRP_HASH +#endif + +#ifdef CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE +#define MCUBOOT_SERIAL_IMG_GRP_IMAGE_STATE +#endif + +#ifdef CONFIG_BOOT_SERIAL_IMG_GRP_SLOT_INFO +#define MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO +#endif + +#ifdef CONFIG_MCUBOOT_SERIAL +#define MCUBOOT_SERIAL_RECOVERY +#endif + +#if (defined(CONFIG_BOOT_USB_DFU_WAIT) || \ + defined(CONFIG_BOOT_USB_DFU_GPIO)) +#define MCUBOOT_USB_DFU +#endif + +/* + * The option enables code, currently in boot_serial, that attempts + * to erase flash progressively, as update fragments are received, + * instead of erasing whole image size of flash area after receiving + * first frame. + * Enabling this options prevents stalling the beginning of transfer + * for the time needed to erase large chunk of flash. + */ +#ifdef CONFIG_BOOT_ERASE_PROGRESSIVELY +#define MCUBOOT_ERASE_PROGRESSIVELY +#endif + +/* + * Enabling this option uses newer flash map APIs. This saves RAM and + * avoids deprecated API usage. + * + * (This can be deleted when flash_area_to_sectors() is removed instead + * of simply deprecated.) + */ +#define MCUBOOT_USE_FLASH_AREA_GET_SECTORS + +#if (defined(CONFIG_BOOT_USB_DFU_WAIT) || \ + defined(CONFIG_BOOT_USB_DFU_GPIO)) +# ifndef CONFIG_MULTITHREADING +# error "USB DFU Requires MULTITHREADING" +# endif +#endif + +#if defined(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO) && defined(MIN_SECTOR_COUNT) + +#define MCUBOOT_MAX_IMG_SECTORS MIN_SECTOR_COUNT + +#elif defined(CONFIG_BOOT_MAX_IMG_SECTORS) + +#define MCUBOOT_MAX_IMG_SECTORS CONFIG_BOOT_MAX_IMG_SECTORS + +#else +#define MCUBOOT_MAX_IMG_SECTORS 128 +#endif + +#ifdef CONFIG_BOOT_SERIAL_MAX_RECEIVE_SIZE +#define MCUBOOT_SERIAL_MAX_RECEIVE_SIZE CONFIG_BOOT_SERIAL_MAX_RECEIVE_SIZE +#endif + +#ifdef CONFIG_BOOT_SERIAL_UNALIGNED_BUFFER_SIZE +#define MCUBOOT_SERIAL_UNALIGNED_BUFFER_SIZE CONFIG_BOOT_SERIAL_UNALIGNED_BUFFER_SIZE +#endif + +#if defined(MCUBOOT_DATA_SHARING) && defined(ZEPHYR_VER_INCLUDE) +#include + +#define MCUBOOT_VERSION_AVAILABLE +#define MCUBOOT_VERSION_MAJOR APP_VERSION_MAJOR +#define MCUBOOT_VERSION_MINOR APP_VERSION_MINOR +#define MCUBOOT_VERSION_PATCHLEVEL APP_PATCHLEVEL +#endif + +/* Support 32-byte aligned flash sizes */ +#if DT_HAS_CHOSEN(zephyr_flash) + #if DT_PROP_OR(DT_CHOSEN(zephyr_flash), write_block_size, 0) > 8 + #define MCUBOOT_BOOT_MAX_ALIGN \ + DT_PROP(DT_CHOSEN(zephyr_flash), write_block_size) + #endif +#endif + +#ifdef CONFIG_MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP +#define MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP 1 +#endif + +#if CONFIG_BOOT_WATCHDOG_FEED +#if CONFIG_NRFX_WDT +#include + +#define FEED_WDT_INST(id) \ + do { \ + nrfx_wdt_t wdt_inst_##id = NRFX_WDT_INSTANCE(id); \ + for (uint8_t i = 0; i < NRF_WDT_CHANNEL_NUMBER; i++) \ + { \ + nrf_wdt_reload_request_set(wdt_inst_##id.p_reg, \ + (nrf_wdt_rr_register_t)(NRF_WDT_RR0 + i)); \ + } \ + } while (0) +#if defined(CONFIG_NRFX_WDT0) && defined(CONFIG_NRFX_WDT1) +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + FEED_WDT_INST(0); \ + FEED_WDT_INST(1); \ + } while (0) +#elif defined(CONFIG_NRFX_WDT0) +#define MCUBOOT_WATCHDOG_FEED() \ + FEED_WDT_INST(0); +#elif defined(CONFIG_NRFX_WDT30) && defined(CONFIG_NRFX_WDT31) +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + FEED_WDT_INST(30); \ + FEED_WDT_INST(31); \ + } while (0) +#elif defined(CONFIG_NRFX_WDT30) +#define MCUBOOT_WATCHDOG_FEED() \ + FEED_WDT_INST(30); +#elif defined(CONFIG_NRFX_WDT31) +#define MCUBOOT_WATCHDOG_FEED() \ + FEED_WDT_INST(31); +#else +#error "No NRFX WDT instances enabled" +#endif + +#elif DT_NODE_HAS_STATUS(DT_ALIAS(watchdog0), okay) /* CONFIG_NRFX_WDT */ +#include +#include + +#define MCUBOOT_WATCHDOG_SETUP() \ + do { \ + const struct device* wdt = \ + DEVICE_DT_GET(DT_ALIAS(watchdog0)); \ + if (device_is_ready(wdt)) { \ + wdt_setup(wdt, 0); \ + } \ + } while (0) + +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + const struct device* wdt = \ + DEVICE_DT_GET(DT_ALIAS(watchdog0)); \ + if (device_is_ready(wdt)) { \ + wdt_feed(wdt, 0); \ + } \ + } while (0) +#else /* DT_NODE_HAS_STATUS(DT_ALIAS(watchdog0), okay) */ +/* No vendor implementation, no-op for historical reasons */ +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + } while (0) +#endif +#else /* CONFIG_BOOT_WATCHDOG_FEED */ +/* Not enabled, no feed activity */ +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + } while (0) + +#endif /* CONFIG_BOOT_WATCHDOG_FEED */ + +#ifndef MCUBOOT_WATCHDOG_SETUP +#define MCUBOOT_WATCHDOG_SETUP() +#endif + +#define MCUBOOT_CPU_IDLE() \ + if (!IS_ENABLED(CONFIG_MULTITHREADING)) { \ + k_cpu_idle(); \ + } + +#endif /* __MCUBOOT_CONFIG_H__ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_logging.h b/bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000..83c9a1f --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Runtime Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_LOGGING_H__ +#define __MCUBOOT_LOGGING_H__ + +/* + * When building for targets running Zephyr, delegate to its native + * logging subsystem. + */ +#ifdef CONFIG_MCUBOOT +#define MCUBOOT_LOG_MODULE_DECLARE(domain) LOG_MODULE_DECLARE(domain, CONFIG_MCUBOOT_LOG_LEVEL) +#define MCUBOOT_LOG_MODULE_REGISTER(domain) LOG_MODULE_REGISTER(domain, CONFIG_MCUBOOT_LOG_LEVEL) +#else +#define MCUBOOT_LOG_MODULE_DECLARE(domain) LOG_MODULE_DECLARE(domain, CONFIG_MCUBOOT_UTIL_LOG_LEVEL) +#define MCUBOOT_LOG_MODULE_REGISTER(domain) LOG_MODULE_REGISTER(domain, CONFIG_MCUBOOT_UTIL_LOG_LEVEL) +#endif + +#define MCUBOOT_LOG_ERR(...) LOG_ERR(__VA_ARGS__) +#define MCUBOOT_LOG_WRN(...) LOG_WRN(__VA_ARGS__) +#define MCUBOOT_LOG_INF(...) LOG_INF(__VA_ARGS__) +#define MCUBOOT_LOG_DBG(...) LOG_DBG(__VA_ARGS__) +#define MCUBOOT_LOG_SIM(...) IGNORE(__VA_ARGS__) + +#include + +#endif /* __MCUBOOT_LOGGING_H__ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/nrf_cleanup.h b/bootloader/mcuboot/boot/zephyr/include/nrf_cleanup.h new file mode 100644 index 0000000..9e87e13 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/nrf_cleanup.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_NRF_CLEANUP_ +#define H_NRF_CLEANUP_ + +/** + * Perform cleanup on some peripheral resources used by MCUBoot prior chainload + * the application. + * + * This function disables all RTC instances and UARTE instances. + * It Disables their interrupts signals as well. + */ +void nrf_cleanup_peripheral(void); + +/** + * Perform cleanup of non-secure RAM that may have been used by MCUBoot. + */ +void nrf_cleanup_ns_ram(void); + +#endif diff --git a/bootloader/mcuboot/boot/zephyr/include/os/os.h b/bootloader/mcuboot/boot/zephyr/include/os/os.h new file mode 100644 index 0000000..e69de29 diff --git a/bootloader/mcuboot/boot/zephyr/include/os/os_heap.h b/bootloader/mcuboot/boot/zephyr/include/os/os_heap.h new file mode 100644 index 0000000..4413568 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/os/os_heap.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_OS_HEAP_ +#define H_OS_HEAP_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *os_malloc(size_t size); +void os_free(void *mem); +void *os_realloc(void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/bootloader/mcuboot/boot/zephyr/include/os/os_malloc.h b/bootloader/mcuboot/boot/zephyr/include/os/os_malloc.h new file mode 100644 index 0000000..32b72c2 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/os/os_malloc.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_OS_MALLOC_ +#define H_OS_MALLOC_ + +#include "os/os_heap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#undef malloc +#define malloc os_malloc + +#undef free +#define free os_free + +#undef realloc +#define realloc os_realloc + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootloader/mcuboot/boot/zephyr/include/platform-bench.h b/bootloader/mcuboot/boot/zephyr/include/platform-bench.h new file mode 100644 index 0000000..d0cb963 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/platform-bench.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 Linaro Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_ZEPHYR_BENCH_H__ +#define H_ZEPHYR_BENCH_H__ + +#include +#include +#include "zephyr.h" +#include "bootutil/bootutil_log.h" + +/* TODO: Unclear if this can be here (redundantly). */ +BOOT_LOG_MODULE_DECLARE(mcuboot); + +typedef uint32_t bench_state_t; + +#define plat_bench_start(_s) do { \ + BOOT_LOG_ERR("start benchmark"); \ + *(_s) = k_cycle_get_32(); \ +} while (0) + +#define plat_bench_stop(_s) do { \ + uint32_t _stop_time = k_cycle_get_32(); \ + BOOT_LOG_ERR("bench: %" PRId32 " cycles", _stop_time - *(_s)); \ +} while (0) + +#endif /* not H_ZEPHYR_BENCH_H__ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/serial_adapter/serial_adapter.h b/bootloader/mcuboot/boot/zephyr/include/serial_adapter/serial_adapter.h new file mode 100644 index 0000000..1f407a1 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/serial_adapter/serial_adapter.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_SERIAL_ADAPTER +#define H_SERIAL_ADAPTER + +int +console_out(int c); + +void +console_write(const char *str, int cnt); + +int +boot_console_init(void); + +int +console_read(char *str, int str_cnt, int *newline); + +#endif // SERIAL_ADAPTER diff --git a/bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash.h b/bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash.h new file mode 100644 index 0000000..42f2518 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef __PM_SYSFLASH_H__ +#define __PM_SYSFLASH_H__ +/* Blocking the __SYSFLASH_H__ */ +#define __SYSFLASH_H__ + +#include +#include +#include + +#ifndef CONFIG_SINGLE_APPLICATION_SLOT + +/* Each pair of slots is separated by , and there is no terminating character */ +#define FLASH_AREA_IMAGE_0_SLOTS PM_MCUBOOT_PRIMARY_ID, PM_MCUBOOT_SECONDARY_ID, +#define FLASH_AREA_IMAGE_1_SLOTS PM_MCUBOOT_PRIMARY_1_ID, PM_MCUBOOT_SECONDARY_1_ID, +#define FLASH_AREA_IMAGE_2_SLOTS PM_MCUBOOT_PRIMARY_2_ID, PM_MCUBOOT_SECONDARY_2_ID, +#define FLASH_AREA_IMAGE_3_SLOTS PM_MCUBOOT_PRIMARY_3_ID, PM_MCUBOOT_SECONDARY_3_ID, + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#ifdef CONFIG_NCS_IS_VARIANT_IMAGE +#define MCUBOOT_S0_S1_SLOTS PM_S0_ID, PM_MCUBOOT_SECONDARY_ID, +#else +#define MCUBOOT_S0_S1_SLOTS PM_S1_ID, PM_MCUBOOT_SECONDARY_ID, +#endif +#else +#define MCUBOOT_S0_S1_SLOTS +#endif + +#if (MCUBOOT_IMAGE_NUMBER == 1) || (MCUBOOT_IMAGE_NUMBER == 2 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 2) || (MCUBOOT_IMAGE_NUMBER == 3 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 3) || (MCUBOOT_IMAGE_NUMBER == 4 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ + FLASH_AREA_IMAGE_2_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 4) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ + FLASH_AREA_IMAGE_2_SLOTS \ + FLASH_AREA_IMAGE_3_SLOTS +#else +#error Unsupported number of images +#endif + +static inline uint32_t __flash_area_ids_for_slot(int img, int slot) +{ + static const int all_slots[] = { + ALL_AVAILABLE_SLOTS + MCUBOOT_S0_S1_SLOTS + }; + return all_slots[img * 2 + slot]; +}; + +#undef FLASH_AREA_IMAGE_0_SLOTS +#undef FLASH_AREA_IMAGE_1_SLOTS +#undef FLASH_AREA_IMAGE_2_SLOTS +#undef FLASH_AREA_IMAGE_3_SLOTS +#undef MCUBOOT_S0_S1_SLOTS +#undef ALL_AVAILABLE_SLOTS + +#define FLASH_AREA_IMAGE_PRIMARY(x) __flash_area_ids_for_slot(x, 0) +#define FLASH_AREA_IMAGE_SECONDARY(x) __flash_area_ids_for_slot(x, 1) + +#if !defined(CONFIG_BOOT_SWAP_USING_MOVE) +#define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID +#endif + +#else /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID +#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_PRIMARY_ID +/* NOTE: Scratch parition is not used by single image DFU but some of + * functions in common files reference it, so the definitions has been + * provided to allow compilation of common units. + */ +#define FLASH_AREA_IMAGE_SCRATCH 0 + +#endif /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#endif /* __PM_SYSFLASH_H__ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash_legacy_child_parent.h b/bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash_legacy_child_parent.h new file mode 100644 index 0000000..db60ddd --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/sysflash/pm_sysflash_legacy_child_parent.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef __PM_SYSFLASH_H__ +#define __PM_SYSFLASH_H__ +/* Blocking the __SYSFLASH_H__ */ +#define __SYSFLASH_H__ + +#include +#include +#include + +#ifndef CONFIG_SINGLE_APPLICATION_SLOT + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) +/* If B0 is present then two bootloaders are present, and we must use + * a single secondary slot for both primary slots. + */ +extern uint32_t _image_1_primary_slot_id[]; +#endif /* (MCUBOOT_IMAGE_NUMBER == 2 && defined(PM_B0_ADDRESS) */ + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + +#define FLASH_AREA_IMAGE_PRIMARY(x) \ + ((x == 0) ? \ + PM_MCUBOOT_PRIMARY_ID : \ + (x == 1) ? \ + (uint32_t)_image_1_primary_slot_id : \ + 255 ) + +#define FLASH_AREA_IMAGE_SECONDARY(x) \ + ((x == 0) ? \ + PM_MCUBOOT_SECONDARY_ID: \ + (x == 1) ? \ + PM_MCUBOOT_SECONDARY_ID: \ + 255 ) + +#else /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + */ + +/* Each pair of slots is separated by , and there is no terminating character */ +#define FLASH_AREA_IMAGE_0_SLOTS PM_MCUBOOT_PRIMARY_ID, PM_MCUBOOT_SECONDARY_ID +#define FLASH_AREA_IMAGE_1_SLOTS PM_MCUBOOT_PRIMARY_1_ID, PM_MCUBOOT_SECONDARY_1_ID +#define FLASH_AREA_IMAGE_2_SLOTS PM_MCUBOOT_PRIMARY_2_ID, PM_MCUBOOT_SECONDARY_2_ID + +#if (MCUBOOT_IMAGE_NUMBER == 1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 2) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 3) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS, \ + FLASH_AREA_IMAGE_2_SLOTS +#else +#error Unsupported number of images +#endif + +static inline uint32_t __flash_area_ids_for_slot(int img, int slot) +{ + static const int all_slots[] = { + ALL_AVAILABLE_SLOTS + }; + return all_slots[img * 2 + slot]; +}; + +#undef FLASH_AREA_IMAGE_0_SLOTS +#undef FLASH_AREA_IMAGE_1_SLOTS +#undef FLASH_AREA_IMAGE_2_SLOTS +#undef ALL_AVAILABLE_SLOTS + +#define FLASH_AREA_IMAGE_PRIMARY(x) __flash_area_ids_for_slot(x, 0) +#define FLASH_AREA_IMAGE_SECONDARY(x) __flash_area_ids_for_slot(x, 1) + +#if !defined(CONFIG_BOOT_SWAP_USING_MOVE) +#define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID +#endif + +#endif /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + */ + +#else /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID +#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_PRIMARY_ID +/* NOTE: Scratch parition is not used by single image DFU but some of + * functions in common files reference it, so the definitions has been + * provided to allow compilation of common units. + */ +#define FLASH_AREA_IMAGE_SCRATCH 0 + +#endif /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#endif /* __PM_SYSFLASH_H__ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/sysflash/sysflash.h b/bootloader/mcuboot/boot/zephyr/include/sysflash/sysflash.h new file mode 100644 index 0000000..bee101b --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/sysflash/sysflash.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023-2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#if USE_PARTITION_MANAGER +/* Blocking the rest of the file */ +#define __SYSFLASH_H__ +#if CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER != -1 +/* Sysbuild */ +#include +#else +/* Legacy child/parent */ +#include +#endif +#endif + +#ifndef __SYSFLASH_H__ +#define __SYSFLASH_H__ + +#include +#include +#include +#include + +#ifndef SOC_FLASH_0_ID +#define SOC_FLASH_0_ID 0 +#endif + +#ifndef SPI_FLASH_0_ID +#define SPI_FLASH_0_ID 1 +#endif + +#if !defined(CONFIG_SINGLE_APPLICATION_SLOT) && !defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP) + +/* Each pair of slots is separated by , and there is no terminating character */ +#define FLASH_AREA_IMAGE_0_SLOTS slot0_partition, slot1_partition +#define FLASH_AREA_IMAGE_1_SLOTS slot2_partition, slot3_partition +#define FLASH_AREA_IMAGE_2_SLOTS slot4_partition, slot5_partition + +#if (MCUBOOT_IMAGE_NUMBER == 1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 2) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 3) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS, \ + FLASH_AREA_IMAGE_2_SLOTS +#endif + +static inline uint32_t __flash_area_ids_for_slot(int img, int slot) +{ + static const int all_slots[] = { + FOR_EACH_NONEMPTY_TERM(FIXED_PARTITION_ID, (,), ALL_AVAILABLE_SLOTS) + }; + return all_slots[img * 2 + slot]; +}; + +#undef FLASH_AREA_IMAGE_0_SLOTS +#undef FLASH_AREA_IMAGE_1_SLOTS +#undef FLASH_AREA_IMAGE_2_SLOTS +#undef ALL_AVAILABLE_SLOTS + +#define FLASH_AREA_IMAGE_PRIMARY(x) __flash_area_ids_for_slot(x, 0) +#define FLASH_AREA_IMAGE_SECONDARY(x) __flash_area_ids_for_slot(x, 1) + +#if !defined(CONFIG_BOOT_SWAP_USING_MOVE) +#define FLASH_AREA_IMAGE_SCRATCH FIXED_PARTITION_ID(scratch_partition) +#endif + +#else /* !CONFIG_SINGLE_APPLICATION_SLOT && !CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP */ + +#define FLASH_AREA_IMAGE_PRIMARY(x) FIXED_PARTITION_ID(slot0_partition) +#define FLASH_AREA_IMAGE_SECONDARY(x) FIXED_PARTITION_ID(slot0_partition) + +#endif /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#endif /* __SYSFLASH_H__ */ diff --git a/bootloader/mcuboot/boot/zephyr/include/target.h b/bootloader/mcuboot/boot/zephyr/include/target.h new file mode 100644 index 0000000..40287d5 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/include/target.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017, Linaro Ltd + * Copyright (c) 2019, Arm Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_TARGETS_TARGET_ +#define H_TARGETS_TARGET_ + +#ifndef USE_PARTITION_MANAGER + +#if defined(MCUBOOT_TARGET_CONFIG) +/* + * Target-specific definitions are permitted in legacy cases that + * don't provide the information via DTS, etc. + */ +#include MCUBOOT_TARGET_CONFIG +#else +/* + * Otherwise, the Zephyr SoC header and the DTS provide most + * everything we need. + */ +#include +#include +#include + +#define FLASH_ALIGN FLASH_WRITE_BLOCK_SIZE + +#endif /* !defined(MCUBOOT_TARGET_CONFIG) */ + +/* + * Sanity check the target support. + */ +#if (!defined(CONFIG_XTENSA) && !DT_HAS_CHOSEN(zephyr_flash_controller)) || \ + (defined(CONFIG_XTENSA) && !DT_NODE_EXISTS(DT_INST(0, jedec_spi_nor)) && \ + !defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32)) || \ + !defined(FLASH_ALIGN) || \ + !(FIXED_PARTITION_EXISTS(slot0_partition)) || \ + !(FIXED_PARTITION_EXISTS(slot1_partition) || CONFIG_SINGLE_APPLICATION_SLOT) || \ + (defined(CONFIG_BOOT_SWAP_USING_SCRATCH) && !FIXED_PARTITION_EXISTS(scratch_partition)) +#error "Target support is incomplete; cannot build mcuboot." +#endif + +#if (MCUBOOT_IMAGE_NUMBER == 2) && (!(FIXED_PARTITION_EXISTS(slot2_partition)) || \ + !(FIXED_PARTITION_EXISTS(slot3_partition))) +#error "Target support is incomplete; cannot build mcuboot." +#endif + +#endif /* ifndef USE_PARTITION_MANAGER */ + +#endif /* H_TARGETS_TARGET_ */ diff --git a/bootloader/mcuboot/boot/zephyr/io.c b/bootloader/mcuboot/boot/zephyr/io.c new file mode 100644 index 0000000..309f1ab --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/io.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2012-2014 Wind River Systems, Inc. + * Copyright (c) 2020 Arm Limited + * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "target.h" +#include "bootutil/bootutil_log.h" + +#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +#include +#endif + +#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) +#include +#endif + +/* Validate serial recovery configuration */ +#ifdef CONFIG_MCUBOOT_SERIAL +#if !defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) && \ + !defined(CONFIG_BOOT_SERIAL_WAIT_FOR_DFU) && \ + !defined(CONFIG_BOOT_SERIAL_BOOT_MODE) && \ + !defined(CONFIG_BOOT_SERIAL_NO_APPLICATION) && \ + !defined(CONFIG_BOOT_SERIAL_PIN_RESET) +#error "Serial recovery selected without an entrance mode set" +#endif +#endif + +/* Validate firmware loader configuration */ +#ifdef CONFIG_BOOT_FIRMWARE_LOADER +#if !defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +#error "Firmware loader selected without an entrance mode set" +#endif +#endif + +#ifdef CONFIG_MCUBOOT_INDICATION_LED + +/* + * The led0 devicetree alias is optional. If present, we'll use it + * to turn on the LED whenever the button is pressed. + */ +#if DT_NODE_EXISTS(DT_ALIAS(mcuboot_led0)) +#define LED0_NODE DT_ALIAS(mcuboot_led0) +#elif DT_NODE_EXISTS(DT_ALIAS(bootloader_led0)) +#warning "bootloader-led0 alias is deprecated; use mcuboot-led0 instead" +#define LED0_NODE DT_ALIAS(bootloader_led0) +#endif + +#if DT_NODE_HAS_STATUS(LED0_NODE, okay) && DT_NODE_HAS_PROP(LED0_NODE, gpios) +static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios); +#else +/* A build error here means your board isn't set up to drive an LED. */ +#error "Unsupported board: led0 devicetree alias is not defined" +#endif + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +void io_led_init(void) +{ + if (!device_is_ready(led0.port)) { + BOOT_LOG_ERR("Didn't find LED device referred by the LED0_NODE\n"); + return; + } + + gpio_pin_configure_dt(&led0, GPIO_OUTPUT); + gpio_pin_set_dt(&led0, 0); +} + +void io_led_set(int value) +{ + gpio_pin_set_dt(&led0, value); +} +#endif /* CONFIG_MCUBOOT_INDICATION_LED */ + +#if defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) || defined(CONFIG_BOOT_USB_DFU_GPIO) || \ + defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO) + +#if defined(CONFIG_MCUBOOT_SERIAL) +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_SERIAL_DETECT_DELAY +#elif defined(CONFIG_BOOT_FIRMWARE_LOADER) +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_FIRMWARE_LOADER_DETECT_DELAY +#else +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_USB_DFU_DETECT_DELAY +#endif + +#define BUTTON_0_NODE DT_ALIAS(mcuboot_button0) + +#if DT_NODE_EXISTS(BUTTON_0_NODE) && DT_NODE_HAS_PROP(BUTTON_0_NODE, gpios) +static const struct gpio_dt_spec button0 = GPIO_DT_SPEC_GET(BUTTON_0_NODE, gpios); +#else +#error "Serial recovery/USB DFU button must be declared in device tree as 'mcuboot_button0'" +#endif + +bool io_detect_pin(void) +{ + int rc; + int pin_active; + + if (!device_is_ready(button0.port)) { + __ASSERT(false, "GPIO device is not ready.\n"); + return false; + } + + rc = gpio_pin_configure_dt(&button0, GPIO_INPUT); + __ASSERT(rc == 0, "Failed to initialize boot detect pin.\n"); + + rc = gpio_pin_get_dt(&button0); + pin_active = rc; + + __ASSERT(rc >= 0, "Failed to read boot detect pin.\n"); + + if (pin_active) { + if (BUTTON_0_DETECT_DELAY > 0) { +#ifdef CONFIG_MULTITHREADING + k_sleep(K_MSEC(50)); +#else + k_busy_wait(50000); +#endif + + /* Get the uptime for debounce purposes. */ + int64_t timestamp = k_uptime_get(); + + for(;;) { + rc = gpio_pin_get_dt(&button0); + pin_active = rc; + __ASSERT(rc >= 0, "Failed to read boot detect pin.\n"); + + /* Get delta from when this started */ + uint32_t delta = k_uptime_get() - timestamp; + + /* If not pressed OR if pressed > debounce period, stop. */ + if (delta >= BUTTON_0_DETECT_DELAY || !pin_active) { + break; + } + + /* Delay 1 ms */ +#ifdef CONFIG_MULTITHREADING + k_sleep(K_MSEC(1)); +#else + k_busy_wait(1000); +#endif + } + } + } + + return (bool)pin_active; +} +#endif + +#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +bool io_detect_pin_reset(void) +{ + uint32_t reset_cause; + int rc; + + rc = hwinfo_get_reset_cause(&reset_cause); + + if (rc == 0 && reset_cause == RESET_PIN) { + (void)hwinfo_clear_reset_cause(); + return true; + } + + return false; +} +#endif + +#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) +bool io_detect_boot_mode(void) +{ + int32_t boot_mode; + + boot_mode = bootmode_check(BOOT_MODE_TYPE_BOOTLOADER); + + if (boot_mode == 1) { + /* Boot mode to stay in bootloader, clear status and enter serial + * recovery mode + */ + bootmode_clear(); + + return true; + } + + return false; +} +#endif diff --git a/bootloader/mcuboot/boot/zephyr/kernel/banner.c b/bootloader/mcuboot/boot/zephyr/kernel/banner.c new file mode 100644 index 0000000..f917582 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/kernel/banner.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Intel Corporation + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_BOOT_DELAY) && (CONFIG_BOOT_DELAY > 0) +#define DELAY_STR STRINGIFY(CONFIG_BOOT_DELAY) +#define BANNER_POSTFIX " (delayed boot " DELAY_STR "ms)" +#else +#define BANNER_POSTFIX "" +#endif /* defined(CONFIG_BOOT_DELAY) && (CONFIG_BOOT_DELAY > 0) */ + +#ifndef BANNER_VERSION +#if defined(BUILD_VERSION) && !IS_EMPTY(BUILD_VERSION) +#define BANNER_VERSION STRINGIFY(BUILD_VERSION) +#else +#define BANNER_VERSION KERNEL_VERSION_STRING +#endif /* BUILD_VERSION */ +#endif /* !BANNER_VERSION */ + +#if defined(APP_BUILD_VERSION) +#define APPLICATION_BANNER_VERSION STRINGIFY(APP_BUILD_VERSION) +#elif defined(APP_VERSION_EXTENDED_STRING) +#define APPLICATION_BANNER_VERSION APP_VERSION_EXTENDED_STRING +#endif + +#if defined(APPLICATION_BANNER_VERSION) +void boot_banner(void) +{ +#if defined(CONFIG_BOOT_DELAY) && (CONFIG_BOOT_DELAY > 0) + printk("***** delaying boot " DELAY_STR "ms (per build configuration) *****\n"); + k_busy_wait(CONFIG_BOOT_DELAY * USEC_PER_MSEC); +#endif /* defined(CONFIG_BOOT_DELAY) && (CONFIG_BOOT_DELAY > 0) */ + + printk("*** Booting MCUboot " APPLICATION_BANNER_VERSION " ***\n"); + printk("*** " CONFIG_BOOT_BANNER_STRING " " BANNER_VERSION BANNER_POSTFIX " ***\n"); +} +#endif /* APP_BUILD_VERSION */ diff --git a/bootloader/mcuboot/boot/zephyr/keys.c b/bootloader/mcuboot/boot/zephyr/keys.c new file mode 100644 index 0000000..ab403dd --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/keys.c @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +/* + * Even though this is in principle a Zephyr-specific file, the + * simulator builds it and uses it as well. Because of that, we can't + * use Kconfig symbols for key types, and have to rely on the MCUBoot + * symbols (which Zephyr provides via this header, and the simulator + * provides via the compiler command line). + */ +#include + +#if !defined(MCUBOOT_HW_KEY) +#if defined(MCUBOOT_SIGN_RSA) || defined(MCUBOOT_SIGN_EC256) || defined(MCUBOOT_SIGN_ED25519) +#define HAVE_KEYS +#if defined(MCUBOOT_SIGN_RSA) +extern const unsigned char rsa_pub_key[]; +extern unsigned int rsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_EC256) +extern const unsigned char ecdsa_pub_key[]; +extern unsigned int ecdsa_pub_key_len; +#elif defined(MCUBOOT_SIGN_ED25519) +extern const unsigned char ed25519_pub_key[]; +extern unsigned int ed25519_pub_key_len; +#endif +#endif + +/* + * NOTE: *_pub_key and *_pub_key_len are autogenerated based on the provided + * key file. If no key file was configured, the array and length must be + * provided and added to the build manually. + */ +#if defined(HAVE_KEYS) +const struct bootutil_key bootutil_keys[] = { + { +#if defined(MCUBOOT_SIGN_RSA) + .key = rsa_pub_key, + .len = &rsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_EC256) + .key = ecdsa_pub_key, + .len = &ecdsa_pub_key_len, +#elif defined(MCUBOOT_SIGN_ED25519) + .key = ed25519_pub_key, + .len = &ed25519_pub_key_len, +#endif + }, +}; +const int bootutil_key_cnt = 1; +#endif /* HAVE_KEYS */ +#else +unsigned int pub_key_len; +struct bootutil_key bootutil_keys[1] = { + { + .key = 0, + .len = &pub_key_len, + } +}; +const int bootutil_key_cnt = 1; +#endif /* !MCUBOOT_HW_KEY */ + +#if defined(MCUBOOT_ENCRYPT_RSA) || defined(MCUBOOT_ENCRYPT_X25519) || defined(MCUBOOT_ENCRYPT_EC256) +extern const unsigned char enc_priv_key[]; +extern unsigned int enc_priv_key_len; +const struct bootutil_key bootutil_enc_key = { + .key = enc_priv_key, + .len = &enc_priv_key_len, +}; +#elif defined(MCUBOOT_ENCRYPT_KW) +#error "Encrypted images with AES-KW is not implemented yet." +#endif diff --git a/bootloader/mcuboot/boot/zephyr/main.c b/bootloader/mcuboot/boot/zephyr/main.c new file mode 100644 index 0000000..2339084 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/main.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2012-2014 Wind River Systems, Inc. + * Copyright (c) 2020 Arm Limited + * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_BOOT_DISABLE_CACHES) +#include +#endif + +#if defined(CONFIG_ARM) +#include +#endif + +#include "io/io.h" +#include "target.h" + +#include "bootutil/bootutil_log.h" +#include "bootutil/image.h" +#include "bootutil/bootutil.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/mcuboot_status.h" +#include "flash_map_backend/flash_map_backend.h" + +/* Check if Espressif target is supported */ +#ifdef CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 + +#include +#include + +#define IMAGE_INDEX_0 0 +#define IMAGE_INDEX_1 1 + +#define PRIMARY_SLOT 0 +#define SECONDARY_SLOT 1 + +#define IMAGE0_PRIMARY_START_ADDRESS \ + DT_PROP_BY_IDX(DT_NODE_BY_FIXED_PARTITION_LABEL(image_0), reg, 0) +#define IMAGE0_PRIMARY_SIZE \ + DT_PROP_BY_IDX(DT_NODE_BY_FIXED_PARTITION_LABEL(image_0), reg, 1) + +#define IMAGE1_PRIMARY_START_ADDRESS \ + DT_PROP_BY_IDX(DT_NODE_BY_FIXED_PARTITION_LABEL(image_1), reg, 0) +#define IMAGE1_PRIMARY_SIZE \ + DT_PROP_BY_IDX(DT_NODE_BY_FIXED_PARTITION_LABEL(image_1), reg, 1) + +#endif /* CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 */ + +#ifdef CONFIG_FW_INFO +#include +#endif + +#ifdef CONFIG_MCUBOOT_SERIAL +#include "boot_serial/boot_serial.h" +#include "serial_adapter/serial_adapter.h" + +const struct boot_uart_funcs boot_funcs = { + .read = console_read, + .write = console_write +}; +#endif + +#if defined(CONFIG_BOOT_USB_DFU_WAIT) || defined(CONFIG_BOOT_USB_DFU_GPIO) +#include +#endif + +#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE +#include +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) +#include +#endif + +/* CONFIG_LOG_MINIMAL is the legacy Kconfig property, + * replaced by CONFIG_LOG_MODE_MINIMAL. + */ +#if (defined(CONFIG_LOG_MODE_MINIMAL) || defined(CONFIG_LOG_MINIMAL)) +#define ZEPHYR_LOG_MODE_MINIMAL 1 +#endif + +/* CONFIG_LOG_IMMEDIATE is the legacy Kconfig property, + * replaced by CONFIG_LOG_MODE_IMMEDIATE. + */ +#if (defined(CONFIG_LOG_MODE_IMMEDIATE) || defined(CONFIG_LOG_IMMEDIATE)) +#define ZEPHYR_LOG_MODE_IMMEDIATE 1 +#endif + +#if defined(CONFIG_LOG) && !defined(ZEPHYR_LOG_MODE_IMMEDIATE) && \ + !defined(ZEPHYR_LOG_MODE_MINIMAL) +#ifdef CONFIG_LOG_PROCESS_THREAD +#warning "The log internal thread for log processing can't transfer the log"\ + "well for MCUBoot." +#else +#include + +#define BOOT_LOG_PROCESSING_INTERVAL K_MSEC(30) /* [ms] */ + +/* log are processing in custom routine */ +K_THREAD_STACK_DEFINE(boot_log_stack, CONFIG_MCUBOOT_LOG_THREAD_STACK_SIZE); +struct k_thread boot_log_thread; +volatile bool boot_log_stop = false; +K_SEM_DEFINE(boot_log_sem, 1, 1); + +/* log processing need to be initalized by the application */ +#define ZEPHYR_BOOT_LOG_START() zephyr_boot_log_start() +#define ZEPHYR_BOOT_LOG_STOP() zephyr_boot_log_stop() +#endif /* CONFIG_LOG_PROCESS_THREAD */ +#else +/* synchronous log mode doesn't need to be initalized by the application */ +#define ZEPHYR_BOOT_LOG_START() do { } while (false) +#define ZEPHYR_BOOT_LOG_STOP() do { } while (false) +#endif /* defined(CONFIG_LOG) && !defined(ZEPHYR_LOG_MODE_IMMEDIATE) && \ + * !defined(ZEPHYR_LOG_MODE_MINIMAL) + */ + +#if USE_PARTITION_MANAGER && CONFIG_FPROTECT +#include +#include +#endif + +#if CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL || CONFIG_MCUBOOT_NRF_CLEANUP_NONSECURE_RAM +#include +#endif + +static const struct gpio_dt_spec _left_button = GPIO_DT_SPEC_GET(DT_ALIAS(left_button), gpios); +static const struct gpio_dt_spec _right_button = GPIO_DT_SPEC_GET(DT_ALIAS(right_button), gpios); +static const struct gpio_dt_spec _pwr_5v_enable = GPIO_DT_SPEC_GET(DT_ALIAS(pwr_5v_enable), gpios); +static const struct gpio_dt_spec _mcuboot_led = GPIO_DT_SPEC_GET(DT_ALIAS(mcuboot_led), gpios); + +BOOT_LOG_MODULE_REGISTER(mcuboot); + +void os_heap_init(void); + +#if defined(CONFIG_ARM) + +#ifdef CONFIG_SW_VECTOR_RELAY +extern void *_vector_table_pointer; +#endif + +struct arm_vector_table { + uint32_t msp; + uint32_t reset; +}; + +static void do_boot(struct boot_rsp *rsp) +{ + struct arm_vector_table *vt; + + /* The beginning of the image is the ARM vector table, containing + * the initial stack pointer address and the reset vector + * consecutively. Manually set the stack pointer and jump into the + * reset vector + */ +#ifdef CONFIG_BOOT_RAM_LOAD + /* Get ram address for image */ + vt = (struct arm_vector_table *)(rsp->br_hdr->ih_load_addr + rsp->br_hdr->ih_hdr_size); +#else + uintptr_t flash_base; + int rc; + + /* Jump to flash image */ + rc = flash_device_base(rsp->br_flash_dev_id, &flash_base); + assert(rc == 0); + + vt = (struct arm_vector_table *)(flash_base + + rsp->br_image_off + + rsp->br_hdr->ih_hdr_size); +#endif + + if (IS_ENABLED(CONFIG_SYSTEM_TIMER_HAS_DISABLE_SUPPORT)) { + sys_clock_disable(); + } + +#ifdef CONFIG_USB_DEVICE_STACK + /* Disable the USB to prevent it from firing interrupts */ + usb_disable(); +#endif + +#if defined(CONFIG_FW_INFO) && !defined(CONFIG_EXT_API_PROVIDE_EXT_API_UNUSED) + uintptr_t fw_start_addr; + + rc = flash_device_base(rsp->br_flash_dev_id, &fw_start_addr); + assert(rc == 0); + + fw_start_addr += rsp->br_image_off + rsp->br_hdr->ih_hdr_size; + + const struct fw_info *firmware_info = fw_info_find(fw_start_addr); + bool provided = fw_info_ext_api_provide(firmware_info, true); + +#ifdef PM_S0_ADDRESS + /* Only fail if the immutable bootloader is present. */ + if (!provided) { + if (firmware_info == NULL) { + BOOT_LOG_WRN("Unable to find firmware info structure in %p", vt); + } + BOOT_LOG_ERR("Failed to provide EXT_APIs to %p", vt); + } +#endif +#endif +#if CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL + nrf_cleanup_peripheral(); +#endif +#if CONFIG_MCUBOOT_NRF_CLEANUP_NONSECURE_RAM && defined(PM_SRAM_NONSECURE_NAME) + nrf_cleanup_ns_ram(); +#endif +#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE + cleanup_arm_nvic(); /* cleanup NVIC registers */ + +#if defined(CONFIG_BOOT_DISABLE_CACHES) + /* Flush and disable instruction/data caches before chain-loading the application */ + (void)sys_cache_instr_flush_all(); + (void)sys_cache_data_flush_all(); + sys_cache_instr_disable(); + sys_cache_data_disable(); +#endif + +#if CONFIG_CPU_HAS_ARM_MPU || CONFIG_CPU_HAS_NXP_MPU + z_arm_clear_arm_mpu_config(); +#endif + +#if defined(CONFIG_BUILTIN_STACK_GUARD) && \ + defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM) + /* Reset limit registers to avoid inflicting stack overflow on image + * being booted. + */ + __set_PSPLIM(0); + __set_MSPLIM(0); +#endif + +#else + irq_lock(); +#endif /* CONFIG_MCUBOOT_CLEANUP_ARM_CORE */ + +#ifdef CONFIG_BOOT_INTR_VEC_RELOC +#if defined(CONFIG_SW_VECTOR_RELAY) + _vector_table_pointer = vt; +#ifdef CONFIG_CPU_CORTEX_M_HAS_VTOR + SCB->VTOR = (uint32_t)__vector_relay_table; +#endif +#elif defined(CONFIG_CPU_CORTEX_M_HAS_VTOR) + SCB->VTOR = (uint32_t)vt; +#endif /* CONFIG_SW_VECTOR_RELAY */ +#else /* CONFIG_BOOT_INTR_VEC_RELOC */ +#if defined(CONFIG_CPU_CORTEX_M_HAS_VTOR) && defined(CONFIG_SW_VECTOR_RELAY) + _vector_table_pointer = _vector_start; + SCB->VTOR = (uint32_t)__vector_relay_table; +#endif +#endif /* CONFIG_BOOT_INTR_VEC_RELOC */ + + __set_MSP(vt->msp); +#if CONFIG_MCUBOOT_CLEANUP_ARM_CORE + __set_CONTROL(0x00); /* application will configures core on its own */ + __ISB(); +#endif + ((void (*)(void))vt->reset)(); +} + +#elif defined(CONFIG_XTENSA) || defined(CONFIG_RISCV) + +#ifndef CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 + +#define SRAM_BASE_ADDRESS 0xBE030000 + +static void copy_img_to_SRAM(int slot, unsigned int hdr_offset) +{ + const struct flash_area *fap; + int area_id; + int rc; + unsigned char *dst = (unsigned char *)(SRAM_BASE_ADDRESS + hdr_offset); + + BOOT_LOG_INF("Copying image to SRAM"); + + area_id = flash_area_id_from_image_slot(slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + BOOT_LOG_ERR("flash_area_open failed with %d\n", rc); + goto done; + } + + rc = flash_area_read(fap, hdr_offset, dst, fap->fa_size - hdr_offset); + if (rc != 0) { + BOOT_LOG_ERR("flash_area_read failed with %d\n", rc); + goto done; + } + +done: + flash_area_close(fap); +} +#endif /* !CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 */ + +/* Entry point (.ResetVector) is at the very beginning of the image. + * Simply copy the image to a suitable location and jump there. + */ +static void do_boot(struct boot_rsp *rsp) +{ +#ifndef CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 + void *start; +#endif /* CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 */ + + BOOT_LOG_INF("br_image_off = 0x%x\n", rsp->br_image_off); + BOOT_LOG_INF("ih_hdr_size = 0x%x\n", rsp->br_hdr->ih_hdr_size); + +#ifdef CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 + int slot = (rsp->br_image_off == IMAGE0_PRIMARY_START_ADDRESS) ? + PRIMARY_SLOT : SECONDARY_SLOT; + /* Load memory segments and start from entry point */ + start_cpu0_image(IMAGE_INDEX_0, slot, rsp->br_hdr->ih_hdr_size); +#else + /* Copy from the flash to HP SRAM */ + copy_img_to_SRAM(0, rsp->br_hdr->ih_hdr_size); + + /* Jump to entry point */ + start = (void *)(SRAM_BASE_ADDRESS + rsp->br_hdr->ih_hdr_size); + ((void (*)(void))start)(); +#endif /* CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 */ +} + +#else +/* Default: Assume entry point is at the very beginning of the image. Simply + * lock interrupts and jump there. This is the right thing to do for X86 and + * possibly other platforms. + */ +static void do_boot(struct boot_rsp *rsp) +{ + void *start; + +#if defined(MCUBOOT_RAM_LOAD) + start = (void *)(rsp->br_hdr->ih_load_addr + rsp->br_hdr->ih_hdr_size); +#else + uintptr_t flash_base; + int rc; + + rc = flash_device_base(rsp->br_flash_dev_id, &flash_base); + assert(rc == 0); + + start = (void *)(flash_base + rsp->br_image_off + + rsp->br_hdr->ih_hdr_size); +#endif + + /* Lock interrupts and dive into the entry point */ + irq_lock(); + ((void (*)(void))start)(); +} +#endif + +#if defined(CONFIG_LOG) && !defined(ZEPHYR_LOG_MODE_IMMEDIATE) && \ + !defined(CONFIG_LOG_PROCESS_THREAD) && !defined(ZEPHYR_LOG_MODE_MINIMAL) +/* The log internal thread for log processing can't transfer log well as has too + * low priority. + * Dedicated thread for log processing below uses highest application + * priority. This allows to transmit all logs without adding k_sleep/k_yield + * anywhere else int the code. + */ + +/* most simple log processing theread */ +void boot_log_thread_func(void *dummy1, void *dummy2, void *dummy3) +{ + (void)dummy1; + (void)dummy2; + (void)dummy3; + + log_init(); + + while (1) { +#if defined(CONFIG_LOG1) || defined(CONFIG_LOG2) + /* support Zephyr legacy logging implementation before commit c5f2cde */ + if (log_process(false) == false) { +#else + if (log_process() == false) { +#endif + if (boot_log_stop) { + break; + } + k_sleep(BOOT_LOG_PROCESSING_INTERVAL); + } + } + + k_sem_give(&boot_log_sem); +} + +void zephyr_boot_log_start(void) +{ + /* start logging thread */ + k_thread_create(&boot_log_thread, boot_log_stack, + K_THREAD_STACK_SIZEOF(boot_log_stack), + boot_log_thread_func, NULL, NULL, NULL, + K_HIGHEST_APPLICATION_THREAD_PRIO, 0, + BOOT_LOG_PROCESSING_INTERVAL); + + k_thread_name_set(&boot_log_thread, "logging"); +} + +void zephyr_boot_log_stop(void) +{ + boot_log_stop = true; + + /* wait until log procesing thread expired + * This can be reworked using a thread_join() API once a such will be + * available in zephyr. + * see https://github.com/zephyrproject-rtos/zephyr/issues/21500 + */ + (void)k_sem_take(&boot_log_sem, K_FOREVER); +} +#endif /* defined(CONFIG_LOG) && !defined(ZEPHYR_LOG_MODE_IMMEDIATE) && \ + * !defined(CONFIG_LOG_PROCESS_THREAD) && !defined(ZEPHYR_LOG_MODE_MINIMAL) + */ + +#ifdef CONFIG_MCUBOOT_SERIAL +static void boot_serial_enter() +{ + int rc; + +#ifdef CONFIG_MCUBOOT_INDICATION_LED + io_led_set(1); +#endif + + mcuboot_status_change(MCUBOOT_STATUS_SERIAL_DFU_ENTERED); + + BOOT_LOG_INF("Enter the serial recovery mode"); + rc = boot_console_init(); + __ASSERT(rc == 0, "Error initializing boot console.\n"); + boot_serial_start(&boot_funcs); + __ASSERT(0, "Bootloader serial process was terminated unexpectedly.\n"); +} +#endif + +static void _delay_ms(uint32_t ms) +{ + uint32_t starting_uptime = k_uptime_get_32(); + uint32_t ending_uptime = starting_uptime + ms; + + while (starting_uptime < ending_uptime) + { + starting_uptime = k_uptime_get_32(); + } +} + +/* Function to check button sequence and perform swap if pressed */ +static void _check_and_swap(void) +{ + int ret; + int timer_ms; + + /*---------------------------------------------------------------------------------------------- + * Initialize GPIOs + *--------------------------------------------------------------------------------------------*/ + /* Check if right button GPIO is ready. */ + if (!gpio_is_ready_dt(&_right_button)) + { + BOOT_LOG_INF("Right button not ready"); + return; + } + + /* Check if left button GPIO is ready. */ + if (!gpio_is_ready_dt(&_left_button)) + { + BOOT_LOG_INF("Left button not ready"); + return; + } + + /* Check if power enable GPIO is ready. */ + if (!gpio_is_ready_dt(&_pwr_5v_enable)) + { + BOOT_LOG_INF("Left button not ready"); + return; + } + + /* Check if mcuboot LED (bubble) GPIO is ready. */ + if (!gpio_is_ready_dt(&_mcuboot_led)) + { + BOOT_LOG_INF("Left button not ready"); + return; + } + + /*---------------------------------------------------------------------------------------------- + * Configure GPIOs + *--------------------------------------------------------------------------------------------*/ + /* Configure right button. */ + ret = gpio_pin_configure_dt(&_right_button, GPIO_INPUT); + if (ret < 0) + { + BOOT_LOG_INF("Error configuring right button GPIO: %d", ret); + return; + } + + /* Configure left button. */ + ret = gpio_pin_configure_dt(&_left_button, GPIO_INPUT); + if (ret < 0) + { + BOOT_LOG_INF("Error configuring left button GPIO: %d", ret); + return; + } + + /* Configure power enable. */ + ret = gpio_pin_configure_dt(&_pwr_5v_enable, GPIO_OUTPUT); + if (ret < 0) + { + BOOT_LOG_INF("Error configuring power enable GPIO: %d", ret); + return; + } + + /* Configure mcuboot LED. */ + ret = gpio_pin_configure_dt(&_mcuboot_led, GPIO_OUTPUT); + if (ret < 0) + { + BOOT_LOG_INF("Error configuring mcuboot LED GPIO: %d", ret); + return; + } + + /* Small delay to ensure stable reading */ + _delay_ms(10); + + /*---------------------------------------------------------------------------------------------- + * Swap sequence + *--------------------------------------------------------------------------------------------*/ + /* 1. Initial condition, right button must be pressed and left not pressed. */ + if (!gpio_pin_get_dt(&_right_button) || gpio_pin_get_dt(&_left_button)) + { + return; + } + + /* After three seconds, check if right button is still pressed and left not pressed. */ + _delay_ms(1000); + if (!gpio_pin_get_dt(&_right_button) || gpio_pin_get_dt(&_left_button)) + { + return; + } + _delay_ms(1000); + if (!gpio_pin_get_dt(&_right_button) || gpio_pin_get_dt(&_left_button)) + { + return; + } + _delay_ms(1000); + if (!gpio_pin_get_dt(&_right_button) || gpio_pin_get_dt(&_left_button)) + { + return; + } + + + /* 2. Turn on mcuboot LED (bubble) for feedback of swap sequence starting. + NOTE: power chip needs to be enabled for LED to work. */ + gpio_pin_set_dt(&_pwr_5v_enable, true); + gpio_pin_set_dt(&_mcuboot_led, true); + + /* 3. Within the next 3 seconds, left button needs to be pressed while right is still held + to start swap sequence. Check every 100 mS */ + for (timer_ms = 0; timer_ms < 3000; timer_ms += 100) + { + /* If right button released, exit. */ + if (!gpio_pin_get_dt(&_right_button)) + { + /* Disable LED and power. */ + gpio_pin_set_dt(&_mcuboot_led, false); + gpio_pin_set_dt(&_pwr_5v_enable, false); + + return; + } + + if (gpio_pin_get_dt(&_left_button)) + { + BOOT_LOG_INF("Image swap button sequence detected - requesting permanent image swap"); + + /* Blink LED to indicate swap starting. */ + gpio_pin_set_dt(&_mcuboot_led, false); + _delay_ms(400); + gpio_pin_set_dt(&_mcuboot_led, true); + _delay_ms(400); + gpio_pin_set_dt(&_mcuboot_led, false); + _delay_ms(400); + gpio_pin_set_dt(&_mcuboot_led, true); + _delay_ms(400); + gpio_pin_set_dt(&_mcuboot_led, false); + + /* true = permanent swap, false = one time swap. */ + int rc = boot_set_pending(true); + if (rc == 0) + { + BOOT_LOG_INF("permanent image swap requested successfully"); + } else + { + BOOT_LOG_INF("Failed to request swap: %d", rc); + } + + return; + } + + _delay_ms(100); + } + + /* Disable LED and power. */ + gpio_pin_set_dt(&_mcuboot_led, false); + gpio_pin_set_dt(&_pwr_5v_enable, false); +} + +int main(void) +{ + struct boot_rsp rsp; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + MCUBOOT_WATCHDOG_SETUP(); + MCUBOOT_WATCHDOG_FEED(); + +#if !defined(MCUBOOT_DIRECT_XIP) + BOOT_LOG_INF("Starting (MAPS modified) bootloader"); +#else + BOOT_LOG_INF("Starting Direct-XIP bootloader"); +#endif + +#ifdef CONFIG_MCUBOOT_INDICATION_LED + /* LED init */ + io_led_init(); +#endif + + os_heap_init(); + + ZEPHYR_BOOT_LOG_START(); + + (void)rc; + + mcuboot_status_change(MCUBOOT_STATUS_STARTUP); + + _check_and_swap(); + +#ifdef CONFIG_BOOT_SERIAL_ENTRANCE_GPIO + if (io_detect_pin() && + !io_boot_skip_serial_recovery()) { + boot_serial_enter(); + } +#endif + +#ifdef CONFIG_BOOT_SERIAL_PIN_RESET + if (io_detect_pin_reset()) { + boot_serial_enter(); + } +#endif + +#if defined(CONFIG_BOOT_USB_DFU_GPIO) + if (io_detect_pin()) { +#ifdef CONFIG_MCUBOOT_INDICATION_LED + io_led_set(1); +#endif + + mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_ENTERED); + + rc = usb_enable(NULL); + if (rc) { + BOOT_LOG_ERR("Cannot enable USB"); + } else { + BOOT_LOG_INF("Waiting for USB DFU"); + wait_for_usb_dfu(K_FOREVER); + BOOT_LOG_INF("USB DFU wait time elapsed"); + } + } +#elif defined(CONFIG_BOOT_USB_DFU_WAIT) + rc = usb_enable(NULL); + if (rc) { + BOOT_LOG_ERR("Cannot enable USB"); + } else { + BOOT_LOG_INF("Waiting for USB DFU"); + + mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_WAITING); + + wait_for_usb_dfu(K_MSEC(CONFIG_BOOT_USB_DFU_WAIT_DELAY_MS)); + BOOT_LOG_INF("USB DFU wait time elapsed"); + + mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_TIMED_OUT); + } +#endif + +#ifdef CONFIG_BOOT_SERIAL_WAIT_FOR_DFU + /* Initialize the boot console, so we can already fill up our buffers while + * waiting for the boot image check to finish. This image check, can take + * some time, so it's better to reuse thistime to already receive the + * initial mcumgr command(s) into our buffers + */ + rc = boot_console_init(); + int timeout_in_ms = CONFIG_BOOT_SERIAL_WAIT_FOR_DFU_TIMEOUT; + uint32_t start = k_uptime_get_32(); + +#ifdef CONFIG_MCUBOOT_INDICATION_LED + io_led_set(1); +#endif +#endif + + FIH_CALL(boot_go, fih_rc, &rsp); + +#ifdef CONFIG_BOOT_SERIAL_BOOT_MODE + if (io_detect_boot_mode()) { + /* Boot mode to stay in bootloader, clear status and enter serial + * recovery mode + */ + boot_serial_enter(); + } +#endif + +#ifdef CONFIG_BOOT_SERIAL_WAIT_FOR_DFU + timeout_in_ms -= (k_uptime_get_32() - start); + if( timeout_in_ms <= 0 ) { + /* at least one check if time was expired */ + timeout_in_ms = 1; + } + boot_serial_check_start(&boot_funcs,timeout_in_ms); + +#ifdef CONFIG_MCUBOOT_INDICATION_LED + io_led_set(0); +#endif +#endif + + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_LOG_ERR("Unable to find bootable image"); + + mcuboot_status_change(MCUBOOT_STATUS_NO_BOOTABLE_IMAGE_FOUND); + +#ifdef CONFIG_BOOT_SERIAL_NO_APPLICATION + /* No bootable image and configuration set to remain in serial + * recovery mode + */ + boot_serial_enter(); +#elif defined(CONFIG_BOOT_USB_DFU_NO_APPLICATION) + rc = usb_enable(NULL); + if (rc && rc != -EALREADY) { + BOOT_LOG_ERR("Cannot enable USB"); + } else { + BOOT_LOG_INF("Waiting for USB DFU"); + wait_for_usb_dfu(K_FOREVER); + } +#endif + + FIH_PANIC; + } + +#ifdef CONFIG_BOOT_RAM_LOAD + BOOT_LOG_INF("Bootloader chainload address offset: 0x%x", + rsp.br_hdr->ih_load_addr); +#else + BOOT_LOG_INF("Bootloader chainload address offset: 0x%x", + rsp.br_image_off); +#endif + +#if defined(MCUBOOT_DIRECT_XIP) + BOOT_LOG_INF("Jumping to the image slot"); +#else + BOOT_LOG_INF("Jumping to the first image slot"); +#endif + + mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND); + +#if USE_PARTITION_MANAGER && CONFIG_FPROTECT + +#ifdef PM_S1_ADDRESS +/* MCUBoot is stored in either S0 or S1, protect both */ +#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_S0_ADDRESS) +#define PROTECT_ADDR PM_S0_ADDRESS +#else +/* There is only one instance of MCUBoot */ +#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS) +#define PROTECT_ADDR PM_MCUBOOT_ADDRESS +#endif + + rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE); + + if (rc != 0) { + BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup."); + while (1) + ; + } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) && defined(CONFIG_PCD_APP) +#if defined(PM_TFM_SECURE_ADDRESS) + pcd_lock_ram(false); +#else + pcd_lock_ram(true); +#endif +#endif +#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */ + + ZEPHYR_BOOT_LOG_STOP(); + + do_boot(&rsp); + + mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED); + + BOOT_LOG_ERR("Never should get here"); + while (1) + ; +} diff --git a/bootloader/mcuboot/boot/zephyr/nrf52840dk_nrf52840_cc310_ecdsa.conf b/bootloader/mcuboot/boot/zephyr/nrf52840dk_nrf52840_cc310_ecdsa.conf new file mode 100644 index 0000000..d195965 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/nrf52840dk_nrf52840_cc310_ecdsa.conf @@ -0,0 +1,2 @@ +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y +CONFIG_BOOT_ECDSA_CC310=y diff --git a/bootloader/mcuboot/boot/zephyr/nrf_cleanup.c b/bootloader/mcuboot/boot/zephyr/nrf_cleanup.c new file mode 100644 index 0000000..051705e --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/nrf_cleanup.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#if defined(NRF_RTC0) || defined(NRF_RTC1) || defined(NRF_RTC2) + #include +#endif +#if defined(NRF_PPI) + #include +#endif +#if defined(NRF_DPPIC) + #include +#endif + +#include + +#if USE_PARTITION_MANAGER +#include +#endif + +#if defined(NRF_UARTE0) || defined(NRF_UARTE1) || defined(NRF_UARTE20) || \ + defined(NRF_UARTE30) +#define NRF_UARTE_CLEANUP +#endif + +#define NRF_UARTE_SUBSCRIBE_CONF_OFFS offsetof(NRF_UARTE_Type, SUBSCRIBE_STARTRX) +#define NRF_UARTE_SUBSCRIBE_CONF_SIZE (offsetof(NRF_UARTE_Type, EVENTS_CTS) -\ + NRF_UARTE_SUBSCRIBE_CONF_OFFS) + +#define NRF_UARTE_PUBLISH_CONF_OFFS offsetof(NRF_UARTE_Type, PUBLISH_CTS) +#define NRF_UARTE_PUBLISH_CONF_SIZE (offsetof(NRF_UARTE_Type, SHORTS) -\ + NRF_UARTE_PUBLISH_CONF_OFFS) + +#if defined(NRF_RTC0) || defined(NRF_RTC1) || defined(NRF_RTC2) +static inline void nrf_cleanup_rtc(NRF_RTC_Type * rtc_reg) +{ + nrf_rtc_task_trigger(rtc_reg, NRF_RTC_TASK_STOP); + nrf_rtc_event_disable(rtc_reg, 0xFFFFFFFF); + nrf_rtc_int_disable(rtc_reg, 0xFFFFFFFF); +} +#endif + +#if defined(NRF_UARTE_CLEANUP) +static NRF_UARTE_Type *nrf_uarte_to_clean[] = { +#if defined(NRF_UARTE0) + NRF_UARTE0, +#endif +#if defined(NRF_UARTE1) + NRF_UARTE1, +#endif +#if defined(NRF_UARTE20) + NRF_UARTE20, +#endif +#if defined(NRF_UARTE30) + NRF_UARTE30, +#endif +}; +#endif + +static void nrf_cleanup_clock(void) +{ + nrf_clock_int_disable(NRF_CLOCK, 0xFFFFFFFF); +} + +void nrf_cleanup_peripheral(void) +{ +#if defined(NRF_RTC0) + nrf_cleanup_rtc(NRF_RTC0); +#endif +#if defined(NRF_RTC1) + nrf_cleanup_rtc(NRF_RTC1); +#endif +#if defined(NRF_RTC2) + nrf_cleanup_rtc(NRF_RTC2); +#endif + +#if defined(NRF_UARTE_CLEANUP) + for (int i = 0; i < sizeof(nrf_uarte_to_clean) / sizeof(nrf_uarte_to_clean[0]); ++i) { + NRF_UARTE_Type *current = nrf_uarte_to_clean[i]; + + nrfy_uarte_int_disable(current, 0xFFFFFFFF); + nrfy_uarte_int_uninit(current); + nrfy_uarte_task_trigger(current, NRF_UARTE_TASK_STOPRX); + + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_RXSTARTED); + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_ENDRX); + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_RXTO); + nrfy_uarte_disable(current); + +#if defined(NRF_DPPIC) + /* Clear all SUBSCRIBE configurations. */ + memset((uint8_t *)current + NRF_UARTE_SUBSCRIBE_CONF_OFFS, 0, + NRF_UARTE_SUBSCRIBE_CONF_SIZE); + /* Clear all PUBLISH configurations. */ + memset((uint8_t *)current + NRF_UARTE_PUBLISH_CONF_OFFS, 0, + NRF_UARTE_PUBLISH_CONF_SIZE); +#endif + } +#endif + +#if defined(NRF_PPI) + nrf_ppi_channels_disable_all(NRF_PPI); +#endif +#if defined(NRF_DPPIC) + nrf_dppi_channels_disable_all(NRF_DPPIC); +#endif + nrf_cleanup_clock(); +} + +#if USE_PARTITION_MANAGER \ + && defined(CONFIG_ARM_TRUSTZONE_M) \ + && defined(PM_SRAM_NONSECURE_NAME) +void nrf_cleanup_ns_ram(void) +{ + memset((void *) PM_SRAM_NONSECURE_ADDRESS, 0, PM_SRAM_NONSECURE_SIZE); +} +#endif diff --git a/bootloader/mcuboot/boot/zephyr/os.c b/bootloader/mcuboot/boot/zephyr/os.c new file mode 100644 index 0000000..048cb2b --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/os.c @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "os/os_heap.h" + +#ifdef CONFIG_BOOT_USE_MBEDTLS + +#include +#include + +/* + * This is the heap for mbed TLS. The value needed depends on the key + * size and algorithm used. + * + * - RSA-2048 signing without encryption is known to work well with 6144 bytes; + * - When using RSA-2048-OAEP encryption + RSA-2048 signing, or RSA-3072 + * signing (no encryption) 10240 bytes seems to be enough. + * + * NOTE: RSA-3072 signing + RSA-2048-OAEP might require growing the size... + */ +#if (CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN == 2048) && !defined(CONFIG_BOOT_ENCRYPT_RSA) +#define CRYPTO_HEAP_SIZE 6144 +#else +# if !defined(MBEDTLS_RSA_NO_CRT) +# define CRYPTO_HEAP_SIZE 12032 +# else +# define CRYPTO_HEAP_SIZE 16384 +# endif +#endif + +static unsigned char mempool[CRYPTO_HEAP_SIZE]; + +/* + * Initialize mbedtls to be able to use the local heap. + */ +void os_heap_init(void) +{ + mbedtls_memory_buffer_alloc_init(mempool, sizeof(mempool)); +} +#else +void os_heap_init(void) +{ +} +#endif diff --git a/bootloader/mcuboot/boot/zephyr/pm.yml b/bootloader/mcuboot/boot/zephyr/pm.yml new file mode 100644 index 0000000..13ffc44 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/pm.yml @@ -0,0 +1,90 @@ +#include + +mcuboot: + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT + placement: + before: [mcuboot_primary] +#if defined(CONFIG_HIDE_CHILD_PARENT_CONFIG) + align: {end: 0x1000} +#endif + +mcuboot_primary_app: + # All images to be placed in MCUboot's slot 0 should be placed in this + # partition + span: [app] + +mcuboot_primary: + span: [mcuboot_pad, mcuboot_primary_app] + +# Partition for secondary slot is not created if building in single application +# slot configuration. +#if !defined(CONFIG_SINGLE_APPLICATION_SLOT) && !defined(CONFIG_BOOT_DIRECT_XIP) +mcuboot_secondary: + share_size: [mcuboot_primary] +#if defined(CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY) + region: external_flash + placement: + align: {start: 4} +#else + placement: + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + align_next: CONFIG_FPROTECT_BLOCK_SIZE # Ensure that the next partition does not interfere with this image + after: mcuboot_primary +#endif /* CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY */ + +#endif /* !defined(CONFIG_SINGLE_APPLICATION_SLOT) && !defined(CONFIG_BOOT_DIRECT_XIP) */ + +#if CONFIG_BOOT_DIRECT_XIP + +# Direct XIP is enabled, reserve area for metadata (padding) and name the +# partition so that its clear that it is not the secondary slot, but the direct +# XIP alternative. + +mcuboot_secondary_pad: + share_size: mcuboot_pad + placement: + after: mcuboot_primary + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + +mcuboot_secondary_app: + share_size: mcuboot_primary_app + placement: + after: mcuboot_secondary_pad + +mcuboot_secondary: + span: [mcuboot_secondary_pad, mcuboot_secondary_app] + +#endif /* CONFIG_BOOT_DIRECT_XIP */ + +#if CONFIG_BOOT_SWAP_USING_SCRATCH +mcuboot_scratch: + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT_SCRATCH + placement: + after: app + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} +#endif /* CONFIG_BOOT_SWAP_USING_SCRATCH */ + +# Padding placed before image to boot. This reserves space for the MCUboot image header +# and it ensures that the boot image gets linked with the correct address offset in flash. +mcuboot_pad: + # MCUboot pad must be placed before the primary application partition. + # The primary application partition includes the secure firmware if present. + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT_PAD + placement: + before: [mcuboot_primary_app] +#ifdef CONFIG_FPROTECT + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} +#endif + +#if (CONFIG_NRF53_MCUBOOT_PRIMARY_1_RAM_FLASH) +mcuboot_primary_1: + region: ram_flash + size: CONFIG_NRF53_RAM_FLASH_SIZE +#endif /* CONFIG_NRF53_MULTI_IMAGE_UPDATE */ + +#if (CONFIG_NRF53_MULTI_IMAGE_UPDATE) +mcuboot_secondary_1: + region: external_flash + size: CONFIG_NRF53_RAM_FLASH_SIZE + +#endif /* CONFIG_NRF53_MULTI_IMAGE_UPDATE */ diff --git a/bootloader/mcuboot/boot/zephyr/prj.conf b/bootloader/mcuboot/boot/zephyr/prj.conf new file mode 100644 index 0000000..d7c33fc --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/prj.conf @@ -0,0 +1,45 @@ +CONFIG_PM=n + +CONFIG_MAIN_STACK_SIZE=10240 + +# Enable GPIO for button detection +CONFIG_GPIO=y +CONFIG_BOOT_SWAP_USING_MOVE=y + +CONFIG_BOOT_SWAP_SAVE_ENCTLV=n +CONFIG_BOOT_ENCRYPT_IMAGE=n + +CONFIG_BOOT_UPGRADE_ONLY=n +CONFIG_BOOT_BOOTSTRAP=n + +### mbedTLS has its own heap +# CONFIG_HEAP_MEM_POOL_SIZE is not set + +### We never want Zephyr's copy of tinycrypt. If tinycrypt is needed, +### MCUboot has its own copy in tree. +# CONFIG_TINYCRYPT is not set +# CONFIG_TINYCRYPT_ECC_DSA is not set +# CONFIG_TINYCRYPT_SHA256 is not set + +CONFIG_FLASH=y +CONFIG_FPROTECT=y + +### Various Zephyr boards enable features that we don't want. +# CONFIG_BT is not set +# CONFIG_BT_CTLR is not set +# CONFIG_I2C is not set + +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y # former CONFIG_MODE_MINIMAL +### Ensure Zephyr logging changes don't use more resources +CONFIG_LOG_DEFAULT_LEVEL=0 +### Use info log level by default +CONFIG_MCUBOOT_LOG_LEVEL_INF=y +### Decrease footprint by ~4 KB in comparison to CBPRINTF_COMPLETE=y +CONFIG_CBPRINTF_NANO=y +### Use the minimal C library to reduce flash usage +CONFIG_MINIMAL_LIBC=y +CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=0 + +# NCS boot banner +CONFIG_NCS_APPLICATION_BOOT_BANNER_STRING="MCUboot" diff --git a/bootloader/mcuboot/boot/zephyr/prj_minimal.conf b/bootloader/mcuboot/boot/zephyr/prj_minimal.conf new file mode 100644 index 0000000..55d4c61 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/prj_minimal.conf @@ -0,0 +1,40 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_MAIN_STACK_SIZE=10240 +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_FLASH=y +CONFIG_FPROTECT=y +CONFIG_PM=n + +CONFIG_BOOT_SWAP_SAVE_ENCTLV=n +CONFIG_BOOT_ENCRYPT_IMAGE=n + +CONFIG_BOOT_BOOTSTRAP=n +CONFIG_BOOT_UPGRADE_ONLY=n + +### Minimal Configurations ### +CONFIG_BOOT_USE_MIN_PARTITION_SIZE=y +CONFIG_ASSERT=n +CONFIG_BOOT_BANNER=n +CONFIG_CLOCK_CONTROL=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_GPIO=n +CONFIG_KERNEL_MEM_POOL=n +CONFIG_LOG=n +CONFIG_MINIMAL_LIBC_CALLOC=n +CONFIG_MINIMAL_LIBC_MALLOC=n +CONFIG_MINIMAL_LIBC_REALLOCARRAY=n +CONFIG_NCS_SAMPLES_DEFAULTS=n +CONFIG_NO_RUNTIME_CHECKS=y +CONFIG_NRF_RTC_TIMER=n +CONFIG_PRINTK=n +CONFIG_SERIAL=n +CONFIG_SIZE_OPTIMIZATIONS=y +CONFIG_SYS_CLOCK_EXISTS=n +CONFIG_UART_CONSOLE=n diff --git a/bootloader/mcuboot/boot/zephyr/ram_load.conf b/bootloader/mcuboot/boot/zephyr/ram_load.conf new file mode 100644 index 0000000..4cca450 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/ram_load.conf @@ -0,0 +1,6 @@ +# Enables RAM load mode and specifies 16 MB of memory from 0x80000000 +# as a valid location for the firmware to be loaded into. + +CONFIG_BOOT_RAM_LOAD=y +CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_START=0x80000000 +CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_SIZE=16777216 \ No newline at end of file diff --git a/bootloader/mcuboot/boot/zephyr/sample.yaml b/bootloader/mcuboot/boot/zephyr/sample.yaml new file mode 100644 index 0000000..9ab3307 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/sample.yaml @@ -0,0 +1,59 @@ +sample: + description: mcuboot test build sample + name: mcuboot + +tests: + sample.bootloader.mcuboot: + tags: bootloader_mcuboot + platform_allow: nrf52840dk/nrf52840 frdm_k64f disco_l475_iot1 + integration_platforms: + - nrf52840dk/nrf52840 + - frdm_k64f + - disco_l475_iot1 + sample.bootloader.mcuboot.serial_recovery: + extra_args: OVERLAY_CONFIG=serial_recovery.conf + platform_allow: nrf52840dk/nrf52840 + integration_platforms: + - nrf52840dk/nrf52840 + tags: bootloader_mcuboot + sample.bootloader.mcuboot.usb_cdc_acm_recovery: + tags: bootloader_mcuboot + platform_allow: nrf52840dongle/nrf52840 + extra_args: DTC_OVERLAY_FILE="./usb_cdc_acm.overlay;app.overlay" + integration_platforms: + - nrf52840dongle/nrf52840 + sample.bootloader.mcuboot.usb_cdc_acm_recovery_log: + extra_args: OVERLAY_CONFIG=./usb_cdc_acm_log_recovery.conf + DTC_OVERLAY_FILE="./boards/nrf52840_big.overlay;app.overlay" + platform_allow: nrf52840dk/nrf52840 + integration_platforms: + - nrf52840dk/nrf52840 + tags: bootloader_mcuboot + sample.bootloader.mcuboot.single_slot: + extra_args: OVERLAY_CONFIG=./single_slot.conf + DTC_OVERLAY_FILE="./boards/nrf52840_single_slot.overlay;app.overlay" + platform_allow: nrf52840dk/nrf52840 + integration_platforms: + - nrf52840dk/nrf52840 + tags: bootloader_mcuboot + sample.bootloader.mcuboot.qspi_nor_slot: + extra_args: DTC_OVERLAY_FILE="./boards/nrf52840dk_qspi_nor_secondary.overlay;app.overlay" + OVERLAY_CONFIG="./boards/nrf52840dk_qspi_nor.conf;./boards/nrf52840dk_qspi_secondary_boot.conf" + platform_allow: nrf52840dk/nrf52840 + integration_platforms: + - nrf52840dk/nrf52840 + tags: bootloader_mcuboot + sample.bootloader.mcuboot.hooks_multi: + extra_args: DTC_OVERLAY_FILE="./boards/nrf52840dk_ram_multi.overlay;app.overlay" + OVERLAY_CONFIG=./boards/nrf52840dk_hooks_sample_overlay.conf + TEST_BOOT_IMAGE_ACCESS_HOOKS=Y + platform_allow: nrf52840dk/nrf52840 + integration_platforms: + - nrf52840dk/nrf52840 + tags: bootloader_mcuboot + sample.bootloader.mcuboot.ram_load: + extra_args: OVERLAY_CONFIG=./ram_load.conf + tags: bootloader_mcuboot + platform_allow: mimxrt1020_evk + integration_platforms: + - mimxrt1020_evk diff --git a/bootloader/mcuboot/boot/zephyr/serial_adapter.c b/bootloader/mcuboot/boot/zephyr/serial_adapter.c new file mode 100644 index 0000000..de2e5c5 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/serial_adapter.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2017-2023 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil_log.h" +#include + +#if defined(CONFIG_BOOT_SERIAL_UART) && defined(CONFIG_UART_CONSOLE) && \ + (!DT_HAS_CHOSEN(zephyr_uart_mcumgr) || \ + DT_SAME_NODE(DT_CHOSEN(zephyr_uart_mcumgr), DT_CHOSEN(zephyr_console))) +#error Zephyr UART console must be disabled if serial_adapter module is used. +#endif + +#if defined(CONFIG_BOOT_SERIAL_CDC_ACM) && \ + defined(CONFIG_UART_CONSOLE) && (!DT_HAS_CHOSEN(zephyr_uart_mcumgr) || \ + DT_SAME_NODE(DT_CHOSEN(zephyr_uart_mcumgr), DT_CHOSEN(zephyr_console))) +#error Zephyr UART console must be disabled if CDC ACM is enabled and MCUmgr \ + has not been redirected to other UART with DTS chosen zephyr,uart-mcumgr. +#endif + +#if defined(CONFIG_BOOT_SERIAL_CDC_ACM) && CONFIG_MAIN_THREAD_PRIORITY < 0 +#error CONFIG_MAIN_THREAD_PRIORITY must be preemptible to support USB CDC ACM \ + (0 or above) +#endif + +BOOT_LOG_MODULE_REGISTER(serial_adapter); + +/** @brief Console input representation + * + * This struct is used to represent an input line from a serial interface. + */ +struct line_input { + /** Required to use sys_slist */ + sys_snode_t node; + + int len; + /** Buffer where the input line is recorded */ + char line[CONFIG_BOOT_MAX_LINE_INPUT_LEN]; +}; + +static struct device const *uart_dev; +static struct line_input line_bufs[CONFIG_BOOT_LINE_BUFS]; + +static sys_slist_t avail_queue; +static sys_slist_t lines_queue; + +static uint16_t cur; + +static int boot_uart_fifo_getline(char **line); +static int boot_uart_fifo_init(void); + +int +console_out(int c) +{ + uart_poll_out(uart_dev, c); + + return c; +} + +void +console_write(const char *str, int cnt) +{ + int i; + + for (i = 0; i < cnt; i++) { + if (console_out((int)str[i]) == EOF) { + break; + } + } +} + +int +console_read(char *str, int str_size, int *newline) +{ + char *line; + int len; + + len = boot_uart_fifo_getline(&line); + + if (line == NULL) { + *newline = 0; + return 0; + } + + if (len > str_size - 1) { + len = str_size - 1; + } + + memcpy(str, line, len); + str[len] = '\0'; + *newline = 1; + return len + 1; +} + +int +boot_console_init(void) +{ + int i; + + /* Zephyr UART handler takes an empty buffer from avail_queue, + * stores UART input in it until EOL, and then puts it into + * lines_queue. + */ + sys_slist_init(&avail_queue); + sys_slist_init(&lines_queue); + + for (i = 0; i < ARRAY_SIZE(line_bufs); i++) { + sys_slist_append(&avail_queue, &line_bufs[i].node); + } + + return boot_uart_fifo_init(); +} + +static void +boot_uart_fifo_callback(const struct device *dev, void *user_data) +{ + static struct line_input *cmd; + uint8_t byte; + int rx; + + uart_irq_update(uart_dev); + + if (!uart_irq_rx_ready(uart_dev)) { + return; + } + + while (true) { + rx = uart_fifo_read(uart_dev, &byte, 1); + if (rx != 1) { + break; + } + + if (!cmd) { + sys_snode_t *node; + + node = sys_slist_get(&avail_queue); + if (!node) { + BOOT_LOG_ERR("Not enough memory to store" + " incoming data!"); + return; + } + cmd = CONTAINER_OF(node, struct line_input, node); + } + + if (cur < CONFIG_BOOT_MAX_LINE_INPUT_LEN) { + cmd->line[cur++] = byte; + } + + if (byte == '\n') { + cmd->len = cur; + sys_slist_append(&lines_queue, &cmd->node); + cur = 0; + cmd = NULL; + } + } +} + +static int +boot_uart_fifo_getline(char **line) +{ + static struct line_input *cmd; + sys_snode_t *node; + int key; + + key = irq_lock(); + /* Recycle cmd buffer returned previous time */ + if (cmd != NULL) { + if (sys_slist_peek_tail(&avail_queue) != &cmd->node) { + sys_slist_append(&avail_queue, &cmd->node); + } + } + + node = sys_slist_get(&lines_queue); + irq_unlock(key); + + if (node == NULL) { + cmd = NULL; + *line = NULL; + + return 0; + } + + cmd = CONTAINER_OF(node, struct line_input, node); + *line = cmd->line; + return cmd->len; +} + +static int +boot_uart_fifo_init(void) +{ +#if defined(CONFIG_BOOT_SERIAL_UART) + +#if DT_HAS_CHOSEN(zephyr_uart_mcumgr) + uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_uart_mcumgr)); +#else + uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); +#endif + +#elif defined(CONFIG_BOOT_SERIAL_CDC_ACM) + uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); +#else +#error No serial recovery device selected +#endif + + + if (!device_is_ready(uart_dev)) { + return (-1); + } + +#if CONFIG_BOOT_SERIAL_CDC_ACM + int rc = usb_enable(NULL); + if (rc) { + return (-1); + } +#endif + + uart_irq_callback_set(uart_dev, boot_uart_fifo_callback); + + /* Drain the fifo */ + if (uart_irq_rx_ready(uart_dev)) { + uint8_t c; + + while (uart_fifo_read(uart_dev, &c, 1)) { + ; + } + } + + cur = 0; + + uart_irq_rx_enable(uart_dev); + + return 0; +} diff --git a/bootloader/mcuboot/boot/zephyr/serial_recovery.conf b/bootloader/mcuboot/boot/zephyr/serial_recovery.conf new file mode 100644 index 0000000..ddd08fc --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/serial_recovery.conf @@ -0,0 +1,3 @@ +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_UART=y +CONFIG_UART_CONSOLE=n diff --git a/bootloader/mcuboot/boot/zephyr/shared_data.c b/bootloader/mcuboot/boot/zephyr/shared_data.c new file mode 100644 index 0000000..5554a7e --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/shared_data.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include <../../bootutil/src/bootutil_priv.h> + +#define SHARED_MEMORY_MIN_SIZE 8 + +LOG_MODULE_REGISTER(bootloader_info, CONFIG_RETENTION_LOG_LEVEL); + +static bool shared_memory_init_done = false; +static uint16_t shared_data_size = SHARED_DATA_HEADER_SIZE; +static ssize_t shared_data_max_size = 0; +static const struct device *bootloader_info_dev = + DEVICE_DT_GET(DT_CHOSEN(zephyr_bootloader_info)); + +BUILD_ASSERT(SHARED_MEMORY_MIN_SIZE < \ + DT_REG_SIZE_BY_IDX(DT_CHOSEN(zephyr_bootloader_info), 0), \ + "zephyr,bootloader-info area is too small for bootloader information struct"); + +int boot_add_data_to_shared_area(uint8_t major_type, + uint16_t minor_type, + size_t size, + const uint8_t *data) +{ + struct shared_data_tlv_header header = { + .tlv_magic = SHARED_DATA_TLV_INFO_MAGIC, + .tlv_tot_len = shared_data_size, + }; + struct shared_data_tlv_entry tlv_entry = {0}; + uint16_t boot_data_size; + uintptr_t tlv_end, offset; + int rc; + + if (data == NULL) { + return SHARED_MEMORY_GEN_ERROR; + } + + /* Check whether first time to call this function. If does then initialise + * shared data area. + */ + if (!shared_memory_init_done) { + retention_clear(bootloader_info_dev); + shared_data_max_size = retention_size(bootloader_info_dev); + shared_memory_init_done = true; + } + + /* Check whether TLV entry is already added. + * Get the boundaries of TLV section + */ + tlv_end = shared_data_size; + offset = SHARED_DATA_HEADER_SIZE; + + /* Iterates over the TLV section looks for the same entry if found then + * returns with error: SHARED_MEMORY_OVERWRITE + */ + while (offset < tlv_end) { + /* Create local copy to avoid unaligned access */ + rc = retention_read(bootloader_info_dev, offset, (void *)&tlv_entry, + SHARED_DATA_ENTRY_HEADER_SIZE); + + if (rc) { + return SHARED_MEMORY_READ_ERROR; + } + + if (GET_MAJOR(tlv_entry.tlv_type) == major_type && + GET_MINOR(tlv_entry.tlv_type) == minor_type) { + return SHARED_MEMORY_OVERWRITE; + } + + offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len); + } + + /* Add TLV entry */ + tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type); + tlv_entry.tlv_len = size; + + if (!boot_u16_safe_add(&boot_data_size, shared_data_size, + SHARED_DATA_ENTRY_SIZE(size))) { + return SHARED_MEMORY_GEN_ERROR; + } + + /* Verify overflow of shared area */ + if (boot_data_size > shared_data_max_size) { + return SHARED_MEMORY_OVERFLOW; + } + + offset = shared_data_size; + rc = retention_write(bootloader_info_dev, offset, (void*)&tlv_entry, + SHARED_DATA_ENTRY_HEADER_SIZE); + if (rc) { + LOG_ERR("Shared data TLV header write failed: %d", rc); + return SHARED_MEMORY_WRITE_ERROR; + } + + offset += SHARED_DATA_ENTRY_HEADER_SIZE; + rc = retention_write(bootloader_info_dev, offset, data, size); + + if (rc) { + LOG_ERR("Shared data TLV data write failed: %d", rc); + return SHARED_MEMORY_WRITE_ERROR; + } + + shared_data_size += SHARED_DATA_ENTRY_SIZE(size); + header.tlv_tot_len = shared_data_size; + + rc = retention_write(bootloader_info_dev, 0, (void *)&header, + sizeof(header)); + + if (rc) { + return SHARED_MEMORY_WRITE_ERROR; + } + + return SHARED_MEMORY_OK; +} diff --git a/bootloader/mcuboot/boot/zephyr/single_loader.c b/bootloader/mcuboot/boot/zephyr/single_loader.c new file mode 100644 index 0000000..75374d2 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/single_loader.c @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2020 Nordic Semiconductor ASA + * Copyright (c) 2020 Arm Limited + */ + +#include +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/fault_injection_hardening.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +/* Variables passed outside of unit via poiters. */ +static const struct flash_area *_fa_p; +static struct image_header _hdr = { 0 }; + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) +/** + * Validate hash of a primary boot image. + * + * @param[in] fa_p flash area pointer + * @param[in] hdr boot image header pointer + * + * @return FIH_SUCCESS on success, error code otherwise + */ +fih_ret +boot_image_validate(const struct flash_area *fa_p, + struct image_header *hdr) +{ + static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + /* NOTE: The first argument to boot_image_validate, for enc_state pointer, + * is allowed to be NULL only because the single image loader compiles + * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses + * the pointer from compilation. + */ + /* Validate hash */ + if (IS_ENCRYPTED(hdr)) + { + /* Clear the encrypted flag we didn't supply a key + * This flag could be set if there was a decryption in place + * was performed. We will try to validate the image, and if still + * encrypted the validation will fail, and go in panic mode + */ + hdr->ih_flags &= ~(ENCRYPTIONFLAGS); + } + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, hdr, fa_p, tmpbuf, + BOOT_TMPBUF_SZ, NULL, 0, NULL); + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/ + +#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) +inline static fih_ret +boot_image_validate_once(const struct flash_area *fa_p, + struct image_header *hdr) +{ + static struct boot_swap_state state; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + memset(&state, 0, sizeof(struct boot_swap_state)); + rc = boot_read_swap_state(fa_p, &state); + if (rc != 0) + FIH_RET(FIH_FAILURE); + if (state.magic != BOOT_MAGIC_GOOD + || state.image_ok != BOOT_FLAG_SET) { + /* At least validate the image once */ + FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(FIH_FAILURE); + } + if (state.magic != BOOT_MAGIC_GOOD) { + rc = boot_write_magic(fa_p); + if (rc != 0) + FIH_RET(FIH_FAILURE); + } + rc = boot_write_image_ok(fa_p); + if (rc != 0) + FIH_RET(FIH_FAILURE); + } + FIH_RET(FIH_SUCCESS); +} +#endif + +/** + * Gather information on image and prepare for booting. + * + * @parami[out] rsp Parameters for booting image, on success + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + int rc = -1; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p); + assert(rc == 0); + + rc = boot_image_load_header(_fa_p, &_hdr); + if (rc != 0) + goto out; + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT + FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto out; + } +#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) + FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto out; + } +#else + fih_rc = FIH_SUCCESS; +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ + + rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p); + rsp->br_image_off = flash_area_get_off(_fa_p); + rsp->br_hdr = &_hdr; + +out: + flash_area_close(_fa_p); + + FIH_RET(fih_rc); +} diff --git a/bootloader/mcuboot/boot/zephyr/single_slot.conf b/bootloader/mcuboot/boot/zephyr/single_slot.conf new file mode 100644 index 0000000..34cc92a --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/single_slot.conf @@ -0,0 +1 @@ +CONFIG_SINGLE_APPLICATION_SLOT=y diff --git a/bootloader/mcuboot/boot/zephyr/socs/esp32_procpu.conf b/bootloader/mcuboot/boot/zephyr/socs/esp32_procpu.conf new file mode 100644 index 0000000..b2b4059 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/socs/esp32_procpu.conf @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_SIGNATURE_TYPE_NONE=y +CONFIG_BOOT_BANNER=n + +CONFIG_UART_CONSOLE=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n + +CONFIG_MCUBOOT_LOG_LEVEL_OFF=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_DEBUG=n + +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_MINIMAL_LIBC=y diff --git a/bootloader/mcuboot/boot/zephyr/socs/esp32c2.conf b/bootloader/mcuboot/boot/zephyr/socs/esp32c2.conf new file mode 100644 index 0000000..5b49f16 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/socs/esp32c2.conf @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_SIGNATURE_TYPE_NONE=y +CONFIG_BOOT_BANNER=n + +CONFIG_UART_CONSOLE=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n + +CONFIG_MCUBOOT_LOG_LEVEL_OFF=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_DEBUG=n + +CONFIG_XIP=n +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_MINIMAL_LIBC=y diff --git a/bootloader/mcuboot/boot/zephyr/socs/esp32c3.conf b/bootloader/mcuboot/boot/zephyr/socs/esp32c3.conf new file mode 100644 index 0000000..56298cd --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/socs/esp32c3.conf @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_SIGNATURE_TYPE_NONE=y +CONFIG_BOOT_BANNER=n + +CONFIG_UART_CONSOLE=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n + +CONFIG_MCUBOOT_LOG_LEVEL_OFF=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_DEBUG=n + +CONFIG_XIP=n +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_MINIMAL_LIBC=y diff --git a/bootloader/mcuboot/boot/zephyr/socs/esp32c6.conf b/bootloader/mcuboot/boot/zephyr/socs/esp32c6.conf new file mode 100644 index 0000000..5b49f16 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/socs/esp32c6.conf @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_SIGNATURE_TYPE_NONE=y +CONFIG_BOOT_BANNER=n + +CONFIG_UART_CONSOLE=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n + +CONFIG_MCUBOOT_LOG_LEVEL_OFF=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_DEBUG=n + +CONFIG_XIP=n +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_MINIMAL_LIBC=y diff --git a/bootloader/mcuboot/boot/zephyr/socs/esp32s2.conf b/bootloader/mcuboot/boot/zephyr/socs/esp32s2.conf new file mode 100644 index 0000000..b2b4059 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/socs/esp32s2.conf @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_SIGNATURE_TYPE_NONE=y +CONFIG_BOOT_BANNER=n + +CONFIG_UART_CONSOLE=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n + +CONFIG_MCUBOOT_LOG_LEVEL_OFF=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_DEBUG=n + +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_MINIMAL_LIBC=y diff --git a/bootloader/mcuboot/boot/zephyr/socs/esp32s3_procpu.conf b/bootloader/mcuboot/boot/zephyr/socs/esp32s3_procpu.conf new file mode 100644 index 0000000..b2b4059 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/socs/esp32s3_procpu.conf @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_BOOT_UPGRADE_ONLY=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_SIGNATURE_TYPE_NONE=y +CONFIG_BOOT_BANNER=n + +CONFIG_UART_CONSOLE=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n + +CONFIG_MCUBOOT_LOG_LEVEL_OFF=y +CONFIG_LOG_DEFAULT_LEVEL=0 +CONFIG_DEBUG=n + +CONFIG_HEAP_MEM_POOL_SIZE=4096 +CONFIG_MINIMAL_LIBC=y diff --git a/bootloader/mcuboot/boot/zephyr/sysbuild/CMakeLists.txt b/bootloader/mcuboot/boot/zephyr/sysbuild/CMakeLists.txt new file mode 100644 index 0000000..5a2a881 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/sysbuild/CMakeLists.txt @@ -0,0 +1,43 @@ +function(mathup num align result) + math(EXPR out "(((${num}) + ((${align}) - 1)) & ~((${align}) - 1))") + set(${result} "${out}" PARENT_SCOPE) +endfunction() + +function(${SYSBUILD_CURRENT_MODULE_NAME}_pre_image_cmake) + cmake_parse_arguments(PRE_IMAGE_CMAKE "" "IMAGE" "IMAGES" ${ARGN}) + + if(NOT "${PRE_IMAGE_CMAKE_IMAGE}" STREQUAL "mcuboot" OR NOT SB_CONFIG_BOOTLOADER_MCUBOOT) + return() + endif() + + set_property( + DIRECTORY APPEND PROPERTY + CMAKE_CONFIGURE_DEPENDS + ${CMAKE_BINARY_DIR}/mcuboot/CMakeCache.txt + ${CMAKE_BINARY_DIR}/mcuboot/zephyr/.config + ) +endfunction(${SYSBUILD_CURRENT_MODULE_NAME}_pre_image_cmake) + +function(${SYSBUILD_CURRENT_MODULE_NAME}_post_image_cmake) + cmake_parse_arguments(POST_IMAGE_CMAKE "" "IMAGE" "IMAGES" ${ARGN}) + + if(NOT "${POST_IMAGE_CMAKE_IMAGE}" STREQUAL "mcuboot" OR NOT SB_CONFIG_BOOTLOADER_MCUBOOT) + return() + endif() + + foreach(image ${IMAGES}) + set(app_type) + get_property(app_type TARGET ${image} PROPERTY APP_TYPE) + + if("${app_type}" STREQUAL "MAIN") + sysbuild_get(mcuboot_image_footer_size IMAGE mcuboot CACHE) + sysbuild_get(mcuboot_image_upgrade_footer_size IMAGE mcuboot CACHE) + math(EXPR mcuboot_image_footer_size "${mcuboot_image_footer_size}" OUTPUT_FORMAT HEXADECIMAL) + math(EXPR mcuboot_image_upgrade_footer_size "${mcuboot_image_upgrade_footer_size}" OUTPUT_FORMAT HEXADECIMAL) + + set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "CONFIG_ROM_END_OFFSET=${mcuboot_image_footer_size}\n") + set_property(TARGET ${image} APPEND_STRING PROPERTY CONFIG "CONFIG_MCUBOOT_UPDATE_FOOTER_SIZE=${mcuboot_image_upgrade_footer_size}\n") + return() + endif() + endforeach() +endfunction(${SYSBUILD_CURRENT_MODULE_NAME}_post_image_cmake) diff --git a/bootloader/mcuboot/boot/zephyr/targets/arduino_101.h b/bootloader/mcuboot/boot/zephyr/targets/arduino_101.h new file mode 100644 index 0000000..19095b3 --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/targets/arduino_101.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @brief Bootloader device specific configuration. + */ + +/* + * NOTE: This flash layout is a simple one for demonstration purposes. + * It assumes we are building MCUboot as a 3rd-stage loader, leaving the + * stock bootloaders on the Arduino 101 untouched. + * + * In this configuration MCUboot will live at 0x40010000 + */ + +#define FLASH_DEV_NAME CONFIG_SOC_FLASH_QMSI_DEV_NAME +#define FLASH_ALIGN 4 +#define FLASH_AREA_IMAGE_0_OFFSET 0x40020000 +#define FLASH_AREA_IMAGE_0_SIZE 0x10000 +#define FLASH_AREA_IMAGE_1_OFFSET 0x40030000 +#define FLASH_AREA_IMAGE_1_SIZE 0x10000 +#define FLASH_AREA_IMAGE_SCRATCH_OFFSET 0x40040000 +#define FLASH_AREA_IMAGE_SCRATCH_SIZE 0x10000 diff --git a/bootloader/mcuboot/boot/zephyr/usb_cdc_acm.overlay b/bootloader/mcuboot/boot/zephyr/usb_cdc_acm.overlay new file mode 100644 index 0000000..800d63c --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/usb_cdc_acm.overlay @@ -0,0 +1,5 @@ +&zephyr_udc0 { + cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; diff --git a/bootloader/mcuboot/boot/zephyr/usb_cdc_acm_log_recovery.conf b/bootloader/mcuboot/boot/zephyr/usb_cdc_acm_log_recovery.conf new file mode 100644 index 0000000..2312c0e --- /dev/null +++ b/bootloader/mcuboot/boot/zephyr/usb_cdc_acm_log_recovery.conf @@ -0,0 +1,11 @@ +CONFIG_LOG=y + +# Serial +CONFIG_UART_CONSOLE=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y + +# MCUBoot serial +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y diff --git a/bootloader/mcuboot/ci/check-signed-off-by.sh b/bootloader/mcuboot/ci/check-signed-off-by.sh new file mode 100644 index 0000000..5d41afe --- /dev/null +++ b/bootloader/mcuboot/ci/check-signed-off-by.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +DEPENDABOT_COMMITER='GitHub ' +DEPENDABOT_AUTHOR='dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>' + +if [[ -n "$1" ]]; then + commits=$(git show -s --format=%h ${1}~..HEAD) +else + parents=(`git log -n 1 --format=%p HEAD`) + if [[ "${#parents[@]}" -ne 2 ]]; then + echo "HEAD is not a merge commit, please supply the oldest SHA" + exit 1 + fi + commits=$(git show -s --format=%h ${parents[0]}..${parents[1]}) +fi + +if [[ -z "${commits}" ]]; then + echo "No commits found in this PR!" + exit 1 +fi + +for sha in $commits; do + author="$(git show -s --format="%an <%ae>" ${sha})" + committer="$(git show -s --format="%cn <%ce>" ${sha})" + + if [[ "${committer}" == "${DEPENDABOT_COMMITER}" ]] && + [[ "${author}" == "${DEPENDABOT_AUTHOR}" ]]; then + continue + fi + + author="Signed-off-by: ${author}" + committer="Signed-off-by: ${committer}" + + lines="$(git show -s --format=%B ${sha})" + + found_author=false + # Don't enforce committer email on forks; this primarily avoids issues + # running workflows on the zephyr fork, because rebases done in the GH UX + # use the primary email of the committer, which might not match the one + # used in git CLI. + if [[ $GITHUB_REPOSITORY == mcu-tools/* ]]; then + found_committer=false + else + found_committer=true + fi + + IFS=$'\n' + for line in ${lines}; do + stripped=$(echo $line | sed -e 's/^\s*//' | sed -e 's/\s*$//') + if [[ "${stripped}" == "${author}" ]]; then + found_author=true + fi + if [[ "${stripped}" == "${committer}" ]]; then + found_committer=true + fi + + [[ ${found_author} == true && ${found_committer} == true ]] && break + done + + if [[ ${found_author} == false ]]; then + echo -e "Missing \"${author}\" in commit ${sha}" + fi + if [[ ${found_committer} == false ]]; then + echo -e "Missing \"${committer}\" in commit ${sha}" + fi + if [[ ${found_author} == false || ${found_committer} == false ]]; then + exit 1 + fi +done diff --git a/bootloader/mcuboot/ci/compare_versions.py b/bootloader/mcuboot/ci/compare_versions.py new file mode 100644 index 0000000..d97392e --- /dev/null +++ b/bootloader/mcuboot/ci/compare_versions.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from packaging.version import parse, InvalidVersion +import argparse +import sys + +try: + from packaging.version import LegacyVersion +except ImportError: + LegacyVersion = () # trick isinstance! + +# exit with 0 if --new is equal to --old +# exit with 1 on errors +# exit with 2 if --new is newer than --old +# exit with 3 if --new is older than --old + +parser = argparse.ArgumentParser() +parser.add_argument('--old', help='Version currently in use') +parser.add_argument('--new', help='New version to publish') + +args = parser.parse_args() +if args.old is None or args.new is None: + parser.print_help() + exit(1) + +# packaging>=22 only supports PEP-440 version numbers, and a non-valid version +# will throw InvalidVersion. Previous packaging releases would create a +# LegacyVersion object if the given version string failed to parse as PEP-440, +# and since we use versions closer to semver, we want to fail in that case. + +versions = [] +for version in [args.old, args.new]: + try: + versions.append(parse(version)) + except InvalidVersion: + print("Invalid version parsed: {}".format(version)) + sys.exit(1) + +old, new = versions[0], versions[1] +for version in [old, new]: + if isinstance(version, LegacyVersion): + print("Invalid version parsed: {}".format(version)) + sys.exit(1) + +if new == old: + print("No version change") + sys.exit(0) +elif new > old: + print("Upgrade detected ({} > {})".format(new, old)) + sys.exit(2) + +print("Downgrade detected ({} < {})".format(new, old)) +sys.exit(3) diff --git a/bootloader/mcuboot/ci/espressif_install.sh b/bootloader/mcuboot/ci/espressif_install.sh new file mode 100644 index 0000000..c2ec2c4 --- /dev/null +++ b/bootloader/mcuboot/ci/espressif_install.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +set -eo pipefail + +install_imgtool() { + pip install imgtool +} + +install_idf() { + pushd $HOME + git clone --depth=1 https://github.com/espressif/esp-idf.git --branch release/v5.1 + [[ $? -ne 0 ]] && exit 1 + + $HOME/esp-idf/install.sh + [[ $? -ne 0 ]] && exit 1 + + popd +} + +install_imgtool +install_idf diff --git a/bootloader/mcuboot/ci/espressif_run.sh b/bootloader/mcuboot/ci/espressif_run.sh new file mode 100644 index 0000000..74b77cb --- /dev/null +++ b/bootloader/mcuboot/ci/espressif_run.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +SCRIPT_ROOTDIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")") +MCUBOOT_ROOTDIR=$(realpath "${SCRIPT_ROOTDIR}/..") +ESPRESSIF_ROOT="${MCUBOOT_ROOTDIR}/boot/espressif" +IDF_PATH="${HOME}/esp-idf" + +set -eo pipefail + +prepare_environment() { + # Prepare the environment for ESP-IDF + + . "${IDF_PATH}"/export.sh +} + +build_mcuboot() { + local target=${1} + local feature=${2} + local img_num=${3} + local build_dir=".build-${target}" + local toolchain_file="${ESPRESSIF_ROOT}/tools/toolchain-${target}.cmake" + + if [ -n "$img_num" ]; then + img_num="-${img_num}" + fi + local mcuboot_config="${ESPRESSIF_ROOT}/port/${target}/bootloader${img_num}.conf" + + if [ -n "${feature}" ]; then + mcuboot_config="${mcuboot_config};${ESPRESSIF_ROOT}/ci_configs/${feature}.conf" + build_dir=".build-${target}-${feature}" + fi + + # Build MCUboot for selected target + + cd "${MCUBOOT_ROOTDIR}" &>/dev/null + cmake -DCMAKE_TOOLCHAIN_FILE="${toolchain_file}" \ + -DMCUBOOT_TARGET="${target}" \ + -DMCUBOOT_CONFIG_FILE="${mcuboot_config}" \ + -DESP_HAL_PATH="${IDF_PATH}" \ + -B "${build_dir}" \ + "${ESPRESSIF_ROOT}" + cmake --build "${build_dir}"/ +} + +prepare_environment + +if [ -n "${MCUBOOT_FEATURES}" ]; then + IFS=',' + read -ra target_list <<< "${MCUBOOT_TARGETS}" + read img_num <<< "${MCUBOOT_IMG_NUM}" + for target in "${target_list[@]}"; do + read -ra feature_list <<< "${MCUBOOT_FEATURES}" + for feature in "${feature_list[@]}"; do + echo "Building MCUboot for \"${target}\" with support for \"${feature}\"" + build_mcuboot "${target}" "${feature}" "${img_num}" + done + done +fi diff --git a/bootloader/mcuboot/ci/fih-tests_install.sh b/bootloader/mcuboot/ci/fih-tests_install.sh new file mode 100644 index 0000000..1e78aaf --- /dev/null +++ b/bootloader/mcuboot/ci/fih-tests_install.sh @@ -0,0 +1,34 @@ +#!/bin/bash -x + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +source $(dirname "$0")/fih-tests_version.sh + +DOCKER_DIR=docker + +IMAGE=fih-test:$FIH_IMAGE_VERSION + +CACHED_IMAGE=$DOCKER_DIR/$IMAGE + +[[ -f $CACHED_IMAGE ]] && (gzip -dc $CACHED_IMAGE | docker load) + +if [[ $? -ne 0 ]]; then + docker pull mcuboot/$IMAGE + if [[ $GITHUB_ACTIONS != true ]]; then + docker save mcuboot/$IMAGE | gzip > $CACHED_IMAGE + fi +fi diff --git a/bootloader/mcuboot/ci/fih-tests_run.sh b/bootloader/mcuboot/ci/fih-tests_run.sh new file mode 100644 index 0000000..66357d5 --- /dev/null +++ b/bootloader/mcuboot/ci/fih-tests_run.sh @@ -0,0 +1,58 @@ +#!/bin/bash -x + +# Copyright (c) 2020-2024 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +source $(dirname "$0")/fih-tests_version.sh + +pushd .. &&\ + git clone https://git.trustedfirmware.org/TF-M/trusted-firmware-m.git &&\ + pushd trusted-firmware-m &&\ + git checkout eb8ff0db7d657b77abcd0262d5bf7f38eb1e1cdc &&\ + source lib/ext/tf-m-tests/version.txt &&\ + popd &&\ + git clone https://git.trustedfirmware.org/TF-M/tf-m-tests.git &&\ + pushd tf-m-tests &&\ + git checkout $version &&\ + popd + +if [[ $GITHUB_ACTIONS == true ]]; then + if [[ -z $FIH_ENV ]]; then + echo "Workflow has found no \$FIH_ENV" + exit 1 + fi + + args=($FIH_ENV) + len=${#args[@]} + if [[ $len < 3 ]]; then + echo "Invalid number of \$FIH_ENV args" + exit 1 + fi + + BUILD_TYPE=${args[0]} + SKIP_SIZE=${args[1]} + DAMAGE_TYPE=${args[2]} + + if [[ $len > 3 ]]; then + FIH_LEVEL=${args[3]} + fi +fi + +if test -z "$FIH_LEVEL"; then + docker run --rm -v $(pwd):/root/work/tfm:rw,z mcuboot/fih-test:$FIH_IMAGE_VERSION /bin/sh -c '/root/work/tfm/mcuboot/ci/fih_test_docker/execute_test.sh $0 $1 $2' $SKIP_SIZE $BUILD_TYPE $DAMAGE_TYPE +else + docker run --rm -v $(pwd):/root/work/tfm:rw,z mcuboot/fih-test:$FIH_IMAGE_VERSION /bin/sh -c '/root/work/tfm/mcuboot/ci/fih_test_docker/execute_test.sh $0 $1 $2 $3' $SKIP_SIZE $BUILD_TYPE $DAMAGE_TYPE $FIH_LEVEL +fi diff --git a/bootloader/mcuboot/ci/fih-tests_version.sh b/bootloader/mcuboot/ci/fih-tests_version.sh new file mode 100644 index 0000000..a46aae3 --- /dev/null +++ b/bootloader/mcuboot/ci/fih-tests_version.sh @@ -0,0 +1 @@ +FIH_IMAGE_VERSION=0.0.3 diff --git a/bootloader/mcuboot/ci/fih_test_docker/damage_image.py b/bootloader/mcuboot/ci/fih_test_docker/damage_image.py new file mode 100644 index 0000000..afa25b8 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/damage_image.py @@ -0,0 +1,211 @@ +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import logging +import struct +import sys + +from imgtool.image import (IMAGE_HEADER_SIZE, IMAGE_MAGIC, + TLV_INFO_MAGIC, TLV_PROT_INFO_MAGIC, TLV_VALUES) +from shutil import copyfile + + +def get_tlv_type_string(tlv_type): + tlvs = {v: f"IMAGE_TLV_{k}" for k, v in TLV_VALUES.items()} + return tlvs.get(tlv_type, "UNKNOWN({:d})".format(tlv_type)) + + +class ImageHeader: + + def __init__(self): + self.ih_magic = 0 + self.ih_load_addr = 0 + self.ih_hdr_size = 0 + self.ih_protect_tlv_size = 0 + self.ih_img_size = 0 + self.ih_flags = 0 + self.iv_major = 0 + self.iv_minor = 0 + self.iv_revision = 0 + self.iv_build_num = 0 + self._pad1 = 0 + + @staticmethod + def read_from_binary(in_file): + h = ImageHeader() + + (h.ih_magic, h.ih_load_addr, h.ih_hdr_size, h.ih_protect_tlv_size, h.ih_img_size, + h.ih_flags, h.iv_major, h.iv_minor, h.iv_revision, h.iv_build_num, h._pad1 + ) = struct.unpack('=1.9.0 \ + Jinja2>=2.10.3 \ + PyYAML \ + pyasn1 + +# Add tfm work directory && get rid of spurious git ownership errors +RUN mkdir -p /root/work/tfm &&\ + git config --global --add safe.directory '*' + +# run the command +CMD ["bash"] diff --git a/bootloader/mcuboot/ci/fih_test_docker/docker-build/build.sh b/bootloader/mcuboot/ci/fih_test_docker/docker-build/build.sh new file mode 100644 index 0000000..d0d00b3 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/docker-build/build.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +trap cleanup_exit INT TERM EXIT + +cleanup_exit() +{ + rm -f *.list *.key +} + +export LANG=C + +image=mcuboot/fih-test +docker build --pull --tag=$image . diff --git a/bootloader/mcuboot/ci/fih_test_docker/execute_test.sh b/bootloader/mcuboot/ci/fih_test_docker/execute_test.sh new file mode 100644 index 0000000..cc67d84 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/execute_test.sh @@ -0,0 +1,68 @@ +#!/bin/bash -x + +# Copyright (c) 2020-2023 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +source $(dirname "$0")/paths.sh + +SKIP_SIZE=$1 +BUILD_TYPE=$2 +DAMAGE_TYPE=$3 +FIH_LEVEL=$4 + +if test -z "$FIH_LEVEL"; then + # Use the default level + CMAKE_FIH_LEVEL="" +else + CMAKE_FIH_LEVEL="-DMCUBOOT_FIH_PROFILE=\"$FIH_LEVEL\"" +fi + +# build TF-M with MCUBoot +mkdir -p $TFM_BUILD_PATH $TFM_SPE_BUILD_PATH + +cmake -S $TFM_TESTS_PATH/tests_reg/spe \ + -B $TFM_SPE_BUILD_PATH \ + -DTFM_PLATFORM=arm/mps2/an521 \ + -DCONFIG_TFM_SOURCE_PATH=$TFM_PATH \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + -DTFM_TOOLCHAIN_FILE=$TFM_PATH/toolchain_GNUARM.cmake \ + -DTEST_S=ON \ + -DTEST_NS=ON \ + -DTFM_PSA_API=ON \ + -DMCUBOOT_PATH=$MCUBOOT_PATH \ + -DMCUBOOT_LOG_LEVEL=INFO \ + $CMAKE_FIH_LEVEL +cmake --build $TFM_SPE_BUILD_PATH -- install + +cmake -S $TFM_TESTS_PATH/tests_reg \ + -B $TFM_BUILD_PATH \ + -DCONFIG_SPE_PATH=$TFM_SPE_BUILD_PATH/api_ns \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + -DTFM_TOOLCHAIN_FILE=$TFM_SPE_BUILD_PATH/api_ns/cmake/toolchain_ns_GNUARM.cmake +cmake --build $TFM_BUILD_PATH + +cd $TFM_BUILD_PATH +$MCUBOOT_PATH/ci/fih_test_docker/run_fi_test.sh $BOOTLOADER_AXF_PATH $SKIP_SIZE $DAMAGE_TYPE> fih_test_output.yaml + +echo "" +echo "test finished with" +echo " - BUILD_TYPE: $BUILD_TYPE" +echo " - FIH_LEVEL: $FIH_LEVEL" +echo " - SKIP_SIZE: $SKIP_SIZE" +echo " - DAMAGE_TYPE: $DAMAGE_TYPE" + +python3 $MCUBOOT_PATH/ci/fih_test_docker/generate_test_report.py fih_test_output.yaml +python3 $MCUBOOT_PATH/ci/fih_test_docker/validate_output.py fih_test_output.yaml $SKIP_SIZE $FIH_LEVEL diff --git a/bootloader/mcuboot/ci/fih_test_docker/fi_make_manifest.sh b/bootloader/mcuboot/ci/fih_test_docker/fi_make_manifest.sh new file mode 100644 index 0000000..190a316 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/fi_make_manifest.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +OBJDUMP=arm-none-eabi-objdump +GDB=gdb-multiarch + +# Check if the ELF file specified is compatible +if test $# -eq 0 || ! file $1 | grep "ELF" | grep "ARM" | grep "32" &>/dev/null; then + echo "Incompatible file: $1" 1>&2 + exit 1 +fi + +# Extract the full path +AXF_PATH=$(realpath $1) +#Dump all objects that have a name containing FIH_LABEL +ADDRESSES=$($OBJDUMP $AXF_PATH -t | grep "FIH_LABEL") +# strip all data except "address, label_name" +ADDRESSES=$(echo "$ADDRESSES" | sed "s/\([[:xdigit:]]*\).*\(FIH_LABEL_FIH_CALL_[a-zA-Z]*\)_.*/0x\1, \2/g") +# Sort by address in ascending order +ADDRESSES=$(echo "$ADDRESSES" | sort) +# In the case that there is a START followed by another START take the first one +ADDRESSES=$(echo "$ADDRESSES" | sed "N;s/\(.*START.*\)\n\(.*START.*\)/\1/;P;D") +# Same for END except take the second one +ADDRESSES=$(echo "$ADDRESSES" | sed "N;s/\(.*END.*\)\n\(.*END.*\)/\2/;P;D") + +# Output in CSV format with a label +echo "Address, Type" +echo "$ADDRESSES" diff --git a/bootloader/mcuboot/ci/fih_test_docker/fi_tester_gdb.sh b/bootloader/mcuboot/ci/fih_test_docker/fi_tester_gdb.sh new file mode 100644 index 0000000..ffb0bc7 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/fi_tester_gdb.sh @@ -0,0 +1,204 @@ +#!/bin/bash + +# Copyright (c) 2020-2022 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source $(dirname "$0")/paths.sh + +function skip_instruction { + + local SKIP_ADDRESS=$1 + local SKIP_SIZE=$2 + + # Parse the ASM instruction from the address using gdb + INSTR=$($GDB $AXF_FILE --batch -ex "disassemble $SKIP_ADDRESS" | grep "^ *$SKIP_ADDRESS" | sed "s/.*:[ \t]*\(.*\)$/\1/g") + # Parse the C line from the address using gdb + LINE=$($GDB $AXF_FILE --batch -ex "info line *$SKIP_ADDRESS" | sed "s/Line \([0-9]*\).*\"\(.*\)\".*/\2:\1/g") + + # Sometimes an address is in the middle of a 4 byte instruction. In that case + # don't run the test + if test "$INSTR" == ""; then + return + fi + + # Print out the meta-info about the test, in YAML + echo "- skip_test:" + echo " addr: $SKIP_ADDRESS" + echo " asm: \"$INSTR\"" + echo " line: \"$LINE\"" + echo " skip: $SKIP_SIZE" + # echo -ne "$SKIP_ADDRESS | $INSTR...\t" + + cat >commands.gdb <&2 + + # start qemu, dump the serial output to $QEMU_LOG_FILE + QEMU_LOG_FILE=qemu.log + QEMU_PID_FILE=qemu_pid.txt + rm -f $QEMU_PID_FILE $QEMU_LOG_FILE + /usr/bin/qemu-system-arm \ + -M mps2-an521 \ + -s -S \ + -kernel $AXF_FILE \ + -device loader,file=$TFM_IMAGE_PATH,addr=0x10080000 \ + -chardev file,id=char0,path=$QEMU_LOG_FILE \ + -serial chardev:char0 \ + -display none \ + -pidfile $QEMU_PID_FILE \ + -daemonize + + # start qemu, skip the instruction, and continue execution + $GDB < ./commands.gdb &>gdb_out.txt + + # kill qemu + kill -9 `cat $QEMU_PID_FILE` + + # If "Secure image initializing" is seen the TFM booted, which means that a skip + # managed to defeat the signature check. Write out whether the image booted or + # not to the log in YAML + if cat $QEMU_LOG_FILE | grep -i "Starting bootloader" &>/dev/null; then + # bootloader started successfully + if cat gdb_out.txt | grep -i "Stopped at breakpoint" &>/dev/null; then + # The target was stopped at the desired address + if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then + echo " test_exec_ok: True" + echo " skipped: True" + echo " boot: True" + + #print the address that was skipped, and some context to the console + echo "" 1>&2 + echo "Boot success: address: $SKIP_ADDRESS skipped: $SKIP_SIZE" 1>&2 + arm-none-eabi-objdump -d $AXF_FILE --start-address=$SKIP_ADDRESS -S | tail -n +7 | head -n 14 1>&2 + echo "" 1>&2 + echo "" 1>&2 + else + LAST_LINE=`tail -n 1 $QEMU_LOG_FILE | tr -dc '[:print:]'` + echo " test_exec_ok: True" + echo " skipped: True" + echo " boot: False" + echo " last_line: \"$LAST_LINE\" " + fi + else + # The target was not stopped at the desired address. + # The most probable reason is that the instruction for that address is + # on a call path that is not taken in this run (e.g. error handling) + if cat $QEMU_LOG_FILE | grep -i "Secure image initializing" &>/dev/null; then + # The image booted, although it shouldn't happen as the test is to + # be run with a corrupt image. + echo " test_exec_ok: False" + echo " test_exec_fail_reason: \"No instructions were skipped (e.g. branch was not executed), but booted successfully\"" + else + # the execution didn't stop at the address (e.g. the instruction + # is on a branch that is not taken) + echo " test_exec_ok: True" + echo " skipped: False" + fi + fi + else + # failed before the first printout + echo " test_exec_ok: True" + echo " skipped: True" + echo " boot: False" + echo " last_line: 'N/A' " + fi +} + +# Inform how the script is used +usage() { + echo "$0 [] [(-s | --skip) ]" +} + +#defaults +SKIP=2 +AXF_FILE=${BOOTLOADER_AXF_PATH} +GDB=gdb-multiarch +BOOTLOADER=true + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + -s|--skip) + SKIP="$2" + shift + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + if test -z "$IMAGE_DIR"; then + IMAGE_DIR=$1 + elif test -z "$START"; then + START=$1 + elif test -z "$END"; then + END=$1 + else + usage + exit 1 + fi + shift + ;; + esac +done + +# Check that image directory, start and end address have been supplied +if test -z "$IMAGE_DIR"; then + usage + exit 2 +fi + +if test -z "$START"; then + usage + exit 2 +fi + +if test -z "$END"; then + END=$START +fi + +if test -z "$SKIP"; then + SKIP='2' +fi + +# Create the start-end address range (step 2) +ADDRS=$(printf '0x%x\n' $(seq "$START" 2 "$END")) + +# For each address run the skip_instruction function on it +for ADDR in $ADDRS; do + skip_instruction $ADDR $SKIP +done diff --git a/bootloader/mcuboot/ci/fih_test_docker/generate_test_report.py b/bootloader/mcuboot/ci/fih_test_docker/generate_test_report.py new file mode 100644 index 0000000..2d68949 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/generate_test_report.py @@ -0,0 +1,47 @@ +# Copyright (c) 2020-2023 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +from utils import CATEGORIES, parse_yaml_file + + +def print_results(results): + + test_stats, failed_boot_last_lines, exec_fail_reasons = results + + print("{:s}: {:d}.".format(CATEGORIES['TOTAL'], test_stats[CATEGORIES['TOTAL']])) + print("{:s} ({:d}):".format(CATEGORIES['SUCCESS'], test_stats[CATEGORIES['SUCCESS']])) + print(" {:s}: ({:d}):".format(CATEGORIES['ADDRES_NOEXEC'], test_stats[CATEGORIES['ADDRES_NOEXEC']])) + test_with_skip = test_stats[CATEGORIES['SUCCESS']] - test_stats[CATEGORIES['ADDRES_NOEXEC']] + print(" {:s}: ({:d}):".format(CATEGORIES['SKIPPED'], test_with_skip)) + print(" {:s} ({:d}):".format(CATEGORIES['NO_BOOT'], test_with_skip - test_stats[CATEGORIES['BOOT']])) + for last_line in failed_boot_last_lines.keys(): + print(" last line: {:s} ({:d})".format(last_line, failed_boot_last_lines[last_line])) + print(" {:s} ({:d})".format(CATEGORIES['BOOT'], test_stats[CATEGORIES['BOOT']])) + print("{:s} ({:d}):".format(CATEGORIES['FAILED'], test_stats[CATEGORIES['TOTAL']] - test_stats[CATEGORIES['SUCCESS']])) + for reason in exec_fail_reasons.keys(): + print(" {:s} ({:d})".format(reason, exec_fail_reasons[reason])) + + +def main(): + parser = argparse.ArgumentParser(description='''Process a FIH test output yaml file, and output a human readable report''') + parser.add_argument('filename', help='yaml file to process') + + args = parser.parse_args() + results = parse_yaml_file(args.filename) + print_results(results) + + +if __name__ == "__main__": + main() diff --git a/bootloader/mcuboot/ci/fih_test_docker/paths.sh b/bootloader/mcuboot/ci/fih_test_docker/paths.sh new file mode 100644 index 0000000..6afeab7 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/paths.sh @@ -0,0 +1,10 @@ +WORK_PATH=/root/work/tfm +MCUBOOT_PATH=$WORK_PATH/mcuboot +TFM_PATH=$WORK_PATH/trusted-firmware-m +TFM_TESTS_PATH=$WORK_PATH/tf-m-tests +TFM_SPE_BUILD_PATH=$TFM_PATH/build_spe +TFM_BUILD_PATH=$TFM_PATH/build +BOOTLOADER_AXF_PATH=$TFM_SPE_BUILD_PATH/bin/bl2.axf +TFM_IMAGE_NAME=tfm_s_ns_signed.bin +TFM_IMAGE_OUTPUT_PATH=$TFM_BUILD_PATH +TFM_IMAGE_PATH=$TFM_IMAGE_OUTPUT_PATH/$TFM_IMAGE_NAME diff --git a/bootloader/mcuboot/ci/fih_test_docker/run_fi_test.sh b/bootloader/mcuboot/ci/fih_test_docker/run_fi_test.sh new file mode 100644 index 0000000..5bf884c --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/run_fi_test.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +# Copyright (c) 2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +# Get the dir this is running in and the dir the script is in. +PWD=$(pwd) +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd ) + +# PAD is the amount of extra instructions that should be tested on each side of +# the critical region +PAD=6 + +MCUBOOT_AXF=$1 +SKIP_SIZES=$2 +DAMAGE_TYPE=$3 + +source $(dirname "$0")/paths.sh + +# Take an image and make it unbootable. This is done by replacing one of the +# strings in the image with a different string. This causes the signature check +# to fail +function damage_image +{ + local IMAGE_NAME=${TFM_IMAGE_NAME} + local BACKUP_IMAGE_NAME=${TFM_IMAGE_NAME}.orig + local IMAGE=$TFM_IMAGE_OUTPUT_PATH/$IMAGE_NAME + mv $IMAGE $TFM_IMAGE_OUTPUT_PATH/$BACKUP_IMAGE_NAME + + if [ "$DAMAGE_TYPE" = "SIGNATURE" ]; then + DAMAGE_PARAM="--signature" + elif [ "$DAMAGE_TYPE" = "IMAGE_HASH" ]; then + DAMAGE_PARAM="--image-hash" + else + echo "Failed to damage image $IMAGE with param $DAMAGE_TYPE" 1>&2 + exit -1 + fi + + python3 $DIR/damage_image.py -i $TFM_IMAGE_OUTPUT_PATH/$BACKUP_IMAGE_NAME -o $IMAGE $DAMAGE_PARAM 1>&2 +} + +function run_test +{ + local SKIP_SIZE=$1 + + $DIR/fi_make_manifest.sh $MCUBOOT_AXF > $PWD/fih_manifest.csv + + # Load the CSV FI manifest file, and output in START, END lines. Effectively + # join START and END lines together with a comma seperator. + REGIONS=$(sed "N;s/\(0x[[:xdigit:]]*\).*START\n\(0x[[:xdigit:]]*\).*END.*/\1,\2/g;P;D" $PWD/fih_manifest.csv) + # Ignore the first line, which includes the CSV header + REGIONS=$(echo "$REGIONS" | tail -n+2) + + for REGION in $REGIONS; do + #Split the START,END pairs into the two variables + START=$(echo $REGION | cut -d"," -f 1) + END=$(echo $REGION | cut -d"," -f 2) + + # Apply padding, converting back to hex + START=$(printf "0x%X" $((START - PAD))) + END=$(printf "0x%X" $((END + PAD))) + + # Invoke the fi tester script + $DIR/fi_tester_gdb.sh $TFM_IMAGE_OUTPUT_PATH $START $END --skip $SKIP_SIZE + done +} + +damage_image $MCUBOOT_AXF +# Run the run_test function with each skip length between min and max in turn. + +IFS=', ' read -r -a sizes <<< "$SKIP_SIZES" +for size in "${sizes[@]}"; do + echo "Run tests with skip size $size" 1>&2 + run_test $size +done diff --git a/bootloader/mcuboot/ci/fih_test_docker/utils.py b/bootloader/mcuboot/ci/fih_test_docker/utils.py new file mode 100644 index 0000000..cd58f28 --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/utils.py @@ -0,0 +1,63 @@ +# Copyright (c) 2023 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import collections +import yaml + +CATEGORIES = { + 'TOTAL': 'Total tests run', + 'SUCCESS': 'Tests executed successfully', + 'FAILED': 'Tests failed to execute successfully', + # the execution never reached the address + 'ADDRES_NOEXEC': 'Address was not executed', + # The address was successfully skipped by the debugger + 'SKIPPED': 'Address was skipped', + 'NO_BOOT': 'System not booted (desired behaviour)', + 'BOOT': 'System booted (undesired behaviour)' +} + + +def parse_yaml_file(filepath): + with open(filepath) as f: + results = yaml.safe_load(f) + + if not results: + raise ValueError("Failed to parse output yaml file.") + + test_stats = collections.Counter() + failed_boot_last_lines = collections.Counter() + exec_fail_reasons = collections.Counter() + + for test in results: + test = test["skip_test"] + + test_stats.update([CATEGORIES['TOTAL']]) + + if test["test_exec_ok"]: + test_stats.update([CATEGORIES['SUCCESS']]) + + if "skipped" in test.keys() and not test["skipped"]: + # The debugger didn't stop at this address + test_stats.update([CATEGORIES['ADDRES_NOEXEC']]) + continue + + if test["boot"]: + test_stats.update([CATEGORIES['BOOT']]) + continue + + failed_boot_last_lines.update([test["last_line"]]) + else: + exec_fail_reasons.update([test["test_exec_fail_reason"]]) + + return test_stats, failed_boot_last_lines, exec_fail_reasons diff --git a/bootloader/mcuboot/ci/fih_test_docker/validate_output.py b/bootloader/mcuboot/ci/fih_test_docker/validate_output.py new file mode 100644 index 0000000..7c334ba --- /dev/null +++ b/bootloader/mcuboot/ci/fih_test_docker/validate_output.py @@ -0,0 +1,39 @@ +# Copyright (c) 2023 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +from utils import CATEGORIES, parse_yaml_file + + +def validate_output(test_stats, skip_size, fih_level): + if (test_stats[CATEGORIES['BOOT']] > 0 + and skip_size == "2,4,6" and fih_level == "MEDIUM"): + raise ValueError("The number of sucessful boots was more than zero") + + +def main(): + parser = argparse.ArgumentParser(description='''Process a FIH test output yaml file, + and validate no sucessfull boots have happened''') + parser.add_argument('filename', help='yaml file to process') + parser.add_argument('skip_size', help='instruction skip size') + parser.add_argument('fih_level', nargs="?", + help='fault injection hardening level') + + args = parser.parse_args() + test_stats = parse_yaml_file(args.filename)[0] + validate_output(test_stats, args.skip_size, args.fih_level) + + +if __name__ == "__main__": + main() diff --git a/bootloader/mcuboot/ci/get_features.py b/bootloader/mcuboot/ci/get_features.py new file mode 100644 index 0000000..50da557 --- /dev/null +++ b/bootloader/mcuboot/ci/get_features.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# Copyright 2020 JUUL Labs +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os.path +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib + +parser = argparse.ArgumentParser(description='Print features from a Cargo.toml.') +parser.add_argument('infile', help='Input file to parse') + +args = parser.parse_args() +if not os.path.isfile(args.infile): + print("File not found") + exit(1) + +try: + cargo_toml = open(args.infile).read() +except Exception: + print("Error reading \"{}\"".format(args.infile)) + exit(1) + +config = tomllib.loads(cargo_toml) +if 'features' not in config: + print("Missing \"[features]\" section") + exit(1) + +print(" ".join([k for k in config['features'] if k != 'default'])) diff --git a/bootloader/mcuboot/ci/imgtool_install.sh b/bootloader/mcuboot/ci/imgtool_install.sh new file mode 100644 index 0000000..66b44cf --- /dev/null +++ b/bootloader/mcuboot/ci/imgtool_install.sh @@ -0,0 +1,23 @@ +#!/bin/bash -x + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [[ $TRAVIS == "true" ]]; then + if [[ $TRAVIS_PULL_REQUEST != "false" || $TRAVIS_BRANCH != "main" ]]; then + echo "Either a PR or not \"main\" branch, exiting" + exit 0 + fi +fi + +pip install setuptools twine packaging wheel +pip install --pre imgtool diff --git a/bootloader/mcuboot/ci/imgtool_run.sh b/bootloader/mcuboot/ci/imgtool_run.sh new file mode 100644 index 0000000..97f0b21 --- /dev/null +++ b/bootloader/mcuboot/ci/imgtool_run.sh @@ -0,0 +1,53 @@ +#!/bin/bash -x + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [[ $TRAVIS == "true" ]]; then + if [[ $TRAVIS_PULL_REQUEST != "false" || $TRAVIS_BRANCH != "main" ]]; then + echo "Either a PR or not \"main\" branch, exiting" + exit 0 + fi +fi + +IMGTOOL_VER_PREFIX="\+imgtool_version = " +IMGTOOL_VER_FILE="imgtool/__init__.py" +DIST_DIR="dist" + +if [[ -z "$TWINE_TOKEN" ]]; then + echo "\$TWINE_TOKEN must be set in Travis or GH settings" + exit 0 +fi + +cd scripts/ + +last_release=$(pip show imgtool | grep "Version: " | cut -d" " -f2) +repo_version=$(grep "imgtool_version = " imgtool/__init__.py | sed 's/^.* = "\(.*\)"/\1/g') + +python ../ci/compare_versions.py --old $last_release --new $repo_version +rc=$? + +if [[ $rc -eq 0 ]]; then + echo "Imgtool version not changed; will not publish" + exit 0 +elif [[ $rc -eq 1 ]]; then + echo "Error parsing versions" + exit 1 +elif [[ $rc -eq 3 ]]; then + echo "Imgtool downgrade detected!" + exit 1 +fi + +rm -rf $DIST_DIR +python setup.py sdist bdist_wheel + +twine upload --username __token__ --password "${TWINE_TOKEN}" "${DIST_DIR}/*" diff --git a/bootloader/mcuboot/ci/mynewt_install.sh b/bootloader/mcuboot/ci/mynewt_install.sh new file mode 100644 index 0000000..3ce73a4 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_install.sh @@ -0,0 +1,83 @@ +#!/bin/bash -x + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +install_newt() { + pushd $HOME + git clone --depth=1 https://github.com/apache/mynewt-newt + [[ $? -ne 0 ]] && exit 1 + + pushd mynewt-newt && ./build.sh + [[ $? -ne 0 ]] && exit 1 + + cp newt/newt $HOME/bin + popd + popd +} + +shallow_clone_mynewt() { + mkdir -p repos/apache-mynewt-core + git clone --depth=1 https://github.com/apache/mynewt-core repos/apache-mynewt-core + [[ $? -ne 0 ]] && exit 1 + + # nrfx is now taken from original repository + git clone --depth=1 --branch v3.3.0 https://github.com/NordicSemiconductor/nrfx.git repos/nordic-nrfx + [[ $? -ne 0 ]] && exit 1 + + # Mbed-TLS is now taken from original repository + git clone --depth=1 --branch v2.28.4 https://github.com/Mbed-TLS/mbedtls.git repos/mbedtls + [[ $? -ne 0 ]] && exit 1 + + # CMSIS is now taken from original repository + git clone --depth=1 --branch 5.4.0 https://github.com/ARM-software/CMSIS_5.git repos/arm-CMSIS_5 + [[ $? -ne 0 ]] && exit 1 +} + +arm_toolchain_install() { + TOOLCHAIN_PATH=$HOME/TOOLCHAIN + + GCC_URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2017q4/gcc-arm-none-eabi-7-2017-q4-major-linux.tar.bz2 + GCC_BASE=gcc-arm-none-eabi-7-2017-q4-major + + mkdir -p $TOOLCHAIN_PATH + + if [ ! -s ${TOOLCHAIN_PATH}/$GCC_BASE/bin/arm-none-eabi-gcc ]; then + wget -O ${TOOLCHAIN_PATH}/${GCC_BASE}.tar.bz2 $GCC_URL + [[ $? -ne 0 ]] && exit 1 + + tar xfj ${TOOLCHAIN_PATH}/${GCC_BASE}.tar.bz2 -C $TOOLCHAIN_PATH + fi + + for i in ${TOOLCHAIN_PATH}/${GCC_BASE}/bin/arm-none-eabi-* ; do + rm -f $HOME/bin/${i##*/} + ln -s $i $HOME/bin/${i##*/} + done +} + +native_test_setup() { + sudo apt-get update + sudo apt-get install -y gcc-multilib +} + +mkdir -p $HOME/bin +export PATH=$HOME/bin:$PATH + +install_newt +shallow_clone_mynewt +arm_toolchain_install +native_test_setup diff --git a/bootloader/mcuboot/ci/mynewt_keys/enc_kw/pkg.yml b/bootloader/mcuboot/ci/mynewt_keys/enc_kw/pkg.yml new file mode 100644 index 0000000..0e993a4 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_keys/enc_kw/pkg.yml @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: keys/enc_kw +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" diff --git a/bootloader/mcuboot/ci/mynewt_keys/enc_kw/src/keys.c b/bootloader/mcuboot/ci/mynewt_keys/enc_kw/src/keys.c new file mode 100644 index 0000000..ae4c5c7 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_keys/enc_kw/src/keys.c @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +unsigned char enc_key[] = { + 0x96, 0x69, 0xd2, 0xcf, 0x0e, 0xb1, 0xc6, 0x56, 0xf2, 0xa0, 0x1f, 0x46, + 0x06, 0xd3, 0x49, 0x31, +}; +static unsigned int enc_key_len = 16; +const struct bootutil_key bootutil_enc_key = { + .key = enc_key, + .len = &enc_key_len, +}; diff --git a/bootloader/mcuboot/ci/mynewt_keys/enc_rsa/pkg.yml b/bootloader/mcuboot/ci/mynewt_keys/enc_rsa/pkg.yml new file mode 100644 index 0000000..61918f1 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_keys/enc_rsa/pkg.yml @@ -0,0 +1,25 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: keys/enc_rsa +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" + +pkg.cflags: + - '-DMBEDTLS_USER_CONFIG_FILE="mbedtls/config_mynewt.h"' diff --git a/bootloader/mcuboot/ci/mynewt_keys/enc_rsa/src/keys.c b/bootloader/mcuboot/ci/mynewt_keys/enc_rsa/src/keys.c new file mode 100644 index 0000000..201d6ad --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_keys/enc_rsa/src/keys.c @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +unsigned char enc_key[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xb4, 0x26, 0x14, 0x49, 0x3d, 0x16, 0x13, 0x3a, 0x6d, 0x9c, 0x84, 0xa9, + 0x8b, 0x6a, 0x10, 0x20, 0x61, 0xef, 0x48, 0x04, 0xa4, 0x4b, 0x24, 0xf3, + 0x00, 0x32, 0xac, 0x22, 0xe0, 0x30, 0x27, 0x70, 0x18, 0xe5, 0x55, 0xc8, + 0xb8, 0x05, 0x34, 0x03, 0xb0, 0xf8, 0xa5, 0x96, 0xd2, 0x48, 0x58, 0xef, + 0x70, 0xb0, 0x09, 0xdb, 0xe3, 0x58, 0x62, 0xef, 0x99, 0x63, 0x01, 0xb2, + 0x89, 0xc4, 0xb3, 0xf6, 0x9e, 0x62, 0xbf, 0x4d, 0xc2, 0x8a, 0xd0, 0xc9, + 0x4d, 0x43, 0xa3, 0xd8, 0xe5, 0x1d, 0xec, 0x62, 0x63, 0x08, 0xe2, 0x20, + 0xa5, 0xfc, 0x78, 0xd0, 0x3e, 0x74, 0xc8, 0xa4, 0x1b, 0x36, 0xad, 0x7b, + 0xf5, 0x06, 0xae, 0x4d, 0x51, 0x9b, 0x40, 0xce, 0x30, 0x4f, 0x6c, 0xea, + 0xf9, 0xe9, 0x74, 0xea, 0x06, 0xee, 0x9c, 0xe4, 0x14, 0x68, 0x20, 0xb9, + 0x3d, 0xe7, 0x11, 0x14, 0x8b, 0x25, 0xa3, 0xff, 0x4c, 0x8a, 0xf3, 0x53, + 0xee, 0x6b, 0x3e, 0xef, 0x34, 0xcd, 0x6a, 0x3f, 0x62, 0x68, 0xc0, 0xff, + 0x78, 0x4c, 0xb0, 0xc3, 0xe6, 0x96, 0x61, 0xfc, 0x1f, 0x18, 0xf1, 0x7a, + 0x82, 0xe2, 0x8f, 0x35, 0xa8, 0x2b, 0x86, 0x16, 0xa4, 0x46, 0xfb, 0xac, + 0x7e, 0x41, 0xdb, 0x02, 0x05, 0x91, 0x6d, 0xdf, 0xc1, 0xde, 0x13, 0x95, + 0x9c, 0xf9, 0x9e, 0x5e, 0x72, 0xba, 0xa7, 0x25, 0x93, 0xfb, 0xdc, 0xe8, + 0xab, 0x86, 0x45, 0x88, 0x47, 0x2d, 0xed, 0xee, 0xee, 0x97, 0x9e, 0xce, + 0x5d, 0x9b, 0x04, 0x04, 0x40, 0x7c, 0xcb, 0x7c, 0x3d, 0x2c, 0x74, 0xab, + 0xa4, 0xcc, 0x64, 0xa3, 0x5c, 0x95, 0x3d, 0xd4, 0xa2, 0xdc, 0x92, 0xb2, + 0xc8, 0x18, 0xcb, 0xf9, 0x00, 0x39, 0x81, 0x8f, 0x8f, 0x40, 0xc2, 0xdf, + 0x99, 0x29, 0xac, 0x8a, 0xc2, 0x3b, 0xd8, 0xa4, 0xf2, 0xad, 0xaf, 0x74, + 0xc0, 0x11, 0xc7, 0x99, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x42, 0x47, 0x80, 0x4f, 0x31, 0xda, 0x5d, 0x58, 0xb1, 0xdb, 0x54, + 0x33, 0xcc, 0xc7, 0x49, 0x07, 0xa1, 0x00, 0x98, 0x4e, 0x9c, 0xe3, 0xc8, + 0xc4, 0x5e, 0xde, 0x45, 0xd6, 0xcf, 0x04, 0xe8, 0x7d, 0xa5, 0xab, 0x3a, + 0xd4, 0x8e, 0x5f, 0xdb, 0xb3, 0x3f, 0xf9, 0x3b, 0x73, 0x32, 0x0a, 0xcc, + 0x2d, 0xcc, 0x17, 0xf8, 0x88, 0x9e, 0x2c, 0x76, 0xba, 0x10, 0x85, 0x0c, + 0xaa, 0xd3, 0x65, 0x3b, 0x91, 0x10, 0xd4, 0xe3, 0xed, 0x88, 0x15, 0xea, + 0x9b, 0x25, 0x82, 0x2d, 0x56, 0x2f, 0x75, 0xc2, 0xf2, 0xaf, 0xdd, 0x24, + 0xd5, 0x3e, 0x3c, 0x95, 0x76, 0x88, 0x84, 0x0f, 0x0d, 0xd1, 0xb5, 0x5c, + 0x3e, 0xae, 0xf7, 0xb6, 0x49, 0x5c, 0x2c, 0xf2, 0xba, 0xe9, 0xab, 0x4f, + 0x37, 0x64, 0x9b, 0x30, 0x18, 0xaa, 0x54, 0x40, 0x04, 0xea, 0x3d, 0x25, + 0x4d, 0x02, 0x29, 0x71, 0x6f, 0x4d, 0x82, 0x9b, 0xc3, 0x44, 0x2a, 0x9d, + 0x0c, 0x98, 0xd3, 0xc8, 0x15, 0x0d, 0x04, 0x93, 0x60, 0x30, 0xc7, 0x5e, + 0x79, 0xea, 0x53, 0x9d, 0xc0, 0x0e, 0x81, 0xac, 0x90, 0xbc, 0x9e, 0x1e, + 0xd2, 0x28, 0x0f, 0x10, 0xf5, 0x1f, 0xdf, 0x38, 0x7f, 0x8a, 0x90, 0x8d, + 0x49, 0x07, 0x7d, 0x78, 0xcb, 0xa7, 0xef, 0x92, 0x6d, 0x3b, 0x13, 0x95, + 0x9b, 0xba, 0x83, 0xc6, 0xb3, 0x71, 0x25, 0x27, 0x07, 0x99, 0x54, 0x82, + 0x3d, 0xec, 0xc5, 0xf8, 0xb4, 0xa0, 0x38, 0x7a, 0x59, 0x6a, 0x0b, 0xca, + 0x69, 0x6c, 0x17, 0xa4, 0x18, 0xe0, 0xb4, 0xaa, 0x89, 0x99, 0x8f, 0xcb, + 0x71, 0x34, 0x09, 0x1b, 0x6e, 0xe6, 0x87, 0x00, 0xb5, 0xba, 0x70, 0x8a, + 0x29, 0x3d, 0x9a, 0x06, 0x18, 0x2d, 0x66, 0x5e, 0x61, 0x37, 0xeb, 0xdd, + 0x5e, 0xc8, 0x28, 0x92, 0x05, 0x30, 0xfd, 0xb8, 0x65, 0xb1, 0x7f, 0xbf, + 0x2d, 0x55, 0x12, 0x91, 0xc1, 0x02, 0x81, 0x81, 0x00, 0xda, 0x65, 0xda, + 0x38, 0x7c, 0x18, 0xfb, 0x00, 0x11, 0x60, 0xeb, 0x37, 0x65, 0xb8, 0x83, + 0x62, 0x88, 0xc4, 0x3a, 0x4e, 0x64, 0x6a, 0xf3, 0x3e, 0x4e, 0xc0, 0x34, + 0x19, 0x8a, 0xcb, 0x4a, 0xca, 0x2f, 0x5d, 0x50, 0x7a, 0xac, 0xf7, 0x9e, + 0x87, 0x5a, 0xfc, 0x4d, 0x49, 0xd7, 0xf9, 0x21, 0xf5, 0x0b, 0x6f, 0x57, + 0x41, 0x3d, 0x8f, 0xb8, 0xec, 0x7f, 0xcc, 0x92, 0x09, 0xbe, 0xd3, 0xa4, + 0xc3, 0x14, 0x85, 0x21, 0x5d, 0x05, 0xa3, 0xaa, 0x20, 0xf6, 0x62, 0x44, + 0x50, 0x03, 0x5e, 0x53, 0x4a, 0xcd, 0x6a, 0xb6, 0x65, 0x8e, 0x4e, 0x4b, + 0x3f, 0x25, 0xc6, 0x16, 0x31, 0xf5, 0x99, 0x13, 0x77, 0x42, 0xda, 0xdc, + 0x70, 0x4d, 0x65, 0xb0, 0x99, 0x0f, 0xdf, 0x5a, 0xb1, 0x45, 0xf0, 0xb9, + 0x8e, 0xa0, 0xae, 0x4f, 0x4d, 0x65, 0x09, 0x84, 0xb5, 0x38, 0x29, 0xbf, + 0x69, 0xe0, 0x88, 0x1f, 0x27, 0x02, 0x81, 0x81, 0x00, 0xd3, 0x2a, 0x59, + 0xec, 0x28, 0xc3, 0x0d, 0x4f, 0x92, 0x96, 0xca, 0x67, 0x94, 0xfc, 0x2e, + 0xa6, 0x86, 0x68, 0x45, 0x53, 0x92, 0xcc, 0x86, 0x7f, 0x8a, 0xe1, 0x5d, + 0xe8, 0x1d, 0x9e, 0xbb, 0x1e, 0x00, 0x26, 0x1d, 0x80, 0x12, 0xff, 0x9c, + 0x11, 0x0a, 0xbd, 0xa6, 0xc3, 0x8d, 0x48, 0xda, 0xfc, 0x10, 0xf7, 0x7a, + 0x16, 0x07, 0x15, 0xa0, 0x3a, 0xd3, 0x94, 0xfb, 0x52, 0x87, 0x39, 0xee, + 0xe7, 0xc4, 0x26, 0x49, 0x16, 0xc6, 0xc0, 0x83, 0x25, 0xbf, 0x6a, 0x4e, + 0x8c, 0x0b, 0x10, 0x85, 0x66, 0xab, 0x7e, 0xae, 0xac, 0x4c, 0x69, 0x3c, + 0x44, 0xeb, 0xcd, 0xe9, 0xf6, 0x64, 0x8b, 0x4a, 0xd8, 0x6a, 0x4d, 0x6d, + 0x47, 0xa9, 0xb8, 0x55, 0x72, 0xc1, 0xfd, 0xf4, 0x81, 0x4c, 0x66, 0xbe, + 0x49, 0xf2, 0x75, 0x4f, 0x80, 0xf1, 0x20, 0x38, 0xb8, 0x6a, 0x1b, 0x75, + 0x41, 0x30, 0x0f, 0x1b, 0x3f, 0x02, 0x81, 0x80, 0x09, 0x35, 0xfa, 0x7a, + 0x1f, 0x61, 0xbe, 0x54, 0x46, 0x67, 0x5c, 0x04, 0x3e, 0x1a, 0x06, 0x10, + 0x85, 0xcc, 0x20, 0xd9, 0x65, 0x8a, 0xcd, 0x2f, 0x77, 0x8a, 0xcb, 0xa7, + 0xb8, 0x1e, 0xd2, 0xcc, 0xac, 0x2a, 0xb7, 0x56, 0x35, 0x2d, 0x4c, 0x56, + 0x51, 0x14, 0x0a, 0xfe, 0x6e, 0x49, 0x67, 0x91, 0x3a, 0x26, 0x3b, 0xfb, + 0xd8, 0x68, 0xd3, 0x57, 0xc6, 0x1c, 0x0e, 0x9c, 0xb2, 0x9b, 0xa2, 0x7b, + 0x47, 0xc6, 0x45, 0x9d, 0xf2, 0xba, 0xf0, 0x55, 0xeb, 0x8e, 0x41, 0x6b, + 0x4e, 0x79, 0x0f, 0xf2, 0x3b, 0xaf, 0xa0, 0x79, 0xb0, 0x02, 0xc5, 0x51, + 0xa8, 0x7a, 0x2e, 0x3d, 0x75, 0x2a, 0x3b, 0x93, 0xf0, 0x11, 0xe2, 0xf2, + 0x29, 0x91, 0x7c, 0x5d, 0x38, 0x3a, 0x27, 0x4d, 0x0a, 0xb2, 0x18, 0x61, + 0x57, 0x8d, 0x82, 0x72, 0xb5, 0x2c, 0x2d, 0x98, 0xa7, 0x01, 0xbb, 0xbc, + 0xef, 0x67, 0x4e, 0x49, 0x02, 0x81, 0x81, 0x00, 0xb2, 0x70, 0x53, 0x54, + 0x70, 0x8d, 0x82, 0xad, 0xff, 0x1d, 0x55, 0x24, 0x7a, 0x8d, 0x2f, 0x8e, + 0xa0, 0x7d, 0x74, 0x37, 0xcf, 0x10, 0xed, 0x86, 0xd1, 0x80, 0xe7, 0xad, + 0xc1, 0x79, 0xe4, 0x7c, 0xd1, 0x7b, 0x63, 0xea, 0x5a, 0x23, 0x8d, 0x6a, + 0x09, 0x3d, 0x81, 0xb2, 0x35, 0xad, 0x9e, 0xfe, 0xea, 0x07, 0x76, 0x2f, + 0x2f, 0x05, 0x63, 0x44, 0xd2, 0x8e, 0x4e, 0x61, 0xca, 0xcb, 0x75, 0xca, + 0x7b, 0xc2, 0x2e, 0x79, 0x04, 0xb2, 0xa1, 0x20, 0x40, 0xc4, 0x40, 0x63, + 0xae, 0xe5, 0xe3, 0x14, 0x83, 0x4e, 0xa5, 0xa4, 0x0b, 0x5d, 0xd2, 0x04, + 0x1b, 0x8f, 0x01, 0x69, 0xa8, 0x44, 0xdc, 0x96, 0x4c, 0x1d, 0xe9, 0x7e, + 0x69, 0x38, 0xcf, 0x5c, 0x0d, 0xf9, 0xdf, 0xa7, 0x73, 0x3c, 0x4f, 0x08, + 0x85, 0xce, 0x03, 0xc4, 0xdd, 0xfd, 0x70, 0x70, 0xc5, 0x99, 0x36, 0x58, + 0x43, 0x98, 0x40, 0x59, 0x02, 0x81, 0x81, 0x00, 0xd5, 0xaa, 0xfb, 0xec, + 0x8d, 0xc6, 0xdd, 0xfa, 0x2b, 0x5a, 0x24, 0xd0, 0xda, 0x58, 0xbd, 0x87, + 0x92, 0x1a, 0x29, 0x62, 0x13, 0x1d, 0x4b, 0x79, 0x1b, 0xbe, 0x79, 0x7d, + 0xad, 0x79, 0xca, 0x17, 0x75, 0xda, 0xe8, 0x32, 0xe8, 0xa0, 0x9e, 0xa8, + 0x77, 0x53, 0xac, 0x38, 0xd6, 0xeb, 0xe6, 0x22, 0x65, 0xc4, 0xaa, 0x4c, + 0xc8, 0xd0, 0x33, 0x1a, 0x1e, 0xbe, 0xbd, 0x73, 0x09, 0x4a, 0xfa, 0x85, + 0x5c, 0xf3, 0x0c, 0x9c, 0x81, 0x56, 0x30, 0xa7, 0xf7, 0x9b, 0xf4, 0x92, + 0x9c, 0x6b, 0x93, 0x6a, 0x00, 0x33, 0xdc, 0x2f, 0x54, 0x1e, 0x78, 0xd4, + 0x97, 0xec, 0x24, 0xa2, 0xdb, 0x3d, 0x03, 0x33, 0x09, 0xb2, 0x2c, 0x03, + 0x05, 0x40, 0xde, 0x52, 0xf2, 0x9b, 0xfa, 0x00, 0x8d, 0x4b, 0xfe, 0x5b, + 0x9b, 0x9c, 0x73, 0xad, 0xfb, 0x7a, 0x00, 0x42, 0x62, 0x9e, 0xa0, 0x95, + 0x55, 0x50, 0x32, 0x87 +}; +static unsigned int enc_key_len = 1192; +const struct bootutil_key bootutil_enc_key = { + .key = enc_key, + .len = &enc_key_len, +}; diff --git a/bootloader/mcuboot/ci/mynewt_run.sh b/bootloader/mcuboot/ci/mynewt_run.sh new file mode 100644 index 0000000..6639662 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_run.sh @@ -0,0 +1,33 @@ +#!/bin/bash -x + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +export PATH=$HOME/bin:$PATH +pwd + +for target in $(ls ci/mynewt_targets); do + newt build $target + [[ $? -ne 0 ]] && exit 1 +done + +mkdir targets +cp -r repos/apache-mynewt-core/targets/unittest targets +newt test boot/boot_serial +[[ $? -ne 0 ]] && exit 1 + +exit 0 diff --git a/bootloader/mcuboot/ci/mynewt_targets/basic/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/basic/pkg.yml new file mode 100644 index 0000000..736f278 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/basic/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/basic" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" diff --git a/bootloader/mcuboot/ci/mynewt_targets/basic/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/basic/syscfg.yml new file mode 100644 index 0000000..e404468 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/basic/syscfg.yml @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + BOOT_SERIAL: 0 + BOOT_SERIAL_DETECT_PIN: 11 + BOOT_SERIAL_DETECT_PIN_VAL: 0 + BOOT_SERIAL_REPORT_PIN: 13 + BOOTUTIL_VALIDATE_SLOT0: 0 + BOOTUTIL_MAX_IMG_SECTORS: 256 + BOOTUTIL_SIGN_EC256: 0 + BOOTUTIL_SIGN_RSA: 0 + BOOTUTIL_ENCRYPT_RSA: 0 + BOOTUTIL_ENCRYPT_KW: 0 + BOOTUTIL_USE_MBED_TLS: 0 + BOOTUTIL_USE_TINYCRYPT: 1 + BOOTUTIL_OVERWRITE_ONLY: 0 + BOOTUTIL_OVERWRITE_ONLY_FAST: 1 + BOOTUTIL_HAVE_LOGGING: 0 + BOOTUTIL_NO_LOGGING: 1 + BOOTUTIL_LOG_LEVEL: 'BOOTUTIL_LOG_LEVEL_INFO' + CONSOLE_COMPAT: 1 + CONSOLE_INPUT: 0 + CONSOLE_UART: 0 + CONSOLE_RTT: 0 + OS_CPUTIME_TIMER_NUM: 0 + TIMER_0: 1 + UART_0: 0 + BOOTUTIL_BOOTSTRAP: 0 + MBEDTLS_NIST_KW_C: 0 diff --git a/bootloader/mcuboot/ci/mynewt_targets/basic/target.yml b/bootloader/mcuboot/ci/mynewt_targets/basic/target.yml new file mode 100644 index 0000000..6cb85a9 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/basic/target.yml @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" diff --git a/bootloader/mcuboot/ci/mynewt_targets/bootserial/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/bootserial/pkg.yml new file mode 100644 index 0000000..b7dd9e2 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/bootserial/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/bootserial" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" diff --git a/bootloader/mcuboot/ci/mynewt_targets/bootserial/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/bootserial/syscfg.yml new file mode 100644 index 0000000..304cca5 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/bootserial/syscfg.yml @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOT_SERIAL: 1 + UART_0: 1 diff --git a/bootloader/mcuboot/ci/mynewt_targets/bootserial/target.yml b/bootloader/mcuboot/ci/mynewt_targets/bootserial/target.yml new file mode 100644 index 0000000..6cb85a9 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/bootserial/target.yml @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" diff --git a/bootloader/mcuboot/ci/mynewt_targets/ecdsa/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/ecdsa/pkg.yml new file mode 100644 index 0000000..81bc2b4 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/ecdsa/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/ecdsa" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" diff --git a/bootloader/mcuboot/ci/mynewt_targets/ecdsa/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/ecdsa/syscfg.yml new file mode 100644 index 0000000..5162df8 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/ecdsa/syscfg.yml @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOTUTIL_VALIDATE_SLOT0: 1 + BOOTUTIL_SIGN_EC256: 1 diff --git a/bootloader/mcuboot/ci/mynewt_targets/ecdsa/target.yml b/bootloader/mcuboot/ci/mynewt_targets/ecdsa/target.yml new file mode 100644 index 0000000..ac122f8 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/ecdsa/target.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" +target.key_file: "@mcuboot/root-ec-p256.pem" diff --git a/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/pkg.yml new file mode 100644 index 0000000..d56caeb --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/pkg.yml @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/ecdsa_kw" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" + - "@mcuboot/keys/enc_kw" diff --git a/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/syscfg.yml new file mode 100644 index 0000000..6065e56 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/syscfg.yml @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOTUTIL_VALIDATE_SLOT0: 1 + BOOTUTIL_SIGN_EC256: 1 + BOOTUTIL_ENCRYPT_KW: 1 diff --git a/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/target.yml b/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/target.yml new file mode 100644 index 0000000..ac122f8 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/ecdsa_kw/target.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" +target.key_file: "@mcuboot/root-ec-p256.pem" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa/pkg.yml new file mode 100644 index 0000000..8e3264c --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/rsa" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa/syscfg.yml new file mode 100644 index 0000000..b40d0ab --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa/syscfg.yml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOTUTIL_VALIDATE_SLOT0: 1 + BOOTUTIL_SIGN_EC256: 0 + BOOTUTIL_SIGN_RSA: 1 + BOOTUTIL_USE_MBED_TLS: 1 + BOOTUTIL_USE_TINYCRYPT: 0 diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa/target.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa/target.yml new file mode 100644 index 0000000..cbfc8c7 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa/target.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" +target.key_file: "@mcuboot/root-rsa-2048.pem" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/pkg.yml new file mode 100644 index 0000000..99af67b --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/pkg.yml @@ -0,0 +1,28 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/rsa_kw" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" + - "@mcuboot/keys/enc_kw" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/syscfg.yml new file mode 100644 index 0000000..97dfcb6 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/syscfg.yml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOTUTIL_VALIDATE_SLOT0: 1 + BOOTUTIL_SIGN_EC256: 0 + BOOTUTIL_SIGN_RSA: 1 + BOOTUTIL_USE_MBED_TLS: 1 + BOOTUTIL_USE_TINYCRYPT: 0 + BOOTUTIL_ENCRYPT_KW: 1 + MBEDTLS_NIST_KW_C: 1 + MBEDTLS_CIPHER_MODE_CTR: 1 diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/target.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/target.yml new file mode 100644 index 0000000..cbfc8c7 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_kw/target.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" +target.key_file: "@mcuboot/root-rsa-2048.pem" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/pkg.yml new file mode 100644 index 0000000..1225607 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/rsa_overwriteonly" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/syscfg.yml new file mode 100644 index 0000000..8dabbe8 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/syscfg.yml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOTUTIL_VALIDATE_SLOT0: 1 + BOOTUTIL_SIGN_EC256: 0 + BOOTUTIL_SIGN_RSA: 1 + BOOTUTIL_USE_MBED_TLS: 1 + BOOTUTIL_USE_TINYCRYPT: 0 + BOOTUTIL_OVERWRITE_ONLY: 1 diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/target.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/target.yml new file mode 100644 index 0000000..cbfc8c7 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_overwriteonly/target.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" +target.key_file: "@mcuboot/root-rsa-2048.pem" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/pkg.yml new file mode 100644 index 0000000..fc69f10 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/pkg.yml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/rsa_rsaoaep" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" + - "@mcuboot/keys/enc_rsa" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/syscfg.yml new file mode 100644 index 0000000..29d7140 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/syscfg.yml @@ -0,0 +1,29 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOTUTIL_VALIDATE_SLOT0: 1 + BOOTUTIL_SIGN_EC256: 0 + BOOTUTIL_SIGN_RSA: 1 + BOOTUTIL_USE_MBED_TLS: 1 + BOOTUTIL_USE_TINYCRYPT: 0 + BOOTUTIL_ENCRYPT_RSA: 1 + MBEDTLS_CIPHER_MODE_CTR: 1 diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/target.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/target.yml new file mode 100644 index 0000000..cbfc8c7 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep/target.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" +target.key_file: "@mcuboot/root-rsa-2048.pem" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/pkg.yml new file mode 100644 index 0000000..d0b3619 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/pkg.yml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/rsa_rsaoaep_bootstrap" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" + - "@mcuboot/keys/enc_rsa" diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/syscfg.yml new file mode 100644 index 0000000..ddadbbf --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/syscfg.yml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$import: + - '@mcuboot/ci/mynewt_targets/basic/syscfg.yml' + +syscfg.vals: + BOOTUTIL_VALIDATE_SLOT0: 1 + BOOTUTIL_SIGN_EC256: 0 + BOOTUTIL_SIGN_RSA: 1 + BOOTUTIL_USE_MBED_TLS: 1 + BOOTUTIL_USE_TINYCRYPT: 0 + BOOTUTIL_ENCRYPT_RSA: 1 + BOOTUTIL_BOOTSTRAP: 1 + MBEDTLS_CIPHER_MODE_CTR: 1 diff --git a/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/target.yml b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/target.yml new file mode 100644 index 0000000..cbfc8c7 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/rsa_rsaoaep_bootstrap/target.yml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" +target.key_file: "@mcuboot/root-rsa-2048.pem" diff --git a/bootloader/mcuboot/ci/mynewt_targets/swap_move/pkg.yml b/bootloader/mcuboot/ci/mynewt_targets/swap_move/pkg.yml new file mode 100644 index 0000000..54924ed --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/swap_move/pkg.yml @@ -0,0 +1,27 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: "targets/swap_move" +pkg.type: "target" +pkg.description: +pkg.author: +pkg.homepage: + +pkg.deps: + - "@mcuboot/boot/mynewt" diff --git a/bootloader/mcuboot/ci/mynewt_targets/swap_move/syscfg.yml b/bootloader/mcuboot/ci/mynewt_targets/swap_move/syscfg.yml new file mode 100644 index 0000000..57c4d54 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/swap_move/syscfg.yml @@ -0,0 +1,46 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.vals: + BOOT_SERIAL: 0 + BOOT_SERIAL_DETECT_PIN: 11 + BOOT_SERIAL_DETECT_PIN_VAL: 0 + BOOT_SERIAL_REPORT_PIN: 13 + BOOTUTIL_VALIDATE_SLOT0: 0 + BOOTUTIL_MAX_IMG_SECTORS: 256 + BOOTUTIL_SWAP_USING_MOVE: 1 + BOOTUTIL_SIGN_EC256: 0 + BOOTUTIL_SIGN_RSA: 0 + BOOTUTIL_ENCRYPT_RSA: 0 + BOOTUTIL_ENCRYPT_KW: 0 + BOOTUTIL_USE_MBED_TLS: 0 + BOOTUTIL_USE_TINYCRYPT: 1 + BOOTUTIL_OVERWRITE_ONLY: 0 + BOOTUTIL_OVERWRITE_ONLY_FAST: 1 + BOOTUTIL_HAVE_LOGGING: 0 + BOOTUTIL_NO_LOGGING: 1 + BOOTUTIL_LOG_LEVEL: 'BOOTUTIL_LOG_LEVEL_INFO' + CONSOLE_COMPAT: 1 + CONSOLE_INPUT: 0 + CONSOLE_UART: 0 + CONSOLE_RTT: 0 + OS_CPUTIME_TIMER_NUM: 0 + TIMER_0: 1 + UART_0: 0 + BOOTUTIL_BOOTSTRAP: 0 + MBEDTLS_NIST_KW_C: 0 diff --git a/bootloader/mcuboot/ci/mynewt_targets/swap_move/target.yml b/bootloader/mcuboot/ci/mynewt_targets/swap_move/target.yml new file mode 100644 index 0000000..6cb85a9 --- /dev/null +++ b/bootloader/mcuboot/ci/mynewt_targets/swap_move/target.yml @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +target.app: "@mcuboot/boot/mynewt" +target.bsp: "@apache-mynewt-core/hw/bsp/nordic_pca10056" +target.build_profile: "optimized" diff --git a/bootloader/mcuboot/ci/requirements.txt b/bootloader/mcuboot/ci/requirements.txt new file mode 100644 index 0000000..aab392a --- /dev/null +++ b/bootloader/mcuboot/ci/requirements.txt @@ -0,0 +1 @@ +tomli diff --git a/bootloader/mcuboot/ci/sim_install.sh b/bootloader/mcuboot/ci/sim_install.sh new file mode 100644 index 0000000..8ec96cf --- /dev/null +++ b/bootloader/mcuboot/ci/sim_install.sh @@ -0,0 +1,19 @@ +#!/bin/bash -x + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +pip3 install --user -r ci/requirements.txt + +pushd sim && cargo fetch +[[ $? -ne 0 ]] && exit 1 +popd diff --git a/bootloader/mcuboot/ci/sim_run.sh b/bootloader/mcuboot/ci/sim_run.sh new file mode 100644 index 0000000..7a9ff2d --- /dev/null +++ b/bootloader/mcuboot/ci/sim_run.sh @@ -0,0 +1,65 @@ +#!/bin/bash -x + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +GET_FEATURES="$(pwd)/ci/get_features.py" +CARGO_TOML="$(pwd)/sim/Cargo.toml" + +pushd sim + +all_features="$(${GET_FEATURES} ${CARGO_TOML})" +[ $? -ne 0 ] && exit 1 + +EXIT_CODE=0 + +if [[ ! -z $SINGLE_FEATURES ]]; then + if [[ $SINGLE_FEATURES =~ "none" ]]; then + echo "Running cargo with no features" + time cargo test --no-run + time cargo test + rc=$? && [ $rc -ne 0 ] && EXIT_CODE=$rc + fi + + for feature in $all_features; do + if [[ $SINGLE_FEATURES =~ $feature ]]; then + echo "Running cargo for feature=\"${feature}\"" + time cargo test --no-run --features $feature + time cargo test --features $feature + rc=$? && [ $rc -ne 0 ] && EXIT_CODE=$rc + fi + done +fi + +if [[ ! -z $MULTI_FEATURES ]]; then + IFS=',' + read -ra multi_features <<< "$MULTI_FEATURES" + + # psa crypto tests require single thread mode + TEST_ARGS='' + for features in "${multi_features[@]}"; do + if [[ $features =~ "psa" ]]; then + TEST_ARGS='--test-threads=1' + break + fi + done + + for features in "${multi_features[@]}"; do + echo "Running cargo for features=\"${features}\"" + time cargo test --no-run --features "$features" -- $TEST_ARGS + time cargo test --features "$features" -- $TEST_ARGS + rc=$? && [ $rc -ne 0 ] && EXIT_CODE=$rc + done +fi + +popd +exit $EXIT_CODE diff --git a/bootloader/mcuboot/docs/.gitignore b/bootloader/mcuboot/docs/.gitignore new file mode 100644 index 0000000..cf1879a --- /dev/null +++ b/bootloader/mcuboot/docs/.gitignore @@ -0,0 +1,2 @@ +.sass-cache/ +_site/ diff --git a/bootloader/mcuboot/docs/CNAME b/bootloader/mcuboot/docs/CNAME new file mode 100644 index 0000000..64ca4fe --- /dev/null +++ b/bootloader/mcuboot/docs/CNAME @@ -0,0 +1 @@ +docs.mcuboot.com \ No newline at end of file diff --git a/bootloader/mcuboot/docs/Gemfile b/bootloader/mcuboot/docs/Gemfile new file mode 100644 index 0000000..b88481b --- /dev/null +++ b/bootloader/mcuboot/docs/Gemfile @@ -0,0 +1,26 @@ +source "https://rubygems.org" + +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve + +# This is the default theme for new Jekyll sites. You may change this to anything you like. +# gem "jekyll-theme-cayman", "~> 0.1" + +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +gem "github-pages", group: :jekyll_plugins + +# If you have any plugins, put them here! +# group :jekyll_plugins do +# gem "jekyll-feed", "~> 0.6" +# end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +gem "jemoji", "~> 0.12.0" + +gem "webrick", "~> 1.8" diff --git a/bootloader/mcuboot/docs/Gemfile.lock b/bootloader/mcuboot/docs/Gemfile.lock new file mode 100644 index 0000000..9ee5e43 --- /dev/null +++ b/bootloader/mcuboot/docs/Gemfile.lock @@ -0,0 +1,266 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (7.0.7.2) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + addressable (2.8.5) + public_suffix (>= 2.0.2, < 6.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.11.1) + colorator (1.1.0) + commonmarker (0.23.10) + concurrent-ruby (1.2.2) + dnsruby (1.70.0) + simpleidn (~> 0.2.1) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + ethon (0.16.0) + ffi (>= 1.15.0) + eventmachine (1.2.7) + execjs (2.8.1) + faraday (2.7.10) + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.2) + ffi (1.15.5) + forwardable-extended (2.6.0) + gemoji (3.0.1) + github-pages (228) + github-pages-health-check (= 1.17.9) + jekyll (= 3.9.3) + jekyll-avatar (= 0.7.0) + jekyll-coffeescript (= 1.1.1) + jekyll-commonmark-ghpages (= 0.4.0) + jekyll-default-layout (= 0.1.4) + jekyll-feed (= 0.15.1) + jekyll-gist (= 1.5.0) + jekyll-github-metadata (= 2.13.0) + jekyll-include-cache (= 0.2.1) + jekyll-mentions (= 1.6.0) + jekyll-optional-front-matter (= 0.3.2) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.3.0) + jekyll-redirect-from (= 0.16.0) + jekyll-relative-links (= 0.6.1) + jekyll-remote-theme (= 0.4.3) + jekyll-sass-converter (= 1.5.2) + jekyll-seo-tag (= 2.8.0) + jekyll-sitemap (= 1.4.0) + jekyll-swiss (= 1.0.0) + jekyll-theme-architect (= 0.2.0) + jekyll-theme-cayman (= 0.2.0) + jekyll-theme-dinky (= 0.2.0) + jekyll-theme-hacker (= 0.2.0) + jekyll-theme-leap-day (= 0.2.0) + jekyll-theme-merlot (= 0.2.0) + jekyll-theme-midnight (= 0.2.0) + jekyll-theme-minimal (= 0.2.0) + jekyll-theme-modernist (= 0.2.0) + jekyll-theme-primer (= 0.6.0) + jekyll-theme-slate (= 0.2.0) + jekyll-theme-tactile (= 0.2.0) + jekyll-theme-time-machine (= 0.2.0) + jekyll-titles-from-headings (= 0.5.3) + jemoji (= 0.12.0) + kramdown (= 2.3.2) + kramdown-parser-gfm (= 1.1.0) + liquid (= 4.0.4) + mercenary (~> 0.3) + minima (= 2.5.1) + nokogiri (>= 1.13.6, < 2.0) + rouge (= 3.26.0) + terminal-table (~> 1.4) + github-pages-health-check (1.17.9) + addressable (~> 2.3) + dnsruby (~> 1.60) + octokit (~> 4.0) + public_suffix (>= 3.0, < 5.0) + typhoeus (~> 1.3) + html-pipeline (2.14.3) + activesupport (>= 2) + nokogiri (>= 1.4) + http_parser.rb (0.8.0) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + jekyll (3.9.3) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (>= 0.7, < 2) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (>= 1.17, < 3) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-avatar (0.7.0) + jekyll (>= 3.0, < 5.0) + jekyll-coffeescript (1.1.1) + coffee-script (~> 2.2) + coffee-script-source (~> 1.11.1) + jekyll-commonmark (1.4.0) + commonmarker (~> 0.22) + jekyll-commonmark-ghpages (0.4.0) + commonmarker (~> 0.23.7) + jekyll (~> 3.9.0) + jekyll-commonmark (~> 1.4.0) + rouge (>= 2.0, < 5.0) + jekyll-default-layout (0.1.4) + jekyll (~> 3.0) + jekyll-feed (0.15.1) + jekyll (>= 3.7, < 5.0) + jekyll-gist (1.5.0) + octokit (~> 4.2) + jekyll-github-metadata (2.13.0) + jekyll (>= 3.4, < 5.0) + octokit (~> 4.0, != 4.4.0) + jekyll-include-cache (0.2.1) + jekyll (>= 3.7, < 5.0) + jekyll-mentions (1.6.0) + html-pipeline (~> 2.3) + jekyll (>= 3.7, < 5.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.16.0) + jekyll (>= 3.3, < 5.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-remote-theme (0.4.3) + addressable (~> 2.0) + jekyll (>= 3.5, < 5.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-seo-tag (2.8.0) + jekyll (>= 3.8, < 5.0) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-swiss (1.0.0) + jekyll-theme-architect (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-cayman (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-dinky (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-hacker (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-leap-day (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-merlot (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-midnight (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (0.6.0) + jekyll (> 3.5, < 5.0) + jekyll-github-metadata (~> 2.9) + jekyll-seo-tag (~> 2.0) + jekyll-theme-slate (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-tactile (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-theme-time-machine (0.2.0) + jekyll (> 3.5, < 5.0) + jekyll-seo-tag (~> 2.0) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + jemoji (0.12.0) + gemoji (~> 3.0) + html-pipeline (~> 2.2) + jekyll (>= 3.0, < 5.0) + kramdown (2.3.2) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.8.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.3.6) + mini_portile2 (2.8.6) + minima (2.5.1) + jekyll (>= 3.5, < 5.0) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + minitest (5.19.0) + nokogiri (1.16.5) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (4.0.7) + racc (1.7.3) + rb-fsevent (0.11.2) + rb-inotify (0.10.1) + ffi (~> 1.0) + rexml (3.2.8) + strscan (>= 3.0.9) + rouge (3.26.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + simpleidn (0.2.1) + unf (~> 0.1.4) + strscan (3.1.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) + webrick (1.8.1) + +PLATFORMS + ruby + +DEPENDENCIES + github-pages + jemoji (~> 0.12.0) + tzinfo-data + webrick (~> 1.8) + +BUNDLED WITH + 1.17.2 diff --git a/bootloader/mcuboot/docs/PORTING.md b/bootloader/mcuboot/docs/PORTING.md new file mode 100644 index 0000000..4804096 --- /dev/null +++ b/bootloader/mcuboot/docs/PORTING.md @@ -0,0 +1,195 @@ +# Porting how-to + +This document describes the requirements and necessary steps required to port +`MCUboot` to a new target `OS`. + +# Requirements + +* `MCUboot` requires a configuration file, which can be included as + mcuboot_config/mcuboot_config.h, which configures various options + (that begin with MCUBOOT_). + +* `MCUboot` requires that the target provides a `flash` API with ability to + get the flash's minimum write size, and read/write/erase individual sectors. + +* `MCUboot` doesn't bundle a cryptographic library, which means the target + OS must already have it bundled. The supported libraries at the moment are + either `Mbed TLS` or the set `tinycrypt` + `Mbed TLS` (where `Mbed TLS` is + used to provide functionality not existing in `tinycrypt`). + +# Steps to port + +## Main app and calling the bootloader + +From the perspective of the target OS, the bootloader can be seen as a library, +so an entry point must be provided. This is likely a typical `app` for the +target OS, and it must call the following function to run the bootloader: + +```c +int boot_go(struct boot_rsp *rsp); +``` + +This function is located at `boot/bootutil/loader.c` and receives a `struct +boot_rsp` pointer. The `struct boot_rsp` is defined as: + +```c +struct boot_rsp { + /** A pointer to the header of the image to be executed. */ + const struct image_header *br_hdr; + + /** + * The flash offset of the image to execute. Indicates the position of + * the image header. + */ + uint8_t br_flash_id; + uint32_t br_image_addr; +}; +``` + +After running the management functions of the bootloader, `boot_go` returns +an initialized `boot_rsp` which has pointers to the location of the image +where the target firmware is located which can be used to jump to. + +## Configuration file + +You must provide a file, mcuboot_config/mcuboot_config.h. This is +included by several files in the "library" portion of MCUboot; it +provides preprocessor definitions that configure the library's +build. + +See the file samples/mcuboot_config/mcuboot_config.template.h for a +starting point and more information. This is a good place to convert +settings in your environment's configuration system to those required +by MCUboot. For example, Mynewt uses MYNEWT_VAL() and Zephyr uses +Kconfig; these configuration systems are converted to MCUBOOT_ options +in the following files: + +- boot/zephyr/include/mcuboot_config/mcuboot_config.h +- boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h + +## Flash Map + +The bootloader requires to be able to address flash regions where the code +for MCUboot and images of applications are stored, in system-agnostic way. +For that purpose the MCUboot uses ID, which is integer (uint8_t) number +that should uniquely identify each flash region. +Such flash regions are served by object of `const struct flash_area` type while +layout of these objects is gathered under `flash_map`. +The common code of MCUboot, that is non-system specific, does not directly +access contents of that object and never modifies it, instead it calls +`flash_area_` API to perform any actions on that object. +This way systems are free to implement internal logic of flash map or define +`struct flash_area` as they wish; the only restriction is that ID should be +uniquely tied to region characterized by device, offset and size. + +Changes to common MCUboot code should not affect system specific internals +of flash map, on the other side system specific code, within MCUboot, is +is not restricted from directly accessing `struct flash_area` elements. + + +An implementation of `struct flash_area` may take form of: +```c +struct flash_area { + uint8_t fa_id; /** The slot/scratch identification */ + uint8_t fa_device_id; /** The device id (usually there's only one) */ + uint16_t pad16; + uint32_t fa_off; /** The flash offset from the beginning */ + uint32_t fa_size; /** The size of this sector */ +}; +``` +The above example of structure hold all information that is currently required +by MCUboot, although the MCUboot will not be trying to access them directly, +instead a system is required to provide following mandatory getter functions: + +```c +/*< Obtains ID of the flash area characterized by `fa` */ +int flash_area_get_id(const struct flash_area *fa); +/*< Obtains ID of a device the flash area `fa` described region resides on */ +int flash_area_get_device_id(const struct flash_area *fa) +/*< Obtains offset, from the beginning of a device, the flash area described + * region starts at */ +uint32_t flash_area_get_off(const struct flash_area *fa) +/*< Obtains size, from the offset, of the flash area `fa` characterized region */ +uint32_t flash_area_get_size(const struct flash_area *fa) + +``` + +The MCUboot common code uses following defines that should be defined by system +specific header files and are used to identify destination of flash area by ID: + +```c +/* Independent from multiple image boot */ +#define FLASH_AREA_BOOTLOADER 0 +#define FLASH_AREA_IMAGE_SCRATCH 3 +``` +```c +/* Flash area IDs of the first image in case of multiple images */ +#define FLASH_AREA_IMAGE_PRIMARY 1 +#define FLASH_AREA_IMAGE_SECONDARY 2 +``` +```c +/* Flash area IDs of the second image in case of multiple images */ +#define FLASH_AREA_IMAGE_PRIMARY 5 +#define FLASH_AREA_IMAGE_SECONDARY 6 +``` + +The numbers, given above, are provided as an example and depend on system +implementation. + +The main, also required, set of API functions that perform operations on +flash characterized by `struct flash_area` objects is as follows: + +```c +/*< Opens the area for use. id is one of the `fa_id`s */ +int flash_area_open(uint8_t id, const struct flash_area **); +void flash_area_close(const struct flash_area *); +/*< Reads `len` bytes of flash memory at `off` to the buffer at `dst` */ +int flash_area_read(const struct flash_area *, uint32_t off, void *dst, + uint32_t len); +/*< Writes `len` bytes of flash memory at `off` from the buffer at `src` */ +int flash_area_write(const struct flash_area *, uint32_t off, + const void *src, uint32_t len); +/*< Erases `len` bytes of flash memory at `off` */ +int flash_area_erase(const struct flash_area *, uint32_t off, uint32_t len); +/*< Returns this `flash_area`s alignment */ +uint32_t flash_area_align(const struct flash_area *); +/*< What is value is read from erased flash bytes. */ +uint8_t flash_area_erased_val(const struct flash_area *); +/*< Given flash area ID, return info about sectors within the area. */ +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors); +/*< Returns the `fa_id` for slot, where slot is 0 (primary) or 1 (secondary). + `image_index` (0 or 1) is the index of the image. Image index is + relevant only when multi-image support support is enabled */ +int flash_area_id_from_multi_image_slot(int image_index, int slot); +/*< Returns the slot (0 for primary or 1 for secondary), for the supplied + `image_index` and `area_id`. `area_id` is unique and is represented by + `fa_id` in the `flash_area` struct. */ +int flash_area_id_to_multi_image_slot(int image_index, int area_id); +``` + +--- +***Note*** + +*As of writing, it is possible that MCUboot will open a flash area multiple times simultaneously (through nested calls to `flash_area_open`). As a result, MCUboot may call `flash_area_close` on a flash area that is still opened by another part of MCUboot. As a workaround when porting, it may be necessary to implement a counter of the number of times a given flash area has been opened by MCUboot. The `flash_area_close` implementation should only fully deinitialize the underlying flash area when the open counter is decremented to 0. See [this GitHub PR](https://github.com/mcu-tools/mcuboot/pull/894/) for a more detailed discussion.* + +--- + +## Memory management for Mbed TLS + +`Mbed TLS` employs dynamic allocation of memory, making use of the pair +`calloc/free`. If `Mbed TLS` is to be used for crypto, your target RTOS +needs to provide this pair of function. + +To configure the what functions are called when allocating/deallocating +memory `Mbed TLS` uses the following call: + +``` +int mbedtls_platform_set_calloc_free (void *(*calloc_func)(size_t, size_t), + void (*free_func)(void *)); +``` + +For reference see [Mbed TLS platform.h](https://tls.mbed.org/api/platform_8h.html). +If your system already provides functions with compatible signatures, those can +be used directly here, otherwise create new functions that glue to your +`calloc/free` implementations. diff --git a/bootloader/mcuboot/docs/SECURITY.md b/bootloader/mcuboot/docs/SECURITY.md new file mode 100644 index 0000000..f995385 --- /dev/null +++ b/bootloader/mcuboot/docs/SECURITY.md @@ -0,0 +1,56 @@ +# Project security policy + +The MCUboot team takes security, vulnerabilities, and weaknesses +seriously. + +## Reporting security issues + +The preferred way to report security issues with MCUboot is via the "Report a +security vulnerability" button on the main [security +page](https://github.com/mcu-tools/mcuboot/security). + +You can also directly contact the following maintainers of the project: + +- David Brown: davidb@davidb.org or david.brown@linaro.org +- Fabio Utzig: utzig@apache.org + +If you wish to send an encrypted email, you may use these PGP keys: + +``` + pub rsa4096 2011-10-14 [SC] + DAFD760825AE2636AEA9CB19E6BA9F5C5E54DF82 + uid [ultimate] David Brown + uid [ultimate] David Brown + sub rsa4096 2011-10-14 [E] +``` + +and + +``` + pub rsa4096 2017-07-28 [SC] + 126087C7E725625BC7E89CC7537097EDFD4A7339 + uid [ unknown] Fabio Utzig + uid [ unknown] Fabio Utzig + sub rsa4096 2017-07-28 [E] +``` + +Please include the word "SECURITY" as well as "MCUboot" in the subject +of any message. + +We will make our best effort to respond in a timely manner. Most +vulnerabilities found within published code will undergo an embargo of +90 days to allow time fixes to be developed and deployed. + +## Vulnerability advisories + +Vulnerability reports and published fixes will be reported as follows: + +- Issues will be entered into MCUboot's [security advisory + system](https://github.com/mcu-tools/mcuboot/security/advisories) on GitHub, with + the interested parties (including the reporter) added as viewers. + +- The release notes will contain a reference to any allocated CVE(s). + +- When the embargo is lifted, the security advisory page will be made + public, and the public CVE database will be updated with all + relevant information. diff --git a/bootloader/mcuboot/docs/SubmittingPatches.md b/bootloader/mcuboot/docs/SubmittingPatches.md new file mode 100644 index 0000000..4328272 --- /dev/null +++ b/bootloader/mcuboot/docs/SubmittingPatches.md @@ -0,0 +1,89 @@ +# Patch submission + +The development of MCUboot takes place in the [MCUboot GitHub +repository](https://github.com/mcu-tools/mcuboot). + +To submit patches, use GitHub pull requests. + +Each commit has to have, in the commit message, a "Signed-off-by" line +that mentions the author (and the committer, if that is different). You +must add this line at the end of the commit text, separated by a blank +line. You can also add a line linking the commit to a specific GitHub +issue, as this section supports multiple lines, similarly to RFC-2822. + +The supported trailer lines are structured as follows: + +- A line that indicates that the signer agrees to the "Developer +Certificate of Origin" located at the bottom of this page: + + ``` + Signed-off-by: Developer Name + ``` + +- A line that links this commit to specific GitHub issues, if present: + + ``` + Keyword #GH_issue_number + ``` + + For more details about linking a GitHub pull request to a GitHub issue, + see this [link] + (https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). + +## Release notes + +To facilitate creating release notes at release time, all non-trivial +changes must include a release note snippet in the pull request. +This can be either a separate commit, or as part of a single commit +(generally, if there are multiple commits to the pull request, the +release note snippet should be a separate commit, and a pull request +that is a single commit can include the release note snippet in that +commit). In either case, the release notes must be included in the +same PR that makes the given change. + +The release notes should be placed in the `docs/release-notes.d` +directory. Please see the readme file in that directory for specifics. + +## Developer certificate of origin + +The following is the "Developer Certificate of Origin": + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` diff --git a/bootloader/mcuboot/docs/_config.yml b/bootloader/mcuboot/docs/_config.yml new file mode 100644 index 0000000..be854e8 --- /dev/null +++ b/bootloader/mcuboot/docs/_config.yml @@ -0,0 +1,3 @@ +theme: jekyll-theme-cayman +plugins: + - jemoji diff --git a/bootloader/mcuboot/docs/design.md b/bootloader/mcuboot/docs/design.md new file mode 100644 index 0000000..8539ae0 --- /dev/null +++ b/bootloader/mcuboot/docs/design.md @@ -0,0 +1,1512 @@ + + +# Bootloader + +## [Summary](#summary) + +MCUboot comprises two packages: + +* The bootutil library (boot/bootutil) +* The boot application (each port has its own at boot/) + +The bootutil library performs most of the functions of a bootloader. In +particular, the piece that is missing is the final step of actually jumping to +the main image. This last step is instead implemented by the boot application. +Bootloader functionality is separated in this manner to enable unit testing of +the bootloader. A library can be unit tested, but an application can't. +Therefore, functionality is delegated to the bootutil library when possible. + +## [Limitations](#limitations) + +The bootloader currently only supports images with the following +characteristics: +* Built to run from flash. +* Built to run from a fixed location (i.e., not position-independent). + +## [Image format](#image-format) + +The following definitions describe the image format. + +``` c +#define IMAGE_MAGIC 0x96f3b83d + +#define IMAGE_HEADER_SIZE 32 + +struct image_version { + uint8_t iv_major; + uint8_t iv_minor; + uint16_t iv_revision; + uint32_t iv_build_num; +}; + +/** Image header. All fields are in little endian byte order. */ +struct image_header { + uint32_t ih_magic; + uint32_t ih_load_addr; + uint16_t ih_hdr_size; /* Size of image header (bytes). */ + uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */ + uint32_t ih_img_size; /* Does not include header. */ + uint32_t ih_flags; /* IMAGE_F_[...]. */ + struct image_version ih_ver; + uint32_t _pad1; +}; + +#define IMAGE_TLV_INFO_MAGIC 0x6907 +#define IMAGE_TLV_PROT_INFO_MAGIC 0x6908 + +/** Image TLV header. All fields in little endian. */ +struct image_tlv_info { + uint16_t it_magic; + uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */ +}; + +/** Image trailer TLV format. All fields in little endian. */ +struct image_tlv { + uint8_t it_type; /* IMAGE_TLV_[...]. */ + uint8_t _pad; + uint16_t it_len; /* Data length (not including TLV header). */ +}; + +/* + * Image header flags. + */ +#define IMAGE_F_PIC 0x00000001 /* Not supported. */ +#define IMAGE_F_ENCRYPTED_AES128 0x00000004 /* Encrypted using AES128. */ +#define IMAGE_F_ENCRYPTED_AES256 0x00000008 /* Encrypted using AES256. */ +#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */ +#define IMAGE_F_RAM_LOAD 0x00000020 + +/* + * Image trailer TLV types. + */ +#define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */ +#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */ +#define IMAGE_TLV_RSA2048_PSS 0x20 /* RSA2048 of hash output */ +#define IMAGE_TLV_ECDSA224 0x21 /* ECDSA of hash output - Not supported anymore */ +#define IMAGE_TLV_ECDSA_SIG 0x22 /* ECDSA of hash output */ +#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */ +#define IMAGE_TLV_ED25519 0x24 /* ED25519 of hash output */ +#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */ +#define IMAGE_TLV_ENC_KW 0x31 /* Key encrypted with AES-KW-128 or + 256 */ +#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-P256 */ +#define IMAGE_TLV_ENC_X25519 0x33 /* Key encrypted with ECIES-X25519 */ +#define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */ +#define IMAGE_TLV_SEC_CNT 0x50 /* security counter */ +``` + +Optional type-length-value records (TLVs) containing image metadata are placed +after the end of the image. + +The `ih_protect_tlv_size` field indicates the length of the protected TLV area. +If protected TLVs are present then a TLV info header with magic equal to +`IMAGE_TLV_PROT_INFO_MAGIC` must be present and the protected TLVs (plus the +info header itself) have to be included in the hash calculation. Otherwise the +hash is only calculated over the image header and the image itself. In this +case the value of the `ih_protect_tlv_size` field is 0. + +The `ih_hdr_size` field indicates the length of the header, and therefore the +offset of the image itself. This field provides for backwards compatibility in +case of changes to the format of the image header. + +## [Flash map](#flash-map) + +A device's flash is partitioned according to its _flash map_. At a high +level, the flash map maps numeric IDs to _flash areas_. A flash area is a +region of disk with the following properties: +1. An area can be fully erased without affecting any other areas. +2. A write to one area does not restrict writes to other areas. + +The bootloader uses the following flash area IDs: +```c +/* Independent from multiple image boot */ +#define FLASH_AREA_BOOTLOADER 0 +#define FLASH_AREA_IMAGE_SCRATCH 3 +``` +```c +/* If the bootloader is working with the first image */ +#define FLASH_AREA_IMAGE_PRIMARY 1 +#define FLASH_AREA_IMAGE_SECONDARY 2 +``` +```c +/* If the bootloader is working with the second image */ +#define FLASH_AREA_IMAGE_PRIMARY 5 +#define FLASH_AREA_IMAGE_SECONDARY 6 +``` + +The bootloader area contains the bootloader image itself. The other areas are +described in subsequent sections. The flash could contain multiple executable +images therefore the flash area IDs of primary and secondary areas are mapped +based on the number of the active image (on which the bootloader is currently +working). + +## [Image slots](#image-slots) + +A portion of the flash memory can be partitioned into multiple image areas, each +contains two image slots: a primary slot and a secondary slot. +Normally, the bootloader will only run an image from the primary slot, so +images must be built such that they can run from that fixed location in flash +(the exception to this is the [direct-xip](#direct-xip) and the +[ram-load](#ram-load) upgrade mode). If the bootloader needs to run the +image resident in the secondary slot, it must copy its contents into the primary +slot before doing so, either by swapping the two images or by overwriting the +contents of the primary slot. The bootloader supports either swap- or +overwrite-based image upgrades, but must be configured at build time to choose +one of these two strategies. + +### [Swap using scratch](#image-swap-using-scratch) + +When swap-using-scratch algorithm is used, in addition to the slots of +image areas, the bootloader requires a scratch area to allow for reliable +image swapping. The scratch area must have a size +that is enough to store at least the largest sector that is going to be swapped. +Many devices have small equally sized flash sectors, eg 4K, while others have +variable sized sectors where the largest sectors might be 128K or 256K, so the +scratch must be big enough to store that. The scratch is only ever used when +swapping firmware, which means only when doing an upgrade. Given that, the main +reason for using a larger size for the scratch is that flash wear will be more +evenly distributed, because a single sector would be written twice the number of +times than using two sectors, for example. To evaluate the ideal size of the +scratch for your use case the following parameters are relevant: + +* the ratio of image size / scratch size +* the number of erase cycles supported by the flash hardware + +The image size is used (instead of slot size) because only the slot's sectors +that are actually used for storing the image are copied. The image/scratch ratio +is the number of times the scratch will be erased on every upgrade. The number +of erase cycles divided by the image/scratch ratio will give you the number of +times an upgrade can be performed before the device goes out of spec. + +``` +num_upgrades = number_of_erase_cycles / (image_size / scratch_size) +``` + +Let's assume, for example, a device with 10000 erase cycles, an image size of +150K and a scratch of 4K (usual minimum size of 4K sector devices). This would +result in a total of: + +`10000 / (150 / 4) ~ 267` + +Increasing the scratch to 16K would give us: + +`10000 / (150 / 16) ~ 1067` + +There is no *best* ratio, as the right size is use-case dependent. Factors to +consider include the number of times a device will be upgraded both in the field +and during development, as well as any desired safety margin on the +manufacturer's specified number of erase cycles. In general, using a ratio that +allows hundreds to thousands of field upgrades in production is recommended. + +swap-using scratch algorithm assumes that the primary and the secondary image +slot areas sizes are equal. +The maximum image size available for the application +will be: +``` +maximum-image-size = image-slot-size - image-trailer-size +``` + +Where: + `image-slot-size` is the size of the image slot. + `image-trailer-size` is the size of the image trailer. + +### [Swap without using scratch](#image-swap-no-scratch) + +This algorithm is an alternative to the swap-using-scratch algorithm. +It uses an additional sector in the primary slot to make swap possible. +The algorithm works as follows: + + 1. Moves all sectors of the primary slot up by one sector. + Beginning from N=0: + 2. Copies the N-th sector from the secondary slot to the N-th sector of the + primary slot. + 3. Copies the (N+1)-th sector from the primary slot to the N-th sector of the + secondary slot. + 4. Repeats steps 2. and 3. until all the slots' sectors are swapped. + +This algorithm is designed so that the higher sector of the primary slot is +used only for allowing sectors to move up. Therefore the most +memory-size-effective slot layout is when the primary slot is exactly one sector +larger than the secondary slot, although same-sized slots are allowed as well. +The algorithm is limited to support sectors of the same +sector layout. All slot's sectors should be of the same size. + +When using this algorithm the maximum image size available for the application +will be: +``` +maximum-image-size = (N-1) * slot-sector-size - image-trailer-sectors-size +``` + +Where: + `N` is the number of sectors in the primary slot. + `image-trailer-sectors-size` is the size of the image trailer rounded up to + the total size of sectors its occupied. For instance if the image-trailer-size + is equal to 1056 B and the sector size is equal to 1024 B, then + `image-trailer-sectors-size` will be equal to 2048 B. + +The algorithm does two erase cycles on the primary slot and one on the secondary +slot during each swap. Assuming that receiving a new image by the DFU +application requires 1 erase cycle on the secondary slot, this should result in +leveling the flash wear between the slots. + +The algorithm is enabled using the `MCUBOOT_SWAP_USING_MOVE` option. + +### [Equal slots (direct-xip)](#direct-xip) + +When the direct-xip mode is enabled the active image flag is "moved" between the +slots during image upgrade and in contrast to the above, the bootloader can +run an image directly from either the primary or the secondary slot (without +having to move/copy it into the primary slot). Therefore the image update +client, which downloads the new images must be aware, which slot contains the +active image and which acts as a staging area and it is responsible for loading +the proper images into the proper slot. All this requires that the images be +built to be executed from the corresponding slot. At boot time the bootloader +first looks for images in the slots and then inspects the version numbers in the +image headers. It selects the newest image (with the highest version number) and +then checks its validity (integrity check, signature verification etc.). If the +image is invalid MCUboot erases its memory slot and starts to validate the other +image. After a successful validation of the selected image the bootloader +chain-loads it. + +An additional "revert" mechanism is also supported. For more information, please +read the [corresponding section](#direct-xip-revert). +Handling the primary and secondary slots as equals has its drawbacks. Since the +images are not moved between the slots, the on-the-fly image +encryption/decryption can't be supported (it only applies to storing the image +in an external flash on the device, the transport of encrypted image data is +still feasible). + +The overwrite and the direct-xip upgrade strategies are substantially simpler to +implement than the image swapping strategy, especially since the bootloader must +work properly even when it is reset during the middle of an image swap. For this +reason, the rest of the document describes its behavior when configured to swap +images during an upgrade. + +### [RAM loading](#ram-load) + +In ram-load mode the slots are equal. Like the direct-xip mode, this mode +also selects the newest image by reading the image version numbers in the image +headers. But instead of executing it in place, the newest image is copied to the +RAM for execution. The load address, the location in RAM where the image is +copied to, is stored in the image header. The ram-load upgrade mode can be +useful when there is no internal flash in the SoC, but there is a big enough +internal RAM to hold the images. Usually in this case the images are stored +in an external storage device. Execution from external storage has some +drawbacks (lower execution speed, image is exposed to attacks) therefore the +image is always copied to the internal RAM before the authentication and +execution. Ram-load mode requires the image to be built to be executed from +the RAM address range instead of the storage device address range. If +ram-load is enabled then platform must define the following parameters: + +```c +#define IMAGE_EXECUTABLE_RAM_START +#define IMAGE_EXECUTABLE_RAM_SIZE +``` + +For multiple image load if multiple ram regions are used platform must define +the `MULTIPLE_EXECUTABLE_RAM_REGIONS` flag instead and implement the following +function: + +```c +int boot_get_image_exec_ram_info(uint32_t image_id, + uint32_t *exec_ram_start, + uint32_t *exec_ram_size) +``` + +When ram-load is enabled, the `--load-addr ` option of the `imgtool` +script must also be used when signing the images. This option set the `RAM_LOAD` +flag in the image header which indicates that the image should be loaded to the +RAM and also set the load address in the image header. + +When the encryption option is enabled (`MCUBOOT_ENC_IMAGES`) along with ram-load +the image is checked for encryption. If the image is not encrypted, RAM loading +happens as described above. If the image is encrypted, it is copied in RAM at +the provided address and then decrypted. Finally, the decrypted image is +authenticated in RAM and executed. + +## [Boot swap types](#boot-swap-types) + +When the device first boots under normal circumstances, there is an up-to-date +firmware image in each primary slot, which MCUboot can validate and then +chain-load. In this case, no image swaps are necessary. During device upgrades, +however, new candidate image(s) is present in the secondary slot(s), which +MCUboot must swap into the primary slot(s) before booting as discussed above. + +Upgrading an old image with a new one by swapping can be a two-step process. In +this process, MCUboot performs a "test" swap of image data in flash and boots +the new image or it will be executed during operation. The new image can then +update the contents of flash at runtime to mark itself "OK", and MCUboot will +then still choose to run it during the next boot. When this happens, the swap is +made "permanent". If this doesn't happen, MCUboot will perform a "revert" swap +during the next boot by swapping the image(s) back into its original location(s) +, and attempting to boot the old image(s). + +Depending on the use case, the first swap can also be made permanent directly. +In this case, MCUboot will never attempt to revert the images on the next reset. + +Test swaps are supported to provide a rollback mechanism to prevent devices +from becoming "bricked" by bad firmware. If the device crashes immediately +upon booting a new (bad) image, MCUboot will revert to the old (working) image +at the next device reset, rather than booting the bad image again. This allows +device firmware to make test swaps permanent only after performing a self-test +routine. + +On startup, MCUboot inspects the contents of flash to decide for each images +which of these "swap types" to perform; this decision determines how it +proceeds. + +The possible swap types, and their meanings, are: + +- `BOOT_SWAP_TYPE_NONE`: The "usual" or "no upgrade" case; attempt to boot the + contents of the primary slot. + +- `BOOT_SWAP_TYPE_TEST`: Boot the contents of the secondary slot by swapping + images. Unless the swap is made permanent, revert back on the next boot. + +- `BOOT_SWAP_TYPE_PERM`: Permanently swap images, and boot the upgraded image + firmware. + +- `BOOT_SWAP_TYPE_REVERT`: A previous test swap was not made permanent; + swap back to the old image whose data are now in the secondary slot. If the + old image marks itself "OK" when it boots, the next boot will have swap type + `BOOT_SWAP_TYPE_NONE`. + +- `BOOT_SWAP_TYPE_FAIL`: Swap failed because image to be run is not valid. + +- `BOOT_SWAP_TYPE_PANIC`: Swapping encountered an unrecoverable error. + +The "swap type" is a high-level representation of the outcome of the +boot. Subsequent sections describe how MCUboot determines the swap type from +the bit-level contents of flash. + +### [Revert mechanism in direct-xip mode](#direct-xip-revert) + +The direct-xip mode also supports a "revert" mechanism which is the equivalent +of the swap mode's "revert" swap. When the direct-xip mode is selected it can be +enabled with the MCUBOOT_DIRECT_XIP_REVERT config option and an image trailer +must also be added to the signed images (the "--pad" option of the `imgtool` +script must be used). For more information on this please read the +[Image Trailer](#image-trailer) section and the [imgtool](imgtool.md) +documentation. Making the images permanent (marking them as confirmed in +advance) is also supported just like in swap mode. The individual steps of the +direct-xip mode's "revert" mechanism are the following: + +1. Select the slot which holds the newest potential image. +2. Was the image previously selected to run (during a previous boot)? + + Yes: Did the image mark itself "OK" (was the self-test successful)? + + Yes. + - Proceed to step 3. + + No. + - Erase the image from the slot to prevent it from being selected + again during the next boot. + - Return to step 1 (the bootloader will attempt to select and + possibly boot the previous image if there is one). + + No. + - Mark the image as "selected" (set the copy_done flag in the trailer). + - Proceed to step 3. +3. Proceed to image validation ... + +## [Image trailer](#image-trailer) + +For the bootloader to be able to determine the current state and what actions +should be taken during the current boot operation, it uses metadata stored in +the image flash areas. While swapping, some of this metadata is temporarily +copied into and out of the scratch area. + +This metadata is located at the end of the image flash areas, and is called an +image trailer. An image trailer has the following structure: + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ ~ + ~ Swap status (BOOT_MAX_IMG_SECTORS * min-write-size * 3) ~ + ~ ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Encryption key 0 (16 octets) [*] | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xff padding as needed | + | (BOOT_MAX_ALIGN minus 16 octets from Encryption key 0) [*] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Encryption key 1 (16 octets) [*] | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xff padding as needed | + | (BOOT_MAX_ALIGN minus 16 octets from Encryption key 1) [*] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Swap size (4 octets) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xff padding as needed | + | (BOOT_MAX_ALIGN minus 4 octets from Swap size) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Swap info | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Copy done | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Image OK | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xff padding as needed | + | (BOOT_MAX_ALIGN minus 16 octets from MAGIC) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAGIC (16 octets) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +[*]: Only present if the encryption option is enabled (`MCUBOOT_ENC_IMAGES`). + +The offset immediately following such a record represents the start of the next +flash area. + +--- +***Note*** + +*"min-write-size" is a property of the flash hardware. If the hardware* +*allows individual bytes to be written at arbitrary addresses, then* +*min-write-size is 1. If the hardware only allows writes at even addresses,* +*then min-write-size is 2, and so on.* + +--- + +An image trailer contains the following fields: + +1. Swap status: A series of records which records the progress of an image + swap. To swap entire images, data are swapped between the two image areas + one or more sectors at a time, like this: + + - sector data in the primary slot is copied into scratch, then erased + - sector data in the secondary slot is copied into the primary slot, + then erased + - sector data in scratch is copied into the secondary slot + +As it swaps images, the bootloader updates the swap status field in a way that +allows it to compute how far this swap operation has progressed for each +sector. The swap status field can thus used to resume a swap operation if the +bootloader is halted while a swap operation is ongoing and later reset. The +`BOOT_MAX_IMG_SECTORS` value is the configurable maximum number of sectors +MCUboot supports for each image; its value defaults to 128, but allows for +either decreasing this size, to limit RAM usage, or to increase it in devices +that have massive amounts of Flash or very small sized sectors and thus require +a bigger configuration to allow for the handling of all slot's sectors. +The factor of min-write-size is due to the behavior of flash hardware. The factor +of 3 is explained below. + +2. Encryption keys: key-encrypting keys (KEKs). These keys are needed for + image encryption and decryption. See the + [encrypted images](encrypted_images.md) document for more information. + +3. Swap size: When beginning a new swap operation, the total size that needs + to be swapped (based on the slot with largest image + TLVs) is written to + this location for easier recovery in case of a reset while performing the + swap. + +4. Swap info: A single byte which encodes the following information: + - Swap type: Stored in bits 0-3. Indicating the type of swap operation in + progress. When MCUboot resumes an interrupted swap, it uses this field to + determine the type of operation to perform. This field contains one of the + following values in the table below. + - Image number: Stored in bits 4-7. It has always 0 value at single image + boot. In case of multi image boot it indicates, which image was swapped when + interrupt happened. The same scratch area is used during in case of all + image swap operation. Therefore this field is used to determine which image + the trailer belongs to if boot status is found on scratch area when the swap + operation is resumed. + +| Name | Value | +| ------------------------- | ----- | +| `BOOT_SWAP_TYPE_TEST` | 2 | +| `BOOT_SWAP_TYPE_PERM` | 3 | +| `BOOT_SWAP_TYPE_REVERT` | 4 | + + +5. Copy done: A single byte indicating whether the image in this slot is + complete (0x01=done; 0xff=not done). + +6. Image OK: A single byte indicating whether the image in this slot has been + confirmed as good by the user (0x01=confirmed; 0xff=not confirmed). + +7. MAGIC: A 16-byte field identifying the image trailer layout. It may assume + distinct values depending on the maximum supported write alignment + (`BOOT_MAX_ALIGN`) of the image, as defined by the following construct: + +``` c +union boot_img_magic_t +{ + struct { + uint16_t align; + uint8_t magic[14]; + }; + uint8_t val[16]; +}; +``` + If `BOOT_MAX_ALIGN` is **8 bytes**, then MAGIC contains the following 16 bytes: + +``` c +const union boot_img_magic_t boot_img_magic = { + .val = { + 0x77, 0xc2, 0x95, 0xf3, + 0x60, 0xd2, 0xef, 0x7f, + 0x35, 0x52, 0x50, 0x0f, + 0x2c, 0xb6, 0x79, 0x80 + } +}; +``` + + In case `BOOT_MAX_ALIGN` is defined to any value different than **8**, then the maximum + supported write alignment value is encoded in the MAGIC field, followed by a fixed + 14-byte pattern: + +``` c +const union boot_img_magic_t boot_img_magic = { + .align = BOOT_MAX_ALIGN, + .magic = { + 0x2d, 0xe1, + 0x5d, 0x29, 0x41, 0x0b, + 0x8d, 0x77, 0x67, 0x9c, + 0x11, 0x0f, 0x1f, 0x8a + } +}; +``` + +--- +***Note*** +Be aware that the image trailers make the ending area of the image slot +unavailable for carrying the image data. In particular, the swap status size +could be huge. For example, for 128 slot sectors with a 4-byte alignment, +it would become 1536 B. + +--- + +## [Image trailers](#image-trailers) + +At startup, the bootloader determines the boot swap type by inspecting the +image trailers. When using the term "image trailers" what is meant is the +aggregate information provided by both image slot's trailers. + +### [New swaps (non-resumes)](#new-swaps-non-resumes) + +For new swaps, MCUboot must inspect a collection of fields to determine which +swap operation to perform. + +The image trailers records are structured around the limitations imposed by +flash hardware. As a consequence, they do not have a very intuitive design, and +it is difficult to get a sense of the state of the device just by looking at the +image trailers. It is better to map all the possible trailer states to the swap +types described above via a set of tables. These tables are reproduced below. + +--- +***Note*** + +*An important caveat about the tables described below is that they must* +*be evaluated in the order presented here. Lower state numbers must have a* +*higher priority when testing the image trailers.* + +--- + +``` + State I + | primary slot | secondary slot | + -----------------+--------------+----------------| + magic | Any | Good | + image-ok | Any | Unset | + copy-done | Any | Any | + -----------------+--------------+----------------' + result: BOOT_SWAP_TYPE_TEST | + -------------------------------------------------' + + + State II + | primary slot | secondary slot | + -----------------+--------------+----------------| + magic | Any | Good | + image-ok | Any | 0x01 | + copy-done | Any | Any | + -----------------+--------------+----------------' + result: BOOT_SWAP_TYPE_PERM | + -------------------------------------------------' + + + State III + | primary slot | secondary slot | + -----------------+--------------+----------------| + magic | Good | Unset | + image-ok | 0xff | Any | + copy-done | 0x01 | Any | + -----------------+--------------+----------------' + result: BOOT_SWAP_TYPE_REVERT | + -------------------------------------------------' +``` + +Any of the above three states results in MCUboot attempting to swap images. + +Otherwise, MCUboot does not attempt to swap images, resulting in one of the +other three swap types, as illustrated by State IV. + +``` + State IV + | primary slot | secondary slot | + -----------------+--------------+----------------| + magic | Any | Any | + image-ok | Any | Any | + copy-done | Any | Any | + -----------------+--------------+----------------' + result: BOOT_SWAP_TYPE_NONE, | + BOOT_SWAP_TYPE_FAIL, or | + BOOT_SWAP_TYPE_PANIC | + -------------------------------------------------' +``` + +In State IV, when no errors occur, MCUboot will attempt to boot the contents of +the primary slot directly, and the result is `BOOT_SWAP_TYPE_NONE`. If the image +in the primary slot is not valid, the result is `BOOT_SWAP_TYPE_FAIL`. If a +fatal error occurs during boot, the result is `BOOT_SWAP_TYPE_PANIC`. If the +result is either `BOOT_SWAP_TYPE_FAIL` or `BOOT_SWAP_TYPE_PANIC`, MCUboot hangs +rather than booting an invalid or compromised image. + +--- +***Note*** + +*An important caveat to the above is the result when a swap is requested* +*and the image in the secondary slot fails to validate, due to a hashing or* +*signing error. This state behaves as State IV with the extra action of* +*marking the image in the primary slot as "OK", to prevent further attempts* +*to swap.* + +--- + +### [Resumed swaps](#resumed-swaps) + +If MCUboot determines that it is resuming an interrupted swap (i.e., a reset +occurred mid-swap), it fully determines the operation to resume by reading the +`swap info` field from the active trailer and extracting the swap type from bits +0-3. The set of tables in the previous section are not necessary in the resume +case. + +## [High-level operation](#high-level-operation) + +With the terms defined, we can now explore the bootloader's operation. First, +a high-level overview of the boot process is presented. Then, the following +sections describe each step of the process in more detail. + +Procedure: + +1. Inspect swap status region; is an interrupted swap being resumed? + + Yes: Complete the partial swap operation; skip to step 3. + + No: Proceed to step 2. + +2. Inspect image trailers; is a swap requested? + + Yes: + 1. Is the requested image valid (integrity and security check)? + + Yes. + a. Perform swap operation. + b. Persist completion of swap procedure to image trailers. + c. Proceed to step 3. + + No. + a. Erase invalid image. + b. Persist failure of swap procedure to image trailers. + c. Proceed to step 3. + + + No: Proceed to step 3. + +3. Boot into image in primary slot. + +### [Multiple image boot](#multiple-image-boot) + +When the flash contains multiple executable images the bootloader's operation +is a bit more complex but similar to the previously described procedure with +one image. Every image can be updated independently therefore the flash is +partitioned further to arrange two slots for each image. +``` ++--------------------+ +| MCUboot | ++--------------------+ + ~~~~~ <- memory might be not contiguous ++--------------------+ +| Image 0 | +| primary slot | ++--------------------+ +| Image 0 | +| secondary slot | ++--------------------+ + ~~~~~ <- memory might be not contiguous ++--------------------+ +| Image N | +| primary slot | ++--------------------+ +| Image N | +| secondary slot | ++--------------------+ +| Scratch | ++--------------------+ +``` +MCUboot is also capable of handling dependencies between images. For example +if an image needs to be reverted it might be necessary to revert another one too +(e.g. due to API incompatibilities) or simply to prevent from being updated +because of an unsatisfied dependency. Therefore all aborted swaps have to be +completed and all the swap types have to be determined for each image before +the dependency checks. Dependency handling is described in more detail in a +following section. The multiple image boot procedure is organized in loops which +iterate over all the firmware images. The high-level overview of the boot +process is presented below. + ++ Loop 1. Iterate over all images + 1. Inspect swap status region of current image; is an interrupted swap being + resumed? + + Yes: + + Review the validity of previously determined swap types + of other images. + + Complete the partial swap operation. + + Mark the swap type as `None`. + + Skip to next image. + + No: Proceed to step 2. + + 2. Inspect image trailers in the primary and secondary slot; is an image + swap requested? + + Yes: Review the validity of previously determined swap types of other + images. Is the requested image valid (integrity and security + check)? + + Yes: + + Set the previously determined swap type for the current image. + + Skip to next image. + + No: + + Erase invalid image. + + Persist failure of swap procedure to image trailers. + + Mark the swap type as `Fail`. + + Skip to next image. + + No: + + Mark the swap type as `None`. + + Skip to next image. + ++ Loop 2. Iterate over all images + 1. Does the current image depend on other image(s)? + + Yes: Are all the image dependencies satisfied? + + Yes: Skip to next image. + + No: + + Modify swap type depending on what the previous type was. + + Restart dependency check from the first image. + + No: Skip to next image. + ++ Loop 3. Iterate over all images + 1. Is an image swap requested? + + Yes: + + Perform image update operation. + + Persist completion of swap procedure to image trailers. + + Skip to next image. + + No: Skip to next image. + ++ Loop 4. Iterate over all images + 1. Validate image in the primary slot (integrity and security check) or + at least do a basic sanity check to avoid booting into an empty flash + area. + ++ Boot into image in the primary slot of the 0th image position\ + (other image in the boot chain is started by another image). + +### [Multiple image boot for RAM loading and direct-xip](#multiple-image-boot-for-ram-loading-and-direct-xip) + +The operation of the bootloader is different when the ram-load or the +direct-xip strategy is chosen. The flash map is very similar to the swap +strategy but there is no need for Scratch area. + ++ Loop 1. Until all images are loaded and all dependencies are satisfied + 1. Subloop 1. Iterate over all images + + Does any of the slots contain an image? + + Yes: + + Choose the newer image. + + Copy it to RAM in case of ram-load strategy. + + Validate the image (integrity and security check). + + If validation fails delete the image from flash and try the other + slot. (Image must be deleted from RAM too in case of ram-load + strategy.) + + No: Return with failure. + + 2. Subloop 2. Iterate over all images + + Does the current image depend on other image(s)? + + Yes: Are all the image dependencies satisfied? + + Yes: Skip to next image. + + No: + + Delete the image from RAM in case of ram-load strategy, but + do not delete it from flash. + + Try to load the image from the other slot. + + Restart dependency check from the first image. + + No: Skip to next image. + ++ Loop 2. Iterate over all images + + Increase the security counter if needed. + + Do the measured boot and the data sharing if needed. + ++ Boot the loaded slot of image 0. + +## [Image swapping](#image-swapping) + +The bootloader swaps the contents of the two image slots for two reasons: + + * User has issued a "set pending" operation; the image in the secondary slot + should be run once (state I) or repeatedly (state II), depending on + whether a permanent swap was specified. + * Test image rebooted without being confirmed; the bootloader should + revert to the original image currently in the secondary slot (state III). + +If the image trailers indicates that the image in the secondary slot should be +run, the bootloader needs to copy it to the primary slot. The image currently +in the primary slot also needs to be retained in flash so that it can be used +later. Furthermore, both images need to be recoverable if the bootloader +resets in the middle of the swap operation. The two images are swapped +according to the following procedure: + +1. Determine if both slots are compatible enough to have their images swapped. + To be compatible, both have to have only sectors that can fit into the + scratch area and if one of them has larger sectors than the other, it must + be able to entirely fit some rounded number of sectors from the other slot. + In the next steps we'll use the terminology "region" for the total amount of + data copied/erased because this can be any amount of sectors depending on + how many the scratch is able to fit for some swap operation. +2. Iterate the list of region indices in descending order (i.e., starting + with the greatest index); only regions that are predetermined to be part of + the image are copied; current element = "index". + + a. Erase scratch area. + + b. Copy secondary_slot[index] to scratch area. + - If this is the last region in the slot, scratch area has a temporary + status area initialized to store the initial state, because the + primary slot's last region will have to be erased. In this case, + only the data that was calculated to amount to the image is copied. + - Else if this is the first swapped region but not the last region in + the slot, initialize the status area in primary slot and copy the + full region contents. + - Else, copy entire region contents. + + c. Write updated swap status (i). + + d. Erase secondary_slot[index] + + e. Copy primary_slot[index] to secondary_slot[index] according to amount + previosly copied at step b. + - If this is not the last region in the slot, erase the trailer in the + secondary slot, to always use the one in the primary slot. + + f. Write updated swap status (ii). + + g. Erase primary_slot[index]. + + h. Copy scratch area to primary_slot[index] according to amount + previously copied at step b. + - If this is the last region in the slot, the status is read from + scratch (where it was stored temporarily) and written anew in the + primary slot. + + i. Write updated swap status (iii). +3. Persist completion of swap procedure to the primary slot image trailer. + +The additional caveats in step 2f are necessary so that the secondary slot image +trailer can be written by the user at a later time. With the image trailer +unwritten, the user can test the image in the secondary slot +(i.e., transition to state I). + +--- +***Note*** + +*If the region being copied contains the last sector, then swap status is* +*temporarily maintained on scratch for the duration of this operation, always* +*using the primary slot's area otherwise.* + +--- +***Note*** + +*The bootloader tries to copy only used sectors (based on largest image* +*installed on any of the slots), minimizing the amount of sectors copied and* +*reducing the amount of time required for a swap operation.* + +--- + +The particulars of step 3 vary depending on whether an image is being tested, +permanently used, reverted or a validation failure of the secondary slot +happened when a swap was requested: + + * test: + o Write primary_slot.copy_done = 1 + (swap caused the following values to be written: + primary_slot.magic = BOOT_MAGIC + secondary_slot.magic = UNSET + primary_slot.image_ok = Unset) + + * permanent: + o Write primary_slot.copy_done = 1 + (swap caused the following values to be written: + primary_slot.magic = BOOT_MAGIC + secondary_slot.magic = UNSET + primary_slot.image_ok = 0x01) + + * revert: + o Write primary_slot.copy_done = 1 + o Write primary_slot.image_ok = 1 + (swap caused the following values to be written: + primary_slot.magic = BOOT_MAGIC) + + * failure to validate the secondary slot: + o Write primary_slot.image_ok = 1 + +After completing the operations as described above the image in the primary slot +should be booted. + +## [Swap status](#swap-status) + +The swap status region allows the bootloader to recover in case it restarts in +the middle of an image swap operation. The swap status region consists of a +series of single-byte records. These records are written independently, and +therefore must be padded according to the minimum write size imposed by the +flash hardware. In the below figure, a min-write-size of 1 is assumed for +simplicity. The structure of the swap status region is illustrated below. In +this figure, a min-write-size of 1 is assumed for simplicity. + +``` + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |sec127,state 0 |sec127,state 1 |sec127,state 2 |sec126,state 0 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |sec126,state 1 |sec126,state 2 |sec125,state 0 |sec125,state 1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |sec125,state 2 | | + +-+-+-+-+-+-+-+-+ + + ~ ~ + ~ [Records for indices 124 through 1 ~ + ~ ~ + ~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ |sec000,state 0 |sec000,state 1 |sec000,state 2 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +The above is probably not helpful at all; here is a description in English. + +Each image slot is partitioned into a sequence of flash sectors. If we were to +enumerate the sectors in a single slot, starting at 0, we would have a list of +sector indices. Since there are two image slots, each sector index would +correspond to a pair of sectors. For example, sector index 0 corresponds to +the first sector in the primary slot and the first sector in the secondary slot. +Finally, reverse the list of indices such that the list starts with index +`BOOT_MAX_IMG_SECTORS - 1` and ends with 0. The swap status region is a +representation of this reversed list. + +During a swap operation, each sector index transitions through four separate +states: +``` +0. primary slot: image 0, secondary slot: image 1, scratch: N/A +1. primary slot: image 0, secondary slot: N/A, scratch: image 1 (1->s, erase 1) +2. primary slot: N/A, secondary slot: image 0, scratch: image 1 (0->1, erase 0) +3. primary slot: image 1, secondary slot: image 0, scratch: N/A (s->0) +``` + +Each time a sector index transitions to a new state, the bootloader writes a +record to the swap status region. Logically, the bootloader only needs one +record per sector index to keep track of the current swap state. However, due +to limitations imposed by flash hardware, a record cannot be overwritten when +an index's state changes. To solve this problem, the bootloader uses three +records per sector index rather than just one. + +Each sector-state pair is represented as a set of three records. The record +values map to the above four states as follows + +``` + | rec0 | rec1 | rec2 + --------+------+------+------ + state 0 | 0xff | 0xff | 0xff + state 1 | 0x01 | 0xff | 0xff + state 2 | 0x01 | 0x02 | 0xff + state 3 | 0x01 | 0x02 | 0x03 +``` + +The swap status region can accommodate `BOOT_MAX_IMG_SECTORS` sector indices. +Hence, the size of the region, in bytes, is +`BOOT_MAX_IMG_SECTORS * min-write-size * 3`. The only requirement for the index +count is that it is great enough to account for a maximum-sized image +(i.e., at least as great as the total sector count in an image slot). If a +device's image slots have been configured with `BOOT_MAX_IMG_SECTORS: 128` and +use less than 128 sectors, the first record that gets written will be somewhere +in the middle of the region. For example, if a slot uses 64 sectors, the first +sector index that gets swapped is 63, which corresponds to the exact halfway +point within the region. + +--- +***Note*** + +*Since the scratch area only ever needs to record swapping of the last* +*sector, it uses at most min-write-size * 3 bytes for its own status area.* + +--- + +## [Reset recovery](#reset-recovery) + +If the bootloader resets in the middle of a swap operation, the two images may +be discontiguous in flash. Bootutil recovers from this condition by using the +image trailers to determine how the image parts are distributed in flash. + +The first step is determine where the relevant swap status region is located. +Because this region is embedded within the image slots, its location in flash +changes during a swap operation. The below set of tables map image trailers +contents to swap status location. In these tables, the "source" field +indicates where the swap status region is located. In case of multi image boot +the images primary area and the single scratch area is always examined in pairs. +If swap status found on scratch area then it might not belong to the current +image. The swap_info field of swap status stores the corresponding image number. +If it does not match then "source: none" is returned. + +``` + | primary slot | scratch | + ----------+--------------+--------------| + magic | Good | Any | + copy-done | 0x01 | N/A | + ----------+--------------+--------------' + source: none | + ----------------------------------------' + + | primary slot | scratch | + ----------+--------------+--------------| + magic | Good | Any | + copy-done | 0xff | N/A | + ----------+--------------+--------------' + source: primary slot | + ----------------------------------------' + + | primary slot | scratch | + ----------+--------------+--------------| + magic | Any | Good | + copy-done | Any | N/A | + ----------+--------------+--------------' + source: scratch | + ----------------------------------------' + + | primary slot | scratch | + ----------+--------------+--------------| + magic | Unset | Any | + copy-done | 0xff | N/A | + ----------+--------------+--------------| + source: primary slot | + ----------------------------------------+------------------------------+ + This represents one of two cases: | + o No swaps ever (no status to read, so no harm in checking). | + o Mid-revert; status in the primary slot. | + For this reason we assume the primary slot as source, to trigger a | + check of the status area and find out if there was swapping under way. | + -----------------------------------------------------------------------' +``` + +If the swap status region indicates that the images are not contiguous, MCUboot +determines the type of swap operation that was interrupted by reading the `swap +info` field in the active image trailer and extracting the swap type from bits +0-3 then resumes the operation. In other words, it applies the procedure defined +in the previous section, moving image 1 into the primary slot and image 0 into +the secondary slot. If the boot status indicates that an image part is present +in the scratch area, this part is copied into the correct location by starting +at step e or step h in the area-swap procedure, depending on whether the part +belongs to image 0 or image 1. + +After the swap operation has been completed, the bootloader proceeds as though +it had just been started. + +## [Integrity check](#integrity-check) + +An image is checked for integrity immediately before it gets copied into the +primary slot. If the bootloader doesn't perform an image swap, then it can +perform an optional integrity check of the image in the primary slot if +`MCUBOOT_VALIDATE_PRIMARY_SLOT` is set, otherwise it doesn't perform an +integrity check. + +During the integrity check, the bootloader verifies the following aspects of +an image: + + * 32-bit magic number must be correct (`IMAGE_MAGIC`). + * Image must contain an `image_tlv_info` struct, identified by its magic + (`IMAGE_TLV_PROT_INFO_MAGIC` or `IMAGE_TLV_INFO_MAGIC`) exactly following + the firmware (`hdr_size` + `img_size`). If `IMAGE_TLV_PROT_INFO_MAGIC` is + found then after `ih_protect_tlv_size` bytes, another `image_tlv_info` + with magic equal to `IMAGE_TLV_INFO_MAGIC` must be present. + * Image must contain a SHA256 TLV. + * Calculated SHA256 must match SHA256 TLV contents. + * Image *may* contain a signature TLV. If it does, it must also have a + KEYHASH TLV with the hash of the key that was used to sign. The list of + keys will then be iterated over looking for the matching key, which then + will then be used to verify the image contents. + +For low performance MCU's where the validation is a heavy process at boot +(~1-2 seconds on a arm-cortex-M0), the `MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE` +could be used. This option will cache the validation result as described above +into the magic area of the primary slot. The next boot, the validation will be +skipped if the previous validation was succesfull. This option is reducing the +security level since if an attacker could modify the contents of the flash after +a good image has been validated, the attacker could run his own image without +running validation again. Enabling this option should be done with care. + +## [Security](#security) + +As indicated above, the final step of the integrity check is signature +verification. The bootloader can have one or more public keys embedded in it +at build time. During signature verification, the bootloader verifies that an +image was signed with a private key that corresponds to the embedded KEYHASH +TLV. + +For information on embedding public keys in the bootloader, as well as +producing signed images, see: [signed_images](signed_images.md). + +If you want to enable and use encrypted images, see: +[encrypted_images](encrypted_images.md). + +--- +***Note*** + +*Image encryption is not supported when the direct-xip upgrade strategy* +*is selected.* + +--- + +### [Using hardware keys for verification](#hw-key-support) + +By default, the whole public key is embedded in the bootloader code and its +hash is added to the image manifest as a KEYHASH TLV entry. As an alternative +the bootloader can be made independent of the keys (avoiding the incorporation +of the public key into the code) by using one of the following options: +`MCUBOOT_HW_KEY` or `MCUBOOT_BUILTIN_KEY`. + +Using any of these options makes MCUboot independent from the public key(s). +The key(s) can be provisioned any time and by different parties. + +Hardware KEYs support options details: +- `MCUBOOT_HW_KEY`: In this case the hash of the public key must be +provisioned to the target device and MCUboot must be able to retrieve the +key-hash from there. For this reason the target must provide a definition +for the `boot_retrieve_public_key_hash()` function which is declared in +`boot/bootutil/include/bootutil/sign_key.h`. It is also required to use +the `full` option for the `--public-key-format` imgtool argument in order to +add the whole public key (PUBKEY TLV) to the image manifest instead of its +hash (KEYHASH TLV). During boot the public key is validated before using it for +signature verification, MCUboot calculates the hash of the public key from the +TLV area and compares it with the key-hash that was retrieved from the device. +- `MCUBOOT_BUILTIN_KEY`: With this option the whole public key(s) used for +signature verification must be provisioned to the target device and the used +[cryptographic library](PORTING.md) must support the usage of builtin keys based +on key IDs. In this case, neither the code nor the image metadata needs to +contain any public key data. During image validation only a key ID is passed to +the verifier function. The key handling is entirely the responsibility of the +crypto library and the details of the key handling mechanism are abstracted away +from the boot code.\ +***Note:*** *At the moment the usage of builtin keys is only available with the* +*PSA Crypto API based crypto backend (`MCUBOOT_USE_PSA_CRYPTO`) for ECDSA* +*signatures.* + +## [Protected TLVs](#protected-tlvs) + +If the TLV area contains protected TLV entries, by beginning with a `struct +image_tlv_info` with a magic value of `IMAGE_TLV_PROT_INFO_MAGIC` then the +data of those TLVs must also be integrity and authenticity protected. Beyond +the full size of the protected TLVs being stored in the `image_tlv_info`, +the size of the protected TLVs together with the size of the `image_tlv_info` +struct itself are also saved in the `ih_protected_size` field inside the +header. + +Whenever an image has protected TLVs the SHA256 has to be calculated over +not just the image header and the image but also the TLV info header and the +protected TLVs. + +``` +A +---------------------+ + | Header | <- struct image_header + +---------------------+ + | Payload | + +---------------------+ + | TLV area | + | +-----------------+ | struct image_tlv_info with + | | TLV area header | | <- IMAGE_TLV_PROT_INFO_MAGIC (optional) + | +-----------------+ | + | | Protected TLVs | | <- Protected TLVs (struct image_tlv) +B | +-----------------+ | + | | TLV area header | | <- struct image_tlv_info with IMAGE_TLV_INFO_MAGIC +C | +-----------------+ | + | | SHA256 hash | | <- hash from A - B (struct image_tlv) +D | +-----------------+ | + | | Keyhash | | <- indicates which pub. key for sig (struct image_tlv) + | +-----------------+ | + | | Signature | | <- signature from C - D (struct image_tlv), only hash + | +-----------------+ | + +---------------------+ +``` + +## [Dependency check](#dependency-check) + +MCUboot can handle multiple firmware images. It is possible to update them +independently but in many cases it can be desired to be able to describe +dependencies between the images (e.g. to ensure API compliance and avoid +interoperability issues). + +The dependencies between images can be described with additional TLV entries in +the protected TLV area after the end of an image. There can be more than one +dependency entry, but in practice if the platform only supports two individual +images then there can be maximum one entry which reflects to the other image. + +At the phase of dependency check all aborted swaps are finalized if there were +any. During the dependency check the bootloader verifies whether the image +dependencies are all satisfied. If at least one of the dependencies of an image +is not fulfilled then the swap type of that image has to be modified +accordingly and the dependency check needs to be restarted. This way the number +of unsatisfied dependencies will decrease or remain the same. There is always at +least 1 valid configuration. In worst case, the system returns to the initial +state after dependency check. + +For more information on adding dependency entries to an image, +see: [imgtool](imgtool.md). + +## [Downgrade prevention](#downgrade-prevention) + +Downgrade prevention is a feature which enforces that the new image must have a +higher version/security counter number than the image it is replacing, thus +preventing the malicious downgrading of the device to an older and possibly +vulnerable version of its firmware. + +### [Software-based downgrade prevention](#sw-downgrade-prevention) + +During the software based downgrade prevention the image version numbers are +compared. This feature is enabled with the `MCUBOOT_DOWNGRADE_PREVENTION` +option. In this case downgrade prevention is only available when the +overwrite-based image update strategy is used (i.e. `MCUBOOT_OVERWRITE_ONLY` +is set). + +### [Hardware-based downgrade prevention](#hw-downgrade-prevention) + +Each signed image can contain a security counter in its protected TLV area, which +can be added to the image using the `-s` option of the [imgtool](imgtool.md) script. +During the hardware based downgrade prevention (alias rollback protection) the +new image's security counter will be compared with the currently active security +counter value which must be stored in a non-volatile and trusted component of +the device. It is beneficial to handle this counter independently from image +version number: + + * It does not need to increase with each software release, + * It makes it possible to do software downgrade to some extent: if the + security counter has the same value in the older image then it is accepted. + +It is an optional step of the image validation process and can be enabled with +the `MCUBOOT_HW_ROLLBACK_PROT` config option. When enabled, the target must +provide an implementation of the security counter interface defined in +`boot/bootutil/include/security_cnt.h`. + +## [Measured boot and data sharing](#boot-data-sharing) + +MCUboot defines a mechanism for sharing boot status information (also known as +measured boot) and an interface for sharing application specific information +with the runtime software. If any of these are enabled the target must provide +a shared data area between the bootloader and runtime firmware and define the +following parameters: + +```c +#define MCUBOOT_SHARED_DATA_BASE +#define MCUBOOT_SHARED_DATA_SIZE +``` + +In the shared memory area all data entries are stored in a type-length-value +(TLV) format. Before adding the first data entry, the whole area is overwritten +with zeros and a TLV header is added at the beginning of the area during an +initialization phase. This TLV header contains a `tlv_magic` field with a value +of `SHARED_DATA_TLV_INFO_MAGIC` and a `tlv_tot_len` field which is indicating +the total length of shared TLV area including this header. The header is +followed by the the data TLV entries which are composed from a +`shared_data_tlv_entry` header and the data itself. In the data header there is +a `tlv_type` field which identifies the consumer of the entry (in the runtime +software) and specifies the subtype of that data item. More information about +the `tlv_type` field and data types can be found in the +`boot/bootutil/include/bootutil/boot_status.h` file. The type is followed by a +`tlv_len` field which indicates the size of the data entry in bytes, not +including the entry header. After this header structure comes the actual data. + +```c +/** Shared data TLV header. All fields in little endian. */ +struct shared_data_tlv_header { + uint16_t tlv_magic; + uint16_t tlv_tot_len; /* size of whole TLV area (including this header) */ +}; + +/** Shared data TLV entry header format. All fields in little endian. */ +struct shared_data_tlv_entry { + uint16_t tlv_type; + uint16_t tlv_len; /* TLV data length (not including this header). */ +}; +``` + +The measured boot can be enabled with the `MCUBOOT_MEASURED_BOOT` config option. +When enabled, the `--boot_record` argument of the imgtool script must also be +used during the image signing process to add a BOOT_RECORD TLV to the image +manifest. This TLV contains the following attributes/measurements of the +image in CBOR encoded format: + + * Software type (role of the software component) + * Software version + * Signer ID (identifies the signing authority) + * Measurement value (hash of the image) + * Measurement type (algorithm used to calculate the measurement value) + +The `sw_type` string that is passed as the `--boot_record` option's parameter +will be the value of the "Software type" attribute in the generated BOOT_RECORD +TLV. The target must also define the `MAX_BOOT_RECORD_SZ` macro which indicates +the maximum size of the CBOR encoded boot record in bytes. +During boot, MCUboot will look for these TLVs (in case of multiple images) in +the manifests of the active images (the latest and validated) and copy the CBOR +encoded binary data to the shared data area. Preserving all these image +attributes from the boot stage for use by later runtime services (such as an +attestation service) is known as a measured boot. + +Setting the `MCUBOOT_DATA_SHARING` option enables the sharing of application +specific data using the same shared data area as for the measured boot. For +this, the target must provide a definition for the `boot_save_shared_data()` +function which is declared in `boot/bootutil/include/bootutil/boot_record.h`. +The `boot_add_data_to_shared_area()` function can be used for adding new TLV +entries to the shared data area. Alternatively, setting the +`MCUBOOT_DATA_SHARING_BOOTINFO` option will provide a default function for +this which saves information such as the maximum application size, bootloader +version (if available), running slot number, if recovery is part of MCUboot +and the signature type. Details of the TLVs for this information can be found +in `boot/bootutil/include/bootutil/boot_status.h` with `BLINFO_` prefixes. + +## [Testing in CI](#testing-in-ci) + +### [Testing Fault Injection Hardening (FIH)](#testing-fih) + +The CI currently tests the Fault Injection Hardening feature of MCUboot by +executing instruction skip during execution, and looking at whether a corrupted +image was booted by the bootloader or not. + +The main idea is that instruction skipping can be automated by scripting a +debugger to automatically execute the following steps: + +- Set breakpoint at specified address. +- Continue execution. +- On breakpoint hit increase the Program Counter. +- Continue execution. +- Detach from target after a timeout reached. + +Whether or not the corrupted image was booted or not can be decided by looking +for certain entries in the log. + +As MCUboot is deployed on a microcontroller, testing FI would not make much +sense in the simulator environment running on a host machine with different +architecture than the MCU's, as the degree of hardening depends on compiler +behavior. For example, (a bit counterintuitively) the code produced by gcc +with `-O0` optimisation is more resilient against FI attacks than the code +generated with `-O3` or `-Os` optimizations. + +To run on a desired architecture in the CI, the tests need to be executed on an +emulator (as real devices are not available in the CI environment). For this +implementation QEMU is selected. + +For the tests MCUboot needs a set of drivers and an implementation of a main +function. For the purpose of this test Trusted-Firmware-M has been selected as +it supports Armv8-M platforms that are also emulated by QEMU. + +The tests run in a docker container inside the CI VMs, to make it more easy to +deploy build and test environment (QEMU, compilers, interpreters). The CI VMs +seems to be using quite old Ubuntu (16.04). + +The sequence of the testing is the following (pseudo code): + +```sh +fn main() + # Implemented in ci/fih-tests_install.sh + generate_docker_image(Dockerfile) + + # See details below. Implemented in ci/fih-tests_run.sh. + # Calling the function with different parameters is done by Travis CI based on + # the values provided in the .travis.yaml + start_docker_image(skip_sizes, build_type, damage_type, fih_level) + +fn start_docker_image(skip_sizes, build_type, damage_type, fih_level) + # implemented in ci/fih_test_docker/execute_test.sh + compile_mcuboot(build_type) + + # implemented in ci/fih_test_docker/damage_image.py + damage_image(damage_type) + + # implemented in ci/fih_test_docker/run_fi_test.sh + ranges = generate_address_ranges() + for s in skip_sizes + for r in ranges + do_skip_in_qemu(s, r) # See details below + evaluate_logs() + +fn do_skip_in_qemu(size, range) + for a in r + run_qemu(a, size) # See details below + +# this part is implemented in ci/fih_test_docker/fi_tester_gdb.sh +fn run_qemu(a, size) + script = create_debugger_script(a, size) + start_qemu_in_bacground() # logs serial out to a file + gdb_attach_to_qemu(script) + kill_qemu() + + # This checks the debugger and the quemu logs, and decides whether the tets + # was executed successfully, and whether the image is booted or not. Then + # emits a yaml fragment on the standard out to be processed by the caller + # script + evaluate_run(qemu_log_file) +``` + +Further notes: + +- The image is corrupted by changing its signature. +- MCUBOOT_FIH_PROFILE_MAX is not tested as it requires TRNG, and the AN521 +platform has no support for it. However this profile adds the random +execution delay to the code, so should not affect the instruction skip results +too much, because break point is placed at exact address. But in practice this +makes harder the accurate timing of the attack. +- The test cases defined in .travis.yml always return `passed`, if they were +executed successfully. A yaml file is created during test execution with the +details of the test execution results. A summary of the collected results is +printed in the log at the end of the test. + +An advantage of having the tests running in a docker image is that it is +possible to run the tests on a local machine that has git and docker, without +installing any additional software. + +So, running the test on the host looks like the following (The commands below +are issued from the MCUboot source directory): + +```sh +$ mkdir docker +$ ./ci/fih-tests_install.sh +$ FIH_LEVEL=MEDIUM BUILD_TYPE=RELEASE SKIP_SIZE=2 DAMAGE_TYPE=SIGNATURE \ + ./ci/fih-tests_run.sh +``` +On the travis CI the environment variables in the last command are set based on +the configs provided in the `.travis.yaml` + +This starts the tests, however the shell that it is running in is not +interactive, it is not possible to examine the results of the test run. To have +an interactive shell where the results can be examined, the following can be +done: + +- The docker image needs to be built with `ci/fih-tests_install.sh` as described + above. +- Start the docker image with the following command: + `docker run -i -t mcuboot/fih-test`. +- Execute the test with a command similar to the following: + `/root/execute_test.sh 8 RELEASE SIGNATURE MEDIUM`. After the test finishes, + the shell returns, and it is possible to investigate the results. It is also + possible to stop the test with _Ctrl+c_. The parameters to the + `execute_test.sh` are `SKIP_SIZE`, `BUILD_TYPE`, `DAMAGE_TYPE`, `FIH_LEVEL` in + order. diff --git a/bootloader/mcuboot/docs/ecdsa.md b/bootloader/mcuboot/docs/ecdsa.md new file mode 100644 index 0000000..d3a5a25 --- /dev/null +++ b/bootloader/mcuboot/docs/ecdsa.md @@ -0,0 +1,90 @@ +# ECDSA signature format + +When the ECDSA SECP256R1 (EC256) signature support was added to MCUboot, a +shortcut was taken, and these signatures were padded to make them +always a fixed length. Unfortunately, this padding was done in a way +that is not easily reversible. Some crypto libraries (specifically, Mbed +TLS) are fairly strict about the formatting of the ECDSA signature. + +There are two ways to fix this: + + - Use a reversible padding scheme. This solution requires + at least one pad byte to always be added (to set the length). This + padding would be somewhat incompatible across versions (older + EC256 would work, while newer MCUboot code would reject old + signatures. The EC code would work reliably only in the new + combination). + + - Remove the padding entirely. Depending on the tool used, this solution + requires some rethinking of how TLV generation is implemented so + that the length does not need to be known until the signature is + generated. These tools are usually written in higher-level + languages, so this change should not be difficult. + + However, this will also break compatibility with older versions, + because images generated with newer tools will not + work with older versions of MCUboot. + +This document proposes a multi-stage approach to give a transition +period: + + 1. Add a `--no-pad-sig` argument to the sign command in + `imgtool.py`. + + Without this argument, the images are padded with the + existing scheme. With this argument, the ECDSA is encoded + without any padding. The `--pad-sig` argument is also + accepted, but it is already the default. + + 2. MCUboot will be modified to allow unpadded signatures right away. + The existing EC256 implementations will still work (with or + without padding), and the existing EC implementation will be able + to accept padded and unpadded signatures. + + 3. An Mbed TLS implementation of EC256 can be added, but it will require + the `--no-pad-sig` signature to be able to boot all generated + images. Without the argument, 3 out of 4 images generated will have + padding and will be considered invalid. + +After one or more MCUboot release cycles and announcements in the +relevant channels, the arguments to `imgtool.py` will change: + + - `--no-pad-sig` will still be accepted but will have no effect. + + - `--pad-sig` will now bring back the old padding behavior. + +This will require an update to any scripts that will rely on the default +behavior, but will not specify a specific version of imgtool. + +The signature generation in the simulator can be changed at the same +time the boot code begins to accept unpadded signatures. The simulator is +always run out of the same tree as the MCUboot code, so there should +not be any compatibility issues. + +## Background + +ECDSA signatures are encoded as ASN.1, notably with the signature +itself encoded as follows: + +``` + ECDSA-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } +``` + +Both `r` and `s` are 256-bit numbers. Because these are +unsigned numbers that are being encoded in ASN.1 as signed values, if +the high bit of the number is set, the DER-encoded representation will +require 33 bytes instead of 32. This means that the length of the +signature will vary by a couple of bytes, depending on whether one or +both of these numbers have the high bit set. + +Originally, MCUboot added padding to the entire signature and just +removed any trailing 0 bytes from the data block. This turned out to be fine 255 out of 256 +times, each time the last byte of the signature was non-zero, but if the +signature ended in a zero, MCUboot would remove too many bytes and render the +signature invalid. + +The correct approach here is to accept that ECDSA signatures are of +variable length, and to make sure that we can handle them as such. diff --git a/bootloader/mcuboot/docs/encrypted_images.md b/bootloader/mcuboot/docs/encrypted_images.md new file mode 100644 index 0000000..85d26d8 --- /dev/null +++ b/bootloader/mcuboot/docs/encrypted_images.md @@ -0,0 +1,175 @@ + + +# Encrypted images + +## [Rationale](#rationale) + +To provide confidentiality of image data while in transport to the +device or while residing on an external flash, `MCUboot` has support +for encrypting/decrypting images on-the-fly while upgrading. + +The image header needs to flag this image as `ENCRYPTED` (0x04) and +a TLV with the key must be present in the image. When upgrading the +image from the `secondary slot` to the `primary slot` it is automatically +decrypted (after validation). If swap upgrades are enabled, the image +located in the `primary slot`, also having the `ENCRYPTED` flag set and the +TLV present, is re-encrypted while swapping to the `secondary slot`. + +## [Threat model](#threat-model) + +The encrypted image support is supposed to allow for confidentiality +if the image is not residing on the device or is written to external +storage, eg a SPI flash being used for the secondary slot. + +It does not protect against the possibility of attaching a JTAG and +reading the internal flash memory, or using some attack vector that +enables dumping the internal flash in any way. + +Since decrypting requires a private key (or secret if using symmetric +crypto) to reside inside the device, it is the responsibility of the +device manufacturer to guarantee that this key is already in the device +and not possible to extract. + +## [Design](#design) + +When encrypting an image, only the payload (FW) is encrypted. The header, +TLVs are still sent as plain data. + +Hashing and signing also remain functionally the same way as before, +applied over the un-encrypted data. Validation on encrypted images, checks +that the encrypted flag is set and TLV data is OK, then it decrypts each +image block before sending the data to the hash routines. + +The image is encrypted using AES-CTR-128 or AES-CTR-256, with a counter +that starts from zero (over the payload blocks) and increments by 1 for each +16-byte block. AES-CTR was chosen for speed/simplicity and allowing for any +block to be encrypted/decrypted without requiring knowledge of any other +block (allowing for simple resume operations on swap interruptions). + +The key used is a randomized when creating a new image, by `imgtool` or +`newt`. This key should never be reused and no checks are done for this, +but randomizing a 16-byte block with a TRNG should make it highly +improbable that duplicates ever happen. + +To distribute this AES-CTR key, new TLVs were defined. The key can be +encrypted using either RSA-OAEP, AES-KW (128 or 256 bits depending on the +AES-CTR key length), ECIES-P256 or ECIES-X25519. + +For RSA-OAEP a new TLV with value `0x30` is added to the image, for +AES-KW a new TLV with value `0x31` is added to the image, for +ECIES-P256 a new TLV with value `0x32` is added, and for ECIES-X25519 a +newt TLV with value `0x33` is added. The contents of those TLVs +are the results of applying the given operations over the AES-CTR key. + +## [ECIES encryption](#ecies-encryption) + +ECIES follows a well defined protocol to generate an encryption key. There are +multiple standards which differ only on which building blocks are used; for +MCUboot we settled on some primitives that are easily found on our crypto +libraries. The whole key encryption can be summarized as: + +* Generate a new private key and derive the public key; when using ECIES-P256 + this is a secp256r1 keypair, when using ECIES-X25519 this will be a x25519 + keypair. Those keys will be our ephemeral keys. +* Generate a new secret (DH) using the ephemeral private key and the public key + that corresponds to the private key embedded in the HW. +* Derive the new keys from the secret using HKDF (built on HMAC-SHA256). We + are not using a `salt` and using an `info` of `MCUBoot_ECIES_v1`, generating + 48 bytes of key material. +* A new random encryption key is generated (for AES). This is + the AES key used to encrypt the images. +* The key is encrypted with AES-128-CTR or AES-256-CTR and a `nonce` of 0 using + the first 16 bytes of key material generated previously by the HKDF. +* The encrypted key now goes through a HMAC-SHA256 using the remaining 32 + bytes of key material from the HKDF. + +The final TLV is built from the 65 bytes for ECIES-P256 or 32 bytes for +ECIES-X25519, which correspond to the ephemeral public key, followed by the +32 bytes of MAC tag and the 16 or 32 bytes of the encrypted key, resulting in +a TLV of 113 or 129 bytes for ECIES-P256 and 80 or 96 bytes for ECIES-X25519. + +The implemenation of ECIES-P256 is named ENC_EC256 in the source code and +artifacts while ECIES-X25519 is named ENC_X25519. + +## [Upgrade process](#upgrade-process) + +When starting a new upgrade process, `MCUboot` checks that the image in the +`secondary slot` has the `ENCRYPTED` flag set and has the required TLV with the +encrypted key. It then uses its internal private/secret key to decrypt +the TLV containing the key. Given that no errors are found, it will then +start the validation process, decrypting the blocks before check. A good +image being determined, the upgrade consists in reading the blocks from +the `secondary slot`, decrypting and writing to the `primary slot`. + +If swap using scratch is used for the upgrade process, the decryption happens +when copying the content of the scratch area to the `primary slot`, which means +the scratch area does not contain the image unencrypted. However, unless +`MCUBOOT_SWAP_SAVE_ENCTLV` is enabled, the decryption keys are stored in +plaintext in the scratch area. Therefore, `MCUBOOT_SWAP_SAVE_ENCTLV` must be +enabled if the scratch area does not reside in the internal flash memory of the +MCU, to avoid attacks that could interrupt the upgrade and read the plaintext +decryption keys from external flash memory. + +Also when swap is used, the image in the `primary slot` is checked for +presence of the `ENCRYPTED` flag and the key TLV. If those are present the +sectors are re-encrypted when copying from the `primary slot` to +the `secondary slot`. + +--- +***Note*** + +*Each encrypted image must have its own key TLV that should be unique* +*and used only for this particular image.* + +--- + +Also when swap method is employed, the sizes of both images are saved to +the status area just before starting the upgrade process, because it +would be very hard to determine this information when an interruption +occurs and the information is spread across multiple areas. + +## [Creating your keys with imgtool](#creating-your-keys-with-imgtool) + +`imgtool` can generate keys by using `imgtool keygen -k -t `, + where type can be one of `rsa-2048`, `rsa-3072`, `ecdsa-p256` +or `ed25519`. This will generate a keypair or private key. + +To extract the public key in source file form, use +`imgtool getpub -k -e `, where `encoding` can be one of +`lang-c` or `lang-rust` (defaults to `lang-c`). To extract a public key in PEM +format, use `imgtool getpub -k -e pem`. + +If using AES-KW, follow the steps in the next section to generate the +required keys. + +## [Creating your keys with Unix tooling](#creating-your-keys-with-unix-tooling) + +* If using RSA-OAEP, generate a keypair following steps similar to those + described in [signed_images](signed_images.md) to create RSA keys. +* If using ECIES-P256, generate a keypair following steps similar to those + described in [signed_images](signed_images.md) to create ECDSA256 keys. +* If using ECIES-X25519, generate a private key passing the option `-t x25519` + to `imgtool keygen` command. To generate public key PEM file the following + command can be used: `openssl pkey -in -pubout` +* If using AES-KW (`newt` only), the `kek` can be generated with a + command like (change count to 32 for a 256 bit key) + `dd if=/dev/urandom bs=1 count=16 | base64 > my_kek.b64` diff --git a/bootloader/mcuboot/docs/imgtool.md b/bootloader/mcuboot/docs/imgtool.md new file mode 100644 index 0000000..b4dc3b5 --- /dev/null +++ b/bootloader/mcuboot/docs/imgtool.md @@ -0,0 +1,135 @@ +# Image tool + +The Python program `scripts/imgtool.py` can be used to perform the +operations that are necessary to manage keys and sign images. Using +this script should be preferred to the manual steps described in +`doc/signed_images.md`. + +This program is written for Python3, and has several dependencies on +Python libraries. These can be installed using 'pip3': + + pip3 install --user -r scripts/requirements.txt + +## [Managing keys](#managing-keys) + +This tool currently supports rsa-2048, rsa-3072, ecdsa-p256 and ed25519 keys. +You can generate a keypair for one of these types using the 'keygen' command: + + ./scripts/imgtool.py keygen -k filename.pem -t rsa-2048 + +or use rsa-3072, ecdsa-p256, or ed25519 for the type. The key type used +should match what MCUboot is configured to verify. + +This key file is what is used to sign images, this file should be +protected, and not widely distributed. + +You can add the `-p` argument to `keygen`, which will cause it to +prompt for a password. You will need to enter this password in every +time you use the private key. + +## [Incorporating the public key into the code](#incorporating-the-public-key-into-the-code) + +There is a development key distributed with MCUboot that can be used +for testing. Since this private key is widely distributed, it should +never be used for production. Once you have generated a production +key, as described above, you should replace the public key in the +bootloader with the generated one. + +For Zephyr, the keys live in the file `boot/zephyr/keys.c`. For +mynewt, follow the instructions in `doc/signed_images.md` to generate +the key file. + + ./scripts/imgtool.py getpub -k filename.pem + +will extract the public key from the given private key file, and +output it as a C data structure. You can replace or insert this code +into the key file. However, when the `MCUBOOT_HW_KEY` config option is +enabled, this last step is unnecessary and can be skipped. + +## [Signing images](#signing-images) + +Image signing takes an image in binary or Intel Hex format intended for the +primary slot and adds a header and trailer that the bootloader is expecting: + + Usage: imgtool.py sign [OPTIONS] INFILE OUTFILE + + Create a signed or unsigned image + + INFILE and OUTFILE are parsed as Intel HEX if the params have .hex + extension, otherwise binary format is used + + Options: + -k, --key filename + --public-key-format [hash|full] + --align [1|2|4|8|16|32] Alignment used by swap update modes. + -v, --version TEXT [required] + -s, --security-counter TEXT Specify the value of security counter. Use + the `auto` keyword to automatically generate + it from the image version. + -d, --dependencies TEXT + --pad-sig Add 0-2 bytes of padding to ECDSA signature + (for MCUboot <1.5) + -H, --header-size INTEGER [required] + --pad-header Add --header-size zeroed bytes at the + beginning of the image + -S, --slot-size INTEGER Size of the slot where the image will be + written [required] + --pad Pad image to --slot-size bytes, adding + trailer magic + --confirm When padding the image, mark it as confirmed + -M, --max-sectors INTEGER When padding allow for this amount of + sectors (defaults to 128) + --boot-record sw_type Create CBOR encoded boot record TLV. The + sw_type represents the role of the software + component (e.g. CoFM for coprocessor + firmware). [max. 12 characters] + --overwrite-only Use overwrite-only instead of swap upgrades + -e, --endian [little|big] Select little or big endian + -E, --encrypt filename Encrypt image using the provided public key + --save-enctlv When upgrading, save encrypted key TLVs + instead of plain keys. Enable when + BOOT_SWAP_SAVE_ENCTLV config option was set. + -L, --load-addr INTEGER Load address for image when it should run + from RAM. + -x, --hex-addr INTEGER Adjust address in hex output file. + -R, --erased-val [0|0xff] The value that is read back from erased + flash. + -h, --help Show this message and exit. + +The main arguments given are the key file generated above, a version +field to place in the header (1.2.3 for example), the alignment of the +flash device in question, and the header size. + +The header size depends on the operating system and the particular +flash device. For Zephyr, it will be configured as part of the build, +and will be a small power of two. By default, the Zephyr build system will +already prepended a zeroed header to the image. If another build system is +in use that does not automatically add this zeroed header, `--pad-header` can +be passed and the `--header-size` will be added by imgtool. If `--pad-header` +is used with an Intel Hex file, `--header-size` bytes will be subtracted from +the load address (in Intel Hex terms, the Extended Linear Address record) to +adjust for the new bytes prepended to the file. The load address of all data +existing in the file should not change. + +The `--slot-size` argument is required and used to check that the firmware +does not overflow into the swap status area (metadata). If swap upgrades are +not being used, `--overwrite-only` can be passed to avoid adding the swap +status area size when calculating overflow. + +The optional `--pad` argument will place a trailer on the image that +indicates that the image should be considered an upgrade. Writing this image +in the secondary slot will then cause the bootloader to upgrade to it. + +A dependency can be specified in the following way: +`-d "(image_id, image_version)"`. The `image_id` is the number of the image +which the current image depends on. The `image_version` is the minimum version +of that image to satisfy compliance. For example `-d "(1, 1.2.3+0)"` means this +image depends on Image 1 which version has to be at least 1.2.3+0. + +The `--public-key-format` argument can be used to distinguish where the public +key is stored for image authentication. The `hash` option is used by default, in +which case only the hash of the public key is added to the TLV area (the full +public key is incorporated into the bootloader). When the `full` option is used +instead, the TLV area will contain the whole public key and thus the bootloader +can be independent from the key(s). For more information on the additional +requirements of this option, see the [design](design.md) document. diff --git a/bootloader/mcuboot/docs/index.md b/bootloader/mcuboot/docs/index.md new file mode 100644 index 0000000..05ef174 --- /dev/null +++ b/bootloader/mcuboot/docs/index.md @@ -0,0 +1,93 @@ +# MCUboot + +MCUboot is a secure bootloader for 32-bits microcontrollers. + +## Overview + +MCUboot defines a common infrastructure for the bootloader and the system flash +layout on microcontroller systems, and provides a secure bootloader that +enables easy software upgrade. + +MCUboot is not dependent on any specific operating system and hardware and +relies on hardware porting layers from the operating system it works with. +Currently MCUboot works with the following operating systems and SoCs: +- [Zephyr](https://www.zephyrproject.org/) +- [Apache Mynewt](https://mynewt.apache.org/) +- [Apache NuttX](https://nuttx.apache.org/) +- [RIOT](https://www.riot-os.org/) +- [Mbed OS](https://os.mbed.com/) +- [Espressif](https://www.espressif.com/) +- [Cypress/Infineon](https://www.cypress.com/) + +RIOT is supported only as a boot target. We will accept any new port +contributed by the community once it is good enough. + +MCUboot is an open governance project. See the [membership +list](https://github.com/mcu-tools/mcuboot/wiki/Members) for current +members, and the +[project charter](https://github.com/mcu-tools/mcuboot/wiki/MCUboot-Project-Charter) +for more details. + +## Documentation + +The MCUboot documentation is composed of the following pages: + +- General - this document +- [Release notes](release-notes.md) +- [Bootloader design](design.md) +- [Encrypted images](encrypted_images.md) +- [imgtool](imgtool.md) - image signing and key management +- [ECDSA](ecdsa.md) - information about ECDSA signature formats +- [Serial Recovery](serial_recovery.md) - MCUmgr as the serial recovery protocol +- Usage instructions: + - [Zephyr](readme-zephyr.md) + - [Apache Mynewt](readme-mynewt.md) + - [Apache NuttX](readme-nuttx.md) + - [RIOT](readme-riot.md) + - [Mbed OS](readme-mbed.md) + - [Espressif](readme-espressif.md) + - [Cypress/Infineon](https://github.com/mcu-tools/mcuboot/tree/main/boot/cypress/README.md) + - [Simulator](https://github.com/mcu-tools/mcuboot/tree/main/sim/README.rst) +- Testing + - [Zephyr](testplan-zephyr.md) - Zephyr test plan + - [Apache Mynewt](testplan-mynewt.md) - Apache Mynewt test plan +- [Release process](release.md) +- [Project security policy](SECURITY.md) +- [Patch submission](SubmittingPatches.md) - information + on how to contribute to MCUboot + +The documentation page about [signed images](signed_images.md) is currently +outdated. Follow the instructions in [imgtool](imgtool.md) instead. + +## Roadmap + +The issues being planned and worked on are tracked using GitHub issues. To +give your input, visit [MCUboot GitHub +Issues](https://github.com/mcu-tools/mcuboot/issues). + +## Source files + +You can find additional documentation on the bootloader in the source files. +For more information, use the following links: +- [boot/bootutil](https://github.com/mcu-tools/mcuboot/tree/main/boot/bootutil) - The core of the bootloader itself. +- [boot/boot\_serial](https://github.com/mcu-tools/mcuboot/tree/main/boot/boot_serial) - Support for serial upgrade within the bootloader itself. +- [boot/zephyr](https://github.com/mcu-tools/mcuboot/tree/main/boot/zephyr) - Port of the bootloader to Zephyr. +- [boot/mynewt](https://github.com/mcu-tools/mcuboot/tree/main/boot/mynewt) - Bootloader application for Apache Mynewt. +- [boot/nuttx](https://github.com/mcu-tools/mcuboot/tree/main/boot/nuttx) - Bootloader application and port of MCUboot interfaces for Apache NuttX. +- [boot/mbed](https://github.com/mcu-tools/mcuboot/tree/main/boot/mbed) - Port of the bootloader to Mbed OS. +- [boot/espressif](https://github.com/mcu-tools/mcuboot/tree/main/boot/espressif) - Bootloader application and MCUboot port for Espressif SoCs. +- [boot/cypress](https://github.com/mcu-tools/mcuboot/tree/main/boot/cypress) - Bootloader application and MCUboot port for Cypress/Infineon SoCs. +- [imgtool](https://github.com/mcu-tools/mcuboot/tree/main/scripts/imgtool.py) - A tool to securely sign firmware images for booting by MCUboot. +- [sim](https://github.com/mcu-tools/mcuboot/tree/main/sim) - A bootloader simulator for testing and regression. + +## Joining the project + +Developers are welcome! + +Use the following links to join or see more about the project: + +* [Our developer mailing list](https://groups.io/g/MCUBoot) +* [Our Discord channel](https://discord.com/channels/1106321706588577904/1106322802308550716)
+ Get [your invite](https://discord.com/invite/5PpXhvda5p) +* [Current members](https://github.com/mcu-tools/mcuboot/wiki/Members) +* [Project charter](https://github.com/mcu-tools/mcuboot/wiki/MCUboot-Project-Charter) diff --git a/bootloader/mcuboot/docs/readme-espressif.md b/bootloader/mcuboot/docs/readme-espressif.md new file mode 100644 index 0000000..21004df --- /dev/null +++ b/bootloader/mcuboot/docs/readme-espressif.md @@ -0,0 +1,1253 @@ +# [Building and using MCUboot with Espressif's chips](#building-and-using-mcuboot-with-espressifs-chips) + +The MCUBoot Espressif's port depends on HAL (Hardware Abstraction Layer) sources based on ESP-IDF +or 3rd party frameworks as such as Zephyr-RTOS (`zephyrproject-rtos/hal_espressif/`) or NuttX RTOS +(`espressif/esp-hal-3rdparty`). Building the MCUboot Espressif's port and its features is platform +dependent, therefore, the system environment including toolchains, must be set accordingly. A +standalone build version means that ESP-IDF and its toolchain are used as source. For 3rd parties +framework, HAL path and toolchain must be set. + +Documentation about the MCUboot bootloader design, operation and features can be found in the +[design document](design.md). + +## [SoC support availability](#soc-support-availability) + +The current port is available for use in the following SoCs within the OSes: + +| | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 | ESP32-C2 | ESP32-C6 | ESP32-H2 | +| :----: | :-----: | :-----: | :-----: | :-----: | :---------: | :-----: | :-----: | +| Zephyr | Supported | Supported | Supported | Supported | In progress | In progress | In progress | +| NuttX | Supported | Supported | Supported | Supported | In progress | In progress | In progress | + +Notice that any customization in the memory layout from the OS application must be done aware of +the bootloader own memory layout to avoid overlapping. More information on the section +[Memory map organization for OS compatibility](#memory-map-organization-for-os-compatibility). + +## [Installing requirements and dependencies](#installing-requirements-and-dependencies) + +The following instructions considers a MCUboot Espressif port standalone build. + +1. Install additional packages required for development with MCUboot: + + ```bash + cd ~/mcuboot # or to your directory where MCUboot is cloned + ``` + + ```bash + pip3 install --user -r scripts/requirements.txt + ``` + +2. Update the Mbed TLS submodule required by MCUboot: + + ```bash + git submodule update --init --recursive ext/mbedtls + ``` + +3. If ESP-IDF is the chosen option for use as HAL layer and the system already have ESP-IDF + installed, ensure that the environment is set: + + ```bash + /install.sh + ``` + + ```bash + . /export.sh + ``` + + --- + ***Note*** + + *If desirable, instructions for ESP-IDF installation can be found + [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#manual-installation)* + + --- + + --- + ***Note*** + + *The other HALs mentioned above like `hal_espressif` from Zephyr RTOS or `esp-hal-3rdparty` + from NuttX RTOS environments also can be used for the bootloader standalone build, however as + eventually code revision may differ from what is currently expected, it is recommended using + them only within their RTOS build system.* + + --- + +4. If ESP-IDF is not installed and will not be used, install `esptool`: + + ```bash + pip3 install esptool + ``` + +## [Building the bootloader itself](#building-the-bootloader-itself) + +The MCUboot Espressif port bootloader is built using the toolchain and tools provided by Espressif. +Additional configuration related to MCUboot features and slot partitioning may be made using the +`port//bootloader.conf` file or passing a custom config file using the +`-DMCUBOOT_CONFIG_FILE` argument on the first step below. + +--- +***Note*** + +*Replace `` with the target ESP32 family (like `esp32`, `esp32s2` and others).* + +--- + +1. Compile and generate the BIN: + + ```bash + cmake -DCMAKE_TOOLCHAIN_FILE=tools/toolchain-.cmake -DMCUBOOT_TARGET= -DESP_HAL_PATH= -DMCUBOOT_FLASH_PORT= -B build -GNinja + ``` + + ```bash + ninja -C build/ + ``` + + --- + ***Note*** + + *If using ESP-IDF as HAL layer source, `ESP_HAL_PATH` can be ommited.* + + *If desirable, `` can be defined with the path for a different compatible + toolchain, however it is recommended to actually create a CMake toolchain file and + pass it through `` variable since it may require a distinct set of + compilation flags.* + + --- + +2. Flash MCUboot in your device: + + ```bash + ninja -C build/ flash + ``` + + If `MCUBOOT_FLASH_PORT` arg was not passed to `cmake`, the default `PORT` for flashing will be + `/dev/ttyUSB0`. + + Alternatively: + + ```bash + esptool.py -p -b --before default_reset --after no_reset --chip write_flash --flash_mode dio --flash_size --flash_freq 40m build/mcuboot_.bin + ``` + + --- + ***Note*** + + You may adjust the port `` (like `/dev/ttyUSB0`) and baud rate `` (like `2000000`) + according to the connection with your board. You can also skip `` and `` parameters + so that esptool tries to automatically detect it. + + *`` can be found using the command below:* + + ```bash + esptool.py -p -b flash_id + ``` + + The output contains device information and its flash size: + + ``` + Detected flash size: 4MB + ``` + + *`` value must follow one of the addresses below:* + + | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 | ESP32-C2 | ESP32-C6 | ESP32-H2 | + | :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | + | 0x1000 | 0x1000 | 0x0000 | 0x0000 | 0x0000 | 0x0000 | 0x0000 | + + --- + +3. Reset your device + +## [Signing and flashing an application](#signing-and-flashing-an-application) + +1. Images can be regularly signed with the `scripts/imgtool.py` script: + + ```bash + imgtool.py sign --align 4 -v 0 -H 32 --pad-header -S + ``` + + --- + + ***Note*** + + `` is the size of the slot to be used. + Default slot0 size is `0x100000`, but it can change as per application flash partitions. + + For Zephyr images, `--pad-header` is not needed as it already has the padding for MCUboot + header. + + --- + + :warning: ***ATTENTION*** + + *This is the basic signing needed for adding MCUboot headers and trailers. + For signing with a crypto key and guarantee the authenticity of the image being booted, see the + section [MCUboot image signature verification](#mcuboot-image-signature-verification) below.* + + --- + +2. Flash the signed application: + + ```bash + esptool.py -p -b --before default_reset --after hard_reset --chip write_flash --flash_mode dio --flash_size --flash_freq 40m + ``` + +# [Downgrade prevention](#downgrade-prevention) + +Downgrade prevention (avoid updating of images to an older version) can be enabled using the +following configuration: + +``` +CONFIG_ESP_DOWNGRADE_PREVENTION=y +``` + +MCUboot will then verify and compare the new image version number with the current one before +perform an update swap. + +Version number is added to the image when signing it with `imgtool` (`-v` parameter, e.g. +`-v 1.0.0`). + +### [Downgrade prevention with security counter](#downgrade-prevention-with-security-counter) + +It is also possible to rely on a security counter, also added to the image when signing with +`imgtool` (`-s` parameter), apart from version number. This allows image downgrade at some extent, +since any update must have greater or equal security counter value. Enable using the following +configuration: + +``` +CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y +``` + +E.g.: if the current image was signed using `-s 1` parameter, an eventual update image must have +been signed using security counter `-s 1` or greater. + +# [Security Chain on Espressif port](#security-chain-on-espressif-port) + +[MCUboot encrypted images](encrypted_images.md) do not provide full code confidentiality when only +external storage is available (see [Threat model](encrypted_images.md#threat-model)) since by +MCUboot design the image in Primary Slot, from where the image is executed, is stored plaintext. +Espressif chips have off-chip flash memory, so to ensure a security chain along with MCUboot image +signature verification, the hardware-assisted Secure Boot and Flash Encryption were made available +on the MCUboot Espressif port. + +## [MCUboot image signature verification](#mcuboot-image-signature-verification) + +The image that MCUboot is booting can be signed with 4 types of keys: RSA-2048, RSA-3072, EC256 and +ED25519. In order to enable the feature, the **bootloader** must be compiled with the following +configurations: + +--- +***Note*** + +*It is strongly recommended to generate a new signing key using `imgtool` instead of use the +existent samples.* + +--- + +#### For EC256 algorithm use + +``` +CONFIG_ESP_SIGN_EC256=y + +# Use Tinycrypt lib for EC256 or ED25519 signing +CONFIG_ESP_USE_TINYCRYPT=y + +CONFIG_ESP_SIGN_KEY_FILE= +``` + +#### For ED25519 algorithm use + +``` +CONFIG_ESP_SIGN_ED25519=y + +# Use Tinycrypt lib for EC256 or ED25519 signing +CONFIG_ESP_USE_TINYCRYPT=y + +CONFIG_ESP_SIGN_KEY_FILE= +``` + +#### For RSA (2048 or 3072) algorithm use + +``` +CONFIG_ESP_SIGN_RSA=y +# RSA_LEN is 2048 or 3072 +CONFIG_ESP_SIGN_RSA_LEN= + +# Use Mbed TLS lib for RSA image signing +CONFIG_ESP_USE_MBEDTLS=y + +CONFIG_ESP_SIGN_KEY_FILE= +``` + +Notice that the public key will be embedded in the bootloader code, since the hardware key storage +is not supported by Espressif port. + +### [Signing the image](#signing-the-image) + +Now you need to sign the **image binary**, use the `imgtool` with `-k` parameter: + +```bash +imgtool.py sign -k --pad --pad-sig --align 4 -v 0 -H 32 --pad-header -S 0x00100000 +``` + +If signing a Zephyr image, the `--pad-header` is not needed, as it already have the padding for +MCUboot header. + + +## [Secure Boot](#secure-boot) + +The Secure Boot implementation is based on +[IDF's Secure Boot V2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/secure-boot-v2.html), +is hardware-assisted and RSA based - except ESP32-C2 that uses ECDSA signing scheme - and has the +role for ensuring that only authorized code will be executed on the device. This is done through +bootloader signature checking by the ROM bootloader. + +***Note***: ROM bootloader is the First Stage Bootloader, while the Espressif MCUboot port is the +Second Stage Bootloader. + +### [Building bootloader with Secure Boot](#building-bootloader-with-secure-boot) + +In order to build the bootloader with the feature on, the following configurations must be enabled: + +``` +CONFIG_SECURE_BOOT=1 +CONFIG_SECURE_BOOT_V2_ENABLED=1 +CONFIG_SECURE_SIGNED_ON_BOOT=1 +``` + +For the currently supported chips, with exception of ESP32-C2, enable RSA signing scheme: + +``` +CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1 +CONFIG_SECURE_BOOT_SUPPORTS_RSA=1 +``` + +For ESP32-C2, enable ECDSA signing scheme and, if working with Flash Encryption too, enable the +configuration to burn keys to efuse together: + +``` +CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME=1 + +CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER=1 +``` + +--- +:warning: ***ATTENTION*** + +*On development phase is recommended add the following configuration in order to keep the debugging +enabled and also to avoid any unrecoverable/permanent state change:* + +``` +CONFIG_SECURE_BOOT_ALLOW_JTAG=1 +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 + +# Options for enabling eFuse emulation in Flash +CONFIG_EFUSE_VIRTUAL=1 +CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 +``` +--- + +--- +:warning: ***ATTENTION*** + +*You can disable UART Download Mode by adding the following configuration:* + +``` +CONFIG_SECURE_DISABLE_ROM_DL_MODE=1 +``` + +*This may be suitable for __production__ builds. __After disabling UART Download Mode you will not +be able to flash other images through UART.__* + +*Otherwise, you can switch the UART ROM Download Mode to the Secure Download Mode. It will limit +the use of Download Mode functions to simple flash read, write and erase operations.* + +``` +CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=1 +``` + +*Once the device makes its first full boot, these configurations cannot be reverted* + +--- + +Once the **bootloader image** is built, the resulting binary file is required to be signed with +`espsecure.py` tool. + +First create a signing key: + +```bash +espsecure.py generate_signing_key --version 2 +``` + +Then sign the bootloader image: + +```bash +espsecure.py sign_data --version 2 --keyfile -o +``` + +--- +:warning: ***ATTENTION*** + +*Once the bootloader is flashed and the device resets, the **first boot will enable Secure Boot** +and the bootloader and key **no longer can be modified**. So **ENSURE** that both bootloader and +key are correct and you did not forget anything before flashing.* + +--- + +Flash the bootloader as following, with `--after no_reset` flag, so you can reset the device only +when assured: + +```bash +esptool.py -p -b 2000000 --after no_reset --chip write_flash --flash_mode dio --flash_size --flash_freq 40m +``` + +### [Secure Boot Process](#secure-boot-process) + +Secure boot uses a signature block appended to the bootloader image in order to verify the +authenticity. The signature block contains the RSA-3072 signature of that image and the RSA-3072 +public key. + +On its **first boot** the Secure Boot is not enabled on the device eFuses yet, neither the key nor +digests. So the first boot will have the following process: + +1. On startup, since it is the first boot, the ROM bootloader will not verify the bootloader image + (the Secure Boot bit in the eFuse is disabled) yet, so it proceeds to execute it (our MCUboot + bootloader port). +2. Bootloader calculates the SHA-256 hash digest of the public key and writes the result to eFuse. +3. Bootloader validates the application images and prepare the booting process (MCUboot phase). +4. Bootloader burns eFuse to enable Secure Boot V2. +5. Bootloader proceeds to load the Primary image. + +After that the Secure Boot feature is permanently enabled and on every next boot the ROM bootloader +will verify the MCUboot bootloader image. The process of an usual boot: + +1. On startup, the ROM bootloader checks the Secure Boot enable bit in the eFuse. If it is enabled, + the boot will proceed as following. +2. ROM bootloader verifies the bootloader's signature block integrity (magic number and CRC). + Interrupt boot if it fails. +3. ROM bootloader verifies the bootloader image, interrupt boot if any step fails: + 1. Compare the SHA-256 hash digest of the public key embedded in the bootloader’s signature + block with the digest saved in the eFuses. + 2. Generate the application image digest and match it with the image digest in the signature + block. + 3. Use the public key to verify the signature of the bootloader image, using RSA-PSS with the + image digest calculated from previous step for comparison. +4. ROM bootloader executes the bootloader image. +5. Bootloader does the usual verification (MCUboot phase). +6. Proceeds to boot the Primary image. + +## [Flash Encryption](#flash-encryption) + +The Espressif Flash Encryption is hardware-assisted, transparent to the MCUboot process and is an +additional security measure beyond MCUboot existent features. +The Flash Encryption implementation is also based on +[IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html) +and is intended for encrypting off-chip flash memory contents, so it is protected against physical +reading. + +When enabling the Flash Encryption, the user can encrypt the content either using a **device +generated key** (remains unknown and unreadable) or a **host generated key** (owner is responsible +for keeping the key private and safe). After the flash encryption gets enabled through eFuse +burning on the device, all read and write operations are decrypted/encrypted in runtime. + +### [Building bootloader with Flash Encryption](#building-bootloader-with-flash-encryption) + +In order to build the bootloader with the feature on, the following configurations must be enabled: + +For **release mode**: + +``` +CONFIG_SECURE_FLASH_ENC_ENABLED=1 +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=1 +``` + +For **development mode**: + +``` +CONFIG_SECURE_FLASH_ENC_ENABLED=1 +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=1 +``` + +--- +:warning: ***ATTENTION*** + +*On development phase is strongly recommended adding the following configuration in order to keep +the debugging enabled and also to avoid any unrecoverable/permanent state change:* + +``` +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=1 +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=1 +CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=1 +CONFIG_SECURE_BOOT_ALLOW_JTAG=1 + +# Options for enabling eFuse emulation in Flash +CONFIG_EFUSE_VIRTUAL=1 +CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=1 +``` +--- + +--- +:warning: ***ATTENTION*** + +*Unless the recommended flags for __DEVELOPMENT MODE__ were enabled, the actions made by Flash +Encryption process are __PERMANENT__.* \ +*Once the bootloader is flashed and the device resets, the __first boot will enable Flash +Encryption, encrypt the flash content including bootloader and image slots, burn the eFuses that no +longer can be modified__ and if device generated the key __it will not be recoverable__.* \ +*When on __RELEASE MODE__, __ENSURE__ that the application with an update agent is flashed before +reset the device.* + +*In the same way as Secure Boot feature, you can disable UART Download Mode by adding the following +configuration:* + +``` +CONFIG_SECURE_DISABLE_ROM_DL_MODE=1 +``` + +*This may be suitable for __production__ builds. __After disabling UART Download Mode you will not +be able to flash other images through UART.__* + +*Otherwise, you can switch the UART Download Mode to the Secure Download Mode. It will limit the +use of Download Mode functions to simple flash read, write and erase operations.* + +``` +CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=1 +``` + +*These configurations cannot be reverted after the device's first boot* + +--- + +### [Signing the image when working with Flash Encryption](#signing-the-image-when-working-with-flash-encryption) + +When enabling flash encryption, it is required to signed the image using 32-byte alignment: +`--align 32 --max-align 32`. + +Command example: + +```bash +imgtool.py sign -k --pad --pad-sig --align 32 --max-align 32 -v 0 -H 32 --pad-header -S +``` + +### [Device generated key](#device-generated-key) + +First ensure that the application image is able to perform encrypted read and write operations to +the SPI Flash. Flash the bootloader and application normally: + +```bash +esptool.py -p -b 2000000 --after no_reset --chip write_flash --flash_mode dio --flash_size --flash_freq 40m +``` + +```bash +esptool.py -p -b 2000000 --after no_reset --chip write_flash --flash_mode dio --flash_size --flash_freq 40m +``` + +On the **first boot**, the bootloader will: + +1. Generate Flash Encryption key and write to eFuse. +2. Encrypt flash in-place including bootloader, image primary/secondary slot and scratch. +3. Burn eFuse to enable Flash Encryption. +4. Reset system to ensure Flash Encryption cache resets properly. + +### [Host generated key](#host-generated-key) + +First ensure that the application image is able to perform encrypted read and write operations to +the SPI Flash. Also ensure that the **UART ROM Download Mode is not disabled** - or that the +**Secure Download Mode is enabled**. Before flashing, generate the encryption key using +`espsecure.py` tool: + +```bash +espsecure.py generate_flash_encryption_key +``` + +Burn the key into the device's eFuse (keep a copy on the host), this action can be done **only +once**: + +--- +:warning: ***ATTENTION*** + +*eFuse emulation in Flash configuration options do not have any effect, so if the key burning +command below is used, it will actually burn the physical eFuse.* + +--- + +- ESP32 + +```bash +espefuse.py --port PORT burn_key flash_encryption +``` + +- ESP32S2, ESP32C3 and ESP32S3 + +```bash +espefuse.py --port PORT burn_key BLOCK +``` + +`BLOCK` is a free keyblock between `BLOCK_KEY0` and `BLOCK_KEY5`. And `KEYPURPOSE` is either +`XTS_AES_128_KEY`, `XTS_AES_256_KEY_1`, `XTS_AES_256_KEY_2` (AES XTS 256 is available only in +ESP32S2). + +Now, similar as the Device generated key, the bootloader and application can be flashed plaintext. +The **first boot** will encrypt the flash content using the host key burned in the eFuse instead +of generate a new one. + +Flashing the bootloader and application: + +```bash +esptool.py -p -b 2000000 --after no_reset --chip write_flash --flash_mode dio --flash_size --flash_freq 40m +``` + +```bash +esptool.py -p -b 2000000 --after no_reset --chip write_flash --flash_mode dio --flash_size --flash_freq 40m +``` + +On the **first boot**, the bootloader will: + +1. Encrypt flash in-place including bootloader, image primary/secondary slot and scratch using the + written key. +2. Burn eFuse to enable Flash Encryption. +3. Reset system to ensure Flash Encryption cache resets properly. + +Encrypting data on the host: + +- ESP32 + +```bash +espsecure.py encrypt_flash_data --keyfile --address --output +``` + +- ESP32-S2, ESP32-C3 and ESP32-S3 + +```bash +espsecure.py encrypt_flash_data --aes_xts --keyfile --address --output +``` + +--- +***Note*** + +OTA updates are required to be sent plaintext. The reason is that, as said before, after the Flash +Encryption is enabled all read/write operations are decrypted/encrypted in runtime, so as e.g. if +pre-encrypted data is sent for an OTA update, it would be wrongly double-encrypted when the update +agent writes to the flash. + +For updating with an image encrypted on the host, flash it through serial using `esptool.py` as +above. **UART ROM Download Mode must not be disabled**. + +--- + +## [Security Chain scheme](#security-chain-scheme) + +Using the 3 features, Secure Boot, Image signature verification and Flash Encryption, a Security +Chain can be established so only trusted code is executed, and also the code and content residing +in the off-chip flash are protected against undesirable reading. + +The overall final process when all features are enabled: + +1. ROM bootloader validates the MCUboot bootloader using RSA signature verification. +2. MCUboot bootloader validates the image using the chosen algorithm EC256/RSA/ED25519. It also + validates an upcoming image when updating. +3. Flash Encryption guarantees that code and data are not exposed. + +### [Size Limitation](#size-limitation) + +When all 3 features are enable at same time, the bootloader size may exceed the fixed limit for +the ROM bootloader checking on the Espressif chips **depending on which algorithm** was chosen for +MCUboot image signing. The issue was created to +track this limitation. + +## [Multi image](#multi-image) + +The multi image feature (currently limited to 2 images) allows the images to be updated separately +(each one has its own primary and secondary slot) by MCUboot. + +The Espressif port bootloader handles the boot in two different approaches: + +### [Host OS boots second image](#host-os-boots-second-image) + +Host OS from the *first image* is responsible for booting the *second image*, therefore the +bootloader is aware of the second image regions and can update it, however it does not load +neither boots it. + +Configuration example (`bootloader.conf`): + +``` +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y + +# Enables multi image, if it is not defined, its assumed +# only one updatable image +CONFIG_ESP_IMAGE_NUMBER=2 + +# Example of values to be used when multi image is enabled +# Notice that the OS layer and update agent must be aware +# of these regions +CONFIG_ESP_APPLICATION_SIZE=0x50000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x60000 +CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS=0xB0000 +CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS=0x100000 +CONFIG_ESP_SCRATCH_OFFSET=0x150000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 +``` + +### [Multi boot](#multi-boot) + +In the multi boot approach the bootloader is responsible for booting two different images in two +different CPUs, firstly the *second image* on the APP CPU and then the *first image* on the PRO +CPU (current CPU), it is also responsible for update both images as well. Thus multi boot will be +only supported by Espressif multi core chips - currently only ESP32 is implemented. + +--- +***Note*** + +*The host OSes in each CPU must handle how the resources are divided/controlled between then.* + +--- + +Configuration example: + +``` +CONFIG_ESP_BOOTLOADER_SIZE=0xF000 +CONFIG_ESP_MCUBOOT_WDT_ENABLE=y + +# Enables multi image, if it is not defined, its assumed +# only one updatable image +CONFIG_ESP_IMAGE_NUMBER=2 + +# Enables multi image boot on independent processors +# (main host OS is not responsible for booting the second image) +# Use only with CONFIG_ESP_IMAGE_NUMBER=2 +CONFIG_ESP_MULTI_PROCESSOR_BOOT=y + +# Example of values to be used when multi image is enabled +# Notice that the OS layer and update agent must be aware +# of these regions +CONFIG_ESP_APPLICATION_SIZE=0x50000 +CONFIG_ESP_IMAGE0_PRIMARY_START_ADDRESS=0x10000 +CONFIG_ESP_IMAGE0_SECONDARY_START_ADDRESS=0x60000 +CONFIG_ESP_IMAGE1_PRIMARY_START_ADDRESS=0xB0000 +CONFIG_ESP_IMAGE1_SECONDARY_START_ADDRESS=0x100000 +CONFIG_ESP_SCRATCH_OFFSET=0x150000 +CONFIG_ESP_SCRATCH_SIZE=0x40000 +``` + +### [Image version dependency](#image-version-dependency) + +MCUboot allows version dependency check between the images when updating them. As `imgtool.py` +allows a version assigment when signing an image, it is also possible to add the version +dependency constraint: + +```bash +imgtool.py sign --align 4 -v -d "(, )" -H 32 --pad-header -S +``` + +- `` defines the version of the image being signed. +- `"(, )"` defines the minimum version and from which image is + needed to satisfy the dependency. + +--- +Example: + +```bash +imgtool.py sign --align 4 -v 1.0.0 -d "(1, 0.0.1+0)" -H 32 --pad-header -S 0x100000 image0.bin image0-signed.bin +``` + +Supposing that the image 0 is being signed, its version is 1.0.0 and it depends on image 1 with +version at least 0.0.1+0. + +--- + +## [Serial recovery mode](#serial-recovery-mode) + +Serial recovery mode allows management through MCUMGR (more information and how to install it: +) for communicating and uploading a firmware to the +device. + +Configuration example: + +``` +# Enables the MCUboot Serial Recovery, that allows the use of +# MCUMGR to upload a firmware through the serial port +CONFIG_ESP_MCUBOOT_SERIAL=y +# GPIO used to boot on Serial Recovery +CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=32 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +# UART port used for serial communication +CONFIG_ESP_SERIAL_BOOT_UART_NUM=1 +# GPIO for Serial RX signal +CONFIG_ESP_SERIAL_BOOT_GPIO_RX=25 +# GPIO for Serial TX signal +CONFIG_ESP_SERIAL_BOOT_GPIO_TX=26 +``` + +When enabled, the bootloader checks the if the GPIO `` +configured has the signal value `` for approximately +`` seconds for entering the Serial recovery mode. Example: +a button configured on GPIO 32 pressed for 5 seconds. + +Serial mode then uses the UART port configured for communication +(``, pins ``, +``). + +### [Serial Recovery through USB JTAG Serial port](#serial-recovery-through-usb-jtag-serial-port) + +Some chips, like ESP32-C3 and ESP32-S3 have an integrated USB JTAG Serial Controller that +implements a serial port (CDC) that can also be used for handling MCUboot Serial Recovery. +More information about the USB pins and hardware configuration: + +- ESP32-C3: +- ESP32-S3: +- ESP32-C6: +- ESP32-H2: + +Configuration example: + +``` +# Use Serial through USB JTAG Serial port for Serial Recovery +CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG=y +# Use sector erasing (recommended) instead of entire image size +# erasing when uploading through Serial Recovery +CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y +# GPIO used to boot on Serial Recovery +CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=5 +# GPIO input type (0 for Pull-down, 1 for Pull-up) +CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0 +# GPIO signal value +CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1 +# Delay time for identify the GPIO signal +CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5 +``` + +--- +:warning: ***ATTENTION*** + +*When working with Flash Encryption enabled, `CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY` must be +__disabled__, although it is recommended for common Serial Recovery usage* + +--- + +### [MCUMGR image upload example](#mcumgr-image-upload-example) + +After entering the Serial recovery mode on the device, MCUMGR can be used as following: + +Configure the connection: +```bash +mcumgr conn add esp type="serial" connstring="dev=,baud=115200,mtu=256" +``` + +Upload the image (the process may take some time): +```bash +mcumgr -c esp image upload +``` + +Reset the device: +```bash +mcumgr -c esp reset +``` + +--- +:warning: ***ATTENTION*** + +*Serial recovery mode uploads the image to the PRIMARY_SLOT, therefore if the upload process gets +interrupted the image may be corrupted and unable to boot* + +--- + +## [Memory map organization for OS compatibility](#memory-map-organization-for-os-compatibility) + +When adding support for this MCUboot port to an OS or even customizing an already supported +application memory layout, it is mandatory for the OS linker script to avoid overlaping on +`iram_loader_seg` and `dram_seg` bootloader RAM regions. Although part of the RAM becomes initially +unavailable, it is reclaimable by the OS after boot as heap. + +Therefore, the application must be designed aware of the bootloader memory usage. + +--- +***Note*** + +*Mostly of the Espressif chips have a separation on the address space for the same physical memory +ammount: IRAM (accessed by the instruction bus) and DRAM (accessed by the data bus), which means +that they need to be accessed by different addresses ranges depending on type, but refer to the +same region. More information on the +[Espressif TRMs](https://www.espressif.com/en/support/documents/technical-documents?keys=&field_download_document_type_tid%5B%5D=963).* + +--- + +The following diagrams illustrate a memory organization from the bootloader point of view (notice +that the addresses and sizes may vary depending on the chip), they reflect the linker script +`boot/espressif/port//ld/bootloader.ld`: + +### ESP32 + +#### ESP32 standard + +``` + SRAM0 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40070000 / --------- - SRAM0 START + * | ^ | + * | | PRO CPU Cache | *NOT CLAIMABLE BY OS RAM + * | v | + * +--------+--------------+------+ 0x40078000 / ---------- + * | ^ | + * | | | *NOT CLAIMABLE BY OS RAM + * | | iram_loader_seg | *Region usable as iram_loader_seg during boot + * | | (APP CPU Cache) | as APP CPU is not initialized yet + * | | | + * | v | + * +--------+--------------+------+ 0x40080000 / ---------- + * | ^ | + * | | FREE | *CLAIMABLE BY OS RAM + * | v | + * +------------------------------+ 0x40090000 / ---------- + * | ^ | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | v | + * +--------+--------------+------+ 0x40099000 / ---------- + * | | FREE | *CLAIMABLE BY OS RAM + * +------------------------------+ 0x4009FFFF / ---------- - SRAM0 END + + SRAM1 + IRAM ADDR / DRAM ADDR + * +------------------------------+ 0x400A0000 / 0x3FFFFFFF - SRAM1 START + * | ^ | + * | | | *** SHOULD NOT BE OVERLAPPED *** + * | | dram_seg | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x400AB900 / 0x3FFF4700 + * | ^ | + * | | | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +--------+--------------+------+ 0x400BFFFF / 0x3FFE0000 - SRAM1 END + Note: On ESP32 the SRAM1 addresses are accessed in reverse order comparing Instruction + bus (IRAM) and Data bus (DRAM), but refer to the same location. See the TRM for more + information. + + SRAM2 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ ---------- / 0x3FFAE000 - SRAM2 START + * | | FREE | *CLAIMABLE BY OS RAM + * +--------+--------------+------+ ---------- / 0x3FFDFFFF - SRAM2 END +``` + +#### ESP32 Multi Processor Boot + +This is the linker script mapping when the `CONFIG_ESP_MULTI_PROCESSOR_BOOT` is enabled +([Multi boot](#multi-boot)) since APP CPU Cache region cannot be used for `iram_loader_seg` region +as there would be conflict when the bootloader starts the APP CPU before jump to the main +application. + +``` + SRAM0 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40070000 / --------- - SRAM0 START + * | ^ | + * | | | + * | | Cache | *Used by PRO CPU and APP CPU as Cache + * | | | + * | v | + * +--------+--------------+------+ 0x40080000 / ---------- + * | ^ | + * | | FREE | *CLAIMABLE BY OS RAM + * | v | + * +------------------------------+ 0x40090000 / ---------- + * | ^ | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | v | + * +--------+--------------+------+ 0x40099000 / ---------- + * | | FREE | *CLAIMABLE BY OS RAM + * +------------------------------+ 0x4009FFFF / ---------- - SRAM0 END + + SRAM1 + IRAM ADDR / DRAM ADDR + * +------------------------------+ 0x400A0000 / 0x3FFFFFFF - SRAM1 START + * | ^ | + * | | | *** SHOULD NOT BE OVERLAPPED *** + * | | dram_seg | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x400AB900 / 0x3FFF4700 + * | ^ | + * | | | *** SHOULD NOT BE OVERLAPPED *** + * | | iram_loader_seg | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | | | + * | v | + * +------------------------------+ 0x400B1E00 / 0x3FFEE200 + * | ^ | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | v | + * +--------+--------------+------+ 0x400BFFFF / 0x3FFE0000 - SRAM1 END + Note: On ESP32 the SRAM1 addresses are accessed in reverse order comparing Instruction + bus (IRAM) and Data bus (DRAM), but refer to the same location. See the TRM for more + information. + + SRAM2 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ ---------- / 0x3FFAE000 - SRAM2 START + * | | FREE | *CLAIMABLE BY OS RAM + * +--------+--------------+------+ ---------- / 0x3FFDFFFF - SRAM2 END +``` + +### ESP32-S2 + +``` + SRAM0 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40020000 / 0x3FFB0000 - SRAM0 START + * | | FREE | *CLAIMABLE BY OS RAM + * +--------+--------------+------+ 0x40027FFF / 0x3FFB7FFF - SRAM0 END + + SRAM1 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40028000 / 0x3FFB8000 - SRAM1 START + * | ^ | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | v | + * +--------+--------------+------+ 0x40047000 / 0x3FFD7000 + * | ^ | + * | | | + * | | | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +------------------------------+ 0x40050000 / 0x3FFE0000 + * | ^ | + * | | | + * | | | + * | | iram_loader_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | | | + * | v | + * +------------------------------+ 0x40056000 / 0x3FFE6000 + * | ^ | + * | | | + * | | dram_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x4006FFFF / 0x3FFFFFFF - SRAM1 END +``` + +### ESP32-S3 + +``` + SRAM0 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40370000 / ---------- - SRAM0 START + * | | FREE | *CLAIMABLE BY OS RAM + * +--------+--------------+------+ 0x40377FFF / ---------- - SRAM0 END + + SRAM1 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40378000 / 0x3FC88000 - SRAM1 START + * | ^ | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | v | + * +--------+--------------+------+ 0x403B0000 / 0x3FCC0000 + * | ^ | + * | | | + * | | | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +------------------------------+ 0x403BA000 / 0x3FCCA000 + * | ^ | + * | | | + * | | | + * | | iram_loader_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | | | + * | v | + * +------------------------------+ 0x403C0000 / 0x3FCD0000 + * | ^ | + * | | | + * | | dram_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x403DFFFF / 0x3FCEFFFF - SRAM1 END + + SRAM2 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ ---------- / 0x3FCF0000 - SRAM2 START + * | | FREE | *CLAIMABLE BY OS RAM + * +--------+--------------+------+ ---------- / 0x3FCFFFFF - SRAM2 END +``` + +### ESP32-C2 + +``` + SRAM0 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x4037C000 / ---------- - SRAM0 START + * | | FREE | *CLAIMABLE BY OS RAM + * +--------+--------------+------+ 0x4037FFFF / ---------- - SRAM0 END + + SRAM1 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40380000 / 0x3FCA0000 - SRAM1 START + * | ^ | + * | | | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +--------+--------------+------+ 0x403A1370 / 0x3FCC1370 + * | ^ | + * | | | + * | | | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +------------------------------+ 0x403A9B70 / 0x3FCC9B70 + * | ^ | + * | | | + * | | | + * | | iram_loader_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | | | + * | v | + * +------------------------------+ 0x403B0B70 / 0x3FCD0B70 + * | ^ | + * | | | + * | | dram_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x403BFFFF / 0x3FCDFFFF - SRAM1 END +``` + +### ESP32-C3 + +``` + SRAM0 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x4037C000 / ---------- - SRAM0 START + * | | FREE | *CLAIMABLE BY OS RAM + * +--------+--------------+------+ 0x4037FFFF / ---------- - SRAM0 END + + SRAM1 + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40380000 / 0x3FC80000 - SRAM1 START + * | ^ | + * | | | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +--------+--------------+------+ 0x403C7000 / 0x3FCC7000 + * | ^ | + * | | | + * | | | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +------------------------------+ 0x403D0000 / 0x3FCD0000 + * | ^ | + * | | | + * | | | + * | | iram_loader_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | | | + * | v | + * +------------------------------+ 0x403D5400 / 0x3FCD5400 + * | ^ | + * | | | + * | | dram_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x403DFFFF / 0x3FCDFFFF - SRAM1 END +``` + +### ESP32-C6 + +``` + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40800000 / 0x40800000 - HP SRAM START + * | ^ | + * | | | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +--------+--------------+------+ 0x40860610 / 0x40860610 + * | ^ | + * | | | + * | | | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +------------------------------+ 0x40869610 / 0x40869610 + * | ^ | + * | | | + * | | | + * | | iram_loader_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | | | + * | v | + * +------------------------------+ 0x40870610 / 0x40870610 + * | ^ | + * | | | + * | | dram_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x4087FFFF / 0x4087FFFF - HP SRAM END +``` + +### ESP32-H2 + +``` + IRAM ADDR / DRAM ADDR + * +--------+--------------+------+ 0x40800000 / 0x40800000 - HP SRAM START + * | ^ | + * | | | + * | | | + * | | FREE | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +--------+--------------+------+ 0x408317D0 / 0x408317D0 + * | ^ | + * | | | + * | | | + * | | iram_seg | *CLAIMABLE BY OS RAM + * | | | + * | | | + * | v | + * +------------------------------+ 0x40839FD0 / 0x40839FD0 + * | ^ | + * | | | + * | | | + * | | iram_loader_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | | | + * | v | + * +------------------------------+ 0x40840FD0 / 0x40840FD0 + * | ^ | + * | | | + * | | dram_seg | *** SHOULD NOT BE OVERLAPPED *** + * | | | *** OS CAN RECLAIM IT AFTER BOOT LATER AS HEAP *** + * | v | + * +--------+--------------+------+ 0x4084FFFF / 0x4084FFFF - HP SRAM END +``` diff --git a/bootloader/mcuboot/docs/readme-mbed.md b/bootloader/mcuboot/docs/readme-mbed.md new file mode 100644 index 0000000..2162678 --- /dev/null +++ b/bootloader/mcuboot/docs/readme-mbed.md @@ -0,0 +1,41 @@ +# MCUboot port for Mbed OS + +This is an MCUboot port for Mbed OS. + +## Using MCUboot + +Note: The following is a general overview. It does not cover MCUboot or Mbed OS basics. + +See https://github.com/AGlass0fMilk/mbed-mcuboot-demo as a detailed example. + +### Basic configurations + +To use MCUboot, you need to create an Mbed OS project with the following configurations: +* `"mcuboot.primary-slot-address"`: address of the primary slot in the internal flash +* `"mcuboot.slot-size"`: size of an image slot (only one image, two slots are currently supported) +* `"mcuboot.max-img-sectors"`: maximum number of sectors, should be at least the number of sectors in each slot +* `"target.restrict_size"`: the maximum size of the bootloader, such that it does not overlap with the primary slot + +More configurations such as signing algorithm, slot swapping, etc. can be found in [mbed_lib.json](https://github.com/mcu-tools/mcuboot/tree/main/boot/mbed/mbed_lib.json). Please note that certain features are not currently supported. + +### Providing a secondary slot + +You need to provide an instance of `mbed::BlockDevice` as the secondary slot. It can be any types of internal or external storage provided that: +* Its size equals the `"mcuboot.slot-size"` you have set +* Its minimum supported read and write sizes (granularities) are _no larger than_ 16 byte, which MCUboot's read/write operations are aligned to. If the read size is larger than _one byte_, you need to set `"mcuboot.read-granularity"` to the read size of the storage - this buffers smaller read operations. + +In order for MCUboot to access your secondary slot, the interface to implement is +```cpp +mbed::BlockDevice* get_secondary_bd(void); +``` +which should return an uninitialized instance of BlockDevice. + +### Building the bootloader + +To build a bootloader based on MCUboot, make sure `"mcuboot.bootloader-build"` is `true` (already the default) and you have provided configurations and a secondary slot BlockDevice as explained above. + +### Building a user application + +To build a user application, set `"mcuboot.bootloader-build"` to `false` so MCUboot is built as a _library only_ without a bootloader application. This is useful if your user application needs to confirm the current image with `boot_set_confirmed()` after an update, or set a new image in the secondary slot as pending with `boot_set_pending()` in order to trigger an update upon reboot. + +As your application starts in the primary slots (instead of the beginning of the whole flash), you need to set the start address (`"target.mbed_app_start"`) to be equal to `"mcuboot.primary-slot-address"` + `"mcuboot.header-size"` of your bootloader. And its size (`"target.mbed_app_size"`) must be no larger than `"mcuboot.slot-size"` - `"mcuboot.header-size"`, and some space must be left for the image trailer too (see [this](design.md#image-trailer)). diff --git a/bootloader/mcuboot/docs/readme-mynewt.md b/bootloader/mcuboot/docs/readme-mynewt.md new file mode 100644 index 0000000..0ead2a4 --- /dev/null +++ b/bootloader/mcuboot/docs/readme-mynewt.md @@ -0,0 +1,52 @@ +# Running mynewt apps with MCUboot + +Due to small differences between Mynewt's bundled bootloader and MCUboot, +when building an app that will be run with MCUboot as the bootloader and +which at the same time requires to use `newtmgr` to manage images, MCUboot +must be added as a new dependency for this app. + +First you need to add the repo to your `project.yml`: + +``` + project.repositories: + - mcuboot + + repository.mcuboot: + type: github + vers: 0-dev + user: mcu-tools + repo: mcuboot +``` + +Then update your app's `pkg.yml` adding the extra dependency: + +``` + pkg.deps: + - "@mcuboot/boot/bootutil" +``` + +Also remove any dependency on `boot/bootutil` (mynewt's bundled bootloader) +which might exist. + +To configure MCUboot check all the options available in +`boot/mynewt/mcuboot_config/syscfg.yml`. + +Also, MCUboot uses a different image header struct as well as slightly +different TLV structure, so images created by `newt` have to be generated +in this new format. That is done by passing the extra parameter `-2` as in: + +`newt create-image -2` + +# Boot serial functionality with Mynewt + +Building with `BOOT_SERIAL: 1` enables some basic management functionality +like listing images and uploading a new image to `slot0`. The serial bootloader +requires that `mtu` is set to a value that is less than or equal to `256`. +This can be done either by editing `~/.newtmgr.cp.json` and setting the `mtu` +for the connection profile, or specifying you connection string manually as in: + +``` +newtmgr --conntype serial --connstring "dev=/dev/ttyUSB0,mtu=256" image upload -e blinky.img +``` + +where `/dev/ttyUSB0` is your serial port. diff --git a/bootloader/mcuboot/docs/readme-nuttx.md b/bootloader/mcuboot/docs/readme-nuttx.md new file mode 100644 index 0000000..1c06964 --- /dev/null +++ b/bootloader/mcuboot/docs/readme-nuttx.md @@ -0,0 +1,52 @@ +# MCUboot port for NuttX + +## Description + +The NuttX port of MCUboot secure boot library expects that the platform provides a Flash storage with the following partitions: +- `CONFIG_MCUBOOT_PRIMARY_SLOT_PATH`: MTD partition for the application firmware image PRIMARY slot; +- `CONFIG_MCUBOOT_SECONDARY_SLOT_PATH`: MTD partition for the application firmware image SECONDARY slot; +- `CONFIG_MCUBOOT_SCRATCH_PATH`: MTD partition for the Scratch area; + +Also, these are optional features that may be enabled: + +- `CONFIG_MCUBOOT_WATCHDOG`: If `CONFIG_WATCHDOG` is enabled, MCUboot shall reset the watchdog timer indicated by `CONFIG_MCUBOOT_WATCHDOG_DEVPATH` to the current timeout value, preventing any imminent watchdog timeouts. + +The porting layer of MCUboot library consists of the following interfaces: +- ``, for enabling MCUboot to manage the application firmware image slots in the device storage. +- ``, for configuration of MCUboot's features. +- ``, for providing logging capabilities. +- ``, for providing MCUboot access to the OS memory management interfaces. +- ``, for configuration of the system's flash area organization. + +The NuttX port of MCUboot is implemented at application-level and requires minimal knowledge about characteristics of the underlying storage device. This is achieved by means of the `BCH` and `FTL` subsystems, which enable MCUboot to manage MTD partitions via character device drivers using standard POSIX filesystem operations (e.g. `open()` / `close()` / `read()` / `write()`). + +## Creating MCUboot-compatible application firmware images + +One common use case for MCUboot is to integrate it to a firmware update agent, which is an important component of a secure firmware update subsystem. Through MCUboot APIs an application is able to install a newly received application firmware image and, once this application firmware image is assured to be valid, the application may confirm it as a stable image. In case that application firmware image is deemed bogus, MCUboot provides an API for invalidating that update, which will induce a rollback procedure to the most recent stable application firmware image. + +The `CONFIG_MCUBOOT_UPDATE_AGENT_EXAMPLE` example demonstrates this workflow by downloading an application firmware image from a webserver, installing it and triggering the firmware update process for the next boot after a system reset. There is also the `CONFIG_MCUBOOT_SLOT_CONFIRM_EXAMPLE`, which is a fairly simple example that just calls an MCUboot API for confirming the executing application firmware image as stable. + +## Using MCUboot on NuttX as a secure boot solution + +NuttX port for MCUboot also enables the creation of a secure bootloader application requiring minimal platform-specific implementation. The logical implementation for the secure boot is performed at application-level by the MCUboot library. Once MCUboot validates the application firmware image, it delegates the loading and execution of the application firmware image to a platform-specific routine, which is accessed via `boardctl(BOARDIOC_BOOT_IMAGE)` call. Each platform must then provide an implementation for the `board_boot_image()` for executing the required actions in order to boot a new application firmware image (e.g. deinitialize peripherals, load the Program Counter register with the application firmware image entry point address). + +The MCUboot bootloader application may be enabled by selecting the `CONFIG_MCUBOOT_BOOTLOADER` option. + +## Assumptions + +### IOCTL MTD commands + +The implementation of `` expects that the MTD driver for a given image partition handles the following `ioctl` commands: +- `MTDIOC_GEOMETRY`, for retrieving information about the geometry of the MTD, required for the configuration of the size of each flash area. +- `MTDIOC_ERASESTATE`, for retrieving the byte value of an erased cell of the MTD, required for the implementation of `flash_area_erased_val()` interface. + +### Write access alignment + +Through `flash_area_align()` interface MCUboot expects that the implementation provides the shortest data length that may be written via `flash_area_write()` interface. The NuttX implementation passes through the `BCH` and `FTL` layers, which appropriately handle the write alignment restrictions of the underlying MTD. So The NuttX implementation of `flash_area_align()` is able to return a fixed value of 1 byte, even if the MTD does not support byte operations. + +## Limitations + +### `` functions are not multitasking-safe + +MCUboot's documentation imposes no restrictions regarding the usage of its public interfaces, which doesn't mean they are thread-safe. +But, regarding NuttX implementation of the ``, it is safe to state that they are **not** multitasking-safe. NuttX implementation manages the MTD partitions via character device drivers. As file-descriptors cannot be shared between different tasks, if one task calls `flash_area_open` and another task calls `flash_area_` passing the same `struct flash_area` instance, it will result in failure. diff --git a/bootloader/mcuboot/docs/readme-riot.md b/bootloader/mcuboot/docs/readme-riot.md new file mode 100644 index 0000000..3aba53b --- /dev/null +++ b/bootloader/mcuboot/docs/readme-riot.md @@ -0,0 +1,47 @@ +# Building and using MCUboot with RIOT + +MCUboot began its life as the bootloader for Mynewt. It has since +acquired the ability to be used as a bootloader for RIOT as well. +Currently the support is limited to the nrf52dk platform. + +## Building the bootloader itself + +In this first version, a prebuilt Mynewt binary is downloaded at +compile time. This binary was compiled to do an integrity check, but +not a signature check. In order to configure the bootloader for +signature check it is necessary to re-compile it either with Mynewt +or Zephyr, following the provided instructions. + +In the next version, it is planned to compile MCUboot using RIOT, +which should be able to boot any of the supported OS images. + +## Building applications for the bootloader + +A compatible MCUboot image can be compiled by typing: `make mcuboot`. + +The only variable which needs to be set is `IMAGE_VERSION` loaded +with a valid formatted value. The format is `major.minor.patch+other` +(e.g. `export IMAGE_VERSION= 1.1.1+1`. This variable can be either +exported in the Makefile or manually, prior to the compilation process. + +The goal is to produce an ELF file which is linked to be flashed at a +`BOOTLOADER_OFFSET` offset rather than the beginning of ROM. MCUboot +also expects an image padded with some specific headers containing the +version information, and trailer type-length-value records (TLVs) with +hash and signing information. This is done through the imgtool.py +application, which is executed automatically by the RIOT build system. + +### Signing the application + +The application will be automatically signed with the provided key. +If no key is provided, a new key will be automatically generated. The +default key type is RSA-2048. + +In order to use your provided key, you need to recompile the bootloader +using you public key, either in Zephyr or Mynewt by following the +provided procedure for the selected OS. + +### Flashing the application + +The application can be flashed by typing: `make flash-mcuboot`. +This will flash both the bootloader and the application. diff --git a/bootloader/mcuboot/docs/readme-zephyr.md b/bootloader/mcuboot/docs/readme-zephyr.md new file mode 100644 index 0000000..1e55ea7 --- /dev/null +++ b/bootloader/mcuboot/docs/readme-zephyr.md @@ -0,0 +1,256 @@ +# Building and using MCUboot with Zephyr + +MCUboot began its life as the bootloader for Mynewt. It has since +acquired the ability to be used as a bootloader for Zephyr as well. +There are some pretty significant differences in how apps are built +for Zephyr, and these are documented here. + +Please see the [design document](design.md) for documentation on the design +and operation of the bootloader itself. This functionality should be the same +on all supported RTOSs. + +The first step required for Zephyr is making sure your board has flash +partitions defined in its device tree. These partitions are: + +- `boot_partition`: for MCUboot itself +- `slot0_partition`: the primary slot of Image 0 +- `slot1_partition`: the secondary slot of Image 0 + +It is not recommended to use the swap-using-scratch algorithm of MCUboot, but +if this operating mode is desired then the following flash partition is also +needed (see end of this help file for details on creating a scratch partition +and how to use the swap-using-scratch algorithm): + +- `scratch_partition`: the scratch slot + +Currently, the two image slots must be contiguous. If you are running +MCUboot as your stage 1 bootloader, `boot_partition` must be configured +so your SoC runs it out of reset. If there are multiple updateable images +then the corresponding primary and secondary partitions must be defined for +the rest of the images too (for example, `slot2_partition` and +`slot3_partition` for Image 1). + +The flash partitions are typically defined in the Zephyr boards folder, in a +file named `boards///.dts`. An example `.dts` file with +flash partitions defined is the frdm_k64f's in +`boards/arm/frdm_k64f/frdm_k64f.dts`. Make sure the DT node labels in your board's +`.dts` file match the ones used there. + +## Installing requirements and dependencies + +Install additional packages required for development with MCUboot: + +``` + cd ~/mcuboot # or to your directory where MCUboot is cloned + pip3 install --user -r scripts/requirements.txt +``` + +## Building the bootloader itself + +The bootloader is an ordinary Zephyr application, at least from +Zephyr's point of view. There is a bit of configuration that needs to +be made before building it. Most of this can be done as documented in +the `CMakeLists.txt` file in boot/zephyr. There are comments there for +guidance. It is important to select a signature algorithm, and decide +if the primary slot should be validated on every boot. + +To build MCUboot, create a build directory in boot/zephyr, and build +it as usual: + +``` + cd boot/zephyr + west build -b +``` + +In addition to the partitions defined in DTS, some additional +information about the flash layout is currently required to build +MCUboot itself. All the needed configuration is collected in +`boot/zephyr/include/target.h`. Depending on the board, this information +may come from board-specific headers, Device Tree, or be configured by +MCUboot on a per-SoC family basis. + +After building the bootloader, the binaries should reside in +`build/zephyr/zephyr.{bin,hex,elf}`, where `build` is the build +directory you chose when running `west build`. Use `west flash` +to flash these binaries from the build directory. Depending +on the target and flash tool used, this might erase the whole of the flash +memory (mass erase) or only the sectors where the bootloader resides prior to +programming the bootloader image itself. + +## Building applications for the bootloader + +In addition to flash partitions in DTS, some additional configuration +is required to build applications for MCUboot. + +This is handled internally by the Zephyr configuration system and is wrapped +in the `CONFIG_BOOTLOADER_MCUBOOT` Kconfig variable, which must be enabled in +the application's `prj.conf` file. + +The directory `samples/zephyr/hello-world` in the MCUboot tree contains +a simple application with everything you need. You can try it on your +board and then just make a copy of it to get started on your own +application; see samples/zephyr/README.md for a tutorial. + +The Zephyr `CONFIG_BOOTLOADER_MCUBOOT` configuration option +[documentation](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_BOOTLOADER_MCUBOOT) +provides additional details regarding the changes it makes to the image +placement and generation in order for an application to be bootable by MCUboot. + +With this, build the application as your normally would. + +### Signing the application + +In order to upgrade to an image (or even boot it, if +`MCUBOOT_VALIDATE_PRIMARY_SLOT` is enabled), the images must be signed. +To make development easier, MCUboot is distributed with some example +keys. It is important to stress that these should never be used for +production, since the private key is publicly available in this +repository. See below on how to make your own signatures. + +Images can be signed with the `scripts/imgtool.py` script. It is best +to look at `samples/zephyr/Makefile` for examples on how to use this. + +### Flashing the application + +The application itself can flashed with regular flash tools, but will +need to be programmed at the offset of the primary slot for this particular +target. Depending on the platform and flash tool you might need to manually +specify a flash offset corresponding to the primary slot starting address. This +is usually not relevant for flash tools that use Intel Hex images (.hex) instead +of raw binary images (.bin) since the former include destination address +information. Additionally you will need to make sure that the flash tool does +not perform a mass erase (erasing the whole of the flash) or else you would be +deleting MCUboot. +These images can also be marked for upgrade, and loaded into the secondary slot, +at which point the bootloader should perform an upgrade. It is up to +the image to mark the primary slot as "image ok" before the next reboot, +otherwise the bootloader will revert the application. + +## Managing signing keys + +The signing keys used by MCUboot are represented in standard formats, +and can be generated and processed using conventional tools. However, +`scripts/imgtool.py` is able to generate key pairs in all of the +supported formats. See [the docs](imgtool.md) for more details on +this tool. + +### Generating a new keypair + +Generating a keypair with imgtool is a matter of running the keygen +subcommand: + +``` + $ ./scripts/imgtool.py keygen -k mykey.pem -t rsa-2048 +``` + +The argument to `-t` should be the desired key type. See the +[the docs](imgtool.md) for more details on the possible key types. + +### Extracting the public key + +The generated keypair above contains both the public and the private +key. It is necessary to extract the public key and insert it into the +bootloader. Use the ``CONFIG_BOOT_SIGNATURE_KEY_FILE`` Kconfig option to +provide the path to the key file so the build system can extract +the public key in a format usable by the C compiler. +The generated public key is saved in `build/zephyr/autogen-pubkey.h`, which is included +by the `boot/zephyr/keys.c`. + +Currently, the Zephyr RTOS port limits its support to one keypair at the time, +although MCUboot's key management infrastructure supports multiple keypairs. + +Once MCUboot is built, this new keypair file (`mykey.pem` in this +example) can be used to sign images. + +## Using swap-using-scratch flash algorithm + +To use the swap-using-scratch flash algorithm, a scratch partition needs to be +present for the target board which is used for holding the data being swapped +from both slots, this section must be at least as big as the largest sector +size of the 2 partitions (e.g. if a device has a primary slot in main flash +with a sector size of 512 bytes and secondar slot in external off-chip flash +with a sector size of 4KB then the scratch area must be at least 4KB in size). +The number of sectors must also be evenly divisable by this sector size, e.g. +4KB, 8KB, 12KB, 16KB are allowed, 7KB, 7.5KB are not. This scratch partition +needs adding to the .dts file for the board, e.g. for the nrf52dk_nrf52832 +board thus would involve updating +`/boards/nordic/nrf52dk/nrf52dk_nrf52832.dts` with: + +``` + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0xc000>; + }; + slot0_partition: partition@c000 { + label = "image-0"; + reg = <0x0000C000 0x37000>; + }; + slot1_partition: partition@43000 { + label = "image-1"; + reg = <0x00043000 0x37000>; + }; + scratch_partition: partition@7a000 { + label = "image-scratch"; + reg = <0x0007a000 0x00006000>; + }; +``` + +Which would make the application size 220KB and scratch size 24KB (the nRF52832 +has a 4KB sector size so the size of the scratch partition can be reduced at +the cost of vastly reducing flash lifespan, e.g. for a 32KB firmware update +with an 8KB scratch area, the scratch area would be erased and programmed 8 +times per image upgrade/revert). To configure MCUboot to work in +swap-using-scratch mode, the Kconfig value must be set when building it: +`CONFIG_BOOT_SWAP_USING_SCRATCH=y`. + +Note that it is possible for an application to get into a stuck state when +swap-using-scratch is used whereby an application has loaded a firmware update +and marked it as test/confirmed but MCUboot will not swap the images and +erasing the secondary slot from the zephyr application returns an error +because the slot is marked for upgrade. + +## Serial recovery + +### Interface selection + +A serial recovery protocol is available over either a hardware serial port or a USB CDC ACM virtual serial port. +The SMP server implementation can be enabled by the ``CONFIG_MCUBOOT_SERIAL=y`` Kconfig option. +To set a type of an interface, use the ``BOOT_SERIAL_DEVICE`` Kconfig choice, and select either the ``CONFIG_BOOT_SERIAL_UART`` or the ``CONFIG_BOOT_SERIAL_CDC_ACM`` value. +Which interface belongs to the protocol shall be set by the devicetree-chosen node: +- `zephyr,console` - If a hardware serial port is used. +- `zephyr,cdc-acm-uart` - If a virtual serial port is used. + +### Entering the serial recovery mode + +To enter the serial recovery mode, the device has to initiate rebooting, and a triggering event has to occur (for example, pressing a button). + +By default, the serial recovery GPIO pin active state enters the serial recovery mode. +Use the ``mcuboot_button0`` devicetree button alias to assign the GPIO pin to the MCUboot. + +Alternatively, MCUboot can wait for a limited time to check if DFU is invoked by receiving an MCUmgr command. +Select ``CONFIG_BOOT_SERIAL_WAIT_FOR_DFU=y`` to use this mode. ``CONFIG_BOOT_SERIAL_WAIT_FOR_DFU_TIMEOUT`` option defines +the amount of time in milliseconds the device will wait for the trigger. + +### Direct image upload + +By default, the SMP server implementation will only use the first slot. +To change it, invoke the `image upload` MCUmgr command with a selected image number, and make sure the ``CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y`` Kconfig option is enabled. +Note that the ``CONFIG_UPDATEABLE_IMAGE_NUMBER`` Kconfig option adjusts the number of image-pairs supported by the MCUboot. + +The mapping of image number to partition is as follows: +* 0 and 1 - image-0, the primary slot of the first image. +* 2 - image-1, the secondary slot of the first image. +* 3 - image-2. +* 4 - image-3. + +0 is a default upload target when no explicit selection is done. + +### System-specific commands + +Use the ``CONFIG_ENABLE_MGMT_PERUSER=y`` Kconfig option to enable the following additional commands: +* Storage erase - This command allows erasing the storage partition (enable with ``CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y``). +* Custom image list - This command allows fetching version and installation status (custom properties) for all images (enable with ``CONFIG_BOOT_MGMT_CUSTOM_IMG_LIST=y``). + +### More configuration + +For details on other available configuration options for the serial recovery protocol, check the Kconfig options (for example by using ``menuconfig``). diff --git a/bootloader/mcuboot/docs/release-notes.d/00readme.md b/bootloader/mcuboot/docs/release-notes.d/00readme.md new file mode 100644 index 0000000..12b8c12 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/00readme.md @@ -0,0 +1,29 @@ +# Pending release notes directory + +This directory contains release note entries that have not been merged +into the main release-notes.md document. + +Generally a release note entry should be created for changes that: + +- Fix bugs in the code. +- Implement new features. +- Change existing behavior. + +Release notes are generally not needed for: + +- Some documentation improvements. +- Strictly internal changes to the code that won't be visible to users + of the code. + +## Release note format + +Release notes are included in files under this `docs/release-notes.d` +directory and have a name of `*.md`. They will be included in the +`release-notes.md` file, and should be formatted as a Markdown list +entry. (A script will be developed to collect these, ordered by when +the commits were added to the tree.) + +Choose a filename that is related to what this change does. The names +are not used for anything in particular, but to keep the files +distinct so that there isn't a concern with merge conflicts as +different pull requests merge in different orders. diff --git a/bootloader/mcuboot/docs/release-notes.d/bootutil-enc-hw-keys.md b/bootloader/mcuboot/docs/release-notes.d/bootutil-enc-hw-keys.md new file mode 100644 index 0000000..2640b67 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/bootutil-enc-hw-keys.md @@ -0,0 +1,2 @@ +- Added support for retrieving hw embed private keys for image encryption + (The private key can be retrieved from trusted sources like OTP, TPM.). diff --git a/bootloader/mcuboot/docs/release-notes.d/bootutil-image-verification.md b/bootloader/mcuboot/docs/release-notes.d/bootutil-image-verification.md new file mode 100644 index 0000000..a1cc588 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/bootutil-image-verification.md @@ -0,0 +1,4 @@ +- Changed bootutil's order of events to verify the image header + before checking the image. +- Added the bootloader state object to the bootutil + boot_is_header_valid() function diff --git a/bootloader/mcuboot/docs/release-notes.d/encrypted-scratch-partition.md b/bootloader/mcuboot/docs/release-notes.d/encrypted-scratch-partition.md new file mode 100644 index 0000000..ea51605 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/encrypted-scratch-partition.md @@ -0,0 +1,3 @@ +- When using swap with scratch, the image is now decrypted when copying from + the scratch partition to the primary slot. Therefore, the sratch partition + doesn't contain plaintext firmware data anymore. diff --git a/bootloader/mcuboot/docs/release-notes.d/fix-nordic.md b/bootloader/mcuboot/docs/release-notes.d/fix-nordic.md new file mode 100644 index 0000000..9706f8a --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/fix-nordic.md @@ -0,0 +1,2 @@ +- Fixed errors when building for ``thingy52``, ``thingy53`` and + ``nrf9160dk`` boards. diff --git a/bootloader/mcuboot/docs/release-notes.d/fix-ram-load-zephyr-address.md b/bootloader/mcuboot/docs/release-notes.d/fix-ram-load-zephyr-address.md new file mode 100644 index 0000000..c636bc9 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/fix-ram-load-zephyr-address.md @@ -0,0 +1,2 @@ +- Fixed chain load address output log message for RAM load + mode in Zephyr diff --git a/bootloader/mcuboot/docs/release-notes.d/fix-zephyr-sysbuild-name.md b/bootloader/mcuboot/docs/release-notes.d/fix-zephyr-sysbuild-name.md new file mode 100644 index 0000000..1e241b6 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/fix-zephyr-sysbuild-name.md @@ -0,0 +1,2 @@ +- Fixed clash when using sysbuild with other + applications (i.e. tests) using the name sysbuild diff --git a/bootloader/mcuboot/docs/release-notes.d/imgtool_sanity_test.md b/bootloader/mcuboot/docs/release-notes.d/imgtool_sanity_test.md new file mode 100644 index 0000000..5123921 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/imgtool_sanity_test.md @@ -0,0 +1,2 @@ +- imgtool: added initial sanity tests for imgtool commands, +- imgtool: added and enabled unittests in GitGub workflow, diff --git a/bootloader/mcuboot/docs/release-notes.d/max-app-size-changes.md b/bootloader/mcuboot/docs/release-notes.d/max-app-size-changes.md new file mode 100644 index 0000000..21d25db --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/max-app-size-changes.md @@ -0,0 +1,4 @@ +- Fixed wrong maximum application size calculation when + operating in swap using move mode +- Added additional images max size support to shared data + function diff --git a/bootloader/mcuboot/docs/release-notes.d/serial-recovery-slot-info.md b/bootloader/mcuboot/docs/release-notes.d/serial-recovery-slot-info.md new file mode 100644 index 0000000..fbd3335 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/serial-recovery-slot-info.md @@ -0,0 +1 @@ +- Added slot info command support to serial recovery mode diff --git a/bootloader/mcuboot/docs/release-notes.d/zephyr-auto-max-sectors.md b/bootloader/mcuboot/docs/release-notes.d/zephyr-auto-max-sectors.md new file mode 100644 index 0000000..a45aeb0 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/zephyr-auto-max-sectors.md @@ -0,0 +1,5 @@ +- Added support for automatically calculating the maximum number + of sectors that are needed for a build by checking the erase + sizes of the partitions using CMake for Zephyr. This behaviour + can be reverted to the old manual behaviour by disabling + ``CONFIG_BOOT_MAX_IMG_SECTORS_AUTO`` diff --git a/bootloader/mcuboot/docs/release-notes.d/zephyr-compression.md b/bootloader/mcuboot/docs/release-notes.d/zephyr-compression.md new file mode 100644 index 0000000..ba9ec2a --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.d/zephyr-compression.md @@ -0,0 +1,7 @@ +- Added protected TLV size to image size check in bootutil +- Added Kconfig for decompression support in Zephyr +- Added compressed image flags and TLV to bootutil +- Added support for removing images with conflicting flags in + bootutil +- Added support for removing encrypted/compressed images when + MCUboot is compiled without support for them diff --git a/bootloader/mcuboot/docs/release-notes.md b/bootloader/mcuboot/docs/release-notes.md new file mode 100644 index 0000000..728a5b4 --- /dev/null +++ b/bootloader/mcuboot/docs/release-notes.md @@ -0,0 +1,553 @@ +# MCUboot release notes + +- Table of Contents +{:toc} + +## Version 2.1.0 + +- Boot serial: Add response to echo command if support is not + enabled, previously the command would have been accepted but no + response indicating that the command is not supported would have + been sent. +- Added support for using builtin keys for image validation + (available with the PSA Crypto API based crypto backend for ECDSA signatures). +- Enforce that TLV entries that should be protected are. + This can be disabled by defining `ALLOW_ROGUE_TLVS` +- bootutil: Fixed issue with comparing sector sizes for + compatibility, this now also checks against the number of usable + sectors (which is the slot size minus the swap status and moved + up by one sector). +- bootutil: Added debug logging to show write location of swap status + and details on sectors including if slot sizes are not optimal for + a given board. +- Update ptest to support test selection. Ptest can now be invoked with `list` + to show the available tests and `run` to run them. The `-t` argument will + select specific tests to run. +- Allow sim tests to skip slow tests. By setting `MCUBOOT_SKIP_SLOW_TESTS` in + the environment, the sim will skip two tests that are very slow. In one + instance this reduces the test time from 2 hours to about 5 minutes. These + slow tests are useful, in that they test bad powerdown recovery, but are + inconvenient when testing other areas. +- Zephyr: Fixes support for disabling instruction/data caches prior + to chain-loading an application, this will be automatically + enabled if one or both of these caches are present. This feature + can be disabled by setting `CONFIG_BOOT_DISABLE_CACHES` to `n`. +- Zephyr: Fix issue with single application slot mode, serial + recovery and encryption whereby an encrypted image is loaded + and being wrongly treated as encrypted after decryption. +- Zephyr: Add estimated image footer size to cache in sysbuild. +- Added firmware loader configuration type support for Zephyr, this + allows for a single application slot and firmware loader image in + the secondary slot which is used to update the primary image + (loading it in any way it sees fit e.g. via Bluetooth). +- Zephyr: Remove deprecated ZEPHYR_TRY_MASS_ERASE Kconfig option. +- Zephyr: Prevent MBEDTLS Kconfig selection when tinycrypt is used. +- Zephyr: Add USB CDC serial recovery check that now causes a build + failure if console is enabled and device is the same as the USB + CDC device. +- Zephyr: Add USB CDC serial recovery check that now causes a build + failure if the main thread priority is below 0 (cooperative + thread), this would prevent USB CDC from working as the driver + would not have been able to fire callbacks. +- Use general flash operations to determine the flash reset vector. This + improves support a bit for some configurations of external flash. +- fix a memory leak in the HKDF implementation. +- Zephyr: Added a MCUboot banner which displays the version of + MCUboot being used and the version of zephyr. This can be + disabled by setting ``CONFIG_MCUBOOT_BOOT_BANNER=n`` which + will revert back to the default zephyr boot banner. + +## Version 2.0.0 + +Note that this release, 2.0.0 is a new major number, and contains a small API +change in the interface between mcuboot and the platform. All platforms +contained within the MCUboot tree have been updated, but any external platforms +will have to be adjusted. The following commit makes the API change, in the +function `boot_save_shared_data`. + + commit 3016d00cd765e7c09a14af55fb4dcad945e4b982 + Author: Jamie McCrae + Date: Tue Mar 14 12:35:51 2023 +0000 + + bootutil: Add active slot number and max app size to shared data + +### About this release + +- Add error when flash device fails to open. +- Panic bootloader when flash device fails to open. +- Fixed issue with serial recovery not showing image details for + decrypted images. +- Fixes issue with serial recovery in single slot mode wrongly + iterating over 2 image slots. +- CDDL auto-generated function code has been replaced with zcbor function + calls, this now allows the parameters to be supplied in any order. +- Added currently running slot ID and maximum application size to + shared data function definition. +- Make the ECDSA256 TLV curve agnostic and rename it to ECDSA_SIG. +- imgtool: add P384 support along with SHA384. +- espressif: refactor after removing IDF submodule +- espressif: add ESP32-C6, ESP32-C2 and ESP32-H2 new chips support +- espressif: adjustments after IDF v5.1 compatibility, secure boot build and memory map organization +- Serial recovery image state and image set state optional commands added +- imgtool: add 'dumpinfo' command for signed image parsing. +- imgtool: add 'getpubhash' command to dump the sha256 hash of the public key +- imgtool's getpub can print the output to a file +- imgtool can dump the raw versions of the public keys +- Drop ECDSA P224 support +- Fixed an issue with boot_serial repeats not being processed when + output was sent, this would lead to a divergence of commands + whereby later commands being sent would have the previous command + output sent instead. +- Fixed an issue with the boot_serial zcbor setup encoder function + wrongly including the buffer address in the size which caused + serial recovery to fail on some platforms. +- zcbor library files have been updated to version 0.7.0 +- Reworked boot serial extensions so that they can be used by modules + or from user repositories by switching to iterable sections. +- Removed Zephyr custom img list boot serial extension support. +- (Zephyr) Adds support for sharing boot information with + application via retention subsystem +- Zephyr no longer builds in optimize for debug mode, this saves a + significant amount of flash space. +- Reworked image encryption support for Zephyr, static dummy key files + are no longer in the code, a pem file must be supplied to extract + the private and public keys. The Kconfig menu has changed to only + show a single option for enabling encryption and selecting the key + file. +- Serial recovery can now read and handle encrypted seondary slot + partitions. +- Serial recovery with MBEDTLS no longer has undefined operations which + led to usage faults when the secondary slot image was encrypted. +- espressif: allow the use of a different toolchain for building + +## Version 1.10.0 + +The 1.10.0 release of MCUboot contains... + +### About this release + +- Various fixes to boot serial. +- Various fixes to the mbed target. +- Various fixes to the Espressif native target. +- Various fixes to the Zephyr target. +- Workflow improvements with Zephyr CI. +- Add multi image support to the espressif esp32 target. +- Improvements and corrections to the simulator. +- Improve imgtool, including adding 3rd party signing support. +- Various fixes to the mynewt target. +- Various fixes to the nuttx target. +- Dates to dependencies for doc generation. +- Add downgrade prevention for modes using swap. +- Various general fixes to the boot code. +- Prefer swap move on zephyr if the scratch partition is not enabled. +- Upgrade fault-injection hardening, improving cases injections are detected. +- Add a new flash api `flash_area_get_sector`, along with support for each + target, that replaces `flash_area_sector_from_off`. This is a step in cleaning + up the flash API used by MCUboot. + +### Security fixes + +There are no security vulnerabilities reported on the MCUboot code for this +release. There have been several updates to the dependencies in the Ruby code +used to generate the documentation. This should only affect users that generate +their own documentation. + +## Version 1.9.0 + +The 1.9.0 release of MCUboot contains various bug fixes, improves +support on some recent targets, and adds support for devices with a +write alignment larger than 8. + +This change introduces a potentially incompatible change to the format +of the image trailer. If `BOOT_MAX_ALIGN` is kept at 8, the trailer +format does not change. However, to support larger write alignments, +this value can be increased, which will result in a different magic +number value. These targets were previously unsupported in MCUboot, +so this change should not affect any existing targets. The change has +been tested with a `BOOT_MAX_ALIGN` up to 32 bytes. + +### About this release + +- Add native flash encryption to Espressif targets +- Numerous documentation improvements +- Increase coverage of large images in the simulator +- Add stm32 watchdog support +- Add support for the `mimxrt685_evk` board +- Add support for "partial multi-image booting" +- Add support for clear image generation with encryption capability to + imgtool +- Fix Zephyr when `CONFIG_BOOT_ENCRYPTION_KEY_FILE` is not defined +- Remove zephyr example test running in shell. The Go version is + primary and much more featureful. +- imgtool: make `--max-align` default reasonable in most cases. +- Implement the mcumgr echo command in serial boot mode + +### Security fixes + +## Version 1.8.0 + +The 1.8.0 release of MCUboot contains numerous fixes, and adds support +for the NuttX RTOS, and the Espressif ESP32 SDK. + +### About this release + +- Add support for the NuttX RTOS. +- Add support for the Espressif ESP32 SDK. +- `boot_serial` changed to use cddl-gen, which removes the dependency + on tinycbor. +- Add various hooks to be able to change how image data is accessed. +- Cypress supports Mbed TLS for encryption. +- Support using Mbed TLS for ECDSA. This can be useful if Mbed TLS is + brought in for another reason. +- Add simulator support for testing direct-XIP and ramload. +- Support Mbed TLS 3.0. Updates the submodule for Mbed TLS to 3.0. +- Enable direct-xip mode in Mbed-OS port. +- extract `bootutil_public` library, a common interface for MCUboot + and the application. +- Allow to boot primary image if secondary one is unreachable. +- Add AES256 image encryption support. +- Add Multiimage boot for direct-xip and ram-load mode. +- Cargo files moved to top level, now `cargo test` can be run from the + top level directory. +- Fault injection tests use updated TF-M. +- Thingy:53 now supports multi-image DFU. +- ram load and image encryption can be used together, allowing the + entire contents of flash to always remain encrypted. + +### Security fixes + +- [GHSA-gcxh-546h-phg4](https://github.com/mcu-tools/mcuboot/security/advisories/GHSA-gcxh-546h-phg4) + has been published. There is not a fix at this time, but a caution + to be sure to follow the instructions carefully, and make sure that + the development keys in the repo are never used in a production + system. + +## Version 1.7.0 + +The 1.7.0 release of MCUboot adds support for the Mbed-OS platform, +Equal slots (direct-xip) upgrade mode, RAM loading upgrade mode, +hardening against hardware level fault injection and timing attacks +and single image mode. +There are bug fixes, and associated imgtool updates as well. + +### About this release + +- Initial support for the Mbed-OS platform. +- Added possibility to enter deep sleep mode after MCUboot app execution + for cypress platform. +- Added hardening against hardware level fault injection and timing attacks. +- Introduced Abstract crypto primitives to simplify porting. +- Added RAM-load upgrade mode. +- Renamed single-image mode to single-slot mode. +- Allow larger primary slot in swap-move +- Fixed boostrapping in swap-move mode. +- Fixed issue causing that interrupted swap-move operation might brick device + if the primary image was padded. +- Abstracting MCUboot crypto functions for cleaner porting +- Droped flash_area_read_is_empty() porting API. +- boot/zephyr: Added watchdog feed on nRF devices. + See `CONFIG_BOOT_WATCHDOG_FEED` option. +- boot/zephyr: Added patch for turning off cache for Cortex M7 before + chain-loading. +- boot/zephyr: added option to relocate interrupts to application +- boot/zephyr: clean ARM core configuration only when selected by user +- boot/boot_serial: allow nonaligned last image data chunk +- imgtool: added custom TLV support. +- imgtool: added possibility to set confirm flag for hex files as well. +- imgtool: Print image digest during verify. + +### Zephyr-RTOS compatibility + +This release of MCUboot works with the Zephyr "main" at the time of the +release. It was tested as of has 7a3b253ce. This version of MCUboot also +works with the Zephyr v2.4.0, however it is recommended to enable +`CONFIG_MCUBOOT_CLEANUP_ARM_CORE` while using that version. + +## Version 1.6.0 + +The 1.6.0 release of MCUboot adds support for the PSOC6 platform, +X25519 encrypted images, rollback protection, hardware keys, and a +shared boot record to communicate boot attestation information to +later boot stages. There are bug fixes, and associated imgtool +updates as well. + +### About this release + +- Initial support for the Cypress PSOC6 plaformt. This platform + builds using the Cypress SDK, which has been added as submodules. +- CBOR decoding in serial recovery replaced by code generated from a + CDDL description. +- Add support for X25519 encrypted images. +- Add rollback protection. There is support for a HW rollback counter + (which must be provided as part of the platform), as well as a SW + solution that protects against some types of rollback. +- Add an optional boot record in shared memory to communicate boot + attributes to later-run code. +- Add support for hardware keys. +- Various fixes to work with the latest Zephyr version. + +### Security issues addressed + +- CVE-2020-7595 "xmlStringLenDecodeEntities in parser.c in libxml2 + 2.9.10 has an infinite loop in a certain end-of-file situation." Fix + by updating a dependency in documentation generation. + +### Zephyr-RTOS compatibility + +This release of MCUboot works the Zephyr "main" at the time of the +release. It was tested as of has 1a89ca1238. When Zephyr v2.3.0 is +released, there will be a possible 1.6.1 or similar release of Zephyr +if needed to address any issues. There also may be branch releases of +MCUboot specifically for the current version of Zephyr, e.g. +v1.6.0-zephyr-2.2.1. + +## Version 1.5.0 + +The 1.5.0 release of MCUboot adds support for encrypted images using +ECIES with secp256r1 as an Elliptic Curve alternative to RSA-OAEP. A +new swap method was added which allows for upgrades without using a +scratch partition. There are also lots of bug fixes, extra simulator +testing coverage and some imgtool updates. + +### About this release + +- TLVs were updated to use 16-bit lengths (from previous 8). This + should work with no changes for little-endian targets, but will + break compatibility with big-endian targets. +- A benchmark framework was added to Zephyr +- ed25519 signature validation can now build without using Mbed TLS + by relying on a bundled tinycrypt based sha-512 implementation. +- imgtool was updated to correctly detect trailer overruns by image. +- Encrypted image TLVs can be saved in swap metadata during a swap + upgrade instead of the plain AES key. +- imgtool can dump private keys in C format (getpriv command), which + can be added as decryption keys. Optionally can remove superfluous + fields from the ASN1 by passing it `--minimal`. +- Lots of other smaller bugs fixes. +- Added downgrade prevention feature (available when the overwrite-based + image update strategy is used) + +### Known issues + +- TLV size change breaks compatibility with big-endian targets. + +## Version 1.4.0 + +The 1.4.0 release of MCUboot primarily adds support for multi-image +booting. With this release, MCUboot can manage two images that can be +updated independently. With this, it also supports additions to the +TLV that allow these dependencies to be specified. + +Multi-image support adds backward-incompatible changes to the format +of the images: specifically adding support for protected TLV entries. +If multiple images and dependencies are not used, the images will be +compatible with previous releases of MCUboot. + +### About this release + +- Fixed CVE-2019-5477, and CVE-2019-16892. These fix issue with + dependencies used in the generation of the documentation on github. +- Numerous code cleanups and refactorings +- Documentation updates for multi-image features +- Update imgtool.py to support the new features +- Updated the Mbed TLS submodule to current stable version 2.16.3 +- Moved the Mbed TLS submodule from within sim/mcuboot-sys to ext. + This will make it easier for other board supports to use this code. +- Added some additional overflow and bound checks to data in the image + header, and TLV data. +- Add a `-x` (or `--hex_addr`) flag to imgtool to set the base address + written to a hex-format image. This allows the image to be flashed + at an offset, without having to use additional tools to modify the + image. + +## Version 1.3.1 + +The 1.3.1 release of MCUboot consists mostly of small bug fixes and updates. +There are no breaking changes in functionality. This release should work with +Mynewt 1.6.0 and up, and any Zephyr `main` after sha +f51e3c296040f73bca0e8fe1051d5ee63ce18e0d. + +### About this release + +- Fixed a revert interruption bug +- Added ed25519 signing support +- Added RSA-3072 signing support +- Allow ec256 to run on CC310 interface +- Some preparation work was done to allow for multi image support, which + should land in 1.4.0. This includes a simulator update for testing + multi-images, and a new name for slot0/slot1 which are now called + "primary slot" and "secondary slot". +- Other minor bugfixes and improvements + +## Version 1.3.0 + +The 1.3.0 release of MCUboot brings in many fixes and updates. There +are no breaking changes in functionality. Many of the changes are +refactorings that will make the code easier to maintain going forward. +In addition, support has been added for encrypted images. See [the +docs](encrypted_images.md) for more information. + +### About this release + +- Modernize the Zephyr build scripts. +- Add a `ptest` utility to help run the simulator in different + configurations. +- Migrate the simulator to Rust 2018 edition. The sim now requires at + least Rust 1.32 to build. +- Simulator cleanups. The simulator code is now built the same way + for every configuration, and queries the MCUboot code for how it was + compiled. +- Abstract logging in MCUboot. This was needed to support the new + logging system used in Zephyr. +- Add multiple flash support. Allows slot1/scratch to be stored in an + external flash device. +- Add support for [encrypted images](encrypted_images.md). +- Add support for flash devices that read as '0' when erased. +- Add support to Zephyr for the `nrf52840_pca10059`. This board + supports serial recovery over USB with CDC ACM. +- imgtool is now also available as a python package on pypi.org. +- Add an option to erase flash pages progressively during recovery to + avoid possible timeouts (required especially by serial recovery + using USB with CDC ACM). +- imgtool: big-endian support +- imgtool: saves in intel-hex format when output filename has `.hex` + extension; otherwise saves in binary format. + +## Version 1.2.0 + +The 1.2.0 release of MCUboot brings a lot of fixes/updates, where much of the +changes were on the boot serial functionality and imgtool utility. There are +no breaking changes in MCUboot functionality, but some of the CLI parameters +in imgtool were changed (either removed or added or updated). + +### About this release + +- imgtool accepts .hex formatted input +- Logging system is now configurable +- Most Zephyr configuration has been switched to Kconfig +- Build system accepts .pem files in build system to autogenerate required + key arrays used internally +- Zephyr build switched to using built-in flash_map and TinyCBOR modules +- Serial boot has substantially decreased in space usage after refactorings +- Serial boot build doesn't require newlib-c anymore on Zephyr +- imgtool updates: + + "create" subcommand can be used as an alias for "sign" + + To allow imgtool to always perform the check that firmware does not + overflow the status area, `--slot-size` was added and `--pad` was updated + to act as a flag parameter. + + `--overwrite-only` can be passed if not using swap upgrades + + `--max-sectors` can be used to adjust the maximum amount of sectors that + a swap can handle; this value must also be configured for the bootloader + + `--pad-header` substitutes `--included-header` with reverted semantics, + so it's not required for firmware built by Zephyr build system + +### Known issues + +None + +## Version 1.1.0 + +The 1.1.0 release of MCUboot brings a lot of fixes/updates to its +inner workings, specially to its testing infrastructure which now +enables a more thorough quality assurance of many of the available +options. As expected of the 1.x.x release cycle, no breaking changes +were made. From the tooling perpective the main addition is +newt/imgtool support for password protected keys. + +### About this release + +- serial recovery functionality support under Zephyr +- simulator: lots of refactors were applied, which result in the + simulator now leveraging the Rust testing infrastructure; testing + of ecdsa (secp256r1) was added +- imgtool: removed PKCS1.5 support, added support for password + protected keys +- tinycrypt 0.2.8 and the Mbed TLS ASN1 parser are now bundled with + MCUboot (eg secp256r1 is now free of external dependencies!) +- Overwrite-only mode was updated to erase/copy only sectors that + actually store firmware +- A lot of small code and documentation fixes and updates. + +### Known issues + +None + +## Version 1.0.0 + +The 1.0.0 release of MCUboot introduces a format change. It is +important to either use the `imgtool.py` also from this release, or +pass the `-2` to recent versions of the `newt` tool in order to +generate image headers with the new format. There should be no +incompatible format changes throughout the 1.x.y release series. + +### About this release + +- Header format change. This change was made to move all of the + information about signatures out of the header and into the TLV + block appended to the image. This allows + - The signature to be replaced without changing the image. + - Multiple signatures to be applied. This can be used, for example, + to sign an image with two algorithms, to support different + bootloader configurations based on these image. + - The public key is referred to by its SHA1 hash (or a prefix of the + hash), instead of an index that has to be maintained with the + bootloader. + - Allow new types of signatures in the future. +- Support for PKCS#1 v1.5 signatures has been dropped. All RSA + signatures should be made with PSS. The tools have been changed to + reflect this. +- The source for Tinycrypt has been placed in the MCUboot tree. A + recent version of Tinycrypt introduced breaking API changes. To + allow MCUboot to work across various platforms, we stop using the + Tinycrypt bundled with the OS platform, and use our own version. A + future release of MCUboot will update the Tinycrypt version. +- Support for some new targets: + - Nordic nRF51 and nRF52832 dev kits + - Hexiwear K64 +- Clearer sample applications have been added under `samples`. +- Test plans for [zephyr](testplan-zephyr.md), and + [mynewt](testplan-mynewt.md). +- The simulator is now able to test RSA signatures. +- There is an unimplemented `load_addr` header for future support for + RAM loading in the bootloader. +- Numerous documentation. + +### Known issues + +None + +## Version 0.9.0 + +This is the first release of MCUboot, a secure bootloader for 32-bit MCUs. +It is designed to be operating system-agnostic and works over any transport - +wired or wireless. It is also hardware independent, and relies on hardware +porting layers from the operating system it works with. For the first release, +we have support for three open source operating systems: Apache Mynewt, Zephyr +and RIOT. + +### About this release + +- This release supports building with and running Apache Mynewt and Zephyr + targets. +- RIOT is supported as a running target. +- Image integrity is provided with SHA256. +- Image originator authenticity is provided supporting the following + signature algorithms: + - RSA 2048 and RSA PKCS#1 v1.5 or v2.1 + - Elliptic curve DSA with secp224r1 and secp256r1 +- Two firmware upgrade algorithms are provided: + - An overwrite only which upgrades slot 0 with the image in slot 1. + - A swapping upgrade which enables image test, allowing for rollback to a + previous known good image. +- Supports both Mbed TLS and tinycrypt as backend crypto libraries. One of them + must be defined and the chosen signing algorithm will require a particular + library according to this list: + - RSA 2048 needs Mbed TLS + - ECDSA secp224r1 needs Mbed TLS + - ECDSA secp256r1 needs tinycrypt as well as the ASN.1 code from Mbed TLS + (so still needs that present). + +### Known issues + +- The image header and TLV formats are planned to change with release 1.0: + https://runtimeco.atlassian.net/browse/MCUB-66 diff --git a/bootloader/mcuboot/docs/release.md b/bootloader/mcuboot/docs/release.md new file mode 100644 index 0000000..c6e88b8 --- /dev/null +++ b/bootloader/mcuboot/docs/release.md @@ -0,0 +1,133 @@ +# Release process + +This page describes the release process used with MCUboot. + +## Version numbering + +MCUboot uses [semantic versioning][semver], where version numbers +follow a `MAJOR.MINOR.PATCH` format with the following guidelines on +incrementing the numbers: + +1. MAJOR version when there are incompatible API changes. +2. MINOR version when new functionalities were added in a + backward-compatible manner. +3. PATCH version when there are backward-compatible bug fixes. + +We add pre-release tags using the format `MAJOR.MINOR.PATCH-rc1`. + +In the documentation, we mark an MCUBoot development version using the +format `MAJOR.MINOR.PATCH-dev`. + +## Release notes + +Before making a release, update the `docs/release-notes.md` file +to describe the release. This should be a high-level description of +the changes, not a list of the git commits. + +Provided that changes going into the release have followed the +contribution guidelines, this should mostly consist of collecting the +various snippets in the `docs/release-notes.d` directory. After +incorporating these snippets into the release notes, the snippet files +should be removed to ready the directory for the next release cycle. + +## Release candidates + +Before each release, tags are made (see below) for at least one +release candidate (a.b.c-rc1, followed by a.b.c-rc2 and the subsequent +release candidates, followed by the official a.b.c release). The intent +is to freeze the code and allow testing. + +During the time between the rc1 and the final release, the only changes +that should be merged into the main branch are those to fix bugs found +in the RC and in the Mynewt metadata, as described in the next section. + +## Imgtool release + +imgtool is released through pypi.org (The Python package index). +It requires its version to be updated by editing +`scripts/imgtool/__init__.py` and modifying the exported version: +``` +imgtool_version = "A.B.CrcN" +``` + +This version should match the current release number of MCUboot. The +suffix `rcN` (with no dash) is accepted only for the pre-release versions +under test, while numbers are accepted only for the final releases. + +For more information, see [this +link](https://www.python.org/dev/peps/pep-0440/#pre-releases). + +## Mynewt release information + +On Mynewt, `newt` always fetches a versioned MCUboot release, so after +the RC step is finished, the release needs to be exported by modifying +`repository.yml` in the root directory; it must be updated with the +new release version, including updates to the pseudo keys +(`*-(latest|dev)`). + +## Zephyr release information + +There is a version file used by Zephyr builds to indicate the version +of MCUboot being used which needs to be updated at +`boot/zephyr/VERSION`. For alignment with Zephyr versions, development +versions should set `PATCHLEVEL` to `99` and `EXTRAVERSION` to `dev`, +whilst production versions should correctly set `PATCHLEVEL` and clear +`EXTRAVERSION`. + +## Tagging and release + +To make a release, make sure your local repo is on the tip version by +fetching from origin. Typically, the releaser should create a branch +named after the particular release. + +Create a commit on top of the branch that modifies the version number +in the top-level `README.md`, and create a commit, with just this +change, with a commit text similar to "Bump to version a.b.c". +Having the version bump helps to make the releases +easier to find, as each release has a commit associated with it, and +not just a tag pointing to another commit. + +Once this is done, the release should create a signed tag with the +appropriate tag name: +``` bash +git tag -s va.b.c-rcn +``` +The releaser will need to make sure that git is configured to use the +proper signing key, and that the public key is signed by enough parties to +be trusted. + +At this point, the tag can be pushed to GitHub to make the actual release +happen: +``` bash +git push origin HEAD:refs/heads/main +git push origin va.b.c-rcn +``` + +## Branching after a release + +After the final (non-`rc`) a.b.0 release is made, a new branch must +be created and pushed: + +``` bash +git checkout va.b.c +git checkout -b va.b-branch +git push origin va.b-branch +``` + +This branch will be used to generate new incremental `PATCH` releases +for bug fixes or required minor updates (for example, new `imgtool` features). + +## Post release actions + +Mark the MCUboot version as a development version. + +The version number used should be specified for the next expected release. +It should be larger than the last release version by incrementing the MAJOR or +the MINOR number. It is not necessary to define the next version precisely as +the next release version might still be different as it might be needed to do: + +- a patch release +- a MINOR release while a MAJOR release was expected +- a MAJOR release while a MINOR release was expected + +[semver]: http://semver.org/ diff --git a/bootloader/mcuboot/docs/serial_recovery.md b/bootloader/mcuboot/docs/serial_recovery.md new file mode 100644 index 0000000..ade2419 --- /dev/null +++ b/bootloader/mcuboot/docs/serial_recovery.md @@ -0,0 +1,128 @@ + + +# Serial recovery + +MCUboot implements a Simple Management Protocol (SMP) server. +SMP is a basic transfer encoding for use with the MCUmgr management protocol. +See the Zephyr [Device Management](https://docs.zephyrproject.org/latest/services/device_mgmt/index.html#device-mgmt) documentation for more information about MCUmgr and SMP. + +MCUboot supports the following subset of the MCUmgr commands: +* echo (OS group) +* reset (OS group) +* image list (IMG group) +* image upload (IMG group) + +It can also support system-specific MCUmgr commands depending on the given mcuboot-port +if the ``MCUBOOT_PERUSER_MGMT_GROUP_ENABLED`` option is enabled. + +The serial recovery feature can use any serial interface provided by the platform. + +## Image uploading + +Uploading an image is targeted to the primary slot by default. + +An image can be loaded to other slots only when the ``MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD`` option is enabled for the platform. + +MCUboot supports progressive erasing of a slot to which an image is uploaded to if the ``MCUBOOT_ERASE_PROGRESSIVELY`` option is enabled. +As a result, a device can receive images smoothly, and can erase required part of a flash automatically. + +## Configuration of serial recovery + +How to enable and configure the serial recovery feature depends on the given mcuboot-port implementation. +Refer to the respective documentation and source code for more details. + +## Entering serial recovery mode + +Entering the serial recovery mode is usually possible right after a device reset, for instance as a reaction on a GPIO pin state. +Refer to the given mcuboot-port details to get information on how to enter the serial recovery mode. + +## Serial recovery mode usage + +### MCU Manager CLI installation + +The MCUmgr command line tool can be used as an SMP client for evaluation purposes. +The tool is available under the [MCU Manager](https://github.com/apache/mynewt-mcumgr-cli) +Github page and is described in the Zephyr +[MCU Manager CLI](https://docs.zephyrproject.org/latest/services/device_mgmt/mcumgr.html#mcumgr-cli) documentation. + +Use the following command to install the MCU Manager CLI tool: +``` console +go install github.com/apache/mynewt-mcumgr-cli/mcumgr@latest +``` +Enter serial recovery mode in the device and use an SMP client application for communication with the MCUboot's SMP server. + +### Connection configuration + +Use the following command to set the connection configuration, +for linux: +``` console +mcumgr conn add serial_1 type="serial" connstring="dev=/dev/ttyACM0,baud=115200" +``` +for windows: +``` console +mcumgr conn add serial_1 type="serial" connstring="COM1,baud=115200" +``` + +### Image management + +The connection configuration must be established to perform the following image-related commands: + +* Upload the image: + ``` console + mcumgr image upload -c serial_1 + ``` + + Once done successfully, the following notification will be displayed: + ``` console + 20.25 KiB / 20.25 KiB [=================================] 100.00% 3.08 KiB/s 6s + Done + ``` + +* List all images: + ``` console + mcumgr image list -c serial_1 + ``` + The terminal will show the response from the module: + ``` console + Images: + image=0 slot=0 + version: 0.0.0.0 + bootable: false + flags: + hash: Unavailable + Split status: N/A (0) + ``` + +### Device reset + +Reset your device with the following command: +``` console +mcumgr reset -c serial_1 +``` +The terminal should respond: +``` console +Done +``` diff --git a/bootloader/mcuboot/docs/signed_images.md b/bootloader/mcuboot/docs/signed_images.md new file mode 100644 index 0000000..bcc201b --- /dev/null +++ b/bootloader/mcuboot/docs/signed_images.md @@ -0,0 +1,100 @@ + + +## Image signing + +This signs the image by computing hash over the image, and then +signing that hash. Signature is computed by newt tool when it's +creating the image. This signature is placed in the image trailer. + +The public key of this keypair must be included in the bootloader, +as it verifies it before allowing the image to run. + +This facility allows you to use multiple signing keys. This would +be useful when you want to prevent production units from booting +development images, but want development units to be able to boot +both production images and development images. + +For an alternative solution when the public key(s) doesn't need to be +included in the bootloader, see the [design](design.md) document. + +## Creating signing keys +First you need a keypair to use for signing. You can create +one with openssl command line tool. + +openssl genrsa -out image_sign.pem 2048 + +This created a file which contains both the private and public key, +and will be used when signing images. + +Then you need to extract the public key from this to include it +in the bootloader. Bootloader need to keep key parsing minimal, +so it expects simple key format. + +openssl rsa -in image_sign.pem -pubout -out image_sign_pub.der -outform DER -RSAPublicKey_out + +Now the public key is in file called image_sign_pub.der. + +For ECDSA256 these commands are similar. +openssl ecparam -name prime256v1 -genkey -noout -out image_sign.pem +openssl ec -in image_sign.pem -pubout -outform DER -out image_sign_pub.der + +## Creating a key package + +xxd -i image_sign_pub.der image_sign_pub.c.import + +Then you need to create a package containing this key, or keys. + +## Sample pkg.yml +This gets bootutil to turn on image signature validation. + + pkg.name: libs/mykeys + pkg.deps: + - "@apache-mynewt-core/boot/bootutil" + +## Sample source file +This exports the keys. + + #include + + #include "image_sign_pub.c.import" + + const struct bootutil_key bootutil_keys[] = { + [0] = { + .key = image_sign_pub_der, + .len = &image_sign_pub_der_len, + } + }; + + const int bootutil_key_cnt = sizeof(bootutil_keys) / sizeof(bootutil_keys[0]); + +## Building the bootloader + +Enable the BOOTUTIL_SIGN_RSA syscfg setting in your app or target syscfg.yml +file + + syscfg.vals: + BOOTUTIL_SIGN_RSA: 1 + +After you've created the key package, you must include it in the build +for bootloader. So modify the pkg.yml for apps/boot to include it. + +The syscfg variable to enable ECDSA256 is BOOTUTIL_SIGN_EC256. diff --git a/bootloader/mcuboot/docs/testplan-mynewt.md b/bootloader/mcuboot/docs/testplan-mynewt.md new file mode 100644 index 0000000..402a493 --- /dev/null +++ b/bootloader/mcuboot/docs/testplan-mynewt.md @@ -0,0 +1,162 @@ +## MCUboot test plan + +The current target for running the tests is the Freedom K64F board. + +### Basic sign support (RSA/EC/EC256) + +For each supported signing algorithm, check that non-signed, and signed +with wrong key images are not swapped to, and image signed with correct key +is swapped to. + +For the 3 algorithms supported, rsa, ec and ec256, two files are provided: +key_.pem, key__2.pem. And a keys file with the C public +key data for key_.pem. + +Build and load MCUboot: + +* `newt build k64f_boot_` +* `newt load k64f_boot_` + +Build and load good image in slot 0: + +* `newt create-image k64f_blinky 1.0.1 key_.pem` +* `newt load k64f_blinky` + +--- +***Note*** + +*If testing RSA/PSS `newt create-image` needs to be passed in the extra* +*flag `--rsa-pss` eg:* + +`newt create-image k64f_blinky 1.0.1 key_rsa.pem --rsa-pss` + +--- + +Build and load image in slot 1 with no signing, signed with +key__2.pem and signed with key_.pem. Mark each one as +test image and check that swap only happens for image signed with +key_.pem. Both others should be erased. + +* `newt create-image k64f_blinky2 1.0.2 ` +* `newtmgr image upload k64f_blinky2` +* `newtmgr image list` +* `newtmgr image test ` + +### Image signed with more than one key + +FIXME: this is currently not functional, skip this section! + +Build and load MCUboot: + +* `newt build k64f_boot_rsa_ec` +* `newt load k64f_boot_rsa_ec` + +Build and load good image in slot 0: + +* `newt create-image k64f_blinky 1.0.1 key_rsa.pem` +* `newt load k64f_blinky` + +Build and load image in slot 1 with no signing, signed with +key__2.pem and signed with key_.pem. Mark each one as +test image and check that swap only happens for image signed with +key_.pem. Both others should be erased. + +Use all of this options: + +* `newt create-image k64f_blinky2 1.0.2` + +And load + +* `newtmgr image upload k64f_blinky2` +* `newtmgr image list` +* `newtmgr image test ` + +### Overwrite only functionality + +Build/load MCUboot: + +* `newt build k64f_boot_rsa_noswap` +* `newt load k64f_boot_rsa_noswap` + +Build/load blinky to slot 0: + +* `newt create-image k64f_blinky 1.0.1 key_rsa.pem` +* `newt load k64f_blinky` + +Build/load blinky2 both with bad and good key, followed by a permanent swap +request: + +* `newt create-image k64f_blinky2 1.0.2 .pem` +* `newtmgr image upload k64f_blinky2` +* `newtmgr image list` +* `newtmgr image confirm ` + +This should not swap and delete the image in slot 1 when signed with the wrong +key, otherwise the image in slot 1 should be *moved* to slot 0 and slot 1 should +be empty. + +### Validate slot 0 option + +Build/load MCUboot: + +* `newt build k64f_boot_rsa_validate0` +* `newt load k64f_boot_rsa_validate0` + +Build non-signed image: + +* `newt create-image k64f_blinky 1.0.1` +* `newt load k64f_blinky` +* Reset and no image should be run + +Build signed image with invalid key: + +* `newt create-image k64f_blinky 1.0.1 key_rsa_2.pem` +* `newt load k64f_blinky` +* Reset and no image should be run + +Build signed image with *valid* key: + +* `newt create-image k64f_blinky 1.0.1 key_rsa.pem` +* `newt load k64f_blinky` +* Reset and image *should* run + +### Swap with random failures + +DISCLAIMER: be careful with copy/paste of commands, this test uses another +target/app! + +Build/load MCUboot: + +* `newt build k64f_boot_rsa` +* `newt load k64f_boot_rsa` + +Build/load slinky to slot 0: + +* `newt create-image k64f_slinky 1.0.1 key_rsa.pem` +* `newt load k64f_slinky` + +Build/load slinky2 to slot 1: + +* `newt create-image k64f_slinky2 1.0.2 key_rsa.pem` +* `newtmgr image upload k64f_slinky2` + +Confirm that both images are installed, request a permanent request to the +image in slot 1 and check that it works. + +* `newtmgr image list` +* `newtmgr image confirm ` + +If everything works, now proceed with requests for permanent swap to the image +in slot 1 and do random swaps (as much as you like!). When the swap finishes +confirm that the swap was finished with the previous slot 1 image now in +slot 0 and vice-versa. + +### Help + +* Mass erase MCU + + $ pyocd erase --chip + +* Flashing image in slot 1: + + $ pyocd flash -e sector -a 0x80000 ${IMG_FILE} bin diff --git a/bootloader/mcuboot/docs/testplan-zephyr.md b/bootloader/mcuboot/docs/testplan-zephyr.md new file mode 100644 index 0000000..9b70151 --- /dev/null +++ b/bootloader/mcuboot/docs/testplan-zephyr.md @@ -0,0 +1,71 @@ +# Zephyr test plan + +The following roughly describes how MCUboot is tested on Zephyr. The +testing is done with the code in `samples/zephyr`. These examples +were written using the FRDM-K64F, but other boards should be similar. +At this time, however, the partitions are hardcoded in the Makefile +targets to flash. + +Note that the script "run-tests.sh" in that directory is helpful for +automating the process, and provides simple "y or n" prompts for each +test case and expected result. + +## Building and running. + +The tests are build using the various `test-*` targets in +`samples/zephyr/Makefile`. For each test, invoke `make` with that +target: + + $ make test-good-rsa + +Begin by doing a full erase, and programming the bootloader itself: + + $ pyocd erase --chip + $ make flash_boot + +After it resets, look for "main: Starting bootloader", a few debug +messages, and lastly: "main: Unable to find bootable image". + +Then, load hello1: + + $ make flash_hello1 + +This should print "main: Jumping to the first image slot", and you +should get an image "hello1". + +Note that there are comments with each test target describing the +intended behavior for each of these steps. Sometimes an upgrade will +happen and sometimes it will not. + + $ make flash_hello2 + +This should print a message: `boot_swap_type: Swap type: test`, and +you should see "hello2". + +Now reset the target:: + + $ pyocd commander -c reset + +And you should see a revert and "hello1" running. + +## Testing that mark ok works + +Repeat this, to make sure we can mark the image as OK, and that a +revert doesn't happen: + + $ make flash_hello1 + $ make flash_hello2 + +We should have just booted the hello2. Mark this as OK: + + $ pyocd flash -a 0x7ffe8 image_ok.bin + $ pyocd commander -c reset + +And make sure this stays in the "hello2" image. + +This step doesn't make sense on the tests where the upgrade doesn't +happen. + +## Testing all configurations + +Repeat these steps for each of the `test-*` targest in the Makefile. diff --git a/bootloader/mcuboot/enc-aes128kw.b64 b/bootloader/mcuboot/enc-aes128kw.b64 new file mode 100644 index 0000000..ed2c9fb --- /dev/null +++ b/bootloader/mcuboot/enc-aes128kw.b64 @@ -0,0 +1 @@ +0VoElcTCqP8weM5Jtfyy3Q== diff --git a/bootloader/mcuboot/enc-aes256kw.b64 b/bootloader/mcuboot/enc-aes256kw.b64 new file mode 100644 index 0000000..866aa6d --- /dev/null +++ b/bootloader/mcuboot/enc-aes256kw.b64 @@ -0,0 +1 @@ +5FxRRtIcgjXMGhmvofKqIMiMf0Bs2yKqarXLqvixW7Q= diff --git a/bootloader/mcuboot/enc-ec256-priv.pem b/bootloader/mcuboot/enc-ec256-priv.pem new file mode 100644 index 0000000..352615a --- /dev/null +++ b/bootloader/mcuboot/enc-ec256-priv.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg9h5Rnfj63aG32alk +ZDtU0D3QH+V42ReYpSjKzGtnngahRANCAASKRHMAlMmAJzENIzZr6Wmfy8V8yEQa +k+bufYamrl6TcnTZ4Vocm2UaK2FBKAJzhBKXOi2ioGd3AtpnGkvd13HM +-----END PRIVATE KEY----- diff --git a/bootloader/mcuboot/enc-ec256-pub.pem b/bootloader/mcuboot/enc-ec256-pub.pem new file mode 100644 index 0000000..dfd21e1 --- /dev/null +++ b/bootloader/mcuboot/enc-ec256-pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEikRzAJTJgCcxDSM2a+lpn8vFfMhE +GpPm7n2Gpq5ek3J02eFaHJtlGithQSgCc4QSlzotoqBndwLaZxpL3ddxzA== +-----END PUBLIC KEY----- diff --git a/bootloader/mcuboot/enc-rsa2048-priv.pem b/bootloader/mcuboot/enc-rsa2048-priv.pem new file mode 100644 index 0000000..a2bf0cb --- /dev/null +++ b/bootloader/mcuboot/enc-rsa2048-priv.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAtCYUST0WEzptnISpi2oQIGHvSASkSyTzADKsIuAwJ3AY5VXI +uAU0A7D4pZbSSFjvcLAJ2+NYYu+ZYwGyicSz9p5iv03CitDJTUOj2OUd7GJjCOIg +pfx40D50yKQbNq179QauTVGbQM4wT2zq+el06gbunOQUaCC5PecRFIslo/9MivNT +7ms+7zTNaj9iaMD/eEyww+aWYfwfGPF6guKPNagrhhakRvusfkHbAgWRbd/B3hOV +nPmeXnK6pyWT+9zoq4ZFiEct7e7ul57OXZsEBEB8y3w9LHSrpMxko1yVPdSi3JKy +yBjL+QA5gY+PQMLfmSmsisI72KTyra90wBHHmQIDAQABAoIBAEJHgE8x2l1YsdtU +M8zHSQehAJhOnOPIxF7eRdbPBOh9pas61I5f27M/+TtzMgrMLcwX+IieLHa6EIUM +qtNlO5EQ1OPtiBXqmyWCLVYvdcLyr90k1T48lXaIhA8N0bVcPq73tklcLPK66atP +N2SbMBiqVEAE6j0lTQIpcW9NgpvDRCqdDJjTyBUNBJNgMMdeeepTncAOgayQvJ4e +0igPEPUf3zh/ipCNSQd9eMun75JtOxOVm7qDxrNxJScHmVSCPezF+LSgOHpZagvK +aWwXpBjgtKqJmY/LcTQJG27mhwC1unCKKT2aBhgtZl5hN+vdXsgokgUw/bhlsX+/ +LVUSkcECgYEA2mXaOHwY+wARYOs3ZbiDYojEOk5kavM+TsA0GYrLSsovXVB6rPee +h1r8TUnX+SH1C29XQT2PuOx/zJIJvtOkwxSFIV0Fo6og9mJEUANeU0rNarZljk5L +PyXGFjH1mRN3QtrccE1lsJkP31qxRfC5jqCuT01lCYS1OCm/aeCIHycCgYEA0ypZ +7CjDDU+SlspnlPwupoZoRVOSzIZ/iuFd6B2eux4AJh2AEv+cEQq9psONSNr8EPd6 +FgcVoDrTlPtShznu58QmSRbGwIMlv2pOjAsQhWarfq6sTGk8ROvN6fZki0rYak1t +R6m4VXLB/fSBTGa+SfJ1T4DxIDi4aht1QTAPGz8CgYAJNfp6H2G+VEZnXAQ+GgYQ +hcwg2WWKzS93isunuB7SzKwqt1Y1LUxWURQK/m5JZ5E6Jjv72GjTV8YcDpyym6J7 +R8ZFnfK68FXrjkFrTnkP8juvoHmwAsVRqHouPXUqO5PwEeLyKZF8XTg6J00Kshhh +V42CcrUsLZinAbu872dOSQKBgQCycFNUcI2Crf8dVSR6jS+OoH10N88Q7YbRgOet +wXnkfNF7Y+paI41qCT2BsjWtnv7qB3YvLwVjRNKOTmHKy3XKe8IueQSyoSBAxEBj +ruXjFINOpaQLXdIEG48BaahE3JZMHel+aTjPXA3536dzPE8Ihc4DxN39cHDFmTZY +Q5hAWQKBgQDVqvvsjcbd+itaJNDaWL2HkhopYhMdS3kbvnl9rXnKF3Xa6DLooJ6o +d1OsONbr5iJlxKpMyNAzGh6+vXMJSvqFXPMMnIFWMKf3m/SSnGuTagAz3C9UHnjU +l+wkots9AzMJsiwDBUDeUvKb+gCNS/5bm5xzrft6AEJinqCVVVAyhw== +-----END RSA PRIVATE KEY----- diff --git a/bootloader/mcuboot/enc-rsa2048-pub.pem b/bootloader/mcuboot/enc-rsa2048-pub.pem new file mode 100644 index 0000000..adc2adb --- /dev/null +++ b/bootloader/mcuboot/enc-rsa2048-pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtCYUST0WEzptnISpi2oQ +IGHvSASkSyTzADKsIuAwJ3AY5VXIuAU0A7D4pZbSSFjvcLAJ2+NYYu+ZYwGyicSz +9p5iv03CitDJTUOj2OUd7GJjCOIgpfx40D50yKQbNq179QauTVGbQM4wT2zq+el0 +6gbunOQUaCC5PecRFIslo/9MivNT7ms+7zTNaj9iaMD/eEyww+aWYfwfGPF6guKP +NagrhhakRvusfkHbAgWRbd/B3hOVnPmeXnK6pyWT+9zoq4ZFiEct7e7ul57OXZsE +BEB8y3w9LHSrpMxko1yVPdSi3JKyyBjL+QA5gY+PQMLfmSmsisI72KTyra90wBHH +mQIDAQAB +-----END PUBLIC KEY----- diff --git a/bootloader/mcuboot/enc-x25519-priv.pem b/bootloader/mcuboot/enc-x25519-priv.pem new file mode 100644 index 0000000..cb92491 --- /dev/null +++ b/bootloader/mcuboot/enc-x25519-priv.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VuBCIEICiAL+/vgpVQ8UGTA2wbuUlsUeUmh493B/i0HwRFbYRP +-----END PRIVATE KEY----- diff --git a/bootloader/mcuboot/enc-x25519-pub.pem b/bootloader/mcuboot/enc-x25519-pub.pem new file mode 100644 index 0000000..80d89a6 --- /dev/null +++ b/bootloader/mcuboot/enc-x25519-pub.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VuAyEApnrfU1lvc6n45HmsC6ogwvmJkv+Wltehx3CxYYsqD3A= +-----END PUBLIC KEY----- diff --git a/bootloader/mcuboot/ext/fiat/LICENSE b/bootloader/mcuboot/ext/fiat/LICENSE new file mode 100644 index 0000000..bd46c61 --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015-2016 the fiat-crypto authors (see +https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bootloader/mcuboot/ext/fiat/METADATA b/bootloader/mcuboot/ext/fiat/METADATA new file mode 100644 index 0000000..0e4012f --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/METADATA @@ -0,0 +1,13 @@ +name: "fiat" +description: "Fiat-Crypto: Synthesizing Correct-by-Construction Code for Cryptographic Primitives." + +third_party { + url { + type: GIT + value: "https://github.com/mit-plv/fiat-crypto" + } + version: "4441785fb44b88bb6943ddbf639d872c8c903281" + last_upgrade_date { year: 2019 month: 1 day: 16 } + + local_modifications: "Fiat-generated code has been integrated into existing BoringSSL code" +} diff --git a/bootloader/mcuboot/ext/fiat/README.chromium b/bootloader/mcuboot/ext/fiat/README.chromium new file mode 100644 index 0000000..73c5ba2 --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/README.chromium @@ -0,0 +1,10 @@ +Name: Fiat-Crypto: Synthesizing Correct-by-Construction Code for Cryptographic Primitives +Short Name: fiat-crypto +URL: https://github.com/mit-plv/fiat-crypto +Version: git (see METADATA) +License: MIT +License File: LICENSE +Security Critical: yes + +Description: +See README.md and METADATA. diff --git a/bootloader/mcuboot/ext/fiat/README.md b/bootloader/mcuboot/ext/fiat/README.md new file mode 100644 index 0000000..cf66900 --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/README.md @@ -0,0 +1,47 @@ +# Fiat + +Some of the code in this directory is generated by +[Fiat](https://github.com/mit-plv/fiat-crypto) and thus these files are +licensed under the MIT license. (See LICENSE file.) + +## Curve25519 + +To generate the field arithmetic procedures in `curve25519.c` from a fiat-crypto +checkout (as of `7892c66d5e0e5770c79463ce551193ceef870641`), run +`make src/Specific/solinas32_2e255m19_10limbs/femul.c` (replacing `femul` with +the desired field operation). The "source" file specifying the finite field and +referencing the desired implementation strategy is +`src/Specific/solinas32_2e255m19_10limbs/CurveParameters.v`, specifying roughly +"unsaturated arithmetic modulo 2^255-19 using 10 limbs of radix 2^25.5 in 32-bit +unsigned integers with a single carry chain and two wraparound carries" where +only the prime is considered normative and everything else is treated as +"compiler hints". + +The 64-bit implementation uses 5 limbs of radix 2^51 with instruction scheduling +taken from curve25519-donna-c64. It is found in +`src/Specific/solinas64_2e255m19_5limbs_donna`. + +## P256 + +To generate the field arithmetic procedures in `p256.c` from a fiat-crypto +checkout, run +`make src/Specific/montgomery64_2e256m2e224p2e192p2e96m1_4limbs/femul.c`. +The corresponding "source" file is +`src/Specific/montgomery64_2e256m2e224p2e192p2e96m1_4limbs/CurveParameters.v`, +specifying roughly "64-bit saturated word-by-word Montgomery reduction modulo +2^256 - 2^224 + 2^192 + 2^96 - 1". Again, everything except for the prime is +untrusted. There is currently a known issue where `fesub.c` for p256 does not +manage to complete the build (specialization) within a week on Coq 8.7.0. + +does manage to build that file, but the work on that branch was never finished +(the correctness proofs of implementation templates still apply, but the +now abandoned prototype specialization facilities there are unverified). + +## Working With Fiat Crypto Field Arithmetic + +The fiat-crypto readme +contains an overview of the implementation templates followed by a tour of the +specialization machinery. It may be helpful to first read about the less messy +parts of the system from chapter 3 of . +There is work ongoing to replace the entire specialization mechanism with +something much more principled . diff --git a/bootloader/mcuboot/ext/fiat/pkg.yml b/bootloader/mcuboot/ext/fiat/pkg.yml new file mode 100644 index 0000000..73a6559 --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/pkg.yml @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: ext/fiat +pkg.description: "MCUboot's bundled fiat-crypto" +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: diff --git a/bootloader/mcuboot/ext/fiat/src/curve25519.c b/bootloader/mcuboot/ext/fiat/src/curve25519.c new file mode 100644 index 0000000..765e0ca --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/src/curve25519.c @@ -0,0 +1,1314 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2016 the fiat-crypto authors (see the AUTHORS file). +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Some of this code is taken from the ref10 version of Ed25519 in SUPERCOP +// 20141124 (http://bench.cr.yp.to/supercop.html). That code is released as +// public domain but parts have been replaced with code generated by Fiat +// (https://github.com/mit-plv/fiat-crypto), which is MIT licensed. +// +// The field functions are shared by Ed25519 and X25519 where possible. + +#include +#include + +#include + +#if defined(MCUBOOT_USE_MBED_TLS) +#include +#include +#include + #if MBEDTLS_VERSION_NUMBER >= 0x03000000 + #include + #endif +#else +#include +#include +#include +#endif + +#include "curve25519.h" +// Various pre-computed constants. +#include "curve25519_tables.h" + +#define SHA512_DIGEST_LENGTH 64 + +// Low-level intrinsic operations + +static uint64_t load_3(const uint8_t *in) { + uint64_t result; + result = (uint64_t)in[0]; + result |= ((uint64_t)in[1]) << 8; + result |= ((uint64_t)in[2]) << 16; + return result; +} + +static uint64_t load_4(const uint8_t *in) { + uint64_t result; + result = (uint64_t)in[0]; + result |= ((uint64_t)in[1]) << 8; + result |= ((uint64_t)in[2]) << 16; + result |= ((uint64_t)in[3]) << 24; + return result; +} + + +// Field operations. + +typedef uint32_t fe_limb_t; +#define FE_NUM_LIMBS 10 + +// assert_fe asserts that |f| satisfies bounds: +// +// [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], +// [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], +// [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], +// [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], +// [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] +// +// See comments in curve25519_32.h for which functions use these bounds for +// inputs or outputs. +#define assert_fe(f) \ + do { \ + for (unsigned _assert_fe_i = 0; _assert_fe_i < 10; _assert_fe_i++) { \ + assert(f[_assert_fe_i] <= \ + ((_assert_fe_i & 1) ? 0x2333333u : 0x4666666u)); \ + } \ + } while (0) + +// assert_fe_loose asserts that |f| satisfies bounds: +// +// [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], +// [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], +// [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], +// [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], +// [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] +// +// See comments in curve25519_32.h for which functions use these bounds for +// inputs or outputs. +#define assert_fe_loose(f) \ + do { \ + for (unsigned _assert_fe_i = 0; _assert_fe_i < 10; _assert_fe_i++) { \ + assert(f[_assert_fe_i] <= \ + ((_assert_fe_i & 1) ? 0x6999999u : 0xd333332u)); \ + } \ + } while (0) + +//FIXME: use Zephyr macro +_Static_assert(sizeof(fe) == sizeof(fe_limb_t) * FE_NUM_LIMBS, + "fe_limb_t[FE_NUM_LIMBS] is inconsistent with fe"); + +static void fe_frombytes_strict(fe *h, const uint8_t s[32]) { + // |fiat_25519_from_bytes| requires the top-most bit be clear. + assert((s[31] & 0x80) == 0); + fiat_25519_from_bytes(h->v, s); + assert_fe(h->v); +} + +static void fe_frombytes(fe *h, const uint8_t s[32]) { + uint8_t s_copy[32]; + memcpy(s_copy, s, 32); + s_copy[31] &= 0x7f; + fe_frombytes_strict(h, s_copy); +} + +static void fe_tobytes(uint8_t s[32], const fe *f) { + assert_fe(f->v); + fiat_25519_to_bytes(s, f->v); +} + +// h = 0 +static void fe_0(fe *h) { +#if defined(MCUBOOT_USE_MBED_TLS) + mbedtls_platform_zeroize(h, sizeof(fe)); +#else + _set(h, 0, sizeof(fe)); +#endif +} + +// h = 1 +static void fe_1(fe *h) { +#if defined(MCUBOOT_USE_MBED_TLS) + mbedtls_platform_zeroize(h, sizeof(fe)); +#else + _set(h, 0, sizeof(fe)); +#endif + h->v[0] = 1; +} + +// h = f + g +// Can overlap h with f or g. +static void fe_add(fe_loose *h, const fe *f, const fe *g) { + assert_fe(f->v); + assert_fe(g->v); + fiat_25519_add(h->v, f->v, g->v); + assert_fe_loose(h->v); +} + +// h = f - g +// Can overlap h with f or g. +static void fe_sub(fe_loose *h, const fe *f, const fe *g) { + assert_fe(f->v); + assert_fe(g->v); + fiat_25519_sub(h->v, f->v, g->v); + assert_fe_loose(h->v); +} + +static void fe_carry(fe *h, const fe_loose* f) { + assert_fe_loose(f->v); + fiat_25519_carry(h->v, f->v); + assert_fe(h->v); +} + +static void fe_mul_impl(fe_limb_t out[FE_NUM_LIMBS], + const fe_limb_t in1[FE_NUM_LIMBS], + const fe_limb_t in2[FE_NUM_LIMBS]) { + assert_fe_loose(in1); + assert_fe_loose(in2); + fiat_25519_carry_mul(out, in1, in2); + assert_fe(out); +} + +static void fe_mul_ltt(fe_loose *h, const fe *f, const fe *g) { + fe_mul_impl(h->v, f->v, g->v); +} + +static void fe_mul_ttt(fe *h, const fe *f, const fe *g) { + fe_mul_impl(h->v, f->v, g->v); +} + +static void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g) { + fe_mul_impl(h->v, f->v, g->v); +} + +static void fe_mul_ttl(fe *h, const fe *f, const fe_loose *g) { + fe_mul_impl(h->v, f->v, g->v); +} + +static void fe_mul_tll(fe *h, const fe_loose *f, const fe_loose *g) { + fe_mul_impl(h->v, f->v, g->v); +} + +static void fe_sq_tl(fe *h, const fe_loose *f) { + assert_fe_loose(f->v); + fiat_25519_carry_square(h->v, f->v); + assert_fe(h->v); +} + +static void fe_sq_tt(fe *h, const fe *f) { + assert_fe_loose(f->v); + fiat_25519_carry_square(h->v, f->v); + assert_fe(h->v); +} + +// h = -f +static void fe_neg(fe_loose *h, const fe *f) { + assert_fe(f->v); + fiat_25519_opp(h->v, f->v); + assert_fe_loose(h->v); +} + +// h = f +static void fe_copy(fe *h, const fe *f) { + memmove(h, f, sizeof(fe)); +} + +static void fe_copy_lt(fe_loose *h, const fe *f) { + //FIXME: use Zephyr macro + _Static_assert(sizeof(fe_loose) == sizeof(fe), "fe and fe_loose mismatch"); + memmove(h, f, sizeof(fe)); +} + +static void fe_loose_invert(fe *out, const fe_loose *z) { + fe t0; + fe t1; + fe t2; + fe t3; + int i; + + fe_sq_tl(&t0, z); + fe_sq_tt(&t1, &t0); + for (i = 1; i < 2; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_tlt(&t1, z, &t1); + fe_mul_ttt(&t0, &t0, &t1); + fe_sq_tt(&t2, &t0); + fe_mul_ttt(&t1, &t1, &t2); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 5; ++i) { + fe_sq_tt(&t2, &t2); + } + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 10; ++i) { + fe_sq_tt(&t2, &t2); + } + fe_mul_ttt(&t2, &t2, &t1); + fe_sq_tt(&t3, &t2); + for (i = 1; i < 20; ++i) { + fe_sq_tt(&t3, &t3); + } + fe_mul_ttt(&t2, &t3, &t2); + fe_sq_tt(&t2, &t2); + for (i = 1; i < 10; ++i) { + fe_sq_tt(&t2, &t2); + } + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 50; ++i) { + fe_sq_tt(&t2, &t2); + } + fe_mul_ttt(&t2, &t2, &t1); + fe_sq_tt(&t3, &t2); + for (i = 1; i < 100; ++i) { + fe_sq_tt(&t3, &t3); + } + fe_mul_ttt(&t2, &t3, &t2); + fe_sq_tt(&t2, &t2); + for (i = 1; i < 50; ++i) { + fe_sq_tt(&t2, &t2); + } + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t1, &t1); + for (i = 1; i < 5; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_ttt(out, &t1, &t0); +} + +static void fe_invert(fe *out, const fe *z) { + fe_loose l; + fe_copy_lt(&l, z); + fe_loose_invert(out, &l); +} + +static int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len) { + const uint8_t *a = in_a; + const uint8_t *b = in_b; + uint8_t x = 0; + + for (size_t i = 0; i < len; i++) { + x |= a[i] ^ b[i]; + } + + return x; +} + +// return 0 if f == 0 +// return 1 if f != 0 +static int fe_isnonzero(const fe_loose *f) { + fe tight; + fe_carry(&tight, f); + uint8_t s[32]; + fe_tobytes(s, &tight); + + static const uint8_t zero[32] = {0}; + return CRYPTO_memcmp(s, zero, sizeof(zero)) != 0; +} + +// return 1 if f is in {1,3,5,...,q-2} +// return 0 if f is in {0,2,4,...,q-1} +static int fe_isnegative(const fe *f) { + uint8_t s[32]; + fe_tobytes(s, f); + return s[0] & 1; +} + +static void fe_sq2_tt(fe *h, const fe *f) { + // h = f^2 + fe_sq_tt(h, f); + + // h = h + h + fe_loose tmp; + fe_add(&tmp, h, h); + fe_carry(h, &tmp); +} + +static void fe_pow22523(fe *out, const fe *z) { + fe t0; + fe t1; + fe t2; + int i; + + fe_sq_tt(&t0, z); + fe_sq_tt(&t1, &t0); + for (i = 1; i < 2; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_ttt(&t1, z, &t1); + fe_mul_ttt(&t0, &t0, &t1); + fe_sq_tt(&t0, &t0); + fe_mul_ttt(&t0, &t1, &t0); + fe_sq_tt(&t1, &t0); + for (i = 1; i < 5; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_ttt(&t0, &t1, &t0); + fe_sq_tt(&t1, &t0); + for (i = 1; i < 10; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_ttt(&t1, &t1, &t0); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 20; ++i) { + fe_sq_tt(&t2, &t2); + } + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t1, &t1); + for (i = 1; i < 10; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_ttt(&t0, &t1, &t0); + fe_sq_tt(&t1, &t0); + for (i = 1; i < 50; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_ttt(&t1, &t1, &t0); + fe_sq_tt(&t2, &t1); + for (i = 1; i < 100; ++i) { + fe_sq_tt(&t2, &t2); + } + fe_mul_ttt(&t1, &t2, &t1); + fe_sq_tt(&t1, &t1); + for (i = 1; i < 50; ++i) { + fe_sq_tt(&t1, &t1); + } + fe_mul_ttt(&t0, &t1, &t0); + fe_sq_tt(&t0, &t0); + for (i = 1; i < 2; ++i) { + fe_sq_tt(&t0, &t0); + } + fe_mul_ttt(out, &t0, z); +} + + +// Group operations. + +void x25519_ge_tobytes(uint8_t s[32], const ge_p2 *h) { + fe recip; + fe x; + fe y; + + fe_invert(&recip, &h->Z); + fe_mul_ttt(&x, &h->X, &recip); + fe_mul_ttt(&y, &h->Y, &recip); + fe_tobytes(s, &y); + s[31] ^= fe_isnegative(&x) << 7; +} + +int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t s[32]) { + fe u; + fe_loose v; + fe v3; + fe vxx; + fe_loose check; + + fe_frombytes(&h->Y, s); + fe_1(&h->Z); + fe_sq_tt(&v3, &h->Y); + fe_mul_ttt(&vxx, &v3, &d); + fe_sub(&v, &v3, &h->Z); // u = y^2-1 + fe_carry(&u, &v); + fe_add(&v, &vxx, &h->Z); // v = dy^2+1 + + fe_sq_tl(&v3, &v); + fe_mul_ttl(&v3, &v3, &v); // v3 = v^3 + fe_sq_tt(&h->X, &v3); + fe_mul_ttl(&h->X, &h->X, &v); + fe_mul_ttt(&h->X, &h->X, &u); // x = uv^7 + + fe_pow22523(&h->X, &h->X); // x = (uv^7)^((q-5)/8) + fe_mul_ttt(&h->X, &h->X, &v3); + fe_mul_ttt(&h->X, &h->X, &u); // x = uv^3(uv^7)^((q-5)/8) + + fe_sq_tt(&vxx, &h->X); + fe_mul_ttl(&vxx, &vxx, &v); + fe_sub(&check, &vxx, &u); + if (fe_isnonzero(&check)) { + fe_add(&check, &vxx, &u); + if (fe_isnonzero(&check)) { + return 0; + } + fe_mul_ttt(&h->X, &h->X, &sqrtm1); + } + + if (fe_isnegative(&h->X) != (s[31] >> 7)) { + fe_loose t; + fe_neg(&t, &h->X); + fe_carry(&h->X, &t); + } + + fe_mul_ttt(&h->T, &h->X, &h->Y); + return 1; +} + +static void ge_p2_0(ge_p2 *h) { + fe_0(&h->X); + fe_1(&h->Y); + fe_1(&h->Z); +} + +// r = p +static void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { + fe_copy(&r->X, &p->X); + fe_copy(&r->Y, &p->Y); + fe_copy(&r->Z, &p->Z); +} + +// r = p +void x25519_ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { + fe_add(&r->YplusX, &p->Y, &p->X); + fe_sub(&r->YminusX, &p->Y, &p->X); + fe_copy_lt(&r->Z, &p->Z); + fe_mul_ltt(&r->T2d, &p->T, &d2); +} + +// r = p +void x25519_ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { + fe_mul_tll(&r->X, &p->X, &p->T); + fe_mul_tll(&r->Y, &p->Y, &p->Z); + fe_mul_tll(&r->Z, &p->Z, &p->T); +} + +// r = p +void x25519_ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { + fe_mul_tll(&r->X, &p->X, &p->T); + fe_mul_tll(&r->Y, &p->Y, &p->Z); + fe_mul_tll(&r->Z, &p->Z, &p->T); + fe_mul_tll(&r->T, &p->X, &p->Y); +} + +// r = 2 * p +static void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { + fe trX, trZ, trT; + fe t0; + + fe_sq_tt(&trX, &p->X); + fe_sq_tt(&trZ, &p->Y); + fe_sq2_tt(&trT, &p->Z); + fe_add(&r->Y, &p->X, &p->Y); + fe_sq_tl(&t0, &r->Y); + + fe_add(&r->Y, &trZ, &trX); + fe_sub(&r->Z, &trZ, &trX); + fe_carry(&trZ, &r->Y); + fe_sub(&r->X, &t0, &trZ); + fe_carry(&trZ, &r->Z); + fe_sub(&r->T, &trT, &trZ); +} + +// r = 2 * p +static void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { + ge_p2 q; + ge_p3_to_p2(&q, p); + ge_p2_dbl(r, &q); +} + +// r = p + q +static void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe trY, trZ, trT; + + fe_add(&r->X, &p->Y, &p->X); + fe_sub(&r->Y, &p->Y, &p->X); + fe_mul_tll(&trZ, &r->X, &q->yplusx); + fe_mul_tll(&trY, &r->Y, &q->yminusx); + fe_mul_tlt(&trT, &q->xy2d, &p->T); + fe_add(&r->T, &p->Z, &p->Z); + fe_sub(&r->X, &trZ, &trY); + fe_add(&r->Y, &trZ, &trY); + fe_carry(&trZ, &r->T); + fe_add(&r->Z, &trZ, &trT); + fe_sub(&r->T, &trZ, &trT); +} + +// r = p - q +static void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe trY, trZ, trT; + + fe_add(&r->X, &p->Y, &p->X); + fe_sub(&r->Y, &p->Y, &p->X); + fe_mul_tll(&trZ, &r->X, &q->yminusx); + fe_mul_tll(&trY, &r->Y, &q->yplusx); + fe_mul_tlt(&trT, &q->xy2d, &p->T); + fe_add(&r->T, &p->Z, &p->Z); + fe_sub(&r->X, &trZ, &trY); + fe_add(&r->Y, &trZ, &trY); + fe_carry(&trZ, &r->T); + fe_sub(&r->Z, &trZ, &trT); + fe_add(&r->T, &trZ, &trT); +} + +// r = p + q +void x25519_ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe trX, trY, trZ, trT; + + fe_add(&r->X, &p->Y, &p->X); + fe_sub(&r->Y, &p->Y, &p->X); + fe_mul_tll(&trZ, &r->X, &q->YplusX); + fe_mul_tll(&trY, &r->Y, &q->YminusX); + fe_mul_tlt(&trT, &q->T2d, &p->T); + fe_mul_ttl(&trX, &p->Z, &q->Z); + fe_add(&r->T, &trX, &trX); + fe_sub(&r->X, &trZ, &trY); + fe_add(&r->Y, &trZ, &trY); + fe_carry(&trZ, &r->T); + fe_add(&r->Z, &trZ, &trT); + fe_sub(&r->T, &trZ, &trT); +} + +// r = p - q +void x25519_ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe trX, trY, trZ, trT; + + fe_add(&r->X, &p->Y, &p->X); + fe_sub(&r->Y, &p->Y, &p->X); + fe_mul_tll(&trZ, &r->X, &q->YminusX); + fe_mul_tll(&trY, &r->Y, &q->YplusX); + fe_mul_tlt(&trT, &q->T2d, &p->T); + fe_mul_ttl(&trX, &p->Z, &q->Z); + fe_add(&r->T, &trX, &trX); + fe_sub(&r->X, &trZ, &trY); + fe_add(&r->Y, &trZ, &trY); + fe_carry(&trZ, &r->T); + fe_sub(&r->Z, &trZ, &trT); + fe_add(&r->T, &trZ, &trT); +} + +static void slide(signed char *r, const uint8_t *a) { + int i; + int b; + int k; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + + for (i = 0; i < 256; ++i) { + if (r[i]) { + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; + r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } else { + break; + } + } + } + } + } +} + +// r = a * A + b * B +// where a = a[0]+256*a[1]+...+256^31 a[31]. +// and b = b[0]+256*b[1]+...+256^31 b[31]. +// B is the Ed25519 base point (x,4/5) with x positive. +static void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a, + const ge_p3 *A, const uint8_t *b) { + signed char aslide[256]; + signed char bslide[256]; + ge_cached Ai[8]; // A,3A,5A,7A,9A,11A,13A,15A + ge_p1p1 t; + ge_p3 u; + ge_p3 A2; + int i; + + slide(aslide, a); + slide(bslide, b); + + x25519_ge_p3_to_cached(&Ai[0], A); + ge_p3_dbl(&t, A); + x25519_ge_p1p1_to_p3(&A2, &t); + x25519_ge_add(&t, &A2, &Ai[0]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[1], &u); + x25519_ge_add(&t, &A2, &Ai[1]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[2], &u); + x25519_ge_add(&t, &A2, &Ai[2]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[3], &u); + x25519_ge_add(&t, &A2, &Ai[3]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[4], &u); + x25519_ge_add(&t, &A2, &Ai[4]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[5], &u); + x25519_ge_add(&t, &A2, &Ai[5]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[6], &u); + x25519_ge_add(&t, &A2, &Ai[6]); + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_p3_to_cached(&Ai[7], &u); + + ge_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) { + break; + } + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, r); + + if (aslide[i] > 0) { + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_add(&t, &u, &Ai[aslide[i] / 2]); + } else if (aslide[i] < 0) { + x25519_ge_p1p1_to_p3(&u, &t); + x25519_ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + if (bslide[i] > 0) { + x25519_ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + x25519_ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); + } + + x25519_ge_p1p1_to_p2(r, &t); + } +} + +// int64_lshift21 returns |a << 21| but is defined when shifting bits into the +// sign bit. This works around a language flaw in C. +static inline int64_t int64_lshift21(int64_t a) { + return (int64_t)((uint64_t)a << 21); +} + +// The set of scalars is \Z/l +// where l = 2^252 + 27742317777372353535851937790883648493. + +// Input: +// s[0]+256*s[1]+...+256^63*s[63] = s +// +// Output: +// s[0]+256*s[1]+...+256^31*s[31] = s mod l +// where l = 2^252 + 27742317777372353535851937790883648493. +// Overwrites s in place. +void x25519_sc_reduce(uint8_t s[64]) { + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = 2097151 & (load_4(s + 28) >> 7); + int64_t s12 = 2097151 & (load_4(s + 31) >> 4); + int64_t s13 = 2097151 & (load_3(s + 34) >> 1); + int64_t s14 = 2097151 & (load_4(s + 36) >> 6); + int64_t s15 = 2097151 & (load_3(s + 39) >> 3); + int64_t s16 = 2097151 & load_3(s + 42); + int64_t s17 = 2097151 & (load_4(s + 44) >> 5); + int64_t s18 = 2097151 & (load_3(s + 47) >> 2); + int64_t s19 = 2097151 & (load_4(s + 49) >> 7); + int64_t s20 = 2097151 & (load_4(s + 52) >> 4); + int64_t s21 = 2097151 & (load_3(s + 55) >> 1); + int64_t s22 = 2097151 & (load_4(s + 57) >> 6); + int64_t s23 = (load_4(s + 60) >> 3); + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= int64_lshift21(carry6); + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= int64_lshift21(carry8); + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= int64_lshift21(carry10); + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= int64_lshift21(carry12); + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= int64_lshift21(carry14); + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= int64_lshift21(carry16); + + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= int64_lshift21(carry7); + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= int64_lshift21(carry9); + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= int64_lshift21(carry11); + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= int64_lshift21(carry13); + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= int64_lshift21(carry15); + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= int64_lshift21(carry0); + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= int64_lshift21(carry2); + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= int64_lshift21(carry4); + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= int64_lshift21(carry6); + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= int64_lshift21(carry8); + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= int64_lshift21(carry10); + + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= int64_lshift21(carry1); + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= int64_lshift21(carry3); + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= int64_lshift21(carry5); + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= int64_lshift21(carry7); + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= int64_lshift21(carry9); + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= int64_lshift21(carry11); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= int64_lshift21(carry0); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= int64_lshift21(carry1); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= int64_lshift21(carry2); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= int64_lshift21(carry3); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= int64_lshift21(carry4); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= int64_lshift21(carry5); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= int64_lshift21(carry6); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= int64_lshift21(carry7); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= int64_lshift21(carry8); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= int64_lshift21(carry9); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= int64_lshift21(carry10); + carry11 = s11 >> 21; + s12 += carry11; + s11 -= int64_lshift21(carry11); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= int64_lshift21(carry0); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= int64_lshift21(carry1); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= int64_lshift21(carry2); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= int64_lshift21(carry3); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= int64_lshift21(carry4); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= int64_lshift21(carry5); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= int64_lshift21(carry6); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= int64_lshift21(carry7); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= int64_lshift21(carry8); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= int64_lshift21(carry9); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= int64_lshift21(carry10); + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 << 5); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 << 2); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 << 7); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 << 4); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 << 1); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 << 6); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 << 3); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 << 5); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 << 2); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 << 7); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + +int ED25519_verify(const uint8_t *message, size_t message_len, + const uint8_t signature[64], const uint8_t public_key[32]) { + ge_p3 A; + if ((signature[63] & 224) != 0 || + !x25519_ge_frombytes_vartime(&A, public_key)) { + return 0; + } + + fe_loose t; + fe_neg(&t, &A.X); + fe_carry(&A.X, &t); + fe_neg(&t, &A.T); + fe_carry(&A.T, &t); + + uint8_t pkcopy[32]; + memcpy(pkcopy, public_key, 32); + uint8_t rcopy[32]; + memcpy(rcopy, signature, 32); + union { + uint64_t u64[4]; + uint8_t u8[32]; + } scopy; + memcpy(&scopy.u8[0], signature + 32, 32); + + // https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in + // the range [0, order) in order to prevent signature malleability. + + // kOrder is the order of Curve25519 in little-endian form. + static const uint64_t kOrder[4] = { + UINT64_C(0x5812631a5cf5d3ed), + UINT64_C(0x14def9dea2f79cd6), + 0, + UINT64_C(0x1000000000000000), + }; + for (size_t i = 3;; i--) { + if (scopy.u64[i] > kOrder[i]) { + return 0; + } else if (scopy.u64[i] < kOrder[i]) { + break; + } else if (i == 0) { + return 0; + } + } + +#if defined(MCUBOOT_USE_MBED_TLS) + + mbedtls_sha512_context ctx; + int ret; + + mbedtls_sha512_init(&ctx); + + ret = mbedtls_sha512_starts_ret(&ctx, 0); + assert(ret == 0); + + ret = mbedtls_sha512_update_ret(&ctx, signature, 32); + assert(ret == 0); + ret = mbedtls_sha512_update_ret(&ctx, public_key, 32); + assert(ret == 0); + ret = mbedtls_sha512_update_ret(&ctx, message, message_len); + assert(ret == 0); + + uint8_t h[SHA512_DIGEST_LENGTH]; + ret = mbedtls_sha512_finish_ret(&ctx, h); + assert(ret == 0); + mbedtls_sha512_free(&ctx); + +#else + + struct tc_sha512_state_struct s; + int rc; + + rc = tc_sha512_init(&s); + assert(rc == TC_CRYPTO_SUCCESS); + + rc = tc_sha512_update(&s, signature, 32); + assert(rc == TC_CRYPTO_SUCCESS); + rc = tc_sha512_update(&s, public_key, 32); + assert(rc == TC_CRYPTO_SUCCESS); + rc = tc_sha512_update(&s, message, message_len); + assert(rc == TC_CRYPTO_SUCCESS); + + uint8_t h[TC_SHA512_DIGEST_SIZE]; + rc = tc_sha512_final(h, &s); + assert(rc == TC_CRYPTO_SUCCESS); + +#endif + + x25519_sc_reduce(h); + + ge_p2 R; + ge_double_scalarmult_vartime(&R, h, &A, scopy.u8); + + uint8_t rcheck[32]; + x25519_ge_tobytes(rcheck, &R); + + return CRYPTO_memcmp(rcheck, rcopy, sizeof(rcheck)) == 0; +} + +static void fe_cswap(fe *f, fe *g, fe_limb_t b) { + b = 0-b; + for (unsigned i = 0; i < FE_NUM_LIMBS; i++) { + fe_limb_t x = f->v[i] ^ g->v[i]; + x &= b; + f->v[i] ^= x; + g->v[i] ^= x; + } +} + +static void fiat_25519_carry_scmul_121666(uint32_t out1[10], const uint32_t arg1[10]) { + uint64_t x1 = ((uint64_t)UINT32_C(0x1db42) * (arg1[9])); + uint64_t x2 = ((uint64_t)UINT32_C(0x1db42) * (arg1[8])); + uint64_t x3 = ((uint64_t)UINT32_C(0x1db42) * (arg1[7])); + uint64_t x4 = ((uint64_t)UINT32_C(0x1db42) * (arg1[6])); + uint64_t x5 = ((uint64_t)UINT32_C(0x1db42) * (arg1[5])); + uint64_t x6 = ((uint64_t)UINT32_C(0x1db42) * (arg1[4])); + uint64_t x7 = ((uint64_t)UINT32_C(0x1db42) * (arg1[3])); + uint64_t x8 = ((uint64_t)UINT32_C(0x1db42) * (arg1[2])); + uint64_t x9 = ((uint64_t)UINT32_C(0x1db42) * (arg1[1])); + uint64_t x10 = ((uint64_t)UINT32_C(0x1db42) * (arg1[0])); + uint32_t x11 = (uint32_t)(x10 >> 26); + uint32_t x12 = (uint32_t)(x10 & UINT32_C(0x3ffffff)); + uint64_t x13 = (x11 + x9); + uint32_t x14 = (uint32_t)(x13 >> 25); + uint32_t x15 = (uint32_t)(x13 & UINT32_C(0x1ffffff)); + uint64_t x16 = (x14 + x8); + uint32_t x17 = (uint32_t)(x16 >> 26); + uint32_t x18 = (uint32_t)(x16 & UINT32_C(0x3ffffff)); + uint64_t x19 = (x17 + x7); + uint32_t x20 = (uint32_t)(x19 >> 25); + uint32_t x21 = (uint32_t)(x19 & UINT32_C(0x1ffffff)); + uint64_t x22 = (x20 + x6); + uint32_t x23 = (uint32_t)(x22 >> 26); + uint32_t x24 = (uint32_t)(x22 & UINT32_C(0x3ffffff)); + uint64_t x25 = (x23 + x5); + uint32_t x26 = (uint32_t)(x25 >> 25); + uint32_t x27 = (uint32_t)(x25 & UINT32_C(0x1ffffff)); + uint64_t x28 = (x26 + x4); + uint32_t x29 = (uint32_t)(x28 >> 26); + uint32_t x30 = (uint32_t)(x28 & UINT32_C(0x3ffffff)); + uint64_t x31 = (x29 + x3); + uint32_t x32 = (uint32_t)(x31 >> 25); + uint32_t x33 = (uint32_t)(x31 & UINT32_C(0x1ffffff)); + uint64_t x34 = (x32 + x2); + uint32_t x35 = (uint32_t)(x34 >> 26); + uint32_t x36 = (uint32_t)(x34 & UINT32_C(0x3ffffff)); + uint64_t x37 = (x35 + x1); + uint32_t x38 = (uint32_t)(x37 >> 25); + uint32_t x39 = (uint32_t)(x37 & UINT32_C(0x1ffffff)); + uint32_t x40 = (x38 * (uint32_t)UINT8_C(0x13)); + uint32_t x41 = (x12 + x40); + uint32_t x42 = (x41 >> 26); + uint32_t x43 = (x41 & UINT32_C(0x3ffffff)); + uint32_t x44 = (x42 + x15); + uint32_t x45 = (x44 >> 25); + uint32_t x46 = (x44 & UINT32_C(0x1ffffff)); + uint32_t x47 = (x45 + x18); + out1[0] = x43; + out1[1] = x46; + out1[2] = x47; + out1[3] = x21; + out1[4] = x24; + out1[5] = x27; + out1[6] = x30; + out1[7] = x33; + out1[8] = x36; + out1[9] = x39; +} + +static void fe_mul121666(fe *h, const fe_loose *f) { + assert_fe_loose(f->v); + fiat_25519_carry_scmul_121666(h->v, f->v); + assert_fe(h->v); +} + +static void x25519_scalar_mult_generic(uint8_t out[32], + const uint8_t scalar[32], + const uint8_t point[32]) { + fe x1, x2, z2, x3, z3, tmp0, tmp1; + fe_loose x2l, z2l, x3l, tmp0l, tmp1l; + + uint8_t e[32]; + memcpy(e, scalar, 32); + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + + // The following implementation was transcribed to Coq and proven to + // correspond to unary scalar multiplication in affine coordinates given that + // x1 != 0 is the x coordinate of some point on the curve. It was also checked + // in Coq that doing a ladderstep with x1 = x3 = 0 gives z2' = z3' = 0, and z2 + // = z3 = 0 gives z2' = z3' = 0. The statement was quantified over the + // underlying field, so it applies to Curve25519 itself and the quadratic + // twist of Curve25519. It was not proven in Coq that prime-field arithmetic + // correctly simulates extension-field arithmetic on prime-field values. + // The decoding of the byte array representation of e was not considered. + // Specification of Montgomery curves in affine coordinates: + // + // Proof that these form a group that is isomorphic to a Weierstrass curve: + // + // Coq transcription and correctness proof of the loop (where scalarbits=255): + // + // + // preconditions: 0 <= e < 2^255 (not necessarily e < order), fe_invert(0) = 0 + fe_frombytes(&x1, point); + fe_1(&x2); + fe_0(&z2); + fe_copy(&x3, &x1); + fe_1(&z3); + + unsigned swap = 0; + int pos; + for (pos = 254; pos >= 0; --pos) { + // loop invariant as of right before the test, for the case where x1 != 0: + // pos >= -1; if z2 = 0 then x2 is nonzero; if z3 = 0 then x3 is nonzero + // let r := e >> (pos+1) in the following equalities of projective points: + // to_xz (r*P) === if swap then (x3, z3) else (x2, z2) + // to_xz ((r+1)*P) === if swap then (x2, z2) else (x3, z3) + // x1 is the nonzero x coordinate of the nonzero point (r*P-(r+1)*P) + unsigned b = 1 & (e[pos / 8] >> (pos & 7)); + swap ^= b; + fe_cswap(&x2, &x3, swap); + fe_cswap(&z2, &z3, swap); + swap = b; + // Coq transcription of ladderstep formula (called from transcribed loop): + // + // + // x1 != 0 + // x1 = 0 + fe_sub(&tmp0l, &x3, &z3); + fe_sub(&tmp1l, &x2, &z2); + fe_add(&x2l, &x2, &z2); + fe_add(&z2l, &x3, &z3); + fe_mul_tll(&z3, &tmp0l, &x2l); + fe_mul_tll(&z2, &z2l, &tmp1l); + fe_sq_tl(&tmp0, &tmp1l); + fe_sq_tl(&tmp1, &x2l); + fe_add(&x3l, &z3, &z2); + fe_sub(&z2l, &z3, &z2); + fe_mul_ttt(&x2, &tmp1, &tmp0); + fe_sub(&tmp1l, &tmp1, &tmp0); + fe_sq_tl(&z2, &z2l); + fe_mul121666(&z3, &tmp1l); + fe_sq_tl(&x3, &x3l); + fe_add(&tmp0l, &tmp0, &z3); + fe_mul_ttt(&z3, &x1, &z2); + fe_mul_tll(&z2, &tmp1l, &tmp0l); + } + // here pos=-1, so r=e, so to_xz (e*P) === if swap then (x3, z3) else (x2, z2) + fe_cswap(&x2, &x3, swap); + fe_cswap(&z2, &z3, swap); + + fe_invert(&z2, &z2); + fe_mul_ttt(&x2, &x2, &z2); + fe_tobytes(out, &x2); +} + +int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32], + const uint8_t peer_public_value[32]) { + static const uint8_t kZeros[32] = {0}; + x25519_scalar_mult_generic(out_shared_key, private_key, peer_public_value); + // The all-zero output results when the input is a point of small order. + return CRYPTO_memcmp(kZeros, out_shared_key, 32) != 0; +} diff --git a/bootloader/mcuboot/ext/fiat/src/curve25519.h b/bootloader/mcuboot/ext/fiat/src/curve25519.h new file mode 100644 index 0000000..e98334a --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/src/curve25519.h @@ -0,0 +1,884 @@ +/* Autogenerated */ +/* curve description: 25519 */ +/* requested operations: carry_mul, carry_square, carry_scmul121666, carry, add, sub, opp, selectznz, to_bytes, from_bytes */ +/* n = 10 (from "10") */ +/* s = 0x8000000000000000000000000000000000000000000000000000000000000000 (from "2^255") */ +/* c = [(1, 19)] (from "1,19") */ +/* machine_wordsize = 32 (from "32") */ + +#include + +#ifndef UINT64_C +#define UINT64_C(x) x##ULL +#endif +#ifndef UINT32_C +#define UINT32_C(x) x##UL +#endif +#ifndef UINT8_C +#define UINT8_C(x) (x) +#endif + +// fe means field element. Here the field is \Z/(2^255-19). An element t, +// entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 +// t[3]+2^102 t[4]+...+2^230 t[9]. +// fe limbs are bounded by 1.125*2^26,1.125*2^25,1.125*2^26,1.125*2^25,etc. +// Multiplication and carrying produce fe from fe_loose. +typedef struct fe { uint32_t v[10]; } fe; + +// fe_loose limbs are bounded by 3.375*2^26,3.375*2^25,3.375*2^26,3.375*2^25,etc. +// Addition and subtraction produce fe_loose from (fe, fe). +typedef struct fe_loose { uint32_t v[10]; } fe_loose; + +// ge means group element. +// +// Here the group is the set of pairs (x,y) of field elements (see fe.h) +// satisfying -x^2 + y^2 = 1 + d x^2y^2 +// where d = -121665/121666. +// +// Representations: +// ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z +// ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT +// ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T +// ge_precomp (Duif): (y+x,y-x,2dxy) + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe_loose X; + fe_loose Y; + fe_loose Z; + fe_loose T; +} ge_p1p1; + +typedef struct { + fe_loose yplusx; + fe_loose yminusx; + fe_loose xy2d; +} ge_precomp; + +typedef struct { + fe_loose YplusX; + fe_loose YminusX; + fe_loose Z; + fe_loose T2d; +} ge_cached; + +typedef unsigned char fiat_25519_uint1; +typedef signed char fiat_25519_int1; + +/* + * Input Bounds: + * arg1: [0x0 ~> 0x1] + * arg2: [0x0 ~> 0x3ffffff] + * arg3: [0x0 ~> 0x3ffffff] + * Output Bounds: + * out1: [0x0 ~> 0x3ffffff] + * out2: [0x0 ~> 0x1] + */ +static void fiat_25519_addcarryx_u26(uint32_t* out1, fiat_25519_uint1* out2, fiat_25519_uint1 arg1, uint32_t arg2, uint32_t arg3) { + uint32_t x1 = ((arg1 + arg2) + arg3); + uint32_t x2 = (x1 & UINT32_C(0x3ffffff)); + fiat_25519_uint1 x3 = (fiat_25519_uint1)(x1 >> 26); + *out1 = x2; + *out2 = x3; +} + +/* + * Input Bounds: + * arg1: [0x0 ~> 0x1] + * arg2: [0x0 ~> 0x3ffffff] + * arg3: [0x0 ~> 0x3ffffff] + * Output Bounds: + * out1: [0x0 ~> 0x3ffffff] + * out2: [0x0 ~> 0x1] + */ +static void fiat_25519_subborrowx_u26(uint32_t* out1, fiat_25519_uint1* out2, fiat_25519_uint1 arg1, uint32_t arg2, uint32_t arg3) { + int32_t x1 = ((int32_t)(arg2 - arg1) - (int32_t)arg3); + fiat_25519_int1 x2 = (fiat_25519_int1)(x1 >> 26); + uint32_t x3 = (x1 & UINT32_C(0x3ffffff)); + *out1 = x3; + *out2 = (fiat_25519_uint1)(0x0 - x2); +} + +/* + * Input Bounds: + * arg1: [0x0 ~> 0x1] + * arg2: [0x0 ~> 0x1ffffff] + * arg3: [0x0 ~> 0x1ffffff] + * Output Bounds: + * out1: [0x0 ~> 0x1ffffff] + * out2: [0x0 ~> 0x1] + */ +static void fiat_25519_addcarryx_u25(uint32_t* out1, fiat_25519_uint1* out2, fiat_25519_uint1 arg1, uint32_t arg2, uint32_t arg3) { + uint32_t x1 = ((arg1 + arg2) + arg3); + uint32_t x2 = (x1 & UINT32_C(0x1ffffff)); + fiat_25519_uint1 x3 = (fiat_25519_uint1)(x1 >> 25); + *out1 = x2; + *out2 = x3; +} + +/* + * Input Bounds: + * arg1: [0x0 ~> 0x1] + * arg2: [0x0 ~> 0x1ffffff] + * arg3: [0x0 ~> 0x1ffffff] + * Output Bounds: + * out1: [0x0 ~> 0x1ffffff] + * out2: [0x0 ~> 0x1] + */ +static void fiat_25519_subborrowx_u25(uint32_t* out1, fiat_25519_uint1* out2, fiat_25519_uint1 arg1, uint32_t arg2, uint32_t arg3) { + int32_t x1 = ((int32_t)(arg2 - arg1) - (int32_t)arg3); + fiat_25519_int1 x2 = (fiat_25519_int1)(x1 >> 25); + uint32_t x3 = (x1 & UINT32_C(0x1ffffff)); + *out1 = x3; + *out2 = (fiat_25519_uint1)(0x0 - x2); +} + +// value_barrier_u32 returns |a|, but prevents GCC and Clang from reasoning about +// the returned value. This is used to mitigate compilers undoing constant-time +// code, until we can express our requirements directly in the language. +// +// Note the compiler is aware that |value_barrier_u32| has no side effects and +// always has the same output for a given input. This allows it to eliminate +// dead code, move computations across loops, and vectorize. +static inline uint32_t value_barrier_u32(uint32_t a) { +#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__)) + __asm__("" : "+r"(a) : /* no inputs */); +#endif + return a; +} + +/* + * Input Bounds: + * arg1: [0x0 ~> 0x1] + * arg2: [0x0 ~> 0xffffffff] + * arg3: [0x0 ~> 0xffffffff] + * Output Bounds: + * out1: [0x0 ~> 0xffffffff] + */ +static void fiat_25519_cmovznz_u32(uint32_t* out1, fiat_25519_uint1 arg1, uint32_t arg2, uint32_t arg3) { + fiat_25519_uint1 x1 = (!(!arg1)); + uint32_t x2 = ((fiat_25519_int1)(0x0 - x1) & UINT32_C(0xffffffff)); + // Note this line has been patched from the synthesized code to add value + // barriers. + // + // Clang recognizes this pattern as a select. While it usually transforms it + // to a cmov, it sometimes further transforms it into a branch, which we do + // not want. + uint32_t x3 = ((value_barrier_u32(x2) & arg3) | (value_barrier_u32(~x2) & arg2)); + *out1 = x3; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] + * arg2: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] + * Output Bounds: + * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + */ +static void fiat_25519_carry_mul(uint32_t out1[10], const uint32_t arg1[10], const uint32_t arg2[10]) { + uint64_t x1 = ((uint64_t)(arg1[9]) * ((arg2[9]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x2 = ((uint64_t)(arg1[9]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x3 = ((uint64_t)(arg1[9]) * ((arg2[7]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x4 = ((uint64_t)(arg1[9]) * ((arg2[6]) * (uint32_t)UINT8_C(0x13))); + uint64_t x5 = ((uint64_t)(arg1[9]) * ((arg2[5]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x6 = ((uint64_t)(arg1[9]) * ((arg2[4]) * (uint32_t)UINT8_C(0x13))); + uint64_t x7 = ((uint64_t)(arg1[9]) * ((arg2[3]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x8 = ((uint64_t)(arg1[9]) * ((arg2[2]) * (uint32_t)UINT8_C(0x13))); + uint64_t x9 = ((uint64_t)(arg1[9]) * ((arg2[1]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x10 = ((uint64_t)(arg1[8]) * ((arg2[9]) * (uint32_t)UINT8_C(0x13))); + uint64_t x11 = ((uint64_t)(arg1[8]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x12 = ((uint64_t)(arg1[8]) * ((arg2[7]) * (uint32_t)UINT8_C(0x13))); + uint64_t x13 = ((uint64_t)(arg1[8]) * ((arg2[6]) * (uint32_t)UINT8_C(0x13))); + uint64_t x14 = ((uint64_t)(arg1[8]) * ((arg2[5]) * (uint32_t)UINT8_C(0x13))); + uint64_t x15 = ((uint64_t)(arg1[8]) * ((arg2[4]) * (uint32_t)UINT8_C(0x13))); + uint64_t x16 = ((uint64_t)(arg1[8]) * ((arg2[3]) * (uint32_t)UINT8_C(0x13))); + uint64_t x17 = ((uint64_t)(arg1[8]) * ((arg2[2]) * (uint32_t)UINT8_C(0x13))); + uint64_t x18 = ((uint64_t)(arg1[7]) * ((arg2[9]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x19 = ((uint64_t)(arg1[7]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x20 = ((uint64_t)(arg1[7]) * ((arg2[7]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x21 = ((uint64_t)(arg1[7]) * ((arg2[6]) * (uint32_t)UINT8_C(0x13))); + uint64_t x22 = ((uint64_t)(arg1[7]) * ((arg2[5]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x23 = ((uint64_t)(arg1[7]) * ((arg2[4]) * (uint32_t)UINT8_C(0x13))); + uint64_t x24 = ((uint64_t)(arg1[7]) * ((arg2[3]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x25 = ((uint64_t)(arg1[6]) * ((arg2[9]) * (uint32_t)UINT8_C(0x13))); + uint64_t x26 = ((uint64_t)(arg1[6]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x27 = ((uint64_t)(arg1[6]) * ((arg2[7]) * (uint32_t)UINT8_C(0x13))); + uint64_t x28 = ((uint64_t)(arg1[6]) * ((arg2[6]) * (uint32_t)UINT8_C(0x13))); + uint64_t x29 = ((uint64_t)(arg1[6]) * ((arg2[5]) * (uint32_t)UINT8_C(0x13))); + uint64_t x30 = ((uint64_t)(arg1[6]) * ((arg2[4]) * (uint32_t)UINT8_C(0x13))); + uint64_t x31 = ((uint64_t)(arg1[5]) * ((arg2[9]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x32 = ((uint64_t)(arg1[5]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x33 = ((uint64_t)(arg1[5]) * ((arg2[7]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x34 = ((uint64_t)(arg1[5]) * ((arg2[6]) * (uint32_t)UINT8_C(0x13))); + uint64_t x35 = ((uint64_t)(arg1[5]) * ((arg2[5]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x36 = ((uint64_t)(arg1[4]) * ((arg2[9]) * (uint32_t)UINT8_C(0x13))); + uint64_t x37 = ((uint64_t)(arg1[4]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x38 = ((uint64_t)(arg1[4]) * ((arg2[7]) * (uint32_t)UINT8_C(0x13))); + uint64_t x39 = ((uint64_t)(arg1[4]) * ((arg2[6]) * (uint32_t)UINT8_C(0x13))); + uint64_t x40 = ((uint64_t)(arg1[3]) * ((arg2[9]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x41 = ((uint64_t)(arg1[3]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x42 = ((uint64_t)(arg1[3]) * ((arg2[7]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x43 = ((uint64_t)(arg1[2]) * ((arg2[9]) * (uint32_t)UINT8_C(0x13))); + uint64_t x44 = ((uint64_t)(arg1[2]) * ((arg2[8]) * (uint32_t)UINT8_C(0x13))); + uint64_t x45 = ((uint64_t)(arg1[1]) * ((arg2[9]) * ((uint32_t)0x2 * UINT8_C(0x13)))); + uint64_t x46 = ((uint64_t)(arg1[9]) * (arg2[0])); + uint64_t x47 = ((uint64_t)(arg1[8]) * (arg2[1])); + uint64_t x48 = ((uint64_t)(arg1[8]) * (arg2[0])); + uint64_t x49 = ((uint64_t)(arg1[7]) * (arg2[2])); + uint64_t x50 = ((uint64_t)(arg1[7]) * ((arg2[1]) * (uint32_t)0x2)); + uint64_t x51 = ((uint64_t)(arg1[7]) * (arg2[0])); + uint64_t x52 = ((uint64_t)(arg1[6]) * (arg2[3])); + uint64_t x53 = ((uint64_t)(arg1[6]) * (arg2[2])); + uint64_t x54 = ((uint64_t)(arg1[6]) * (arg2[1])); + uint64_t x55 = ((uint64_t)(arg1[6]) * (arg2[0])); + uint64_t x56 = ((uint64_t)(arg1[5]) * (arg2[4])); + uint64_t x57 = ((uint64_t)(arg1[5]) * ((arg2[3]) * (uint32_t)0x2)); + uint64_t x58 = ((uint64_t)(arg1[5]) * (arg2[2])); + uint64_t x59 = ((uint64_t)(arg1[5]) * ((arg2[1]) * (uint32_t)0x2)); + uint64_t x60 = ((uint64_t)(arg1[5]) * (arg2[0])); + uint64_t x61 = ((uint64_t)(arg1[4]) * (arg2[5])); + uint64_t x62 = ((uint64_t)(arg1[4]) * (arg2[4])); + uint64_t x63 = ((uint64_t)(arg1[4]) * (arg2[3])); + uint64_t x64 = ((uint64_t)(arg1[4]) * (arg2[2])); + uint64_t x65 = ((uint64_t)(arg1[4]) * (arg2[1])); + uint64_t x66 = ((uint64_t)(arg1[4]) * (arg2[0])); + uint64_t x67 = ((uint64_t)(arg1[3]) * (arg2[6])); + uint64_t x68 = ((uint64_t)(arg1[3]) * ((arg2[5]) * (uint32_t)0x2)); + uint64_t x69 = ((uint64_t)(arg1[3]) * (arg2[4])); + uint64_t x70 = ((uint64_t)(arg1[3]) * ((arg2[3]) * (uint32_t)0x2)); + uint64_t x71 = ((uint64_t)(arg1[3]) * (arg2[2])); + uint64_t x72 = ((uint64_t)(arg1[3]) * ((arg2[1]) * (uint32_t)0x2)); + uint64_t x73 = ((uint64_t)(arg1[3]) * (arg2[0])); + uint64_t x74 = ((uint64_t)(arg1[2]) * (arg2[7])); + uint64_t x75 = ((uint64_t)(arg1[2]) * (arg2[6])); + uint64_t x76 = ((uint64_t)(arg1[2]) * (arg2[5])); + uint64_t x77 = ((uint64_t)(arg1[2]) * (arg2[4])); + uint64_t x78 = ((uint64_t)(arg1[2]) * (arg2[3])); + uint64_t x79 = ((uint64_t)(arg1[2]) * (arg2[2])); + uint64_t x80 = ((uint64_t)(arg1[2]) * (arg2[1])); + uint64_t x81 = ((uint64_t)(arg1[2]) * (arg2[0])); + uint64_t x82 = ((uint64_t)(arg1[1]) * (arg2[8])); + uint64_t x83 = ((uint64_t)(arg1[1]) * ((arg2[7]) * (uint32_t)0x2)); + uint64_t x84 = ((uint64_t)(arg1[1]) * (arg2[6])); + uint64_t x85 = ((uint64_t)(arg1[1]) * ((arg2[5]) * (uint32_t)0x2)); + uint64_t x86 = ((uint64_t)(arg1[1]) * (arg2[4])); + uint64_t x87 = ((uint64_t)(arg1[1]) * ((arg2[3]) * (uint32_t)0x2)); + uint64_t x88 = ((uint64_t)(arg1[1]) * (arg2[2])); + uint64_t x89 = ((uint64_t)(arg1[1]) * ((arg2[1]) * (uint32_t)0x2)); + uint64_t x90 = ((uint64_t)(arg1[1]) * (arg2[0])); + uint64_t x91 = ((uint64_t)(arg1[0]) * (arg2[9])); + uint64_t x92 = ((uint64_t)(arg1[0]) * (arg2[8])); + uint64_t x93 = ((uint64_t)(arg1[0]) * (arg2[7])); + uint64_t x94 = ((uint64_t)(arg1[0]) * (arg2[6])); + uint64_t x95 = ((uint64_t)(arg1[0]) * (arg2[5])); + uint64_t x96 = ((uint64_t)(arg1[0]) * (arg2[4])); + uint64_t x97 = ((uint64_t)(arg1[0]) * (arg2[3])); + uint64_t x98 = ((uint64_t)(arg1[0]) * (arg2[2])); + uint64_t x99 = ((uint64_t)(arg1[0]) * (arg2[1])); + uint64_t x100 = ((uint64_t)(arg1[0]) * (arg2[0])); + uint64_t x101 = (x100 + (x45 + (x44 + (x42 + (x39 + (x35 + (x30 + (x24 + (x17 + x9))))))))); + uint64_t x102 = (x101 >> 26); + uint32_t x103 = (uint32_t)(x101 & UINT32_C(0x3ffffff)); + uint64_t x104 = (x91 + (x82 + (x74 + (x67 + (x61 + (x56 + (x52 + (x49 + (x47 + x46))))))))); + uint64_t x105 = (x92 + (x83 + (x75 + (x68 + (x62 + (x57 + (x53 + (x50 + (x48 + x1))))))))); + uint64_t x106 = (x93 + (x84 + (x76 + (x69 + (x63 + (x58 + (x54 + (x51 + (x10 + x2))))))))); + uint64_t x107 = (x94 + (x85 + (x77 + (x70 + (x64 + (x59 + (x55 + (x18 + (x11 + x3))))))))); + uint64_t x108 = (x95 + (x86 + (x78 + (x71 + (x65 + (x60 + (x25 + (x19 + (x12 + x4))))))))); + uint64_t x109 = (x96 + (x87 + (x79 + (x72 + (x66 + (x31 + (x26 + (x20 + (x13 + x5))))))))); + uint64_t x110 = (x97 + (x88 + (x80 + (x73 + (x36 + (x32 + (x27 + (x21 + (x14 + x6))))))))); + uint64_t x111 = (x98 + (x89 + (x81 + (x40 + (x37 + (x33 + (x28 + (x22 + (x15 + x7))))))))); + uint64_t x112 = (x99 + (x90 + (x43 + (x41 + (x38 + (x34 + (x29 + (x23 + (x16 + x8))))))))); + uint64_t x113 = (x102 + x112); + uint64_t x114 = (x113 >> 25); + uint32_t x115 = (uint32_t)(x113 & UINT32_C(0x1ffffff)); + uint64_t x116 = (x114 + x111); + uint64_t x117 = (x116 >> 26); + uint32_t x118 = (uint32_t)(x116 & UINT32_C(0x3ffffff)); + uint64_t x119 = (x117 + x110); + uint64_t x120 = (x119 >> 25); + uint32_t x121 = (uint32_t)(x119 & UINT32_C(0x1ffffff)); + uint64_t x122 = (x120 + x109); + uint64_t x123 = (x122 >> 26); + uint32_t x124 = (uint32_t)(x122 & UINT32_C(0x3ffffff)); + uint64_t x125 = (x123 + x108); + uint64_t x126 = (x125 >> 25); + uint32_t x127 = (uint32_t)(x125 & UINT32_C(0x1ffffff)); + uint64_t x128 = (x126 + x107); + uint64_t x129 = (x128 >> 26); + uint32_t x130 = (uint32_t)(x128 & UINT32_C(0x3ffffff)); + uint64_t x131 = (x129 + x106); + uint64_t x132 = (x131 >> 25); + uint32_t x133 = (uint32_t)(x131 & UINT32_C(0x1ffffff)); + uint64_t x134 = (x132 + x105); + uint64_t x135 = (x134 >> 26); + uint32_t x136 = (uint32_t)(x134 & UINT32_C(0x3ffffff)); + uint64_t x137 = (x135 + x104); + uint64_t x138 = (x137 >> 25); + uint32_t x139 = (uint32_t)(x137 & UINT32_C(0x1ffffff)); + uint64_t x140 = (x138 * (uint64_t)UINT8_C(0x13)); + uint64_t x141 = (x103 + x140); + uint32_t x142 = (uint32_t)(x141 >> 26); + uint32_t x143 = (uint32_t)(x141 & UINT32_C(0x3ffffff)); + uint32_t x144 = (x142 + x115); + uint32_t x145 = (x144 >> 25); + uint32_t x146 = (x144 & UINT32_C(0x1ffffff)); + uint32_t x147 = (x145 + x118); + out1[0] = x143; + out1[1] = x146; + out1[2] = x147; + out1[3] = x121; + out1[4] = x124; + out1[5] = x127; + out1[6] = x130; + out1[7] = x133; + out1[8] = x136; + out1[9] = x139; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] + * Output Bounds: + * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + */ +static void fiat_25519_carry_square(uint32_t out1[10], const uint32_t arg1[10]) { + uint32_t x1 = ((arg1[9]) * (uint32_t)UINT8_C(0x13)); + uint32_t x2 = (x1 * (uint32_t)0x2); + uint32_t x3 = ((arg1[9]) * (uint32_t)0x2); + uint32_t x4 = ((arg1[8]) * (uint32_t)UINT8_C(0x13)); + uint64_t x5 = (x4 * (uint64_t)0x2); + uint32_t x6 = ((arg1[8]) * (uint32_t)0x2); + uint32_t x7 = ((arg1[7]) * (uint32_t)UINT8_C(0x13)); + uint32_t x8 = (x7 * (uint32_t)0x2); + uint32_t x9 = ((arg1[7]) * (uint32_t)0x2); + uint32_t x10 = ((arg1[6]) * (uint32_t)UINT8_C(0x13)); + uint64_t x11 = (x10 * (uint64_t)0x2); + uint32_t x12 = ((arg1[6]) * (uint32_t)0x2); + uint32_t x13 = ((arg1[5]) * (uint32_t)UINT8_C(0x13)); + uint32_t x14 = ((arg1[5]) * (uint32_t)0x2); + uint32_t x15 = ((arg1[4]) * (uint32_t)0x2); + uint32_t x16 = ((arg1[3]) * (uint32_t)0x2); + uint32_t x17 = ((arg1[2]) * (uint32_t)0x2); + uint32_t x18 = ((arg1[1]) * (uint32_t)0x2); + uint64_t x19 = ((uint64_t)(arg1[9]) * (x1 * (uint32_t)0x2)); + uint64_t x20 = ((uint64_t)(arg1[8]) * x2); + uint64_t x21 = ((uint64_t)(arg1[8]) * x4); + uint64_t x22 = ((arg1[7]) * (x2 * (uint64_t)0x2)); + uint64_t x23 = ((arg1[7]) * x5); + uint64_t x24 = ((uint64_t)(arg1[7]) * (x7 * (uint32_t)0x2)); + uint64_t x25 = ((uint64_t)(arg1[6]) * x2); + uint64_t x26 = ((arg1[6]) * x5); + uint64_t x27 = ((uint64_t)(arg1[6]) * x8); + uint64_t x28 = ((uint64_t)(arg1[6]) * x10); + uint64_t x29 = ((arg1[5]) * (x2 * (uint64_t)0x2)); + uint64_t x30 = ((arg1[5]) * x5); + uint64_t x31 = ((arg1[5]) * (x8 * (uint64_t)0x2)); + uint64_t x32 = ((arg1[5]) * x11); + uint64_t x33 = ((uint64_t)(arg1[5]) * (x13 * (uint32_t)0x2)); + uint64_t x34 = ((uint64_t)(arg1[4]) * x2); + uint64_t x35 = ((arg1[4]) * x5); + uint64_t x36 = ((uint64_t)(arg1[4]) * x8); + uint64_t x37 = ((arg1[4]) * x11); + uint64_t x38 = ((uint64_t)(arg1[4]) * x14); + uint64_t x39 = ((uint64_t)(arg1[4]) * (arg1[4])); + uint64_t x40 = ((arg1[3]) * (x2 * (uint64_t)0x2)); + uint64_t x41 = ((arg1[3]) * x5); + uint64_t x42 = ((arg1[3]) * (x8 * (uint64_t)0x2)); + uint64_t x43 = ((uint64_t)(arg1[3]) * x12); + uint64_t x44 = ((uint64_t)(arg1[3]) * (x14 * (uint32_t)0x2)); + uint64_t x45 = ((uint64_t)(arg1[3]) * x15); + uint64_t x46 = ((uint64_t)(arg1[3]) * ((arg1[3]) * (uint32_t)0x2)); + uint64_t x47 = ((uint64_t)(arg1[2]) * x2); + uint64_t x48 = ((arg1[2]) * x5); + uint64_t x49 = ((uint64_t)(arg1[2]) * x9); + uint64_t x50 = ((uint64_t)(arg1[2]) * x12); + uint64_t x51 = ((uint64_t)(arg1[2]) * x14); + uint64_t x52 = ((uint64_t)(arg1[2]) * x15); + uint64_t x53 = ((uint64_t)(arg1[2]) * x16); + uint64_t x54 = ((uint64_t)(arg1[2]) * (arg1[2])); + uint64_t x55 = ((arg1[1]) * (x2 * (uint64_t)0x2)); + uint64_t x56 = ((uint64_t)(arg1[1]) * x6); + uint64_t x57 = ((uint64_t)(arg1[1]) * (x9 * (uint32_t)0x2)); + uint64_t x58 = ((uint64_t)(arg1[1]) * x12); + uint64_t x59 = ((uint64_t)(arg1[1]) * (x14 * (uint32_t)0x2)); + uint64_t x60 = ((uint64_t)(arg1[1]) * x15); + uint64_t x61 = ((uint64_t)(arg1[1]) * (x16 * (uint32_t)0x2)); + uint64_t x62 = ((uint64_t)(arg1[1]) * x17); + uint64_t x63 = ((uint64_t)(arg1[1]) * ((arg1[1]) * (uint32_t)0x2)); + uint64_t x64 = ((uint64_t)(arg1[0]) * x3); + uint64_t x65 = ((uint64_t)(arg1[0]) * x6); + uint64_t x66 = ((uint64_t)(arg1[0]) * x9); + uint64_t x67 = ((uint64_t)(arg1[0]) * x12); + uint64_t x68 = ((uint64_t)(arg1[0]) * x14); + uint64_t x69 = ((uint64_t)(arg1[0]) * x15); + uint64_t x70 = ((uint64_t)(arg1[0]) * x16); + uint64_t x71 = ((uint64_t)(arg1[0]) * x17); + uint64_t x72 = ((uint64_t)(arg1[0]) * x18); + uint64_t x73 = ((uint64_t)(arg1[0]) * (arg1[0])); + uint64_t x74 = (x73 + (x55 + (x48 + (x42 + (x37 + x33))))); + uint64_t x75 = (x74 >> 26); + uint32_t x76 = (uint32_t)(x74 & UINT32_C(0x3ffffff)); + uint64_t x77 = (x64 + (x56 + (x49 + (x43 + x38)))); + uint64_t x78 = (x65 + (x57 + (x50 + (x44 + (x39 + x19))))); + uint64_t x79 = (x66 + (x58 + (x51 + (x45 + x20)))); + uint64_t x80 = (x67 + (x59 + (x52 + (x46 + (x22 + x21))))); + uint64_t x81 = (x68 + (x60 + (x53 + (x25 + x23)))); + uint64_t x82 = (x69 + (x61 + (x54 + (x29 + (x26 + x24))))); + uint64_t x83 = (x70 + (x62 + (x34 + (x30 + x27)))); + uint64_t x84 = (x71 + (x63 + (x40 + (x35 + (x31 + x28))))); + uint64_t x85 = (x72 + (x47 + (x41 + (x36 + x32)))); + uint64_t x86 = (x75 + x85); + uint64_t x87 = (x86 >> 25); + uint32_t x88 = (uint32_t)(x86 & UINT32_C(0x1ffffff)); + uint64_t x89 = (x87 + x84); + uint64_t x90 = (x89 >> 26); + uint32_t x91 = (uint32_t)(x89 & UINT32_C(0x3ffffff)); + uint64_t x92 = (x90 + x83); + uint64_t x93 = (x92 >> 25); + uint32_t x94 = (uint32_t)(x92 & UINT32_C(0x1ffffff)); + uint64_t x95 = (x93 + x82); + uint64_t x96 = (x95 >> 26); + uint32_t x97 = (uint32_t)(x95 & UINT32_C(0x3ffffff)); + uint64_t x98 = (x96 + x81); + uint64_t x99 = (x98 >> 25); + uint32_t x100 = (uint32_t)(x98 & UINT32_C(0x1ffffff)); + uint64_t x101 = (x99 + x80); + uint64_t x102 = (x101 >> 26); + uint32_t x103 = (uint32_t)(x101 & UINT32_C(0x3ffffff)); + uint64_t x104 = (x102 + x79); + uint64_t x105 = (x104 >> 25); + uint32_t x106 = (uint32_t)(x104 & UINT32_C(0x1ffffff)); + uint64_t x107 = (x105 + x78); + uint64_t x108 = (x107 >> 26); + uint32_t x109 = (uint32_t)(x107 & UINT32_C(0x3ffffff)); + uint64_t x110 = (x108 + x77); + uint64_t x111 = (x110 >> 25); + uint32_t x112 = (uint32_t)(x110 & UINT32_C(0x1ffffff)); + uint64_t x113 = (x111 * (uint64_t)UINT8_C(0x13)); + uint64_t x114 = (x76 + x113); + uint32_t x115 = (uint32_t)(x114 >> 26); + uint32_t x116 = (uint32_t)(x114 & UINT32_C(0x3ffffff)); + uint32_t x117 = (x115 + x88); + uint32_t x118 = (x117 >> 25); + uint32_t x119 = (x117 & UINT32_C(0x1ffffff)); + uint32_t x120 = (x118 + x91); + out1[0] = x116; + out1[1] = x119; + out1[2] = x120; + out1[3] = x94; + out1[4] = x97; + out1[5] = x100; + out1[6] = x103; + out1[7] = x106; + out1[8] = x109; + out1[9] = x112; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] + * Output Bounds: + * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + */ +static void fiat_25519_carry(uint32_t out1[10], const uint32_t arg1[10]) { + uint32_t x1 = (arg1[0]); + uint32_t x2 = ((x1 >> 26) + (arg1[1])); + uint32_t x3 = ((x2 >> 25) + (arg1[2])); + uint32_t x4 = ((x3 >> 26) + (arg1[3])); + uint32_t x5 = ((x4 >> 25) + (arg1[4])); + uint32_t x6 = ((x5 >> 26) + (arg1[5])); + uint32_t x7 = ((x6 >> 25) + (arg1[6])); + uint32_t x8 = ((x7 >> 26) + (arg1[7])); + uint32_t x9 = ((x8 >> 25) + (arg1[8])); + uint32_t x10 = ((x9 >> 26) + (arg1[9])); + uint32_t x11 = ((x1 & UINT32_C(0x3ffffff)) + ((x10 >> 25) * (uint32_t)UINT8_C(0x13))); + uint32_t x12 = ((x11 >> 26) + (x2 & UINT32_C(0x1ffffff))); + uint32_t x13 = (x11 & UINT32_C(0x3ffffff)); + uint32_t x14 = (x12 & UINT32_C(0x1ffffff)); + uint32_t x15 = ((x12 >> 25) + (x3 & UINT32_C(0x3ffffff))); + uint32_t x16 = (x4 & UINT32_C(0x1ffffff)); + uint32_t x17 = (x5 & UINT32_C(0x3ffffff)); + uint32_t x18 = (x6 & UINT32_C(0x1ffffff)); + uint32_t x19 = (x7 & UINT32_C(0x3ffffff)); + uint32_t x20 = (x8 & UINT32_C(0x1ffffff)); + uint32_t x21 = (x9 & UINT32_C(0x3ffffff)); + uint32_t x22 = (x10 & UINT32_C(0x1ffffff)); + out1[0] = x13; + out1[1] = x14; + out1[2] = x15; + out1[3] = x16; + out1[4] = x17; + out1[5] = x18; + out1[6] = x19; + out1[7] = x20; + out1[8] = x21; + out1[9] = x22; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + * arg2: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + * Output Bounds: + * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] + */ +static void fiat_25519_add(uint32_t out1[10], const uint32_t arg1[10], const uint32_t arg2[10]) { + uint32_t x1 = ((arg1[0]) + (arg2[0])); + uint32_t x2 = ((arg1[1]) + (arg2[1])); + uint32_t x3 = ((arg1[2]) + (arg2[2])); + uint32_t x4 = ((arg1[3]) + (arg2[3])); + uint32_t x5 = ((arg1[4]) + (arg2[4])); + uint32_t x6 = ((arg1[5]) + (arg2[5])); + uint32_t x7 = ((arg1[6]) + (arg2[6])); + uint32_t x8 = ((arg1[7]) + (arg2[7])); + uint32_t x9 = ((arg1[8]) + (arg2[8])); + uint32_t x10 = ((arg1[9]) + (arg2[9])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; + out1[4] = x5; + out1[5] = x6; + out1[6] = x7; + out1[7] = x8; + out1[8] = x9; + out1[9] = x10; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + * arg2: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + * Output Bounds: + * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] + */ +static void fiat_25519_sub(uint32_t out1[10], const uint32_t arg1[10], const uint32_t arg2[10]) { + uint32_t x1 = ((UINT32_C(0x7ffffda) + (arg1[0])) - (arg2[0])); + uint32_t x2 = ((UINT32_C(0x3fffffe) + (arg1[1])) - (arg2[1])); + uint32_t x3 = ((UINT32_C(0x7fffffe) + (arg1[2])) - (arg2[2])); + uint32_t x4 = ((UINT32_C(0x3fffffe) + (arg1[3])) - (arg2[3])); + uint32_t x5 = ((UINT32_C(0x7fffffe) + (arg1[4])) - (arg2[4])); + uint32_t x6 = ((UINT32_C(0x3fffffe) + (arg1[5])) - (arg2[5])); + uint32_t x7 = ((UINT32_C(0x7fffffe) + (arg1[6])) - (arg2[6])); + uint32_t x8 = ((UINT32_C(0x3fffffe) + (arg1[7])) - (arg2[7])); + uint32_t x9 = ((UINT32_C(0x7fffffe) + (arg1[8])) - (arg2[8])); + uint32_t x10 = ((UINT32_C(0x3fffffe) + (arg1[9])) - (arg2[9])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; + out1[4] = x5; + out1[5] = x6; + out1[6] = x7; + out1[7] = x8; + out1[8] = x9; + out1[9] = x10; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + * Output Bounds: + * out1: [[0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999], [0x0 ~> 0xd333332], [0x0 ~> 0x6999999]] + */ +static void fiat_25519_opp(uint32_t out1[10], const uint32_t arg1[10]) { + uint32_t x1 = (UINT32_C(0x7ffffda) - (arg1[0])); + uint32_t x2 = (UINT32_C(0x3fffffe) - (arg1[1])); + uint32_t x3 = (UINT32_C(0x7fffffe) - (arg1[2])); + uint32_t x4 = (UINT32_C(0x3fffffe) - (arg1[3])); + uint32_t x5 = (UINT32_C(0x7fffffe) - (arg1[4])); + uint32_t x6 = (UINT32_C(0x3fffffe) - (arg1[5])); + uint32_t x7 = (UINT32_C(0x7fffffe) - (arg1[6])); + uint32_t x8 = (UINT32_C(0x3fffffe) - (arg1[7])); + uint32_t x9 = (UINT32_C(0x7fffffe) - (arg1[8])); + uint32_t x10 = (UINT32_C(0x3fffffe) - (arg1[9])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; + out1[4] = x5; + out1[5] = x6; + out1[6] = x7; + out1[7] = x8; + out1[8] = x9; + out1[9] = x10; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + * Output Bounds: + * out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x7f]] + */ +static void fiat_25519_to_bytes(uint8_t out1[32], const uint32_t arg1[10]) { + uint32_t x1; + fiat_25519_uint1 x2; + fiat_25519_subborrowx_u26(&x1, &x2, 0x0, (arg1[0]), UINT32_C(0x3ffffed)); + uint32_t x3; + fiat_25519_uint1 x4; + fiat_25519_subborrowx_u25(&x3, &x4, x2, (arg1[1]), UINT32_C(0x1ffffff)); + uint32_t x5; + fiat_25519_uint1 x6; + fiat_25519_subborrowx_u26(&x5, &x6, x4, (arg1[2]), UINT32_C(0x3ffffff)); + uint32_t x7; + fiat_25519_uint1 x8; + fiat_25519_subborrowx_u25(&x7, &x8, x6, (arg1[3]), UINT32_C(0x1ffffff)); + uint32_t x9; + fiat_25519_uint1 x10; + fiat_25519_subborrowx_u26(&x9, &x10, x8, (arg1[4]), UINT32_C(0x3ffffff)); + uint32_t x11; + fiat_25519_uint1 x12; + fiat_25519_subborrowx_u25(&x11, &x12, x10, (arg1[5]), UINT32_C(0x1ffffff)); + uint32_t x13; + fiat_25519_uint1 x14; + fiat_25519_subborrowx_u26(&x13, &x14, x12, (arg1[6]), UINT32_C(0x3ffffff)); + uint32_t x15; + fiat_25519_uint1 x16; + fiat_25519_subborrowx_u25(&x15, &x16, x14, (arg1[7]), UINT32_C(0x1ffffff)); + uint32_t x17; + fiat_25519_uint1 x18; + fiat_25519_subborrowx_u26(&x17, &x18, x16, (arg1[8]), UINT32_C(0x3ffffff)); + uint32_t x19; + fiat_25519_uint1 x20; + fiat_25519_subborrowx_u25(&x19, &x20, x18, (arg1[9]), UINT32_C(0x1ffffff)); + uint32_t x21; + fiat_25519_cmovznz_u32(&x21, x20, 0x0, UINT32_C(0xffffffff)); + uint32_t x22; + fiat_25519_uint1 x23; + fiat_25519_addcarryx_u26(&x22, &x23, 0x0, (x21 & UINT32_C(0x3ffffed)), x1); + uint32_t x24; + fiat_25519_uint1 x25; + fiat_25519_addcarryx_u25(&x24, &x25, x23, (x21 & UINT32_C(0x1ffffff)), x3); + uint32_t x26; + fiat_25519_uint1 x27; + fiat_25519_addcarryx_u26(&x26, &x27, x25, (x21 & UINT32_C(0x3ffffff)), x5); + uint32_t x28; + fiat_25519_uint1 x29; + fiat_25519_addcarryx_u25(&x28, &x29, x27, (x21 & UINT32_C(0x1ffffff)), x7); + uint32_t x30; + fiat_25519_uint1 x31; + fiat_25519_addcarryx_u26(&x30, &x31, x29, (x21 & UINT32_C(0x3ffffff)), x9); + uint32_t x32; + fiat_25519_uint1 x33; + fiat_25519_addcarryx_u25(&x32, &x33, x31, (x21 & UINT32_C(0x1ffffff)), x11); + uint32_t x34; + fiat_25519_uint1 x35; + fiat_25519_addcarryx_u26(&x34, &x35, x33, (x21 & UINT32_C(0x3ffffff)), x13); + uint32_t x36; + fiat_25519_uint1 x37; + fiat_25519_addcarryx_u25(&x36, &x37, x35, (x21 & UINT32_C(0x1ffffff)), x15); + uint32_t x38; + fiat_25519_uint1 x39; + fiat_25519_addcarryx_u26(&x38, &x39, x37, (x21 & UINT32_C(0x3ffffff)), x17); + uint32_t x40; + fiat_25519_uint1 x41; + fiat_25519_addcarryx_u25(&x40, &x41, x39, (x21 & UINT32_C(0x1ffffff)), x19); + uint32_t x42 = (x40 << 6); + uint32_t x43 = (x38 << 4); + uint32_t x44 = (x36 << 3); + uint32_t x45 = (x34 * (uint32_t)0x2); + uint32_t x46 = (x30 << 6); + uint32_t x47 = (x28 << 5); + uint32_t x48 = (x26 << 3); + uint32_t x49 = (x24 << 2); + uint32_t x50 = (x22 >> 8); + uint8_t x51 = (uint8_t)(x22 & UINT8_C(0xff)); + uint32_t x52 = (x50 >> 8); + uint8_t x53 = (uint8_t)(x50 & UINT8_C(0xff)); + uint8_t x54 = (uint8_t)(x52 >> 8); + uint8_t x55 = (uint8_t)(x52 & UINT8_C(0xff)); + uint32_t x56 = (x54 + x49); + uint32_t x57 = (x56 >> 8); + uint8_t x58 = (uint8_t)(x56 & UINT8_C(0xff)); + uint32_t x59 = (x57 >> 8); + uint8_t x60 = (uint8_t)(x57 & UINT8_C(0xff)); + uint8_t x61 = (uint8_t)(x59 >> 8); + uint8_t x62 = (uint8_t)(x59 & UINT8_C(0xff)); + uint32_t x63 = (x61 + x48); + uint32_t x64 = (x63 >> 8); + uint8_t x65 = (uint8_t)(x63 & UINT8_C(0xff)); + uint32_t x66 = (x64 >> 8); + uint8_t x67 = (uint8_t)(x64 & UINT8_C(0xff)); + uint8_t x68 = (uint8_t)(x66 >> 8); + uint8_t x69 = (uint8_t)(x66 & UINT8_C(0xff)); + uint32_t x70 = (x68 + x47); + uint32_t x71 = (x70 >> 8); + uint8_t x72 = (uint8_t)(x70 & UINT8_C(0xff)); + uint32_t x73 = (x71 >> 8); + uint8_t x74 = (uint8_t)(x71 & UINT8_C(0xff)); + uint8_t x75 = (uint8_t)(x73 >> 8); + uint8_t x76 = (uint8_t)(x73 & UINT8_C(0xff)); + uint32_t x77 = (x75 + x46); + uint32_t x78 = (x77 >> 8); + uint8_t x79 = (uint8_t)(x77 & UINT8_C(0xff)); + uint32_t x80 = (x78 >> 8); + uint8_t x81 = (uint8_t)(x78 & UINT8_C(0xff)); + uint8_t x82 = (uint8_t)(x80 >> 8); + uint8_t x83 = (uint8_t)(x80 & UINT8_C(0xff)); + uint8_t x84 = (uint8_t)(x82 & UINT8_C(0xff)); + uint32_t x85 = (x32 >> 8); + uint8_t x86 = (uint8_t)(x32 & UINT8_C(0xff)); + uint32_t x87 = (x85 >> 8); + uint8_t x88 = (uint8_t)(x85 & UINT8_C(0xff)); + fiat_25519_uint1 x89 = (fiat_25519_uint1)(x87 >> 8); + uint8_t x90 = (uint8_t)(x87 & UINT8_C(0xff)); + uint32_t x91 = (x89 + x45); + uint32_t x92 = (x91 >> 8); + uint8_t x93 = (uint8_t)(x91 & UINT8_C(0xff)); + uint32_t x94 = (x92 >> 8); + uint8_t x95 = (uint8_t)(x92 & UINT8_C(0xff)); + uint8_t x96 = (uint8_t)(x94 >> 8); + uint8_t x97 = (uint8_t)(x94 & UINT8_C(0xff)); + uint32_t x98 = (x96 + x44); + uint32_t x99 = (x98 >> 8); + uint8_t x100 = (uint8_t)(x98 & UINT8_C(0xff)); + uint32_t x101 = (x99 >> 8); + uint8_t x102 = (uint8_t)(x99 & UINT8_C(0xff)); + uint8_t x103 = (uint8_t)(x101 >> 8); + uint8_t x104 = (uint8_t)(x101 & UINT8_C(0xff)); + uint32_t x105 = (x103 + x43); + uint32_t x106 = (x105 >> 8); + uint8_t x107 = (uint8_t)(x105 & UINT8_C(0xff)); + uint32_t x108 = (x106 >> 8); + uint8_t x109 = (uint8_t)(x106 & UINT8_C(0xff)); + uint8_t x110 = (uint8_t)(x108 >> 8); + uint8_t x111 = (uint8_t)(x108 & UINT8_C(0xff)); + uint32_t x112 = (x110 + x42); + uint32_t x113 = (x112 >> 8); + uint8_t x114 = (uint8_t)(x112 & UINT8_C(0xff)); + uint32_t x115 = (x113 >> 8); + uint8_t x116 = (uint8_t)(x113 & UINT8_C(0xff)); + uint8_t x117 = (uint8_t)(x115 >> 8); + uint8_t x118 = (uint8_t)(x115 & UINT8_C(0xff)); + out1[0] = x51; + out1[1] = x53; + out1[2] = x55; + out1[3] = x58; + out1[4] = x60; + out1[5] = x62; + out1[6] = x65; + out1[7] = x67; + out1[8] = x69; + out1[9] = x72; + out1[10] = x74; + out1[11] = x76; + out1[12] = x79; + out1[13] = x81; + out1[14] = x83; + out1[15] = x84; + out1[16] = x86; + out1[17] = x88; + out1[18] = x90; + out1[19] = x93; + out1[20] = x95; + out1[21] = x97; + out1[22] = x100; + out1[23] = x102; + out1[24] = x104; + out1[25] = x107; + out1[26] = x109; + out1[27] = x111; + out1[28] = x114; + out1[29] = x116; + out1[30] = x118; + out1[31] = x117; +} + +/* + * Input Bounds: + * arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x7f]] + * Output Bounds: + * out1: [[0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333], [0x0 ~> 0x4666666], [0x0 ~> 0x2333333]] + */ +static void fiat_25519_from_bytes(uint32_t out1[10], const uint8_t arg1[32]) { + uint32_t x1 = ((uint32_t)(arg1[31]) << 18); + uint32_t x2 = ((uint32_t)(arg1[30]) << 10); + uint32_t x3 = ((uint32_t)(arg1[29]) << 2); + uint32_t x4 = ((uint32_t)(arg1[28]) << 20); + uint32_t x5 = ((uint32_t)(arg1[27]) << 12); + uint32_t x6 = ((uint32_t)(arg1[26]) << 4); + uint32_t x7 = ((uint32_t)(arg1[25]) << 21); + uint32_t x8 = ((uint32_t)(arg1[24]) << 13); + uint32_t x9 = ((uint32_t)(arg1[23]) << 5); + uint32_t x10 = ((uint32_t)(arg1[22]) << 23); + uint32_t x11 = ((uint32_t)(arg1[21]) << 15); + uint32_t x12 = ((uint32_t)(arg1[20]) << 7); + uint32_t x13 = ((uint32_t)(arg1[19]) << 24); + uint32_t x14 = ((uint32_t)(arg1[18]) << 16); + uint32_t x15 = ((uint32_t)(arg1[17]) << 8); + uint8_t x16 = (arg1[16]); + uint32_t x17 = ((uint32_t)(arg1[15]) << 18); + uint32_t x18 = ((uint32_t)(arg1[14]) << 10); + uint32_t x19 = ((uint32_t)(arg1[13]) << 2); + uint32_t x20 = ((uint32_t)(arg1[12]) << 19); + uint32_t x21 = ((uint32_t)(arg1[11]) << 11); + uint32_t x22 = ((uint32_t)(arg1[10]) << 3); + uint32_t x23 = ((uint32_t)(arg1[9]) << 21); + uint32_t x24 = ((uint32_t)(arg1[8]) << 13); + uint32_t x25 = ((uint32_t)(arg1[7]) << 5); + uint32_t x26 = ((uint32_t)(arg1[6]) << 22); + uint32_t x27 = ((uint32_t)(arg1[5]) << 14); + uint32_t x28 = ((uint32_t)(arg1[4]) << 6); + uint32_t x29 = ((uint32_t)(arg1[3]) << 24); + uint32_t x30 = ((uint32_t)(arg1[2]) << 16); + uint32_t x31 = ((uint32_t)(arg1[1]) << 8); + uint8_t x32 = (arg1[0]); + uint32_t x33 = (x32 + (x31 + (x30 + x29))); + uint8_t x34 = (uint8_t)(x33 >> 26); + uint32_t x35 = (x33 & UINT32_C(0x3ffffff)); + uint32_t x36 = (x3 + (x2 + x1)); + uint32_t x37 = (x6 + (x5 + x4)); + uint32_t x38 = (x9 + (x8 + x7)); + uint32_t x39 = (x12 + (x11 + x10)); + uint32_t x40 = (x16 + (x15 + (x14 + x13))); + uint32_t x41 = (x19 + (x18 + x17)); + uint32_t x42 = (x22 + (x21 + x20)); + uint32_t x43 = (x25 + (x24 + x23)); + uint32_t x44 = (x28 + (x27 + x26)); + uint32_t x45 = (x34 + x44); + uint8_t x46 = (uint8_t)(x45 >> 25); + uint32_t x47 = (x45 & UINT32_C(0x1ffffff)); + uint32_t x48 = (x46 + x43); + uint8_t x49 = (uint8_t)(x48 >> 26); + uint32_t x50 = (x48 & UINT32_C(0x3ffffff)); + uint32_t x51 = (x49 + x42); + uint8_t x52 = (uint8_t)(x51 >> 25); + uint32_t x53 = (x51 & UINT32_C(0x1ffffff)); + uint32_t x54 = (x52 + x41); + uint32_t x55 = (x54 & UINT32_C(0x3ffffff)); + uint8_t x56 = (uint8_t)(x40 >> 25); + uint32_t x57 = (x40 & UINT32_C(0x1ffffff)); + uint32_t x58 = (x56 + x39); + uint8_t x59 = (uint8_t)(x58 >> 26); + uint32_t x60 = (x58 & UINT32_C(0x3ffffff)); + uint32_t x61 = (x59 + x38); + uint8_t x62 = (uint8_t)(x61 >> 25); + uint32_t x63 = (x61 & UINT32_C(0x1ffffff)); + uint32_t x64 = (x62 + x37); + uint8_t x65 = (uint8_t)(x64 >> 26); + uint32_t x66 = (x64 & UINT32_C(0x3ffffff)); + uint32_t x67 = (x65 + x36); + out1[0] = x35; + out1[1] = x47; + out1[2] = x50; + out1[3] = x53; + out1[4] = x55; + out1[5] = x57; + out1[6] = x60; + out1[7] = x63; + out1[8] = x66; + out1[9] = x67; +} + diff --git a/bootloader/mcuboot/ext/fiat/src/curve25519_tables.h b/bootloader/mcuboot/ext/fiat/src/curve25519_tables.h new file mode 100644 index 0000000..bfefc1c --- /dev/null +++ b/bootloader/mcuboot/ext/fiat/src/curve25519_tables.h @@ -0,0 +1,107 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015-2016 the fiat-crypto authors (see the AUTHORS file). +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This file is generated from +// ./make_curve25519_tables.py > curve25519_tables.h + +static const fe d = {{ + 56195235, 13857412, 51736253, 6949390, 114729, 24766616, + 60832955, 30306712, 48412415, 21499315 +}}; + +static const fe sqrtm1 = {{ + 34513072, 25610706, 9377949, 3500415, 12389472, + 33281959, 41962654, 31548777, 326685, 11406482 +}}; + +static const fe d2 = {{ + 45281625, 27714825, 36363642, 13898781, 229458, 15978800, + 54557047, 27058993, 29715967, 9444199 +}}; + +// Bi[i] = (2*i+1)*B +static const ge_precomp Bi[8] = { + { + {{25967493, 19198397, 29566455, 3660896, 54414519, 4014786, 27544626, + 21800161, 61029707, 2047604}}, + {{54563134, 934261, 64385954, 3049989, 66381436, 9406985, 12720692, + 5043384, 19500929, 18085054}}, + {{58370664, 4489569, 9688441, 18769238, 10184608, 21191052, 29287918, + 11864899, 42594502, 29115885}}, + }, + { + {{15636272, 23865875, 24204772, 25642034, 616976, 16869170, 27787599, + 18782243, 28944399, 32004408}}, + {{16568933, 4717097, 55552716, 32452109, 15682895, 21747389, 16354576, + 21778470, 7689661, 11199574}}, + {{30464137, 27578307, 55329429, 17883566, 23220364, 15915852, 7512774, + 10017326, 49359771, 23634074}}, + }, + { + {{10861363, 11473154, 27284546, 1981175, 37044515, 12577860, 32867885, + 14515107, 51670560, 10819379}}, + {{4708026, 6336745, 20377586, 9066809, 55836755, 6594695, 41455196, + 12483687, 54440373, 5581305}}, + {{19563141, 16186464, 37722007, 4097518, 10237984, 29206317, 28542349, + 13850243, 43430843, 17738489}}, + }, + { + {{5153727, 9909285, 1723747, 30776558, 30523604, 5516873, 19480852, + 5230134, 43156425, 18378665}}, + {{36839857, 30090922, 7665485, 10083793, 28475525, 1649722, 20654025, + 16520125, 30598449, 7715701}}, + {{28881826, 14381568, 9657904, 3680757, 46927229, 7843315, 35708204, + 1370707, 29794553, 32145132}}, + }, + { + {{44589871, 26862249, 14201701, 24808930, 43598457, 8844725, 18474211, + 32192982, 54046167, 13821876}}, + {{60653668, 25714560, 3374701, 28813570, 40010246, 22982724, 31655027, + 26342105, 18853321, 19333481}}, + {{4566811, 20590564, 38133974, 21313742, 59506191, 30723862, 58594505, + 23123294, 2207752, 30344648}}, + }, + { + {{41954014, 29368610, 29681143, 7868801, 60254203, 24130566, 54671499, + 32891431, 35997400, 17421995}}, + {{25576264, 30851218, 7349803, 21739588, 16472781, 9300885, 3844789, + 15725684, 171356, 6466918}}, + {{23103977, 13316479, 9739013, 17404951, 817874, 18515490, 8965338, + 19466374, 36393951, 16193876}}, + }, + { + {{33587053, 3180712, 64714734, 14003686, 50205390, 17283591, 17238397, + 4729455, 49034351, 9256799}}, + {{41926547, 29380300, 32336397, 5036987, 45872047, 11360616, 22616405, + 9761698, 47281666, 630304}}, + {{53388152, 2639452, 42871404, 26147950, 9494426, 27780403, 60554312, + 17593437, 64659607, 19263131}}, + }, + { + {{63957664, 28508356, 9282713, 6866145, 35201802, 32691408, 48168288, + 15033783, 25105118, 25659556}}, + {{42782475, 15950225, 35307649, 18961608, 55446126, 28463506, 1573891, + 30928545, 2198789, 17749813}}, + {{64009494, 10324966, 64867251, 7453182, 61661885, 30818928, 53296841, + 17317989, 34647629, 21263748}}, + }, +}; diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/README b/bootloader/mcuboot/ext/mbedtls-asn1/README new file mode 100644 index 0000000..b6b4dcb --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/README @@ -0,0 +1,3 @@ +This bundles the asn1 parser from mbed-tls into mcuboot, which allows +adding EC crypto (tinycrypt based) functionality for target OSes that +don't bundle mbed-tls. diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/common.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/common.h new file mode 100644 index 0000000..a2c8a1e --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/common.h @@ -0,0 +1,56 @@ +/** + * \file common.h + * + * \brief Utility macros for internal use in the library + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_LIBRARY_COMMON_H +#define MBEDTLS_LIBRARY_COMMON_H + +#include "mbedtls/build_info.h" + +/** Helper to define a function as static except when building invasive tests. + * + * If a function is only used inside its own source file and should be + * declared `static` to allow the compiler to optimize for code size, + * but that function has unit tests, define it with + * ``` + * MBEDTLS_STATIC_TESTABLE int mbedtls_foo(...) { ... } + * ``` + * and declare it in a header in the `library/` directory with + * ``` + * #if defined(MBEDTLS_TEST_HOOKS) + * int mbedtls_foo(...); + * #endif + * ``` + */ +#if defined(MBEDTLS_TEST_HOOKS) +#define MBEDTLS_STATIC_TESTABLE +#else +#define MBEDTLS_STATIC_TESTABLE static +#endif + +/** Allow library to access its structs' private members. + * + * Although structs defined in header files are publicly available, + * their members are private and should not be accessed by the user. + */ +#define MBEDTLS_ALLOW_PRIVATE_ACCESS + +#endif /* MBEDTLS_LIBRARY_COMMON_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/asn1.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/asn1.h new file mode 100644 index 0000000..4668581 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/asn1.h @@ -0,0 +1,604 @@ +/** + * \file asn1.h + * + * \brief Generic ASN.1 parsing + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ASN1_H +#define MBEDTLS_ASN1_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +/** + * \addtogroup asn1_module + * \{ + */ + +/** + * \name ASN1 Error codes + * These error codes are OR'ed to X509 error codes for + * higher error granularity. + * ASN1 is a standard to specify data structures. + * \{ + */ +#define MBEDTLS_ERR_ASN1_OUT_OF_DATA -0x0060 /**< Out of data when parsing an ASN1 data structure. */ +#define MBEDTLS_ERR_ASN1_UNEXPECTED_TAG -0x0062 /**< ASN1 tag was of an unexpected value. */ +#define MBEDTLS_ERR_ASN1_INVALID_LENGTH -0x0064 /**< Error when trying to determine the length or invalid length. */ +#define MBEDTLS_ERR_ASN1_LENGTH_MISMATCH -0x0066 /**< Actual length differs from expected length. */ +#define MBEDTLS_ERR_ASN1_INVALID_DATA -0x0068 /**< Data is invalid. */ +#define MBEDTLS_ERR_ASN1_ALLOC_FAILED -0x006A /**< Memory allocation failed */ +#define MBEDTLS_ERR_ASN1_BUF_TOO_SMALL -0x006C /**< Buffer too small when writing ASN.1 data structure. */ + +/* \} name */ + +/** + * \name DER constants + * These constants comply with the DER encoded ASN.1 type tags. + * DER encoding uses hexadecimal representation. + * An example DER sequence is:\n + * - 0x02 -- tag indicating INTEGER + * - 0x01 -- length in octets + * - 0x05 -- value + * Such sequences are typically read into \c ::mbedtls_x509_buf. + * \{ + */ +#define MBEDTLS_ASN1_BOOLEAN 0x01 +#define MBEDTLS_ASN1_INTEGER 0x02 +#define MBEDTLS_ASN1_BIT_STRING 0x03 +#define MBEDTLS_ASN1_OCTET_STRING 0x04 +#define MBEDTLS_ASN1_NULL 0x05 +#define MBEDTLS_ASN1_OID 0x06 +#define MBEDTLS_ASN1_ENUMERATED 0x0A +#define MBEDTLS_ASN1_UTF8_STRING 0x0C +#define MBEDTLS_ASN1_SEQUENCE 0x10 +#define MBEDTLS_ASN1_SET 0x11 +#define MBEDTLS_ASN1_PRINTABLE_STRING 0x13 +#define MBEDTLS_ASN1_T61_STRING 0x14 +#define MBEDTLS_ASN1_IA5_STRING 0x16 +#define MBEDTLS_ASN1_UTC_TIME 0x17 +#define MBEDTLS_ASN1_GENERALIZED_TIME 0x18 +#define MBEDTLS_ASN1_UNIVERSAL_STRING 0x1C +#define MBEDTLS_ASN1_BMP_STRING 0x1E +#define MBEDTLS_ASN1_PRIMITIVE 0x00 +#define MBEDTLS_ASN1_CONSTRUCTED 0x20 +#define MBEDTLS_ASN1_CONTEXT_SPECIFIC 0x80 + +/* Slightly smaller way to check if tag is a string tag + * compared to canonical implementation. */ +#define MBEDTLS_ASN1_IS_STRING_TAG( tag ) \ + ( ( tag ) < 32u && ( \ + ( ( 1u << ( tag ) ) & ( ( 1u << MBEDTLS_ASN1_BMP_STRING ) | \ + ( 1u << MBEDTLS_ASN1_UTF8_STRING ) | \ + ( 1u << MBEDTLS_ASN1_T61_STRING ) | \ + ( 1u << MBEDTLS_ASN1_IA5_STRING ) | \ + ( 1u << MBEDTLS_ASN1_UNIVERSAL_STRING ) | \ + ( 1u << MBEDTLS_ASN1_PRINTABLE_STRING ) | \ + ( 1u << MBEDTLS_ASN1_BIT_STRING ) ) ) != 0 ) ) + +/* + * Bit masks for each of the components of an ASN.1 tag as specified in + * ITU X.690 (08/2015), section 8.1 "General rules for encoding", + * paragraph 8.1.2.2: + * + * Bit 8 7 6 5 1 + * +-------+-----+------------+ + * | Class | P/C | Tag number | + * +-------+-----+------------+ + */ +#define MBEDTLS_ASN1_TAG_CLASS_MASK 0xC0 +#define MBEDTLS_ASN1_TAG_PC_MASK 0x20 +#define MBEDTLS_ASN1_TAG_VALUE_MASK 0x1F + +/* \} name */ +/* \} addtogroup asn1_module */ + +/** Returns the size of the binary string, without the trailing \\0 */ +#define MBEDTLS_OID_SIZE(x) (sizeof(x) - 1) + +/** + * Compares an mbedtls_asn1_buf structure to a reference OID. + * + * Only works for 'defined' oid_str values (MBEDTLS_OID_HMAC_SHA1), you cannot use a + * 'unsigned char *oid' here! + */ +#define MBEDTLS_OID_CMP(oid_str, oid_buf) \ + ( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf)->len ) || \ + memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) != 0 ) + +#define MBEDTLS_OID_CMP_RAW(oid_str, oid_buf, oid_buf_len) \ + ( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf_len) ) || \ + memcmp( (oid_str), (oid_buf), (oid_buf_len) ) != 0 ) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name Functions to parse ASN.1 data structures + * \{ + */ + +/** + * Type-length-value structure that allows for ASN1 using DER. + */ +typedef struct mbedtls_asn1_buf +{ + int MBEDTLS_PRIVATE(tag); /**< ASN1 type, e.g. MBEDTLS_ASN1_UTF8_STRING. */ + size_t MBEDTLS_PRIVATE(len); /**< ASN1 length, in octets. */ + unsigned char *MBEDTLS_PRIVATE(p); /**< ASN1 data, e.g. in ASCII. */ +} +mbedtls_asn1_buf; + +/** + * Container for ASN1 bit strings. + */ +typedef struct mbedtls_asn1_bitstring +{ + size_t MBEDTLS_PRIVATE(len); /**< ASN1 length, in octets. */ + unsigned char MBEDTLS_PRIVATE(unused_bits); /**< Number of unused bits at the end of the string */ + unsigned char *MBEDTLS_PRIVATE(p); /**< Raw ASN1 data for the bit string */ +} +mbedtls_asn1_bitstring; + +/** + * Container for a sequence of ASN.1 items + */ +typedef struct mbedtls_asn1_sequence +{ + mbedtls_asn1_buf MBEDTLS_PRIVATE(buf); /**< Buffer containing the given ASN.1 item. */ + struct mbedtls_asn1_sequence *MBEDTLS_PRIVATE(next); /**< The next entry in the sequence. */ +} +mbedtls_asn1_sequence; + +/** + * Container for a sequence or list of 'named' ASN.1 data items + */ +typedef struct mbedtls_asn1_named_data +{ + mbedtls_asn1_buf MBEDTLS_PRIVATE(oid); /**< The object identifier. */ + mbedtls_asn1_buf MBEDTLS_PRIVATE(val); /**< The named value. */ + struct mbedtls_asn1_named_data *MBEDTLS_PRIVATE(next); /**< The next entry in the sequence. */ + unsigned char MBEDTLS_PRIVATE(next_merged); /**< Merge next item into the current one? */ +} +mbedtls_asn1_named_data; + +/** + * \brief Get the length of an ASN.1 element. + * Updates the pointer to immediately behind the length. + * + * \param p On entry, \c *p points to the first byte of the length, + * i.e. immediately after the tag. + * On successful completion, \c *p points to the first byte + * after the length, i.e. the first byte of the content. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On successful completion, \c *len contains the length + * read from the ASN.1 input. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element + * would end beyond \p end. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparseable. + */ +int mbedtls_asn1_get_len( unsigned char **p, + const unsigned char *end, + size_t *len ); + +/** + * \brief Get the tag and length of the element. + * Check for the requested tag. + * Updates the pointer to immediately behind the tag and length. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * after the length, i.e. the first byte of the content. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On successful completion, \c *len contains the length + * read from the ASN.1 input. + * \param tag The expected tag. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the data does not start + * with the requested tag. + * \return #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element + * would end beyond \p end. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparseable. + */ +int mbedtls_asn1_get_tag( unsigned char **p, + const unsigned char *end, + size_t *len, int tag ); + +/** + * \brief Retrieve a boolean ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value (\c 0 or \c 1). + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BOOLEAN. + */ +int mbedtls_asn1_get_bool( unsigned char **p, + const unsigned char *end, + int *val ); + +/** + * \brief Retrieve an integer ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value. + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + */ +int mbedtls_asn1_get_int( unsigned char **p, + const unsigned char *end, + int *val ); + +/** + * \brief Retrieve an enumerated ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value. + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 ENUMERATED. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + */ +int mbedtls_asn1_get_enum( unsigned char **p, + const unsigned char *end, + int *val ); + +/** + * \brief Retrieve a bitstring ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p is equal to \p end. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param bs On success, ::mbedtls_asn1_bitstring information about + * the parsed value. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input contains + * extra data after a valid BIT STRING. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BIT STRING. + */ +int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end, + mbedtls_asn1_bitstring *bs ); + +/** + * \brief Retrieve a bitstring ASN.1 tag without unused bits and its + * value. + * Updates the pointer to the beginning of the bit/octet string. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * of the content of the BIT STRING. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On success, \c *len is the length of the content in bytes. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_INVALID_DATA if the input starts with + * a valid BIT STRING with a nonzero number of unused bits. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BIT STRING. + */ +int mbedtls_asn1_get_bitstring_null( unsigned char **p, + const unsigned char *end, + size_t *len ); + +/** + * \brief Parses and splits an ASN.1 "SEQUENCE OF ". + * Updates the pointer to immediately behind the full sequence tag. + * + * This function allocates memory for the sequence elements. You can free + * the allocated memory with mbedtls_asn1_sequence_free(). + * + * \note On error, this function may return a partial list in \p cur. + * You must set `cur->next = NULL` before calling this function! + * Otherwise it is impossible to distinguish a previously non-null + * pointer from a pointer to an object allocated by this function. + * + * \note If the sequence is empty, this function does not modify + * \c *cur. If the sequence is valid and non-empty, this + * function sets `cur->buf.tag` to \p tag. This allows + * callers to distinguish between an empty sequence and + * a one-element sequence. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p is equal to \p end. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param cur A ::mbedtls_asn1_sequence which this function fills. + * When this function returns, \c *cur is the head of a linked + * list. Each node in this list is allocated with + * mbedtls_calloc() apart from \p cur itself, and should + * therefore be freed with mbedtls_free(). + * The list describes the content of the sequence. + * The head of the list (i.e. \c *cur itself) describes the + * first element, `*cur->next` describes the second element, etc. + * For each element, `buf.tag == tag`, `buf.len` is the length + * of the content of the content of the element, and `buf.p` + * points to the first byte of the content (i.e. immediately + * past the length of the element). + * Note that list elements may be allocated even on error. + * \param tag Each element of the sequence must have this tag. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input contains + * extra data after a valid SEQUENCE OF \p tag. + * \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the input starts with + * an ASN.1 SEQUENCE in which an element has a tag that + * is different from \p tag. + * \return #MBEDTLS_ERR_ASN1_ALLOC_FAILED if a memory allocation failed. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 SEQUENCE. + */ +int mbedtls_asn1_get_sequence_of( unsigned char **p, + const unsigned char *end, + mbedtls_asn1_sequence *cur, + int tag ); +/** + * \brief Free a heap-allocated linked list presentation of + * an ASN.1 sequence, including the first element. + * + * There are two common ways to manage the memory used for the representation + * of a parsed ASN.1 sequence: + * - Allocate a head node `mbedtls_asn1_sequence *head` with mbedtls_calloc(). + * Pass this node as the `cur` argument to mbedtls_asn1_get_sequence_of(). + * When you have finished processing the sequence, + * call mbedtls_asn1_sequence_free() on `head`. + * - Allocate a head node `mbedtls_asn1_sequence *head` in any manner, + * for example on the stack. Make sure that `head->next == NULL`. + * Pass `head` as the `cur` argument to mbedtls_asn1_get_sequence_of(). + * When you have finished processing the sequence, + * call mbedtls_asn1_sequence_free() on `head->cur`, + * then free `head` itself in the appropriate manner. + * + * \param seq The address of the first sequence component. This may + * be \c NULL, in which case this functions returns + * immediately. + */ +void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq ); + +/** + * \brief Traverse an ASN.1 SEQUENCE container and + * call a callback for each entry. + * + * This function checks that the input is a SEQUENCE of elements that + * each have a "must" tag, and calls a callback function on the elements + * that have a "may" tag. + * + * For example, to validate that the input is a SEQUENCE of `tag1` and call + * `cb` on each element, use + * ``` + * mbedtls_asn1_traverse_sequence_of(&p, end, 0xff, tag1, 0, 0, cb, ctx); + * ``` + * + * To validate that the input is a SEQUENCE of ANY and call `cb` on + * each element, use + * ``` + * mbedtls_asn1_traverse_sequence_of(&p, end, 0, 0, 0, 0, cb, ctx); + * ``` + * + * To validate that the input is a SEQUENCE of CHOICE {NULL, OCTET STRING} + * and call `cb` on each element that is an OCTET STRING, use + * ``` + * mbedtls_asn1_traverse_sequence_of(&p, end, 0xfe, 0x04, 0xff, 0x04, cb, ctx); + * ``` + * + * The callback is called on the elements with a "may" tag from left to + * right. If the input is not a valid SEQUENCE of elements with a "must" tag, + * the callback is called on the elements up to the leftmost point where + * the input is invalid. + * + * \warning This function is still experimental and may change + * at any time. + * + * \param p The address of the pointer to the beginning of + * the ASN.1 SEQUENCE header. This is updated to + * point to the end of the ASN.1 SEQUENCE container + * on a successful invocation. + * \param end The end of the ASN.1 SEQUENCE container. + * \param tag_must_mask A mask to be applied to the ASN.1 tags found within + * the SEQUENCE before comparing to \p tag_must_value. + * \param tag_must_val The required value of each ASN.1 tag found in the + * SEQUENCE, after masking with \p tag_must_mask. + * Mismatching tags lead to an error. + * For example, a value of \c 0 for both \p tag_must_mask + * and \p tag_must_val means that every tag is allowed, + * while a value of \c 0xFF for \p tag_must_mask means + * that \p tag_must_val is the only allowed tag. + * \param tag_may_mask A mask to be applied to the ASN.1 tags found within + * the SEQUENCE before comparing to \p tag_may_value. + * \param tag_may_val The desired value of each ASN.1 tag found in the + * SEQUENCE, after masking with \p tag_may_mask. + * Mismatching tags will be silently ignored. + * For example, a value of \c 0 for \p tag_may_mask and + * \p tag_may_val means that any tag will be considered, + * while a value of \c 0xFF for \p tag_may_mask means + * that all tags with value different from \p tag_may_val + * will be ignored. + * \param cb The callback to trigger for each component + * in the ASN.1 SEQUENCE that matches \p tag_may_val. + * The callback function is called with the following + * parameters: + * - \p ctx. + * - The tag of the current element. + * - A pointer to the start of the current element's + * content inside the input. + * - The length of the content of the current element. + * If the callback returns a non-zero value, + * the function stops immediately, + * forwarding the callback's return value. + * \param ctx The context to be passed to the callback \p cb. + * + * \return \c 0 if successful the entire ASN.1 SEQUENCE + * was traversed without parsing or callback errors. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input + * contains extra data after a valid SEQUENCE + * of elements with an accepted tag. + * \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the input starts + * with an ASN.1 SEQUENCE in which an element has a tag + * that is not accepted. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 SEQUENCE. + * \return A non-zero error code forwarded from the callback + * \p cb in case the latter returns a non-zero value. + */ +int mbedtls_asn1_traverse_sequence_of( + unsigned char **p, + const unsigned char *end, + unsigned char tag_must_mask, unsigned char tag_must_val, + unsigned char tag_may_mask, unsigned char tag_may_val, + int (*cb)( void *ctx, int tag, + unsigned char* start, size_t len ), + void *ctx ); + +#if defined(MBEDTLS_BIGNUM_C) +/** + * \brief Retrieve an integer ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param X On success, the parsed value. + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + * \return An MPI error code if the parsed value is too large. + */ +int mbedtls_asn1_get_mpi( unsigned char **p, + const unsigned char *end, + mbedtls_mpi *X ); +#endif /* MBEDTLS_BIGNUM_C */ + +/** + * \brief Retrieve an AlgorithmIdentifier ASN.1 sequence. + * Updates the pointer to immediately behind the full + * AlgorithmIdentifier. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the AlgorithmIdentifier element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param alg The buffer to receive the OID. + * \param params The buffer to receive the parameters. + * This is zeroized if there are no parameters. + * + * \return 0 if successful or a specific ASN.1 or MPI error code. + */ +int mbedtls_asn1_get_alg( unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params ); + +/** + * \brief Retrieve an AlgorithmIdentifier ASN.1 sequence with NULL or no + * params. + * Updates the pointer to immediately behind the full + * AlgorithmIdentifier. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the AlgorithmIdentifier element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param alg The buffer to receive the OID. + * + * \return 0 if successful or a specific ASN.1 or MPI error code. + */ +int mbedtls_asn1_get_alg_null( unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg ); + +/** + * \brief Find a specific named_data entry in a sequence or list based on + * the OID. + * + * \param list The list to seek through + * \param oid The OID to look for + * \param len Size of the OID + * + * \return NULL if not found, or a pointer to the existing entry. + */ +const mbedtls_asn1_named_data *mbedtls_asn1_find_named_data( const mbedtls_asn1_named_data *list, + const char *oid, size_t len ); + +/** + * \brief Free a mbedtls_asn1_named_data entry + * + * \param entry The named data entry to free. + * This function calls mbedtls_free() on + * `entry->oid.p` and `entry->val.p`. + */ +void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *entry ); + +/** + * \brief Free all entries in a mbedtls_asn1_named_data list. + * + * \param head Pointer to the head of the list of named data entries to free. + * This function calls mbedtls_asn1_free_named_data() and + * mbedtls_free() on each list element and + * sets \c *head to \c NULL. + */ +void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head ); + +#ifdef __cplusplus +} +#endif + +#endif /* asn1.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/bignum.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/bignum.h new file mode 100644 index 0000000..5187d86 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/bignum.h @@ -0,0 +1,1021 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_BIGNUM_H +#define MBEDTLS_BIGNUM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +#if defined(MBEDTLS_FS_IO) +#include +#endif + +#define MBEDTLS_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */ +#define MBEDTLS_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */ +#define MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */ +#define MBEDTLS_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */ +#define MBEDTLS_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */ +#define MBEDTLS_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */ +#define MBEDTLS_ERR_MPI_ALLOC_FAILED -0x0010 /**< Memory allocation failed. */ + +#define MBEDTLS_MPI_CHK(f) \ + do \ + { \ + if( ( ret = (f) ) != 0 ) \ + goto cleanup; \ + } while( 0 ) + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define MBEDTLS_MPI_MAX_LIMBS 10000 + +#if !defined(MBEDTLS_MPI_WINDOW_SIZE) +/* + * Maximum window size used for modular exponentiation. Default: 6 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 ** MBEDTLS_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define MBEDTLS_MPI_WINDOW_SIZE 6 /**< Maximum window size used. */ +#endif /* !MBEDTLS_MPI_WINDOW_SIZE */ + +#if !defined(MBEDTLS_MPI_MAX_SIZE) +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can temporarily result in larger MPIs. So the number + * of limbs required (MBEDTLS_MPI_MAX_LIMBS) is higher. + */ +#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */ +#endif /* !MBEDTLS_MPI_MAX_SIZE */ + +#define MBEDTLS_MPI_MAX_BITS ( 8 * MBEDTLS_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mbedtls_mpi_read_file() and writing to files with + * mbedtls_mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of MBEDTLS_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * MBEDTLS_MPI_RW_BUFFER_SIZE = ceil(MBEDTLS_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define MBEDTLS_MPI_MAX_BITS_SCALE100 ( 100 * MBEDTLS_MPI_MAX_BITS ) +#define MBEDTLS_LN_2_DIV_LN_10_SCALE100 332 +#define MBEDTLS_MPI_RW_BUFFER_SIZE ( ((MBEDTLS_MPI_MAX_BITS_SCALE100 + MBEDTLS_LN_2_DIV_LN_10_SCALE100 - 1) / MBEDTLS_LN_2_DIV_LN_10_SCALE100) + 10 + 6 ) + +/* + * Define the base integer type, architecture-wise. + * + * 32 or 64-bit integer types can be forced regardless of the underlying + * architecture by defining MBEDTLS_HAVE_INT32 or MBEDTLS_HAVE_INT64 + * respectively and undefining MBEDTLS_HAVE_ASM. + * + * Double-width integers (e.g. 128-bit in 64-bit architectures) can be + * disabled by defining MBEDTLS_NO_UDBL_DIVISION. + */ +#if !defined(MBEDTLS_HAVE_INT32) + #if defined(_MSC_VER) && defined(_M_AMD64) + /* Always choose 64-bit when using MSC */ + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* !MBEDTLS_HAVE_INT64 */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #elif defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + ( defined(__sparc__) && defined(__arch64__) ) || \ + defined(__s390x__) || defined(__mips64) || \ + defined(__aarch64__) ) + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* MBEDTLS_HAVE_INT64 */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) + /* mbedtls_t_udbl defined as 128-bit unsigned int */ + typedef unsigned int mbedtls_t_udbl __attribute__((mode(TI))); + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ + #elif defined(__ARMCC_VERSION) && defined(__aarch64__) + /* + * __ARMCC_VERSION is defined for both armcc and armclang and + * __aarch64__ is only defined by armclang when compiling 64-bit code + */ + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* !MBEDTLS_HAVE_INT64 */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) + /* mbedtls_t_udbl defined as 128-bit unsigned int */ + typedef __uint128_t mbedtls_t_udbl; + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ + #elif defined(MBEDTLS_HAVE_INT64) + /* Force 64-bit integers with unknown compiler */ + typedef int64_t mbedtls_mpi_sint; + typedef uint64_t mbedtls_mpi_uint; + #endif +#endif /* !MBEDTLS_HAVE_INT32 */ + +#if !defined(MBEDTLS_HAVE_INT64) + /* Default to 32-bit compilation */ + #if !defined(MBEDTLS_HAVE_INT32) + #define MBEDTLS_HAVE_INT32 + #endif /* !MBEDTLS_HAVE_INT32 */ + typedef int32_t mbedtls_mpi_sint; + typedef uint32_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) + typedef uint64_t mbedtls_t_udbl; + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ +#endif /* !MBEDTLS_HAVE_INT64 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MPI structure + */ +typedef struct mbedtls_mpi +{ + int MBEDTLS_PRIVATE(s); /*!< Sign: -1 if the mpi is negative, 1 otherwise */ + size_t MBEDTLS_PRIVATE(n); /*!< total # of limbs */ + mbedtls_mpi_uint *MBEDTLS_PRIVATE(p); /*!< pointer to limbs */ +} +mbedtls_mpi; + +/** + * \brief Initialize an MPI context. + * + * This makes the MPI ready to be set or freed, + * but does not define a value for the MPI. + * + * \param X The MPI context to initialize. This must not be \c NULL. + */ +void mbedtls_mpi_init( mbedtls_mpi *X ); + +/** + * \brief This function frees the components of an MPI context. + * + * \param X The MPI context to be cleared. This may be \c NULL, + * in which case this function is a no-op. If it is + * not \c NULL, it must point to an initialized MPI. + */ +void mbedtls_mpi_free( mbedtls_mpi *X ); + +/** + * \brief Enlarge an MPI to the specified number of limbs. + * + * \note This function does nothing if the MPI is + * already large enough. + * + * \param X The MPI to grow. It must be initialized. + * \param nblimbs The target number of limbs. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs ); + +/** + * \brief This function resizes an MPI downwards, keeping at least the + * specified number of limbs. + * + * If \c X is smaller than \c nblimbs, it is resized up + * instead. + * + * \param X The MPI to shrink. This must point to an initialized MPI. + * \param nblimbs The minimum number of limbs to keep. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + * (this can only happen when resizing up). + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs ); + +/** + * \brief Make a copy of an MPI. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param Y The source MPI. This must point to an initialized MPI. + * + * \note The limb-buffer in the destination MPI is enlarged + * if necessary to hold the value in the source MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y ); + +/** + * \brief Swap the contents of two MPIs. + * + * \param X The first MPI. It must be initialized. + * \param Y The second MPI. It must be initialized. + */ +void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y ); + +/** + * \brief Perform a safe conditional copy of MPI which doesn't + * reveal whether the condition was true or not. + * + * \param X The MPI to conditionally assign to. This must point + * to an initialized MPI. + * \param Y The MPI to be assigned from. This must point to an + * initialized MPI. + * \param assign The condition deciding whether to perform the + * assignment or not. Possible values: + * * \c 1: Perform the assignment `X = Y`. + * * \c 0: Keep the original value of \p X. + * + * \note This function is equivalent to + * `if( assign ) mbedtls_mpi_copy( X, Y );` + * except that it avoids leaking any information about whether + * the assignment was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign ); + +/** + * \brief Perform a safe conditional swap which doesn't + * reveal whether the condition was true or not. + * + * \param X The first MPI. This must be initialized. + * \param Y The second MPI. This must be initialized. + * \param assign The condition deciding whether to perform + * the swap or not. Possible values: + * * \c 1: Swap the values of \p X and \p Y. + * * \c 0: Keep the original values of \p X and \p Y. + * + * \note This function is equivalent to + * if( assign ) mbedtls_mpi_swap( X, Y ); + * except that it avoids leaking any information about whether + * the assignment was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + * + */ +int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char assign ); + +/** + * \brief Store integer value in MPI. + * + * \param X The MPI to set. This must be initialized. + * \param z The value to use. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z ); + +/** + * \brief Get a specific bit from an MPI. + * + * \param X The MPI to query. This must be initialized. + * \param pos Zero-based index of the bit to query. + * + * \return \c 0 or \c 1 on success, depending on whether bit \c pos + * of \c X is unset or set. + * \return A negative error code on failure. + */ +int mbedtls_mpi_get_bit( const mbedtls_mpi *X, size_t pos ); + +/** + * \brief Modify a specific bit in an MPI. + * + * \note This function will grow the target MPI if necessary to set a + * bit to \c 1 in a not yet existing limb. It will not grow if + * the bit should be set to \c 0. + * + * \param X The MPI to modify. This must be initialized. + * \param pos Zero-based index of the bit to modify. + * \param val The desired value of bit \c pos: \c 0 or \c 1. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_set_bit( mbedtls_mpi *X, size_t pos, unsigned char val ); + +/** + * \brief Return the number of bits of value \c 0 before the + * least significant bit of value \c 1. + * + * \note This is the same as the zero-based index of + * the least significant bit of value \c 1. + * + * \param X The MPI to query. + * + * \return The number of bits of value \c 0 before the least significant + * bit of value \c 1 in \p X. + */ +size_t mbedtls_mpi_lsb( const mbedtls_mpi *X ); + +/** + * \brief Return the number of bits up to and including the most + * significant bit of value \c 1. + * + * * \note This is same as the one-based index of the most + * significant bit of value \c 1. + * + * \param X The MPI to query. This must point to an initialized MPI. + * + * \return The number of bits up to and including the most + * significant bit of value \c 1. + */ +size_t mbedtls_mpi_bitlen( const mbedtls_mpi *X ); + +/** + * \brief Return the total size of an MPI value in bytes. + * + * \param X The MPI to use. This must point to an initialized MPI. + * + * \note The value returned by this function may be less than + * the number of bytes used to store \p X internally. + * This happens if and only if there are trailing bytes + * of value zero. + * + * \return The least number of bytes capable of storing + * the absolute value of \p X. + */ +size_t mbedtls_mpi_size( const mbedtls_mpi *X ); + +/** + * \brief Import an MPI from an ASCII string. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param radix The numeric base of the input string. + * \param s Null-terminated string buffer. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_mpi_read_string( mbedtls_mpi *X, int radix, const char *s ); + +/** + * \brief Export an MPI to an ASCII string. + * + * \param X The source MPI. This must point to an initialized MPI. + * \param radix The numeric base of the output string. + * \param buf The buffer to write the string to. This must be writable + * buffer of length \p buflen Bytes. + * \param buflen The available size in Bytes of \p buf. + * \param olen The address at which to store the length of the string + * written, including the final \c NULL byte. This must + * not be \c NULL. + * + * \note You can call this function with `buflen == 0` to obtain the + * minimum required buffer size in `*olen`. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if the target buffer \p buf + * is too small to hold the value of \p X in the desired base. + * In this case, `*olen` is nonetheless updated to contain the + * size of \p buf required for a successful call. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix, + char *buf, size_t buflen, size_t *olen ); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief Read an MPI from a line in an opened file. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param radix The numeric base of the string representation used + * in the source line. + * \param fin The input file handle to use. This must not be \c NULL. + * + * \note On success, this function advances the file stream + * to the end of the current line or to EOF. + * + * The function returns \c 0 on an empty line. + * + * Leading whitespaces are ignored, as is a + * '0x' prefix for radix \c 16. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if the file read buffer + * is too small. + * \return Another negative error code on failure. + */ +int mbedtls_mpi_read_file( mbedtls_mpi *X, int radix, FILE *fin ); + +/** + * \brief Export an MPI into an opened file. + * + * \param p A string prefix to emit prior to the MPI data. + * For example, this might be a label, or "0x" when + * printing in base \c 16. This may be \c NULL if no prefix + * is needed. + * \param X The source MPI. This must point to an initialized MPI. + * \param radix The numeric base to be used in the emitted string. + * \param fout The output file handle. This may be \c NULL, in which case + * the output is written to \c stdout. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_mpi_write_file( const char *p, const mbedtls_mpi *X, + int radix, FILE *fout ); +#endif /* MBEDTLS_FS_IO */ + +/** + * \brief Import an MPI from unsigned big endian binary data. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param buf The input buffer. This must be a readable buffer of length + * \p buflen Bytes. + * \param buflen The length of the input buffer \p p in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_read_binary( mbedtls_mpi *X, const unsigned char *buf, + size_t buflen ); + +/** + * \brief Import X from unsigned binary data, little endian + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param buf The input buffer. This must be a readable buffer of length + * \p buflen Bytes. + * \param buflen The length of the input buffer \p p in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_read_binary_le( mbedtls_mpi *X, + const unsigned char *buf, size_t buflen ); + +/** + * \brief Export X into unsigned binary data, big endian. + * Always fills the whole buffer, which will start with zeros + * if the number is smaller. + * + * \param X The source MPI. This must point to an initialized MPI. + * \param buf The output buffer. This must be a writable buffer of length + * \p buflen Bytes. + * \param buflen The size of the output buffer \p buf in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p buf isn't + * large enough to hold the value of \p X. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_write_binary( const mbedtls_mpi *X, unsigned char *buf, + size_t buflen ); + +/** + * \brief Export X into unsigned binary data, little endian. + * Always fills the whole buffer, which will end with zeros + * if the number is smaller. + * + * \param X The source MPI. This must point to an initialized MPI. + * \param buf The output buffer. This must be a writable buffer of length + * \p buflen Bytes. + * \param buflen The size of the output buffer \p buf in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p buf isn't + * large enough to hold the value of \p X. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_write_binary_le( const mbedtls_mpi *X, + unsigned char *buf, size_t buflen ); + +/** + * \brief Perform a left-shift on an MPI: X <<= count + * + * \param X The MPI to shift. This must point to an initialized MPI. + * \param count The number of bits to shift by. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_shift_l( mbedtls_mpi *X, size_t count ); + +/** + * \brief Perform a right-shift on an MPI: X >>= count + * + * \param X The MPI to shift. This must point to an initialized MPI. + * \param count The number of bits to shift by. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_shift_r( mbedtls_mpi *X, size_t count ); + +/** + * \brief Compare the absolute values of two MPIs. + * + * \param X The left-hand MPI. This must point to an initialized MPI. + * \param Y The right-hand MPI. This must point to an initialized MPI. + * + * \return \c 1 if `|X|` is greater than `|Y|`. + * \return \c -1 if `|X|` is lesser than `|Y|`. + * \return \c 0 if `|X|` is equal to `|Y|`. + */ +int mbedtls_mpi_cmp_abs( const mbedtls_mpi *X, const mbedtls_mpi *Y ); + +/** + * \brief Compare two MPIs. + * + * \param X The left-hand MPI. This must point to an initialized MPI. + * \param Y The right-hand MPI. This must point to an initialized MPI. + * + * \return \c 1 if \p X is greater than \p Y. + * \return \c -1 if \p X is lesser than \p Y. + * \return \c 0 if \p X is equal to \p Y. + */ +int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y ); + +/** + * \brief Check if an MPI is less than the other in constant time. + * + * \param X The left-hand MPI. This must point to an initialized MPI + * with the same allocated length as Y. + * \param Y The right-hand MPI. This must point to an initialized MPI + * with the same allocated length as X. + * \param ret The result of the comparison: + * \c 1 if \p X is less than \p Y. + * \c 0 if \p X is greater than or equal to \p Y. + * + * \return 0 on success. + * \return MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the allocated length of + * the two input MPIs is not the same. + */ +int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X, const mbedtls_mpi *Y, + unsigned *ret ); + +/** + * \brief Compare an MPI with an integer. + * + * \param X The left-hand MPI. This must point to an initialized MPI. + * \param z The integer value to compare \p X to. + * + * \return \c 1 if \p X is greater than \p z. + * \return \c -1 if \p X is lesser than \p z. + * \return \c 0 if \p X is equal to \p z. + */ +int mbedtls_mpi_cmp_int( const mbedtls_mpi *X, mbedtls_mpi_sint z ); + +/** + * \brief Perform an unsigned addition of MPIs: X = |A| + |B| + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first summand. This must point to an initialized MPI. + * \param B The second summand. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_add_abs( mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Perform an unsigned subtraction of MPIs: X = |A| - |B| + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The minuend. This must point to an initialized MPI. + * \param B The subtrahend. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p B is greater than \p A. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Perform a signed addition of MPIs: X = A + B + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first summand. This must point to an initialized MPI. + * \param B The second summand. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Perform a signed subtraction of MPIs: X = A - B + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The minuend. This must point to an initialized MPI. + * \param B The subtrahend. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Perform a signed addition of an MPI and an integer: X = A + b + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first summand. This must point to an initialized MPI. + * \param b The second summand. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A, + mbedtls_mpi_sint b ); + +/** + * \brief Perform a signed subtraction of an MPI and an integer: + * X = A - b + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The minuend. This must point to an initialized MPI. + * \param b The subtrahend. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, + mbedtls_mpi_sint b ); + +/** + * \brief Perform a multiplication of two MPIs: X = A * B + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first factor. This must point to an initialized MPI. + * \param B The second factor. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Perform a multiplication of an MPI with an unsigned integer: + * X = A * b + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first factor. This must point to an initialized MPI. + * \param b The second factor. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A, + mbedtls_mpi_uint b ); + +/** + * \brief Perform a division with remainder of two MPIs: + * A = Q * B + R + * + * \param Q The destination MPI for the quotient. + * This may be \c NULL if the value of the + * quotient is not needed. + * \param R The destination MPI for the remainder value. + * This may be \c NULL if the value of the + * remainder is not needed. + * \param A The dividend. This must point to an initialized MPi. + * \param B The divisor. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p B equals zero. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_div_mpi( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Perform a division with remainder of an MPI by an integer: + * A = Q * b + R + * + * \param Q The destination MPI for the quotient. + * This may be \c NULL if the value of the + * quotient is not needed. + * \param R The destination MPI for the remainder value. + * This may be \c NULL if the value of the + * remainder is not needed. + * \param A The dividend. This must point to an initialized MPi. + * \param b The divisor. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p b equals zero. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_div_int( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, + mbedtls_mpi_sint b ); + +/** + * \brief Perform a modular reduction. R = A mod B + * + * \param R The destination MPI for the residue value. + * This must point to an initialized MPI. + * \param A The MPI to compute the residue of. + * This must point to an initialized MPI. + * \param B The base of the modular reduction. + * This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p B equals zero. + * \return #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p B is negative. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_mod_mpi( mbedtls_mpi *R, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Perform a modular reduction with respect to an integer. + * r = A mod b + * + * \param r The address at which to store the residue. + * This must not be \c NULL. + * \param A The MPI to compute the residue of. + * This must point to an initialized MPi. + * \param b The integer base of the modular reduction. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p b equals zero. + * \return #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p b is negative. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, + mbedtls_mpi_sint b ); + +/** + * \brief Perform a sliding-window exponentiation: X = A^E mod N + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The base of the exponentiation. + * This must point to an initialized MPI. + * \param E The exponent MPI. This must point to an initialized MPI. + * \param N The base for the modular reduction. This must point to an + * initialized MPI. + * \param _RR A helper MPI depending solely on \p N which can be used to + * speed-up multiple modular exponentiations for the same value + * of \p N. This may be \c NULL. If it is not \c NULL, it must + * point to an initialized MPI. If it hasn't been used after + * the call to mbedtls_mpi_init(), this function will compute + * the helper value and store it in \p _RR for reuse on + * subsequent calls to this function. Otherwise, the function + * will assume that \p _RR holds the helper value set by a + * previous call to mbedtls_mpi_exp_mod(), and reuse it. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \c N is negative or + * even, or if \c E is negative. + * \return Another negative error code on different kinds of failures. + * + */ +int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *E, const mbedtls_mpi *N, + mbedtls_mpi *_RR ); + +/** + * \brief Fill an MPI with a number of random bytes. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param size The number of random bytes to generate. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on failure. + * + * \note The bytes obtained from the RNG are interpreted + * as a big-endian representation of an MPI; this can + * be relevant in applications like deterministic ECDSA. + */ +int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** Generate a random number uniformly in a range. + * + * This function generates a random number between \p min inclusive and + * \p N exclusive. + * + * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA) + * when the RNG is a suitably parametrized instance of HMAC_DRBG + * and \p min is \c 1. + * + * \note There are `N - min` possible outputs. The lower bound + * \p min can be reached, but the upper bound \p N cannot. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param min The minimum value to return. + * It must be nonnegative. + * \param N The upper bound of the range, exclusive. + * In other words, this is one plus the maximum value to return. + * \p N must be strictly larger than \p min. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p min or \p N is invalid + * or if they are incompatible. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was + * unable to find a suitable value within a limited number + * of attempts. This has a negligible probability if \p N + * is significantly larger than \p min, which is the case + * for all usual cryptographic applications. + * \return Another negative error code on failure. + */ +int mbedtls_mpi_random( mbedtls_mpi *X, + mbedtls_mpi_sint min, + const mbedtls_mpi *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Compute the greatest common divisor: G = gcd(A, B) + * + * \param G The destination MPI. This must point to an initialized MPI. + * \param A The first operand. This must point to an initialized MPI. + * \param B The second operand. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_gcd( mbedtls_mpi *G, const mbedtls_mpi *A, + const mbedtls_mpi *B ); + +/** + * \brief Compute the modular inverse: X = A^-1 mod N + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The MPI to calculate the modular inverse of. This must point + * to an initialized MPI. + * \param N The base of the modular inversion. This must point to an + * initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p N is less than + * or equal to one. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p has no modular inverse + * with respect to \p N. + */ +int mbedtls_mpi_inv_mod( mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *N ); + +/** + * \brief Miller-Rabin primality test. + * + * \warning If \p X is potentially generated by an adversary, for example + * when validating cryptographic parameters that you didn't + * generate yourself and that are supposed to be prime, then + * \p rounds should be at least the half of the security + * strength of the cryptographic algorithm. On the other hand, + * if \p X is chosen uniformly or non-adversially (as is the + * case when mbedtls_mpi_gen_prime calls this function), then + * \p rounds can be much lower. + * + * \param X The MPI to check for primality. + * This must point to an initialized MPI. + * \param rounds The number of bases to perform the Miller-Rabin primality + * test for. The probability of returning 0 on a composite is + * at most 2-2*\p rounds. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * This may be \c NULL if \p f_rng doesn't use + * a context parameter. + * + * \return \c 0 if successful, i.e. \p X is probably prime. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p X is not prime. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_is_prime_ext( const mbedtls_mpi *X, int rounds, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); +/** + * \brief Flags for mbedtls_mpi_gen_prime() + * + * Each of these flags is a constraint on the result X returned by + * mbedtls_mpi_gen_prime(). + */ +typedef enum { + MBEDTLS_MPI_GEN_PRIME_FLAG_DH = 0x0001, /**< (X-1)/2 is prime too */ + MBEDTLS_MPI_GEN_PRIME_FLAG_LOW_ERR = 0x0002, /**< lower error rate from 2-80 to 2-128 */ +} mbedtls_mpi_gen_prime_flag_t; + +/** + * \brief Generate a prime number. + * + * \param X The destination MPI to store the generated prime in. + * This must point to an initialized MPi. + * \param nbits The required size of the destination MPI in bits. + * This must be between \c 3 and #MBEDTLS_MPI_MAX_BITS. + * \param flags A mask of flags of type #mbedtls_mpi_gen_prime_flag_t. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * This may be \c NULL if \p f_rng doesn't use + * a context parameter. + * + * \return \c 0 if successful, in which case \p X holds a + * probably prime number. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if `nbits` is not between + * \c 3 and #MBEDTLS_MPI_MAX_BITS. + */ +int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int flags, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_mpi_self_test( int verbose ); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/build_info.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/build_info.h new file mode 100644 index 0000000..23f85ba --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/build_info.h @@ -0,0 +1,83 @@ +/** + * \file build_info.h + * + * \brief Build-time configuration info + * + * Include this file if you need to depend on the + * configuration options defined in mbedtls_config.h or MBEDTLS_CONFIG_FILE + */ + /* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_BUILD_INFO_H +#define MBEDTLS_BUILD_INFO_H + +/* + * This set of compile-time defines can be used to determine the version number + * of the Mbed TLS library used. Run-time variables for the same can be found in + * version.h + */ + +/** + * The version number x.y.z is split into three parts. + * Major, Minor, Patchlevel + */ +#define MBEDTLS_VERSION_MAJOR 3 +#define MBEDTLS_VERSION_MINOR 0 +#define MBEDTLS_VERSION_PATCH 0 + +/** + * The single version number has the following structure: + * MMNNPP00 + * Major version | Minor version | Patch version + */ +#define MBEDTLS_VERSION_NUMBER 0x03000000 +#define MBEDTLS_VERSION_STRING "3.0.0" +#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 3.0.0" + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/mbedtls_config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_CONFIG_VERSION) && ( \ + MBEDTLS_CONFIG_VERSION < 0x03000000 || \ + MBEDTLS_CONFIG_VERSION > MBEDTLS_VERSION_NUMBER ) +#error "Invalid config version, defined value of MBEDTLS_CONFIG_VERSION is unsupported" +#endif + +/* Target and application specific configurations + * + * Allow user to override any previous default. + * + */ +#if defined(MBEDTLS_USER_CONFIG_FILE) +#include MBEDTLS_USER_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) +#include "mbedtls/config_psa.h" +#endif + +#include "mbedtls/check_config.h" + +#endif /* MBEDTLS_BUILD_INFO_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/check_config.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/check_config.h new file mode 100644 index 0000000..e38892d --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/check_config.h @@ -0,0 +1,838 @@ +/** + * \file check_config.h + * + * \brief Consistency checks for configuration options + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CHECK_CONFIG_H +#define MBEDTLS_CHECK_CONFIG_H + +/* + * We assume CHAR_BIT is 8 in many places. In practice, this is true on our + * target platforms, so not an issue, but let's just be extra sure. + */ +#include +#if CHAR_BIT != 8 +#error "mbed TLS requires a platform with 8-bit chars" +#endif + +#if defined(_WIN32) +#if !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_C is required on Windows" +#endif + +/* Fix the config here. Not convenient to put an #ifdef _WIN32 in mbedtls_config.h as + * it would confuse config.py. */ +#if !defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) && \ + !defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) +#define MBEDTLS_PLATFORM_SNPRINTF_ALT +#endif + +#if !defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) && \ + !defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) +#define MBEDTLS_PLATFORM_VSNPRINTF_ALT +#endif +#endif /* _WIN32 */ + +#if defined(TARGET_LIKE_MBED) && defined(MBEDTLS_NET_C) +#error "The NET module is not available for mbed OS - please use the network functions provided by Mbed OS" +#endif + +#if defined(MBEDTLS_DEPRECATED_WARNING) && \ + !defined(__GNUC__) && !defined(__clang__) +#error "MBEDTLS_DEPRECATED_WARNING only works with GCC and Clang" +#endif + +#if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_HAVE_TIME) +#error "MBEDTLS_HAVE_TIME_DATE without MBEDTLS_HAVE_TIME does not make sense" +#endif + +#if defined(MBEDTLS_AESNI_C) && !defined(MBEDTLS_HAVE_ASM) +#error "MBEDTLS_AESNI_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_CTR_DRBG_C) && !defined(MBEDTLS_AES_C) +#error "MBEDTLS_CTR_DRBG_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_DHM_C) && !defined(MBEDTLS_BIGNUM_C) +#error "MBEDTLS_DHM_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_CMAC_C) && \ + !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_DES_C) +#error "MBEDTLS_CMAC_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_NIST_KW_C) && \ + ( !defined(MBEDTLS_AES_C) || !defined(MBEDTLS_CIPHER_C) ) +#error "MBEDTLS_NIST_KW_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECDH_C) && !defined(MBEDTLS_ECP_C) +#error "MBEDTLS_ECDH_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECDSA_C) && \ + ( !defined(MBEDTLS_ECP_C) || \ + !( defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) ) || \ + !defined(MBEDTLS_ASN1_PARSE_C) || \ + !defined(MBEDTLS_ASN1_WRITE_C) ) +#error "MBEDTLS_ECDSA_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECJPAKE_C) && \ + ( !defined(MBEDTLS_ECP_C) || !defined(MBEDTLS_MD_C) ) +#error "MBEDTLS_ECJPAKE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) && \ + ( defined(MBEDTLS_USE_PSA_CRYPTO) || \ + defined(MBEDTLS_ECDH_COMPUTE_SHARED_ALT) || \ + defined(MBEDTLS_ECDH_GEN_PUBLIC_ALT) || \ + defined(MBEDTLS_ECDSA_SIGN_ALT) || \ + defined(MBEDTLS_ECDSA_VERIFY_ALT) || \ + defined(MBEDTLS_ECDSA_GENKEY_ALT) || \ + defined(MBEDTLS_ECP_INTERNAL_ALT) || \ + defined(MBEDTLS_ECP_ALT) ) +#error "MBEDTLS_ECP_RESTARTABLE defined, but it cannot coexist with an alternative or PSA-based ECP implementation" +#endif + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) && !defined(MBEDTLS_HMAC_DRBG_C) +#error "MBEDTLS_ECDSA_DETERMINISTIC defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_C) && ( !defined(MBEDTLS_BIGNUM_C) || ( \ + !defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) ) ) +#error "MBEDTLS_ECP_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_PARSE_C) && !defined(MBEDTLS_ASN1_PARSE_C) +#error "MBEDTLS_PK_PARSE_C defined, but not all prerequesites" +#endif + +#if defined(MBEDTLS_ENTROPY_C) && (!defined(MBEDTLS_SHA512_C) && \ + !defined(MBEDTLS_SHA256_C)) +#error "MBEDTLS_ENTROPY_C defined, but not all prerequisites" +#endif +#if defined(MBEDTLS_ENTROPY_C) && defined(MBEDTLS_SHA512_C) && \ + defined(MBEDTLS_CTR_DRBG_ENTROPY_LEN) && (MBEDTLS_CTR_DRBG_ENTROPY_LEN > 64) +#error "MBEDTLS_CTR_DRBG_ENTROPY_LEN value too high" +#endif +#if defined(MBEDTLS_ENTROPY_C) && \ + ( !defined(MBEDTLS_SHA512_C) || defined(MBEDTLS_ENTROPY_FORCE_SHA256) ) \ + && defined(MBEDTLS_CTR_DRBG_ENTROPY_LEN) && (MBEDTLS_CTR_DRBG_ENTROPY_LEN > 32) +#error "MBEDTLS_CTR_DRBG_ENTROPY_LEN value too high" +#endif +#if defined(MBEDTLS_ENTROPY_C) && \ + defined(MBEDTLS_ENTROPY_FORCE_SHA256) && !defined(MBEDTLS_SHA256_C) +#error "MBEDTLS_ENTROPY_FORCE_SHA256 defined, but not all prerequisites" +#endif + +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#define MBEDTLS_HAS_MEMSAN +#endif +#endif +#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) && !defined(MBEDTLS_HAS_MEMSAN) +#error "MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN requires building with MemorySanitizer" +#endif +#undef MBEDTLS_HAS_MEMSAN + +#if defined(MBEDTLS_GCM_C) && ( \ + !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_CAMELLIA_C) && !defined(MBEDTLS_ARIA_C) ) +#error "MBEDTLS_GCM_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_RANDOMIZE_JAC_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_ADD_MIXED_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_ADD_MIXED_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_DOUBLE_JAC_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NORMALIZE_JAC_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_RANDOMIZE_MXZ_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NORMALIZE_MXZ_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NO_FALLBACK defined, but no alternative implementation enabled" +#endif + +#if defined(MBEDTLS_HKDF_C) && !defined(MBEDTLS_MD_C) +#error "MBEDTLS_HKDF_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_HMAC_DRBG_C) && !defined(MBEDTLS_MD_C) +#error "MBEDTLS_HMAC_DRBG_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || !defined(MBEDTLS_ECDSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) && !defined(MBEDTLS_DHM_C) +#error "MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) && \ + !defined(MBEDTLS_ECDH_C) +#error "MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + ( !defined(MBEDTLS_DHM_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || !defined(MBEDTLS_ECDSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) && \ + ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) || \ + !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) && \ + ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) || \ + !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \ + ( !defined(MBEDTLS_ECJPAKE_C) || !defined(MBEDTLS_SHA256_C) || \ + !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) ) +#error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) && \ + !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) && \ + ( !defined(MBEDTLS_SHA256_C) && \ + !defined(MBEDTLS_SHA512_C) && \ + !defined(MBEDTLS_SHA1_C) ) +#error "!MBEDTLS_SSL_KEEP_PEER_CERTIFICATE requires MBEDTLS_SHA512_C, MBEDTLS_SHA256_C or MBEDTLS_SHA1_C" +#endif + +#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) ) +#error "MBEDTLS_MEMORY_BUFFER_ALLOC_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_MEMORY_BACKTRACE) && !defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) +#error "MBEDTLS_MEMORY_BACKTRACE defined, but not all prerequesites" +#endif + +#if defined(MBEDTLS_MEMORY_DEBUG) && !defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) +#error "MBEDTLS_MEMORY_DEBUG defined, but not all prerequesites" +#endif + +#if defined(MBEDTLS_PADLOCK_C) && !defined(MBEDTLS_HAVE_ASM) +#error "MBEDTLS_PADLOCK_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PEM_PARSE_C) && !defined(MBEDTLS_BASE64_C) +#error "MBEDTLS_PEM_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PEM_WRITE_C) && !defined(MBEDTLS_BASE64_C) +#error "MBEDTLS_PEM_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_C) && \ + ( !defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_ECP_C) ) +#error "MBEDTLS_PK_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_PARSE_C) && !defined(MBEDTLS_PK_C) +#error "MBEDTLS_PK_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_WRITE_C) && !defined(MBEDTLS_PK_C) +#error "MBEDTLS_PK_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_EXIT_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_EXIT_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_EXIT_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_EXIT_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_EXIT_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_EXIT) ||\ + defined(MBEDTLS_PLATFORM_EXIT_ALT) ) +#error "MBEDTLS_PLATFORM_EXIT_MACRO and MBEDTLS_PLATFORM_STD_EXIT/MBEDTLS_PLATFORM_EXIT_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_ALT) &&\ + ( !defined(MBEDTLS_PLATFORM_C) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_TIME_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_TIME_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_TYPE_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_TIME_TYPE_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_TIME) ||\ + defined(MBEDTLS_PLATFORM_TIME_ALT) ) +#error "MBEDTLS_PLATFORM_TIME_MACRO and MBEDTLS_PLATFORM_STD_TIME/MBEDTLS_PLATFORM_TIME_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_TYPE_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_TIME) ||\ + defined(MBEDTLS_PLATFORM_TIME_ALT) ) +#error "MBEDTLS_PLATFORM_TIME_TYPE_MACRO and MBEDTLS_PLATFORM_STD_TIME/MBEDTLS_PLATFORM_TIME_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_FPRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_FPRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_FPRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_FPRINTF) ||\ + defined(MBEDTLS_PLATFORM_FPRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_FPRINTF_MACRO and MBEDTLS_PLATFORM_STD_FPRINTF/MBEDTLS_PLATFORM_FPRINTF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) ) +#error "MBEDTLS_PLATFORM_FREE_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) &&\ + defined(MBEDTLS_PLATFORM_STD_FREE) +#error "MBEDTLS_PLATFORM_FREE_MACRO and MBEDTLS_PLATFORM_STD_FREE cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) && !defined(MBEDTLS_PLATFORM_CALLOC_MACRO) +#error "MBEDTLS_PLATFORM_CALLOC_MACRO must be defined if MBEDTLS_PLATFORM_FREE_MACRO is" +#endif + +#if defined(MBEDTLS_PLATFORM_CALLOC_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) ) +#error "MBEDTLS_PLATFORM_CALLOC_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_CALLOC_MACRO) &&\ + defined(MBEDTLS_PLATFORM_STD_CALLOC) +#error "MBEDTLS_PLATFORM_CALLOC_MACRO and MBEDTLS_PLATFORM_STD_CALLOC cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_CALLOC_MACRO) && !defined(MBEDTLS_PLATFORM_FREE_MACRO) +#error "MBEDTLS_PLATFORM_FREE_MACRO must be defined if MBEDTLS_PLATFORM_CALLOC_MACRO is" +#endif + +#if defined(MBEDTLS_PLATFORM_MEMORY) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_MEMORY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_PRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_PRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_PRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_PRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_PRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_PRINTF) ||\ + defined(MBEDTLS_PLATFORM_PRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_PRINTF_MACRO and MBEDTLS_PLATFORM_STD_PRINTF/MBEDTLS_PLATFORM_PRINTF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_SNPRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_SNPRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_SNPRINTF) ||\ + defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_SNPRINTF_MACRO and MBEDTLS_PLATFORM_STD_SNPRINTF/MBEDTLS_PLATFORM_SNPRINTF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_MEM_HDR) &&\ + !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) +#error "MBEDTLS_PLATFORM_STD_MEM_HDR defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_CALLOC) && !defined(MBEDTLS_PLATFORM_MEMORY) +#error "MBEDTLS_PLATFORM_STD_CALLOC defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_CALLOC) && !defined(MBEDTLS_PLATFORM_MEMORY) +#error "MBEDTLS_PLATFORM_STD_CALLOC defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_FREE) && !defined(MBEDTLS_PLATFORM_MEMORY) +#error "MBEDTLS_PLATFORM_STD_FREE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_EXIT) &&\ + !defined(MBEDTLS_PLATFORM_EXIT_ALT) +#error "MBEDTLS_PLATFORM_STD_EXIT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_TIME) &&\ + ( !defined(MBEDTLS_PLATFORM_TIME_ALT) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_STD_TIME defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_FPRINTF) &&\ + !defined(MBEDTLS_PLATFORM_FPRINTF_ALT) +#error "MBEDTLS_PLATFORM_STD_FPRINTF defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_PRINTF) &&\ + !defined(MBEDTLS_PLATFORM_PRINTF_ALT) +#error "MBEDTLS_PLATFORM_STD_PRINTF defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_SNPRINTF) &&\ + !defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) +#error "MBEDTLS_PLATFORM_STD_SNPRINTF defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ENTROPY_NV_SEED) &&\ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_ENTROPY_C) ) +#error "MBEDTLS_ENTROPY_NV_SEED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_ALT) &&\ + !defined(MBEDTLS_ENTROPY_NV_SEED) +#error "MBEDTLS_PLATFORM_NV_SEED_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_NV_SEED_READ) &&\ + !defined(MBEDTLS_PLATFORM_NV_SEED_ALT) +#error "MBEDTLS_PLATFORM_STD_NV_SEED_READ defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_NV_SEED_WRITE) &&\ + !defined(MBEDTLS_PLATFORM_NV_SEED_ALT) +#error "MBEDTLS_PLATFORM_STD_NV_SEED_WRITE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_READ_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_NV_SEED_READ) ||\ + defined(MBEDTLS_PLATFORM_NV_SEED_ALT) ) +#error "MBEDTLS_PLATFORM_NV_SEED_READ_MACRO and MBEDTLS_PLATFORM_STD_NV_SEED_READ cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_NV_SEED_WRITE) ||\ + defined(MBEDTLS_PLATFORM_NV_SEED_ALT) ) +#error "MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO and MBEDTLS_PLATFORM_STD_NV_SEED_WRITE cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_C) && \ + !( ( ( defined(MBEDTLS_CTR_DRBG_C) || defined(MBEDTLS_HMAC_DRBG_C) ) && \ + defined(MBEDTLS_ENTROPY_C) ) || \ + defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) ) +#error "MBEDTLS_PSA_CRYPTO_C defined, but not all prerequisites (missing RNG)" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_SPM) && !defined(MBEDTLS_PSA_CRYPTO_C) +#error "MBEDTLS_PSA_CRYPTO_SPM defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) && \ + ! ( defined(MBEDTLS_PSA_CRYPTO_C) && \ + defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) ) +#error "MBEDTLS_PSA_CRYPTO_SE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) && \ + ! defined(MBEDTLS_PSA_CRYPTO_C) +#error "MBEDTLS_PSA_CRYPTO_STORAGE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + !( defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) && \ + defined(MBEDTLS_ENTROPY_NV_SEED) ) +#error "MBEDTLS_PSA_INJECT_ENTROPY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + !defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) +#error "MBEDTLS_PSA_INJECT_ENTROPY is not compatible with actual entropy sources" +#endif + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +#error "MBEDTLS_PSA_INJECT_ENTROPY is not compatible with MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG" +#endif + +#if defined(MBEDTLS_PSA_ITS_FILE_C) && \ + !defined(MBEDTLS_FS_IO) +#error "MBEDTLS_PSA_ITS_FILE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) && \ + defined(MBEDTLS_USE_PSA_CRYPTO) +#error "MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER defined, but it cannot coexist with MBEDTLS_USE_PSA_CRYPTO." +#endif + +#if defined(MBEDTLS_RSA_C) && ( !defined(MBEDTLS_BIGNUM_C) || \ + !defined(MBEDTLS_OID_C) ) +#error "MBEDTLS_RSA_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_RSA_C) && ( !defined(MBEDTLS_PKCS1_V21) && \ + !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_RSA_C defined, but none of the PKCS1 versions enabled" +#endif + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ + ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_PKCS1_V21) ) +#error "MBEDTLS_X509_RSASSA_PSS_SUPPORT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SHA384_C) && !defined(MBEDTLS_SHA512_C) +#error "MBEDTLS_SHA384_C defined without MBEDTLS_SHA512_C" +#endif + +#if defined(MBEDTLS_SHA224_C) && !defined(MBEDTLS_SHA256_C) +#error "MBEDTLS_SHA224_C defined without MBEDTLS_SHA256_C" +#endif + +#if defined(MBEDTLS_SHA256_C) && !defined(MBEDTLS_SHA224_C) +#error "MBEDTLS_SHA256_C defined without MBEDTLS_SHA224_C" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && ( !defined(MBEDTLS_SHA1_C) && \ + !defined(MBEDTLS_SHA256_C) && !defined(MBEDTLS_SHA512_C) ) +#error "MBEDTLS_SSL_PROTO_TLS1_2 defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL) && ( !defined(MBEDTLS_HKDF_C) && \ + !defined(MBEDTLS_SHA256_C) && !defined(MBEDTLS_SHA512_C) ) +#error "MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \ + !(defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) ) +#error "One or more versions of the TLS protocol are enabled " \ + "but no key exchange methods defined with MBEDTLS_KEY_EXCHANGE_xxxx" +#endif + +#if defined(MBEDTLS_SSL_PROTO_DTLS) && \ + !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_PROTO_DTLS defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_CLI_C) && !defined(MBEDTLS_SSL_TLS_C) +#error "MBEDTLS_SSL_CLI_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) && ( !defined(MBEDTLS_CIPHER_C) || \ + !defined(MBEDTLS_MD_C) ) +#error "MBEDTLS_SSL_TLS_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_SRV_C) && !defined(MBEDTLS_SSL_TLS_C) +#error "MBEDTLS_SSL_SRV_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) && !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_TLS_C defined, but no protocols are active" +#endif + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && !defined(MBEDTLS_SSL_PROTO_DTLS) +#error "MBEDTLS_SSL_DTLS_HELLO_VERIFY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && \ + !defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) +#error "MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) && \ + ( !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) ) +#error "MBEDTLS_SSL_DTLS_ANTI_REPLAY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + ( !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) ) +#error "MBEDTLS_SSL_DTLS_CONNECTION_ID defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + defined(MBEDTLS_SSL_CID_IN_LEN_MAX) && \ + MBEDTLS_SSL_CID_IN_LEN_MAX > 255 +#error "MBEDTLS_SSL_CID_IN_LEN_MAX too large (max 255)" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + defined(MBEDTLS_SSL_CID_OUT_LEN_MAX) && \ + MBEDTLS_SSL_CID_OUT_LEN_MAX > 255 +#error "MBEDTLS_SSL_CID_OUT_LEN_MAX too large (max 255)" +#endif + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) && \ + !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_ENCRYPT_THEN_MAC defined, but not all prerequsites" +#endif + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) && \ + !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_EXTENDED_MASTER_SECRET defined, but not all prerequsites" +#endif + +#if defined(MBEDTLS_SSL_TICKET_C) && !defined(MBEDTLS_CIPHER_C) +#error "MBEDTLS_SSL_TICKET_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + !defined(MBEDTLS_X509_CRT_PARSE_C) +#error "MBEDTLS_SSL_SERVER_NAME_INDICATION defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_THREADING_PTHREAD) +#if !defined(MBEDTLS_THREADING_C) || defined(MBEDTLS_THREADING_IMPL) +#error "MBEDTLS_THREADING_PTHREAD defined, but not all prerequisites" +#endif +#define MBEDTLS_THREADING_IMPL +#endif + +#if defined(MBEDTLS_THREADING_ALT) +#if !defined(MBEDTLS_THREADING_C) || defined(MBEDTLS_THREADING_IMPL) +#error "MBEDTLS_THREADING_ALT defined, but not all prerequisites" +#endif +#define MBEDTLS_THREADING_IMPL +#endif + +#if defined(MBEDTLS_THREADING_C) && !defined(MBEDTLS_THREADING_IMPL) +#error "MBEDTLS_THREADING_C defined, single threading implementation required" +#endif +#undef MBEDTLS_THREADING_IMPL + +#if defined(MBEDTLS_USE_PSA_CRYPTO) && !defined(MBEDTLS_PSA_CRYPTO_C) +#error "MBEDTLS_USE_PSA_CRYPTO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_VERSION_FEATURES) && !defined(MBEDTLS_VERSION_C) +#error "MBEDTLS_VERSION_FEATURES defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_USE_C) && ( !defined(MBEDTLS_BIGNUM_C) || \ + !defined(MBEDTLS_OID_C) || !defined(MBEDTLS_ASN1_PARSE_C) || \ + !defined(MBEDTLS_PK_PARSE_C) ) +#error "MBEDTLS_X509_USE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CREATE_C) && ( !defined(MBEDTLS_BIGNUM_C) || \ + !defined(MBEDTLS_OID_C) || !defined(MBEDTLS_ASN1_WRITE_C) || \ + !defined(MBEDTLS_PK_WRITE_C) ) +#error "MBEDTLS_X509_CREATE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && ( !defined(MBEDTLS_X509_USE_C) ) +#error "MBEDTLS_X509_CRT_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CRL_PARSE_C) && ( !defined(MBEDTLS_X509_USE_C) ) +#error "MBEDTLS_X509_CRL_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CSR_PARSE_C) && ( !defined(MBEDTLS_X509_USE_C) ) +#error "MBEDTLS_X509_CSR_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CRT_WRITE_C) && ( !defined(MBEDTLS_X509_CREATE_C) ) +#error "MBEDTLS_X509_CRT_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CSR_WRITE_C) && ( !defined(MBEDTLS_X509_CREATE_C) ) +#error "MBEDTLS_X509_CSR_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_HAVE_INT32) && defined(MBEDTLS_HAVE_INT64) +#error "MBEDTLS_HAVE_INT32 and MBEDTLS_HAVE_INT64 cannot be defined simultaneously" +#endif /* MBEDTLS_HAVE_INT32 && MBEDTLS_HAVE_INT64 */ + +#if ( defined(MBEDTLS_HAVE_INT32) || defined(MBEDTLS_HAVE_INT64) ) && \ + defined(MBEDTLS_HAVE_ASM) +#error "MBEDTLS_HAVE_INT32/MBEDTLS_HAVE_INT64 and MBEDTLS_HAVE_ASM cannot be defined simultaneously" +#endif /* (MBEDTLS_HAVE_INT32 || MBEDTLS_HAVE_INT64) && MBEDTLS_HAVE_ASM */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) && ( !defined(MBEDTLS_SSL_PROTO_DTLS) ) +#error "MBEDTLS_SSL_DTLS_SRTP defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) && ( !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) ) +#error "MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH defined, but not all prerequisites" +#endif + + + +/* Reject attempts to enable options that have been removed and that could + * cause a build to succeed but with features removed. */ + +#if defined(MBEDTLS_HAVEGE_C) //no-check-names +#error "MBEDTLS_HAVEGE_C was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/2599" +#endif + +#if defined(MBEDTLS_SSL_HW_RECORD_ACCEL) //no-check-names +#error "MBEDTLS_SSL_HW_RECORD_ACCEL was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_SSL_PROTO_SSL3) //no-check-names +#error "MBEDTLS_SSL_PROTO_SSL3 (SSL v3.0 support) was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) //no-check-names +#error "MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO (SSL v2 ClientHello support) was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT) //no-check-names +#error "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT (compatibility with the buggy implementation of truncated HMAC in Mbed TLS up to 2.7) was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES) //no-check-names +#error "MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES was removed in Mbed TLS 3.0. See the ChangeLog entry if you really need SHA-1-signed certificates." +#endif + +#if defined(MBEDTLS_ZLIB_SUPPORT) //no-check-names +#error "MBEDTLS_ZLIB_SUPPORT was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1) //no-check-names +#error "MBEDTLS_SSL_PROTO_TLS1 (TLS v1.0 support) was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4286" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_1) //no-check-names +#error "MBEDTLS_SSL_PROTO_TLS1_1 (TLS v1.1 support) was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4286" +#endif + +#if defined(MBEDTLS_CHECK_PARAMS) //no-check-names +#error "MBEDTLS_CHECK_PARAMS was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4313" +#endif + +#if defined(MBEDTLS_SSL_CID_PADDING_GRANULARITY) //no-check-names +#error "MBEDTLS_SSL_CID_PADDING_GRANULARITY was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4335" +#endif + +#if defined(MBEDTLS_SSL_TLS1_3_PADDING_GRANULARITY) //no-check-names +#error "MBEDTLS_SSL_TLS1_3_PADDING_GRANULARITY was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4335" +#endif + +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC) //no-check-names +#error "MBEDTLS_SSL_TRUNCATED_HMAC was removed in Mbed TLS 3.0. See https://github.com/ARMmbed/mbedtls/issues/4341" +#endif + +/* + * Avoid warning from -pedantic. This is a convenient place for this + * workaround since this is included by every single file before the + * #if defined(MBEDTLS_xxx_C) that results in empty translation units. + */ +typedef int mbedtls_iso_c_forbids_empty_translation_units; + +#endif /* MBEDTLS_CHECK_CONFIG_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecdsa.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecdsa.h new file mode 100644 index 0000000..71b73ee --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecdsa.h @@ -0,0 +1,506 @@ +/** + * \file ecdsa.h + * + * \brief This file contains ECDSA definitions and functions. + * + * The Elliptic Curve Digital Signature Algorithm (ECDSA) is defined in + * Standards for Efficient Cryptography Group (SECG): + * SEC1 Elliptic Curve Cryptography. + * The use of ECDSA for TLS is defined in RFC-4492: Elliptic Curve + * Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS). + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_ECDSA_H +#define MBEDTLS_ECDSA_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/md.h" + +/** + * \brief Maximum ECDSA signature size for a given curve bit size + * + * \param bits Curve size in bits + * \return Maximum signature size in bytes + * + * \note This macro returns a compile-time constant if its argument + * is one. It may evaluate its argument multiple times. + */ +/* + * Ecdsa-Sig-Value ::= SEQUENCE { + * r INTEGER, + * s INTEGER + * } + * + * For each of r and s, the value (V) may include an extra initial "0" bit. + */ +#define MBEDTLS_ECDSA_MAX_SIG_LEN( bits ) \ + ( /*T,L of SEQUENCE*/ ( ( bits ) >= 61 * 8 ? 3 : 2 ) + \ + /*T,L of r,s*/ 2 * ( ( ( bits ) >= 127 * 8 ? 3 : 2 ) + \ + /*V of r,s*/ ( ( bits ) + 8 ) / 8 ) ) + +/** The maximal size of an ECDSA signature in Bytes. */ +#define MBEDTLS_ECDSA_MAX_LEN MBEDTLS_ECDSA_MAX_SIG_LEN( MBEDTLS_ECP_MAX_BITS ) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief The ECDSA context structure. + * + * \warning Performing multiple operations concurrently on the same + * ECDSA context is not supported; objects of this type + * should not be shared between multiple threads. + */ +typedef mbedtls_ecp_keypair mbedtls_ecdsa_context; + +#if defined(MBEDTLS_ECP_RESTARTABLE) + +/** + * \brief Internal restart context for ecdsa_verify() + * + * \note Opaque struct, defined in ecdsa.c + */ +typedef struct mbedtls_ecdsa_restart_ver mbedtls_ecdsa_restart_ver_ctx; + +/** + * \brief Internal restart context for ecdsa_sign() + * + * \note Opaque struct, defined in ecdsa.c + */ +typedef struct mbedtls_ecdsa_restart_sig mbedtls_ecdsa_restart_sig_ctx; + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +/** + * \brief Internal restart context for ecdsa_sign_det() + * + * \note Opaque struct, defined in ecdsa.c + */ +typedef struct mbedtls_ecdsa_restart_det mbedtls_ecdsa_restart_det_ctx; +#endif + +/** + * \brief General context for resuming ECDSA operations + */ +typedef struct +{ + mbedtls_ecp_restart_ctx MBEDTLS_PRIVATE(ecp); /*!< base context for ECP restart and + shared administrative info */ + mbedtls_ecdsa_restart_ver_ctx *MBEDTLS_PRIVATE(ver); /*!< ecdsa_verify() sub-context */ + mbedtls_ecdsa_restart_sig_ctx *MBEDTLS_PRIVATE(sig); /*!< ecdsa_sign() sub-context */ +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + mbedtls_ecdsa_restart_det_ctx *MBEDTLS_PRIVATE(det); /*!< ecdsa_sign_det() sub-context */ +#endif +} mbedtls_ecdsa_restart_ctx; + +#else /* MBEDTLS_ECP_RESTARTABLE */ + +/* Now we can declare functions that take a pointer to that */ +typedef void mbedtls_ecdsa_restart_ctx; + +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief This function checks whether a given group can be used + * for ECDSA. + * + * \param gid The ECP group ID to check. + * + * \return \c 1 if the group can be used, \c 0 otherwise + */ +int mbedtls_ecdsa_can_do( mbedtls_ecp_group_id gid ); + +/** + * \brief This function computes the ECDSA signature of a + * previously-hashed message. + * + * \note The deterministic version implemented in + * mbedtls_ecdsa_sign_det_ext() is usually preferred. + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated + * as defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.3, step 5. + * + * \see ecp.h + * + * \param grp The context for the elliptic curve to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param r The MPI context in which to store the first part + * the signature. This must be initialized. + * \param s The MPI context in which to store the second part + * the signature. This must be initialized. + * \param d The private signing key. This must be initialized. + * \param buf The content to be signed. This is usually the hash of + * the original data to be signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX + * or \c MBEDTLS_MPI_XXX error code on failure. + */ +int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s, + const mbedtls_mpi *d, const unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +/** + * \brief This function computes the ECDSA signature of a + * previously-hashed message, deterministic version. + * + * For more information, see RFC-6979: Deterministic + * Usage of the Digital Signature Algorithm (DSA) and Elliptic + * Curve Digital Signature Algorithm (ECDSA). + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.3, step 5. + * + * \see ecp.h + * + * \param grp The context for the elliptic curve to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param r The MPI context in which to store the first part + * the signature. This must be initialized. + * \param s The MPI context in which to store the second part + * the signature. This must be initialized. + * \param d The private signing key. This must be initialized + * and setup, for example through mbedtls_ecp_gen_privkey(). + * \param buf The hashed content to be signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param md_alg The hash algorithm used to hash the original data. + * \param f_rng_blind The RNG function used for blinding. This must not be + * \c NULL. + * \param p_rng_blind The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX + * error code on failure. + */ +int mbedtls_ecdsa_sign_det_ext( mbedtls_ecp_group *grp, mbedtls_mpi *r, + mbedtls_mpi *s, const mbedtls_mpi *d, + const unsigned char *buf, size_t blen, + mbedtls_md_type_t md_alg, + int (*f_rng_blind)(void *, unsigned char *, size_t), + void *p_rng_blind ); +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ + +/** + * \brief This function verifies the ECDSA signature of a + * previously-hashed message. + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.4, step 3. + * + * \see ecp.h + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param buf The hashed content that was signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param Q The public key to use for verification. This must be + * initialized and setup. + * \param r The first integer of the signature. + * This must be initialized. + * \param s The second integer of the signature. + * This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the signature + * is invalid. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX + * error code on failure for any other reason. + */ +int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp, + const unsigned char *buf, size_t blen, + const mbedtls_ecp_point *Q, const mbedtls_mpi *r, + const mbedtls_mpi *s); + +/** + * \brief This function computes the ECDSA signature and writes it + * to a buffer, serialized as defined in RFC-4492: + * Elliptic Curve Cryptography (ECC) Cipher Suites for + * Transport Layer Security (TLS). + * + * \warning It is not thread-safe to use the same context in + * multiple threads. + * + * \note The deterministic version is used if + * #MBEDTLS_ECDSA_DETERMINISTIC is defined. For more + * information, see RFC-6979: Deterministic Usage + * of the Digital Signature Algorithm (DSA) and Elliptic + * Curve Digital Signature Algorithm (ECDSA). + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.3, step 5. + * + * \see ecp.h + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and private key bound to it, for example + * via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair(). + * \param md_alg The message digest that was used to hash the message. + * \param hash The message hash to be signed. This must be a readable + * buffer of length \p blen Bytes. + * \param hlen The length of the hash \p hash in Bytes. + * \param sig The buffer to which to write the signature. This must be a + * writable buffer of length at least twice as large as the + * size of the curve used, plus 9. For example, 73 Bytes if + * a 256-bit curve is used. A buffer length of + * #MBEDTLS_ECDSA_MAX_LEN is always safe. + * \param sig_size The size of the \p sig buffer in bytes. + * \param slen The address at which to store the actual length of + * the signature written. Must not be \c NULL. + * \param f_rng The RNG function. This must not be \c NULL if + * #MBEDTLS_ECDSA_DETERMINISTIC is unset. Otherwise, + * it is used only for blinding and may be set to \c NULL, but + * doing so is DEPRECATED. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng is \c NULL or doesn't use a context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or + * \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_ecdsa_write_signature( mbedtls_ecdsa_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function computes the ECDSA signature and writes it + * to a buffer, in a restartable way. + * + * \see \c mbedtls_ecdsa_write_signature() + * + * \note This function is like \c mbedtls_ecdsa_write_signature() + * but it can return early and restart according to the limit + * set with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and private key bound to it, for example + * via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair(). + * \param md_alg The message digest that was used to hash the message. + * \param hash The message hash to be signed. This must be a readable + * buffer of length \p blen Bytes. + * \param hlen The length of the hash \p hash in Bytes. + * \param sig The buffer to which to write the signature. This must be a + * writable buffer of length at least twice as large as the + * size of the curve used, plus 9. For example, 73 Bytes if + * a 256-bit curve is used. A buffer length of + * #MBEDTLS_ECDSA_MAX_LEN is always safe. + * \param sig_size The size of the \p sig buffer in bytes. + * \param slen The address at which to store the actual length of + * the signature written. Must not be \c NULL. + * \param f_rng The RNG function. This must not be \c NULL if + * #MBEDTLS_ECDSA_DETERMINISTIC is unset. Otherwise, + * it is unused and may be set to \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng is \c NULL or doesn't use a context. + * \param rs_ctx The restart context to use. This may be \c NULL to disable + * restarting. If it is not \c NULL, it must point to an + * initialized restart context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or + * \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_ecdsa_write_signature_restartable( mbedtls_ecdsa_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecdsa_restart_ctx *rs_ctx ); + +/** + * \brief This function reads and verifies an ECDSA signature. + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.4, step 3. + * + * \see ecp.h + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and public key bound to it. + * \param hash The message hash that was signed. This must be a readable + * buffer of length \p size Bytes. + * \param hlen The size of the hash \p hash. + * \param sig The signature to read and verify. This must be a readable + * buffer of length \p slen Bytes. + * \param slen The size of \p sig in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if signature is invalid. + * \return #MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH if there is a valid + * signature in \p sig, but its length is less than \p siglen. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX + * error code on failure for any other reason. + */ +int mbedtls_ecdsa_read_signature( mbedtls_ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen ); + +/** + * \brief This function reads and verifies an ECDSA signature, + * in a restartable way. + * + * \see \c mbedtls_ecdsa_read_signature() + * + * \note This function is like \c mbedtls_ecdsa_read_signature() + * but it can return early and restart according to the limit + * set with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and public key bound to it. + * \param hash The message hash that was signed. This must be a readable + * buffer of length \p size Bytes. + * \param hlen The size of the hash \p hash. + * \param sig The signature to read and verify. This must be a readable + * buffer of length \p slen Bytes. + * \param slen The size of \p sig in Bytes. + * \param rs_ctx The restart context to use. This may be \c NULL to disable + * restarting. If it is not \c NULL, it must point to an + * initialized restart context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if signature is invalid. + * \return #MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH if there is a valid + * signature in \p sig, but its length is less than \p siglen. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX + * error code on failure for any other reason. + */ +int mbedtls_ecdsa_read_signature_restartable( mbedtls_ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen, + mbedtls_ecdsa_restart_ctx *rs_ctx ); + +/** + * \brief This function generates an ECDSA keypair on the given curve. + * + * \see ecp.h + * + * \param ctx The ECDSA context to store the keypair in. + * This must be initialized. + * \param gid The elliptic curve to use. One of the various + * \c MBEDTLS_ECP_DP_XXX macros depending on configuration. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX code on failure. + */ +int mbedtls_ecdsa_genkey( mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief This function sets up an ECDSA context from an EC key pair. + * + * \see ecp.h + * + * \param ctx The ECDSA context to setup. This must be initialized. + * \param key The EC key to use. This must be initialized and hold + * a private-public key pair or a public key. In the former + * case, the ECDSA context may be used for signature creation + * and verification after this call. In the latter case, it + * may be used for signature verification. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX code on failure. + */ +int mbedtls_ecdsa_from_keypair( mbedtls_ecdsa_context *ctx, + const mbedtls_ecp_keypair *key ); + +/** + * \brief This function initializes an ECDSA context. + * + * \param ctx The ECDSA context to initialize. + * This must not be \c NULL. + */ +void mbedtls_ecdsa_init( mbedtls_ecdsa_context *ctx ); + +/** + * \brief This function frees an ECDSA context. + * + * \param ctx The ECDSA context to free. This may be \c NULL, + * in which case this function does nothing. If it + * is not \c NULL, it must be initialized. + */ +void mbedtls_ecdsa_free( mbedtls_ecdsa_context *ctx ); + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Initialize a restart context. + * + * \param ctx The restart context to initialize. + * This must not be \c NULL. + */ +void mbedtls_ecdsa_restart_init( mbedtls_ecdsa_restart_ctx *ctx ); + +/** + * \brief Free the components of a restart context. + * + * \param ctx The restart context to free. This may be \c NULL, + * in which case this function does nothing. If it + * is not \c NULL, it must be initialized. + */ +void mbedtls_ecdsa_restart_free( mbedtls_ecdsa_restart_ctx *ctx ); +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +#ifdef __cplusplus +} +#endif + +#endif /* ecdsa.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecp.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecp.h new file mode 100644 index 0000000..b87114b --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/ecp.h @@ -0,0 +1,1286 @@ +/** + * \file ecp.h + * + * \brief This file provides an API for Elliptic Curves over GF(P) (ECP). + * + * The use of ECP in cryptography and TLS is defined in + * Standards for Efficient Cryptography Group (SECG): SEC1 + * Elliptic Curve Cryptography and + * RFC-4492: Elliptic Curve Cryptography (ECC) Cipher Suites + * for Transport Layer Security (TLS). + * + * RFC-2409: The Internet Key Exchange (IKE) defines ECP + * group types. + * + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_ECP_H +#define MBEDTLS_ECP_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/bignum.h" + +/* + * ECP error codes + */ +#define MBEDTLS_ERR_ECP_BAD_INPUT_DATA -0x4F80 /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL -0x4F00 /**< The buffer is too small to write to. */ +#define MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE -0x4E80 /**< The requested feature is not available, for example, the requested curve is not supported. */ +#define MBEDTLS_ERR_ECP_VERIFY_FAILED -0x4E00 /**< The signature is not valid. */ +#define MBEDTLS_ERR_ECP_ALLOC_FAILED -0x4D80 /**< Memory allocation failed. */ +#define MBEDTLS_ERR_ECP_RANDOM_FAILED -0x4D00 /**< Generation of random value, such as ephemeral key, failed. */ +#define MBEDTLS_ERR_ECP_INVALID_KEY -0x4C80 /**< Invalid private or public key. */ +#define MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH -0x4C00 /**< The buffer contains a valid signature followed by more data. */ +#define MBEDTLS_ERR_ECP_IN_PROGRESS -0x4B00 /**< Operation in progress, call again with the same parameters to continue. */ + +/* Flags indicating whether to include code that is specific to certain + * types of curves. These flags are for internal library use only. */ +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +#define MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED +#endif +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) || \ + defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +#define MBEDTLS_ECP_MONTGOMERY_ENABLED +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Domain-parameter identifiers: curve, subgroup, and generator. + * + * \note Only curves over prime fields are supported. + * + * \warning This library does not support validation of arbitrary domain + * parameters. Therefore, only standardized domain parameters from trusted + * sources should be used. See mbedtls_ecp_group_load(). + */ +/* Note: when adding a new curve: + * - Add it at the end of this enum, otherwise you'll break the ABI by + * changing the numerical value for existing curves. + * - Increment MBEDTLS_ECP_DP_MAX below if needed. + * - Update the calculation of MBEDTLS_ECP_MAX_BITS below. + * - Add the corresponding MBEDTLS_ECP_DP_xxx_ENABLED macro definition to + * mbedtls_config.h. + * - List the curve as a dependency of MBEDTLS_ECP_C and + * MBEDTLS_ECDSA_C if supported in check_config.h. + * - Add the curve to the appropriate curve type macro + * MBEDTLS_ECP_yyy_ENABLED above. + * - Add the necessary definitions to ecp_curves.c. + * - Add the curve to the ecp_supported_curves array in ecp.c. + * - Add the curve to applicable profiles in x509_crt.c. + * - Add the curve to applicable presets in ssl_tls.c. + */ +typedef enum +{ + MBEDTLS_ECP_DP_NONE = 0, /*!< Curve not defined. */ + MBEDTLS_ECP_DP_SECP192R1, /*!< Domain parameters for the 192-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP224R1, /*!< Domain parameters for the 224-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP256R1, /*!< Domain parameters for the 256-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP384R1, /*!< Domain parameters for the 384-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP521R1, /*!< Domain parameters for the 521-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_BP256R1, /*!< Domain parameters for 256-bit Brainpool curve. */ + MBEDTLS_ECP_DP_BP384R1, /*!< Domain parameters for 384-bit Brainpool curve. */ + MBEDTLS_ECP_DP_BP512R1, /*!< Domain parameters for 512-bit Brainpool curve. */ + MBEDTLS_ECP_DP_CURVE25519, /*!< Domain parameters for Curve25519. */ + MBEDTLS_ECP_DP_SECP192K1, /*!< Domain parameters for 192-bit "Koblitz" curve. */ + MBEDTLS_ECP_DP_SECP224K1, /*!< Domain parameters for 224-bit "Koblitz" curve. */ + MBEDTLS_ECP_DP_SECP256K1, /*!< Domain parameters for 256-bit "Koblitz" curve. */ + MBEDTLS_ECP_DP_CURVE448, /*!< Domain parameters for Curve448. */ +} mbedtls_ecp_group_id; + +/** + * The number of supported curves, plus one for #MBEDTLS_ECP_DP_NONE. + * + * \note Montgomery curves are currently excluded. + */ +#define MBEDTLS_ECP_DP_MAX 12 + +/* + * Curve types + */ +typedef enum +{ + MBEDTLS_ECP_TYPE_NONE = 0, + MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS, /* y^2 = x^3 + a x + b */ + MBEDTLS_ECP_TYPE_MONTGOMERY, /* y^2 = x^3 + a x^2 + x */ +} mbedtls_ecp_curve_type; + +/** + * Curve information, for use by other modules. + */ +typedef struct mbedtls_ecp_curve_info +{ + mbedtls_ecp_group_id MBEDTLS_PRIVATE(grp_id); /*!< An internal identifier. */ + uint16_t MBEDTLS_PRIVATE(tls_id); /*!< The TLS NamedCurve identifier. */ + uint16_t MBEDTLS_PRIVATE(bit_size); /*!< The curve size in bits. */ + const char *MBEDTLS_PRIVATE(name); /*!< A human-friendly name. */ +} mbedtls_ecp_curve_info; + +/** + * \brief The ECP point structure, in Jacobian coordinates. + * + * \note All functions expect and return points satisfying + * the following condition: Z == 0 or + * Z == 1. Other values of \p Z are + * used only by internal functions. + * The point is zero, or "at infinity", if Z == 0. + * Otherwise, \p X and \p Y are its standard (affine) + * coordinates. + */ +typedef struct mbedtls_ecp_point +{ + mbedtls_mpi MBEDTLS_PRIVATE(X); /*!< The X coordinate of the ECP point. */ + mbedtls_mpi MBEDTLS_PRIVATE(Y); /*!< The Y coordinate of the ECP point. */ + mbedtls_mpi MBEDTLS_PRIVATE(Z); /*!< The Z coordinate of the ECP point. */ +} +mbedtls_ecp_point; + +#if !defined(MBEDTLS_ECP_ALT) +/* + * default mbed TLS elliptic curve arithmetic implementation + * + * (in case MBEDTLS_ECP_ALT is defined then the developer has to provide an + * alternative implementation for the whole module and it will replace this + * one.) + */ + +/** + * \brief The ECP group structure. + * + * We consider two types of curve equations: + *
  • Short Weierstrass: y^2 = x^3 + A x + B mod P + * (SEC1 + RFC-4492)
  • + *
  • Montgomery: y^2 = x^3 + A x^2 + x mod P (Curve25519, + * Curve448)
+ * In both cases, the generator (\p G) for a prime-order subgroup is fixed. + * + * For Short Weierstrass, this subgroup is the whole curve, and its + * cardinality is denoted by \p N. Our code requires that \p N is an + * odd prime as mbedtls_ecp_mul() requires an odd number, and + * mbedtls_ecdsa_sign() requires that it is prime for blinding purposes. + * + * For Montgomery curves, we do not store \p A, but (A + 2) / 4, + * which is the quantity used in the formulas. Additionally, \p nbits is + * not the size of \p N but the required size for private keys. + * + * If \p modp is NULL, reduction modulo \p P is done using a generic algorithm. + * Otherwise, \p modp must point to a function that takes an \p mbedtls_mpi in the + * range of 0..2^(2*pbits)-1, and transforms it in-place to an integer + * which is congruent mod \p P to the given MPI, and is close enough to \p pbits + * in size, so that it may be efficiently brought in the 0..P-1 range by a few + * additions or subtractions. Therefore, it is only an approximative modular + * reduction. It must return 0 on success and non-zero on failure. + * + * \note Alternative implementations of the ECP module must obey the + * following constraints. + * * Group IDs must be distinct: if two group structures have + * the same ID, then they must be identical. + * * The fields \c id, \c P, \c A, \c B, \c G, \c N, + * \c pbits and \c nbits must have the same type and semantics + * as in the built-in implementation. + * They must be available for reading, but direct modification + * of these fields does not need to be supported. + * They do not need to be at the same offset in the structure. + */ +typedef struct mbedtls_ecp_group +{ + mbedtls_ecp_group_id id; /*!< An internal group identifier. */ + mbedtls_mpi P; /*!< The prime modulus of the base field. */ + mbedtls_mpi A; /*!< For Short Weierstrass: \p A in the equation. For + Montgomery curves: (A + 2) / 4. */ + mbedtls_mpi B; /*!< For Short Weierstrass: \p B in the equation. + For Montgomery curves: unused. */ + mbedtls_ecp_point G; /*!< The generator of the subgroup used. */ + mbedtls_mpi N; /*!< The order of \p G. */ + size_t pbits; /*!< The number of bits in \p P.*/ + size_t nbits; /*!< For Short Weierstrass: The number of bits in \p P. + For Montgomery curves: the number of bits in the + private keys. */ + /* End of public fields */ + + unsigned int MBEDTLS_PRIVATE(h); /*!< \internal 1 if the constants are static. */ + int (*MBEDTLS_PRIVATE(modp))(mbedtls_mpi *); /*!< The function for fast pseudo-reduction + mod \p P (see above).*/ + int (*MBEDTLS_PRIVATE(t_pre))(mbedtls_ecp_point *, void *); /*!< Unused. */ + int (*MBEDTLS_PRIVATE(t_post))(mbedtls_ecp_point *, void *); /*!< Unused. */ + void *MBEDTLS_PRIVATE(t_data); /*!< Unused. */ + mbedtls_ecp_point *MBEDTLS_PRIVATE(T); /*!< Pre-computed points for ecp_mul_comb(). */ + size_t MBEDTLS_PRIVATE(T_size); /*!< The number of dynamic allocated pre-computed points. */ +} +mbedtls_ecp_group; + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h, or define them using the compiler command line. + * \{ + */ + +#if !defined(MBEDTLS_ECP_WINDOW_SIZE) +/* + * Maximum "window" size used for point multiplication. + * Default: a point where higher memory usage yields disminishing performance + * returns. + * Minimum value: 2. Maximum value: 7. + * + * Result is an array of at most ( 1 << ( MBEDTLS_ECP_WINDOW_SIZE - 1 ) ) + * points used for point multiplication. This value is directly tied to EC + * peak memory usage, so decreasing it by one should roughly cut memory usage + * by two (if large curves are in use). + * + * Reduction in size may reduce speed, but larger curves are impacted first. + * Sample performances (in ECDHE handshakes/s, with FIXED_POINT_OPTIM = 1): + * w-size: 6 5 4 3 2 + * 521 145 141 135 120 97 + * 384 214 209 198 177 146 + * 256 320 320 303 262 226 + * 224 475 475 453 398 342 + * 192 640 640 633 587 476 + */ +#define MBEDTLS_ECP_WINDOW_SIZE 4 /**< The maximum window size used. */ +#endif /* MBEDTLS_ECP_WINDOW_SIZE */ + +#if !defined(MBEDTLS_ECP_FIXED_POINT_OPTIM) +/* + * Trade code size for speed on fixed-point multiplication. + * + * This speeds up repeated multiplication of the generator (that is, the + * multiplication in ECDSA signatures, and half of the multiplications in + * ECDSA verification and ECDHE) by a factor roughly 3 to 4. + * + * For each n-bit Short Weierstrass curve that is enabled, this adds 4n bytes + * of code size if n < 384 and 8n otherwise. + * + * Change this value to 0 to reduce code size. + */ +#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up. */ +#endif /* MBEDTLS_ECP_FIXED_POINT_OPTIM */ + +/* \} name SECTION: Module settings */ + +#else /* MBEDTLS_ECP_ALT */ +#include "ecp_alt.h" +#endif /* MBEDTLS_ECP_ALT */ + +/** + * The maximum size of the groups, that is, of \c N and \c P. + */ +#if !defined(MBEDTLS_ECP_C) +/* Dummy definition to help code that has optional ECP support and + * defines an MBEDTLS_ECP_MAX_BYTES-sized array unconditionally. */ +#define MBEDTLS_ECP_MAX_BITS 1 +/* Note: the curves must be listed in DECREASING size! */ +#elif defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 521 +#elif defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 512 +#elif defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 448 +#elif defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 384 +#elif defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 384 +#elif defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 256 +#elif defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 256 +#elif defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 256 +#elif defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 255 +#elif defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 225 // n is slightly above 2^224 +#elif defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 224 +#elif defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 192 +#elif defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 192 +#else +#error "Missing definition of MBEDTLS_ECP_MAX_BITS" +#endif + +#define MBEDTLS_ECP_MAX_BYTES ( ( MBEDTLS_ECP_MAX_BITS + 7 ) / 8 ) +#define MBEDTLS_ECP_MAX_PT_LEN ( 2 * MBEDTLS_ECP_MAX_BYTES + 1 ) + +#if defined(MBEDTLS_ECP_RESTARTABLE) + +/** + * \brief Internal restart context for multiplication + * + * \note Opaque struct + */ +typedef struct mbedtls_ecp_restart_mul mbedtls_ecp_restart_mul_ctx; + +/** + * \brief Internal restart context for ecp_muladd() + * + * \note Opaque struct + */ +typedef struct mbedtls_ecp_restart_muladd mbedtls_ecp_restart_muladd_ctx; + +/** + * \brief General context for resuming ECC operations + */ +typedef struct +{ + unsigned MBEDTLS_PRIVATE(ops_done); /*!< current ops count */ + unsigned MBEDTLS_PRIVATE(depth); /*!< call depth (0 = top-level) */ + mbedtls_ecp_restart_mul_ctx *MBEDTLS_PRIVATE(rsm); /*!< ecp_mul_comb() sub-context */ + mbedtls_ecp_restart_muladd_ctx *MBEDTLS_PRIVATE(ma); /*!< ecp_muladd() sub-context */ +} mbedtls_ecp_restart_ctx; + +/* + * Operation counts for restartable functions + */ +#define MBEDTLS_ECP_OPS_CHK 3 /*!< basic ops count for ecp_check_pubkey() */ +#define MBEDTLS_ECP_OPS_DBL 8 /*!< basic ops count for ecp_double_jac() */ +#define MBEDTLS_ECP_OPS_ADD 11 /*!< basic ops count for see ecp_add_mixed() */ +#define MBEDTLS_ECP_OPS_INV 120 /*!< empirical equivalent for mpi_mod_inv() */ + +/** + * \brief Internal; for restartable functions in other modules. + * Check and update basic ops budget. + * + * \param grp Group structure + * \param rs_ctx Restart context + * \param ops Number of basic ops to do + * + * \return \c 0 if doing \p ops basic ops is still allowed, + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS otherwise. + */ +int mbedtls_ecp_check_budget( const mbedtls_ecp_group *grp, + mbedtls_ecp_restart_ctx *rs_ctx, + unsigned ops ); + +/* Utility macro for checking and updating ops budget */ +#define MBEDTLS_ECP_BUDGET( ops ) \ + MBEDTLS_MPI_CHK( mbedtls_ecp_check_budget( grp, rs_ctx, \ + (unsigned) (ops) ) ); + +#else /* MBEDTLS_ECP_RESTARTABLE */ + +#define MBEDTLS_ECP_BUDGET( ops ) /* no-op; for compatibility */ + +/* We want to declare restartable versions of existing functions anyway */ +typedef void mbedtls_ecp_restart_ctx; + +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief The ECP key-pair structure. + * + * A generic key-pair that may be used for ECDSA and fixed ECDH, for example. + * + * \note Members are deliberately in the same order as in the + * ::mbedtls_ecdsa_context structure. + */ +typedef struct mbedtls_ecp_keypair +{ + mbedtls_ecp_group MBEDTLS_PRIVATE(grp); /*!< Elliptic curve and base point */ + mbedtls_mpi MBEDTLS_PRIVATE(d); /*!< our secret value */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Q); /*!< our public value */ +} +mbedtls_ecp_keypair; + +/* + * Point formats, from RFC 4492's enum ECPointFormat + */ +#define MBEDTLS_ECP_PF_UNCOMPRESSED 0 /**< Uncompressed point format. */ +#define MBEDTLS_ECP_PF_COMPRESSED 1 /**< Compressed point format. */ + +/* + * Some other constants from RFC 4492 + */ +#define MBEDTLS_ECP_TLS_NAMED_CURVE 3 /**< The named_curve of ECCurveType. */ + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Set the maximum number of basic operations done in a row. + * + * If more operations are needed to complete a computation, + * #MBEDTLS_ERR_ECP_IN_PROGRESS will be returned by the + * function performing the computation. It is then the + * caller's responsibility to either call again with the same + * parameters until it returns 0 or an error code; or to free + * the restart context if the operation is to be aborted. + * + * It is strictly required that all input parameters and the + * restart context be the same on successive calls for the + * same operation, but output parameters need not be the + * same; they must not be used until the function finally + * returns 0. + * + * This only applies to functions whose documentation + * mentions they may return #MBEDTLS_ERR_ECP_IN_PROGRESS (or + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS for functions in the + * SSL module). For functions that accept a "restart context" + * argument, passing NULL disables restart and makes the + * function equivalent to the function with the same name + * with \c _restartable removed. For functions in the ECDH + * module, restart is disabled unless the function accepts + * an "ECDH context" argument and + * mbedtls_ecdh_enable_restart() was previously called on + * that context. For function in the SSL module, restart is + * only enabled for specific sides and key exchanges + * (currently only for clients and ECDHE-ECDSA). + * + * \param max_ops Maximum number of basic operations done in a row. + * Default: 0 (unlimited). + * Lower (non-zero) values mean ECC functions will block for + * a lesser maximum amount of time. + * + * \note A "basic operation" is defined as a rough equivalent of a + * multiplication in GF(p) for the NIST P-256 curve. + * As an indication, with default settings, a scalar + * multiplication (full run of \c mbedtls_ecp_mul()) is: + * - about 3300 basic operations for P-256 + * - about 9400 basic operations for P-384 + * + * \note Very low values are not always respected: sometimes + * functions need to block for a minimum number of + * operations, and will do so even if max_ops is set to a + * lower value. That minimum depends on the curve size, and + * can be made lower by decreasing the value of + * \c MBEDTLS_ECP_WINDOW_SIZE. As an indication, here is the + * lowest effective value for various curves and values of + * that parameter (w for short): + * w=6 w=5 w=4 w=3 w=2 + * P-256 208 208 160 136 124 + * P-384 682 416 320 272 248 + * P-521 1364 832 640 544 496 + * + * \note This setting is currently ignored by Curve25519. + */ +void mbedtls_ecp_set_max_ops( unsigned max_ops ); + +/** + * \brief Check if restart is enabled (max_ops != 0) + * + * \return \c 0 if \c max_ops == 0 (restart disabled) + * \return \c 1 otherwise (restart enabled) + */ +int mbedtls_ecp_restart_is_enabled( void ); +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/* + * Get the type of a curve + */ +mbedtls_ecp_curve_type mbedtls_ecp_get_type( const mbedtls_ecp_group *grp ); + +/** + * \brief This function retrieves the information defined in + * mbedtls_ecp_curve_info() for all supported curves. + * + * \note This function returns information about all curves + * supported by the library. Some curves may not be + * supported for all algorithms. Call mbedtls_ecdh_can_do() + * or mbedtls_ecdsa_can_do() to check if a curve is + * supported for ECDH or ECDSA. + * + * \return A statically allocated array. The last entry is 0. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_list( void ); + +/** + * \brief This function retrieves the list of internal group + * identifiers of all supported curves in the order of + * preference. + * + * \note This function returns information about all curves + * supported by the library. Some curves may not be + * supported for all algorithms. Call mbedtls_ecdh_can_do() + * or mbedtls_ecdsa_can_do() to check if a curve is + * supported for ECDH or ECDSA. + * + * \return A statically allocated array, + * terminated with MBEDTLS_ECP_DP_NONE. + */ +const mbedtls_ecp_group_id *mbedtls_ecp_grp_id_list( void ); + +/** + * \brief This function retrieves curve information from an internal + * group identifier. + * + * \param grp_id An \c MBEDTLS_ECP_DP_XXX value. + * + * \return The associated curve information on success. + * \return NULL on failure. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_grp_id( mbedtls_ecp_group_id grp_id ); + +/** + * \brief This function retrieves curve information from a TLS + * NamedCurve value. + * + * \param tls_id An \c MBEDTLS_ECP_DP_XXX value. + * + * \return The associated curve information on success. + * \return NULL on failure. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_tls_id( uint16_t tls_id ); + +/** + * \brief This function retrieves curve information from a + * human-readable name. + * + * \param name The human-readable name. + * + * \return The associated curve information on success. + * \return NULL on failure. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_name( const char *name ); + +/** + * \brief This function initializes a point as zero. + * + * \param pt The point to initialize. + */ +void mbedtls_ecp_point_init( mbedtls_ecp_point *pt ); + +/** + * \brief This function initializes an ECP group context + * without loading any domain parameters. + * + * \note After this function is called, domain parameters + * for various ECP groups can be loaded through the + * mbedtls_ecp_group_load() or mbedtls_ecp_tls_read_group() + * functions. + */ +void mbedtls_ecp_group_init( mbedtls_ecp_group *grp ); + +/** + * \brief This function initializes a key pair as an invalid one. + * + * \param key The key pair to initialize. + */ +void mbedtls_ecp_keypair_init( mbedtls_ecp_keypair *key ); + +/** + * \brief This function frees the components of a point. + * + * \param pt The point to free. + */ +void mbedtls_ecp_point_free( mbedtls_ecp_point *pt ); + +/** + * \brief This function frees the components of an ECP group. + * + * \param grp The group to free. This may be \c NULL, in which + * case this function returns immediately. If it is not + * \c NULL, it must point to an initialized ECP group. + */ +void mbedtls_ecp_group_free( mbedtls_ecp_group *grp ); + +/** + * \brief This function frees the components of a key pair. + * + * \param key The key pair to free. This may be \c NULL, in which + * case this function returns immediately. If it is not + * \c NULL, it must point to an initialized ECP key pair. + */ +void mbedtls_ecp_keypair_free( mbedtls_ecp_keypair *key ); + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Initialize a restart context. + * + * \param ctx The restart context to initialize. This must + * not be \c NULL. + */ +void mbedtls_ecp_restart_init( mbedtls_ecp_restart_ctx *ctx ); + +/** + * \brief Free the components of a restart context. + * + * \param ctx The restart context to free. This may be \c NULL, in which + * case this function returns immediately. If it is not + * \c NULL, it must point to an initialized restart context. + */ +void mbedtls_ecp_restart_free( mbedtls_ecp_restart_ctx *ctx ); +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief This function copies the contents of point \p Q into + * point \p P. + * + * \param P The destination point. This must be initialized. + * \param Q The source point. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code for other kinds of failure. + */ +int mbedtls_ecp_copy( mbedtls_ecp_point *P, const mbedtls_ecp_point *Q ); + +/** + * \brief This function copies the contents of group \p src into + * group \p dst. + * + * \param dst The destination group. This must be initialized. + * \param src The source group. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_group_copy( mbedtls_ecp_group *dst, + const mbedtls_ecp_group *src ); + +/** + * \brief This function sets a point to the point at infinity. + * + * \param pt The point to set. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_set_zero( mbedtls_ecp_point *pt ); + +/** + * \brief This function checks if a point is the point at infinity. + * + * \param pt The point to test. This must be initialized. + * + * \return \c 1 if the point is zero. + * \return \c 0 if the point is non-zero. + * \return A negative error code on failure. + */ +int mbedtls_ecp_is_zero( mbedtls_ecp_point *pt ); + +/** + * \brief This function compares two points. + * + * \note This assumes that the points are normalized. Otherwise, + * they may compare as "not equal" even if they are. + * + * \param P The first point to compare. This must be initialized. + * \param Q The second point to compare. This must be initialized. + * + * \return \c 0 if the points are equal. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the points are not equal. + */ +int mbedtls_ecp_point_cmp( const mbedtls_ecp_point *P, + const mbedtls_ecp_point *Q ); + +/** + * \brief This function imports a non-zero point from two ASCII + * strings. + * + * \param P The destination point. This must be initialized. + * \param radix The numeric base of the input. + * \param x The first affine coordinate, as a null-terminated string. + * \param y The second affine coordinate, as a null-terminated string. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_MPI_XXX error code on failure. + */ +int mbedtls_ecp_point_read_string( mbedtls_ecp_point *P, int radix, + const char *x, const char *y ); + +/** + * \brief This function exports a point into unsigned binary data. + * + * \param grp The group to which the point should belong. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param P The point to export. This must be initialized. + * \param format The point format. This must be either + * #MBEDTLS_ECP_PF_COMPRESSED or #MBEDTLS_ECP_PF_UNCOMPRESSED. + * (For groups without these formats, this parameter is + * ignored. But it still has to be either of the above + * values.) + * \param olen The address at which to store the length of + * the output in Bytes. This must not be \c NULL. + * \param buf The output buffer. This must be a writable buffer + * of length \p buflen Bytes. + * \param buflen The length of the output buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the output buffer + * is too small to hold the point. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the point format + * or the export for the given group is not implemented. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_point_write_binary( const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *P, + int format, size_t *olen, + unsigned char *buf, size_t buflen ); + +/** + * \brief This function imports a point from unsigned binary data. + * + * \note This function does not check that the point actually + * belongs to the given group, see mbedtls_ecp_check_pubkey() + * for that. + * + * \param grp The group to which the point should belong. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param P The destination context to import the point to. + * This must be initialized. + * \param buf The input buffer. This must be a readable buffer + * of length \p ilen Bytes. + * \param ilen The length of the input buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the input is invalid. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the import for the + * given group is not implemented. + */ +int mbedtls_ecp_point_read_binary( const mbedtls_ecp_group *grp, + mbedtls_ecp_point *P, + const unsigned char *buf, size_t ilen ); + +/** + * \brief This function imports a point from a TLS ECPoint record. + * + * \note On function return, \p *buf is updated to point immediately + * after the ECPoint record. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param pt The destination point. + * \param buf The address of the pointer to the start of the input buffer. + * \param len The length of the buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_MPI_XXX error code on initialization + * failure. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid. + */ +int mbedtls_ecp_tls_read_point( const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt, + const unsigned char **buf, size_t len ); + +/** + * \brief This function exports a point as a TLS ECPoint record + * defined in RFC 4492, Section 5.4. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param pt The point to be exported. This must be initialized. + * \param format The point format to use. This must be either + * #MBEDTLS_ECP_PF_COMPRESSED or #MBEDTLS_ECP_PF_UNCOMPRESSED. + * \param olen The address at which to store the length in Bytes + * of the data written. + * \param buf The target buffer. This must be a writable buffer of + * length \p blen Bytes. + * \param blen The length of the target buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the input is invalid. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the target buffer + * is too small to hold the exported point. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_write_point( const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *pt, + int format, size_t *olen, + unsigned char *buf, size_t blen ); + +/** + * \brief This function sets up an ECP group context + * from a standardized set of domain parameters. + * + * \note The index should be a value of the NamedCurve enum, + * as defined in RFC-4492: Elliptic Curve Cryptography + * (ECC) Cipher Suites for Transport Layer Security (TLS), + * usually in the form of an \c MBEDTLS_ECP_DP_XXX macro. + * + * \param grp The group context to setup. This must be initialized. + * \param id The identifier of the domain parameter set to load. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if \p id doesn't + * correspond to a known group. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_group_load( mbedtls_ecp_group *grp, mbedtls_ecp_group_id id ); + +/** + * \brief This function sets up an ECP group context from a TLS + * ECParameters record as defined in RFC 4492, Section 5.4. + * + * \note The read pointer \p buf is updated to point right after + * the ECParameters record on exit. + * + * \param grp The group context to setup. This must be initialized. + * \param buf The address of the pointer to the start of the input buffer. + * \param len The length of the input buffer \c *buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the group is not + * recognized. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_read_group( mbedtls_ecp_group *grp, + const unsigned char **buf, size_t len ); + +/** + * \brief This function extracts an elliptic curve group ID from a + * TLS ECParameters record as defined in RFC 4492, Section 5.4. + * + * \note The read pointer \p buf is updated to point right after + * the ECParameters record on exit. + * + * \param grp The address at which to store the group id. + * This must not be \c NULL. + * \param buf The address of the pointer to the start of the input buffer. + * \param len The length of the input buffer \c *buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the group is not + * recognized. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_read_group_id( mbedtls_ecp_group_id *grp, + const unsigned char **buf, + size_t len ); +/** + * \brief This function exports an elliptic curve as a TLS + * ECParameters record as defined in RFC 4492, Section 5.4. + * + * \param grp The ECP group to be exported. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param olen The address at which to store the number of Bytes written. + * This must not be \c NULL. + * \param buf The buffer to write to. This must be a writable buffer + * of length \p blen Bytes. + * \param blen The length of the output buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the output + * buffer is too small to hold the exported group. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_write_group( const mbedtls_ecp_group *grp, + size_t *olen, + unsigned char *buf, size_t blen ); + +/** + * \brief This function performs a scalar multiplication of a point + * by an integer: \p R = \p m * \p P. + * + * It is not thread-safe to use same group in multiple threads. + * + * \note To prevent timing attacks, this function + * executes the exact same sequence of base-field + * operations for any valid \p m. It avoids any if-branch or + * array index depending on the value of \p m. If also uses + * \p f_rng to randomize some intermediate results. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply. This must be initialized. + * \param P The point to multiply. This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c + * NULL if \p f_rng doesn't need a context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m is not a valid private + * key, or \p P is not a valid public key. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_mul( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief This function performs multiplication of a point by + * an integer: \p R = \p m * \p P in a restartable way. + * + * \see mbedtls_ecp_mul() + * + * \note This function does the same as \c mbedtls_ecp_mul(), but + * it can return early and restart according to the limit set + * with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply. This must be initialized. + * \param P The point to multiply. This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c + * NULL if \p f_rng doesn't need a context. + * \param rs_ctx The restart context (NULL disables restart). + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m is not a valid private + * key, or \p P is not a valid public key. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_mul_restartable( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx ); + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +/** + * \brief This function performs multiplication and addition of two + * points by integers: \p R = \p m * \p P + \p n * \p Q + * + * It is not thread-safe to use same group in multiple threads. + * + * \note In contrast to mbedtls_ecp_mul(), this function does not + * guarantee a constant execution flow and timing. + * + * \note This function is only defined for short Weierstrass curves. + * It may not be included in builds without any short + * Weierstrass curve. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply \p P. + * This must be initialized. + * \param P The point to multiply by \p m. This must be initialized. + * \param n The integer by which to multiply \p Q. + * This must be initialized. + * \param Q The point to be multiplied by \p n. + * This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m or \p n are not + * valid private keys, or \p P or \p Q are not valid public + * keys. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if \p grp does not + * designate a short Weierstrass curve. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q ); + +/** + * \brief This function performs multiplication and addition of two + * points by integers: \p R = \p m * \p P + \p n * \p Q in a + * restartable way. + * + * \see \c mbedtls_ecp_muladd() + * + * \note This function works the same as \c mbedtls_ecp_muladd(), + * but it can return early and restart according to the limit + * set with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \note This function is only defined for short Weierstrass curves. + * It may not be included in builds without any short + * Weierstrass curve. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply \p P. + * This must be initialized. + * \param P The point to multiply by \p m. This must be initialized. + * \param n The integer by which to multiply \p Q. + * This must be initialized. + * \param Q The point to be multiplied by \p n. + * This must be initialized. + * \param rs_ctx The restart context (NULL disables restart). + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m or \p n are not + * valid private keys, or \p P or \p Q are not valid public + * keys. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if \p grp does not + * designate a short Weierstrass curve. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_muladd_restartable( + mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q, + mbedtls_ecp_restart_ctx *rs_ctx ); +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +/** + * \brief This function checks that a point is a valid public key + * on this curve. + * + * It only checks that the point is non-zero, has + * valid coordinates and lies on the curve. It does not verify + * that it is indeed a multiple of \p G. This additional + * check is computationally more expensive, is not required + * by standards, and should not be necessary if the group + * used has a small cofactor. In particular, it is useless for + * the NIST groups which all have a cofactor of 1. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure, to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group the point should belong to. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param pt The point to check. This must be initialized. + * + * \return \c 0 if the point is a valid public key. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if the point is not + * a valid public key for the given curve. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_check_pubkey( const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *pt ); + +/** + * \brief This function checks that an \p mbedtls_mpi is a + * valid private key for this curve. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group the private key should belong to. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param d The integer to check. This must be initialized. + * + * \return \c 0 if the point is a valid private key. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if the point is not a valid + * private key for the given curve. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_check_privkey( const mbedtls_ecp_group *grp, + const mbedtls_mpi *d ); + +/** + * \brief This function generates a private key. + * + * \param grp The ECP group to generate a private key for. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param d The destination MPI (secret part). This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_privkey( const mbedtls_ecp_group *grp, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function generates a keypair with a configurable base + * point. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group to generate a key pair for. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param G The base point to use. This must be initialized + * and belong to \p grp. It replaces the default base + * point \c grp->G used by mbedtls_ecp_gen_keypair(). + * \param d The destination MPI (secret part). + * This must be initialized. + * \param Q The destination point (public part). + * This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp, + const mbedtls_ecp_point *G, + mbedtls_mpi *d, mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function generates an ECP keypair. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group to generate a key pair for. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param d The destination MPI (secret part). + * This must be initialized. + * \param Q The destination point (public part). + * This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_keypair( mbedtls_ecp_group *grp, mbedtls_mpi *d, + mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function generates an ECP key. + * + * \param grp_id The ECP group identifier. + * \param key The destination key. This must be initialized. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_key( mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function reads an elliptic curve private key. + * + * \param grp_id The ECP group identifier. + * \param key The destination key. + * \param buf The buffer containing the binary representation of the + * key. (Big endian integer for Weierstrass curves, byte + * string for Montgomery curves.) + * \param buflen The length of the buffer in bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY error if the key is + * invalid. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the operation for + * the group is not implemented. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_ecp_read_key( mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, + const unsigned char *buf, size_t buflen ); + +/** + * \brief This function exports an elliptic curve private key. + * + * \param key The private key. + * \param buf The output buffer for containing the binary representation + * of the key. (Big endian integer for Weierstrass curves, byte + * string for Montgomery curves.) + * \param buflen The total length of the buffer in bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the \p key + representation is larger than the available space in \p buf. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the operation for + * the group is not implemented. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_ecp_write_key( mbedtls_ecp_keypair *key, + unsigned char *buf, size_t buflen ); + +/** + * \brief This function checks that the keypair objects + * \p pub and \p prv have the same group and the + * same public point, and that the private key in + * \p prv is consistent with the public key. + * + * \param pub The keypair structure holding the public key. This + * must be initialized. If it contains a private key, that + * part is ignored. + * \param prv The keypair structure holding the full keypair. + * This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c + * NULL if \p f_rng doesn't need a context. + * + * \return \c 0 on success, meaning that the keys are valid and match. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the keys are invalid or do not match. + * \return An \c MBEDTLS_ERR_ECP_XXX or an \c MBEDTLS_ERR_MPI_XXX + * error code on calculation failure. + */ +int mbedtls_ecp_check_pub_priv( + const mbedtls_ecp_keypair *pub, const mbedtls_ecp_keypair *prv, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The ECP checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_ecp_self_test( int verbose ); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* ecp.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/error.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/error.h new file mode 100644 index 0000000..9a8690d --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/error.h @@ -0,0 +1,210 @@ +/** + * \file error.h + * + * \brief Error to string translation + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ERROR_H +#define MBEDTLS_ERROR_H + +#include "mbedtls/build_info.h" + +#include + +#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ + !defined(inline) && !defined(__cplusplus) +#define inline __inline +#endif + +/** + * Error code layout. + * + * Currently we try to keep all error codes within the negative space of 16 + * bits signed integers to support all platforms (-0x0001 - -0x7FFF). In + * addition we'd like to give two layers of information on the error if + * possible. + * + * For that purpose the error codes are segmented in the following manner: + * + * 16 bit error code bit-segmentation + * + * 1 bit - Unused (sign bit) + * 3 bits - High level module ID + * 5 bits - Module-dependent error code + * 7 bits - Low level module errors + * + * For historical reasons, low-level error codes are divided in even and odd, + * even codes were assigned first, and -1 is reserved for other errors. + * + * Low-level module errors (0x0002-0x007E, 0x0001-0x007F) + * + * Module Nr Codes assigned + * ERROR 2 0x006E 0x0001 + * MPI 7 0x0002-0x0010 + * GCM 3 0x0012-0x0014 0x0013-0x0013 + * THREADING 3 0x001A-0x001E + * AES 5 0x0020-0x0022 0x0021-0x0025 + * CAMELLIA 3 0x0024-0x0026 0x0027-0x0027 + * BASE64 2 0x002A-0x002C + * OID 1 0x002E-0x002E 0x000B-0x000B + * PADLOCK 1 0x0030-0x0030 + * DES 2 0x0032-0x0032 0x0033-0x0033 + * CTR_DBRG 4 0x0034-0x003A + * ENTROPY 3 0x003C-0x0040 0x003D-0x003F + * NET 13 0x0042-0x0052 0x0043-0x0049 + * ARIA 4 0x0058-0x005E + * ASN1 7 0x0060-0x006C + * CMAC 1 0x007A-0x007A + * PBKDF2 1 0x007C-0x007C + * HMAC_DRBG 4 0x0003-0x0009 + * CCM 3 0x000D-0x0011 + * MD5 1 0x002F-0x002F + * RIPEMD160 1 0x0031-0x0031 + * SHA1 1 0x0035-0x0035 0x0073-0x0073 + * SHA256 1 0x0037-0x0037 0x0074-0x0074 + * SHA512 1 0x0039-0x0039 0x0075-0x0075 + * CHACHA20 3 0x0051-0x0055 + * POLY1305 3 0x0057-0x005B + * CHACHAPOLY 2 0x0054-0x0056 + * PLATFORM 2 0x0070-0x0072 + * + * High-level module nr (3 bits - 0x0...-0x7...) + * Name ID Nr of Errors + * PEM 1 9 + * PKCS#12 1 4 (Started from top) + * X509 2 20 + * PKCS5 2 4 (Started from top) + * DHM 3 11 + * PK 3 15 (Started from top) + * RSA 4 11 + * ECP 4 10 (Started from top) + * MD 5 5 + * HKDF 5 1 (Started from top) + * SSL 5 2 (Started from 0x5F00) + * CIPHER 6 8 (Started from 0x6080) + * SSL 6 22 (Started from top, plus 0x6000) + * SSL 7 20 (Started from 0x7000, gaps at + * 0x7380, 0x7900-0x7980, 0x7A80-0x7E80) + * + * Module dependent error code (5 bits 0x.00.-0x.F8.) + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MBEDTLS_ERR_ERROR_GENERIC_ERROR -0x0001 /**< Generic error */ +#define MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED -0x006E /**< This is a bug in the library */ + +#define MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED -0x0070 /**< Hardware accelerator failed */ +#define MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED -0x0072 /**< The requested feature is not supported by the platform */ + +/** + * \brief Combines a high-level and low-level error code together. + * + * Wrapper macro for mbedtls_error_add(). See that function for + * more details. + */ +#define MBEDTLS_ERROR_ADD( high, low ) \ + mbedtls_error_add( high, low, __FILE__, __LINE__ ) + +#if defined(MBEDTLS_TEST_HOOKS) +/** + * \brief Testing hook called before adding/combining two error codes together. + * Only used when invasive testing is enabled via MBEDTLS_TEST_HOOKS. + */ +extern void (*mbedtls_test_hook_error_add)( int, int, const char *, int ); +#endif + +/** + * \brief Combines a high-level and low-level error code together. + * + * This function can be called directly however it is usually + * called via the #MBEDTLS_ERROR_ADD macro. + * + * While a value of zero is not a negative error code, it is still an + * error code (that denotes success) and can be combined with both a + * negative error code or another value of zero. + * + * \note When invasive testing is enabled via #MBEDTLS_TEST_HOOKS, also try to + * call \link mbedtls_test_hook_error_add \endlink. + * + * \param high high-level error code. See error.h for more details. + * \param low low-level error code. See error.h for more details. + * \param file file where this error code addition occurred. + * \param line line where this error code addition occurred. + */ +static inline int mbedtls_error_add( int high, int low, + const char *file, int line ) +{ +#if defined(MBEDTLS_TEST_HOOKS) + if( *mbedtls_test_hook_error_add != NULL ) + ( *mbedtls_test_hook_error_add )( high, low, file, line ); +#endif + (void)file; + (void)line; + + return( high + low ); +} + +/** + * \brief Translate a mbed TLS error code into a string representation, + * Result is truncated if necessary and always includes a terminating + * null byte. + * + * \param errnum error code + * \param buffer buffer to place representation in + * \param buflen length of the buffer + */ +void mbedtls_strerror( int errnum, char *buffer, size_t buflen ); + +/** + * \brief Translate the high-level part of an Mbed TLS error code into a string + * representation. + * + * This function returns a const pointer to an un-modifiable string. The caller + * must not try to modify the string. It is intended to be used mostly for + * logging purposes. + * + * \param error_code error code + * + * \return The string representation of the error code, or \c NULL if the error + * code is unknown. + */ +const char * mbedtls_high_level_strerr( int error_code ); + +/** + * \brief Translate the low-level part of an Mbed TLS error code into a string + * representation. + * + * This function returns a const pointer to an un-modifiable string. The caller + * must not try to modify the string. It is intended to be used mostly for + * logging purposes. + * + * \param error_code error code + * + * \return The string representation of the error code, or \c NULL if the error + * code is unknown. + */ +const char * mbedtls_low_level_strerr( int error_code ); + +#ifdef __cplusplus +} +#endif + +#endif /* error.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/mbedtls_config.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/mbedtls_config.h new file mode 100644 index 0000000..2fa14eb --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/mbedtls_config.h @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_CONFIG_H +#define MBEDTLS_CONFIG_H + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +//#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_FS_IO +//#define MBEDTLS_VERSION_FEATURES +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BASE64_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_PEM_PARSE_C + +/** + * \def MBEDTLS_PLATFORM_C + * + * Enable the platform abstraction layer that allows you to re-assign + * functions like calloc(), free(), snprintf(), printf(), fprintf(), exit(). + * + * Enabling MBEDTLS_PLATFORM_C enables to use of MBEDTLS_PLATFORM_XXX_ALT + * or MBEDTLS_PLATFORM_XXX_MACRO directives, allowing the functions mentioned + * above to be specified at runtime or compile time respectively. + * + * \note This abstraction layer must be enabled on Windows (including MSYS2) + * as other module rely on it for a fixed snprintf implementation. + * + * Module: library/platform.c + * Caller: Most other .c files + * + * This module enables abstraction of common (libc) functions. + */ +#define MBEDTLS_PLATFORM_C + +/** + * \def MBEDTLS_TIMING_C + * + * Enable the semi-portable timing interface. + * + * \note The provided implementation only works on POSIX/Unix (including Linux, + * BSD and OS X) and Windows. On other platforms, you can either disable that + * module and provide your own implementations of the callbacks needed by + * \c mbedtls_ssl_set_timer_cb() for DTLS, or leave it enabled and provide + * your own implementation of the whole module by setting + * \c MBEDTLS_TIMING_ALT in the current file. + * + * \note See also our Knowledge Base article about porting to a new + * environment: + * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +#define MBEDTLS_TIMING_C + +//#define MBEDTLS_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +//#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */ +//#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ + +#if defined(TARGET_LIKE_MBED) && defined(YOTTA_CFG_MBEDTLS_TARGET_CONFIG_FILE) +#include YOTTA_CFG_MBEDTLS_TARGET_CONFIG_FILE +#endif + +#if defined(YOTTA_CFG_MBEDTLS_USER_CONFIG_FILE) +#include YOTTA_CFG_MBEDTLS_USER_CONFIG_FILE +#elif defined(MBEDTLS_USER_CONFIG_FILE) +#include MBEDTLS_USER_CONFIG_FILE +#endif + +#include "check_config.h" + +#endif /* MBEDTLS_CONFIG_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/md.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/md.h new file mode 100644 index 0000000..1170bc1 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/md.h @@ -0,0 +1,446 @@ + /** + * \file md.h + * + * \brief This file contains the generic message-digest wrapper. + * + * \author Adriaan de Jong + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_MD_H +#define MBEDTLS_MD_H +#include "mbedtls/private_access.h" + +#include + +#include "mbedtls/build_info.h" + +#define MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE -0x5080 /**< The selected feature is not available. */ +#define MBEDTLS_ERR_MD_BAD_INPUT_DATA -0x5100 /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_MD_ALLOC_FAILED -0x5180 /**< Failed to allocate memory. */ +#define MBEDTLS_ERR_MD_FILE_IO_ERROR -0x5200 /**< Opening or reading of file failed. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Supported message digests. + * + * \warning MD5 and SHA-1 are considered weak message digests and + * their use constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +typedef enum { + MBEDTLS_MD_NONE=0, /**< None. */ + MBEDTLS_MD_MD5, /**< The MD5 message digest. */ + MBEDTLS_MD_SHA1, /**< The SHA-1 message digest. */ + MBEDTLS_MD_SHA224, /**< The SHA-224 message digest. */ + MBEDTLS_MD_SHA256, /**< The SHA-256 message digest. */ + MBEDTLS_MD_SHA384, /**< The SHA-384 message digest. */ + MBEDTLS_MD_SHA512, /**< The SHA-512 message digest. */ + MBEDTLS_MD_RIPEMD160, /**< The RIPEMD-160 message digest. */ +} mbedtls_md_type_t; + +#if defined(MBEDTLS_SHA512_C) +#define MBEDTLS_MD_MAX_SIZE 64 /* longest known is SHA512 */ +#else +#define MBEDTLS_MD_MAX_SIZE 32 /* longest known is SHA256 or less */ +#endif + +#if defined(MBEDTLS_SHA512_C) +#define MBEDTLS_MD_MAX_BLOCK_SIZE 128 +#else +#define MBEDTLS_MD_MAX_BLOCK_SIZE 64 +#endif + +/** + * Opaque struct. + * + * Constructed using either #mbedtls_md_info_from_string or + * #mbedtls_md_info_from_type. + * + * Fields can be accessed with #mbedtls_md_get_size, + * #mbedtls_md_get_type and #mbedtls_md_get_name. + */ +/* Defined internally in library/md_wrap.h. */ +typedef struct mbedtls_md_info_t mbedtls_md_info_t; + +/** + * The generic message-digest context. + */ +typedef struct mbedtls_md_context_t +{ + /** Information about the associated message digest. */ + const mbedtls_md_info_t *MBEDTLS_PRIVATE(md_info); + + /** The digest-specific context. */ + void *MBEDTLS_PRIVATE(md_ctx); + + /** The HMAC part of the context. */ + void *MBEDTLS_PRIVATE(hmac_ctx); +} mbedtls_md_context_t; + +/** + * \brief This function returns the list of digests supported by the + * generic digest module. + * + * \note The list starts with the strongest available hashes. + * + * \return A statically allocated array of digests. Each element + * in the returned list is an integer belonging to the + * message-digest enumeration #mbedtls_md_type_t. + * The last entry is 0. + */ +const int *mbedtls_md_list( void ); + +/** + * \brief This function returns the message-digest information + * associated with the given digest name. + * + * \param md_name The name of the digest to search for. + * + * \return The message-digest information associated with \p md_name. + * \return NULL if the associated message-digest information is not found. + */ +const mbedtls_md_info_t *mbedtls_md_info_from_string( const char *md_name ); + +/** + * \brief This function returns the message-digest information + * associated with the given digest type. + * + * \param md_type The type of digest to search for. + * + * \return The message-digest information associated with \p md_type. + * \return NULL if the associated message-digest information is not found. + */ +const mbedtls_md_info_t *mbedtls_md_info_from_type( mbedtls_md_type_t md_type ); + +/** + * \brief This function initializes a message-digest context without + * binding it to a particular message-digest algorithm. + * + * This function should always be called first. It prepares the + * context for mbedtls_md_setup() for binding it to a + * message-digest algorithm. + */ +void mbedtls_md_init( mbedtls_md_context_t *ctx ); + +/** + * \brief This function clears the internal structure of \p ctx and + * frees any embedded internal structure, but does not free + * \p ctx itself. + * + * If you have called mbedtls_md_setup() on \p ctx, you must + * call mbedtls_md_free() when you are no longer using the + * context. + * Calling this function if you have previously + * called mbedtls_md_init() and nothing else is optional. + * You must not call this function if you have not called + * mbedtls_md_init(). + */ +void mbedtls_md_free( mbedtls_md_context_t *ctx ); + + +/** + * \brief This function selects the message digest algorithm to use, + * and allocates internal structures. + * + * It should be called after mbedtls_md_init() or + * mbedtls_md_free(). Makes it necessary to call + * mbedtls_md_free() later. + * + * \param ctx The context to set up. + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param hmac Defines if HMAC is used. 0: HMAC is not used (saves some memory), + * or non-zero: HMAC is used with this context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + * \return #MBEDTLS_ERR_MD_ALLOC_FAILED on memory-allocation failure. + */ +int mbedtls_md_setup( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac ); + +/** + * \brief This function clones the state of an message-digest + * context. + * + * \note You must call mbedtls_md_setup() on \c dst before calling + * this function. + * + * \note The two contexts must have the same type, + * for example, both are SHA-256. + * + * \warning This function clones the message-digest state, not the + * HMAC state. + * + * \param dst The destination context. + * \param src The context to be cloned. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification failure. + */ +int mbedtls_md_clone( mbedtls_md_context_t *dst, + const mbedtls_md_context_t *src ); + +/** + * \brief This function extracts the message-digest size from the + * message-digest information structure. + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * + * \return The size of the message-digest output in Bytes. + */ +unsigned char mbedtls_md_get_size( const mbedtls_md_info_t *md_info ); + +/** + * \brief This function extracts the message-digest type from the + * message-digest information structure. + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * + * \return The type of the message digest. + */ +mbedtls_md_type_t mbedtls_md_get_type( const mbedtls_md_info_t *md_info ); + +/** + * \brief This function extracts the message-digest name from the + * message-digest information structure. + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * + * \return The name of the message digest. + */ +const char *mbedtls_md_get_name( const mbedtls_md_info_t *md_info ); + +/** + * \brief This function starts a message-digest computation. + * + * You must call this function after setting up the context + * with mbedtls_md_setup(), and before passing data with + * mbedtls_md_update(). + * + * \param ctx The generic message-digest context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_starts( mbedtls_md_context_t *ctx ); + +/** + * \brief This function feeds an input buffer into an ongoing + * message-digest computation. + * + * You must call mbedtls_md_starts() before calling this + * function. You may call this function multiple times. + * Afterwards, call mbedtls_md_finish(). + * + * \param ctx The generic message-digest context. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_update( mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen ); + +/** + * \brief This function finishes the digest operation, + * and writes the result to the output buffer. + * + * Call this function after a call to mbedtls_md_starts(), + * followed by any number of calls to mbedtls_md_update(). + * Afterwards, you may either clear the context with + * mbedtls_md_free(), or call mbedtls_md_starts() to reuse + * the context for another digest operation with the same + * algorithm. + * + * \param ctx The generic message-digest context. + * \param output The buffer for the generic message-digest checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_finish( mbedtls_md_context_t *ctx, unsigned char *output ); + +/** + * \brief This function calculates the message-digest of a buffer, + * with respect to a configurable message-digest algorithm + * in a single call. + * + * The result is calculated as + * Output = message_digest(input buffer). + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param input The buffer holding the data. + * \param ilen The length of the input data. + * \param output The generic message-digest checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md( const mbedtls_md_info_t *md_info, const unsigned char *input, size_t ilen, + unsigned char *output ); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief This function calculates the message-digest checksum + * result of the contents of the provided file. + * + * The result is calculated as + * Output = message_digest(file contents). + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param path The input file name. + * \param output The generic message-digest checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_FILE_IO_ERROR on an I/O error accessing + * the file pointed by \p path. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA if \p md_info was NULL. + */ +int mbedtls_md_file( const mbedtls_md_info_t *md_info, const char *path, + unsigned char *output ); +#endif /* MBEDTLS_FS_IO */ + +/** + * \brief This function sets the HMAC key and prepares to + * authenticate a new message. + * + * Call this function after mbedtls_md_setup(), to use + * the MD context for an HMAC calculation, then call + * mbedtls_md_hmac_update() to provide the input data, and + * mbedtls_md_hmac_finish() to get the HMAC value. + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * \param key The HMAC secret key. + * \param keylen The length of the HMAC key in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key, + size_t keylen ); + +/** + * \brief This function feeds an input buffer into an ongoing HMAC + * computation. + * + * Call mbedtls_md_hmac_starts() or mbedtls_md_hmac_reset() + * before calling this function. + * You may call this function multiple times to pass the + * input piecewise. + * Afterwards, call mbedtls_md_hmac_finish(). + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_hmac_update( mbedtls_md_context_t *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief This function finishes the HMAC operation, and writes + * the result to the output buffer. + * + * Call this function after mbedtls_md_hmac_starts() and + * mbedtls_md_hmac_update() to get the HMAC value. Afterwards + * you may either call mbedtls_md_free() to clear the context, + * or call mbedtls_md_hmac_reset() to reuse the context with + * the same HMAC key. + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * \param output The generic HMAC checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_hmac_finish( mbedtls_md_context_t *ctx, unsigned char *output); + +/** + * \brief This function prepares to authenticate a new message with + * the same key as the previous HMAC operation. + * + * You may call this function after mbedtls_md_hmac_finish(). + * Afterwards call mbedtls_md_hmac_update() to pass the new + * input. + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_hmac_reset( mbedtls_md_context_t *ctx ); + +/** + * \brief This function calculates the full generic HMAC + * on the input buffer with the provided key. + * + * The function allocates the context, performs the + * calculation, and frees the context. + * + * The HMAC result is calculated as + * output = generic HMAC(hmac key, input buffer). + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param key The HMAC secret key. + * \param keylen The length of the HMAC secret key in Bytes. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * \param output The generic HMAC result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +int mbedtls_md_hmac( const mbedtls_md_info_t *md_info, const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ); + +/* Internal use */ +int mbedtls_md_process( mbedtls_md_context_t *ctx, const unsigned char *data ); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_MD_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/oid.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/oid.h new file mode 100644 index 0000000..9e68e91 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/oid.h @@ -0,0 +1,641 @@ +/** + * \file oid.h + * + * \brief Object Identifier (OID) database + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_OID_H +#define MBEDTLS_OID_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/asn1.h" +#include "mbedtls/pk.h" + +#include + +#if defined(MBEDTLS_CIPHER_C) +#include "mbedtls/cipher.h" +#endif + +#if defined(MBEDTLS_MD_C) +#include "mbedtls/md.h" +#endif + +#define MBEDTLS_ERR_OID_NOT_FOUND -0x002E /**< OID is not found. */ +#define MBEDTLS_ERR_OID_BUF_TOO_SMALL -0x000B /**< output buffer is too small */ + +/* This is for the benefit of X.509, but defined here in order to avoid + * having a "backwards" include of x.509.h here */ +/* + * X.509 extension types (internal, arbitrary values for bitsets) + */ +#define MBEDTLS_OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER (1 << 0) +#define MBEDTLS_OID_X509_EXT_SUBJECT_KEY_IDENTIFIER (1 << 1) +#define MBEDTLS_OID_X509_EXT_KEY_USAGE (1 << 2) +#define MBEDTLS_OID_X509_EXT_CERTIFICATE_POLICIES (1 << 3) +#define MBEDTLS_OID_X509_EXT_POLICY_MAPPINGS (1 << 4) +#define MBEDTLS_OID_X509_EXT_SUBJECT_ALT_NAME (1 << 5) +#define MBEDTLS_OID_X509_EXT_ISSUER_ALT_NAME (1 << 6) +#define MBEDTLS_OID_X509_EXT_SUBJECT_DIRECTORY_ATTRS (1 << 7) +#define MBEDTLS_OID_X509_EXT_BASIC_CONSTRAINTS (1 << 8) +#define MBEDTLS_OID_X509_EXT_NAME_CONSTRAINTS (1 << 9) +#define MBEDTLS_OID_X509_EXT_POLICY_CONSTRAINTS (1 << 10) +#define MBEDTLS_OID_X509_EXT_EXTENDED_KEY_USAGE (1 << 11) +#define MBEDTLS_OID_X509_EXT_CRL_DISTRIBUTION_POINTS (1 << 12) +#define MBEDTLS_OID_X509_EXT_INIHIBIT_ANYPOLICY (1 << 13) +#define MBEDTLS_OID_X509_EXT_FRESHEST_CRL (1 << 14) +#define MBEDTLS_OID_X509_EXT_NS_CERT_TYPE (1 << 16) + +/* + * Top level OID tuples + */ +#define MBEDTLS_OID_ISO_MEMBER_BODIES "\x2a" /* {iso(1) member-body(2)} */ +#define MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x2b" /* {iso(1) identified-organization(3)} */ +#define MBEDTLS_OID_ISO_CCITT_DS "\x55" /* {joint-iso-ccitt(2) ds(5)} */ +#define MBEDTLS_OID_ISO_ITU_COUNTRY "\x60" /* {joint-iso-itu-t(2) country(16)} */ + +/* + * ISO Member bodies OID parts + */ +#define MBEDTLS_OID_COUNTRY_US "\x86\x48" /* {us(840)} */ +#define MBEDTLS_OID_ORG_RSA_DATA_SECURITY "\x86\xf7\x0d" /* {rsadsi(113549)} */ +#define MBEDTLS_OID_RSA_COMPANY MBEDTLS_OID_ISO_MEMBER_BODIES MBEDTLS_OID_COUNTRY_US \ + MBEDTLS_OID_ORG_RSA_DATA_SECURITY /* {iso(1) member-body(2) us(840) rsadsi(113549)} */ +#define MBEDTLS_OID_ORG_ANSI_X9_62 "\xce\x3d" /* ansi-X9-62(10045) */ +#define MBEDTLS_OID_ANSI_X9_62 MBEDTLS_OID_ISO_MEMBER_BODIES MBEDTLS_OID_COUNTRY_US \ + MBEDTLS_OID_ORG_ANSI_X9_62 + +/* + * ISO Identified organization OID parts + */ +#define MBEDTLS_OID_ORG_DOD "\x06" /* {dod(6)} */ +#define MBEDTLS_OID_ORG_OIW "\x0e" +#define MBEDTLS_OID_OIW_SECSIG MBEDTLS_OID_ORG_OIW "\x03" +#define MBEDTLS_OID_OIW_SECSIG_ALG MBEDTLS_OID_OIW_SECSIG "\x02" +#define MBEDTLS_OID_OIW_SECSIG_SHA1 MBEDTLS_OID_OIW_SECSIG_ALG "\x1a" +#define MBEDTLS_OID_ORG_CERTICOM "\x81\x04" /* certicom(132) */ +#define MBEDTLS_OID_CERTICOM MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_CERTICOM +#define MBEDTLS_OID_ORG_TELETRUST "\x24" /* teletrust(36) */ +#define MBEDTLS_OID_TELETRUST MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_TELETRUST + +/* + * ISO ITU OID parts + */ +#define MBEDTLS_OID_ORGANIZATION "\x01" /* {organization(1)} */ +#define MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ISO_ITU_COUNTRY MBEDTLS_OID_COUNTRY_US MBEDTLS_OID_ORGANIZATION /* {joint-iso-itu-t(2) country(16) us(840) organization(1)} */ + +#define MBEDTLS_OID_ORG_GOV "\x65" /* {gov(101)} */ +#define MBEDTLS_OID_GOV MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ORG_GOV /* {joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101)} */ + +#define MBEDTLS_OID_ORG_NETSCAPE "\x86\xF8\x42" /* {netscape(113730)} */ +#define MBEDTLS_OID_NETSCAPE MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ORG_NETSCAPE /* Netscape OID {joint-iso-itu-t(2) country(16) us(840) organization(1) netscape(113730)} */ + +/* ISO arc for standard certificate and CRL extensions */ +#define MBEDTLS_OID_ID_CE MBEDTLS_OID_ISO_CCITT_DS "\x1D" /**< id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} */ + +#define MBEDTLS_OID_NIST_ALG MBEDTLS_OID_GOV "\x03\x04" /** { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) */ + +/** + * Private Internet Extensions + * { iso(1) identified-organization(3) dod(6) internet(1) + * security(5) mechanisms(5) pkix(7) } + */ +#define MBEDTLS_OID_INTERNET MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_DOD "\x01" +#define MBEDTLS_OID_PKIX MBEDTLS_OID_INTERNET "\x05\x05\x07" + +/* + * Arc for standard naming attributes + */ +#define MBEDTLS_OID_AT MBEDTLS_OID_ISO_CCITT_DS "\x04" /**< id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} */ +#define MBEDTLS_OID_AT_CN MBEDTLS_OID_AT "\x03" /**< id-at-commonName AttributeType:= {id-at 3} */ +#define MBEDTLS_OID_AT_SUR_NAME MBEDTLS_OID_AT "\x04" /**< id-at-surName AttributeType:= {id-at 4} */ +#define MBEDTLS_OID_AT_SERIAL_NUMBER MBEDTLS_OID_AT "\x05" /**< id-at-serialNumber AttributeType:= {id-at 5} */ +#define MBEDTLS_OID_AT_COUNTRY MBEDTLS_OID_AT "\x06" /**< id-at-countryName AttributeType:= {id-at 6} */ +#define MBEDTLS_OID_AT_LOCALITY MBEDTLS_OID_AT "\x07" /**< id-at-locality AttributeType:= {id-at 7} */ +#define MBEDTLS_OID_AT_STATE MBEDTLS_OID_AT "\x08" /**< id-at-state AttributeType:= {id-at 8} */ +#define MBEDTLS_OID_AT_ORGANIZATION MBEDTLS_OID_AT "\x0A" /**< id-at-organizationName AttributeType:= {id-at 10} */ +#define MBEDTLS_OID_AT_ORG_UNIT MBEDTLS_OID_AT "\x0B" /**< id-at-organizationalUnitName AttributeType:= {id-at 11} */ +#define MBEDTLS_OID_AT_TITLE MBEDTLS_OID_AT "\x0C" /**< id-at-title AttributeType:= {id-at 12} */ +#define MBEDTLS_OID_AT_POSTAL_ADDRESS MBEDTLS_OID_AT "\x10" /**< id-at-postalAddress AttributeType:= {id-at 16} */ +#define MBEDTLS_OID_AT_POSTAL_CODE MBEDTLS_OID_AT "\x11" /**< id-at-postalCode AttributeType:= {id-at 17} */ +#define MBEDTLS_OID_AT_GIVEN_NAME MBEDTLS_OID_AT "\x2A" /**< id-at-givenName AttributeType:= {id-at 42} */ +#define MBEDTLS_OID_AT_INITIALS MBEDTLS_OID_AT "\x2B" /**< id-at-initials AttributeType:= {id-at 43} */ +#define MBEDTLS_OID_AT_GENERATION_QUALIFIER MBEDTLS_OID_AT "\x2C" /**< id-at-generationQualifier AttributeType:= {id-at 44} */ +#define MBEDTLS_OID_AT_UNIQUE_IDENTIFIER MBEDTLS_OID_AT "\x2D" /**< id-at-uniqueIdentifier AttributType:= {id-at 45} */ +#define MBEDTLS_OID_AT_DN_QUALIFIER MBEDTLS_OID_AT "\x2E" /**< id-at-dnQualifier AttributeType:= {id-at 46} */ +#define MBEDTLS_OID_AT_PSEUDONYM MBEDTLS_OID_AT "\x41" /**< id-at-pseudonym AttributeType:= {id-at 65} */ + +#define MBEDTLS_OID_UID "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x01" /** id-domainComponent AttributeType:= {itu-t(0) data(9) pss(2342) ucl(19200300) pilot(100) pilotAttributeType(1) uid(1)} */ +#define MBEDTLS_OID_DOMAIN_COMPONENT "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19" /** id-domainComponent AttributeType:= {itu-t(0) data(9) pss(2342) ucl(19200300) pilot(100) pilotAttributeType(1) domainComponent(25)} */ + +/* + * OIDs for standard certificate extensions + */ +#define MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER MBEDTLS_OID_ID_CE "\x23" /**< id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } */ +#define MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER MBEDTLS_OID_ID_CE "\x0E" /**< id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } */ +#define MBEDTLS_OID_KEY_USAGE MBEDTLS_OID_ID_CE "\x0F" /**< id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } */ +#define MBEDTLS_OID_CERTIFICATE_POLICIES MBEDTLS_OID_ID_CE "\x20" /**< id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } */ +#define MBEDTLS_OID_POLICY_MAPPINGS MBEDTLS_OID_ID_CE "\x21" /**< id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } */ +#define MBEDTLS_OID_SUBJECT_ALT_NAME MBEDTLS_OID_ID_CE "\x11" /**< id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } */ +#define MBEDTLS_OID_ISSUER_ALT_NAME MBEDTLS_OID_ID_CE "\x12" /**< id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } */ +#define MBEDTLS_OID_SUBJECT_DIRECTORY_ATTRS MBEDTLS_OID_ID_CE "\x09" /**< id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } */ +#define MBEDTLS_OID_BASIC_CONSTRAINTS MBEDTLS_OID_ID_CE "\x13" /**< id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } */ +#define MBEDTLS_OID_NAME_CONSTRAINTS MBEDTLS_OID_ID_CE "\x1E" /**< id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } */ +#define MBEDTLS_OID_POLICY_CONSTRAINTS MBEDTLS_OID_ID_CE "\x24" /**< id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } */ +#define MBEDTLS_OID_EXTENDED_KEY_USAGE MBEDTLS_OID_ID_CE "\x25" /**< id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } */ +#define MBEDTLS_OID_CRL_DISTRIBUTION_POINTS MBEDTLS_OID_ID_CE "\x1F" /**< id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 } */ +#define MBEDTLS_OID_INIHIBIT_ANYPOLICY MBEDTLS_OID_ID_CE "\x36" /**< id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } */ +#define MBEDTLS_OID_FRESHEST_CRL MBEDTLS_OID_ID_CE "\x2E" /**< id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 } */ + +/* + * Certificate policies + */ +#define MBEDTLS_OID_ANY_POLICY MBEDTLS_OID_CERTIFICATE_POLICIES "\x00" /**< anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 } */ + +/* + * Netscape certificate extensions + */ +#define MBEDTLS_OID_NS_CERT MBEDTLS_OID_NETSCAPE "\x01" +#define MBEDTLS_OID_NS_CERT_TYPE MBEDTLS_OID_NS_CERT "\x01" +#define MBEDTLS_OID_NS_BASE_URL MBEDTLS_OID_NS_CERT "\x02" +#define MBEDTLS_OID_NS_REVOCATION_URL MBEDTLS_OID_NS_CERT "\x03" +#define MBEDTLS_OID_NS_CA_REVOCATION_URL MBEDTLS_OID_NS_CERT "\x04" +#define MBEDTLS_OID_NS_RENEWAL_URL MBEDTLS_OID_NS_CERT "\x07" +#define MBEDTLS_OID_NS_CA_POLICY_URL MBEDTLS_OID_NS_CERT "\x08" +#define MBEDTLS_OID_NS_SSL_SERVER_NAME MBEDTLS_OID_NS_CERT "\x0C" +#define MBEDTLS_OID_NS_COMMENT MBEDTLS_OID_NS_CERT "\x0D" +#define MBEDTLS_OID_NS_DATA_TYPE MBEDTLS_OID_NETSCAPE "\x02" +#define MBEDTLS_OID_NS_CERT_SEQUENCE MBEDTLS_OID_NS_DATA_TYPE "\x05" + +/* + * OIDs for CRL extensions + */ +#define MBEDTLS_OID_PRIVATE_KEY_USAGE_PERIOD MBEDTLS_OID_ID_CE "\x10" +#define MBEDTLS_OID_CRL_NUMBER MBEDTLS_OID_ID_CE "\x14" /**< id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } */ + +/* + * X.509 v3 Extended key usage OIDs + */ +#define MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE MBEDTLS_OID_EXTENDED_KEY_USAGE "\x00" /**< anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } */ + +#define MBEDTLS_OID_KP MBEDTLS_OID_PKIX "\x03" /**< id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } */ +#define MBEDTLS_OID_SERVER_AUTH MBEDTLS_OID_KP "\x01" /**< id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } */ +#define MBEDTLS_OID_CLIENT_AUTH MBEDTLS_OID_KP "\x02" /**< id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } */ +#define MBEDTLS_OID_CODE_SIGNING MBEDTLS_OID_KP "\x03" /**< id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } */ +#define MBEDTLS_OID_EMAIL_PROTECTION MBEDTLS_OID_KP "\x04" /**< id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } */ +#define MBEDTLS_OID_TIME_STAMPING MBEDTLS_OID_KP "\x08" /**< id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } */ +#define MBEDTLS_OID_OCSP_SIGNING MBEDTLS_OID_KP "\x09" /**< id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } */ + +/** + * Wi-SUN Alliance Field Area Network + * { iso(1) identified-organization(3) dod(6) internet(1) + * private(4) enterprise(1) WiSUN(45605) FieldAreaNetwork(1) } + */ +#define MBEDTLS_OID_WISUN_FAN MBEDTLS_OID_INTERNET "\x04\x01\x82\xe4\x25\x01" + +#define MBEDTLS_OID_ON MBEDTLS_OID_PKIX "\x08" /**< id-on OBJECT IDENTIFIER ::= { id-pkix 8 } */ +#define MBEDTLS_OID_ON_HW_MODULE_NAME MBEDTLS_OID_ON "\x04" /**< id-on-hardwareModuleName OBJECT IDENTIFIER ::= { id-on 4 } */ + +/* + * PKCS definition OIDs + */ + +#define MBEDTLS_OID_PKCS MBEDTLS_OID_RSA_COMPANY "\x01" /**< pkcs OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) 1 } */ +#define MBEDTLS_OID_PKCS1 MBEDTLS_OID_PKCS "\x01" /**< pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } */ +#define MBEDTLS_OID_PKCS5 MBEDTLS_OID_PKCS "\x05" /**< pkcs-5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } */ +#define MBEDTLS_OID_PKCS9 MBEDTLS_OID_PKCS "\x09" /**< pkcs-9 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } */ +#define MBEDTLS_OID_PKCS12 MBEDTLS_OID_PKCS "\x0c" /**< pkcs-12 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } */ + +/* + * PKCS#1 OIDs + */ +#define MBEDTLS_OID_PKCS1_RSA MBEDTLS_OID_PKCS1 "\x01" /**< rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } */ +#define MBEDTLS_OID_PKCS1_MD5 MBEDTLS_OID_PKCS1 "\x04" /**< md5WithRSAEncryption ::= { pkcs-1 4 } */ +#define MBEDTLS_OID_PKCS1_SHA1 MBEDTLS_OID_PKCS1 "\x05" /**< sha1WithRSAEncryption ::= { pkcs-1 5 } */ +#define MBEDTLS_OID_PKCS1_SHA224 MBEDTLS_OID_PKCS1 "\x0e" /**< sha224WithRSAEncryption ::= { pkcs-1 14 } */ +#define MBEDTLS_OID_PKCS1_SHA256 MBEDTLS_OID_PKCS1 "\x0b" /**< sha256WithRSAEncryption ::= { pkcs-1 11 } */ +#define MBEDTLS_OID_PKCS1_SHA384 MBEDTLS_OID_PKCS1 "\x0c" /**< sha384WithRSAEncryption ::= { pkcs-1 12 } */ +#define MBEDTLS_OID_PKCS1_SHA512 MBEDTLS_OID_PKCS1 "\x0d" /**< sha512WithRSAEncryption ::= { pkcs-1 13 } */ + +#define MBEDTLS_OID_RSA_SHA_OBS "\x2B\x0E\x03\x02\x1D" + +#define MBEDTLS_OID_PKCS9_EMAIL MBEDTLS_OID_PKCS9 "\x01" /**< emailAddress AttributeType ::= { pkcs-9 1 } */ + +/* RFC 4055 */ +#define MBEDTLS_OID_RSASSA_PSS MBEDTLS_OID_PKCS1 "\x0a" /**< id-RSASSA-PSS ::= { pkcs-1 10 } */ +#define MBEDTLS_OID_MGF1 MBEDTLS_OID_PKCS1 "\x08" /**< id-mgf1 ::= { pkcs-1 8 } */ + +/* + * Digest algorithms + */ +#define MBEDTLS_OID_DIGEST_ALG_MD5 MBEDTLS_OID_RSA_COMPANY "\x02\x05" /**< id-mbedtls_md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA1 MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_OIW_SECSIG_SHA1 /**< id-mbedtls_sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA224 MBEDTLS_OID_NIST_ALG "\x02\x04" /**< id-sha224 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 4 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA256 MBEDTLS_OID_NIST_ALG "\x02\x01" /**< id-mbedtls_sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 } */ + +#define MBEDTLS_OID_DIGEST_ALG_SHA384 MBEDTLS_OID_NIST_ALG "\x02\x02" /**< id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2 } */ + +#define MBEDTLS_OID_DIGEST_ALG_SHA512 MBEDTLS_OID_NIST_ALG "\x02\x03" /**< id-mbedtls_sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 } */ + +#define MBEDTLS_OID_DIGEST_ALG_RIPEMD160 MBEDTLS_OID_TELETRUST "\x03\x02\x01" /**< id-ripemd160 OBJECT IDENTIFIER :: { iso(1) identified-organization(3) teletrust(36) algorithm(3) hashAlgorithm(2) ripemd160(1) } */ + +#define MBEDTLS_OID_HMAC_SHA1 MBEDTLS_OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */ + +#define MBEDTLS_OID_HMAC_SHA224 MBEDTLS_OID_RSA_COMPANY "\x02\x08" /**< id-hmacWithSHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 8 } */ + +#define MBEDTLS_OID_HMAC_SHA256 MBEDTLS_OID_RSA_COMPANY "\x02\x09" /**< id-hmacWithSHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 9 } */ + +#define MBEDTLS_OID_HMAC_SHA384 MBEDTLS_OID_RSA_COMPANY "\x02\x0A" /**< id-hmacWithSHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 10 } */ + +#define MBEDTLS_OID_HMAC_SHA512 MBEDTLS_OID_RSA_COMPANY "\x02\x0B" /**< id-hmacWithSHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 11 } */ + +/* + * Encryption algorithms + */ +#define MBEDTLS_OID_DES_CBC MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_OIW_SECSIG_ALG "\x07" /**< desCBC OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 7 } */ +#define MBEDTLS_OID_DES_EDE3_CBC MBEDTLS_OID_RSA_COMPANY "\x03\x07" /**< des-ede3-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) -- us(840) rsadsi(113549) encryptionAlgorithm(3) 7 } */ +#define MBEDTLS_OID_AES MBEDTLS_OID_NIST_ALG "\x01" /** aes OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) 1 } */ + +/* + * Key Wrapping algorithms + */ +/* + * RFC 5649 + */ +#define MBEDTLS_OID_AES128_KW MBEDTLS_OID_AES "\x05" /** id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } */ +#define MBEDTLS_OID_AES128_KWP MBEDTLS_OID_AES "\x08" /** id-aes128-wrap-pad OBJECT IDENTIFIER ::= { aes 8 } */ +#define MBEDTLS_OID_AES192_KW MBEDTLS_OID_AES "\x19" /** id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } */ +#define MBEDTLS_OID_AES192_KWP MBEDTLS_OID_AES "\x1c" /** id-aes192-wrap-pad OBJECT IDENTIFIER ::= { aes 28 } */ +#define MBEDTLS_OID_AES256_KW MBEDTLS_OID_AES "\x2d" /** id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } */ +#define MBEDTLS_OID_AES256_KWP MBEDTLS_OID_AES "\x30" /** id-aes256-wrap-pad OBJECT IDENTIFIER ::= { aes 48 } */ +/* + * PKCS#5 OIDs + */ +#define MBEDTLS_OID_PKCS5_PBKDF2 MBEDTLS_OID_PKCS5 "\x0c" /**< id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} */ +#define MBEDTLS_OID_PKCS5_PBES2 MBEDTLS_OID_PKCS5 "\x0d" /**< id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} */ +#define MBEDTLS_OID_PKCS5_PBMAC1 MBEDTLS_OID_PKCS5 "\x0e" /**< id-PBMAC1 OBJECT IDENTIFIER ::= {pkcs-5 14} */ + +/* + * PKCS#5 PBES1 algorithms + */ +#define MBEDTLS_OID_PKCS5_PBE_MD5_DES_CBC MBEDTLS_OID_PKCS5 "\x03" /**< pbeWithMD5AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 3} */ +#define MBEDTLS_OID_PKCS5_PBE_MD5_RC2_CBC MBEDTLS_OID_PKCS5 "\x06" /**< pbeWithMD5AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 6} */ +#define MBEDTLS_OID_PKCS5_PBE_SHA1_DES_CBC MBEDTLS_OID_PKCS5 "\x0a" /**< pbeWithSHA1AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 10} */ +#define MBEDTLS_OID_PKCS5_PBE_SHA1_RC2_CBC MBEDTLS_OID_PKCS5 "\x0b" /**< pbeWithSHA1AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 11} */ + +/* + * PKCS#8 OIDs + */ +#define MBEDTLS_OID_PKCS9_CSR_EXT_REQ MBEDTLS_OID_PKCS9 "\x0e" /**< extensionRequest OBJECT IDENTIFIER ::= {pkcs-9 14} */ + +/* + * PKCS#12 PBE OIDs + */ +#define MBEDTLS_OID_PKCS12_PBE MBEDTLS_OID_PKCS12 "\x01" /**< pkcs-12PbeIds OBJECT IDENTIFIER ::= {pkcs-12 1} */ + +#define MBEDTLS_OID_PKCS12_PBE_SHA1_DES3_EDE_CBC MBEDTLS_OID_PKCS12_PBE "\x03" /**< pbeWithSHAAnd3-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 3} */ +#define MBEDTLS_OID_PKCS12_PBE_SHA1_DES2_EDE_CBC MBEDTLS_OID_PKCS12_PBE "\x04" /**< pbeWithSHAAnd2-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 4} */ +#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC2_128_CBC MBEDTLS_OID_PKCS12_PBE "\x05" /**< pbeWithSHAAnd128BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 5} */ +#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC2_40_CBC MBEDTLS_OID_PKCS12_PBE "\x06" /**< pbeWithSHAAnd40BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 6} */ + +/* + * EC key algorithms from RFC 5480 + */ + +/* id-ecPublicKey OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } */ +#define MBEDTLS_OID_EC_ALG_UNRESTRICTED MBEDTLS_OID_ANSI_X9_62 "\x02\01" + +/* id-ecDH OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) + * schemes(1) ecdh(12) } */ +#define MBEDTLS_OID_EC_ALG_ECDH MBEDTLS_OID_CERTICOM "\x01\x0c" + +/* + * ECParameters namedCurve identifiers, from RFC 5480, RFC 5639, and SEC2 + */ + +/* secp192r1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 1 } */ +#define MBEDTLS_OID_EC_GRP_SECP192R1 MBEDTLS_OID_ANSI_X9_62 "\x03\x01\x01" + +/* secp224r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 33 } */ +#define MBEDTLS_OID_EC_GRP_SECP224R1 MBEDTLS_OID_CERTICOM "\x00\x21" + +/* secp256r1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 7 } */ +#define MBEDTLS_OID_EC_GRP_SECP256R1 MBEDTLS_OID_ANSI_X9_62 "\x03\x01\x07" + +/* secp384r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 34 } */ +#define MBEDTLS_OID_EC_GRP_SECP384R1 MBEDTLS_OID_CERTICOM "\x00\x22" + +/* secp521r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 35 } */ +#define MBEDTLS_OID_EC_GRP_SECP521R1 MBEDTLS_OID_CERTICOM "\x00\x23" + +/* secp192k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 31 } */ +#define MBEDTLS_OID_EC_GRP_SECP192K1 MBEDTLS_OID_CERTICOM "\x00\x1f" + +/* secp224k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 32 } */ +#define MBEDTLS_OID_EC_GRP_SECP224K1 MBEDTLS_OID_CERTICOM "\x00\x20" + +/* secp256k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 10 } */ +#define MBEDTLS_OID_EC_GRP_SECP256K1 MBEDTLS_OID_CERTICOM "\x00\x0a" + +/* RFC 5639 4.1 + * ecStdCurvesAndGeneration OBJECT IDENTIFIER::= {iso(1) + * identified-organization(3) teletrust(36) algorithm(3) signature- + * algorithm(3) ecSign(2) 8} + * ellipticCurve OBJECT IDENTIFIER ::= {ecStdCurvesAndGeneration 1} + * versionOne OBJECT IDENTIFIER ::= {ellipticCurve 1} */ +#define MBEDTLS_OID_EC_BRAINPOOL_V1 MBEDTLS_OID_TELETRUST "\x03\x03\x02\x08\x01\x01" + +/* brainpoolP256r1 OBJECT IDENTIFIER ::= {versionOne 7} */ +#define MBEDTLS_OID_EC_GRP_BP256R1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x07" + +/* brainpoolP384r1 OBJECT IDENTIFIER ::= {versionOne 11} */ +#define MBEDTLS_OID_EC_GRP_BP384R1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x0B" + +/* brainpoolP512r1 OBJECT IDENTIFIER ::= {versionOne 13} */ +#define MBEDTLS_OID_EC_GRP_BP512R1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x0D" + +/* + * SEC1 C.1 + * + * prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } + * id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1)} + */ +#define MBEDTLS_OID_ANSI_X9_62_FIELD_TYPE MBEDTLS_OID_ANSI_X9_62 "\x01" +#define MBEDTLS_OID_ANSI_X9_62_PRIME_FIELD MBEDTLS_OID_ANSI_X9_62_FIELD_TYPE "\x01" + +/* + * ECDSA signature identifiers, from RFC 5480 + */ +#define MBEDTLS_OID_ANSI_X9_62_SIG MBEDTLS_OID_ANSI_X9_62 "\x04" /* signatures(4) */ +#define MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 MBEDTLS_OID_ANSI_X9_62_SIG "\x03" /* ecdsa-with-SHA2(3) */ + +/* ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) 1 } */ +#define MBEDTLS_OID_ECDSA_SHA1 MBEDTLS_OID_ANSI_X9_62_SIG "\x01" + +/* ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 1 } */ +#define MBEDTLS_OID_ECDSA_SHA224 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x01" + +/* ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 2 } */ +#define MBEDTLS_OID_ECDSA_SHA256 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x02" + +/* ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 3 } */ +#define MBEDTLS_OID_ECDSA_SHA384 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x03" + +/* ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 4 } */ +#define MBEDTLS_OID_ECDSA_SHA512 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x04" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Base OID descriptor structure + */ +typedef struct mbedtls_oid_descriptor_t +{ + const char *MBEDTLS_PRIVATE(asn1); /*!< OID ASN.1 representation */ + size_t MBEDTLS_PRIVATE(asn1_len); /*!< length of asn1 */ +#if !defined(MBEDTLS_X509_REMOVE_INFO) + const char *MBEDTLS_PRIVATE(name); /*!< official name (e.g. from RFC) */ + const char *MBEDTLS_PRIVATE(description); /*!< human friendly description */ +#endif +} mbedtls_oid_descriptor_t; + +/** + * \brief Translate an ASN.1 OID into its numeric representation + * (e.g. "\x2A\x86\x48\x86\xF7\x0D" into "1.2.840.113549") + * + * \param buf buffer to put representation in + * \param size size of the buffer + * \param oid OID to translate + * + * \return Length of the string written (excluding final NULL) or + * MBEDTLS_ERR_OID_BUF_TOO_SMALL in case of error + */ +int mbedtls_oid_get_numeric_string( char *buf, size_t size, const mbedtls_asn1_buf *oid ); + +/** + * \brief Translate an X.509 extension OID into local values + * + * \param oid OID to use + * \param ext_type place to store the extension type + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_x509_ext_type( const mbedtls_asn1_buf *oid, int *ext_type ); + +/** + * \brief Translate an X.509 attribute type OID into the short name + * (e.g. the OID for an X520 Common Name into "CN") + * + * \param oid OID to use + * \param short_name place to store the string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_attr_short_name( const mbedtls_asn1_buf *oid, const char **short_name ); + +/** + * \brief Translate PublicKeyAlgorithm OID into pk_type + * + * \param oid OID to use + * \param pk_alg place to store public key algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_pk_alg( const mbedtls_asn1_buf *oid, mbedtls_pk_type_t *pk_alg ); + +/** + * \brief Translate pk_type into PublicKeyAlgorithm OID + * + * \param pk_alg Public key type to look for + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_pk_alg( mbedtls_pk_type_t pk_alg, + const char **oid, size_t *olen ); + +#if defined(MBEDTLS_ECP_C) +/** + * \brief Translate NamedCurve OID into an EC group identifier + * + * \param oid OID to use + * \param grp_id place to store group id + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_ec_grp( const mbedtls_asn1_buf *oid, mbedtls_ecp_group_id *grp_id ); + +/** + * \brief Translate EC group identifier into NamedCurve OID + * + * \param grp_id EC group identifier + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_ec_grp( mbedtls_ecp_group_id grp_id, + const char **oid, size_t *olen ); +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_MD_C) +/** + * \brief Translate SignatureAlgorithm OID into md_type and pk_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * \param pk_alg place to store public key algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_sig_alg( const mbedtls_asn1_buf *oid, + mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg ); + +/** + * \brief Translate SignatureAlgorithm OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_sig_alg_desc( const mbedtls_asn1_buf *oid, const char **desc ); + +/** + * \brief Translate md_type and pk_type into SignatureAlgorithm OID + * + * \param md_alg message digest algorithm + * \param pk_alg public key algorithm + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_sig_alg( mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg, + const char **oid, size_t *olen ); + +/** + * \brief Translate hash algorithm OID into md_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_md_alg( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg ); + +/** + * \brief Translate hmac algorithm OID into md_type + * + * \param oid OID to use + * \param md_hmac place to store message hmac algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_md_hmac( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_hmac ); +#endif /* MBEDTLS_MD_C */ + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/** + * \brief Translate Extended Key Usage OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_extended_key_usage( const mbedtls_asn1_buf *oid, const char **desc ); +#endif + +/** + * \brief Translate certificate policies OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_certificate_policies( const mbedtls_asn1_buf *oid, const char **desc ); + +/** + * \brief Translate md_type into hash algorithm OID + * + * \param md_alg message digest algorithm + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_md( mbedtls_md_type_t md_alg, const char **oid, size_t *olen ); + +#if defined(MBEDTLS_CIPHER_C) +/** + * \brief Translate encryption algorithm OID into cipher_type + * + * \param oid OID to use + * \param cipher_alg place to store cipher algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_cipher_alg( const mbedtls_asn1_buf *oid, mbedtls_cipher_type_t *cipher_alg ); +#endif /* MBEDTLS_CIPHER_C */ + +#if defined(MBEDTLS_PKCS12_C) +/** + * \brief Translate PKCS#12 PBE algorithm OID into md_type and + * cipher_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * \param cipher_alg place to store cipher algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_pkcs12_pbe_alg( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg, + mbedtls_cipher_type_t *cipher_alg ); +#endif /* MBEDTLS_PKCS12_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* oid.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/pk.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/pk.h new file mode 100644 index 0000000..f3553cc --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/pk.h @@ -0,0 +1,890 @@ +/** + * \file pk.h + * + * \brief Public Key abstraction layer + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_PK_H +#define MBEDTLS_PK_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/md.h" + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif + +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif + +#if defined(MBEDTLS_ECDSA_C) +#include "mbedtls/ecdsa.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#endif + +#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ + !defined(inline) && !defined(__cplusplus) +#define inline __inline +#endif + +#define MBEDTLS_ERR_PK_ALLOC_FAILED -0x3F80 /**< Memory allocation failed. */ +#define MBEDTLS_ERR_PK_TYPE_MISMATCH -0x3F00 /**< Type mismatch, eg attempt to encrypt with an ECDSA key */ +#define MBEDTLS_ERR_PK_BAD_INPUT_DATA -0x3E80 /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_PK_FILE_IO_ERROR -0x3E00 /**< Read/write of file failed. */ +#define MBEDTLS_ERR_PK_KEY_INVALID_VERSION -0x3D80 /**< Unsupported key version */ +#define MBEDTLS_ERR_PK_KEY_INVALID_FORMAT -0x3D00 /**< Invalid key tag or value. */ +#define MBEDTLS_ERR_PK_UNKNOWN_PK_ALG -0x3C80 /**< Key algorithm is unsupported (only RSA and EC are supported). */ +#define MBEDTLS_ERR_PK_PASSWORD_REQUIRED -0x3C00 /**< Private key password can't be empty. */ +#define MBEDTLS_ERR_PK_PASSWORD_MISMATCH -0x3B80 /**< Given private key password does not allow for correct decryption. */ +#define MBEDTLS_ERR_PK_INVALID_PUBKEY -0x3B00 /**< The pubkey tag or value is invalid (only RSA and EC are supported). */ +#define MBEDTLS_ERR_PK_INVALID_ALG -0x3A80 /**< The algorithm tag or value is invalid. */ +#define MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE -0x3A00 /**< Elliptic curve is unsupported (only NIST curves are supported). */ +#define MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE -0x3980 /**< Unavailable feature, e.g. RSA disabled for RSA key. */ +#define MBEDTLS_ERR_PK_SIG_LEN_MISMATCH -0x3900 /**< The buffer contains a valid signature followed by more data. */ +#define MBEDTLS_ERR_PK_BUFFER_TOO_SMALL -0x3880 /**< The output buffer is too small. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Public key types + */ +typedef enum { + MBEDTLS_PK_NONE=0, + MBEDTLS_PK_RSA, + MBEDTLS_PK_ECKEY, + MBEDTLS_PK_ECKEY_DH, + MBEDTLS_PK_ECDSA, + MBEDTLS_PK_RSA_ALT, + MBEDTLS_PK_RSASSA_PSS, + MBEDTLS_PK_OPAQUE, +} mbedtls_pk_type_t; + +/** + * \brief Options for RSASSA-PSS signature verification. + * See \c mbedtls_rsa_rsassa_pss_verify_ext() + */ +typedef struct mbedtls_pk_rsassa_pss_options +{ + mbedtls_md_type_t MBEDTLS_PRIVATE(mgf1_hash_id); + int MBEDTLS_PRIVATE(expected_salt_len); + +} mbedtls_pk_rsassa_pss_options; + +/** + * \brief Maximum size of a signature made by mbedtls_pk_sign(). + */ +/* We need to set MBEDTLS_PK_SIGNATURE_MAX_SIZE to the maximum signature + * size among the supported signature types. Do it by starting at 0, + * then incrementally increasing to be large enough for each supported + * signature mechanism. + * + * The resulting value can be 0, for example if MBEDTLS_ECDH_C is enabled + * (which allows the pk module to be included) but neither MBEDTLS_ECDSA_C + * nor MBEDTLS_RSA_C nor any opaque signature mechanism (PSA or RSA_ALT). + */ +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE 0 + +#if ( defined(MBEDTLS_RSA_C) || defined(MBEDTLS_PK_RSA_ALT_SUPPORT) ) && \ + MBEDTLS_MPI_MAX_SIZE > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* For RSA, the signature can be as large as the bignum module allows. + * For RSA_ALT, the signature size is not necessarily tied to what the + * bignum module can do, but in the absence of any specific setting, + * we use that (rsa_alt_sign_wrap in library/pk_wrap.h will check). */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE +#endif + +#if defined(MBEDTLS_ECDSA_C) && \ + MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* For ECDSA, the ecdsa module exports a constant for the maximum + * signature size. */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if PSA_SIGNATURE_MAX_SIZE > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* PSA_SIGNATURE_MAX_SIZE is the maximum size of a signature made + * through the PSA API in the PSA representation. */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE PSA_SIGNATURE_MAX_SIZE +#endif + +#if PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE + 11 > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* The Mbed TLS representation is different for ECDSA signatures: + * PSA uses the raw concatenation of r and s, + * whereas Mbed TLS uses the ASN.1 representation (SEQUENCE of two INTEGERs). + * Add the overhead of ASN.1: up to (1+2) + 2 * (1+2+1) for the + * types, lengths (represented by up to 2 bytes), and potential leading + * zeros of the INTEGERs and the SEQUENCE. */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE ( PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE + 11 ) +#endif +#endif /* defined(MBEDTLS_USE_PSA_CRYPTO) */ + +/** + * \brief Types for interfacing with the debug module + */ +typedef enum +{ + MBEDTLS_PK_DEBUG_NONE = 0, + MBEDTLS_PK_DEBUG_MPI, + MBEDTLS_PK_DEBUG_ECP, +} mbedtls_pk_debug_type; + +/** + * \brief Item to send to the debug module + */ +typedef struct mbedtls_pk_debug_item +{ + mbedtls_pk_debug_type MBEDTLS_PRIVATE(type); + const char *MBEDTLS_PRIVATE(name); + void *MBEDTLS_PRIVATE(value); +} mbedtls_pk_debug_item; + +/** Maximum number of item send for debugging, plus 1 */ +#define MBEDTLS_PK_DEBUG_MAX_ITEMS 3 + +/** + * \brief Public key information and operations + */ +typedef struct mbedtls_pk_info_t mbedtls_pk_info_t; + +/** + * \brief Public key container + */ +typedef struct mbedtls_pk_context +{ + const mbedtls_pk_info_t * MBEDTLS_PRIVATE(pk_info); /**< Public key information */ + void * MBEDTLS_PRIVATE(pk_ctx); /**< Underlying public key context */ +} mbedtls_pk_context; + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Context for resuming operations + */ +typedef struct +{ + const mbedtls_pk_info_t * MBEDTLS_PRIVATE(pk_info); /**< Public key information */ + void * MBEDTLS_PRIVATE(rs_ctx); /**< Underlying restart context */ +} mbedtls_pk_restart_ctx; +#else /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ +/* Now we can declare functions that take a pointer to that */ +typedef void mbedtls_pk_restart_ctx; +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +#if defined(MBEDTLS_RSA_C) +/** + * Quick access to an RSA context inside a PK context. + * + * \warning You must make sure the PK context actually holds an RSA context + * before using this function! + */ +static inline mbedtls_rsa_context *mbedtls_pk_rsa( const mbedtls_pk_context pk ) +{ + return( (mbedtls_rsa_context *) (pk).MBEDTLS_PRIVATE(pk_ctx) ); +} +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) +/** + * Quick access to an EC context inside a PK context. + * + * \warning You must make sure the PK context actually holds an EC context + * before using this function! + */ +static inline mbedtls_ecp_keypair *mbedtls_pk_ec( const mbedtls_pk_context pk ) +{ + return( (mbedtls_ecp_keypair *) (pk).MBEDTLS_PRIVATE(pk_ctx) ); +} +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +/** + * \brief Types for RSA-alt abstraction + */ +typedef int (*mbedtls_pk_rsa_alt_decrypt_func)( void *ctx, size_t *olen, + const unsigned char *input, unsigned char *output, + size_t output_max_len ); +typedef int (*mbedtls_pk_rsa_alt_sign_func)( void *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_md_type_t md_alg, unsigned int hashlen, + const unsigned char *hash, unsigned char *sig ); +typedef size_t (*mbedtls_pk_rsa_alt_key_len_func)( void *ctx ); +#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */ + +/** + * \brief Return information associated with the given PK type + * + * \param pk_type PK type to search for. + * + * \return The PK info associated with the type or NULL if not found. + */ +const mbedtls_pk_info_t *mbedtls_pk_info_from_type( mbedtls_pk_type_t pk_type ); + +/** + * \brief Initialize a #mbedtls_pk_context (as NONE). + * + * \param ctx The context to initialize. + * This must not be \c NULL. + */ +void mbedtls_pk_init( mbedtls_pk_context *ctx ); + +/** + * \brief Free the components of a #mbedtls_pk_context. + * + * \param ctx The context to clear. It must have been initialized. + * If this is \c NULL, this function does nothing. + * + * \note For contexts that have been set up with + * mbedtls_pk_setup_opaque(), this does not free the underlying + * PSA key and you still need to call psa_destroy_key() + * independently if you want to destroy that key. + */ +void mbedtls_pk_free( mbedtls_pk_context *ctx ); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Initialize a restart context + * + * \param ctx The context to initialize. + * This must not be \c NULL. + */ +void mbedtls_pk_restart_init( mbedtls_pk_restart_ctx *ctx ); + +/** + * \brief Free the components of a restart context + * + * \param ctx The context to clear. It must have been initialized. + * If this is \c NULL, this function does nothing. + */ +void mbedtls_pk_restart_free( mbedtls_pk_restart_ctx *ctx ); +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief Initialize a PK context with the information given + * and allocates the type-specific PK subcontext. + * + * \param ctx Context to initialize. It must not have been set + * up yet (type #MBEDTLS_PK_NONE). + * \param info Information to use + * + * \return 0 on success, + * MBEDTLS_ERR_PK_BAD_INPUT_DATA on invalid input, + * MBEDTLS_ERR_PK_ALLOC_FAILED on allocation failure. + * + * \note For contexts holding an RSA-alt key, use + * \c mbedtls_pk_setup_rsa_alt() instead. + */ +int mbedtls_pk_setup( mbedtls_pk_context *ctx, const mbedtls_pk_info_t *info ); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * \brief Initialize a PK context to wrap a PSA key. + * + * \note This function replaces mbedtls_pk_setup() for contexts + * that wrap a (possibly opaque) PSA key instead of + * storing and manipulating the key material directly. + * + * \param ctx The context to initialize. It must be empty (type NONE). + * \param key The PSA key to wrap, which must hold an ECC key pair + * (see notes below). + * + * \note The wrapped key must remain valid as long as the + * wrapping PK context is in use, that is at least between + * the point this function is called and the point + * mbedtls_pk_free() is called on this context. The wrapped + * key might then be independently used or destroyed. + * + * \note This function is currently only available for ECC key + * pairs (that is, ECC keys containing private key material). + * Support for other key types may be added later. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_PK_BAD_INPUT_DATA on invalid input + * (context already used, invalid key identifier). + * \return #MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE if the key is not an + * ECC key pair. + * \return #MBEDTLS_ERR_PK_ALLOC_FAILED on allocation failure. + */ +int mbedtls_pk_setup_opaque( mbedtls_pk_context *ctx, + const psa_key_id_t key ); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +/** + * \brief Initialize an RSA-alt context + * + * \param ctx Context to initialize. It must not have been set + * up yet (type #MBEDTLS_PK_NONE). + * \param key RSA key pointer + * \param decrypt_func Decryption function + * \param sign_func Signing function + * \param key_len_func Function returning key length in bytes + * + * \return 0 on success, or MBEDTLS_ERR_PK_BAD_INPUT_DATA if the + * context wasn't already initialized as RSA_ALT. + * + * \note This function replaces \c mbedtls_pk_setup() for RSA-alt. + */ +int mbedtls_pk_setup_rsa_alt( mbedtls_pk_context *ctx, void * key, + mbedtls_pk_rsa_alt_decrypt_func decrypt_func, + mbedtls_pk_rsa_alt_sign_func sign_func, + mbedtls_pk_rsa_alt_key_len_func key_len_func ); +#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */ + +/** + * \brief Get the size in bits of the underlying key + * + * \param ctx The context to query. It must have been initialized. + * + * \return Key size in bits, or 0 on error + */ +size_t mbedtls_pk_get_bitlen( const mbedtls_pk_context *ctx ); + +/** + * \brief Get the length in bytes of the underlying key + * + * \param ctx The context to query. It must have been initialized. + * + * \return Key length in bytes, or 0 on error + */ +static inline size_t mbedtls_pk_get_len( const mbedtls_pk_context *ctx ) +{ + return( ( mbedtls_pk_get_bitlen( ctx ) + 7 ) / 8 ); +} + +/** + * \brief Tell if a context can do the operation given by type + * + * \param ctx The context to query. It must have been initialized. + * \param type The desired type. + * + * \return 1 if the context can do operations on the given type. + * \return 0 if the context cannot do the operations on the given + * type. This is always the case for a context that has + * been initialized but not set up, or that has been + * cleared with mbedtls_pk_free(). + */ +int mbedtls_pk_can_do( const mbedtls_pk_context *ctx, mbedtls_pk_type_t type ); + +/** + * \brief Verify signature (including padding if relevant). + * + * \param ctx The PK context to use. It must have been set up. + * \param md_alg Hash algorithm used. + * This can be #MBEDTLS_MD_NONE if the signature algorithm + * does not rely on a hash algorithm (non-deterministic + * ECDSA, RSA PKCS#1 v1.5). + * For PKCS#1 v1.5, if \p md_alg is #MBEDTLS_MD_NONE, then + * \p hash is the DigestInfo structure used by RFC 8017 + * §9.2 steps 3–6. If \p md_alg is a valid hash + * algorithm then \p hash is the digest itself, and this + * function calculates the DigestInfo encoding internally. + * \param hash Hash of the message to sign + * \param hash_len Hash length + * \param sig Signature to verify + * \param sig_len Signature length + * + * \return 0 on success (signature is valid), + * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid + * signature in sig but its length is less than \p siglen, + * or a specific error code. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * Use \c mbedtls_pk_verify_ext( MBEDTLS_PK_RSASSA_PSS, ... ) + * to verify RSASSA_PSS signatures. + */ +int mbedtls_pk_verify( mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ); + +/** + * \brief Restartable version of \c mbedtls_pk_verify() + * + * \note Performs the same job as \c mbedtls_pk_verify(), but can + * return early and restart according to the limit set with + * \c mbedtls_ecp_set_max_ops() to reduce blocking for ECC + * operations. For RSA, same as \c mbedtls_pk_verify(). + * + * \param ctx The PK context to use. It must have been set up. + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length or 0 (see notes) + * \param sig Signature to verify + * \param sig_len Signature length + * \param rs_ctx Restart context (NULL to disable restart) + * + * \return See \c mbedtls_pk_verify(), or + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + */ +int mbedtls_pk_verify_restartable( mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len, + mbedtls_pk_restart_ctx *rs_ctx ); + +/** + * \brief Verify signature, with options. + * (Includes verification of the padding depending on type.) + * + * \param type Signature type (inc. possible padding type) to verify + * \param options Pointer to type-specific options, or NULL + * \param ctx The PK context to use. It must have been set up. + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length or 0 (see notes) + * \param sig Signature to verify + * \param sig_len Signature length + * + * \return 0 on success (signature is valid), + * #MBEDTLS_ERR_PK_TYPE_MISMATCH if the PK context can't be + * used for this type of signatures, + * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid + * signature in sig but its length is less than \p siglen, + * or a specific error code. + * + * \note If hash_len is 0, then the length associated with md_alg + * is used instead, or an error returned if it is invalid. + * + * \note md_alg may be MBEDTLS_MD_NONE, only if hash_len != 0 + * + * \note If type is MBEDTLS_PK_RSASSA_PSS, then options must point + * to a mbedtls_pk_rsassa_pss_options structure, + * otherwise it must be NULL. + */ +int mbedtls_pk_verify_ext( mbedtls_pk_type_t type, const void *options, + mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ); + +/** + * \brief Make signature, including padding if relevant. + * + * \param ctx The PK context to use. It must have been set up + * with a private key. + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length + * \param sig Place to write the signature. + * It must have enough room for the signature. + * #MBEDTLS_PK_SIGNATURE_MAX_SIZE is always enough. + * You may use a smaller buffer if it is large enough + * given the key type. + * \param sig_size The size of the \p sig buffer in bytes. + * \param sig_len On successful return, + * the number of bytes written to \p sig. + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \return 0 on success, or a specific error code. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * There is no interface in the PK module to make RSASSA-PSS + * signatures yet. + * + * \note For RSA, md_alg may be MBEDTLS_MD_NONE if hash_len != 0. + * For ECDSA, md_alg may never be MBEDTLS_MD_NONE. + */ +int mbedtls_pk_sign( mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Restartable version of \c mbedtls_pk_sign() + * + * \note Performs the same job as \c mbedtls_pk_sign(), but can + * return early and restart according to the limit set with + * \c mbedtls_ecp_set_max_ops() to reduce blocking for ECC + * operations. For RSA, same as \c mbedtls_pk_sign(). + * + * \param ctx The PK context to use. It must have been set up + * with a private key. + * \param md_alg Hash algorithm used (see notes for mbedtls_pk_sign()) + * \param hash Hash of the message to sign + * \param hash_len Hash length + * \param sig Place to write the signature. + * It must have enough room for the signature. + * #MBEDTLS_PK_SIGNATURE_MAX_SIZE is always enough. + * You may use a smaller buffer if it is large enough + * given the key type. + * \param sig_size The size of the \p sig buffer in bytes. + * \param sig_len On successful return, + * the number of bytes written to \p sig. + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * \param rs_ctx Restart context (NULL to disable restart) + * + * \return See \c mbedtls_pk_sign(). + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + */ +int mbedtls_pk_sign_restartable( mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_pk_restart_ctx *rs_ctx ); + +/** + * \brief Decrypt message (including padding if relevant). + * + * \param ctx The PK context to use. It must have been set up + * with a private key. + * \param input Input to decrypt + * \param ilen Input size + * \param output Decrypted output + * \param olen Decrypted message length + * \param osize Size of the output buffer + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * + * \return 0 on success, or a specific error code. + */ +int mbedtls_pk_decrypt( mbedtls_pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Encrypt message (including padding if relevant). + * + * \param ctx The PK context to use. It must have been set up. + * \param input Message to encrypt + * \param ilen Message size + * \param output Encrypted output + * \param olen Encrypted output length + * \param osize Size of the output buffer + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \note \p f_rng is used for padding generation. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * + * \return 0 on success, or a specific error code. + */ +int mbedtls_pk_encrypt( mbedtls_pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Check if a public-private pair of keys matches. + * + * \param pub Context holding a public key. + * \param prv Context holding a private (and public) key. + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \return \c 0 on success (keys were checked and match each other). + * \return #MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE if the keys could not + * be checked - in that case they may or may not match. + * \return #MBEDTLS_ERR_PK_BAD_INPUT_DATA if a context is invalid. + * \return Another non-zero value if the keys do not match. + */ +int mbedtls_pk_check_pair( const mbedtls_pk_context *pub, + const mbedtls_pk_context *prv, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Export debug information + * + * \param ctx The PK context to use. It must have been initialized. + * \param items Place to write debug items + * + * \return 0 on success or MBEDTLS_ERR_PK_BAD_INPUT_DATA + */ +int mbedtls_pk_debug( const mbedtls_pk_context *ctx, mbedtls_pk_debug_item *items ); + +/** + * \brief Access the type name + * + * \param ctx The PK context to use. It must have been initialized. + * + * \return Type name on success, or "invalid PK" + */ +const char * mbedtls_pk_get_name( const mbedtls_pk_context *ctx ); + +/** + * \brief Get the key type + * + * \param ctx The PK context to use. It must have been initialized. + * + * \return Type on success. + * \return #MBEDTLS_PK_NONE for a context that has not been set up. + */ +mbedtls_pk_type_t mbedtls_pk_get_type( const mbedtls_pk_context *ctx ); + +#if defined(MBEDTLS_PK_PARSE_C) +/** \ingroup pk_module */ +/** + * \brief Parse a private key in PEM or DER format + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param key Input buffer to parse. + * The buffer must contain the input exactly, with no + * extra trailing material. For PEM, the buffer must + * contain a null-terminated string. + * \param keylen Size of \b key in bytes. + * For PEM data, this includes the terminating null byte, + * so \p keylen must be equal to `strlen(key) + 1`. + * \param pwd Optional password for decryption. + * Pass \c NULL if expecting a non-encrypted key. + * Pass a string of \p pwdlen bytes if expecting an encrypted + * key; a non-encrypted key will also be accepted. + * The empty password is not supported. + * \param pwdlen Size of the password in bytes. + * Ignored if \p pwd is \c NULL. + * \param f_rng RNG function, must not be \c NULL. Used for blinding. + * \param p_rng RNG parameter + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If you need a + * specific key type, check the result with mbedtls_pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_key( mbedtls_pk_context *ctx, + const unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** \ingroup pk_module */ +/** + * \brief Parse a public key in PEM or DER format + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param key Input buffer to parse. + * The buffer must contain the input exactly, with no + * extra trailing material. For PEM, the buffer must + * contain a null-terminated string. + * \param keylen Size of \b key in bytes. + * For PEM data, this includes the terminating null byte, + * so \p keylen must be equal to `strlen(key) + 1`. + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If you need a + * specific key type, check the result with mbedtls_pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_public_key( mbedtls_pk_context *ctx, + const unsigned char *key, size_t keylen ); + +#if defined(MBEDTLS_FS_IO) +/** \ingroup pk_module */ +/** + * \brief Load and parse a private key + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param path filename to read the private key from + * \param password Optional password to decrypt the file. + * Pass \c NULL if expecting a non-encrypted key. + * Pass a null-terminated string if expecting an encrypted + * key; a non-encrypted key will also be accepted. + * The empty password is not supported. + * \param f_rng RNG function, must not be \c NULL. Used for blinding. + * \param p_rng RNG parameter + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If you need a + * specific key type, check the result with mbedtls_pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_keyfile( mbedtls_pk_context *ctx, + const char *path, const char *password, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** \ingroup pk_module */ +/** + * \brief Load and parse a public key + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param path filename to read the public key from + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If + * you need a specific key type, check the result with + * mbedtls_pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_public_keyfile( mbedtls_pk_context *ctx, const char *path ); +#endif /* MBEDTLS_FS_IO */ +#endif /* MBEDTLS_PK_PARSE_C */ + +#if defined(MBEDTLS_PK_WRITE_C) +/** + * \brief Write a private key to a PKCS#1 or SEC1 DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx PK context which must contain a valid private key. + * \param buf buffer to write to + * \param size size of the buffer + * + * \return length of data written if successful, or a specific + * error code + */ +int mbedtls_pk_write_key_der( const mbedtls_pk_context *ctx, unsigned char *buf, size_t size ); + +/** + * \brief Write a public key to a SubjectPublicKeyInfo DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx PK context which must contain a valid public or private key. + * \param buf buffer to write to + * \param size size of the buffer + * + * \return length of data written if successful, or a specific + * error code + */ +int mbedtls_pk_write_pubkey_der( const mbedtls_pk_context *ctx, unsigned char *buf, size_t size ); + +#if defined(MBEDTLS_PEM_WRITE_C) +/** + * \brief Write a public key to a PEM string + * + * \param ctx PK context which must contain a valid public or private key. + * \param buf Buffer to write to. The output includes a + * terminating null byte. + * \param size Size of the buffer in bytes. + * + * \return 0 if successful, or a specific error code + */ +int mbedtls_pk_write_pubkey_pem( const mbedtls_pk_context *ctx, unsigned char *buf, size_t size ); + +/** + * \brief Write a private key to a PKCS#1 or SEC1 PEM string + * + * \param ctx PK context which must contain a valid private key. + * \param buf Buffer to write to. The output includes a + * terminating null byte. + * \param size Size of the buffer in bytes. + * + * \return 0 if successful, or a specific error code + */ +int mbedtls_pk_write_key_pem( const mbedtls_pk_context *ctx, unsigned char *buf, size_t size ); +#endif /* MBEDTLS_PEM_WRITE_C */ +#endif /* MBEDTLS_PK_WRITE_C */ + +/* + * WARNING: Low-level functions. You probably do not want to use these unless + * you are certain you do ;) + */ + +#if defined(MBEDTLS_PK_PARSE_C) +/** + * \brief Parse a SubjectPublicKeyInfo DER structure + * + * \param p the position in the ASN.1 data + * \param end end of the buffer + * \param pk The PK context to fill. It must have been initialized + * but not set up. + * + * \return 0 if successful, or a specific PK error code + */ +int mbedtls_pk_parse_subpubkey( unsigned char **p, const unsigned char *end, + mbedtls_pk_context *pk ); +#endif /* MBEDTLS_PK_PARSE_C */ + +#if defined(MBEDTLS_PK_WRITE_C) +/** + * \brief Write a subjectPublicKey to ASN.1 data + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param key PK context which must contain a valid public or private key. + * + * \return the length written or a negative error code + */ +int mbedtls_pk_write_pubkey( unsigned char **p, unsigned char *start, + const mbedtls_pk_context *key ); +#endif /* MBEDTLS_PK_WRITE_C */ + +/* + * Internal module functions. You probably do not want to use these unless you + * know you do. + */ +#if defined(MBEDTLS_FS_IO) +int mbedtls_pk_load_file( const char *path, unsigned char **buf, size_t *n ); +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * \brief Turn an EC key into an opaque one. + * + * \warning This is a temporary utility function for tests. It might + * change or be removed at any time without notice. + * + * \note Only ECDSA keys are supported so far. Signing with the + * specified hash is the only allowed use of that key. + * + * \param pk Input: the EC key to import to a PSA key. + * Output: a PK context wrapping that PSA key. + * \param key Output: a PSA key identifier. + * It's the caller's responsibility to call + * psa_destroy_key() on that key identifier after calling + * mbedtls_pk_free() on the PK context. + * \param hash_alg The hash algorithm to allow for use with that key. + * + * \return \c 0 if successful. + * \return An Mbed TLS error code otherwise. + */ +int mbedtls_pk_wrap_as_opaque( mbedtls_pk_context *pk, + psa_key_id_t *key, + psa_algorithm_t hash_alg ); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_PK_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform.h new file mode 100644 index 0000000..277a85c --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform.h @@ -0,0 +1,411 @@ +/** + * \file platform.h + * + * \brief This file contains the definitions and functions of the + * Mbed TLS platform abstraction layer. + * + * The platform abstraction layer removes the need for the library + * to directly link to standard C library functions or operating + * system services, making the library easier to port and embed. + * Application developers and users of the library can provide their own + * implementations of these functions, or implementations specific to + * their platform, which can be statically linked to the library or + * dynamically configured at runtime. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PLATFORM_H +#define MBEDTLS_PLATFORM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ + +/* The older Microsoft Windows common runtime provides non-conforming + * implementations of some standard library functions, including snprintf + * and vsnprintf. This affects MSVC and MinGW builds. + */ +#if defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER <= 1900) +#define MBEDTLS_PLATFORM_HAS_NON_CONFORMING_SNPRINTF +#define MBEDTLS_PLATFORM_HAS_NON_CONFORMING_VSNPRINTF +#endif + +#if !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) +#include +#include +#include +#if !defined(MBEDTLS_PLATFORM_STD_SNPRINTF) +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_SNPRINTF) +#define MBEDTLS_PLATFORM_STD_SNPRINTF mbedtls_platform_win32_snprintf /**< The default \c snprintf function to use. */ +#else +#define MBEDTLS_PLATFORM_STD_SNPRINTF snprintf /**< The default \c snprintf function to use. */ +#endif +#endif +#if !defined(MBEDTLS_PLATFORM_STD_VSNPRINTF) +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_VSNPRINTF) +#define MBEDTLS_PLATFORM_STD_VSNPRINTF mbedtls_platform_win32_vsnprintf /**< The default \c vsnprintf function to use. */ +#else +#define MBEDTLS_PLATFORM_STD_VSNPRINTF vsnprintf /**< The default \c vsnprintf function to use. */ +#endif +#endif +#if !defined(MBEDTLS_PLATFORM_STD_PRINTF) +#define MBEDTLS_PLATFORM_STD_PRINTF printf /**< The default \c printf function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_FPRINTF) +#define MBEDTLS_PLATFORM_STD_FPRINTF fprintf /**< The default \c fprintf function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_CALLOC) +#define MBEDTLS_PLATFORM_STD_CALLOC calloc /**< The default \c calloc function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_FREE) +#define MBEDTLS_PLATFORM_STD_FREE free /**< The default \c free function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_EXIT) +#define MBEDTLS_PLATFORM_STD_EXIT exit /**< The default \c exit function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_TIME) +#define MBEDTLS_PLATFORM_STD_TIME time /**< The default \c time function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_EXIT_SUCCESS) +#define MBEDTLS_PLATFORM_STD_EXIT_SUCCESS EXIT_SUCCESS /**< The default exit value to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_EXIT_FAILURE) +#define MBEDTLS_PLATFORM_STD_EXIT_FAILURE EXIT_FAILURE /**< The default exit value to use. */ +#endif +#if defined(MBEDTLS_FS_IO) +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_READ) +#define MBEDTLS_PLATFORM_STD_NV_SEED_READ mbedtls_platform_std_nv_seed_read +#endif +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_WRITE) +#define MBEDTLS_PLATFORM_STD_NV_SEED_WRITE mbedtls_platform_std_nv_seed_write +#endif +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_FILE) +#define MBEDTLS_PLATFORM_STD_NV_SEED_FILE "seedfile" +#endif +#endif /* MBEDTLS_FS_IO */ +#else /* MBEDTLS_PLATFORM_NO_STD_FUNCTIONS */ +#if defined(MBEDTLS_PLATFORM_STD_MEM_HDR) +#include MBEDTLS_PLATFORM_STD_MEM_HDR +#endif +#endif /* MBEDTLS_PLATFORM_NO_STD_FUNCTIONS */ + + +/* \} name SECTION: Module settings */ + +/* + * The function pointers for calloc and free. + */ +#if defined(MBEDTLS_PLATFORM_MEMORY) +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) && \ + defined(MBEDTLS_PLATFORM_CALLOC_MACRO) +#define mbedtls_free MBEDTLS_PLATFORM_FREE_MACRO +#define mbedtls_calloc MBEDTLS_PLATFORM_CALLOC_MACRO +#else +/* For size_t */ +#include +extern void *mbedtls_calloc( size_t n, size_t size ); +extern void mbedtls_free( void *ptr ); + +/** + * \brief This function dynamically sets the memory-management + * functions used by the library, during runtime. + * + * \param calloc_func The \c calloc function implementation. + * \param free_func The \c free function implementation. + * + * \return \c 0. + */ +int mbedtls_platform_set_calloc_free( void * (*calloc_func)( size_t, size_t ), + void (*free_func)( void * ) ); +#endif /* MBEDTLS_PLATFORM_FREE_MACRO && MBEDTLS_PLATFORM_CALLOC_MACRO */ +#else /* !MBEDTLS_PLATFORM_MEMORY */ +#define mbedtls_free free +#define mbedtls_calloc calloc +#endif /* MBEDTLS_PLATFORM_MEMORY && !MBEDTLS_PLATFORM_{FREE,CALLOC}_MACRO */ + +/* + * The function pointers for fprintf + */ +#if defined(MBEDTLS_PLATFORM_FPRINTF_ALT) +/* We need FILE * */ +#include +extern int (*mbedtls_fprintf)( FILE *stream, const char *format, ... ); + +/** + * \brief This function dynamically configures the fprintf + * function that is called when the + * mbedtls_fprintf() function is invoked by the library. + * + * \param fprintf_func The \c fprintf function implementation. + * + * \return \c 0. + */ +int mbedtls_platform_set_fprintf( int (*fprintf_func)( FILE *stream, const char *, + ... ) ); +#else +#if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO) +#define mbedtls_fprintf MBEDTLS_PLATFORM_FPRINTF_MACRO +#else +#define mbedtls_fprintf fprintf +#endif /* MBEDTLS_PLATFORM_FPRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_FPRINTF_ALT */ + +/* + * The function pointers for printf + */ +#if defined(MBEDTLS_PLATFORM_PRINTF_ALT) +extern int (*mbedtls_printf)( const char *format, ... ); + +/** + * \brief This function dynamically configures the snprintf + * function that is called when the mbedtls_snprintf() + * function is invoked by the library. + * + * \param printf_func The \c printf function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_printf( int (*printf_func)( const char *, ... ) ); +#else /* !MBEDTLS_PLATFORM_PRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_PRINTF_MACRO) +#define mbedtls_printf MBEDTLS_PLATFORM_PRINTF_MACRO +#else +#define mbedtls_printf printf +#endif /* MBEDTLS_PLATFORM_PRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_PRINTF_ALT */ + +/* + * The function pointers for snprintf + * + * The snprintf implementation should conform to C99: + * - it *must* always correctly zero-terminate the buffer + * (except when n == 0, then it must leave the buffer untouched) + * - however it is acceptable to return -1 instead of the required length when + * the destination buffer is too short. + */ +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_SNPRINTF) +/* For Windows (inc. MSYS2), we provide our own fixed implementation */ +int mbedtls_platform_win32_snprintf( char *s, size_t n, const char *fmt, ... ); +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) +extern int (*mbedtls_snprintf)( char * s, size_t n, const char * format, ... ); + +/** + * \brief This function allows configuring a custom + * \c snprintf function pointer. + * + * \param snprintf_func The \c snprintf function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_snprintf( int (*snprintf_func)( char * s, size_t n, + const char * format, ... ) ); +#else /* MBEDTLS_PLATFORM_SNPRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) +#define mbedtls_snprintf MBEDTLS_PLATFORM_SNPRINTF_MACRO +#else +#define mbedtls_snprintf MBEDTLS_PLATFORM_STD_SNPRINTF +#endif /* MBEDTLS_PLATFORM_SNPRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_SNPRINTF_ALT */ + +/* + * The function pointers for vsnprintf + * + * The vsnprintf implementation should conform to C99: + * - it *must* always correctly zero-terminate the buffer + * (except when n == 0, then it must leave the buffer untouched) + * - however it is acceptable to return -1 instead of the required length when + * the destination buffer is too short. + */ +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_VSNPRINTF) +#include +/* For Older Windows (inc. MSYS2), we provide our own fixed implementation */ +int mbedtls_platform_win32_vsnprintf( char *s, size_t n, const char *fmt, va_list arg ); +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) +#include +extern int (*mbedtls_vsnprintf)( char * s, size_t n, const char * format, va_list arg ); + +/** + * \brief Set your own snprintf function pointer + * + * \param vsnprintf_func The \c vsnprintf function implementation + * + * \return \c 0 + */ +int mbedtls_platform_set_vsnprintf( int (*vsnprintf_func)( char * s, size_t n, + const char * format, va_list arg ) ); +#else /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) +#define mbedtls_vsnprintf MBEDTLS_PLATFORM_VSNPRINTF_MACRO +#else +#define mbedtls_vsnprintf vsnprintf +#endif /* MBEDTLS_PLATFORM_VSNPRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */ + +/* + * The function pointers for exit + */ +#if defined(MBEDTLS_PLATFORM_EXIT_ALT) +extern void (*mbedtls_exit)( int status ); + +/** + * \brief This function dynamically configures the exit + * function that is called when the mbedtls_exit() + * function is invoked by the library. + * + * \param exit_func The \c exit function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_exit( void (*exit_func)( int status ) ); +#else +#if defined(MBEDTLS_PLATFORM_EXIT_MACRO) +#define mbedtls_exit MBEDTLS_PLATFORM_EXIT_MACRO +#else +#define mbedtls_exit exit +#endif /* MBEDTLS_PLATFORM_EXIT_MACRO */ +#endif /* MBEDTLS_PLATFORM_EXIT_ALT */ + +/* + * The default exit values + */ +#if defined(MBEDTLS_PLATFORM_STD_EXIT_SUCCESS) +#define MBEDTLS_EXIT_SUCCESS MBEDTLS_PLATFORM_STD_EXIT_SUCCESS +#else +#define MBEDTLS_EXIT_SUCCESS 0 +#endif +#if defined(MBEDTLS_PLATFORM_STD_EXIT_FAILURE) +#define MBEDTLS_EXIT_FAILURE MBEDTLS_PLATFORM_STD_EXIT_FAILURE +#else +#define MBEDTLS_EXIT_FAILURE 1 +#endif + +/* + * The function pointers for reading from and writing a seed file to + * Non-Volatile storage (NV) in a platform-independent way + * + * Only enabled when the NV seed entropy source is enabled + */ +#if defined(MBEDTLS_ENTROPY_NV_SEED) +#if !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) && defined(MBEDTLS_FS_IO) +/* Internal standard platform definitions */ +int mbedtls_platform_std_nv_seed_read( unsigned char *buf, size_t buf_len ); +int mbedtls_platform_std_nv_seed_write( unsigned char *buf, size_t buf_len ); +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_ALT) +extern int (*mbedtls_nv_seed_read)( unsigned char *buf, size_t buf_len ); +extern int (*mbedtls_nv_seed_write)( unsigned char *buf, size_t buf_len ); + +/** + * \brief This function allows configuring custom seed file writing and + * reading functions. + * + * \param nv_seed_read_func The seed reading function implementation. + * \param nv_seed_write_func The seed writing function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_nv_seed( + int (*nv_seed_read_func)( unsigned char *buf, size_t buf_len ), + int (*nv_seed_write_func)( unsigned char *buf, size_t buf_len ) + ); +#else +#if defined(MBEDTLS_PLATFORM_NV_SEED_READ_MACRO) && \ + defined(MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO) +#define mbedtls_nv_seed_read MBEDTLS_PLATFORM_NV_SEED_READ_MACRO +#define mbedtls_nv_seed_write MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO +#else +#define mbedtls_nv_seed_read mbedtls_platform_std_nv_seed_read +#define mbedtls_nv_seed_write mbedtls_platform_std_nv_seed_write +#endif +#endif /* MBEDTLS_PLATFORM_NV_SEED_ALT */ +#endif /* MBEDTLS_ENTROPY_NV_SEED */ + +#if !defined(MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT) + +/** + * \brief The platform context structure. + * + * \note This structure may be used to assist platform-specific + * setup or teardown operations. + */ +typedef struct mbedtls_platform_context +{ + char MBEDTLS_PRIVATE(dummy); /**< A placeholder member, as empty structs are not portable. */ +} +mbedtls_platform_context; + +#else +#include "platform_alt.h" +#endif /* !MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT */ + +/** + * \brief This function performs any platform-specific initialization + * operations. + * + * \note This function should be called before any other library functions. + * + * Its implementation is platform-specific, and unless + * platform-specific code is provided, it does nothing. + * + * \note The usage and necessity of this function is dependent on the platform. + * + * \param ctx The platform context. + * + * \return \c 0 on success. + */ +int mbedtls_platform_setup( mbedtls_platform_context *ctx ); +/** + * \brief This function performs any platform teardown operations. + * + * \note This function should be called after every other Mbed TLS module + * has been correctly freed using the appropriate free function. + * + * Its implementation is platform-specific, and unless + * platform-specific code is provided, it does nothing. + * + * \note The usage and necessity of this function is dependent on the platform. + * + * \param ctx The platform context. + * + */ +void mbedtls_platform_teardown( mbedtls_platform_context *ctx ); + +#ifdef __cplusplus +} +#endif + +#endif /* platform.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform_util.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform_util.h new file mode 100644 index 0000000..1a0a135 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/platform_util.h @@ -0,0 +1,122 @@ +/** + * \file platform_util.h + * + * \brief Common and shared functions used by multiple modules in the Mbed TLS + * library. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PLATFORM_UTIL_H +#define MBEDTLS_PLATFORM_UTIL_H + +#include "mbedtls/build_info.h" + +#include +#if defined(MBEDTLS_HAVE_TIME_DATE) +#include "mbedtls/platform_time.h" +#include +#endif /* MBEDTLS_HAVE_TIME_DATE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Internal macros meant to be called only from within the library. */ +#define MBEDTLS_INTERNAL_VALIDATE_RET( cond, ret ) do { } while( 0 ) +#define MBEDTLS_INTERNAL_VALIDATE( cond ) do { } while( 0 ) + +/* Internal helper macros for deprecating API constants. */ +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#if defined(MBEDTLS_DEPRECATED_WARNING) +/* Deliberately don't (yet) export MBEDTLS_DEPRECATED here + * to avoid conflict with other headers which define and use + * it, too. We might want to move all these definitions here at + * some point for uniformity. */ +#define MBEDTLS_DEPRECATED __attribute__((deprecated)) +MBEDTLS_DEPRECATED typedef char const * mbedtls_deprecated_string_constant_t; +#define MBEDTLS_DEPRECATED_STRING_CONSTANT( VAL ) \ + ( (mbedtls_deprecated_string_constant_t) ( VAL ) ) +MBEDTLS_DEPRECATED typedef int mbedtls_deprecated_numeric_constant_t; +#define MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( VAL ) \ + ( (mbedtls_deprecated_numeric_constant_t) ( VAL ) ) +#undef MBEDTLS_DEPRECATED +#else /* MBEDTLS_DEPRECATED_WARNING */ +#define MBEDTLS_DEPRECATED_STRING_CONSTANT( VAL ) VAL +#define MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( VAL ) VAL +#endif /* MBEDTLS_DEPRECATED_WARNING */ +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +/** + * \brief Securely zeroize a buffer + * + * The function is meant to wipe the data contained in a buffer so + * that it can no longer be recovered even if the program memory + * is later compromised. Call this function on sensitive data + * stored on the stack before returning from a function, and on + * sensitive data stored on the heap before freeing the heap + * object. + * + * It is extremely difficult to guarantee that calls to + * mbedtls_platform_zeroize() are not removed by aggressive + * compiler optimizations in a portable way. For this reason, Mbed + * TLS provides the configuration option + * MBEDTLS_PLATFORM_ZEROIZE_ALT, which allows users to configure + * mbedtls_platform_zeroize() to use a suitable implementation for + * their platform and needs + * + * \param buf Buffer to be zeroized + * \param len Length of the buffer in bytes + * + */ +void mbedtls_platform_zeroize( void *buf, size_t len ); + +#if defined(MBEDTLS_HAVE_TIME_DATE) +/** + * \brief Platform-specific implementation of gmtime_r() + * + * The function is a thread-safe abstraction that behaves + * similarly to the gmtime_r() function from Unix/POSIX. + * + * Mbed TLS will try to identify the underlying platform and + * make use of an appropriate underlying implementation (e.g. + * gmtime_r() for POSIX and gmtime_s() for Windows). If this is + * not possible, then gmtime() will be used. In this case, calls + * from the library to gmtime() will be guarded by the mutex + * mbedtls_threading_gmtime_mutex if MBEDTLS_THREADING_C is + * enabled. It is recommended that calls from outside the library + * are also guarded by this mutex. + * + * If MBEDTLS_PLATFORM_GMTIME_R_ALT is defined, then Mbed TLS will + * unconditionally use the alternative implementation for + * mbedtls_platform_gmtime_r() supplied by the user at compile time. + * + * \param tt Pointer to an object containing time (in seconds) since the + * epoch to be converted + * \param tm_buf Pointer to an object where the results will be stored + * + * \return Pointer to an object of type struct tm on success, otherwise + * NULL + */ +struct tm *mbedtls_platform_gmtime_r( const mbedtls_time_t *tt, + struct tm *tm_buf ); +#endif /* MBEDTLS_HAVE_TIME_DATE */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_PLATFORM_UTIL_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/private_access.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/private_access.h new file mode 100644 index 0000000..98d3419 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/private_access.h @@ -0,0 +1,32 @@ + /** + * \file private_access.h + * + * \brief Macro wrapper for struct's memebrs. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_PRIVATE_ACCESS_H +#define MBEDTLS_PRIVATE_ACCESS_H + +#ifndef MBEDTLS_ALLOW_PRIVATE_ACCESS +#define MBEDTLS_PRIVATE(member) private_##member +#else +#define MBEDTLS_PRIVATE(member) member +#endif + +#endif /* MBEDTLS_PRIVATE_ACCESS_H */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/rsa.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/rsa.h new file mode 100644 index 0000000..cffbe3b --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/rsa.h @@ -0,0 +1,1119 @@ +/** + * \file rsa.h + * + * \brief This file provides an API for the RSA public-key cryptosystem. + * + * The RSA public-key cryptosystem is defined in Public-Key + * Cryptography Standards (PKCS) #1 v1.5: RSA Encryption + * and Public-Key Cryptography Standards (PKCS) #1 v2.1: + * RSA Cryptography Specifications. + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_RSA_H +#define MBEDTLS_RSA_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/bignum.h" +#include "mbedtls/md.h" + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +/* + * RSA Error codes + */ +#define MBEDTLS_ERR_RSA_BAD_INPUT_DATA -0x4080 /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_RSA_INVALID_PADDING -0x4100 /**< Input data contains invalid padding and is rejected. */ +#define MBEDTLS_ERR_RSA_KEY_GEN_FAILED -0x4180 /**< Something failed during generation of a key. */ +#define MBEDTLS_ERR_RSA_KEY_CHECK_FAILED -0x4200 /**< Key failed to pass the validity check of the library. */ +#define MBEDTLS_ERR_RSA_PUBLIC_FAILED -0x4280 /**< The public key operation failed. */ +#define MBEDTLS_ERR_RSA_PRIVATE_FAILED -0x4300 /**< The private key operation failed. */ +#define MBEDTLS_ERR_RSA_VERIFY_FAILED -0x4380 /**< The PKCS#1 verification failed. */ +#define MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE -0x4400 /**< The output buffer for decryption is not large enough. */ +#define MBEDTLS_ERR_RSA_RNG_FAILED -0x4480 /**< The random generator failed to generate non-zeros. */ + +/* + * RSA constants + */ + +#define MBEDTLS_RSA_PKCS_V15 0 /**< Use PKCS#1 v1.5 encoding. */ +#define MBEDTLS_RSA_PKCS_V21 1 /**< Use PKCS#1 v2.1 encoding. */ + +#define MBEDTLS_RSA_SIGN 1 /**< Identifier for RSA signature operations. */ +#define MBEDTLS_RSA_CRYPT 2 /**< Identifier for RSA encryption and decryption operations. */ + +#define MBEDTLS_RSA_SALT_LEN_ANY -1 + +/* + * The above constants may be used even if the RSA module is compile out, + * eg for alternative (PKCS#11) RSA implemenations in the PK layers. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_RSA_ALT) +// Regular implementation +// + +/** + * \brief The RSA context structure. + */ +typedef struct mbedtls_rsa_context +{ + int MBEDTLS_PRIVATE(ver); /*!< Reserved for internal purposes. + * Do not set this field in application + * code. Its meaning might change without + * notice. */ + size_t MBEDTLS_PRIVATE(len); /*!< The size of \p N in Bytes. */ + + mbedtls_mpi MBEDTLS_PRIVATE(N); /*!< The public modulus. */ + mbedtls_mpi MBEDTLS_PRIVATE(E); /*!< The public exponent. */ + + mbedtls_mpi MBEDTLS_PRIVATE(D); /*!< The private exponent. */ + mbedtls_mpi MBEDTLS_PRIVATE(P); /*!< The first prime factor. */ + mbedtls_mpi MBEDTLS_PRIVATE(Q); /*!< The second prime factor. */ + + mbedtls_mpi MBEDTLS_PRIVATE(DP); /*!< D % (P - 1). */ + mbedtls_mpi MBEDTLS_PRIVATE(DQ); /*!< D % (Q - 1). */ + mbedtls_mpi MBEDTLS_PRIVATE(QP); /*!< 1 / (Q % P). */ + + mbedtls_mpi MBEDTLS_PRIVATE(RN); /*!< cached R^2 mod N. */ + + mbedtls_mpi MBEDTLS_PRIVATE(RP); /*!< cached R^2 mod P. */ + mbedtls_mpi MBEDTLS_PRIVATE(RQ); /*!< cached R^2 mod Q. */ + + mbedtls_mpi MBEDTLS_PRIVATE(Vi); /*!< The cached blinding value. */ + mbedtls_mpi MBEDTLS_PRIVATE(Vf); /*!< The cached un-blinding value. */ + + int MBEDTLS_PRIVATE(padding); /*!< Selects padding mode: + #MBEDTLS_RSA_PKCS_V15 for 1.5 padding and + #MBEDTLS_RSA_PKCS_V21 for OAEP or PSS. */ + int MBEDTLS_PRIVATE(hash_id); /*!< Hash identifier of mbedtls_md_type_t type, + as specified in md.h for use in the MGF + mask generating function used in the + EME-OAEP and EMSA-PSS encodings. */ +#if defined(MBEDTLS_THREADING_C) + /* Invariant: the mutex is initialized iff ver != 0. */ + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); /*!< Thread-safety mutex. */ +#endif +} +mbedtls_rsa_context; + +#else /* MBEDTLS_RSA_ALT */ +#include "rsa_alt.h" +#endif /* MBEDTLS_RSA_ALT */ + +/** + * \brief This function initializes an RSA context. + * + * \note This function initializes the padding and the hash + * identifier to respectively #MBEDTLS_RSA_PKCS_V15 and + * #MBEDTLS_MD_NONE. See mbedtls_rsa_set_padding() for more + * information about those parameters. + * + * \param ctx The RSA context to initialize. This must not be \c NULL. + */ +void mbedtls_rsa_init( mbedtls_rsa_context *ctx ); + +/** + * \brief This function sets padding for an already initialized RSA + * context. + * + * \note Set padding to #MBEDTLS_RSA_PKCS_V21 for the RSAES-OAEP + * encryption scheme and the RSASSA-PSS signature scheme. + * + * \note The \p hash_id parameter is ignored when using + * #MBEDTLS_RSA_PKCS_V15 padding. + * + * \note The choice of padding mode is strictly enforced for private + * key operations, since there might be security concerns in + * mixing padding modes. For public key operations it is + * a default value, which can be overridden by calling specific + * \c mbedtls_rsa_rsaes_xxx or \c mbedtls_rsa_rsassa_xxx + * functions. + * + * \note The hash selected in \p hash_id is always used for OEAP + * encryption. For PSS signatures, it is always used for + * making signatures, but can be overridden for verifying them. + * If set to #MBEDTLS_MD_NONE, it is always overridden. + * + * \param ctx The initialized RSA context to be configured. + * \param padding The padding mode to use. This must be either + * #MBEDTLS_RSA_PKCS_V15 or #MBEDTLS_RSA_PKCS_V21. + * \param hash_id The hash identifier for PSS or OAEP, if \p padding is + * #MBEDTLS_RSA_PKCS_V21. #MBEDTLS_MD_NONE is accepted by this + * function but may be not suitable for some operations. + * Ignored if \p padding is #MBEDTLS_RSA_PKCS_V15. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_RSA_INVALID_PADDING failure: + * \p padding or \p hash_id is invalid. + */ +int mbedtls_rsa_set_padding( mbedtls_rsa_context *ctx, int padding, + mbedtls_md_type_t hash_id ); + +/** + * \brief This function imports a set of core parameters into an + * RSA context. + * + * \note This function can be called multiple times for successive + * imports, if the parameters are not simultaneously present. + * + * Any sequence of calls to this function should be followed + * by a call to mbedtls_rsa_complete(), which checks and + * completes the provided information to a ready-for-use + * public or private RSA key. + * + * \note See mbedtls_rsa_complete() for more information on which + * parameters are necessary to set up a private or public + * RSA key. + * + * \note The imported parameters are copied and need not be preserved + * for the lifetime of the RSA context being set up. + * + * \param ctx The initialized RSA context to store the parameters in. + * \param N The RSA modulus. This may be \c NULL. + * \param P The first prime factor of \p N. This may be \c NULL. + * \param Q The second prime factor of \p N. This may be \c NULL. + * \param D The private exponent. This may be \c NULL. + * \param E The public exponent. This may be \c NULL. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_rsa_import( mbedtls_rsa_context *ctx, + const mbedtls_mpi *N, + const mbedtls_mpi *P, const mbedtls_mpi *Q, + const mbedtls_mpi *D, const mbedtls_mpi *E ); + +/** + * \brief This function imports core RSA parameters, in raw big-endian + * binary format, into an RSA context. + * + * \note This function can be called multiple times for successive + * imports, if the parameters are not simultaneously present. + * + * Any sequence of calls to this function should be followed + * by a call to mbedtls_rsa_complete(), which checks and + * completes the provided information to a ready-for-use + * public or private RSA key. + * + * \note See mbedtls_rsa_complete() for more information on which + * parameters are necessary to set up a private or public + * RSA key. + * + * \note The imported parameters are copied and need not be preserved + * for the lifetime of the RSA context being set up. + * + * \param ctx The initialized RSA context to store the parameters in. + * \param N The RSA modulus. This may be \c NULL. + * \param N_len The Byte length of \p N; it is ignored if \p N == NULL. + * \param P The first prime factor of \p N. This may be \c NULL. + * \param P_len The Byte length of \p P; it ns ignored if \p P == NULL. + * \param Q The second prime factor of \p N. This may be \c NULL. + * \param Q_len The Byte length of \p Q; it is ignored if \p Q == NULL. + * \param D The private exponent. This may be \c NULL. + * \param D_len The Byte length of \p D; it is ignored if \p D == NULL. + * \param E The public exponent. This may be \c NULL. + * \param E_len The Byte length of \p E; it is ignored if \p E == NULL. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_rsa_import_raw( mbedtls_rsa_context *ctx, + unsigned char const *N, size_t N_len, + unsigned char const *P, size_t P_len, + unsigned char const *Q, size_t Q_len, + unsigned char const *D, size_t D_len, + unsigned char const *E, size_t E_len ); + +/** + * \brief This function completes an RSA context from + * a set of imported core parameters. + * + * To setup an RSA public key, precisely \p N and \p E + * must have been imported. + * + * To setup an RSA private key, sufficient information must + * be present for the other parameters to be derivable. + * + * The default implementation supports the following: + *
  • Derive \p P, \p Q from \p N, \p D, \p E.
  • + *
  • Derive \p N, \p D from \p P, \p Q, \p E.
+ * Alternative implementations need not support these. + * + * If this function runs successfully, it guarantees that + * the RSA context can be used for RSA operations without + * the risk of failure or crash. + * + * \warning This function need not perform consistency checks + * for the imported parameters. In particular, parameters that + * are not needed by the implementation might be silently + * discarded and left unchecked. To check the consistency + * of the key material, see mbedtls_rsa_check_privkey(). + * + * \param ctx The initialized RSA context holding imported parameters. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_RSA_BAD_INPUT_DATA if the attempted derivations + * failed. + * + */ +int mbedtls_rsa_complete( mbedtls_rsa_context *ctx ); + +/** + * \brief This function exports the core parameters of an RSA key. + * + * If this function runs successfully, the non-NULL buffers + * pointed to by \p N, \p P, \p Q, \p D, and \p E are fully + * written, with additional unused space filled leading by + * zero Bytes. + * + * Possible reasons for returning + * #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED:
    + *
  • An alternative RSA implementation is in use, which + * stores the key externally, and either cannot or should + * not export it into RAM.
  • + *
  • A SW or HW implementation might not support a certain + * deduction. For example, \p P, \p Q from \p N, \p D, + * and \p E if the former are not part of the + * implementation.
+ * + * If the function fails due to an unsupported operation, + * the RSA context stays intact and remains usable. + * + * \param ctx The initialized RSA context. + * \param N The MPI to hold the RSA modulus. + * This may be \c NULL if this field need not be exported. + * \param P The MPI to hold the first prime factor of \p N. + * This may be \c NULL if this field need not be exported. + * \param Q The MPI to hold the second prime factor of \p N. + * This may be \c NULL if this field need not be exported. + * \param D The MPI to hold the private exponent. + * This may be \c NULL if this field need not be exported. + * \param E The MPI to hold the public exponent. + * This may be \c NULL if this field need not be exported. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED if exporting the + * requested parameters cannot be done due to missing + * functionality or because of security policies. + * \return A non-zero return code on any other failure. + * + */ +int mbedtls_rsa_export( const mbedtls_rsa_context *ctx, + mbedtls_mpi *N, mbedtls_mpi *P, mbedtls_mpi *Q, + mbedtls_mpi *D, mbedtls_mpi *E ); + +/** + * \brief This function exports core parameters of an RSA key + * in raw big-endian binary format. + * + * If this function runs successfully, the non-NULL buffers + * pointed to by \p N, \p P, \p Q, \p D, and \p E are fully + * written, with additional unused space filled leading by + * zero Bytes. + * + * Possible reasons for returning + * #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED:
    + *
  • An alternative RSA implementation is in use, which + * stores the key externally, and either cannot or should + * not export it into RAM.
  • + *
  • A SW or HW implementation might not support a certain + * deduction. For example, \p P, \p Q from \p N, \p D, + * and \p E if the former are not part of the + * implementation.
+ * If the function fails due to an unsupported operation, + * the RSA context stays intact and remains usable. + * + * \note The length parameters are ignored if the corresponding + * buffer pointers are NULL. + * + * \param ctx The initialized RSA context. + * \param N The Byte array to store the RSA modulus, + * or \c NULL if this field need not be exported. + * \param N_len The size of the buffer for the modulus. + * \param P The Byte array to hold the first prime factor of \p N, + * or \c NULL if this field need not be exported. + * \param P_len The size of the buffer for the first prime factor. + * \param Q The Byte array to hold the second prime factor of \p N, + * or \c NULL if this field need not be exported. + * \param Q_len The size of the buffer for the second prime factor. + * \param D The Byte array to hold the private exponent, + * or \c NULL if this field need not be exported. + * \param D_len The size of the buffer for the private exponent. + * \param E The Byte array to hold the public exponent, + * or \c NULL if this field need not be exported. + * \param E_len The size of the buffer for the public exponent. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED if exporting the + * requested parameters cannot be done due to missing + * functionality or because of security policies. + * \return A non-zero return code on any other failure. + */ +int mbedtls_rsa_export_raw( const mbedtls_rsa_context *ctx, + unsigned char *N, size_t N_len, + unsigned char *P, size_t P_len, + unsigned char *Q, size_t Q_len, + unsigned char *D, size_t D_len, + unsigned char *E, size_t E_len ); + +/** + * \brief This function exports CRT parameters of a private RSA key. + * + * \note Alternative RSA implementations not using CRT-parameters + * internally can implement this function based on + * mbedtls_rsa_deduce_opt(). + * + * \param ctx The initialized RSA context. + * \param DP The MPI to hold \c D modulo `P-1`, + * or \c NULL if it need not be exported. + * \param DQ The MPI to hold \c D modulo `Q-1`, + * or \c NULL if it need not be exported. + * \param QP The MPI to hold modular inverse of \c Q modulo \c P, + * or \c NULL if it need not be exported. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + * + */ +int mbedtls_rsa_export_crt( const mbedtls_rsa_context *ctx, + mbedtls_mpi *DP, mbedtls_mpi *DQ, mbedtls_mpi *QP ); + +/** + * \brief This function retrieves the length of RSA modulus in Bytes. + * + * \param ctx The initialized RSA context. + * + * \return The length of the RSA modulus in Bytes. + * + */ +size_t mbedtls_rsa_get_len( const mbedtls_rsa_context *ctx ); + +/** + * \brief This function generates an RSA keypair. + * + * \note mbedtls_rsa_init() must be called before this function, + * to set up the RSA context. + * + * \param ctx The initialized RSA context used to hold the key. + * \param f_rng The RNG function to be used for key generation. + * This is mandatory and must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. + * This may be \c NULL if \p f_rng doesn't need a context. + * \param nbits The size of the public key in bits. + * \param exponent The public exponent to use. For example, \c 65537. + * This must be odd and greater than \c 1. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_gen_key( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ); + +/** + * \brief This function checks if a context contains at least an RSA + * public key. + * + * If the function runs successfully, it is guaranteed that + * enough information is present to perform an RSA public key + * operation using mbedtls_rsa_public(). + * + * \param ctx The initialized RSA context to check. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + * + */ +int mbedtls_rsa_check_pubkey( const mbedtls_rsa_context *ctx ); + +/** + * \brief This function checks if a context contains an RSA private key + * and perform basic consistency checks. + * + * \note The consistency checks performed by this function not only + * ensure that mbedtls_rsa_private() can be called successfully + * on the given context, but that the various parameters are + * mutually consistent with high probability, in the sense that + * mbedtls_rsa_public() and mbedtls_rsa_private() are inverses. + * + * \warning This function should catch accidental misconfigurations + * like swapping of parameters, but it cannot establish full + * trust in neither the quality nor the consistency of the key + * material that was used to setup the given RSA context: + *
  • Consistency: Imported parameters that are irrelevant + * for the implementation might be silently dropped. If dropped, + * the current function does not have access to them, + * and therefore cannot check them. See mbedtls_rsa_complete(). + * If you want to check the consistency of the entire + * content of an PKCS1-encoded RSA private key, for example, you + * should use mbedtls_rsa_validate_params() before setting + * up the RSA context. + * Additionally, if the implementation performs empirical checks, + * these checks substantiate but do not guarantee consistency.
  • + *
  • Quality: This function is not expected to perform + * extended quality assessments like checking that the prime + * factors are safe. Additionally, it is the responsibility of the + * user to ensure the trustworthiness of the source of his RSA + * parameters, which goes beyond what is effectively checkable + * by the library.
+ * + * \param ctx The initialized RSA context to check. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_check_privkey( const mbedtls_rsa_context *ctx ); + +/** + * \brief This function checks a public-private RSA key pair. + * + * It checks each of the contexts, and makes sure they match. + * + * \param pub The initialized RSA context holding the public key. + * \param prv The initialized RSA context holding the private key. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_check_pub_priv( const mbedtls_rsa_context *pub, + const mbedtls_rsa_context *prv ); + +/** + * \brief This function performs an RSA public key operation. + * + * \param ctx The initialized RSA context to use. + * \param input The input buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \note This function does not handle message padding. + * + * \note Make sure to set \p input[0] = 0 or ensure that + * input is smaller than \p N. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_public( mbedtls_rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief This function performs an RSA private key operation. + * + * \note Blinding is used if and only if a PRNG is provided. + * + * \note If blinding is used, both the base of exponentation + * and the exponent are blinded, providing protection + * against some side-channel attacks. + * + * \warning It is deprecated and a security risk to not provide + * a PRNG here and thereby prevent the use of blinding. + * Future versions of the library may enforce the presence + * of a PRNG. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function, used for blinding. It is mandatory. + * \param p_rng The RNG context to pass to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context. + * \param input The input buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + * + */ +int mbedtls_rsa_private( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief This function adds the message padding, then performs an RSA + * operation. + * + * It is the generic wrapper for performing a PKCS#1 encryption + * operation. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG to use. It is used for padding generation + * and it is mandatory. + * \param p_rng The RNG context to be passed to \p f_rng. May be + * \c NULL if \p f_rng doesn't need a context argument. + * \param ilen The length of the plaintext in Bytes. + * \param input The input data to encrypt. This must be a readable + * buffer of size \p ilen Bytes. It may be \c NULL if + * `ilen == 0`. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_encrypt( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief This function performs a PKCS#1 v1.5 encryption operation + * (RSAES-PKCS1-v1_5-ENCRYPT). + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function to use. It is mandatory and used for + * padding generation. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * \param ilen The length of the plaintext in Bytes. + * \param input The input data to encrypt. This must be a readable + * buffer of size \p ilen Bytes. It may be \c NULL if + * `ilen == 0`. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsaes_pkcs1_v15_encrypt( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief This function performs a PKCS#1 v2.1 OAEP encryption + * operation (RSAES-OAEP-ENCRYPT). + * + * \note The output buffer must be as large as the size + * of ctx->N. For example, 128 Bytes if RSA-1024 is used. + * + * \param ctx The initnialized RSA context to use. + * \param f_rng The RNG function to use. This is needed for padding + * generation and is mandatory. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * \param label The buffer holding the custom label to use. + * This must be a readable buffer of length \p label_len + * Bytes. It may be \c NULL if \p label_len is \c 0. + * \param label_len The length of the label in Bytes. + * \param ilen The length of the plaintext buffer \p input in Bytes. + * \param input The input data to encrypt. This must be a readable + * buffer of size \p ilen Bytes. It may be \c NULL if + * `ilen == 0`. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsaes_oaep_encrypt( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief This function performs an RSA operation, then removes the + * message padding. + * + * It is the generic wrapper for performing a PKCS#1 decryption + * operation. + * + * \note The output buffer length \c output_max_len should be + * as large as the size \p ctx->len of \p ctx->N (for example, + * 128 Bytes if RSA-1024 is used) to be able to hold an + * arbitrary decrypted message. If it is not large enough to + * hold the decryption of the particular ciphertext provided, + * the function returns \c MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory; see mbedtls_rsa_private() for more. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context. + * \param olen The address at which to store the length of + * the plaintext. This must not be \c NULL. + * \param input The ciphertext buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The buffer used to hold the plaintext. This must + * be a writable buffer of length \p output_max_len Bytes. + * \param output_max_len The length in Bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_decrypt( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief This function performs a PKCS#1 v1.5 decryption + * operation (RSAES-PKCS1-v1_5-DECRYPT). + * + * \note The output buffer length \c output_max_len should be + * as large as the size \p ctx->len of \p ctx->N, for example, + * 128 Bytes if RSA-1024 is used, to be able to hold an + * arbitrary decrypted message. If it is not large enough to + * hold the decryption of the particular ciphertext provided, + * the function returns #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory; see mbedtls_rsa_private() for more. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context. + * \param olen The address at which to store the length of + * the plaintext. This must not be \c NULL. + * \param input The ciphertext buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The buffer used to hold the plaintext. This must + * be a writable buffer of length \p output_max_len Bytes. + * \param output_max_len The length in Bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + * + */ +int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief This function performs a PKCS#1 v2.1 OAEP decryption + * operation (RSAES-OAEP-DECRYPT). + * + * \note The output buffer length \c output_max_len should be + * as large as the size \p ctx->len of \p ctx->N, for + * example, 128 Bytes if RSA-1024 is used, to be able to + * hold an arbitrary decrypted message. If it is not + * large enough to hold the decryption of the particular + * ciphertext provided, the function returns + * #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context. + * \param label The buffer holding the custom label to use. + * This must be a readable buffer of length \p label_len + * Bytes. It may be \c NULL if \p label_len is \c 0. + * \param label_len The length of the label in Bytes. + * \param olen The address at which to store the length of + * the plaintext. This must not be \c NULL. + * \param input The ciphertext buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The buffer used to hold the plaintext. This must + * be a writable buffer of length \p output_max_len Bytes. + * \param output_max_len The length in Bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsaes_oaep_decrypt( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief This function performs a private RSA operation to sign + * a message digest using PKCS#1. + * + * It is the generic wrapper for performing a PKCS#1 + * signature. + * + * \note The \p sig buffer must be as large as the size + * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used. + * + * \note For PKCS#1 v2.1 encoding, see comments on + * mbedtls_rsa_rsassa_pss_sign() for details on + * \p md_alg and \p hash_id. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function to use. This is mandatory and + * must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_sign( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief This function performs a PKCS#1 v1.5 signature + * operation (RSASSA-PKCS1-v1_5-SIGN). + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory; see mbedtls_rsa_private() for more. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pkcs1_v15_sign( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS signature + * operation (RSASSA-PSS-SIGN). + * + * \note The \c hash_id set in \p ctx by calling + * mbedtls_rsa_set_padding() selects the hash used for the + * encoding operation and for the mask generation function + * (MGF1). For more details on the encoding operation and the + * mask generation function, consult RFC-3447: Public-Key + * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography + * Specifications. + * + * \note This function enforces that the provided salt length complies + * with FIPS 186-4 §5.5 (e) and RFC 8017 (PKCS#1 v2.2) §9.1.1 + * step 3. The constraint is that the hash length plus the salt + * length plus 2 bytes must be at most the key length. If this + * constraint is not met, this function returns + * #MBEDTLS_ERR_RSA_BAD_INPUT_DATA. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. It is mandatory and must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param saltlen The length of the salt that should be used. + * If passed #MBEDTLS_RSA_SALT_LEN_ANY, the function will use + * the largest possible salt length up to the hash length, + * which is the largest permitted by some standards including + * FIPS 186-4 §5.5. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_sign_ext( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + int saltlen, + unsigned char *sig ); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS signature + * operation (RSASSA-PSS-SIGN). + * + * \note The \c hash_id set in \p ctx by calling + * mbedtls_rsa_set_padding() selects the hash used for the + * encoding operation and for the mask generation function + * (MGF1). For more details on the encoding operation and the + * mask generation function, consult RFC-3447: Public-Key + * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography + * Specifications. + * + * \note This function always uses the maximum possible salt size, + * up to the length of the payload hash. This choice of salt + * size complies with FIPS 186-4 §5.5 (e) and RFC 8017 (PKCS#1 + * v2.2) §9.1.1 step 3. Furthermore this function enforces a + * minimum salt size which is the hash size minus 2 bytes. If + * this minimum size is too large given the key size (the salt + * size, plus the hash size, plus 2 bytes must be no more than + * the key size in bytes), this function returns + * #MBEDTLS_ERR_RSA_BAD_INPUT_DATA. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. It is mandatory and must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_sign( mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief This function performs a public RSA operation and checks + * the message digest. + * + * This is the generic wrapper for performing a PKCS#1 + * verification. + * + * \note For PKCS#1 v2.1 encoding, see comments on + * mbedtls_rsa_rsassa_pss_verify() about \p md_alg and + * \p hash_id. + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_verify( mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief This function performs a PKCS#1 v1.5 verification + * operation (RSASSA-PKCS1-v1_5-VERIFY). + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pkcs1_v15_verify( mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS verification + * operation (RSASSA-PSS-VERIFY). + * + * \note The \c hash_id set in \p ctx by calling + * mbedtls_rsa_set_padding() selects the hash used for the + * encoding operation and for the mask generation function + * (MGF1). For more details on the encoding operation and the + * mask generation function, consult RFC-3447: Public-Key + * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography + * Specifications. If the \c hash_id set in \p ctx by + * mbedtls_rsa_set_padding() is #MBEDTLS_MD_NONE, the \p md_alg + * parameter is used. + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_verify( mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS verification + * operation (RSASSA-PSS-VERIFY). + * + * \note The \p sig buffer must be as large as the size + * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used. + * + * \note The \c hash_id set in \p ctx by mbedtls_rsa_set_padding() is + * ignored. + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param mgf1_hash_id The message digest algorithm used for the + * verification operation and the mask generation + * function (MGF1). For more details on the encoding + * operation and the mask generation function, consult + * RFC-3447: Public-Key Cryptography Standards + * (PKCS) #1 v2.1: RSA Cryptography + * Specifications. + * \param expected_salt_len The length of the salt used in padding. Use + * #MBEDTLS_RSA_SALT_LEN_ANY to accept any salt length. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_verify_ext( mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + mbedtls_md_type_t mgf1_hash_id, + int expected_salt_len, + const unsigned char *sig ); + +/** + * \brief This function copies the components of an RSA context. + * + * \param dst The destination context. This must be initialized. + * \param src The source context. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory allocation failure. + */ +int mbedtls_rsa_copy( mbedtls_rsa_context *dst, const mbedtls_rsa_context *src ); + +/** + * \brief This function frees the components of an RSA key. + * + * \param ctx The RSA context to free. May be \c NULL, in which case + * this function is a no-op. If it is not \c NULL, it must + * point to an initialized RSA context. + */ +void mbedtls_rsa_free( mbedtls_rsa_context *ctx ); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The RSA checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_rsa_self_test( int verbose ); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/threading.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/threading.h new file mode 100644 index 0000000..96cadc4 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/threading.h @@ -0,0 +1,116 @@ +/** + * \file threading.h + * + * \brief Threading abstraction layer + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_THREADING_H +#define MBEDTLS_THREADING_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MBEDTLS_ERR_THREADING_BAD_INPUT_DATA -0x001C /**< Bad input parameters to function. */ +#define MBEDTLS_ERR_THREADING_MUTEX_ERROR -0x001E /**< Locking / unlocking / free failed with error code. */ + +#if defined(MBEDTLS_THREADING_PTHREAD) +#include +typedef struct mbedtls_threading_mutex_t +{ + pthread_mutex_t MBEDTLS_PRIVATE(mutex); + /* is_valid is 0 after a failed init or a free, and nonzero after a + * successful init. This field is not considered part of the public + * API of Mbed TLS and may change without notice. */ + char MBEDTLS_PRIVATE(is_valid); +} mbedtls_threading_mutex_t; +#endif + +#if defined(MBEDTLS_THREADING_ALT) +/* You should define the mbedtls_threading_mutex_t type in your header */ +#include "threading_alt.h" + +/** + * \brief Set your alternate threading implementation function + * pointers and initialize global mutexes. If used, this + * function must be called once in the main thread before any + * other mbed TLS function is called, and + * mbedtls_threading_free_alt() must be called once in the main + * thread after all other mbed TLS functions. + * + * \note mutex_init() and mutex_free() don't return a status code. + * If mutex_init() fails, it should leave its argument (the + * mutex) in a state such that mutex_lock() will fail when + * called with this argument. + * + * \param mutex_init the init function implementation + * \param mutex_free the free function implementation + * \param mutex_lock the lock function implementation + * \param mutex_unlock the unlock function implementation + */ +void mbedtls_threading_set_alt( void (*mutex_init)( mbedtls_threading_mutex_t * ), + void (*mutex_free)( mbedtls_threading_mutex_t * ), + int (*mutex_lock)( mbedtls_threading_mutex_t * ), + int (*mutex_unlock)( mbedtls_threading_mutex_t * ) ); + +/** + * \brief Free global mutexes. + */ +void mbedtls_threading_free_alt( void ); +#endif /* MBEDTLS_THREADING_ALT */ + +#if defined(MBEDTLS_THREADING_C) +/* + * The function pointers for mutex_init, mutex_free, mutex_ and mutex_unlock + * + * All these functions are expected to work or the result will be undefined. + */ +extern void (*mbedtls_mutex_init)( mbedtls_threading_mutex_t *mutex ); +extern void (*mbedtls_mutex_free)( mbedtls_threading_mutex_t *mutex ); +extern int (*mbedtls_mutex_lock)( mbedtls_threading_mutex_t *mutex ); +extern int (*mbedtls_mutex_unlock)( mbedtls_threading_mutex_t *mutex ); + +/* + * Global mutexes + */ +#if defined(MBEDTLS_FS_IO) +extern mbedtls_threading_mutex_t mbedtls_threading_readdir_mutex; +#endif + +#if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_PLATFORM_GMTIME_R_ALT) +/* This mutex may or may not be used in the default definition of + * mbedtls_platform_gmtime_r(), but in order to determine that, + * we need to check POSIX features, hence modify _POSIX_C_SOURCE. + * With the current approach, this declaration is orphaned, lacking + * an accompanying definition, in case mbedtls_platform_gmtime_r() + * doesn't need it, but that's not a problem. */ +extern mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex; +#endif /* MBEDTLS_HAVE_TIME_DATE && !MBEDTLS_PLATFORM_GMTIME_R_ALT */ + +#endif /* MBEDTLS_THREADING_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* threading.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/version.h b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/version.h new file mode 100644 index 0000000..773da4a --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/include/mbedtls/version.h @@ -0,0 +1,90 @@ +/** + * \file version.h + * + * \brief Run-time version information + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This set of run-time variables can be used to determine the version number of + * the Mbed TLS library used. Compile-time version defines for the same can be + * found in build_info.h + */ +#ifndef MBEDTLS_VERSION_H +#define MBEDTLS_VERSION_H + +#include "mbedtls/build_info.h" + +#if defined(MBEDTLS_VERSION_C) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get the version number. + * + * \return The constructed version number in the format + * MMNNPP00 (Major, Minor, Patch). + */ +unsigned int mbedtls_version_get_number( void ); + +/** + * Get the version string ("x.y.z"). + * + * \param string The string that will receive the value. + * (Should be at least 9 bytes in size) + */ +void mbedtls_version_get_string( char *string ); + +/** + * Get the full version string ("mbed TLS x.y.z"). + * + * \param string The string that will receive the value. The mbed TLS version + * string will use 18 bytes AT MOST including a terminating + * null byte. + * (So the buffer should be at least 18 bytes to receive this + * version string). + */ +void mbedtls_version_get_string_full( char *string ); + +/** + * \brief Check if support for a feature was compiled into this + * mbed TLS binary. This allows you to see at runtime if the + * library was for instance compiled with or without + * Multi-threading support. + * + * \note only checks against defines in the sections "System + * support", "mbed TLS modules" and "mbed TLS feature + * support" in mbedtls_config.h + * + * \param feature The string for the define to check (e.g. "MBEDTLS_AES_C") + * + * \return 0 if the feature is present, + * -1 if the feature is not present and + * -2 if support for feature checking as a whole was not + * compiled in. + */ +int mbedtls_version_check_feature( const char *feature ); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_VERSION_C */ + +#endif /* version.h */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/pkg.yml b/bootloader/mcuboot/ext/mbedtls-asn1/pkg.yml new file mode 100644 index 0000000..148f9c7 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/pkg.yml @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: ext/mbedtls-asn1 +pkg.description: "MCUboot's bundled mbed-tls ASN1 parser" +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/src/asn1parse.c b/bootloader/mcuboot/ext/mbedtls-asn1/src/asn1parse.c new file mode 100644 index 0000000..83c7c58 --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/src/asn1parse.c @@ -0,0 +1,481 @@ +/* + * Generic ASN.1 parsing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_ASN1_PARSE_C) + +#include "mbedtls/asn1.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#define mbedtls_calloc calloc +#define mbedtls_free free +#endif + +/* + * ASN.1 DER decoding routines + */ +int mbedtls_asn1_get_len( unsigned char **p, + const unsigned char *end, + size_t *len ) +{ + if( ( end - *p ) < 1 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + if( ( **p & 0x80 ) == 0 ) + *len = *(*p)++; + else + { + switch( **p & 0x7F ) + { + case 1: + if( ( end - *p ) < 2 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + *len = (*p)[1]; + (*p) += 2; + break; + + case 2: + if( ( end - *p ) < 3 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (size_t)(*p)[1] << 8 ) | (*p)[2]; + (*p) += 3; + break; + + case 3: + if( ( end - *p ) < 4 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (size_t)(*p)[1] << 16 ) | + ( (size_t)(*p)[2] << 8 ) | (*p)[3]; + (*p) += 4; + break; + + case 4: + if( ( end - *p ) < 5 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (size_t)(*p)[1] << 24 ) | ( (size_t)(*p)[2] << 16 ) | + ( (size_t)(*p)[3] << 8 ) | (*p)[4]; + (*p) += 5; + break; + + default: + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + } + } + + if( *len > (size_t) ( end - *p ) ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + return( 0 ); +} + +int mbedtls_asn1_get_tag( unsigned char **p, + const unsigned char *end, + size_t *len, int tag ) +{ + if( ( end - *p ) < 1 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + if( **p != tag ) + return( MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); + + (*p)++; + + return( mbedtls_asn1_get_len( p, end, len ) ); +} + +int mbedtls_asn1_get_bool( unsigned char **p, + const unsigned char *end, + int *val ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_BOOLEAN ) ) != 0 ) + return( ret ); + + if( len != 1 ) + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + + *val = ( **p != 0 ) ? 1 : 0; + (*p)++; + + return( 0 ); +} + +static int asn1_get_tagged_int( unsigned char **p, + const unsigned char *end, + int tag, int *val ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if( ( ret = mbedtls_asn1_get_tag( p, end, &len, tag ) ) != 0 ) + return( ret ); + + /* + * len==0 is malformed (0 must be represented as 020100 for INTEGER, + * or 0A0100 for ENUMERATED tags + */ + if( len == 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + /* This is a cryptography library. Reject negative integers. */ + if( ( **p & 0x80 ) != 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + + /* Skip leading zeros. */ + while( len > 0 && **p == 0 ) + { + ++( *p ); + --len; + } + + /* Reject integers that don't fit in an int. This code assumes that + * the int type has no padding bit. */ + if( len > sizeof( int ) ) + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + if( len == sizeof( int ) && ( **p & 0x80 ) != 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + + *val = 0; + while( len-- > 0 ) + { + *val = ( *val << 8 ) | **p; + (*p)++; + } + + return( 0 ); +} + +int mbedtls_asn1_get_int( unsigned char **p, + const unsigned char *end, + int *val ) +{ + return( asn1_get_tagged_int( p, end, MBEDTLS_ASN1_INTEGER, val) ); +} + +int mbedtls_asn1_get_enum( unsigned char **p, + const unsigned char *end, + int *val ) +{ + return( asn1_get_tagged_int( p, end, MBEDTLS_ASN1_ENUMERATED, val) ); +} + +#if defined(MBEDTLS_BIGNUM_C) +int mbedtls_asn1_get_mpi( unsigned char **p, + const unsigned char *end, + mbedtls_mpi *X ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_INTEGER ) ) != 0 ) + return( ret ); + + ret = mbedtls_mpi_read_binary( X, *p, len ); + + *p += len; + + return( ret ); +} +#endif /* MBEDTLS_BIGNUM_C */ + +int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end, + mbedtls_asn1_bitstring *bs) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Certificate type is a single byte bitstring */ + if( ( ret = mbedtls_asn1_get_tag( p, end, &bs->len, MBEDTLS_ASN1_BIT_STRING ) ) != 0 ) + return( ret ); + + /* Check length, subtract one for actual bit string length */ + if( bs->len < 1 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + bs->len -= 1; + + /* Get number of unused bits, ensure unused bits <= 7 */ + bs->unused_bits = **p; + if( bs->unused_bits > 7 ) + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + (*p)++; + + /* Get actual bitstring */ + bs->p = *p; + *p += bs->len; + + if( *p != end ) + return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * Traverse an ASN.1 "SEQUENCE OF " + * and call a callback for each entry found. + */ +int mbedtls_asn1_traverse_sequence_of( + unsigned char **p, + const unsigned char *end, + unsigned char tag_must_mask, unsigned char tag_must_val, + unsigned char tag_may_mask, unsigned char tag_may_val, + int (*cb)( void *ctx, int tag, + unsigned char *start, size_t len ), + void *ctx ) +{ + int ret; + size_t len; + + /* Get main sequence tag */ + if( ( ret = mbedtls_asn1_get_tag( p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + { + return( ret ); + } + + if( *p + len != end ) + return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + + while( *p < end ) + { + unsigned char const tag = *(*p)++; + + if( ( tag & tag_must_mask ) != tag_must_val ) + return( MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); + + if( ( ret = mbedtls_asn1_get_len( p, end, &len ) ) != 0 ) + return( ret ); + + if( ( tag & tag_may_mask ) == tag_may_val ) + { + if( cb != NULL ) + { + ret = cb( ctx, tag, *p, len ); + if( ret != 0 ) + return( ret ); + } + } + + *p += len; + } + + return( 0 ); +} + +/* + * Get a bit string without unused bits + */ +int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end, + size_t *len ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if( ( ret = mbedtls_asn1_get_tag( p, end, len, MBEDTLS_ASN1_BIT_STRING ) ) != 0 ) + return( ret ); + + if( *len == 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_DATA ); + --( *len ); + + if( **p != 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_DATA ); + ++( *p ); + + return( 0 ); +} + +void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq ) +{ + while( seq != NULL ) + { + mbedtls_asn1_sequence *next = seq->next; + mbedtls_platform_zeroize( seq, sizeof( *seq ) ); + mbedtls_free( seq ); + seq = next; + } +} + +typedef struct +{ + int tag; + mbedtls_asn1_sequence *cur; +} asn1_get_sequence_of_cb_ctx_t; + +static int asn1_get_sequence_of_cb( void *ctx, + int tag, + unsigned char *start, + size_t len ) +{ + asn1_get_sequence_of_cb_ctx_t *cb_ctx = + (asn1_get_sequence_of_cb_ctx_t *) ctx; + mbedtls_asn1_sequence *cur = + cb_ctx->cur; + + if( cur->buf.p != NULL ) + { + cur->next = + mbedtls_calloc( 1, sizeof( mbedtls_asn1_sequence ) ); + + if( cur->next == NULL ) + return( MBEDTLS_ERR_ASN1_ALLOC_FAILED ); + + cur = cur->next; + } + + cur->buf.p = start; + cur->buf.len = len; + cur->buf.tag = tag; + + cb_ctx->cur = cur; + return( 0 ); +} + +/* + * Parses and splits an ASN.1 "SEQUENCE OF " + */ +int mbedtls_asn1_get_sequence_of( unsigned char **p, + const unsigned char *end, + mbedtls_asn1_sequence *cur, + int tag) +{ + asn1_get_sequence_of_cb_ctx_t cb_ctx = { tag, cur }; + memset( cur, 0, sizeof( mbedtls_asn1_sequence ) ); + return( mbedtls_asn1_traverse_sequence_of( + p, end, 0xFF, tag, 0, 0, + asn1_get_sequence_of_cb, &cb_ctx ) ); +} + +int mbedtls_asn1_get_alg( unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if( ( ret = mbedtls_asn1_get_tag( p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + return( ret ); + + if( ( end - *p ) < 1 ) + return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + + alg->tag = **p; + end = *p + len; + + if( ( ret = mbedtls_asn1_get_tag( p, end, &alg->len, MBEDTLS_ASN1_OID ) ) != 0 ) + return( ret ); + + alg->p = *p; + *p += alg->len; + + if( *p == end ) + { + mbedtls_platform_zeroize( params, sizeof(mbedtls_asn1_buf) ); + return( 0 ); + } + + params->tag = **p; + (*p)++; + + if( ( ret = mbedtls_asn1_get_len( p, end, ¶ms->len ) ) != 0 ) + return( ret ); + + params->p = *p; + *p += params->len; + + if( *p != end ) + return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +int mbedtls_asn1_get_alg_null( unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg ) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_asn1_buf params; + + memset( ¶ms, 0, sizeof(mbedtls_asn1_buf) ); + + if( ( ret = mbedtls_asn1_get_alg( p, end, alg, ¶ms ) ) != 0 ) + return( ret ); + + if( ( params.tag != MBEDTLS_ASN1_NULL && params.tag != 0 ) || params.len != 0 ) + return( MBEDTLS_ERR_ASN1_INVALID_DATA ); + + return( 0 ); +} + +void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *cur ) +{ + if( cur == NULL ) + return; + + mbedtls_free( cur->oid.p ); + mbedtls_free( cur->val.p ); + + mbedtls_platform_zeroize( cur, sizeof( mbedtls_asn1_named_data ) ); +} + +void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head ) +{ + mbedtls_asn1_named_data *cur; + + while( ( cur = *head ) != NULL ) + { + *head = cur->next; + mbedtls_asn1_free_named_data( cur ); + mbedtls_free( cur ); + } +} + +const mbedtls_asn1_named_data *mbedtls_asn1_find_named_data( const mbedtls_asn1_named_data *list, + const char *oid, size_t len ) +{ + while( list != NULL ) + { + if( list->oid.len == len && + memcmp( list->oid.p, oid, len ) == 0 ) + { + break; + } + + list = list->next; + } + + return( list ); +} + +#endif /* MBEDTLS_ASN1_PARSE_C */ diff --git a/bootloader/mcuboot/ext/mbedtls-asn1/src/platform_util.c b/bootloader/mcuboot/ext/mbedtls-asn1/src/platform_util.c new file mode 100644 index 0000000..4e97e4d --- /dev/null +++ b/bootloader/mcuboot/ext/mbedtls-asn1/src/platform_util.c @@ -0,0 +1,133 @@ +/* + * Common and shared functions used by multiple modules in the Mbed TLS + * library. + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Ensure gmtime_r is available even with -std=c99; must be defined before + * mbedtls_config.h, which pulls in glibc's features.h. Harmless on other platforms. + */ +#if !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200112L +#endif + +#include "common.h" + +#include "mbedtls/platform_util.h" +#include "mbedtls/platform.h" +#include "mbedtls/threading.h" + +#include +#include + +#if !defined(MBEDTLS_PLATFORM_ZEROIZE_ALT) +/* + * This implementation should never be optimized out by the compiler + * + * This implementation for mbedtls_platform_zeroize() was inspired from Colin + * Percival's blog article at: + * + * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html + * + * It uses a volatile function pointer to the standard memset(). Because the + * pointer is volatile the compiler expects it to change at + * any time and will not optimize out the call that could potentially perform + * other operations on the input buffer instead of just setting it to 0. + * Nevertheless, as pointed out by davidtgoldblatt on Hacker News + * (refer to http://www.daemonology.net/blog/2014-09-05-erratum.html for + * details), optimizations of the following form are still possible: + * + * if( memset_func != memset ) + * memset_func( buf, 0, len ); + * + * Note that it is extremely difficult to guarantee that + * mbedtls_platform_zeroize() will not be optimized out by aggressive compilers + * in a portable way. For this reason, Mbed TLS also provides the configuration + * option MBEDTLS_PLATFORM_ZEROIZE_ALT, which allows users to configure + * mbedtls_platform_zeroize() to use a suitable implementation for their + * platform and needs. + */ +static void * (* const volatile memset_func)( void *, int, size_t ) = memset; + +void mbedtls_platform_zeroize( void *buf, size_t len ) +{ + MBEDTLS_INTERNAL_VALIDATE( len == 0 || buf != NULL ); + + if( len > 0 ) + memset_func( buf, 0, len ); +} +#endif /* MBEDTLS_PLATFORM_ZEROIZE_ALT */ + +#if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_PLATFORM_GMTIME_R_ALT) +#include +#if !defined(_WIN32) && (defined(unix) || \ + defined(__unix) || defined(__unix__) || (defined(__APPLE__) && \ + defined(__MACH__))) +#include +#endif /* !_WIN32 && (unix || __unix || __unix__ || + * (__APPLE__ && __MACH__)) */ + +#if !( ( defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L ) || \ + ( defined(_POSIX_THREAD_SAFE_FUNCTIONS ) && \ + _POSIX_THREAD_SAFE_FUNCTIONS >= 200112L ) ) +/* + * This is a convenience shorthand macro to avoid checking the long + * preprocessor conditions above. Ideally, we could expose this macro in + * platform_util.h and simply use it in platform_util.c, threading.c and + * threading.h. However, this macro is not part of the Mbed TLS public API, so + * we keep it private by only defining it in this file + */ +#if ! ( defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) ) +#define PLATFORM_UTIL_USE_GMTIME +#endif /* ! ( defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) ) */ + +#endif /* !( ( defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L ) || \ + ( defined(_POSIX_THREAD_SAFE_FUNCTIONS ) && \ + _POSIX_THREAD_SAFE_FUNCTIONS >= 200112L ) ) */ + +struct tm *mbedtls_platform_gmtime_r( const mbedtls_time_t *tt, + struct tm *tm_buf ) +{ +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + return( ( gmtime_s( tm_buf, tt ) == 0 ) ? tm_buf : NULL ); +#elif !defined(PLATFORM_UTIL_USE_GMTIME) + return( gmtime_r( tt, tm_buf ) ); +#else + struct tm *lt; + +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_lock( &mbedtls_threading_gmtime_mutex ) != 0 ) + return( NULL ); +#endif /* MBEDTLS_THREADING_C */ + + lt = gmtime( tt ); + + if( lt != NULL ) + { + memcpy( tm_buf, lt, sizeof( struct tm ) ); + } + +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_unlock( &mbedtls_threading_gmtime_mutex ) != 0 ) + return( NULL ); +#endif /* MBEDTLS_THREADING_C */ + + return( ( lt == NULL ) ? NULL : tm_buf ); +#endif /* _WIN32 && !EFIX64 && !EFI32 */ +} +#endif /* MBEDTLS_HAVE_TIME_DATE && MBEDTLS_PLATFORM_GMTIME_R_ALT */ diff --git a/bootloader/mcuboot/ext/nrf/README.md b/bootloader/mcuboot/ext/nrf/README.md new file mode 100644 index 0000000..8346f42 --- /dev/null +++ b/bootloader/mcuboot/ext/nrf/README.md @@ -0,0 +1,18 @@ +# Building MCUboot with nRF52840 CC310 enabled + +## Prerequisites + +Clone [nrfxlib](https://github.com/NordicPlayground/nrfxlib) next to the MCUboot root folder. So that it's located `../nrfxlib` from MCUboot root folder. + +## Building + +make sure `root-ec-p256.pem` is set as the certificate and that `CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256` is selected not `CONFIG_BOOT_SIGNATURE_TYPE_RSA` in `prj.conf` of `boot/zephyr`. +Since it defaults to tinycrypt you'll have to go into `menuconfig` and change the implementation selection to `cc310` or also set this in `prj.conf`. + +``` +mkdir build && cd build +cmake -GNinja -DBOARD=nrf52840dk/nrf52840 +ninja flash +``` + +Build a hello world example in zephyr and sign it with imgtool.py with the `root-ec-p256.pem` and flash it at `FLASH_AREA_IMAGE_0`. diff --git a/bootloader/mcuboot/ext/nrf/cc310_glue.c b/bootloader/mcuboot/ext/nrf/cc310_glue.c new file mode 100644 index 0000000..759a8b6 --- /dev/null +++ b/bootloader/mcuboot/ext/nrf/cc310_glue.c @@ -0,0 +1,73 @@ +/* + * Copyright Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cc310_glue.h" + +int cc310_init(void) +{ + /* Only initialize once */ + static bool initialized; + + if (!initialized) { + nrf_cc310_enable(); + if (nrf_cc310_bl_init() != 0) { + return -1; + } + initialized = true; + nrf_cc310_disable(); + } + + return 0; +} + +void cc310_sha256_update(nrf_cc310_bl_hash_context_sha256_t *ctx, + const void *data, + uint32_t data_len) +{ + /* + * NRF Cryptocell can only read from RAM this allocates a buffer on the stack + * if the data provided is not located in RAM. + */ + + if ((uint32_t) data < CONFIG_SRAM_BASE_ADDRESS) { + uint8_t stack_buffer[data_len]; + uint32_t block_len = data_len; + memcpy(stack_buffer, data, block_len); + nrf_cc310_bl_hash_sha256_update(ctx, stack_buffer, block_len); + } else { + nrf_cc310_bl_hash_sha256_update(ctx, data, data_len); + } +}; + +int cc310_ecdsa_verify_secp256r1(uint8_t *hash, + uint8_t *public_key, + uint8_t *signature, + size_t hash_len) +{ + int rc; + nrf_cc310_bl_ecdsa_verify_context_secp256r1_t ctx; + cc310_init(); + nrf_cc310_enable(); + rc = nrf_cc310_bl_ecdsa_verify_secp256r1(&ctx, + (nrf_cc310_bl_ecc_public_key_secp256r1_t *) public_key, + (nrf_cc310_bl_ecc_signature_secp256r1_t *) signature, + hash, + hash_len); + nrf_cc310_disable(); + return rc; +} + diff --git a/bootloader/mcuboot/ext/nrf/cc310_glue.h b/bootloader/mcuboot/ext/nrf/cc310_glue.h new file mode 100644 index 0000000..22eb949 --- /dev/null +++ b/bootloader/mcuboot/ext/nrf/cc310_glue.h @@ -0,0 +1,77 @@ +/* + * Copyright Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NRF_CC310_GLUE_H__ +#define NRF_CC310_GLUE_H__ + +#include +#include +#include +#include +#include +#include + +/* + * Name translation for peripherals with only one type of access available. + */ +#if !defined(NRF_TRUSTZONE_NONSECURE) && defined(CONFIG_ARM_TRUSTZONE_M) +#define NRF_CRYPTOCELL NRF_CRYPTOCELL_S +#endif + +typedef nrf_cc310_bl_hash_context_sha256_t bootutil_sha_context; + +int cc310_ecdsa_verify_secp256r1(uint8_t *hash, + uint8_t *public_key, + uint8_t *signature, + size_t hash_len); + + +int cc310_init(void); + +static inline void cc310_sha256_init(nrf_cc310_bl_hash_context_sha256_t *ctx); + +void cc310_sha256_update(nrf_cc310_bl_hash_context_sha256_t *ctx, + const void *data, + uint32_t data_len); + +static inline void nrf_cc310_enable(void) +{ + NRF_CRYPTOCELL->ENABLE=1; +} + +static inline void nrf_cc310_disable(void) +{ + NRF_CRYPTOCELL->ENABLE=0; +} + +/* Enable and disable cc310 to reduce power consumption */ +static inline void cc310_sha256_init(nrf_cc310_bl_hash_context_sha256_t * ctx) +{ + cc310_init(); + nrf_cc310_enable(); + nrf_cc310_bl_hash_sha256_init(ctx); +} + +static inline void cc310_sha256_finalize(nrf_cc310_bl_hash_context_sha256_t *ctx, + uint8_t *output) +{ + nrf_cc310_bl_hash_sha256_finalize(ctx, + (nrf_cc310_bl_hash_digest_sha256_t *)output); + nrf_cc310_disable(); +} + +#endif /* NRF_CC310_GLUE_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt-sha512/lib/include/tinycrypt/sha512.h b/bootloader/mcuboot/ext/tinycrypt-sha512/lib/include/tinycrypt/sha512.h new file mode 100644 index 0000000..f0211b2 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt-sha512/lib/include/tinycrypt/sha512.h @@ -0,0 +1,129 @@ +/* sha512.h - TinyCrypt interface to a SHA-512 implementation */ + +/* + * Copyright (C) 2020 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to a SHA-512 implementation. + * + * Overview: SHA-512 is a NIST approved cryptographic hashing algorithm + * specified in FIPS 180. A hash algorithm maps data of arbitrary + * size to data of fixed length. + * + * Security: SHA-512 provides 256 bits of security against collision attacks + * and 512 bits of security against pre-image attacks. SHA-512 does + * NOT behave like a random oracle, but it can be used as one if + * the string being hashed is prefix-free encoded before hashing. + * + * Usage: 1) call tc_sha512_init to initialize a struct + * tc_sha512_state_struct before hashing a new string. + * + * 2) call tc_sha512_update to hash the next string segment; + * tc_sha512_update can be called as many times as needed to hash + * all of the segments of a string; the order is important. + * + * 3) call tc_sha512_final to out put the digest from a hashing + * operation. + */ + +#ifndef __TC_SHA512_H__ +#define __TC_SHA512_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_SHA512_BLOCK_SIZE (128) +#define TC_SHA512_DIGEST_SIZE (64) +#define TC_SHA512_STATE_BLOCKS (TC_SHA512_DIGEST_SIZE/8) + +struct tc_sha512_state_struct { + uint64_t iv[TC_SHA512_STATE_BLOCKS]; + uint64_t bits_hashed; + uint8_t leftover[TC_SHA512_BLOCK_SIZE]; + size_t leftover_offset; +}; + +typedef struct tc_sha512_state_struct *TCSha512State_t; + +/** + * @brief SHA512 initialization procedure + * Initializes s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if s == NULL + * @param s Sha512 state struct + */ +int tc_sha512_init(TCSha512State_t s); + +/** + * @brief SHA512 update procedure + * Hashes data_length bytes addressed by data into state s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * data == NULL + * @note Assumes s has been initialized by tc_sha512_init + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param s Sha512 state struct + * @param data message to hash + * @param datalen length of message to hash + */ +int tc_sha512_update (TCSha512State_t s, const uint8_t *data, size_t datalen); + +/** + * @brief SHA512 final procedure + * Inserts the completed hash computation into digest + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * digest == NULL + * @note Assumes: s has been initialized by tc_sha512_init + * digest points to at least TC_SHA512_DIGEST_SIZE bytes + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param digest unsigned eight bit integer + * @param Sha512 state struct + */ +int tc_sha512_final(uint8_t *digest, TCSha512State_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_SHA512_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt-sha512/lib/pkg.yml b/bootloader/mcuboot/ext/tinycrypt-sha512/lib/pkg.yml new file mode 100644 index 0000000..b9cb8e6 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt-sha512/lib/pkg.yml @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: ext/tinycrypt-sha512/lib +pkg.description: "MCUboot's SHA512 for tinycrypt" +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.src_dirs: + - "source" + +pkg.cflags: + - "-std=c99" diff --git a/bootloader/mcuboot/ext/tinycrypt-sha512/lib/source/sha512.c b/bootloader/mcuboot/ext/tinycrypt-sha512/lib/source/sha512.c new file mode 100644 index 0000000..eb3aa76 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt-sha512/lib/source/sha512.c @@ -0,0 +1,234 @@ +/* sha512.c - TinyCrypt SHA-512 crypto hash algorithm implementation */ + +/* + * Copyright (C) 2020 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +static void compress(uint64_t *iv, const uint8_t *data); + +int tc_sha512_init(TCSha512State_t s) +{ + /* input sanity check: */ + if (s == (TCSha512State_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* + * Setting the initial state values. + * These values correspond to the first 64 bits of the fractional parts + * of the square roots of the first 8 primes: 2, 3, 5, 7, 11, 13, 17 + * and 19. + */ + _set((uint8_t *) s, 0x00, sizeof(*s)); + s->iv[0] = 0x6a09e667f3bcc908; + s->iv[1] = 0xbb67ae8584caa73b; + s->iv[2] = 0x3c6ef372fe94f82b; + s->iv[3] = 0xa54ff53a5f1d36f1; + s->iv[4] = 0x510e527fade682d1; + s->iv[5] = 0x9b05688c2b3e6c1f; + s->iv[6] = 0x1f83d9abfb41bd6b; + s->iv[7] = 0x5be0cd19137e2179; + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha512_update(TCSha512State_t s, const uint8_t *data, size_t datalen) +{ + /* input sanity check: */ + if (s == (TCSha512State_t) 0 || data == (void *) 0) { + return TC_CRYPTO_FAIL; + } else if (datalen == 0) { + return TC_CRYPTO_SUCCESS; + } + + while (datalen-- > 0) { + s->leftover[s->leftover_offset++] = *(data++); + if (s->leftover_offset >= TC_SHA512_BLOCK_SIZE) { + compress(s->iv, s->leftover); + s->leftover_offset = 0; + s->bits_hashed += (TC_SHA512_BLOCK_SIZE << 3); + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha512_final(uint8_t *digest, TCSha512State_t s) +{ + unsigned int i; + + /* input sanity check: */ + if (digest == (uint8_t *) 0 || s == (TCSha512State_t) 0) { + return TC_CRYPTO_FAIL; + } + + s->bits_hashed += (s->leftover_offset << 3); + + s->leftover[s->leftover_offset++] = 0x80; /* always room for one byte */ + if (s->leftover_offset > (sizeof(s->leftover) - 16)) { + /* there is not room for all the padding in this block */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - s->leftover_offset); + compress(s->iv, s->leftover); + s->leftover_offset = 0; + } + + /* + * add the padding and the length in big-Endian format + * + * NOTE: SHA-512 uses 128 bits for the length of the message, but the + * current implementation is only using 64 bits for size, leaving the + * 64 "upper" bits zeroed. + */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - 8 - s->leftover_offset); + s->leftover[sizeof(s->leftover) - 1] = (uint8_t)(s->bits_hashed); + s->leftover[sizeof(s->leftover) - 2] = (uint8_t)(s->bits_hashed >> 8); + s->leftover[sizeof(s->leftover) - 3] = (uint8_t)(s->bits_hashed >> 16); + s->leftover[sizeof(s->leftover) - 4] = (uint8_t)(s->bits_hashed >> 24); + s->leftover[sizeof(s->leftover) - 5] = (uint8_t)(s->bits_hashed >> 32); + s->leftover[sizeof(s->leftover) - 6] = (uint8_t)(s->bits_hashed >> 40); + s->leftover[sizeof(s->leftover) - 7] = (uint8_t)(s->bits_hashed >> 48); + s->leftover[sizeof(s->leftover) - 8] = (uint8_t)(s->bits_hashed >> 56); + + /* hash the padding and length */ + compress(s->iv, s->leftover); + + /* copy the iv out to digest */ + for (i = 0; i < TC_SHA512_STATE_BLOCKS; ++i) { + uint64_t t = *((uint64_t *) &s->iv[i]); + *digest++ = (uint8_t)(t >> 56); + *digest++ = (uint8_t)(t >> 48); + *digest++ = (uint8_t)(t >> 40); + *digest++ = (uint8_t)(t >> 32); + *digest++ = (uint8_t)(t >> 24); + *digest++ = (uint8_t)(t >> 16); + *digest++ = (uint8_t)(t >> 8); + *digest++ = (uint8_t)(t); + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +/* + * Initializing SHA-512 Hash constant words K. + * These values correspond to the first 64 bits of the fractional parts of the + * cube roots of the first 80 primes between 2 and 409. + */ +static const uint64_t k512[80] = { + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538, + 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe, + 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, + 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab, + 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, + 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, + 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, + 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, + 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, + 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c, + 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6, + 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, + 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 +}; + +static inline uint64_t ROTR(uint64_t a, uint64_t n) +{ + return (((a) >> n) | ((a) << (64 - n))); +} + +#define Sigma0(a)(ROTR((a), 28) ^ ROTR((a), 34) ^ ROTR((a), 39)) +#define Sigma1(a)(ROTR((a), 14) ^ ROTR((a), 18) ^ ROTR((a), 41)) +#define sigma0(a)(ROTR((a), 1) ^ ROTR((a), 8) ^ ((a) >> 7)) +#define sigma1(a)(ROTR((a), 19) ^ ROTR((a), 61) ^ ((a) >> 6)) + +#define Ch(a, b, c)(((a) & (b)) ^ ((~(a)) & (c))) +#define Maj(a, b, c)(((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) + +static inline uint64_t BigEndian(const uint8_t **c) +{ + uint64_t n = 0; + + n = (uint64_t)(*((*c)++)) << 56; + n |= (uint64_t)(*((*c)++)) << 48; + n |= (uint64_t)(*((*c)++)) << 40; + n |= (uint64_t)(*((*c)++)) << 32; + n |= (uint64_t)(*((*c)++)) << 24; + n |= (uint64_t)(*((*c)++)) << 16; + n |= (uint64_t)(*((*c)++)) << 8; + n |= (uint64_t)(*((*c)++)); + return n; +} + +static void compress(uint64_t *iv, const uint8_t *data) +{ + uint64_t a, b, c, d, e, f, g, h; + uint64_t s0, s1; + uint64_t t1, t2; + uint64_t work_space[16]; + uint64_t n; + unsigned int i; + + a = iv[0]; b = iv[1]; c = iv[2]; d = iv[3]; + e = iv[4]; f = iv[5]; g = iv[6]; h = iv[7]; + + for (i = 0; i < 16; ++i) { + n = BigEndian(&data); + t1 = work_space[i] = n; + t1 += h + Sigma1(e) + Ch(e, f, g) + k512[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + for ( ; i < 80; ++i) { + s0 = work_space[(i+1)&0x0f]; + s0 = sigma0(s0); + s1 = work_space[(i+14)&0x0f]; + s1 = sigma1(s1); + + t1 = work_space[i&0xf] += s0 + s1 + work_space[(i+9)&0xf]; + t1 += h + Sigma1(e) + Ch(e, f, g) + k512[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + iv[0] += a; iv[1] += b; iv[2] += c; iv[3] += d; + iv[4] += e; iv[5] += f; iv[6] += g; iv[7] += h; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/.gitignore b/bootloader/mcuboot/ext/tinycrypt/.gitignore new file mode 100644 index 0000000..c960ccc --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/.gitignore @@ -0,0 +1,5 @@ +*.o +*~ +*.d +*.exe +*.a diff --git a/bootloader/mcuboot/ext/tinycrypt/AUTHORS b/bootloader/mcuboot/ext/tinycrypt/AUTHORS new file mode 100644 index 0000000..0a8e9f8 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/AUTHORS @@ -0,0 +1,15 @@ +Architect: +Rafael Misoczki + +Open Source Maintainer: +Constanza Heath +Rafael Misoczki + +Contributors: +Constanza Heath +Rafael Misoczki +Flavio Santes +Jarkko Sakkinen +Chris Morrison +Marti Bolivar +Colin Ian King diff --git a/bootloader/mcuboot/ext/tinycrypt/LICENSE b/bootloader/mcuboot/ext/tinycrypt/LICENSE new file mode 100644 index 0000000..2e1db51 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/LICENSE @@ -0,0 +1,61 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. 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. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +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 OWNER 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. + +================================================================================ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +https://github.com/kmackay/micro-ecc + +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/bootloader/mcuboot/ext/tinycrypt/Makefile b/bootloader/mcuboot/ext/tinycrypt/Makefile new file mode 100644 index 0000000..3c0e42b --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/Makefile @@ -0,0 +1,21 @@ +################################################################################ +# +# Copyright (C) 2017 by Intel Corporation, All Rights Reserved. +# +# Global Makefile. +# See lib/Makefile and tests/Makefile for further configuration. +# +################################################################################ +include config.mk + +all: + $(MAKE) -C lib +ifeq ($(ENABLE_TESTS),true) + $(MAKE) -C tests +endif + +clean: + $(MAKE) -C lib clean + $(MAKE) -C tests clean + $(RM) *~ + diff --git a/bootloader/mcuboot/ext/tinycrypt/README b/bootloader/mcuboot/ext/tinycrypt/README new file mode 100644 index 0000000..fb52c19 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/README @@ -0,0 +1,71 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. 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. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +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 OWNER 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. + +================================================================================ + +Overview: + +The TinyCrypt Library provides an implementation for constrained devices of a +minimal set of standard cryptography primitives. + +Please, ***SEE THE DOCUMENTATION*** folder for more information on the supported +cryptographic primitives and the limitations of TinyCrypt library. For usage, +security and technicalities, please see the corresponding header file of each +cryptographic primitive. + +================================================================================ + +Organization: + +/lib: C source code of the cryptographic primitives. +/lib/include/tinycrypt: C header files of the cryptographic primitives. +/tests: Test vectors of the cryptographic primitives. +/doc: Documentation of TinyCrypt. + +================================================================================ + +Building: + +1) In Makefile.conf set: + - CFLAGS for compiler flags. + - CC for compiler. + - ENABLE_TESTS for enabling (true) or disabling (false) tests compilation. +2) In lib/Makefile select the primitives required by your project. +3) In tests/Makefile select the corresponding tests of the selected primitives. +4) make +5) run tests in tests/ + +================================================================================ + diff --git a/bootloader/mcuboot/ext/tinycrypt/VERSION b/bootloader/mcuboot/ext/tinycrypt/VERSION new file mode 100644 index 0000000..a45be46 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/VERSION @@ -0,0 +1 @@ +0.2.8 diff --git a/bootloader/mcuboot/ext/tinycrypt/config.mk b/bootloader/mcuboot/ext/tinycrypt/config.mk new file mode 100644 index 0000000..a5b9fb7 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/config.mk @@ -0,0 +1,35 @@ +################################################################################ +# +# Copyright (C) 2017 by Intel Corporation, All Rights Reserved. +# +# Global configuration Makefile. Included everywhere. +# +################################################################################ + +# EDIT HERE: +CC:=gcc +CFLAGS:=-Os -std=c99 -Wall -Wextra -D_ISOC99_SOURCE -MMD -I../lib/include/ -I../lib/source/ -I../tests/include/ +vpath %.c ../lib/source/ +ENABLE_TESTS=true + +# override MinGW built-in recipe +%.o: %.c + $(COMPILE.c) $(OUTPUT_OPTION) $< + +ifeq ($(OS),Windows_NT) +DOTEXE:=.exe +endif + +# DO NOT EDIT AFTER THIS POINT: +ifeq ($(ENABLE_TESTS), true) +CFLAGS += -DENABLE_TESTS +else +CFLAGS += -DDISABLE_TESTS +endif + +export CC +export CFLAGS +export VPATH +export ENABLE_TESTS + +################################################################################ diff --git a/bootloader/mcuboot/ext/tinycrypt/documentation/tinycrypt.rst b/bootloader/mcuboot/ext/tinycrypt/documentation/tinycrypt.rst new file mode 100644 index 0000000..356c099 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/documentation/tinycrypt.rst @@ -0,0 +1,352 @@ + +TinyCrypt Cryptographic Library +############################### +Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + +Overview +******** +The TinyCrypt Library provides an implementation for targeting constrained devices +with a minimal set of standard cryptography primitives, as listed below. To better +serve applications targeting constrained devices, TinyCrypt implementations differ +from the standard specifications (see the Important Remarks section for some +important differences). Certain cryptographic primitives depend on other +primitives, as mentioned in the list below. + +Aside from the Important Remarks section below, valuable information on the usage, +security and technicalities of each cryptographic primitive are found in the +corresponding header file. + +* SHA-256: + + * Type of primitive: Hash function. + * Standard Specification: NIST FIPS PUB 180-4. + * Requires: -- + +* HMAC-SHA256: + + * Type of primitive: Message authentication code. + * Standard Specification: RFC 2104. + * Requires: SHA-256 + +* HMAC-PRNG: + + * Type of primitive: Pseudo-random number generator (256-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: SHA-256 and HMAC-SHA256. + +* AES-128: + + * Type of primitive: Block cipher. + * Standard Specification: NIST FIPS PUB 197. + * Requires: -- + +* AES-CBC mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CTR mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CMAC mode: + + * Type of primitive: Message authentication code. + * Standard Specification: NIST SP 800-38B. + * Requires: AES-128. + +* AES-CCM mode: + + * Type of primitive: Authenticated encryption. + * Standard Specification: NIST SP 800-38C. + * Requires: AES-128. + +* CTR-PRNG: + + * Type of primitive: Pseudo-random number generator (128-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: AES-128. + +* ECC-DH: + + * Type of primitive: Key exchange based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +* ECC-DSA: + + * Type of primitive: Digital signature based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +Design Goals +************ + +* Minimize the code size of each cryptographic primitive. This means minimize + the size of a platform-independent implementation, as presented in TinyCrypt. + Note that various applications may require further features, optimizations with + respect to other metrics and countermeasures for particular threats. These + peculiarities would increase the code size and thus are not considered here. + +* Minimize the dependencies among the cryptographic primitives. This means + that it is unnecessary to build and allocate object code for more primitives + than the ones strictly required by the intended application. In other words, + one can select and compile only the primitives required by the application. + + +Important Remarks +***************** + +The cryptographic implementations in TinyCrypt library have some limitations. +Some of these limitations are inherent to the cryptographic primitives +themselves, while others are specific to TinyCrypt. These limitations were accepted +in order to meet its design goals (in special, minimal code size) and to better +serve applications targeting constrained devices in general. Some of these +limitations are discussed in-depth below. + +General Remarks +*************** + +* TinyCrypt does **not** intend to be fully side-channel resistant. Due to the + variety of side-channel attacks, many of them only relevant to certain + platforms. In this sense, instead of penalizing all library users with + side-channel countermeasures such as increasing the overall code size, + TinyCrypt only implements certain generic timing-attack countermeasures. + +Specific Remarks +**************** + +* SHA-256: + + * The number of bits_hashed in the state is not checked for overflow. Note + however that this will only be a problem if you intend to hash more than + 2^64 bits, which is an extremely large window. + +* HMAC: + + * The HMAC verification process is assumed to be performed by the application. + This compares the computed tag with some given tag. + Note that conventional memory-comparison methods (such as memcmp function) + might be vulnerable to timing attacks; thus be sure to use a constant-time + memory comparison function (such as compare_constant_time + function provided in lib/utils.c). + + * The tc_hmac_final function, responsible for computing the message tag, + cleans the state context before exiting. Thus, applications do not need to + clean the TCHmacState_t ctx after calling tc_hmac_final. This should not + be changed in future versions of the library as there are applications + currently relying on this good-practice/feature of TinyCrypt. + +* HMAC-PRNG: + + * Before using HMAC-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + + * NIST SP 800-90A requires three items as seed material in the initialization + step: entropy seed, personalization and a nonce (which is not implemented). + TinyCrypt requires the personalization byte array and automatically creates + the entropy seed using a mandatory call to the re-seed function. + +* AES-128: + + * The current implementation does not support other key-lengths (such as 256 + bits). Note that if you need AES-256, it doesn't sound as though your + application is running in a constrained environment. AES-256 requires keys + twice the size as for AES-128, and the key schedule is 40% larger. + +* CTR mode: + + * The AES-CTR mode limits the size of a data message they encrypt to 2^32 + blocks. If you need to encrypt larger data sets, your application would + need to replace the key after 2^32 block encryptions. + +* CTR-PRNG: + + * Before using CTR-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + +* CBC mode: + + * TinyCrypt CBC decryption assumes that the iv and the ciphertext are + contiguous (as produced by TinyCrypt CBC encryption). This allows for a + very efficient decryption algorithm that would not otherwise be possible. + +* CMAC mode: + + * AES128-CMAC mode of operation offers 64 bits of security against collision + attacks. Note however that an external attacker cannot generate the tags + him/herself without knowing the MAC key. In this sense, to attack the + collision property of AES128-CMAC, an external attacker would need the + cooperation of the legal user to produce an exponentially high number of + tags (e.g. 2^64) to finally be able to look for collisions and benefit + from them. As an extra precaution, the current implementation allows to at + most 2^48 calls to tc_cmac_update function before re-calling tc_cmac_setup + (allowing a new key to be set), as suggested in Appendix B of SP 800-38B. + +* CCM mode: + + * There are a few tradeoffs for the selection of the parameters of CCM mode. + In special, there is a tradeoff between the maximum number of invocations + of CCM under a given key and the maximum payload length for those + invocations. Both things are related to the parameter 'q' of CCM mode. The + maximum number of invocations of CCM under a given key is determined by + the nonce size, which is: 15-q bytes. The maximum payload length for those + invocations is defined as 2^(8q) bytes. + + To achieve minimal code size, TinyCrypt CCM implementation fixes q = 2, + which is a quite reasonable choice for constrained applications. The + implications of this choice are: + + The nonce size is: 13 bytes. + + The maximum payload length is: 2^16 bytes = 65 KB. + + The mac size parameter is an important parameter to estimate the security + against collision attacks (that aim at finding different messages that + produce the same authentication tag). TinyCrypt CCM implementation + accepts any even integer between 4 and 16, as suggested in SP 800-38C. + + * TinyCrypt CCM implementation accepts associated data of any length between + 0 and (2^16 - 2^8) = 65280 bytes. + + * TinyCrypt CCM implementation accepts: + + * Both non-empty payload and associated data (it encrypts and + authenticates the payload and only authenticates the associated data); + + * Non-empty payload and empty associated data (it encrypts and + authenticates the payload); + + * Non-empty associated data and empty payload (it degenerates to an + authentication-only mode on the associated data). + + * RFC-3610, which also specifies CCM, presents a few relevant security + suggestions, such as: it is recommended for most applications to use a + mac size greater than 8. Besides, it is emphasized that the usage of the + same nonce for two different messages which are encrypted with the same + key obviously destroys the security properties of CCM mode. + +* ECC-DH and ECC-DSA: + + * TinyCrypt ECC implementation is based on micro-ecc (see + https://github.com/kmackay/micro-ecc). In the original micro-ecc + documentation, there is an important remark about the way integers are + represented: + + "Integer representation: To reduce code size, all large integers are + represented using little-endian words - so the least significant word is + first. You can use the 'ecc_bytes2native()' and 'ecc_native2bytes()' + functions to convert between the native integer representation and the + standardized octet representation." + + Note that the assumed bit layout is: {31, 30, ..., 0}, {63, 62, ..., 32}, + {95, 94, ..., 64}, {127, 126, ..., 96} for a very-long-integer (vli) + consisting of 4 unsigned integers (as an example). + + * A cryptographically-secure PRNG function must be set (using uECC_set_rng()) + before calling uECC_make_key() or uECC_sign(). + +Examples of Applications +************************ +It is possible to do useful cryptography with only the given small set of +primitives. With this list of primitives it becomes feasible to support a range +of cryptography usages: + + * Measurement of code, data structures, and other digital artifacts (SHA256); + + * Generate commitments (SHA256); + + * Construct keys (HMAC-SHA256); + + * Extract entropy from strings containing some randomness (HMAC-SHA256); + + * Construct random mappings (HMAC-SHA256); + + * Construct nonces and challenges (HMAC-PRNG, CTR-PRNG); + + * Authenticate using a shared secret (HMAC-SHA256); + + * Create an authenticated, replay-protected session (HMAC-SHA256 + HMAC-PRNG); + + * Authenticated encryption (AES-128 + AES-CCM); + + * Key-exchange (EC-DH); + + * Digital signature (EC-DSA); + +Test Vectors +************ + +The library provides a test program for each cryptographic primitive (see 'test' +folder). Besides illustrating how to use the primitives, these tests evaluate +the correctness of the implementations by checking the results against +well-known publicly validated test vectors. + +For the case of the HMAC-PRNG, due to the necessity of performing an extensive +battery test to produce meaningful conclusions, we suggest the user to evaluate +the unpredictability of the implementation by using the NIST Statistical Test +Suite (see References). + +For the case of the EC-DH and EC-DSA implementations, most of the test vectors +were obtained from the site of the NIST Cryptographic Algorithm Validation +Program (CAVP), see References. + +References +********** + +* `NIST FIPS PUB 180-4 (SHA-256)`_ + +.. _NIST FIPS PUB 180-4 (SHA-256): + http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + +* `NIST FIPS PUB 197 (AES-128)`_ + +.. _NIST FIPS PUB 197 (AES-128): + http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +* `NIST SP800-90A (HMAC-PRNG)`_ + +.. _NIST SP800-90A (HMAC-PRNG): + http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf + +* `NIST SP 800-38A (AES-CBC and AES-CTR)`_ + +.. _NIST SP 800-38A (AES-CBC and AES-CTR): + http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + +* `NIST SP 800-38B (AES-CMAC)`_ + +.. _NIST SP 800-38B (AES-CMAC): + http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf + +* `NIST SP 800-38C (AES-CCM)`_ + +.. _NIST SP 800-38C (AES-CCM): + http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + +* `NIST Statistical Test Suite (useful for testing HMAC-PRNG)`_ + +.. _NIST Statistical Test Suite (useful for testing HMAC-PRNG): + http://csrc.nist.gov/groups/ST/toolkit/rng/documentation_software.html + +* `NIST Cryptographic Algorithm Validation Program (CAVP) site`_ + +.. _NIST Cryptographic Algorithm Validation Program (CAVP) site: + http://csrc.nist.gov/groups/STM/cavp/ + +* `RFC 2104 (HMAC-SHA256)`_ + +.. _RFC 2104 (HMAC-SHA256): + https://www.ietf.org/rfc/rfc2104.txt + +* `RFC 6090 (ECC-DH and ECC-DSA)`_ + +.. _RFC 6090 (ECC-DH and ECC-DSA): + https://www.ietf.org/rfc/rfc6090.txt diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/Makefile b/bootloader/mcuboot/ext/tinycrypt/lib/Makefile new file mode 100644 index 0000000..9c4d8e2 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/Makefile @@ -0,0 +1,39 @@ +################################################################################ +# +# Copyright (C) 2017 by Intel Corporation, All Rights Reserved. +# +# Cryptographic Primitives Makefile. +# +################################################################################ + +include ../config.mk + +# Edit the OBJS content to add/remove primitives needed from TinyCrypt library: +OBJS:=aes_decrypt.o \ + aes_encrypt.o \ + cbc_mode.o \ + ctr_mode.o \ + ctr_prng.o \ + hmac.o \ + hmac_prng.o \ + sha256.o \ + ecc.o \ + ecc_dh.o \ + ecc_dsa.o \ + ccm_mode.o \ + cmac_mode.o \ + utils.o + +DEPS:=$(OBJS:.o=.d) + +all: libtinycrypt.a + +libtinycrypt.a: $(OBJS) + $(AR) $(ARFLAGS) $@ $^ + +.PHONY: clean + +clean: + -$(RM) *.exe $(OBJS) $(DEPS) *~ libtinycrypt.a + +-include $(DEPS) diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/aes.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/aes.h new file mode 100644 index 0000000..b612213 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/aes.h @@ -0,0 +1,130 @@ +/* aes.h - TinyCrypt interface to an AES-128 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief -- Interface to an AES-128 implementation. + * + * Overview: AES-128 is a NIST approved block cipher specified in + * FIPS 197. Block ciphers are deterministic algorithms that + * perform a transformation specified by a symmetric key in fixed- + * length data sets, also called blocks. + * + * Security: AES-128 provides approximately 128 bits of security. + * + * Usage: 1) call tc_aes128_set_encrypt/decrypt_key to set the key. + * + * 2) call tc_aes_encrypt/decrypt to process the data. + */ + +#ifndef __TC_AES_H__ +#define __TC_AES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define Nb (4) /* number of columns (32-bit words) comprising the state */ +#define Nk (4) /* number of 32-bit words comprising the key */ +#define Nr (10) /* number of rounds */ +#define TC_AES_BLOCK_SIZE (Nb*Nk) +#define TC_AES_KEY_SIZE (Nb*Nk) + +typedef struct tc_aes_key_sched_struct { + unsigned int words[Nb*(Nr+1)]; +} *TCAesKeySched_t; + +/** + * @brief Set AES-128 encryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This implementation skips the additional steps required for keys + * larger than 128 bits, and must not be used for AES-192 or + * AES-256 key schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Encrypts contents of in buffer into out buffer under key; + * schedule s + * @note Assumes s was initialized by aes_set_encrypt_key; + * out and in point to 16 byte buffers + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out == NULL or in == NULL or s == NULL + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +/** + * @brief Set the AES-128 decryption key + * Uses key k to initialize s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: s == NULL or k == NULL + * @note This is the implementation of the straightforward inverse cipher + * using the cipher documented in FIPS-197 figure 12, not the + * equivalent inverse cipher presented in Figure 15 + * @warning This routine skips the additional steps required for keys larger + * than 128, and must not be used for AES-192 or AES-256 key + * schedule -- see FIPS 197 for details + * @param s IN/OUT -- initialized struct tc_aes_key_sched_struct + * @param k IN -- points to the AES key + */ +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k); + +/** + * @brief AES-128 Encryption procedure + * Decrypts in buffer into out buffer under key schedule s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: out is NULL or in is NULL or s is NULL + * @note Assumes s was initialized by aes_set_encrypt_key + * out and in point to 16 byte buffers + * @param out IN/OUT -- buffer to receive ciphertext block + * @param in IN -- a plaintext block to encrypt + * @param s IN -- initialized AES key schedule + */ +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, + const TCAesKeySched_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_AES_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cbc_mode.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cbc_mode.h new file mode 100644 index 0000000..4a837fd --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cbc_mode.h @@ -0,0 +1,151 @@ +/* cbc_mode.h - TinyCrypt interface to a CBC mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to a CBC mode implementation. + * + * Overview: CBC (for "cipher block chaining") mode is a NIST approved mode of + * operation defined in SP 800-38a. It can be used with any block + * cipher to provide confidentiality of strings whose lengths are + * multiples of the block_size of the underlying block cipher. + * TinyCrypt hard codes AES as the block cipher. + * + * Security: CBC mode provides data confidentiality given that the maximum + * number q of blocks encrypted under a single key satisfies + * q < 2^63, which is not a practical constraint (it is considered a + * good practice to replace the encryption when q == 2^56). CBC mode + * provides NO data integrity. + * + * CBC mode assumes that the IV value input into the + * tc_cbc_mode_encrypt is randomly generated. The TinyCrypt library + * provides HMAC-PRNG module, which generates suitable IVs. Other + * methods for generating IVs are acceptable, provided that the + * values of the IVs generated appear random to any adversary, + * including someone with complete knowledge of the system design. + * + * The randomness property on which CBC mode's security depends is + * the unpredictability of the IV. Since it is unpredictable, this + * means in practice that CBC mode requires that the IV is stored + * somehow with the ciphertext in order to recover the plaintext. + * + * TinyCrypt CBC encryption prepends the IV to the ciphertext, + * because this affords a more efficient (few buffers) decryption. + * Hence tc_cbc_mode_encrypt assumes the ciphertext buffer is always + * 16 bytes larger than the plaintext buffer. + * + * Requires: AES-128 + * + * Usage: 1) call tc_cbc_mode_encrypt to encrypt data. + * + * 2) call tc_cbc_mode_decrypt to decrypt data. + * + */ + +#ifndef __TC_CBC_MODE_H__ +#define __TC_CBC_MODE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CBC encryption procedure + * CBC encrypts inlen bytes of the in buffer into the out buffer + * using the encryption key schedule provided, prepends iv to out + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * ctr == NULL or + * sched == NULL or + * inlen == 0 or + * (inlen % TC_AES_BLOCK_SIZE) != 0 or + * (outlen % TC_AES_BLOCK_SIZE) != 0 or + * outlen != inlen + TC_AES_BLOCK_SIZE + * @note Assumes: - sched has been configured by aes_set_encrypt_key + * - iv contains a 16 byte random string + * - out buffer is large enough to hold the ciphertext + iv + * - out buffer is a contiguous buffer + * - in holds the plaintext and is a contiguous buffer + * - inlen gives the number of bytes in the in buffer + * @param out IN/OUT -- buffer to receive the ciphertext + * @param outlen IN -- length of ciphertext buffer in bytes + * @param in IN -- plaintext to encrypt + * @param inlen IN -- length of plaintext buffer in bytes + * @param iv IN -- the IV for the this encrypt/decrypt + * @param sched IN -- AES key schedule for this encrypt + */ +int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched); + +/** + * @brief CBC decryption procedure + * CBC decrypts inlen bytes of the in buffer into the out buffer + * using the provided encryption key schedule + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * sched == NULL or + * inlen == 0 or + * outlen == 0 or + * (inlen % TC_AES_BLOCK_SIZE) != 0 or + * (outlen % TC_AES_BLOCK_SIZE) != 0 or + * outlen != inlen + TC_AES_BLOCK_SIZE + * @note Assumes:- in == iv + ciphertext, i.e. the iv and the ciphertext are + * contiguous. This allows for a very efficient decryption + * algorithm that would not otherwise be possible + * - sched was configured by aes_set_decrypt_key + * - out buffer is large enough to hold the decrypted plaintext + * and is a contiguous buffer + * - inlen gives the number of bytes in the in buffer + * @param out IN/OUT -- buffer to receive decrypted data + * @param outlen IN -- length of plaintext buffer in bytes + * @param in IN -- ciphertext to decrypt, including IV + * @param inlen IN -- length of ciphertext buffer in bytes + * @param iv IN -- the IV for the this encrypt/decrypt + * @param sched IN -- AES key schedule for this decrypt + * + */ +int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CBC_MODE_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ccm_mode.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ccm_mode.h new file mode 100644 index 0000000..69c798e --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ccm_mode.h @@ -0,0 +1,211 @@ +/* ccm_mode.h - TinyCrypt interface to a CCM mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to a CCM mode implementation. + * + * Overview: CCM (for "Counter with CBC-MAC") mode is a NIST approved mode of + * operation defined in SP 800-38C. + * + * TinyCrypt CCM implementation accepts: + * + * 1) Both non-empty payload and associated data (it encrypts and + * authenticates the payload and also authenticates the associated + * data); + * 2) Non-empty payload and empty associated data (it encrypts and + * authenticates the payload); + * 3) Non-empty associated data and empty payload (it degenerates to + * an authentication mode on the associated data). + * + * TinyCrypt CCM implementation accepts associated data of any length + * between 0 and (2^16 - 2^8) bytes. + * + * Security: The mac length parameter is an important parameter to estimate the + * security against collision attacks (that aim at finding different + * messages that produce the same authentication tag). TinyCrypt CCM + * implementation accepts any even integer between 4 and 16, as + * suggested in SP 800-38C. + * + * RFC-3610, which also specifies CCM, presents a few relevant + * security suggestions, such as: it is recommended for most + * applications to use a mac length greater than 8. Besides, the + * usage of the same nonce for two different messages which are + * encrypted with the same key destroys the security of CCM mode. + * + * Requires: AES-128 + * + * Usage: 1) call tc_ccm_config to configure. + * + * 2) call tc_ccm_mode_encrypt to encrypt data and generate tag. + * + * 3) call tc_ccm_mode_decrypt to decrypt data and verify tag. + */ + +#ifndef __TC_CCM_MODE_H__ +#define __TC_CCM_MODE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* max additional authenticated size in bytes: 2^16 - 2^8 = 65280 */ +#define TC_CCM_AAD_MAX_BYTES 0xff00 + +/* max message size in bytes: 2^(8L) = 2^16 = 65536 */ +#define TC_CCM_PAYLOAD_MAX_BYTES 0x10000 + +/* struct tc_ccm_mode_struct represents the state of a CCM computation */ +typedef struct tc_ccm_mode_struct { + TCAesKeySched_t sched; /* AES key schedule */ + uint8_t *nonce; /* nonce required by CCM */ + unsigned int mlen; /* mac length in bytes (parameter t in SP-800 38C) */ +} *TCCcmMode_t; + +/** + * @brief CCM configuration procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * c == NULL or + * sched == NULL or + * nonce == NULL or + * mlen != {4, 6, 8, 10, 12, 16} + * @param c -- CCM state + * @param sched IN -- AES key schedule + * @param nonce IN - nonce + * @param nlen -- nonce length in bytes + * @param mlen -- mac length in bytes (parameter t in SP-800 38C) + */ +int tc_ccm_config(TCCcmMode_t c, TCAesKeySched_t sched, uint8_t *nonce, + unsigned int nlen, unsigned int mlen); + +/** + * @brief CCM tag generation and encryption procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * c == NULL or + * ((plen > 0) and (payload == NULL)) or + * ((alen > 0) and (associated_data == NULL)) or + * (alen >= TC_CCM_AAD_MAX_BYTES) or + * (plen >= TC_CCM_PAYLOAD_MAX_BYTES) or + * (olen < plen + maclength) + * + * @param out OUT -- encrypted data + * @param olen IN -- output length in bytes + * @param associated_data IN -- associated data + * @param alen IN -- associated data length in bytes + * @param payload IN -- payload + * @param plen IN -- payload length in bytes + * @param c IN -- CCM state + * + * @note: out buffer should be at least (plen + c->mlen) bytes long. + * + * @note: The sequence b for encryption is formatted as follows: + * b = [FLAGS | nonce | counter ], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * counter is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-7 btis: always 0's + * + * @note: The sequence b for authentication is formatted as follows: + * b = [FLAGS | nonce | length(mac length)], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * length(mac length) is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-5 bits: mac length (encoded as: (mlen-2)/2) + * 6: Adata (0 if alen == 0, and 1 otherwise) + * 7: always 0 + */ +int tc_ccm_generation_encryption(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c); + +/** + * @brief CCM decryption and tag verification procedure + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * c == NULL or + * ((plen > 0) and (payload == NULL)) or + * ((alen > 0) and (associated_data == NULL)) or + * (alen >= TC_CCM_AAD_MAX_BYTES) or + * (plen >= TC_CCM_PAYLOAD_MAX_BYTES) or + * (olen < plen - c->mlen) + * + * @param out OUT -- decrypted data + * @param associated_data IN -- associated data + * @param alen IN -- associated data length in bytes + * @param payload IN -- payload + * @param plen IN -- payload length in bytes + * @param c IN -- CCM state + * + * @note: out buffer should be at least (plen - c->mlen) bytes long. + * + * @note: The sequence b for encryption is formatted as follows: + * b = [FLAGS | nonce | counter ], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * counter is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-7 btis: always 0's + * + * @note: The sequence b for authentication is formatted as follows: + * b = [FLAGS | nonce | length(mac length)], where: + * FLAGS is 1 byte long + * nonce is 13 bytes long + * length(mac length) is 2 bytes long + * The byte FLAGS is composed by the following 8 bits: + * 0-2 bits: used to represent the value of q-1 + * 3-5 bits: mac length (encoded as: (mlen-2)/2) + * 6: Adata (0 if alen == 0, and 1 otherwise) + * 7: always 0 + */ +int tc_ccm_decryption_verification(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, unsigned int plen, + TCCcmMode_t c); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CCM_MODE_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cmac_mode.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cmac_mode.h new file mode 100644 index 0000000..f44b0a5 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/cmac_mode.h @@ -0,0 +1,194 @@ +/* cmac_mode.h -- interface to a CMAC implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to a CMAC implementation. + * + * Overview: CMAC is defined NIST in SP 800-38B, and is the standard algorithm + * for computing a MAC using a block cipher. It can compute the MAC + * for a byte string of any length. It is distinguished from CBC-MAC + * in the processing of the final message block; CMAC uses a + * different technique to compute the final message block is full + * size or only partial, while CBC-MAC uses the same technique for + * both. This difference permits CMAC to be applied to variable + * length messages, while all messages authenticated by CBC-MAC must + * be the same length. + * + * Security: AES128-CMAC mode of operation offers 64 bits of security against + * collision attacks. Note however that an external attacker cannot + * generate the tags him/herself without knowing the MAC key. In this + * sense, to attack the collision property of AES128-CMAC, an + * external attacker would need the cooperation of the legal user to + * produce an exponentially high number of tags (e.g. 2^64) to + * finally be able to look for collisions and benefit from them. As + * an extra precaution, the current implementation allows to at most + * 2^48 calls to the tc_cmac_update function before re-calling + * tc_cmac_setup (allowing a new key to be set), as suggested in + * Appendix B of SP 800-38B. + * + * Requires: AES-128 + * + * Usage: This implementation provides a "scatter-gather" interface, so that + * the CMAC value can be computed incrementally over a message + * scattered in different segments throughout memory. Experience shows + * this style of interface tends to minimize the burden of programming + * correctly. Like all symmetric key operations, it is session + * oriented. + * + * To begin a CMAC session, use tc_cmac_setup to initialize a struct + * tc_cmac_struct with encryption key and buffer. Our implementation + * always assume that the AES key to be the same size as the block + * cipher block size. Once setup, this data structure can be used for + * many CMAC computations. + * + * Once the state has been setup with a key, computing the CMAC of + * some data requires three steps: + * + * (1) first use tc_cmac_init to initialize a new CMAC computation. + * (2) next mix all of the data into the CMAC computation state using + * tc_cmac_update. If all of the data resides in a single data + * segment then only one tc_cmac_update call is needed; if data + * is scattered throughout memory in n data segments, then n calls + * will be needed. CMAC IS ORDER SENSITIVE, to be able to detect + * attacks that swap bytes, so the order in which data is mixed + * into the state is critical! + * (3) Once all of the data for a message has been mixed, use + * tc_cmac_final to compute the CMAC tag value. + * + * Steps (1)-(3) can be repeated as many times as you want to CMAC + * multiple messages. A practical limit is 2^48 1K messages before you + * have to change the key. + * + * Once you are done computing CMAC with a key, it is a good idea to + * destroy the state so an attacker cannot recover the key; use + * tc_cmac_erase to accomplish this. + */ + +#ifndef __TC_CMAC_MODE_H__ +#define __TC_CMAC_MODE_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* padding for last message block */ +#define TC_CMAC_PADDING 0x80 + +/* struct tc_cmac_struct represents the state of a CMAC computation */ +typedef struct tc_cmac_struct { +/* initialization vector */ + uint8_t iv[TC_AES_BLOCK_SIZE]; +/* used if message length is a multiple of block_size bytes */ + uint8_t K1[TC_AES_BLOCK_SIZE]; +/* used if message length isn't a multiple block_size bytes */ + uint8_t K2[TC_AES_BLOCK_SIZE]; +/* where to put bytes that didn't fill a block */ + uint8_t leftover[TC_AES_BLOCK_SIZE]; +/* identifies the encryption key */ + unsigned int keyid; +/* next available leftover location */ + unsigned int leftover_offset; +/* AES key schedule */ + TCAesKeySched_t sched; +/* calls to tc_cmac_update left before re-key */ + uint64_t countdown; +} *TCCmacState_t; + +/** + * @brief Configures the CMAC state to use the given AES key + * @return returns TC_CRYPTO_SUCCESS (1) after having configured the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL or + * key == NULL + * + * @param s IN/OUT -- the state to set up + * @param key IN -- the key to use + * @param sched IN -- AES key schedule + */ +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, + TCAesKeySched_t sched); + +/** + * @brief Erases the CMAC state + * @return returns TC_CRYPTO_SUCCESS (1) after having configured the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL + * + * @param s IN/OUT -- the state to erase + */ +int tc_cmac_erase(TCCmacState_t s); + +/** + * @brief Initializes a new CMAC computation + * @return returns TC_CRYPTO_SUCCESS (1) after having initialized the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL + * + * @param s IN/OUT -- the state to initialize + */ +int tc_cmac_init(TCCmacState_t s); + +/** + * @brief Incrementally computes CMAC over the next data segment + * @return returns TC_CRYPTO_SUCCESS (1) after successfully updating the CMAC state + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL or + * if data == NULL when dlen > 0 + * + * @param s IN/OUT -- the CMAC state + * @param data IN -- the next data segment to MAC + * @param dlen IN -- the length of data in bytes + */ +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t dlen); + +/** + * @brief Generates the tag from the CMAC state + * @return returns TC_CRYPTO_SUCCESS (1) after successfully generating the tag + * returns TC_CRYPTO_FAIL (0) if: + * tag == NULL or + * s == NULL + * + * @param tag OUT -- the CMAC tag + * @param s IN -- CMAC state + */ +int tc_cmac_final(uint8_t *tag, TCCmacState_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CMAC_MODE_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/constants.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/constants.h new file mode 100644 index 0000000..965490e --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/constants.h @@ -0,0 +1,61 @@ +/* constants.h - TinyCrypt interface to constants */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief -- Interface to constants. + * + */ + +#ifndef __TC_CONSTANTS_H__ +#define __TC_CONSTANTS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define TC_CRYPTO_SUCCESS 1 +#define TC_CRYPTO_FAIL 0 + +#define TC_ZERO_BYTE 0x00 + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CONSTANTS_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_mode.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_mode.h new file mode 100644 index 0000000..9936c92 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_mode.h @@ -0,0 +1,110 @@ +/* ctr_mode.h - TinyCrypt interface to CTR mode */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to CTR mode. + * + * Overview: CTR (pronounced "counter") mode is a NIST approved mode of + * operation defined in SP 800-38a. It can be used with any + * block cipher to provide confidentiality of strings of any + * length. TinyCrypt hard codes AES128 as the block cipher. + * + * Security: CTR mode achieves confidentiality only if the counter value is + * never reused with a same encryption key. If the counter is + * repeated, than an adversary might be able to defeat the scheme. + * + * A usual method to ensure different counter values refers to + * initialize the counter in a given value (0, for example) and + * increases it every time a new block is enciphered. This naturally + * leaves to a limitation on the number q of blocks that can be + * enciphered using a same key: q < 2^(counter size). + * + * TinyCrypt uses a counter of 32 bits. This means that after 2^32 + * block encryptions, the counter will be reused (thus losing CBC + * security). 2^32 block encryptions should be enough for most of + * applications targeting constrained devices. Applications intended + * to encrypt a larger number of blocks must replace the key after + * 2^32 block encryptions. + * + * CTR mode provides NO data integrity. + * + * Requires: AES-128 + * + * Usage: 1) call tc_ctr_mode to process the data to encrypt/decrypt. + * + */ + +#ifndef __TC_CTR_MODE_H__ +#define __TC_CTR_MODE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CTR mode encryption/decryption procedure. + * CTR mode encrypts (or decrypts) inlen bytes from in buffer into out buffer + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL or + * in == NULL or + * ctr == NULL or + * sched == NULL or + * inlen == 0 or + * outlen == 0 or + * inlen != outlen + * @note Assumes:- The current value in ctr has NOT been used with sched + * - out points to inlen bytes + * - in points to inlen bytes + * - ctr is an integer counter in littleEndian format + * - sched was initialized by aes_set_encrypt_key + * @param out OUT -- produced ciphertext (plaintext) + * @param outlen IN -- length of ciphertext buffer in bytes + * @param in IN -- data to encrypt (or decrypt) + * @param inlen IN -- length of input data in bytes + * @param ctr IN/OUT -- the current counter value + * @param blk_off IN/OUT -- the offset in the block + * @param sched IN -- an initialized AES key schedule + */ +int tc_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, uint32_t *blk_off, + const TCAesKeySched_t sched); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CTR_MODE_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_prng.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_prng.h new file mode 100644 index 0000000..9be06db --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ctr_prng.h @@ -0,0 +1,166 @@ +/* ctr_prng.h - TinyCrypt interface to a CTR-PRNG implementation */ + +/* + * Copyright (c) 2016, Chris Morrison + * 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. + */ + +/** + * @file + * @brief Interface to a CTR-PRNG implementation. + * + * Overview: A pseudo-random number generator (PRNG) generates a sequence + * of numbers that have a distribution close to the one expected + * for a sequence of truly random numbers. The NIST Special + * Publication 800-90A specifies several mechanisms to generate + * sequences of pseudo random numbers, including the CTR-PRNG one + * which is based on AES. TinyCrypt implements CTR-PRNG with + * AES-128. + * + * Security: A cryptographically secure PRNG depends on the existence of an + * entropy source to provide a truly random seed as well as the + * security of the primitives used as the building blocks (AES-128 + * in this instance). + * + * Requires: - AES-128 + * + * Usage: 1) call tc_ctr_prng_init to seed the prng context + * + * 2) call tc_ctr_prng_reseed to mix in additional entropy into + * the prng context + * + * 3) call tc_ctr_prng_generate to output the pseudo-random data + * + * 4) call tc_ctr_prng_uninstantiate to zero out the prng context + */ + +#ifndef __TC_CTR_PRNG_H__ +#define __TC_CTR_PRNG_H__ + +#include + +#define TC_CTR_PRNG_RESEED_REQ -1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /* updated each time another BLOCKLEN_BYTES bytes are produced */ + uint8_t V[TC_AES_BLOCK_SIZE]; + + /* updated whenever the PRNG is reseeded */ + struct tc_aes_key_sched_struct key; + + /* number of requests since initialization/reseeding */ + uint64_t reseedCount; +} TCCtrPrng_t; + + +/** + * @brief CTR-PRNG initialization procedure + * Initializes prng context with entropy and personalization string (if any) + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * entropy == NULL, + * entropyLen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) + * @note Only the first (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes of + * both the entropy and personalization inputs are used - + * supplying additional bytes has no effect. + * @param ctx IN/OUT -- the PRNG context to initialize + * @param entropy IN -- entropy used to seed the PRNG + * @param entropyLen IN -- entropy length in bytes + * @param personalization IN -- personalization string used to seed the PRNG + * (may be null) + * @param plen IN -- personalization length in bytes + * + */ +int tc_ctr_prng_init(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const personalization, + unsigned int pLen); + +/** + * @brief CTR-PRNG reseed procedure + * Mixes entropy and additional_input into the prng context + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * entropy == NULL, + * entropylen < (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) + * @note It is better to reseed an existing prng context rather than + * re-initialise, so that any existing entropy in the context is + * presereved. This offers some protection against undetected failures + * of the entropy source. + * @note Assumes tc_ctr_prng_init has been called for ctx + * @param ctx IN/OUT -- the PRNG state + * @param entropy IN -- entropy to mix into the prng + * @param entropylen IN -- length of entropy in bytes + * @param additional_input IN -- additional input to the prng (may be null) + * @param additionallen IN -- additional input length in bytes + */ +int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const additional_input, + unsigned int additionallen); + +/** + * @brief CTR-PRNG generate procedure + * Generates outlen pseudo-random bytes into out buffer, updates prng + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CTR_PRNG_RESEED_REQ (-1) if a reseed is needed + * returns TC_CRYPTO_FAIL (0) if: + * ctx == NULL, + * out == NULL, + * outlen >= 2^16 + * @note Assumes tc_ctr_prng_init has been called for ctx + * @param ctx IN/OUT -- the PRNG context + * @param additional_input IN -- additional input to the prng (may be null) + * @param additionallen IN -- additional input length in bytes + * @param out IN/OUT -- buffer to receive output + * @param outlen IN -- size of out buffer in bytes + */ +int tc_ctr_prng_generate(TCCtrPrng_t * const ctx, + uint8_t const * const additional_input, + unsigned int additionallen, + uint8_t * const out, + unsigned int outlen); + +/** + * @brief CTR-PRNG uninstantiate procedure + * Zeroes the internal state of the supplied prng context + * @return none + * @param ctx IN/OUT -- the PRNG context + */ +void tc_ctr_prng_uninstantiate(TCCtrPrng_t * const ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_CTR_PRNG_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc.h new file mode 100644 index 0000000..8abc949 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc.h @@ -0,0 +1,545 @@ +/* ecc.h - TinyCrypt interface to common ECC functions */ + +/* 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. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief -- Interface to common ECC functions. + * + * Overview: This software is an implementation of common functions + * necessary to elliptic curve cryptography. This implementation uses + * curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + * + */ + +#ifndef __TC_UECC_H__ +#define __TC_UECC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Word size (4 bytes considering 32-bits architectures) */ +#define uECC_WORD_SIZE 4 + +/* setting max number of calls to prng: */ +#ifndef uECC_RNG_MAX_TRIES +#define uECC_RNG_MAX_TRIES 64 +#endif + +/* defining data types to store word and bit counts: */ +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +/* defining data type for comparison result: */ +typedef int8_t cmpresult_t; +/* defining data type to store ECC coordinate/point in 32bits words: */ +typedef unsigned int uECC_word_t; +/* defining data type to store an ECC coordinate/point in 64bits words: */ +typedef uint64_t uECC_dword_t; + +/* defining masks useful for ecc computations: */ +#define HIGH_BIT_SET 0x80000000 +#define uECC_WORD_BITS 32 +#define uECC_WORD_BITS_SHIFT 5 +#define uECC_WORD_BITS_MASK 0x01F + +/* Number of words of 32 bits to represent an element of the the curve p-256: */ +#define NUM_ECC_WORDS 8 +/* Number of bytes to represent an element of the the curve p-256: */ +#define NUM_ECC_BYTES (uECC_WORD_SIZE*NUM_ECC_WORDS) + +/* structure that represents an elliptic curve (e.g. p256):*/ +struct uECC_Curve_t; +typedef const struct uECC_Curve_t * uECC_Curve; +struct uECC_Curve_t { + wordcount_t num_words; + wordcount_t num_bytes; + bitcount_t num_n_bits; + uECC_word_t p[NUM_ECC_WORDS]; + uECC_word_t n[NUM_ECC_WORDS]; + uECC_word_t G[NUM_ECC_WORDS * 2]; + uECC_word_t b[NUM_ECC_WORDS]; + void (*double_jacobian)(uECC_word_t * X1, uECC_word_t * Y1, uECC_word_t * Z1, + uECC_Curve curve); + void (*x_side)(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); + void (*mmod_fast)(uECC_word_t *result, uECC_word_t *product); +}; + +/* + * @brief computes doubling of point ion jacobian coordinates, in place. + * @param X1 IN/OUT -- x coordinate + * @param Y1 IN/OUT -- y coordinate + * @param Z1 IN/OUT -- z coordinate + * @param curve IN -- elliptic curve + */ +void double_jacobian_default(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * Z1, uECC_Curve curve); + +/* + * @brief Computes x^3 + ax + b. result must not overlap x. + * @param result OUT -- x^3 + ax + b + * @param x IN -- value of x + * @param curve IN -- elliptic curve + */ +void x_side_default(uECC_word_t *result, const uECC_word_t *x, + uECC_Curve curve); + +/* + * @brief Computes result = product % curve_p + * from http://www.nsa.gov/ia/_files/nist-routines.pdf + * @param result OUT -- product % curve_p + * @param product IN -- value to be reduced mod curve_p + */ +void vli_mmod_fast_secp256r1(unsigned int *result, unsigned int *product); + +/* Bytes to words ordering: */ +#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 +#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) + +/* definition of curve NIST p-256: */ +static const struct uECC_Curve_t curve_secp256r1 = { + NUM_ECC_WORDS, + NUM_ECC_BYTES, + 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, + &x_side_default, + &vli_mmod_fast_secp256r1 +}; + +uECC_Curve uECC_secp256r1(void); + +/* + * @brief Generates a random integer in the range 0 < random < top. + * Both random and top have num_words words. + * @param random OUT -- random integer in the range 0 < random < top + * @param top IN -- upper limit + * @param num_words IN -- number of words + * @return a random integer in the range 0 < random < top + */ +int uECC_generate_random_int(uECC_word_t *random, const uECC_word_t *top, + wordcount_t num_words); + + +/* 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(). + * + * A correct RNG function is set by default. 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. + */ +typedef int(*uECC_RNG_Function)(uint8_t *dest, unsigned int size); + +/* + * @brief 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. + * + * @note On platforms where there is no predefined RNG function, this must be + * called before uECC_make_key() or uECC_sign() are used. + * + * @param rng_function IN -- function that will be used to generate random bytes + */ +void uECC_set_rng(uECC_RNG_Function rng_function); + +/* + * @brief provides current uECC_RNG_Function. + * @return Returns the function that will be used to generate random bytes. + */ +uECC_RNG_Function uECC_get_rng(void); + +/* + * @brief computes the size of a private key for the curve in bytes. + * @param curve IN -- elliptic curve + * @return size of a private key for the curve in bytes. + */ +int uECC_curve_private_key_size(uECC_Curve curve); + +/* + * @brief computes the size of a public key for the curve in bytes. + * @param curve IN -- elliptic curve + * @return the size of a public key for the curve in bytes. + */ +int uECC_curve_public_key_size(uECC_Curve curve); + +/* + * @brief Compute the corresponding public key for a private key. + * @param private_key IN -- The private key to compute the public key for + * @param public_key OUT -- Will be filled in with the corresponding public key + * @param curve + * @return Returns 1 if 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); + +/* + * @brief Compute public-key. + * @return corresponding public-key. + * @param result OUT -- public-key + * @param private_key IN -- private-key + * @param curve IN -- elliptic curve + */ +uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, uECC_Curve curve); + +/* + * @brief Regularize the bitcount for the private key so that attackers cannot + * use a side channel attack to learn the number of leading zeros. + * @return Regularized k + * @param k IN -- private-key + * @param k0 IN/OUT -- regularized k + * @param k1 IN/OUT -- regularized k + * @param curve IN -- elliptic curve + */ +uECC_word_t regularize_k(const uECC_word_t * const k, uECC_word_t *k0, + uECC_word_t *k1, uECC_Curve curve); + +/* + * @brief Point multiplication algorithm using Montgomery's ladder with co-Z + * coordinates. See http://eprint.iacr.org/2011/338.pdf. + * @note Result may overlap point. + * @param result OUT -- returns scalar*point + * @param point IN -- elliptic curve point + * @param scalar IN -- scalar + * @param initial_Z IN -- initial value for z + * @param num_bits IN -- number of bits in scalar + * @param curve IN -- elliptic curve + */ +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); + +/* + * @brief Constant-time comparison to zero - secure way to compare long integers + * @param vli IN -- very long integer + * @param num_words IN -- number of words in the vli + * @return 1 if vli == 0, 0 otherwise. + */ +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); + +/* + * @brief Check if 'point' is the point at infinity + * @param point IN -- elliptic curve point + * @param curve IN -- elliptic curve + * @return if 'point' is the point at infinity, 0 otherwise. + */ +uECC_word_t EccPoint_isZero(const uECC_word_t *point, uECC_Curve curve); + +/* + * @brief computes the sign of left - right, in constant time. + * @param left IN -- left term to be compared + * @param right IN -- right term to be compared + * @param num_words IN -- number of words + * @return the sign of left - right + */ +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief computes sign of left - right, not in constant time. + * @note should not be used if inputs are part of a secret + * @param left IN -- left term to be compared + * @param right IN -- right term to be compared + * @param num_words IN -- number of words + * @return the sign of left - right + */ +cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief Computes result = (left - right) % mod. + * @note Assumes that (left < mod) and (right < mod), and that result does not + * overlap mod. + * @param result OUT -- (left - right) % mod + * @param left IN -- leftright term in modular subtraction + * @param right IN -- right term in modular subtraction + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +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); + +/* + * @brief Computes P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) or + * P => P', Q => P + Q + * @note assumes Input P = (x1, y1, Z), Q = (x2, y2, Z) + * @param X1 IN -- x coordinate of P + * @param Y1 IN -- y coordinate of P + * @param X2 IN -- x coordinate of Q + * @param Y2 IN -- y coordinate of Q + * @param curve IN -- elliptic curve + */ +void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1, uECC_word_t * X2, + uECC_word_t * Y2, uECC_Curve curve); + +/* + * @brief Computes (x1 * z^2, y1 * z^3) + * @param X1 IN -- previous x1 coordinate + * @param Y1 IN -- previous y1 coordinate + * @param Z IN -- z value + * @param curve IN -- elliptic curve + */ +void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z, + uECC_Curve curve); + +/* + * @brief Check if bit is set. + * @return Returns nonzero if bit 'bit' of vli is set. + * @warning It is assumed that the value provided in 'bit' is within the + * boundaries of the word-array 'vli'. + * @note The bit ordering layout assumed for vli is: {31, 30, ..., 0}, + * {63, 62, ..., 32}, {95, 94, ..., 64}, {127, 126,..., 96} for a vli consisting + * of 4 uECC_word_t elements. + */ +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); + +/* + * @brief Computes result = product % mod, where product is 2N words long. + * @param result OUT -- product % mod + * @param mod IN -- module + * @param num_words IN -- number of words + * @warning Currently only designed to work for 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); + +/* + * @brief Computes modular product (using curve->mmod_fast) + * @param result OUT -- (left * right) mod % curve_p + * @param left IN -- left term in product + * @param right IN -- right term in product + * @param curve IN -- elliptic curve + */ +void uECC_vli_modMult_fast(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, uECC_Curve curve); + +/* + * @brief Computes result = left - right. + * @note Can modify in place. + * @param result OUT -- left - right + * @param left IN -- left term in subtraction + * @param right IN -- right term in subtraction + * @param num_words IN -- number of words + * @return borrow + */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, const uECC_word_t *left, + const uECC_word_t *right, wordcount_t num_words); + +/* + * @brief Constant-time comparison function(secure way to compare long ints) + * @param left IN -- left term in comparison + * @param right IN -- right term in comparison + * @param num_words IN -- number of words + * @return Returns 0 if left == right, 1 otherwise. + */ +uECC_word_t uECC_vli_equal(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words); + +/* + * @brief Computes (left * right) % mod + * @param result OUT -- (left * right) % mod + * @param left IN -- left term in product + * @param right IN -- right term in product + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +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); + +/* + * @brief Computes (1 / input) % mod + * @note All VLIs are the same size. + * @note See "Euclid's GCD to Montgomery Multiplication to the Great Divide" + * @param result OUT -- (1 / input) % mod + * @param input IN -- value to be modular inverted + * @param mod IN -- mod + * @param num_words -- number of words + */ +void uECC_vli_modInv(uECC_word_t *result, const uECC_word_t *input, + const uECC_word_t *mod, wordcount_t num_words); + +/* + * @brief Sets dest = src. + * @param dest OUT -- destination buffer + * @param src IN -- origin buffer + * @param num_words IN -- number of words + */ +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, + wordcount_t num_words); + +/* + * @brief Computes (left + right) % mod. + * @note Assumes that (left < mod) and right < mod), and that result does not + * overlap mod. + * @param result OUT -- (left + right) % mod. + * @param left IN -- left term in addition + * @param right IN -- right term in addition + * @param mod IN -- mod + * @param num_words IN -- number of words + */ +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); + +/* + * @brief Counts the number of bits required to represent vli. + * @param vli IN -- very long integer + * @param max_words IN -- number of words + * @return number of bits in given vli + */ +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, + const wordcount_t max_words); + +/* + * @brief Erases (set to 0) vli + * @param vli IN -- very long integer + * @param num_words IN -- number of words + */ +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); + +/* + * @brief check if it is a valid point in the curve + * @param point IN -- point to be checked + * @param curve IN -- elliptic curve + * @return 0 if point is valid + * @exception returns -1 if it is a point at infinity + * @exception returns -2 if x or y is smaller than p, + * @exception returns -3 if y^2 != x^3 + ax + b. + */ +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); + +/* + * @brief Check if a public key is valid. + * @param public_key IN -- The public key to be checked. + * @return returns 0 if the public key is valid + * @exception returns -1 if it is a point at infinity + * @exception returns -2 if x or y is smaller than p, + * @exception returns -3 if y^2 != x^3 + ax + b. + * @exception returns -4 if public key is the group generator. + * + * @note 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. + */ +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); + + /* + * @brief Converts an integer in uECC native format to big-endian bytes. + * @param bytes OUT -- bytes representation + * @param num_bytes IN -- number of bytes + * @param native IN -- uECC native representation + */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const unsigned int *native); + +/* + * @brief Converts big-endian bytes to an integer in uECC native format. + * @param native OUT -- uECC native representation + * @param bytes IN -- bytes representation + * @param num_bytes IN -- number of bytes + */ +void uECC_vli_bytesToNative(unsigned int *native, const uint8_t *bytes, + int num_bytes); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_UECC_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dh.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dh.h new file mode 100644 index 0000000..b828e19 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dh.h @@ -0,0 +1,131 @@ +/* ecc_dh.h - TinyCrypt interface to EC-DH implementation */ + +/* + * 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. + */ + +/* Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief -- Interface to EC-DH implementation. + * + * Overview: This software is an implementation of EC-DH. This implementation + * uses curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + */ + +#ifndef __TC_ECC_DH_H__ +#define __TC_ECC_DH_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a public/private key pair. + * @return returns TC_CRYPTO_SUCCESS (1) if the key pair was generated successfully + * returns TC_CRYPTO_FAIL (0) if error while generating key pair + * + * @param p_public_key OUT -- Will be filled in with the public key. Must be at + * least 2 * the curve size (in bytes) long. For curve secp256r1, p_public_key + * must be 64 bytes long. + * @param p_private_key OUT -- Will be filled in with the private key. Must be as + * long as the curve order (for secp256r1, p_private_key must be 32 bytes long). + * + * @note side-channel countermeasure: algorithm strengthened against timing + * attack. + * @warning A cryptographically-secure PRNG function must be set (using + * uECC_set_rng()) before calling uECC_make_key(). + */ +int uECC_make_key(uint8_t *p_public_key, uint8_t *p_private_key, uECC_Curve curve); + +#ifdef ENABLE_TESTS + +/** + * @brief Create a public/private key pair given a specific d. + * + * @note THIS FUNCTION SHOULD BE CALLED ONLY FOR TEST PURPOSES. Refer to + * uECC_make_key() function for real applications. + */ +int uECC_make_key_with_d(uint8_t *p_public_key, uint8_t *p_private_key, + unsigned int *d, uECC_Curve curve); +#endif + +/** + * @brief Compute a shared secret given your secret key and someone else's + * public key. + * @return returns TC_CRYPTO_SUCCESS (1) if the shared secret was computed successfully + * returns TC_CRYPTO_FAIL (0) otherwise + * + * @param p_secret OUT -- Will be filled in with the shared secret value. Must be + * the same size as the curve size (for curve secp256r1, secret must be 32 bytes + * long. + * @param p_public_key IN -- The public key of the remote party. + * @param p_private_key IN -- Your private key. + * + * @warning It is recommended to use the output of uECC_shared_secret() as the + * input of a recommended Key Derivation Function (see NIST SP 800-108) in + * order to produce a cryptographically secure symmetric key. + */ +int uECC_shared_secret(const uint8_t *p_public_key, const uint8_t *p_private_key, + uint8_t *p_secret, uECC_Curve curve); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_ECC_DH_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dsa.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dsa.h new file mode 100644 index 0000000..aca00bc --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_dsa.h @@ -0,0 +1,139 @@ +/* ecc_dh.h - TinyCrypt interface to EC-DSA implementation */ + +/* + * 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. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief -- Interface to EC-DSA implementation. + * + * Overview: This software is an implementation of EC-DSA. This implementation + * uses curve NIST p-256. + * + * Security: The curve NIST p-256 provides approximately 128 bits of security. + * + * Usage: - To sign: Compute a hash of the data you wish to sign (SHA-2 is + * recommended) and pass it in to ecdsa_sign function along with your + * private key and a random number. You must use a new non-predictable + * random number to generate each new signature. + * - To verify a signature: 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). + */ + +#ifndef __TC_ECC_DSA_H__ +#define __TC_ECC_DSA_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generate an ECDSA signature for a given hash value. + * @return returns TC_CRYPTO_SUCCESS (1) if the signature generated successfully + * returns TC_CRYPTO_FAIL (0) if an error occurred. + * + * @param p_private_key IN -- Your private key. + * @param p_message_hash IN -- The hash of the message to sign. + * @param p_hash_size IN -- The size of p_message_hash in bytes. + * @param p_signature OUT -- Will be filled in with the signature value. Must be + * at least 2 * curve size long (for secp256r1, signature must be 64 bytes long). + * + * @warning A cryptographically-secure PRNG function must be set (using + * uECC_set_rng()) before calling uECC_sign(). + * @note 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. + * @note side-channel countermeasure: algorithm strengthened against timing + * attack. + */ +int uECC_sign(const uint8_t *p_private_key, const uint8_t *p_message_hash, + unsigned p_hash_size, uint8_t *p_signature, uECC_Curve curve); + +#ifdef ENABLE_TESTS +/* + * THIS FUNCTION SHOULD BE CALLED FOR TEST PURPOSES ONLY. + * Refer to uECC_sign() function for real applications. + */ +int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, + unsigned int hash_size, uECC_word_t *k, uint8_t *signature, + uECC_Curve curve); +#endif + +/** + * @brief Verify an ECDSA signature. + * @return returns TC_SUCCESS (1) if the signature is valid + * returns TC_FAIL (0) if the signature is invalid. + * + * @param p_public_key IN -- The signer's public key. + * @param p_message_hash IN -- The hash of the signed data. + * @param p_hash_size IN -- The size of p_message_hash in bytes. + * @param p_signature IN -- The signature values. + * + * @note 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 (hash_size and signature). + */ +int uECC_verify(const uint8_t *p_public_key, const uint8_t *p_message_hash, + unsigned int p_hash_size, const uint8_t *p_signature, uECC_Curve curve); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_ECC_DSA_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h new file mode 100644 index 0000000..e2c8823 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h @@ -0,0 +1,81 @@ +/* uECC_platform_specific.h - Interface to platform specific functions*/ + +/* 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.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + * uECC_platform_specific.h -- Interface to platform specific functions + */ + +#ifndef __UECC_PLATFORM_SPECIFIC_H_ +#define __UECC_PLATFORM_SPECIFIC_H_ + +/* + * 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 cryptographically-secure PRNG function must be set (using uECC_set_rng()) + * before calling uECC_make_key() or uECC_sign(). + * + * Setting a cryptographically-secure PRNG function improves the resistance to + * side-channel attacks for uECC_shared_secret(). + * + * A correct PRNG function is set by default (default_RNG_defined = 1) and works + * for some platforms, such as Unix and Linux. For other platforms, you may need + * to provide another PRNG function. +*/ +#define default_RNG_defined 1 + +int default_CSPRNG(uint8_t *dest, unsigned int size); + +#endif /* __UECC_PLATFORM_SPECIFIC_H_ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac.h new file mode 100644 index 0000000..3a08149 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac.h @@ -0,0 +1,139 @@ +/* hmac.h - TinyCrypt interface to an HMAC implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to an HMAC implementation. + * + * Overview: HMAC is a message authentication code based on hash functions. + * TinyCrypt hard codes SHA-256 as the hash function. A message + * authentication code based on hash functions is also called a + * keyed cryptographic hash function since it performs a + * transformation specified by a key in an arbitrary length data + * set into a fixed length data set (also called tag). + * + * Security: The security of the HMAC depends on the length of the key and + * on the security of the hash function. Note that HMAC primitives + * are much less affected by collision attacks than their + * corresponding hash functions. + * + * Requires: SHA-256 + * + * Usage: 1) call tc_hmac_set_key to set the HMAC key. + * + * 2) call tc_hmac_init to initialize a struct hash_state before + * processing the data. + * + * 3) call tc_hmac_update to process the next input segment; + * tc_hmac_update can be called as many times as needed to process + * all of the segments of the input; the order is important. + * + * 4) call tc_hmac_final to out put the tag. + */ + +#ifndef __TC_HMAC_H__ +#define __TC_HMAC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct tc_hmac_state_struct { + /* the internal state required by h */ + struct tc_sha256_state_struct hash_state; + /* HMAC key schedule */ + uint8_t key[2*TC_SHA256_BLOCK_SIZE]; +}; +typedef struct tc_hmac_state_struct *TCHmacState_t; + +/** + * @brief HMAC set key procedure + * Configures ctx to use key + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if + * ctx == NULL or + * key == NULL or + * key_size == 0 + * @param ctx IN/OUT -- the struct tc_hmac_state_struct to initial + * @param key IN -- the HMAC key to configure + * @param key_size IN -- the HMAC key size + */ +int tc_hmac_set_key(TCHmacState_t ctx, const uint8_t *key, + unsigned int key_size); + +/** + * @brief HMAC init procedure + * Initializes ctx to begin the next HMAC operation + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: ctx == NULL or key == NULL + * @param ctx IN/OUT -- struct tc_hmac_state_struct buffer to init + */ +int tc_hmac_init(TCHmacState_t ctx); + +/** + * @brief HMAC update procedure + * Mixes data_length bytes addressed by data into state + * @return returns TC_CRYPTO_SUCCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: ctx == NULL or key == NULL + * @note Assumes state has been initialized by tc_hmac_init + * @param ctx IN/OUT -- state of HMAC computation so far + * @param data IN -- data to incorporate into state + * @param data_length IN -- size of data in bytes + */ +int tc_hmac_update(TCHmacState_t ctx, const void *data, + unsigned int data_length); + +/** + * @brief HMAC final procedure + * Writes the HMAC tag into the tag buffer + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * tag == NULL or + * ctx == NULL or + * key == NULL or + * taglen != TC_SHA256_DIGEST_SIZE + * @note ctx is erased before exiting. This should never be changed/removed. + * @note Assumes the tag bufer is at least sizeof(hmac_tag_size(state)) bytes + * state has been initialized by tc_hmac_init + * @param tag IN/OUT -- buffer to receive computed HMAC tag + * @param taglen IN -- size of tag in bytes + * @param ctx IN/OUT -- the HMAC state for computing tag + */ +int tc_hmac_final(uint8_t *tag, unsigned int taglen, TCHmacState_t ctx); + +#ifdef __cplusplus +} +#endif + +#endif /*__TC_HMAC_H__*/ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac_prng.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac_prng.h new file mode 100644 index 0000000..ad12cbb --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/hmac_prng.h @@ -0,0 +1,164 @@ +/* hmac_prng.h - TinyCrypt interface to an HMAC-PRNG implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to an HMAC-PRNG implementation. + * + * Overview: A pseudo-random number generator (PRNG) generates a sequence + * of numbers that have a distribution close to the one expected + * for a sequence of truly random numbers. The NIST Special + * Publication 800-90A specifies several mechanisms to generate + * sequences of pseudo random numbers, including the HMAC-PRNG one + * which is based on HMAC. TinyCrypt implements HMAC-PRNG with + * certain modifications from the NIST SP 800-90A spec. + * + * Security: A cryptographically secure PRNG depends on the existence of an + * entropy source to provide a truly random seed as well as the + * security of the primitives used as the building blocks (HMAC and + * SHA256, for TinyCrypt). + * + * The NIST SP 800-90A standard tolerates a null personalization, + * while TinyCrypt requires a non-null personalization. This is + * because a personalization string (the host name concatenated + * with a time stamp, for example) is easily computed and might be + * the last line of defense against failure of the entropy source. + * + * Requires: - SHA-256 + * - HMAC + * + * Usage: 1) call tc_hmac_prng_init to set the HMAC key and process the + * personalization data. + * + * 2) call tc_hmac_prng_reseed to process the seed and additional + * input. + * + * 3) call tc_hmac_prng_generate to out put the pseudo-random data. + */ + +#ifndef __TC_HMAC_PRNG_H__ +#define __TC_HMAC_PRNG_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_HMAC_PRNG_RESEED_REQ -1 + +struct tc_hmac_prng_struct { + /* the HMAC instance for this PRNG */ + struct tc_hmac_state_struct h; + /* the PRNG key */ + uint8_t key[TC_SHA256_DIGEST_SIZE]; + /* PRNG state */ + uint8_t v[TC_SHA256_DIGEST_SIZE]; + /* calls to tc_hmac_prng_generate left before re-seed */ + unsigned int countdown; +}; + +typedef struct tc_hmac_prng_struct *TCHmacPrng_t; + +/** + * @brief HMAC-PRNG initialization procedure + * Initializes prng with personalization, disables tc_hmac_prng_generate + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * prng == NULL, + * personalization == NULL, + * plen > MAX_PLEN + * @note Assumes: - personalization != NULL. + * The personalization is a platform unique string (e.g., the host + * name) and is the last line of defense against failure of the + * entropy source + * @warning NIST SP 800-90A specifies 3 items as seed material during + * initialization: entropy seed, personalization, and an optional + * nonce. TinyCrypts requires instead a non-null personalization + * (which is easily computed) and indirectly requires an entropy + * seed (since the reseed function is mandatorily called after + * init) + * @param prng IN/OUT -- the PRNG state to initialize + * @param personalization IN -- personalization string + * @param plen IN -- personalization length in bytes + */ +int tc_hmac_prng_init(TCHmacPrng_t prng, + const uint8_t *personalization, + unsigned int plen); + +/** + * @brief HMAC-PRNG reseed procedure + * Mixes seed into prng, enables tc_hmac_prng_generate + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * prng == NULL, + * seed == NULL, + * seedlen < MIN_SLEN, + * seendlen > MAX_SLEN, + * additional_input != (const uint8_t *) 0 && additionallen == 0, + * additional_input != (const uint8_t *) 0 && additionallen > MAX_ALEN + * @note Assumes:- tc_hmac_prng_init has been called for prng + * - seed has sufficient entropy. + * + * @param prng IN/OUT -- the PRNG state + * @param seed IN -- entropy to mix into the prng + * @param seedlen IN -- length of seed in bytes + * @param additional_input IN -- additional input to the prng + * @param additionallen IN -- additional input length in bytes + */ +int tc_hmac_prng_reseed(TCHmacPrng_t prng, const uint8_t *seed, + unsigned int seedlen, const uint8_t *additional_input, + unsigned int additionallen); + +/** + * @brief HMAC-PRNG generate procedure + * Generates outlen pseudo-random bytes into out buffer, updates prng + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_HMAC_PRNG_RESEED_REQ (-1) if a reseed is needed + * returns TC_CRYPTO_FAIL (0) if: + * out == NULL, + * prng == NULL, + * outlen == 0, + * outlen >= MAX_OUT + * @note Assumes tc_hmac_prng_init has been called for prng + * @param out IN/OUT -- buffer to receive output + * @param outlen IN -- size of out buffer in bytes + * @param prng IN/OUT -- the PRNG state + */ +int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_HMAC_PRNG_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/sha256.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/sha256.h new file mode 100644 index 0000000..af5e8ba --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/sha256.h @@ -0,0 +1,129 @@ +/* sha256.h - TinyCrypt interface to a SHA-256 implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to a SHA-256 implementation. + * + * Overview: SHA-256 is a NIST approved cryptographic hashing algorithm + * specified in FIPS 180. A hash algorithm maps data of arbitrary + * size to data of fixed length. + * + * Security: SHA-256 provides 128 bits of security against collision attacks + * and 256 bits of security against pre-image attacks. SHA-256 does + * NOT behave like a random oracle, but it can be used as one if + * the string being hashed is prefix-free encoded before hashing. + * + * Usage: 1) call tc_sha256_init to initialize a struct + * tc_sha256_state_struct before hashing a new string. + * + * 2) call tc_sha256_update to hash the next string segment; + * tc_sha256_update can be called as many times as needed to hash + * all of the segments of a string; the order is important. + * + * 3) call tc_sha256_final to out put the digest from a hashing + * operation. + */ + +#ifndef __TC_SHA256_H__ +#define __TC_SHA256_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TC_SHA256_BLOCK_SIZE (64) +#define TC_SHA256_DIGEST_SIZE (32) +#define TC_SHA256_STATE_BLOCKS (TC_SHA256_DIGEST_SIZE/4) + +struct tc_sha256_state_struct { + unsigned int iv[TC_SHA256_STATE_BLOCKS]; + uint64_t bits_hashed; + uint8_t leftover[TC_SHA256_BLOCK_SIZE]; + size_t leftover_offset; +}; + +typedef struct tc_sha256_state_struct *TCSha256State_t; + +/** + * @brief SHA256 initialization procedure + * Initializes s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if s == NULL + * @param s Sha256 state struct + */ +int tc_sha256_init(TCSha256State_t s); + +/** + * @brief SHA256 update procedure + * Hashes data_length bytes addressed by data into state s + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * data == NULL + * @note Assumes s has been initialized by tc_sha256_init + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param s Sha256 state struct + * @param data message to hash + * @param datalen length of message to hash + */ +int tc_sha256_update (TCSha256State_t s, const uint8_t *data, size_t datalen); + +/** + * @brief SHA256 final procedure + * Inserts the completed hash computation into digest + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * s == NULL, + * s->iv == NULL, + * digest == NULL + * @note Assumes: s has been initialized by tc_sha256_init + * digest points to at least TC_SHA256_DIGEST_SIZE bytes + * @warning The state buffer 'leftover' is left in memory after processing + * If your application intends to have sensitive data in this + * buffer, remind to erase it after the data has been processed + * @param digest unsigned eight bit integer + * @param Sha256 state struct + */ +int tc_sha256_final(uint8_t *digest, TCSha256State_t s); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_SHA256_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/utils.h b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/utils.h new file mode 100644 index 0000000..bab5c32 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/include/tinycrypt/utils.h @@ -0,0 +1,95 @@ +/* utils.h - TinyCrypt interface to platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 + * @brief Interface to platform-dependent run-time operations. + * + */ + +#ifndef __TC_UTILS_H__ +#define __TC_UTILS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Copy the the buffer 'from' to the buffer 'to'. + * @return returns TC_CRYPTO_SUCCESS (1) + * returns TC_CRYPTO_FAIL (0) if: + * from_len > to_len. + * + * @param to OUT -- destination buffer + * @param to_len IN -- length of destination buffer + * @param from IN -- origin buffer + * @param from_len IN -- length of origin buffer + */ +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len); + +/** + * @brief Set the value 'val' into the buffer 'to', 'len' times. + * + * @param to OUT -- destination buffer + * @param val IN -- value to be set in 'to' + * @param len IN -- number of times the value will be copied + */ +void _set(void *to, uint8_t val, unsigned int len); + +/* + * @brief AES specific doubling function, which utilizes + * the finite field used by AES. + * @return Returns a^2 + * + * @param a IN/OUT -- value to be doubled + */ +uint8_t _double_byte(uint8_t a); + +/* + * @brief Constant-time algorithm to compare if two sequences of bytes are equal + * @return Returns 0 if equal, and non-zero otherwise + * + * @param a IN -- sequence of bytes a + * @param b IN -- sequence of bytes b + * @param size IN -- size of sequences a and b + */ +int _compare(const uint8_t *a, const uint8_t *b, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* __TC_UTILS_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/pkg.yml b/bootloader/mcuboot/ext/tinycrypt/lib/pkg.yml new file mode 100644 index 0000000..42db8aa --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/pkg.yml @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: ext/tinycrypt/lib +pkg.description: "MCUboot's bundled tinycrypt" +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.src_dirs: + - "source" + +pkg.cflags: + - "-std=c99" diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/aes_decrypt.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/aes_decrypt.c new file mode 100644 index 0000000..993a618 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/aes_decrypt.c @@ -0,0 +1,164 @@ +/* aes_decrypt.c - TinyCrypt implementation of AES decryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +static const uint8_t inv_sbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, + 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, + 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, + 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, + 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, + 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, + 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, + 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, + 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, + 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0c, 0x7d +}; + +int tc_aes128_set_decrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + return tc_aes128_set_encrypt_key(s, k); +} + +#define mult8(a)(_double_byte(_double_byte(_double_byte(a)))) +#define mult9(a)(mult8(a)^(a)) +#define multb(a)(mult8(a)^_double_byte(a)^(a)) +#define multd(a)(mult8(a)^_double_byte(_double_byte(a))^(a)) +#define multe(a)(mult8(a)^_double_byte(_double_byte(a))^_double_byte(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = multe(in[0]) ^ multb(in[1]) ^ multd(in[2]) ^ mult9(in[3]); + out[1] = mult9(in[0]) ^ multe(in[1]) ^ multb(in[2]) ^ multd(in[3]); + out[2] = multd(in[0]) ^ mult9(in[1]) ^ multe(in[2]) ^ multb(in[3]); + out[3] = multb(in[0]) ^ multd(in[1]) ^ mult9(in[2]) ^ multe(in[3]); +} + +static inline void inv_mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2*Nb], s+(2*Nb)); + mult_row_column(&t[3*Nb], s+(3*Nb)); + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void inv_sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb*Nk); ++i) { + s[i] = inv_sbox[s[i]]; + } +} + +/* + * This inv_shift_rows also implements the matrix flip required for + * inv_mix_columns, but performs it here to reduce the number of memory + * operations. + */ +static inline void inv_shift_rows(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + t[0] = s[0]; t[1] = s[13]; t[2] = s[10]; t[3] = s[7]; + t[4] = s[4]; t[5] = s[1]; t[6] = s[14]; t[7] = s[11]; + t[8] = s[8]; t[9] = s[5]; t[10] = s[2]; t[11] = s[15]; + t[12] = s[12]; t[13] = s[9]; t[14] = s[6]; t[15] = s[3]; + (void)_copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_decrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + + add_round_key(state, s->words + Nb*Nr); + + for (i = Nr - 1; i > 0; --i) { + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words + Nb*i); + inv_mix_columns(state); + } + + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, s->words); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /*zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + + return TC_CRYPTO_SUCCESS; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/aes_encrypt.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/aes_encrypt.c new file mode 100644 index 0000000..8991aee --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/aes_encrypt.c @@ -0,0 +1,191 @@ +/* aes_encrypt.c - TinyCrypt implementation of AES encryption procedure */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +static const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16 +}; + +static inline unsigned int rotword(unsigned int a) +{ + return (((a) >> 24)|((a) << 8)); +} + +#define subbyte(a, o)(sbox[((a) >> (o))&0xff] << (o)) +#define subword(a)(subbyte(a, 24)|subbyte(a, 16)|subbyte(a, 8)|subbyte(a, 0)) + +int tc_aes128_set_encrypt_key(TCAesKeySched_t s, const uint8_t *k) +{ + const unsigned int rconst[11] = { + 0x00000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, + 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000 + }; + unsigned int i; + unsigned int t; + + if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } else if (k == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + for (i = 0; i < Nk; ++i) { + s->words[i] = (k[Nb*i]<<24) | (k[Nb*i+1]<<16) | + (k[Nb*i+2]<<8) | (k[Nb*i+3]); + } + + for (; i < (Nb * (Nr + 1)); ++i) { + t = s->words[i-1]; + if ((i % Nk) == 0) { + t = subword(rotword(t)) ^ rconst[i/Nk]; + } + s->words[i] = s->words[i-Nk] ^ t; + } + + return TC_CRYPTO_SUCCESS; +} + +static inline void add_round_key(uint8_t *s, const unsigned int *k) +{ + s[0] ^= (uint8_t)(k[0] >> 24); s[1] ^= (uint8_t)(k[0] >> 16); + s[2] ^= (uint8_t)(k[0] >> 8); s[3] ^= (uint8_t)(k[0]); + s[4] ^= (uint8_t)(k[1] >> 24); s[5] ^= (uint8_t)(k[1] >> 16); + s[6] ^= (uint8_t)(k[1] >> 8); s[7] ^= (uint8_t)(k[1]); + s[8] ^= (uint8_t)(k[2] >> 24); s[9] ^= (uint8_t)(k[2] >> 16); + s[10] ^= (uint8_t)(k[2] >> 8); s[11] ^= (uint8_t)(k[2]); + s[12] ^= (uint8_t)(k[3] >> 24); s[13] ^= (uint8_t)(k[3] >> 16); + s[14] ^= (uint8_t)(k[3] >> 8); s[15] ^= (uint8_t)(k[3]); +} + +static inline void sub_bytes(uint8_t *s) +{ + unsigned int i; + + for (i = 0; i < (Nb * Nk); ++i) { + s[i] = sbox[s[i]]; + } +} + +#define triple(a)(_double_byte(a)^(a)) + +static inline void mult_row_column(uint8_t *out, const uint8_t *in) +{ + out[0] = _double_byte(in[0]) ^ triple(in[1]) ^ in[2] ^ in[3]; + out[1] = in[0] ^ _double_byte(in[1]) ^ triple(in[2]) ^ in[3]; + out[2] = in[0] ^ in[1] ^ _double_byte(in[2]) ^ triple(in[3]); + out[3] = triple(in[0]) ^ in[1] ^ in[2] ^ _double_byte(in[3]); +} + +static inline void mix_columns(uint8_t *s) +{ + uint8_t t[Nb*Nk]; + + mult_row_column(t, s); + mult_row_column(&t[Nb], s+Nb); + mult_row_column(&t[2 * Nb], s + (2 * Nb)); + mult_row_column(&t[3 * Nb], s + (3 * Nb)); + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +/* + * This shift_rows also implements the matrix flip required for mix_columns, but + * performs it here to reduce the number of memory operations. + */ +static inline void shift_rows(uint8_t *s) +{ + uint8_t t[Nb * Nk]; + + t[0] = s[0]; t[1] = s[5]; t[2] = s[10]; t[3] = s[15]; + t[4] = s[4]; t[5] = s[9]; t[6] = s[14]; t[7] = s[3]; + t[8] = s[8]; t[9] = s[13]; t[10] = s[2]; t[11] = s[7]; + t[12] = s[12]; t[13] = s[1]; t[14] = s[6]; t[15] = s[11]; + (void) _copy(s, sizeof(t), t, sizeof(t)); +} + +int tc_aes_encrypt(uint8_t *out, const uint8_t *in, const TCAesKeySched_t s) +{ + uint8_t state[Nk*Nb]; + unsigned int i; + + if (out == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (in == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (s == (TCAesKeySched_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)_copy(state, sizeof(state), in, sizeof(state)); + add_round_key(state, s->words); + + for (i = 0; i < (Nr - 1); ++i) { + sub_bytes(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, s->words + Nb*(i+1)); + } + + sub_bytes(state); + shift_rows(state); + add_round_key(state, s->words + Nb*(i+1)); + + (void)_copy(out, sizeof(state), state, sizeof(state)); + + /* zeroing out the state buffer */ + _set(state, TC_ZERO_BYTE, sizeof(state)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/cbc_mode.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/cbc_mode.c new file mode 100644 index 0000000..62d7879 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/cbc_mode.c @@ -0,0 +1,114 @@ +/* cbc_mode.c - TinyCrypt implementation of CBC mode encryption & decryption */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + unsigned int n, m; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen + TC_AES_BLOCK_SIZE) { + return TC_CRYPTO_FAIL; + } + + /* copy iv to the buffer */ + (void)_copy(buffer, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + /* copy iv to the output buffer */ + (void)_copy(out, TC_AES_BLOCK_SIZE, iv, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + + for (n = m = 0; n < inlen; ++n) { + buffer[m++] ^= *in++; + if (m == TC_AES_BLOCK_SIZE) { + (void)tc_aes_encrypt(buffer, buffer, sched); + (void)_copy(out, TC_AES_BLOCK_SIZE, + buffer, TC_AES_BLOCK_SIZE); + out += TC_AES_BLOCK_SIZE; + m = 0; + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, const uint8_t *iv, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + const uint8_t *p; + unsigned int n, m; + + /* sanity check the inputs */ + if (out == (uint8_t *) 0 || + in == (const uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + (inlen % TC_AES_BLOCK_SIZE) != 0 || + (outlen % TC_AES_BLOCK_SIZE) != 0 || + outlen != inlen - TC_AES_BLOCK_SIZE) { + return TC_CRYPTO_FAIL; + } + + /* + * Note that in == iv + ciphertext, i.e. the iv and the ciphertext are + * contiguous. This allows for a very efficient decryption algorithm + * that would not otherwise be possible. + */ + p = iv; + for (n = m = 0; n < inlen; ++n) { + if ((n % TC_AES_BLOCK_SIZE) == 0) { + (void)tc_aes_decrypt(buffer, in, sched); + in += TC_AES_BLOCK_SIZE; + m = 0; + } + *out++ = buffer[m++] ^ *p++; + } + + return TC_CRYPTO_SUCCESS; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/ccm_mode.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/ccm_mode.c new file mode 100644 index 0000000..929adac --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/ccm_mode.c @@ -0,0 +1,266 @@ +/* ccm_mode.c - TinyCrypt implementation of CCM mode */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +#include + +int tc_ccm_config(TCCcmMode_t c, TCAesKeySched_t sched, uint8_t *nonce, + unsigned int nlen, unsigned int mlen) +{ + + /* input sanity check: */ + if (c == (TCCcmMode_t) 0 || + sched == (TCAesKeySched_t) 0 || + nonce == (uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } else if (nlen != 13) { + return TC_CRYPTO_FAIL; /* The allowed nonce size is: 13. See documentation.*/ + } else if ((mlen < 4) || (mlen > 16) || (mlen & 1)) { + return TC_CRYPTO_FAIL; /* The allowed mac sizes are: 4, 6, 8, 10, 12, 14, 16.*/ + } + + c->mlen = mlen; + c->sched = sched; + c->nonce = nonce; + + return TC_CRYPTO_SUCCESS; +} + +/** + * Variation of CBC-MAC mode used in CCM. + */ +static void ccm_cbc_mac(uint8_t *T, const uint8_t *data, unsigned int dlen, + unsigned int flag, TCAesKeySched_t sched) +{ + + unsigned int i; + + if (flag > 0) { + T[0] ^= (uint8_t)(dlen >> 8); + T[1] ^= (uint8_t)(dlen); + dlen += 2; i = 2; + } else { + i = 0; + } + + while (i < dlen) { + T[i++ % (Nb * Nk)] ^= *data++; + if (((i % (Nb * Nk)) == 0) || dlen == i) { + (void) tc_aes_encrypt(T, T, sched); + } + } +} + +/** + * Variation of CTR mode used in CCM. + * The CTR mode used by CCM is slightly different than the conventional CTR + * mode (the counter is increased before encryption, instead of after + * encryption). Besides, it is assumed that the counter is stored in the last + * 2 bytes of the nonce. + */ +static int ccm_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + uint16_t block_num; + unsigned int i; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the counter to the nonce */ + (void) _copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 2 bytes of the nonce to be incremented */ + block_num = (uint16_t) ((nonce[14] << 8)|(nonce[15])); + for (i = 0; i < inlen; ++i) { + if ((i % (TC_AES_BLOCK_SIZE)) == 0) { + block_num++; + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + if (!tc_aes_encrypt(buffer, nonce, sched)) { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[i % (TC_AES_BLOCK_SIZE)] ^ *in++; + } + + /* update the counter */ + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_generation_encryption(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < (plen + c->mlen))) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* GENERATING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0) | (((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i <= 13; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)(plen >> 8); + b[15] = (uint8_t)(plen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(tag, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(tag, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(tag, payload, plen, 0, c->sched); + } + + /* ENCRYPTION: */ + + /* formatting the sequence b for encryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + b[14] = b[15] = TC_ZERO_BYTE; + + /* encrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen, payload, plen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter for ctr_mode (0):*/ + + /* encrypting b and adding the tag to the output: */ + (void) tc_aes_encrypt(b, b, c->sched); + out += plen; + for (i = 0; i < c->mlen; ++i) { + *out++ = tag[i] ^ b[i]; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_ccm_decryption_verification(uint8_t *out, unsigned int olen, + const uint8_t *associated_data, + unsigned int alen, const uint8_t *payload, + unsigned int plen, TCCcmMode_t c) +{ + + /* input sanity check: */ + if ((out == (uint8_t *) 0) || + (c == (TCCcmMode_t) 0) || + ((plen > 0) && (payload == (uint8_t *) 0)) || + ((alen > 0) && (associated_data == (uint8_t *) 0)) || + (alen >= TC_CCM_AAD_MAX_BYTES) || /* associated data size unsupported */ + (plen >= TC_CCM_PAYLOAD_MAX_BYTES) || /* payload size unsupported */ + (olen < plen - c->mlen)) { /* invalid output buffer size */ + return TC_CRYPTO_FAIL; + } + + uint8_t b[Nb * Nk]; + uint8_t tag[Nb * Nk]; + unsigned int i; + + /* DECRYPTION: */ + + /* formatting the sequence b for decryption: */ + b[0] = 1; /* q - 1 = 2 - 1 = 1 */ + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = b[15] = TC_ZERO_BYTE; /* initial counter value is 0 */ + + /* decrypting payload using ctr mode: */ + ccm_ctr_mode(out, plen - c->mlen, payload, plen - c->mlen, b, c->sched); + + b[14] = b[15] = TC_ZERO_BYTE; /* restoring initial counter value (0) */ + + /* encrypting b and restoring the tag from input: */ + (void) tc_aes_encrypt(b, b, c->sched); + for (i = 0; i < c->mlen; ++i) { + tag[i] = *(payload + plen - c->mlen + i) ^ b[i]; + } + + /* VERIFYING THE AUTHENTICATION TAG: */ + + /* formatting the sequence b for authentication: */ + b[0] = ((alen > 0) ? 0x40:0)|(((c->mlen - 2) / 2 << 3)) | (1); + for (i = 1; i < 14; ++i) { + b[i] = c->nonce[i - 1]; + } + b[14] = (uint8_t)((plen - c->mlen) >> 8); + b[15] = (uint8_t)(plen - c->mlen); + + /* computing the authentication tag using cbc-mac: */ + (void) tc_aes_encrypt(b, b, c->sched); + if (alen > 0) { + ccm_cbc_mac(b, associated_data, alen, 1, c->sched); + } + if (plen > 0) { + ccm_cbc_mac(b, out, plen - c->mlen, 0, c->sched); + } + + /* comparing the received tag and the computed one: */ + if (_compare(b, tag, c->mlen) == 0) { + return TC_CRYPTO_SUCCESS; + } else { + /* erase the decrypted buffer in case of mac validation failure: */ + _set(out, 0, plen - c->mlen); + return TC_CRYPTO_FAIL; + } +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/cmac_mode.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/cmac_mode.c new file mode 100644 index 0000000..96d147e --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/cmac_mode.c @@ -0,0 +1,254 @@ +/* cmac_mode.c - TinyCrypt CMAC mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include +#include + +/* max number of calls until change the key (2^48).*/ +const static uint64_t MAX_CALLS = ((uint64_t)1 << 48); + +/* + * gf_wrap -- In our implementation, GF(2^128) is represented as a 16 byte + * array with byte 0 the most significant and byte 15 the least significant. + * High bit carry reduction is based on the primitive polynomial + * + * X^128 + X^7 + X^2 + X + 1, + * + * which leads to the reduction formula X^128 = X^7 + X^2 + X + 1. Indeed, + * since 0 = (X^128 + X^7 + X^2 + 1) mod (X^128 + X^7 + X^2 + X + 1) and since + * addition of polynomials with coefficients in Z/Z(2) is just XOR, we can + * add X^128 to both sides to get + * + * X^128 = (X^7 + X^2 + X + 1) mod (X^128 + X^7 + X^2 + X + 1) + * + * and the coefficients of the polynomial on the right hand side form the + * string 1000 0111 = 0x87, which is the value of gf_wrap. + * + * This gets used in the following way. Doubling in GF(2^128) is just a left + * shift by 1 bit, except when the most significant bit is 1. In the latter + * case, the relation X^128 = X^7 + X^2 + X + 1 says that the high order bit + * that overflows beyond 128 bits can be replaced by addition of + * X^7 + X^2 + X + 1 <--> 0x87 to the low order 128 bits. Since addition + * in GF(2^128) is represented by XOR, we therefore only have to XOR 0x87 + * into the low order byte after a left shift when the starting high order + * bit is 1. + */ +const unsigned char gf_wrap = 0x87; + +/* + * assumes: out != NULL and points to a GF(2^n) value to receive the + * doubled value; + * in != NULL and points to a 16 byte GF(2^n) value + * to double; + * the in and out buffers do not overlap. + * effects: doubles the GF(2^n) value pointed to by "in" and places + * the result in the GF(2^n) value pointed to by "out." + */ +void gf_double(uint8_t *out, uint8_t *in) +{ + + /* start with low order byte */ + uint8_t *x = in + (TC_AES_BLOCK_SIZE - 1); + + /* if msb == 1, we need to add the gf_wrap value, otherwise add 0 */ + uint8_t carry = (in[0] >> 7) ? gf_wrap : 0; + + out += (TC_AES_BLOCK_SIZE - 1); + for (;;) { + *out-- = (*x << 1) ^ carry; + if (x == in) { + break; + } + carry = *x-- >> 7; + } +} + +int tc_cmac_setup(TCCmacState_t s, const uint8_t *key, TCAesKeySched_t sched) +{ + + /* input sanity check: */ + if (s == (TCCmacState_t) 0 || + key == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + /* put s into a known state */ + _set(s, 0, sizeof(*s)); + s->sched = sched; + + /* configure the encryption key used by the underlying block cipher */ + tc_aes128_set_encrypt_key(s->sched, key); + + /* compute s->K1 and s->K2 from s->iv using s->keyid */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + tc_aes_encrypt(s->iv, s->iv, s->sched); + gf_double (s->K1, s->iv); + gf_double (s->K2, s->K1); + + /* reset s->iv to 0 in case someone wants to compute now */ + tc_cmac_init(s); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_erase(TCCmacState_t s) +{ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_init(TCCmacState_t s) +{ + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* CMAC starts with an all zero initialization vector */ + _set(s->iv, 0, TC_AES_BLOCK_SIZE); + + /* and the leftover buffer is empty */ + _set(s->leftover, 0, TC_AES_BLOCK_SIZE); + s->leftover_offset = 0; + + /* Set countdown to max number of calls allowed before re-keying: */ + s->countdown = MAX_CALLS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_update(TCCmacState_t s, const uint8_t *data, size_t data_length) +{ + unsigned int i; + + /* input sanity check: */ + if (s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + if (data_length == 0) { + return TC_CRYPTO_SUCCESS; + } + if (data == (const uint8_t *) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->countdown == 0) { + return TC_CRYPTO_FAIL; + } + + s->countdown--; + + if (s->leftover_offset > 0) { + /* last data added to s didn't end on a TC_AES_BLOCK_SIZE byte boundary */ + size_t remaining_space = TC_AES_BLOCK_SIZE - s->leftover_offset; + + if (data_length < remaining_space) { + /* still not enough data to encrypt this time either */ + _copy(&s->leftover[s->leftover_offset], data_length, data, data_length); + s->leftover_offset += data_length; + return TC_CRYPTO_SUCCESS; + } + /* leftover block is now full; encrypt it first */ + _copy(&s->leftover[s->leftover_offset], + remaining_space, + data, + remaining_space); + data_length -= remaining_space; + data += remaining_space; + s->leftover_offset = 0; + + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + } + + /* CBC encrypt each (except the last) of the data blocks */ + while (data_length > TC_AES_BLOCK_SIZE) { + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= data[i]; + } + tc_aes_encrypt(s->iv, s->iv, s->sched); + data += TC_AES_BLOCK_SIZE; + data_length -= TC_AES_BLOCK_SIZE; + } + + if (data_length > 0) { + /* save leftover data for next time */ + _copy(s->leftover, data_length, data, data_length); + s->leftover_offset = data_length; + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_cmac_final(uint8_t *tag, TCCmacState_t s) +{ + uint8_t *k; + unsigned int i; + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + s == (TCCmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + if (s->leftover_offset == TC_AES_BLOCK_SIZE) { + /* the last message block is a full-sized block */ + k = (uint8_t *) s->K1; + } else { + /* the final message block is not a full-sized block */ + size_t remaining = TC_AES_BLOCK_SIZE - s->leftover_offset; + + _set(&s->leftover[s->leftover_offset], 0, remaining); + s->leftover[s->leftover_offset] = TC_CMAC_PADDING; + k = (uint8_t *) s->K2; + } + for (i = 0; i < TC_AES_BLOCK_SIZE; ++i) { + s->iv[i] ^= s->leftover[i] ^ k[i]; + } + + tc_aes_encrypt(tag, s->iv, s->sched); + + /* erasing state: */ + tc_cmac_erase(s); + + return TC_CRYPTO_SUCCESS; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_mode.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_mode.c new file mode 100644 index 0000000..ec8c394 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_mode.c @@ -0,0 +1,91 @@ +/* ctr_mode.c - TinyCrypt CTR mode implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +int tc_ctr_mode(uint8_t *out, unsigned int outlen, const uint8_t *in, + unsigned int inlen, uint8_t *ctr, uint32_t *blk_off, + const TCAesKeySched_t sched) +{ + + uint8_t buffer[TC_AES_BLOCK_SIZE]; + uint8_t nonce[TC_AES_BLOCK_SIZE]; + unsigned int block_num; + unsigned int i; + uint32_t n; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + in == (uint8_t *) 0 || + ctr == (uint8_t *) 0 || + blk_off == (uint32_t *) 0 || + sched == (TCAesKeySched_t) 0 || + inlen == 0 || + outlen == 0 || + outlen != inlen) { + return TC_CRYPTO_FAIL; + } + + /* copy the ctr to the nonce */ + (void)_copy(nonce, sizeof(nonce), ctr, sizeof(nonce)); + + /* select the last 4 bytes of the nonce to be incremented */ + block_num = (nonce[12] << 24) | (nonce[13] << 16) | + (nonce[14] << 8) | (nonce[15]); + n = *blk_off; + for (i = 0; i < inlen; ++i) { + if (n == 0) { + /* encrypt data using the current nonce */ + if (tc_aes_encrypt(buffer, nonce, sched)) { + block_num++; + nonce[12] = (uint8_t)(block_num >> 24); + nonce[13] = (uint8_t)(block_num >> 16); + nonce[14] = (uint8_t)(block_num >> 8); + nonce[15] = (uint8_t)(block_num); + } else { + return TC_CRYPTO_FAIL; + } + } + /* update the output */ + *out++ = buffer[n] ^ *in++; + n = (n + 1) % TC_AES_BLOCK_SIZE; + } + *blk_off = n; + + /* update the counter */ + ctr[12] = nonce[12]; ctr[13] = nonce[13]; + ctr[14] = nonce[14]; ctr[15] = nonce[15]; + + return TC_CRYPTO_SUCCESS; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_prng.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_prng.c new file mode 100644 index 0000000..cac2cc4 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/ctr_prng.c @@ -0,0 +1,283 @@ +/* ctr_prng.c - TinyCrypt implementation of CTR-PRNG */ + +/* + * Copyright (c) 2016, Chris Morrison + * 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. + */ + +#include +#include +#include +#include + +/* + * This PRNG is based on the CTR_DRBG described in Recommendation for Random + * Number Generation Using Deterministic Random Bit Generators, + * NIST SP 800-90A Rev. 1. + * + * Annotations to particular steps (e.g. 10.2.1.2 Step 1) refer to the steps + * described in that document. + * + */ + +/** + * @brief Array incrementer + * Treats the supplied array as one contiguous number (MSB in arr[0]), and + * increments it by one + * @return none + * @param arr IN/OUT -- array to be incremented + * @param len IN -- size of arr in bytes + */ +static void arrInc(uint8_t arr[], unsigned int len) +{ + unsigned int i; + if (0 != arr) { + for (i = len; i > 0U; i--) { + if (++arr[i-1] != 0U) { + break; + } + } + } +} + +/** + * @brief CTR PRNG update + * Updates the internal state of supplied the CTR PRNG context + * increments it by one + * @return none + * @note Assumes: providedData is (TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE) bytes long + * @param ctx IN/OUT -- CTR PRNG state + * @param providedData IN -- data used when updating the internal state + */ +static void tc_ctr_prng_update(TCCtrPrng_t * const ctx, uint8_t const * const providedData) +{ + if (0 != ctx) { + /* 10.2.1.2 step 1 */ + uint8_t temp[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + unsigned int len = 0U; + + /* 10.2.1.2 step 2 */ + while (len < sizeof temp) { + unsigned int blocklen = sizeof(temp) - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.2 step 2.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.2 step 2.2 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.2 step 2.3/step 3 */ + memcpy(&(temp[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.2 step 4 */ + if (0 != providedData) { + unsigned int i; + for (i = 0U; i < sizeof temp; i++) { + temp[i] ^= providedData[i]; + } + } + + /* 10.2.1.2 step 5 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, temp); + + /* 10.2.1.2 step 6 */ + memcpy(ctx->V, &(temp[TC_AES_KEY_SIZE]), TC_AES_BLOCK_SIZE); + } +} + +int tc_ctr_prng_init(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const personalization, + unsigned int pLen) +{ + int result = TC_CRYPTO_FAIL; + unsigned int i; + uint8_t personalization_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + uint8_t zeroArr[TC_AES_BLOCK_SIZE] = {0U}; + + if (0 != personalization) { + /* 10.2.1.3.1 step 1 */ + unsigned int len = pLen; + if (len > sizeof personalization_buf) { + len = sizeof personalization_buf; + } + + /* 10.2.1.3.1 step 2 */ + memcpy(personalization_buf, personalization, len); + } + + if ((0 != ctx) && (0 != entropy) && (entropyLen >= sizeof seed_material)) { + /* 10.2.1.3.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= personalization_buf[i]; + } + + /* 10.2.1.3.1 step 4 */ + (void)tc_aes128_set_encrypt_key(&ctx->key, zeroArr); + + /* 10.2.1.3.1 step 5 */ + memset(ctx->V, 0x00, sizeof ctx->V); + + /* 10.2.1.3.1 step 6 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.3.1 step 7 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx, + uint8_t const * const entropy, + unsigned int entropyLen, + uint8_t const * const additional_input, + unsigned int additionallen) +{ + unsigned int i; + int result = TC_CRYPTO_FAIL; + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + uint8_t seed_material[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + + if (0 != additional_input) { + /* 10.2.1.4.1 step 1 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + + /* 10.2.1.4.1 step 2 */ + memcpy(additional_input_buf, additional_input, len); + } + + unsigned int seedlen = (unsigned int)TC_AES_KEY_SIZE + (unsigned int)TC_AES_BLOCK_SIZE; + if ((0 != ctx) && (entropyLen >= seedlen)) { + /* 10.2.1.4.1 step 3 */ + memcpy(seed_material, entropy, sizeof seed_material); + for (i = 0U; i < sizeof seed_material; i++) { + seed_material[i] ^= additional_input_buf[i]; + } + + /* 10.2.1.4.1 step 4 */ + tc_ctr_prng_update(ctx, seed_material); + + /* 10.2.1.4.1 step 5 */ + ctx->reseedCount = 1U; + + result = TC_CRYPTO_SUCCESS; + } + return result; +} + +int tc_ctr_prng_generate(TCCtrPrng_t * const ctx, + uint8_t const * const additional_input, + unsigned int additionallen, + uint8_t * const out, + unsigned int outlen) +{ + /* 2^48 - see section 10.2.1 */ + static const uint64_t MAX_REQS_BEFORE_RESEED = 0x1000000000000ULL; + + /* 2^19 bits - see section 10.2.1 */ + static const unsigned int MAX_BYTES_PER_REQ = 65536U; + + unsigned int result = TC_CRYPTO_FAIL; + + if ((0 != ctx) && (0 != out) && (outlen < MAX_BYTES_PER_REQ)) { + /* 10.2.1.5.1 step 1 */ + if (ctx->reseedCount > MAX_REQS_BEFORE_RESEED) { + result = TC_CTR_PRNG_RESEED_REQ; + } else { + uint8_t additional_input_buf[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE] = {0U}; + if (0 != additional_input) { + /* 10.2.1.5.1 step 2 */ + unsigned int len = additionallen; + if (len > sizeof additional_input_buf) { + len = sizeof additional_input_buf; + } + memcpy(additional_input_buf, additional_input, len); + tc_ctr_prng_update(ctx, additional_input_buf); + } + + /* 10.2.1.5.1 step 3 - implicit */ + + /* 10.2.1.5.1 step 4 */ + unsigned int len = 0U; + while (len < outlen) { + unsigned int blocklen = outlen - len; + uint8_t output_block[TC_AES_BLOCK_SIZE]; + + /* 10.2.1.5.1 step 4.1 */ + arrInc(ctx->V, sizeof ctx->V); + + /* 10.2.1.5.1 step 4.2 */ + (void)tc_aes_encrypt(output_block, ctx->V, &ctx->key); + + /* 10.2.1.5.1 step 4.3/step 5 */ + if (blocklen > TC_AES_BLOCK_SIZE) { + blocklen = TC_AES_BLOCK_SIZE; + } + memcpy(&(out[len]), output_block, blocklen); + + len += blocklen; + } + + /* 10.2.1.5.1 step 6 */ + tc_ctr_prng_update(ctx, additional_input_buf); + + /* 10.2.1.5.1 step 7 */ + ctx->reseedCount++; + + /* 10.2.1.5.1 step 8 */ + result = TC_CRYPTO_SUCCESS; + } + } + + return result; +} + +void tc_ctr_prng_uninstantiate(TCCtrPrng_t * const ctx) +{ + if (0 != ctx) { + memset(ctx->key.words, 0x00, sizeof ctx->key.words); + memset(ctx->V, 0x00, sizeof ctx->V); + ctx->reseedCount = 0U; + } +} + + + + diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc.c new file mode 100644 index 0000000..46080bf --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc.c @@ -0,0 +1,942 @@ +/* ecc.c - TinyCrypt implementation of common ECC functions */ + +/* + * 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. + * + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +/* IMPORTANT: Make sure a cryptographically-secure PRNG is set and the platform + * has access to enough entropy in order to feed the PRNG regularly. */ +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#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; +} + +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; + } +} + +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); +} + +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); +} + +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); +} + +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]; + } +} + +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; +} + +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_word_t cond_set(uECC_word_t p_true, uECC_word_t p_false, unsigned int cond) +{ + return (p_true*(cond)) | (p_false*(!cond)); +} + +/* Computes result = left - right, returning borrow, in constant time. + * 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) +{ + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t diff = left[i] - right[i] - borrow; + uECC_word_t val = (diff > left[i]); + borrow = cond_set(val, borrow, (diff != left[i])); + + result[i] = diff; + } + return borrow; +} + +/* Computes result = left + right, returning carry, in constant time. + * Can modify in place. */ +static 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; + uECC_word_t val = (sum < left[i]); + carry = cond_set(val, carry, (sum != left[i])); + result[i] = sum; + } + return carry; +} + +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, + wordcount_t num_words) +{ + uECC_word_t tmp[NUM_ECC_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. */ +static 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); + } +} + +static void muladd(uECC_word_t a, uECC_word_t b, uECC_word_t *r0, + uECC_word_t *r1, uECC_word_t *r2) +{ + + 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; + +} + +/* Computes result = left * right. Result must be 2 * num_words long. */ +static 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; +} + +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); + } +} + +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. */ +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 * NUM_ECC_WORDS]; + uECC_word_t tmp[2 * NUM_ECC_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; + } + /* Swap the index if there was no borrow */ + index = !(index ^ 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); +} + +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 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} + +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 * NUM_ECC_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); + + curve->mmod_fast(result, product); +} + +static 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); +} + + +#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; + } +} + +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[NUM_ECC_WORDS], b[NUM_ECC_WORDS]; + uECC_word_t u[NUM_ECC_WORDS], v[NUM_ECC_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 ------ */ + +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[NUM_ECC_WORDS]; + uECC_word_t t5[NUM_ECC_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) */ + /* t4 = B * (A - x3) - y1^4 = y3: */ + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); + + uECC_vli_set(X1, Z1, num_words); + uECC_vli_set(Z1, Y1, num_words); + uECC_vli_set(Y1, t4, num_words); +} + +void x_side_default(uECC_word_t *result, + const uECC_word_t *x, + uECC_Curve curve) +{ + uECC_word_t _3[NUM_ECC_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 */ + /* r = x^3 - 3x + b: */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); +} + +uECC_Curve uECC_secp256r1(void) +{ + return &curve_secp256r1; +} + +void vli_mmod_fast_secp256r1(unsigned int *result, unsigned int*product) +{ + unsigned int tmp[NUM_ECC_WORDS]; + int carry; + + /* t */ + uECC_vli_set(result, product, NUM_ECC_WORDS); + + /* 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_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* 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_ECC_WORDS); + carry += uECC_vli_add(result, result, tmp, NUM_ECC_WORDS); + + /* 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_ECC_WORDS); + + /* 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_ECC_WORDS); + + /* 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_ECC_WORDS); + + /* 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_ECC_WORDS); + + /* 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_ECC_WORDS); + + /* 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_ECC_WORDS); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + while (carry < 0); + } else { + while (carry || + uECC_vli_cmp_unsafe(curve_secp256r1.p, result, NUM_ECC_WORDS) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, NUM_ECC_WORDS); + } + } +} + +uECC_word_t EccPoint_isZero(const uECC_word_t *point, uECC_Curve curve) +{ + return uECC_vli_isZero(point, curve->num_words * 2); +} + +void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z, + uECC_Curve curve) +{ + uECC_word_t t1[NUM_ECC_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[NUM_ECC_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); +} + +void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1, + uECC_word_t * X2, uECC_word_t * Y2, + uECC_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_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); /* t1 = 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); /* t2 = y1*(C - B) */ + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, X2, 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); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + 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_Curve curve) +{ + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[NUM_ECC_WORDS]; + uECC_word_t t6[NUM_ECC_WORDS]; + uECC_word_t t7[NUM_ECC_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); /* 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) */ + /* t4 = (y2 - y1)*(B - x3) - E = y3: */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); + + 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) */ + /* t2 = (y2+y1)*(x3' - B) - E = y3': */ + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); + + uECC_vli_set(X1, t7, num_words); +} + +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][NUM_ECC_WORDS]; + uECC_word_t Ry[2][NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_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); + + 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], curve); + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + } + + nb = !uECC_vli_testBit(scalar, 0); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], 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))*/ + /* yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, point + num_words, curve); + /* Xb * yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); + /* End 1/Z calculation */ + + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], 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); +} + +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; +} + +uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, + uECC_Curve curve) +{ + + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + 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); + + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + + if (EccPoint_isZero(result, curve)) { + return 0; + } + return 1; +} + +/* Converts an integer in uECC native format to big-endian bytes. */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, + const unsigned int *native) +{ + wordcount_t 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)); + } +} + +/* Converts big-endian bytes to an integer in uECC native format. */ +void uECC_vli_bytesToNative(unsigned int *native, const uint8_t *bytes, + int num_bytes) +{ + wordcount_t 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)); + } +} + +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; +} + + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) +{ + uECC_word_t tmp1[NUM_ECC_WORDS]; + uECC_word_t tmp2[NUM_ECC_WORDS]; + wordcount_t num_words = curve->num_words; + + /* The point at infinity is invalid. */ + if (EccPoint_isZero(point, curve)) { + return -1; + } + + /* 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 -2; + } + + 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 */ + if (uECC_vli_equal(tmp1, tmp2, num_words) != 0) + return -3; + + return 0; +} + +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) +{ + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + curve->num_words, + public_key + curve->num_bytes, + curve->num_bytes); + + if (uECC_vli_cmp_unsafe(_public, curve->G, NUM_ECC_WORDS * 2) == 0) { + return -4; + } + + return uECC_valid_point(_public, curve); +} + +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, + uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + uECC_vli_bytesToNative( + _private, + private_key, + BITS_TO_BYTES(curve->num_n_bits)); + + /* 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; + } + + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + + curve->num_bytes, curve->num_bytes, _public + curve->num_words); + return 1; +} + + + diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dh.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dh.c new file mode 100644 index 0000000..e5257d2 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dh.c @@ -0,0 +1,200 @@ +/* ec_dh.c - TinyCrypt implementation of EC-DH */ + +/* + * 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. + */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include +#include + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +int uECC_make_key_with_d(uint8_t *public_key, uint8_t *private_key, + unsigned int *d, uECC_Curve curve) +{ + + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + + /* This function is designed for test purposes-only (such as validating NIST + * test vectors) as it uses a provided value for d instead of generating + * it uniformly at random. */ + memcpy (_private, d, NUM_ECC_BYTES); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + 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); + + /* erasing temporary buffer used to store secret: */ + memset(_private, 0, NUM_ECC_BYTES); + + return 1; + } + return 0; +} + +int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve) +{ + + uECC_word_t _random[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _private uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2 * NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + /* computing modular reduction of _random (see FIPS 186.4 B.4.1): */ + uECC_vli_mmod(_private, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + /* Computing public-key from private: */ + if (EccPoint_compute_public_key(_public, _private, curve)) { + + /* Converting buffers to correct bit order: */ + 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); + + /* erasing temporary buffer that stored secret: */ + memset(_private, 0, NUM_ECC_BYTES); + + 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[NUM_ECC_WORDS * 2]; + uECC_word_t _private[NUM_ECC_WORDS]; + + uECC_word_t tmp[NUM_ECC_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; + int r; + + /* Converting buffers to correct bit order: */ + 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); + + /* 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)) { + r = 0; + goto clear_and_out; + } + initial_Z = p2[carry]; + } + + EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, + curve); + + uECC_vli_nativeToBytes(secret, num_bytes, _public); + r = !EccPoint_isZero(_public, curve); + +clear_and_out: + /* erasing temporary buffer used to store secret: */ + memset(p2, 0, sizeof(p2)); + __asm__ __volatile__("" :: "g"(p2) : "memory"); + memset(tmp, 0, sizeof(tmp)); + __asm__ __volatile__("" :: "g"(tmp) : "memory"); + memset(_private, 0, sizeof(_private)); + __asm__ __volatile__("" :: "g"(_private) : "memory"); + + return r; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dsa.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dsa.c new file mode 100644 index 0000000..064dfe5 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_dsa.c @@ -0,0 +1,295 @@ +/* ec_dsa.c - TinyCrypt implementation of EC-DSA */ + +/* 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.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_CSPRNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +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); + uECC_vli_bytesToNative(native, bits, bits_size); + 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); + } +} + +int uECC_sign_with_k(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[NUM_ECC_WORDS]; + uECC_word_t s[NUM_ECC_WORDS]; + uECC_word_t *k2[2] = {tmp, s}; + uECC_word_t p[NUM_ECC_WORDS * 2]; + 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); + EccPoint_mult(p, curve->G, k2[!carry], 0, 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 */ + + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ + + /* tmp = d: */ + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); + + 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; + } + + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); + return 1; +} + +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 _random[2*NUM_ECC_WORDS]; + uECC_word_t k[NUM_ECC_WORDS]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + /* Generating _random uniformly at random: */ + uECC_RNG_Function rng_function = uECC_get_rng(); + if (!rng_function || + !rng_function((uint8_t *)_random, 2*NUM_ECC_WORDS*uECC_WORD_SIZE)) { + return 0; + } + + // computing k as modular reduction of _random (see FIPS 186.4 B.5.1): + uECC_vli_mmod(k, _random, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, + curve)) { + return 1; + } + } + 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[NUM_ECC_WORDS], u2[NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + uECC_word_t sum[NUM_ECC_WORDS * 2]; + uECC_word_t rx[NUM_ECC_WORDS]; + uECC_word_t ry[NUM_ECC_WORDS]; + uECC_word_t tx[NUM_ECC_WORDS]; + uECC_word_t ty[NUM_ECC_WORDS]; + uECC_word_t tz[NUM_ECC_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + + uECC_word_t _public[NUM_ECC_WORDS * 2]; + uECC_word_t r[NUM_ECC_WORDS], s[NUM_ECC_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; + + 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); + + /* 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 */ + XYcZ_add(tx, ty, sum, sum + num_words, 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, 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) == 0); +} + diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_platform_specific.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_platform_specific.c new file mode 100644 index 0000000..1867988 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/ecc_platform_specific.c @@ -0,0 +1,105 @@ +/* uECC_platform_specific.c - Implementation of platform specific functions*/ + +/* 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.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + * uECC_platform_specific.c -- Implementation of platform specific functions + */ + + +#if 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 + +#include + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +int default_CSPRNG(uint8_t *dest, unsigned int size) { + + /* input sanity check: */ + if (dest == (uint8_t *) 0 || (size <= 0)) + return 0; + + 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_t) 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; +} + +#endif /* platform */ + diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/hmac.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/hmac.c new file mode 100644 index 0000000..89878ce --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/hmac.c @@ -0,0 +1,148 @@ +/* hmac.c - TinyCrypt implementation of the HMAC algorithm */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +static void rekey(uint8_t *key, const uint8_t *new_key, unsigned int key_size) +{ + const uint8_t inner_pad = (uint8_t) 0x36; + const uint8_t outer_pad = (uint8_t) 0x5c; + unsigned int i; + + for (i = 0; i < key_size; ++i) { + key[i] = inner_pad ^ new_key[i]; + key[i + TC_SHA256_BLOCK_SIZE] = outer_pad ^ new_key[i]; + } + for (; i < TC_SHA256_BLOCK_SIZE; ++i) { + key[i] = inner_pad; key[i + TC_SHA256_BLOCK_SIZE] = outer_pad; + } +} + +int tc_hmac_set_key(TCHmacState_t ctx, const uint8_t *key, + unsigned int key_size) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0 || + key == (const uint8_t *) 0 || + key_size == 0) { + return TC_CRYPTO_FAIL; + } + + const uint8_t dummy_key[key_size]; + struct tc_hmac_state_struct dummy_state; + + if (key_size <= TC_SHA256_BLOCK_SIZE) { + /* + * The next three lines consist of dummy calls just to avoid + * certain timing attacks. Without these dummy calls, + * adversaries would be able to learn whether the key_size is + * greater than TC_SHA256_BLOCK_SIZE by measuring the time + * consumed in this process. + */ + (void)tc_sha256_init(&dummy_state.hash_state); + (void)tc_sha256_update(&dummy_state.hash_state, + dummy_key, + key_size); + (void)tc_sha256_final(&dummy_state.key[TC_SHA256_DIGEST_SIZE], + &dummy_state.hash_state); + + /* Actual code for when key_size <= TC_SHA256_BLOCK_SIZE: */ + rekey(ctx->key, key, key_size); + } else { + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, key, key_size); + (void)tc_sha256_final(&ctx->key[TC_SHA256_DIGEST_SIZE], + &ctx->hash_state); + rekey(ctx->key, + &ctx->key[TC_SHA256_DIGEST_SIZE], + TC_SHA256_DIGEST_SIZE); + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_init(TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_init(&ctx->hash_state); + (void) tc_sha256_update(&ctx->hash_state, ctx->key, TC_SHA256_BLOCK_SIZE); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_update(TCHmacState_t ctx, + const void *data, + unsigned int data_length) +{ + + /* input sanity check: */ + if (ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void)tc_sha256_update(&ctx->hash_state, data, data_length); + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_final(uint8_t *tag, unsigned int taglen, TCHmacState_t ctx) +{ + + /* input sanity check: */ + if (tag == (uint8_t *) 0 || + taglen != TC_SHA256_DIGEST_SIZE || + ctx == (TCHmacState_t) 0) { + return TC_CRYPTO_FAIL; + } + + (void) tc_sha256_final(tag, &ctx->hash_state); + + (void)tc_sha256_init(&ctx->hash_state); + (void)tc_sha256_update(&ctx->hash_state, + &ctx->key[TC_SHA256_BLOCK_SIZE], + TC_SHA256_BLOCK_SIZE); + (void)tc_sha256_update(&ctx->hash_state, tag, TC_SHA256_DIGEST_SIZE); + (void)tc_sha256_final(tag, &ctx->hash_state); + + /* destroy the current state */ + _set(ctx, 0, sizeof(*ctx)); + + return TC_CRYPTO_SUCCESS; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/hmac_prng.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/hmac_prng.c new file mode 100644 index 0000000..68b5b1f --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/hmac_prng.c @@ -0,0 +1,212 @@ +/* hmac_prng.c - TinyCrypt implementation of HMAC-PRNG */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include +#include + +/* + * min bytes in the seed string. + * MIN_SLEN*8 must be at least the expected security level. + */ +static const unsigned int MIN_SLEN = 32; + +/* + * max bytes in the seed string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_SLEN = UINT32_MAX; + +/* + * max bytes in the personalization string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_PLEN = UINT32_MAX; + +/* + * max bytes in the additional_info string; + * SP800-90A specifies a maximum of 2^35 bits (i.e., 2^32 bytes). + */ +static const unsigned int MAX_ALEN = UINT32_MAX; + +/* + * max number of generates between re-seeds; + * TinyCrypt accepts up to (2^32 - 1) which is the maximal value of + * a 32-bit unsigned int variable, while SP800-90A specifies a maximum of 2^48. + */ +static const unsigned int MAX_GENS = UINT32_MAX; + +/* + * maximum bytes per generate call; + * SP800-90A specifies a maximum up to 2^19. + */ +static const unsigned int MAX_OUT = (1 << 19); + +/* + * Assumes: prng != NULL, e != NULL, len >= 0. + */ +static void update(TCHmacPrng_t prng, const uint8_t *e, unsigned int len) +{ + const uint8_t separator0 = 0x00; + const uint8_t separator1 = 0x01; + + /* use current state, e and separator 0 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator0, sizeof(separator0)); + (void)tc_hmac_update(&prng->h, e, len); + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + /* use current state, e and separator 1 to compute a new prng key: */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_update(&prng->h, &separator1, sizeof(separator1)); + (void)tc_hmac_update(&prng->h, e, len); + (void)tc_hmac_final(prng->key, sizeof(prng->key), &prng->h); + /* configure the new prng key into the prng's instance of hmac */ + (void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + + /* use the new key to compute a new state variable v */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); +} + +int tc_hmac_prng_init(TCHmacPrng_t prng, + const uint8_t *personalization, + unsigned int plen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + personalization == (uint8_t *) 0 || + plen > MAX_PLEN) { + return TC_CRYPTO_FAIL; + } + + /* put the generator into a known state: */ + _set(prng->key, 0x00, sizeof(prng->key)); + _set(prng->v, 0x01, sizeof(prng->v)); + tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key)); + /* update assumes SOME key has been configured into HMAC */ + + update(prng, personalization, plen); + + /* force a reseed before allowing tc_hmac_prng_generate to succeed: */ + prng->countdown = 0; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_reseed(TCHmacPrng_t prng, + const uint8_t *seed, + unsigned int seedlen, + const uint8_t *additional_input, + unsigned int additionallen) +{ + + /* input sanity check: */ + if (prng == (TCHmacPrng_t) 0 || + seed == (const uint8_t *) 0 || + seedlen < MIN_SLEN || + seedlen > MAX_SLEN) { + return TC_CRYPTO_FAIL; + } + + if (additional_input != (const uint8_t *) 0) { + /* + * Abort if additional_input is provided but has inappropriate + * length + */ + if (additionallen == 0 || + additionallen > MAX_ALEN) { + return TC_CRYPTO_FAIL; + } else { + /* call update for the seed and additional_input */ + update(prng, seed, seedlen); + update(prng, additional_input, additionallen); + } + } else { + /* call update only for the seed */ + update(prng, seed, seedlen); + } + + /* ... and enable hmac_prng_generate */ + prng->countdown = MAX_GENS; + + return TC_CRYPTO_SUCCESS; +} + +int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng) +{ + unsigned int bufferlen; + + /* input sanity check: */ + if (out == (uint8_t *) 0 || + prng == (TCHmacPrng_t) 0 || + outlen == 0 || + outlen > MAX_OUT) { + return TC_CRYPTO_FAIL; + } else if (prng->countdown == 0) { + return TC_HMAC_PRNG_RESEED_REQ; + } + + prng->countdown--; + + while (outlen != 0) { + /* operate HMAC in OFB mode to create "random" outputs */ + (void)tc_hmac_init(&prng->h); + (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); + (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); + + bufferlen = (TC_SHA256_DIGEST_SIZE > outlen) ? + outlen : TC_SHA256_DIGEST_SIZE; + (void)_copy(out, bufferlen, prng->v, bufferlen); + + out += bufferlen; + outlen = (outlen > TC_SHA256_DIGEST_SIZE) ? + (outlen - TC_SHA256_DIGEST_SIZE) : 0; + } + + /* block future PRNG compromises from revealing past state */ + update(prng, prng->v, TC_SHA256_DIGEST_SIZE); + + return TC_CRYPTO_SUCCESS; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/sha256.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/sha256.c new file mode 100644 index 0000000..b4efd20 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/sha256.c @@ -0,0 +1,217 @@ +/* sha256.c - TinyCrypt SHA-256 crypto hash algorithm implementation */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include +#include + +static void compress(unsigned int *iv, const uint8_t *data); + +int tc_sha256_init(TCSha256State_t s) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + /* + * Setting the initial state values. + * These values correspond to the first 32 bits of the fractional parts + * of the square roots of the first 8 primes: 2, 3, 5, 7, 11, 13, 17 + * and 19. + */ + _set((uint8_t *) s, 0x00, sizeof(*s)); + s->iv[0] = 0x6a09e667; + s->iv[1] = 0xbb67ae85; + s->iv[2] = 0x3c6ef372; + s->iv[3] = 0xa54ff53a; + s->iv[4] = 0x510e527f; + s->iv[5] = 0x9b05688c; + s->iv[6] = 0x1f83d9ab; + s->iv[7] = 0x5be0cd19; + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_update(TCSha256State_t s, const uint8_t *data, size_t datalen) +{ + /* input sanity check: */ + if (s == (TCSha256State_t) 0 || + data == (void *) 0) { + return TC_CRYPTO_FAIL; + } else if (datalen == 0) { + return TC_CRYPTO_SUCCESS; + } + + while (datalen-- > 0) { + s->leftover[s->leftover_offset++] = *(data++); + if (s->leftover_offset >= TC_SHA256_BLOCK_SIZE) { + compress(s->iv, s->leftover); + s->leftover_offset = 0; + s->bits_hashed += (TC_SHA256_BLOCK_SIZE << 3); + } + } + + return TC_CRYPTO_SUCCESS; +} + +int tc_sha256_final(uint8_t *digest, TCSha256State_t s) +{ + unsigned int i; + + /* input sanity check: */ + if (digest == (uint8_t *) 0 || + s == (TCSha256State_t) 0) { + return TC_CRYPTO_FAIL; + } + + s->bits_hashed += (s->leftover_offset << 3); + + s->leftover[s->leftover_offset++] = 0x80; /* always room for one byte */ + if (s->leftover_offset > (sizeof(s->leftover) - 8)) { + /* there is not room for all the padding in this block */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - s->leftover_offset); + compress(s->iv, s->leftover); + s->leftover_offset = 0; + } + + /* add the padding and the length in big-Endian format */ + _set(s->leftover + s->leftover_offset, 0x00, + sizeof(s->leftover) - 8 - s->leftover_offset); + s->leftover[sizeof(s->leftover) - 1] = (uint8_t)(s->bits_hashed); + s->leftover[sizeof(s->leftover) - 2] = (uint8_t)(s->bits_hashed >> 8); + s->leftover[sizeof(s->leftover) - 3] = (uint8_t)(s->bits_hashed >> 16); + s->leftover[sizeof(s->leftover) - 4] = (uint8_t)(s->bits_hashed >> 24); + s->leftover[sizeof(s->leftover) - 5] = (uint8_t)(s->bits_hashed >> 32); + s->leftover[sizeof(s->leftover) - 6] = (uint8_t)(s->bits_hashed >> 40); + s->leftover[sizeof(s->leftover) - 7] = (uint8_t)(s->bits_hashed >> 48); + s->leftover[sizeof(s->leftover) - 8] = (uint8_t)(s->bits_hashed >> 56); + + /* hash the padding and length */ + compress(s->iv, s->leftover); + + /* copy the iv out to digest */ + for (i = 0; i < TC_SHA256_STATE_BLOCKS; ++i) { + unsigned int t = *((unsigned int *) &s->iv[i]); + *digest++ = (uint8_t)(t >> 24); + *digest++ = (uint8_t)(t >> 16); + *digest++ = (uint8_t)(t >> 8); + *digest++ = (uint8_t)(t); + } + + /* destroy the current state */ + _set(s, 0, sizeof(*s)); + + return TC_CRYPTO_SUCCESS; +} + +/* + * Initializing SHA-256 Hash constant words K. + * These values correspond to the first 32 bits of the fractional parts of the + * cube roots of the first 64 primes between 2 and 311. + */ +static const unsigned int k256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static inline unsigned int ROTR(unsigned int a, unsigned int n) +{ + return (((a) >> n) | ((a) << (32 - n))); +} + +#define Sigma0(a)(ROTR((a), 2) ^ ROTR((a), 13) ^ ROTR((a), 22)) +#define Sigma1(a)(ROTR((a), 6) ^ ROTR((a), 11) ^ ROTR((a), 25)) +#define sigma0(a)(ROTR((a), 7) ^ ROTR((a), 18) ^ ((a) >> 3)) +#define sigma1(a)(ROTR((a), 17) ^ ROTR((a), 19) ^ ((a) >> 10)) + +#define Ch(a, b, c)(((a) & (b)) ^ ((~(a)) & (c))) +#define Maj(a, b, c)(((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) + +static inline unsigned int BigEndian(const uint8_t **c) +{ + unsigned int n = 0; + + n = (((unsigned int)(*((*c)++))) << 24); + n |= ((unsigned int)(*((*c)++)) << 16); + n |= ((unsigned int)(*((*c)++)) << 8); + n |= ((unsigned int)(*((*c)++))); + return n; +} + +static void compress(unsigned int *iv, const uint8_t *data) +{ + unsigned int a, b, c, d, e, f, g, h; + unsigned int s0, s1; + unsigned int t1, t2; + unsigned int work_space[16]; + unsigned int n; + unsigned int i; + + a = iv[0]; b = iv[1]; c = iv[2]; d = iv[3]; + e = iv[4]; f = iv[5]; g = iv[6]; h = iv[7]; + + for (i = 0; i < 16; ++i) { + n = BigEndian(&data); + t1 = work_space[i] = n; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + for ( ; i < 64; ++i) { + s0 = work_space[(i+1)&0x0f]; + s0 = sigma0(s0); + s1 = work_space[(i+14)&0x0f]; + s1 = sigma1(s1); + + t1 = work_space[i&0xf] += s0 + s1 + work_space[(i+9)&0xf]; + t1 += h + Sigma1(e) + Ch(e, f, g) + k256[i]; + t2 = Sigma0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + iv[0] += a; iv[1] += b; iv[2] += c; iv[3] += d; + iv[4] += e; iv[5] += f; iv[6] += g; iv[7] += h; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/lib/source/utils.c b/bootloader/mcuboot/ext/tinycrypt/lib/source/utils.c new file mode 100644 index 0000000..13cc495 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/lib/source/utils.c @@ -0,0 +1,74 @@ +/* utils.c - TinyCrypt platform-dependent run-time operations */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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 +#include + +#include + +#define MASK_TWENTY_SEVEN 0x1b + +unsigned int _copy(uint8_t *to, unsigned int to_len, + const uint8_t *from, unsigned int from_len) +{ + if (from_len <= to_len) { + (void)memcpy(to, from, from_len); + return from_len; + } else { + return TC_CRYPTO_FAIL; + } +} + +void _set(void *to, uint8_t val, unsigned int len) +{ + (void)memset(to, val, len); +} + +/* + * Doubles the value of a byte for values up to 127. + */ +uint8_t _double_byte(uint8_t a) +{ + return ((a<<1) ^ ((a>>7) * MASK_TWENTY_SEVEN)); +} + +int _compare(const uint8_t *a, const uint8_t *b, size_t size) +{ + const uint8_t *tempa = a; + const uint8_t *tempb = b; + uint8_t result = 0; + + for (unsigned int i = 0; i < size; i++) { + result |= tempa[i] ^ tempb[i]; + } + return result; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/Makefile b/bootloader/mcuboot/ext/tinycrypt/tests/Makefile new file mode 100644 index 0000000..eff5a88 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/Makefile @@ -0,0 +1,67 @@ +################################################################################ +# +# Copyright (C) 2017 by Intel Corporation, All Rights Reserved. +# +# Tests Makefile. +# +################################################################################ + +include ../config.mk + +TEST_LIB_FILE:=test_ecc_utils.c +TEST_SOURCE:=$(filter-out $(TEST_LIB_FILE), $(wildcard test_*.c)) + +TEST_OBJECTS:=$(TEST_SOURCE:.c=.o) +TEST_DEPS:=$(TEST_SOURCE:.c=.d) +TEST_BINARY:=$(TEST_SOURCE:.c=$(DOTEXE)) + +# Edit the 'all' content to add/remove tests needed from TinyCrypt library: +all: $(TEST_BINARY) + +clean: + -$(RM) $(TEST_BINARY) $(TEST_OBJECTS) $(TEST_DEPS) + -$(RM) *~ *.o *.d + +# Dependencies +test_aes$(DOTEXE): test_aes.o aes_encrypt.o aes_decrypt.o utils.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_cbc_mode$(DOTEXE): test_cbc_mode.o cbc_mode.o \ + aes_encrypt.o aes_decrypt.o utils.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_ctr_mode$(DOTEXE): test_ctr_mode.o ctr_mode.o \ + aes_encrypt.o utils.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_ctr_prng$(DOTEXE): test_ctr_prng.o ctr_prng.o \ + aes_encrypt.o utils.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_cmac_mode$(DOTEXE): test_cmac_mode.o aes_encrypt.o utils.o \ + cmac_mode.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_ccm_mode$(DOTEXE): test_ccm_mode.o aes_encrypt.o \ + utils.o ccm_mode.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_hmac$(DOTEXE): test_hmac.o hmac.o sha256.o utils.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_hmac_prng$(DOTEXE): test_hmac_prng.o hmac_prng.o hmac.o \ + sha256.o utils.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_sha256$(DOTEXE): test_sha256.o sha256.o utils.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_ecc_dh$(DOTEXE): test_ecc_dh.o ecc.o ecc_dh.o test_ecc_utils.o ecc_platform_specific.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +test_ecc_dsa$(DOTEXE): test_ecc_dsa.o ecc.o utils.o ecc_dh.o \ + ecc_dsa.o sha256.o test_ecc_utils.o ecc_platform_specific.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + + +-include $(TEST_DEPS) diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/include/test_ecc_utils.h b/bootloader/mcuboot/ext/tinycrypt/tests/include/test_ecc_utils.h new file mode 100644 index 0000000..4e29054 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/include/test_ecc_utils.h @@ -0,0 +1,100 @@ +/* test_ecc_utils.h - TinyCrypt interface to common functions for ECC tests */ + +/* 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.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + * test_ecc_utils.h -- Interface to common functions for ECC tests. + */ + +#ifndef __TEST_ECC_UTILS_H__ +#define __TEST_ECC_UTILS_H__ + +#include +#include +#include + +int hex2int (char hex); + + +/* + * Convert hex string to byte string + * Return number of bytes written to buf, or 0 on error + */ +int hex2bin(uint8_t *buf, const size_t buflen, const char *hex, + const size_t hexlen); + +/* + * Convert hex string to zero-padded nanoECC scalar + */ +void string2scalar(unsigned int * scalar, unsigned int num_word32, char *str); + + +void print_ecc_scalar(const char *label, const unsigned int * p_vli, + unsigned int num_word32); + +int check_ecc_result(const int num, const char *name, + const unsigned int *expected, + const unsigned int *computed, + const unsigned int num_word32, const bool verbose); + +/* Test ecc_make_keys, and also as keygen part of other tests */ +int keygen_vectors(char **d_vec, char **qx_vec, char **qy_vec, int tests, bool verbose); + +void vli_print_bytes(uint8_t *vli, unsigned int size); + + +int check_code(const int num, const char *name, const int expected, + const int computed, const int verbose); + + +#endif /* __TEST_ECC_UTILS_H__ */ + diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/include/test_utils.h b/bootloader/mcuboot/ext/tinycrypt/tests/include/test_utils.h new file mode 100644 index 0000000..055f91b --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/include/test_utils.h @@ -0,0 +1,125 @@ +/* test_utils.h - TinyCrypt interface to common functions for tests */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + * test_utils.h -- Interface to common functions for tests. + */ + +#ifndef __TEST_UTILS_H__ +#define __TEST_UTILS_H__ + +#include + +#include +#include +#include +#include + +#define PRINT_DATA(fmt, ...) printf(fmt, ##__VA_ARGS__) + +#define PRINT_LINE \ + PRINT_DATA( \ + "============================================================" \ + "=======\n") + + +#define FAIL "FAIL" +#define PASS "PASS" +#define FMT_ERROR "%s - %s@%d. " + +/* TC_ here stands for 'Test Case' not 'TinyCrypt' */ +#define TC_PASS 0 +#define TC_FAIL 1 + +#define TC_ERROR(fmt, ...) \ + do { \ + PRINT_DATA(FMT_ERROR, FAIL, __func__, __LINE__); \ + PRINT_DATA(fmt, ##__VA_ARGS__); \ + } while (0) + +#define TC_PRINT(fmt, ...) PRINT_DATA(fmt, ##__VA_ARGS__) +#define TC_START(name) PRINT_DATA("tc_start() - %s\n", name) +#define TC_END(result, fmt, ...) PRINT_DATA(fmt, ##__VA_ARGS__) + +/* prints result and the function name */ +#define TC_END_RESULT(result) \ + do { \ + PRINT_LINE; \ + TC_END(result, "%s - %s.\n", \ + result == TC_PASS ? PASS : FAIL, __func__); \ + } while (0) + +#define TC_END_REPORT(result) \ + do { \ + PRINT_LINE; \ + TC_END(result, \ + "PROJECT EXECUTION %s\n", \ + result == TC_PASS ? "SUCCESSFUL" : "FAILED"); \ + } while (0) + +static inline void show_str(const char *label, const uint8_t *s, size_t len) +{ + unsigned int i; + + TC_PRINT("%s = ", label); + for (i = 0; i < (unsigned int) len; ++i) { + TC_PRINT("%02x", s[i]); + } + TC_PRINT("\n"); +} + +static inline void fatal(unsigned int testnum, const void *expected, size_t expectedlen, + const void *computed, size_t computedlen) +{ + + TC_ERROR("\tTest #%d Failed!\n", testnum); + show_str("\t\tExpected", expected, expectedlen); + show_str("\t\tComputed ", computed, computedlen); + TC_PRINT("\n"); +} + +static inline unsigned int check_result(unsigned int testnum, const void *expected, size_t expectedlen, + const void *computed, size_t computedlen) +{ + unsigned int result = TC_PASS; + + if (expectedlen != computedlen) { + TC_ERROR("The length of the computed buffer (%zu)", computedlen); + TC_ERROR("does not match the expected length (%zu).", expectedlen); + result = TC_FAIL; + } else if (memcmp(computed, expected, computedlen) != 0) { + fatal(testnum, expected, expectedlen, computed, computedlen); + result = TC_FAIL; + } + + return result; +} + +#endif /* __TEST_UTILS_H__ */ diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/pseudo-random-data.bin b/bootloader/mcuboot/ext/tinycrypt/tests/pseudo-random-data.bin new file mode 100644 index 0000000000000000000000000000000000000000..4f174c58bd3a505ddc1b9aea1937d70e1b8e3833 GIT binary patch literal 524288 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

rJU`^D6d+G+0i0*qW(eD6B_yjwu>SccpL^ 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

  • CR7bpa}{W?5;QW-wW%k?(FX+Y-3; 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 literal 0 HcmV?d00001 diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_aes.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_aes.c new file mode 100644 index 0000000..ea3e2da --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_aes.c @@ -0,0 +1,2078 @@ +/* test_aes.c - TinyCrypt AES-128 tests (including NIST tests) */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ + +/* + * DESCRIPTION + * This module tests the following AES routines: + * + * Scenarios tested include: + * - AES128 NIST key schedule test + * - AES128 NIST encryption test + * - AES128 NIST fixed-key and variable-text + * - AES128 NIST variable-key and fixed-text + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define NUM_OF_NIST_KEYS 16 +#define NUM_OF_FIXED_KEYS 128 + + +struct kat_table { + uint8_t in[NUM_OF_NIST_KEYS]; + uint8_t out[NUM_OF_NIST_KEYS]; +}; + +/* + * NIST test key schedule. + */ +int test_1(void) +{ + int result = TC_PASS; + const uint8_t nist_key[NUM_OF_NIST_KEYS] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + const struct tc_aes_key_sched_struct expected = { + { + 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c, + 0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605, + 0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f, + 0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b, + 0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00, + 0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc, + 0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd, + 0x4e54f70e, 0x5f5fc9f3, 0x84a64fb2, 0x4ea6dc4f, + 0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f, + 0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e, + 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6 + } + }; + struct tc_aes_key_sched_struct s; + + TC_PRINT("AES128 %s (NIST key schedule test):\n", __func__); + + if (tc_aes128_set_encrypt_key(&s, nist_key) == 0) { + TC_ERROR("AES128 test %s (NIST key schedule test) failed.\n", + __func__); + result = TC_FAIL; + goto exitTest1; + } + + result = check_result(1, expected.words, sizeof(expected.words), s.words, + sizeof(s.words)); + +exitTest1: + TC_END_RESULT(result); + return result; +} + +/* + * NIST test vectors for encryption. + */ +int test_2(void) +{ + int result = TC_PASS; + const uint8_t nist_key[NUM_OF_NIST_KEYS] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + const uint8_t nist_input[NUM_OF_NIST_KEYS] = { + 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, + 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 + }; + const uint8_t expected[NUM_OF_NIST_KEYS] = { + 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, + 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32 + }; + struct tc_aes_key_sched_struct s; + uint8_t ciphertext[NUM_OF_NIST_KEYS]; + + TC_PRINT("AES128 %s (NIST encryption test):\n", __func__); + + (void)tc_aes128_set_encrypt_key(&s, nist_key); + if (tc_aes_encrypt(ciphertext, nist_input, &s) == 0) { + TC_ERROR("AES128 %s (NIST encryption test) failed.\n", + __func__); + result = TC_FAIL; + goto exitTest2; + } + + result = check_result(2, expected, sizeof(expected), ciphertext, + sizeof(ciphertext)); + +exitTest2: + TC_END_RESULT(result); + + return result; +} + +int var_text_test(unsigned int r, const uint8_t *in, const uint8_t *out, + TCAesKeySched_t s) +{ + uint8_t ciphertext[NUM_OF_NIST_KEYS]; + uint8_t decrypted[NUM_OF_NIST_KEYS]; + int result = TC_PASS; + + (void)tc_aes_encrypt(ciphertext, in, s); + result = check_result(r, out, NUM_OF_NIST_KEYS, ciphertext, + sizeof(ciphertext)); + if (result != TC_FAIL) { + if (tc_aes_decrypt(decrypted, ciphertext, s) == 0) { + TC_ERROR("aes_decrypt failed\n"); + result = TC_FAIL; + } else { + result = check_result(r, in, NUM_OF_NIST_KEYS, + decrypted, sizeof(decrypted)); + } + } + + return result; +} + +/* + * All NIST tests with fixed key and variable text. + */ +int test_3(void) +{ + int result = TC_PASS; + const uint8_t key[NUM_OF_NIST_KEYS] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const struct kat_table kat_tbl[NUM_OF_FIXED_KEYS] = { + {{ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x3a, 0xd7, 0x8e, 0x72, 0x6c, 0x1e, 0xc0, 0x2b, + 0x7e, 0xbf, 0xe9, 0x2b, 0x23, 0xd9, 0xec, 0x34 + } }, + {{ + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xaa, 0xe5, 0x93, 0x9c, 0x8e, 0xfd, 0xf2, 0xf0, + 0x4e, 0x60, 0xb9, 0xfe, 0x71, 0x17, 0xb2, 0xc2 + } }, + {{ + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf0, 0x31, 0xd4, 0xd7, 0x4f, 0x5d, 0xcb, 0xf3, + 0x9d, 0xaa, 0xf8, 0xca, 0x3a, 0xf6, 0xe5, 0x27 + } }, + {{ + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x96, 0xd9, 0xfd, 0x5c, 0xc4, 0xf0, 0x74, 0x41, + 0x72, 0x7d, 0xf0, 0xf3, 0x3e, 0x40, 0x1a, 0x36 + } }, + {{ + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x30, 0xcc, 0xdb, 0x04, 0x46, 0x46, 0xd7, 0xe1, + 0xf3, 0xcc, 0xea, 0x3d, 0xca, 0x08, 0xb8, 0xc0 + } }, + {{ + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x16, 0xae, 0x4c, 0xe5, 0x04, 0x2a, 0x67, 0xee, + 0x8e, 0x17, 0x7b, 0x7c, 0x58, 0x7e, 0xcc, 0x82 + } }, + {{ + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb6, 0xda, 0x0b, 0xb1, 0x1a, 0x23, 0x85, 0x5d, + 0x9c, 0x5c, 0xb1, 0xb4, 0xc6, 0x41, 0x2e, 0x0a + } }, + {{ + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xdb, 0x4f, 0x1a, 0xa5, 0x30, 0x96, 0x7d, 0x67, + 0x32, 0xce, 0x47, 0x15, 0xeb, 0x0e, 0xe2, 0x4b + } }, + {{ + 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xa8, 0x17, 0x38, 0x25, 0x26, 0x21, 0xdd, 0x18, + 0x0a, 0x34, 0xf3, 0x45, 0x5b, 0x4b, 0xaa, 0x2f + } }, + {{ + 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x77, 0xe2, 0xb5, 0x08, 0xdb, 0x7f, 0xd8, 0x92, + 0x34, 0xca, 0xf7, 0x93, 0x9e, 0xe5, 0x62, 0x1a + } }, + {{ + 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb8, 0x49, 0x9c, 0x25, 0x1f, 0x84, 0x42, 0xee, + 0x13, 0xf0, 0x93, 0x3b, 0x68, 0x8f, 0xcd, 0x19 + } }, + {{ + 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x96, 0x51, 0x35, 0xf8, 0xa8, 0x1f, 0x25, 0xc9, + 0xd6, 0x30, 0xb1, 0x75, 0x02, 0xf6, 0x8e, 0x53 + } }, + {{ + 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x8b, 0x87, 0x14, 0x5a, 0x01, 0xad, 0x1c, 0x6c, + 0xed, 0xe9, 0x95, 0xea, 0x36, 0x70, 0x45, 0x4f + } }, + {{ + 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x8e, 0xae, 0x3b, 0x10, 0xa0, 0xc8, 0xca, 0x6d, + 0x1d, 0x3b, 0x0f, 0xa6, 0x1e, 0x56, 0xb0, 0xb2 + } }, + {{ + 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x64, 0xb4, 0xd6, 0x29, 0x81, 0x0f, 0xda, 0x6b, + 0xaf, 0xdf, 0x08, 0xf3, 0xb0, 0xd8, 0xd2, 0xc5 + } }, + {{ + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xd7, 0xe5, 0xdb, 0xd3, 0x32, 0x45, 0x95, 0xf8, + 0xfd, 0xc7, 0xd7, 0xc5, 0x71, 0xda, 0x6c, 0x2a + } }, + {{ + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf3, 0xf7, 0x23, 0x75, 0x26, 0x4e, 0x16, 0x7f, + 0xca, 0x9d, 0xe2, 0xc1, 0x52, 0x7d, 0x96, 0x06 + } }, + {{ + 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x8e, 0xe7, 0x9d, 0xd4, 0xf4, 0x01, 0xff, 0x9b, + 0x7e, 0xa9, 0x45, 0xd8, 0x66, 0x66, 0xc1, 0x3b + } }, + {{ + 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xdd, 0x35, 0xce, 0xa2, 0x79, 0x99, 0x40, 0xb4, + 0x0d, 0xb3, 0xf8, 0x19, 0xcb, 0x94, 0xc0, 0x8b + } }, + {{ + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x69, 0x41, 0xcb, 0x6b, 0x3e, 0x08, 0xc2, 0xb7, + 0xaf, 0xa5, 0x81, 0xeb, 0xdd, 0x60, 0x7b, 0x87 + } }, + {{ + 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x2c, 0x20, 0xf4, 0x39, 0xf6, 0xbb, 0x09, 0x7b, + 0x29, 0xb8, 0xbd, 0x6d, 0x99, 0xaa, 0xd7, 0x99 + } }, + {{ + 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x62, 0x5d, 0x01, 0xf0, 0x58, 0xe5, 0x65, 0xf7, + 0x7a, 0xe8, 0x63, 0x78, 0xbd, 0x2c, 0x49, 0xb3 + } }, + {{ + 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc0, 0xb5, 0xfd, 0x98, 0x19, 0x0e, 0xf4, 0x5f, + 0xbb, 0x43, 0x01, 0x43, 0x8d, 0x09, 0x59, 0x50 + } }, + {{ + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x13, 0x00, 0x1f, 0xf5, 0xd9, 0x98, 0x06, 0xef, + 0xd2, 0x5d, 0xa3, 0x4f, 0x56, 0xbe, 0x85, 0x4b + } }, + {{ + 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x3b, 0x59, 0x4c, 0x60, 0xf5, 0xc8, 0x27, 0x7a, + 0x51, 0x13, 0x67, 0x7f, 0x94, 0x20, 0x8d, 0x82 + } }, + {{ + 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xe9, 0xc0, 0xfc, 0x18, 0x18, 0xe4, 0xaa, 0x46, + 0xbd, 0x2e, 0x39, 0xd6, 0x38, 0xf8, 0x9e, 0x05 + } }, + {{ + 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf8, 0x02, 0x3e, 0xe9, 0xc3, 0xfd, 0xc4, 0x5a, + 0x01, 0x9b, 0x4e, 0x98, 0x5c, 0x7e, 0x1a, 0x54 + } }, + {{ + 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x35, 0xf4, 0x01, 0x82, 0xab, 0x46, 0x62, 0xf3, + 0x02, 0x3b, 0xae, 0xc1, 0xee, 0x79, 0x6b, 0x57 + } }, + {{ + 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x3a, 0xeb, 0xba, 0xd7, 0x30, 0x36, 0x49, 0xb4, + 0x19, 0x4a, 0x69, 0x45, 0xc6, 0xcc, 0x36, 0x94 + } }, + {{ + 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xa2, 0x12, 0x4b, 0xea, 0x53, 0xec, 0x28, 0x34, + 0x27, 0x9b, 0xed, 0x7f, 0x7e, 0xb0, 0xf9, 0x38 + } }, + {{ + 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb9, 0xfb, 0x43, 0x99, 0xfa, 0x4f, 0xac, 0xc7, + 0x30, 0x9e, 0x14, 0xec, 0x98, 0x36, 0x0b, 0x0a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc2, 0x62, 0x77, 0x43, 0x74, 0x20, 0xc5, 0xd6, + 0x34, 0xf7, 0x15, 0xae, 0xa8, 0x1a, 0x91, 0x32 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x17, 0x1a, 0x0e, 0x1b, 0x2d, 0xd4, 0x24, 0xf0, + 0xe0, 0x89, 0xaf, 0x2c, 0x4c, 0x10, 0xf3, 0x2f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x7c, 0xad, 0xbe, 0x40, 0x2d, 0x1b, 0x20, 0x8f, + 0xe7, 0x35, 0xed, 0xce, 0x00, 0xae, 0xe7, 0xce + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x43, 0xb0, 0x2f, 0xf9, 0x29, 0xa1, 0x48, 0x5a, + 0xf6, 0xf5, 0xc6, 0xd6, 0x55, 0x8b, 0xaa, 0x0f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x09, 0x2f, 0xaa, 0xcc, 0x9b, 0xf4, 0x35, 0x08, + 0xbf, 0x8f, 0xa8, 0x61, 0x3c, 0xa7, 0x5d, 0xea + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xcb, 0x2b, 0xf8, 0x28, 0x0f, 0x3f, 0x97, 0x42, + 0xc7, 0xed, 0x51, 0x3f, 0xe8, 0x02, 0x62, 0x9c + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x21, 0x5a, 0x41, 0xee, 0x44, 0x2f, 0xa9, 0x92, + 0xa6, 0xe3, 0x23, 0x98, 0x6d, 0xed, 0x3f, 0x68 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf2, 0x1e, 0x99, 0xcf, 0x4f, 0x0f, 0x77, 0xce, + 0xa8, 0x36, 0xe1, 0x1a, 0x2f, 0xe7, 0x5f, 0xb1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x95, 0xe3, 0xa0, 0xca, 0x90, 0x79, 0xe6, 0x46, + 0x33, 0x1d, 0xf8, 0xb4, 0xe7, 0x0d, 0x2c, 0xd6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x4a, 0xfe, 0x7f, 0x12, 0x0c, 0xe7, 0x61, 0x3f, + 0x74, 0xfc, 0x12, 0xa0, 0x1a, 0x82, 0x80, 0x73 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x82, 0x7f, 0x00, 0x0e, 0x75, 0xe2, 0xc8, 0xb9, + 0xd4, 0x79, 0xbe, 0xed, 0x91, 0x3f, 0xe6, 0x78 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x35, 0x83, 0x0c, 0x8e, 0x7a, 0xae, 0xfe, 0x2d, + 0x30, 0x31, 0x0e, 0xf3, 0x81, 0xcb, 0xf6, 0x91 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x19, 0x1a, 0xa0, 0xf2, 0xc8, 0x57, 0x01, 0x44, + 0xf3, 0x86, 0x57, 0xea, 0x40, 0x85, 0xeb, 0xe5 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x85, 0x06, 0x2c, 0x2c, 0x90, 0x9f, 0x15, 0xd9, + 0x26, 0x9b, 0x6c, 0x18, 0xce, 0x99, 0xc4, 0xf0 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x67, 0x80, 0x34, 0xdc, 0x9e, 0x41, 0xb5, 0xa5, + 0x60, 0xed, 0x23, 0x9e, 0xea, 0xb1, 0xbc, 0x78 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc2, 0xf9, 0x3a, 0x4c, 0xe5, 0xab, 0x6d, 0x5d, + 0x56, 0xf1, 0xb9, 0x3c, 0xf1, 0x99, 0x11, 0xc1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x1c, 0x31, 0x12, 0xbc, 0xb0, 0xc1, 0xdc, 0xc7, + 0x49, 0xd7, 0x99, 0x74, 0x36, 0x91, 0xbf, 0x82 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x00, 0xc5, 0x5b, 0xd7, 0x5c, 0x7f, 0x9c, 0x88, + 0x19, 0x89, 0xd3, 0xec, 0x19, 0x11, 0xc0, 0xd4 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xea, 0x2e, 0x6b, 0x5e, 0xf1, 0x82, 0xb7, 0xdf, + 0xf3, 0x62, 0x9a, 0xbd, 0x6a, 0x12, 0x04, 0x5f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x22, 0x32, 0x23, 0x27, 0xe0, 0x17, 0x80, 0xb1, + 0x73, 0x97, 0xf2, 0x40, 0x87, 0xf8, 0xcc, 0x6f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc9, 0xca, 0xcb, 0x5c, 0xd1, 0x16, 0x92, 0xc3, + 0x73, 0xb2, 0x41, 0x17, 0x68, 0x14, 0x9e, 0xe7 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xa1, 0x8e, 0x3d, 0xbb, 0xca, 0x57, 0x78, 0x60, + 0xda, 0xb6, 0xb8, 0x0d, 0xa3, 0x13, 0x92, 0x56 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x79, 0xb6, 0x1c, 0x37, 0xbf, 0x32, 0x8e, 0xcc, + 0xa8, 0xd7, 0x43, 0x26, 0x5a, 0x3d, 0x42, 0x5c + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xd2, 0xd9, 0x9c, 0x6b, 0xcc, 0x1f, 0x06, 0xfd, + 0xa8, 0xe2, 0x7e, 0x8a, 0xe3, 0xf1, 0xcc, 0xc7 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x1b, 0xfd, 0x4b, 0x91, 0xc7, 0x01, 0xfd, 0x6b, + 0x61, 0xb7, 0xf9, 0x97, 0x82, 0x9d, 0x66, 0x3b + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x11, 0x00, 0x5d, 0x52, 0xf2, 0x5f, 0x16, 0xbd, + 0xc9, 0x54, 0x5a, 0x87, 0x6a, 0x63, 0x49, 0x0a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x3a, 0x4d, 0x35, 0x4f, 0x02, 0xbb, 0x5a, 0x5e, + 0x47, 0xd3, 0x96, 0x66, 0x86, 0x7f, 0x24, 0x6a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xd4, 0x51, 0xb8, 0xd6, 0xe1, 0xe1, 0xa0, 0xeb, + 0xb1, 0x55, 0xfb, 0xbf, 0x6e, 0x7b, 0x7d, 0xc3 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x68, 0x98, 0xd4, 0xf4, 0x2f, 0xa7, 0xba, 0x6a, + 0x10, 0xac, 0x05, 0xe8, 0x7b, 0x9f, 0x20, 0x80 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb6, 0x11, 0x29, 0x5e, 0x73, 0x9c, 0xa7, 0xd9, + 0xb5, 0x0f, 0x8e, 0x4c, 0x0e, 0x75, 0x4a, 0x3f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x7d, 0x33, 0xfc, 0x7d, 0x8a, 0xbe, 0x3c, 0xa1, + 0x93, 0x67, 0x59, 0xf8, 0xf5, 0xde, 0xaf, 0x20 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x3b, 0x5e, 0x0f, 0x56, 0x6d, 0xc9, 0x6c, 0x29, + 0x8f, 0x0c, 0x12, 0x63, 0x75, 0x39, 0xb2, 0x5c + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf8, 0x07, 0xc3, 0xe7, 0x98, 0x5f, 0xe0, 0xf5, + 0xa5, 0x0e, 0x2c, 0xdb, 0x25, 0xc5, 0x10, 0x9e + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x41, 0xf9, 0x92, 0xa8, 0x56, 0xfb, 0x27, 0x8b, + 0x38, 0x9a, 0x62, 0xf5, 0xd2, 0x74, 0xd7, 0xe9 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x10, 0xd3, 0xed, 0x7a, 0x6f, 0xe1, 0x5a, 0xb4, + 0xd9, 0x1a, 0xcb, 0xc7, 0xd0, 0x76, 0x7a, 0xb1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x21, 0xfe, 0xec, 0xd4, 0x5b, 0x2e, 0x67, 0x59, + 0x73, 0xac, 0x33, 0xbf, 0x0c, 0x54, 0x24, 0xfc + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x14, 0x80, 0xcb, 0x39, 0x55, 0xba, 0x62, 0xd0, + 0x9e, 0xea, 0x66, 0x8f, 0x7c, 0x70, 0x88, 0x17 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x66, 0x40, 0x40, 0x33, 0xd6, 0xb7, 0x2b, 0x60, + 0x93, 0x54, 0xd5, 0x49, 0x6e, 0x7e, 0xb5, 0x11 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x1c, 0x31, 0x7a, 0x22, 0x0a, 0x7d, 0x70, 0x0d, + 0xa2, 0xb1, 0xe0, 0x75, 0xb0, 0x02, 0x66, 0xe1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xab, 0x3b, 0x89, 0x54, 0x22, 0x33, 0xf1, 0x27, + 0x1b, 0xf8, 0xfd, 0x0c, 0x0f, 0x40, 0x35, 0x45 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xd9, 0x3e, 0xae, 0x96, 0x6f, 0xac, 0x46, 0xdc, + 0xa9, 0x27, 0xd6, 0xb1, 0x14, 0xfa, 0x3f, 0x9e + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x1b, 0xde, 0xc5, 0x21, 0x31, 0x65, 0x03, 0xd9, + 0xd5, 0xee, 0x65, 0xdf, 0x3e, 0xa9, 0x4d, 0xdf + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xee, 0xf4, 0x56, 0x43, 0x1d, 0xea, 0x8b, 0x4a, + 0xcf, 0x83, 0xbd, 0xae, 0x37, 0x17, 0xf7, 0x5f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x06, 0xf2, 0x51, 0x9a, 0x2f, 0xaf, 0xaa, 0x59, + 0x6b, 0xfe, 0xf5, 0xcf, 0xa1, 0x5c, 0x21, 0xb9 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x25, 0x1a, 0x7e, 0xac, 0x7e, 0x2f, 0xe8, 0x09, + 0xe4, 0xaa, 0x8d, 0x0d, 0x70, 0x12, 0x53, 0x1a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x3b, 0xff, 0xc1, 0x6e, 0x4c, 0x49, 0xb2, 0x68, + 0xa2, 0x0f, 0x8d, 0x96, 0xa6, 0x0b, 0x40, 0x58 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xe8, 0x86, 0xf9, 0x28, 0x19, 0x99, 0xc5, 0xbb, + 0x3b, 0x3e, 0x88, 0x62, 0xe2, 0xf7, 0xc9, 0x88 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x56, 0x3b, 0xf9, 0x0d, 0x61, 0xbe, 0xef, 0x39, + 0xf4, 0x8d, 0xd6, 0x25, 0xfc, 0xef, 0x13, 0x61 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x4d, 0x37, 0xc8, 0x50, 0x64, 0x45, 0x63, 0xc6, + 0x9f, 0xd0, 0xac, 0xd9, 0xa0, 0x49, 0x32, 0x5b + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb8, 0x7c, 0x92, 0x1b, 0x91, 0x82, 0x9e, 0xf3, + 0xb1, 0x3c, 0xa5, 0x41, 0xee, 0x11, 0x30, 0xa6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x2e, 0x65, 0xeb, 0x6b, 0x6e, 0xa3, 0x83, 0xe1, + 0x09, 0xac, 0xcc, 0xe8, 0x32, 0x6b, 0x03, 0x93 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x9c, 0xa5, 0x47, 0xf7, 0x43, 0x9e, 0xdc, 0x3e, + 0x25, 0x5c, 0x0f, 0x4d, 0x49, 0xaa, 0x89, 0x90 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xa5, 0xe6, 0x52, 0x61, 0x4c, 0x93, 0x00, 0xf3, + 0x78, 0x16, 0xb1, 0xf9, 0xfd, 0x0c, 0x87, 0xf9 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x14, 0x95, 0x4f, 0x0b, 0x46, 0x97, 0x77, 0x6f, + 0x44, 0x49, 0x4f, 0xe4, 0x58, 0xd8, 0x14, 0xed + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x7c, 0x8d, 0x9a, 0xb6, 0xc2, 0x76, 0x17, 0x23, + 0xfe, 0x42, 0xf8, 0xbb, 0x50, 0x6c, 0xbc, 0xf7 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xdb, 0x7e, 0x19, 0x32, 0x67, 0x9f, 0xdd, 0x99, + 0x74, 0x2a, 0xab, 0x04, 0xaa, 0x0d, 0x5a, 0x80 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x4c, 0x6a, 0x1c, 0x83, 0xe5, 0x68, 0xcd, 0x10, + 0xf2, 0x7c, 0x2d, 0x73, 0xde, 0xd1, 0x9c, 0x28 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00 + }, { + 0x90, 0xec, 0xbe, 0x61, 0x77, 0xe6, 0x74, 0xc9, + 0x8d, 0xe4, 0x12, 0x41, 0x3f, 0x7a, 0xc9, 0x15 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00 + }, { + 0x90, 0x68, 0x4a, 0x2a, 0xc5, 0x5f, 0xe1, 0xec, + 0x2b, 0x8e, 0xbd, 0x56, 0x22, 0x52, 0x0b, 0x73 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00 + }, { + 0x74, 0x72, 0xf9, 0xa7, 0x98, 0x86, 0x07, 0xca, + 0x79, 0x70, 0x77, 0x95, 0x99, 0x10, 0x35, 0xe6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 + }, { + 0x56, 0xaf, 0xf0, 0x89, 0x87, 0x8b, 0xf3, 0x35, + 0x2f, 0x8d, 0xf1, 0x72, 0xa3, 0xae, 0x47, 0xd8 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00 + }, { + 0x65, 0xc0, 0x52, 0x6c, 0xbe, 0x40, 0x16, 0x1b, + 0x80, 0x19, 0xa2, 0xa3, 0x17, 0x1a, 0xbd, 0x23 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00 + }, { + 0x37, 0x7b, 0xe0, 0xbe, 0x33, 0xb4, 0xe3, 0xe3, + 0x10, 0xb4, 0xaa, 0xbd, 0xa1, 0x73, 0xf8, 0x4f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00 + }, { + 0x94, 0x02, 0xe9, 0xaa, 0x6f, 0x69, 0xde, 0x65, + 0x04, 0xda, 0x8d, 0x20, 0xc4, 0xfc, 0xaa, 0x2f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 + }, { + 0x12, 0x3c, 0x1f, 0x4a, 0xf3, 0x13, 0xad, 0x8c, + 0x2c, 0xe6, 0x48, 0xb2, 0xe7, 0x1f, 0xb6, 0xe1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00 + }, { + 0x1f, 0xfc, 0x62, 0x6d, 0x30, 0x20, 0x3d, 0xcd, + 0xb0, 0x01, 0x9f, 0xb8, 0x0f, 0x72, 0x6c, 0xf4 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00 + }, { + 0x76, 0xda, 0x1f, 0xbe, 0x3a, 0x50, 0x72, 0x8c, + 0x50, 0xfd, 0x2e, 0x62, 0x1b, 0x5a, 0xd8, 0x85 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00 + }, { + 0x08, 0x2e, 0xb8, 0xbe, 0x35, 0xf4, 0x42, 0xfb, + 0x52, 0x66, 0x8e, 0x16, 0xa5, 0x91, 0xd1, 0xd6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 + }, { + 0xe6, 0x56, 0xf9, 0xec, 0xf5, 0xfe, 0x27, 0xec, + 0x3e, 0x4a, 0x73, 0xd0, 0x0c, 0x28, 0x2f, 0xb3 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00 + }, { + 0x2c, 0xa8, 0x20, 0x9d, 0x63, 0x27, 0x4c, 0xd9, + 0xa2, 0x9b, 0xb7, 0x4b, 0xcd, 0x77, 0x68, 0x3a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00 + }, { + 0x79, 0xbf, 0x5d, 0xce, 0x14, 0xbb, 0x7d, 0xd7, + 0x3a, 0x8e, 0x36, 0x11, 0xde, 0x7c, 0xe0, 0x26 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00 + }, { + 0x3c, 0x84, 0x99, 0x39, 0xa5, 0xd2, 0x93, 0x99, + 0xf3, 0x44, 0xc4, 0xa0, 0xec, 0xa8, 0xa5, 0x76 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 + }, { + 0xed, 0x3c, 0x0a, 0x94, 0xd5, 0x9b, 0xec, 0xe9, + 0x88, 0x35, 0xda, 0x7a, 0xa4, 0xf0, 0x7c, 0xa2 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 + }, { + 0x63, 0x91, 0x9e, 0xd4, 0xce, 0x10, 0x19, 0x64, + 0x38, 0xb6, 0xad, 0x09, 0xd9, 0x9c, 0xd7, 0x95 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00 + }, { + 0x76, 0x78, 0xf3, 0xa8, 0x33, 0xf1, 0x9f, 0xea, + 0x95, 0xf3, 0xc6, 0x02, 0x9e, 0x2b, 0xc6, 0x10 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00 + }, { + 0x3a, 0xa4, 0x26, 0x83, 0x10, 0x67, 0xd3, 0x6b, + 0x92, 0xbe, 0x7c, 0x5f, 0x81, 0xc1, 0x3c, 0x56 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 + }, { + 0x92, 0x72, 0xe2, 0xd2, 0xcd, 0xd1, 0x10, 0x50, + 0x99, 0x8c, 0x84, 0x50, 0x77, 0xa3, 0x0e, 0xa0 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00 + }, { + 0x08, 0x8c, 0x4b, 0x53, 0xf5, 0xec, 0x0f, 0xf8, + 0x14, 0xc1, 0x9a, 0xda, 0xe7, 0xf6, 0x24, 0x6c + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00 + }, { + 0x40, 0x10, 0xa5, 0xe4, 0x01, 0xfd, 0xf0, 0xa0, + 0x35, 0x4d, 0xdb, 0xcc, 0x0d, 0x01, 0x2b, 0x17 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00 + }, { + 0xa8, 0x7a, 0x38, 0x57, 0x36, 0xc0, 0xa6, 0x18, + 0x9b, 0xd6, 0x58, 0x9b, 0xd8, 0x44, 0x5a, 0x93 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 + }, { + 0x54, 0x5f, 0x2b, 0x83, 0xd9, 0x61, 0x6d, 0xcc, + 0xf6, 0x0f, 0xa9, 0x83, 0x0e, 0x9c, 0xd2, 0x87 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00 + }, { + 0x4b, 0x70, 0x6f, 0x7f, 0x92, 0x40, 0x63, 0x52, + 0x39, 0x40, 0x37, 0xa6, 0xd4, 0xf4, 0x68, 0x8d + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 + }, { + 0xb7, 0x97, 0x2b, 0x39, 0x41, 0xc4, 0x4b, 0x90, + 0xaf, 0xa7, 0xb2, 0x64, 0xbf, 0xba, 0x73, 0x87 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00 + }, { + 0x6f, 0x45, 0x73, 0x2c, 0xf1, 0x08, 0x81, 0x54, + 0x6f, 0x0f, 0xd2, 0x38, 0x96, 0xd2, 0xbb, 0x60 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00 + }, { + 0x2e, 0x35, 0x79, 0xca, 0x15, 0xaf, 0x27, 0xf6, + 0x4b, 0x3c, 0x95, 0x5a, 0x5b, 0xfc, 0x30, 0xba + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00 + }, { + 0x34, 0xa2, 0xc5, 0xa9, 0x1a, 0xe2, 0xae, 0xc9, + 0x9b, 0x7d, 0x1b, 0x5f, 0xa6, 0x78, 0x04, 0x47 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00 + }, { + 0xa4, 0xd6, 0x61, 0x6b, 0xd0, 0x4f, 0x87, 0x33, + 0x5b, 0x0e, 0x53, 0x35, 0x12, 0x27, 0xa9, 0xee + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00 + }, { + 0x7f, 0x69, 0x2b, 0x03, 0x94, 0x58, 0x67, 0xd1, + 0x61, 0x79, 0xa8, 0xce, 0xfc, 0x83, 0xea, 0x3f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 + }, { + 0x3b, 0xd1, 0x41, 0xee, 0x84, 0xa0, 0xe6, 0x41, + 0x4a, 0x26, 0xe7, 0xa4, 0xf2, 0x81, 0xf8, 0xa2 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 + }, { + 0xd1, 0x78, 0x8f, 0x57, 0x2d, 0x98, 0xb2, 0xb1, + 0x6e, 0xc5, 0xd5, 0xf3, 0x92, 0x2b, 0x99, 0xbc + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 + }, { + 0x08, 0x33, 0xff, 0x6f, 0x61, 0xd9, 0x8a, 0x57, + 0xb2, 0x88, 0xe8, 0xc3, 0x58, 0x6b, 0x85, 0xa6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0 + }, { + 0x85, 0x68, 0x26, 0x17, 0x97, 0xde, 0x17, 0x6b, + 0xf0, 0xb4, 0x3b, 0xec, 0xc6, 0x28, 0x5a, 0xfb + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 + }, { + 0xf9, 0xb0, 0xfd, 0xa0, 0xc4, 0xa8, 0x98, 0xf5, + 0xb9, 0xe6, 0xf6, 0x61, 0xc4, 0xce, 0x4d, 0x07 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8 + }, { + 0x8a, 0xde, 0x89, 0x59, 0x13, 0x68, 0x5c, 0x67, + 0xc5, 0x26, 0x9f, 0x8a, 0xae, 0x42, 0x98, 0x3e + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc + }, { + 0x39, 0xbd, 0xe6, 0x7d, 0x5c, 0x8e, 0xd8, 0xa8, + 0xb1, 0xc3, 0x7e, 0xb8, 0xfa, 0x9f, 0x5a, 0xc0 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe + }, { + 0x5c, 0x00, 0x5e, 0x72, 0xc1, 0x41, 0x8c, 0x44, + 0xf5, 0x69, 0xf2, 0xea, 0x33, 0xba, 0x54, 0xf3 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }, { + 0x3f, 0x5b, 0x8c, 0xc9, 0xea, 0x85, 0x5a, 0x0a, + 0xfa, 0x73, 0x47, 0xd2, 0x3e, 0x8d, 0x66, 0x4e + } } + }; + struct tc_aes_key_sched_struct s; + unsigned int i; + + TC_PRINT("AES128 %s (NIST fixed-key and variable-text):\n", __func__); + + (void)tc_aes128_set_encrypt_key(&s, key); + + for (i = 0; i < 128; ++i) { + result = var_text_test(i, kat_tbl[i].in, kat_tbl[i].out, &s); + if (result == TC_FAIL) { + break; + } + } + + TC_END_RESULT(result); + + return result; +} + +int var_key_test(unsigned int r, const uint8_t *in, const uint8_t *out) +{ + int result = TC_PASS; + + const uint8_t plaintext[NUM_OF_NIST_KEYS] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + uint8_t ciphertext[NUM_OF_NIST_KEYS]; + struct tc_aes_key_sched_struct s; + + (void)tc_aes128_set_encrypt_key(&s, in); + + (void)tc_aes_encrypt(ciphertext, plaintext, &s); + result = check_result(r, out, NUM_OF_NIST_KEYS, ciphertext, + sizeof(ciphertext)); + + return result; +} + +/* + * All NIST tests with variable key and fixed text. + */ +int test_4(void) +{ + int result = TC_PASS; + const struct kat_table kat_tbl[NUM_OF_FIXED_KEYS] = { + {{ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x0e, 0xdd, 0x33, 0xd3, 0xc6, 0x21, 0xe5, 0x46, + 0x45, 0x5b, 0xd8, 0xba, 0x14, 0x18, 0xbe, 0xc8 + } }, + {{ + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x4b, 0xc3, 0xf8, 0x83, 0x45, 0x0c, 0x11, 0x3c, + 0x64, 0xca, 0x42, 0xe1, 0x11, 0x2a, 0x9e, 0x87 + } }, + {{ + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x72, 0xa1, 0xda, 0x77, 0x0f, 0x5d, 0x7a, 0xc4, + 0xc9, 0xef, 0x94, 0xd8, 0x22, 0xaf, 0xfd, 0x97 + } }, + {{ + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x97, 0x00, 0x14, 0xd6, 0x34, 0xe2, 0xb7, 0x65, + 0x07, 0x77, 0xe8, 0xe8, 0x4d, 0x03, 0xcc, 0xd8 + } }, + {{ + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf1, 0x7e, 0x79, 0xae, 0xd0, 0xdb, 0x7e, 0x27, + 0x9e, 0x95, 0x5b, 0x5f, 0x49, 0x38, 0x75, 0xa7 + } }, + {{ + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x9e, 0xd5, 0xa7, 0x51, 0x36, 0xa9, 0x40, 0xd0, + 0x96, 0x3d, 0xa3, 0x79, 0xdb, 0x4a, 0xf2, 0x6a + } }, + {{ + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc4, 0x29, 0x5f, 0x83, 0x46, 0x5c, 0x77, 0x55, + 0xe8, 0xfa, 0x36, 0x4b, 0xac, 0x6a, 0x7e, 0xa5 + } }, + {{ + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb1, 0xd7, 0x58, 0x25, 0x6b, 0x28, 0xfd, 0x85, + 0x0a, 0xd4, 0x94, 0x42, 0x08, 0xcf, 0x11, 0x55 + } }, + {{ + 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x42, 0xff, 0xb3, 0x4c, 0x74, 0x3d, 0xe4, 0xd8, + 0x8c, 0xa3, 0x80, 0x11, 0xc9, 0x90, 0x89, 0x0b + } }, + {{ + 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x99, 0x58, 0xf0, 0xec, 0xea, 0x8b, 0x21, 0x72, + 0xc0, 0xc1, 0x99, 0x5f, 0x91, 0x82, 0xc0, 0xf3 + } }, + {{ + 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x95, 0x6d, 0x77, 0x98, 0xfa, 0xc2, 0x0f, 0x82, + 0xa8, 0x82, 0x3f, 0x98, 0x4d, 0x06, 0xf7, 0xf5 + } }, + {{ + 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xa0, 0x1b, 0xf4, 0x4f, 0x2d, 0x16, 0xbe, 0x92, + 0x8c, 0xa4, 0x4a, 0xaf, 0x7b, 0x9b, 0x10, 0x6b + } }, + {{ + 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb5, 0xf1, 0xa3, 0x3e, 0x50, 0xd4, 0x0d, 0x10, + 0x37, 0x64, 0xc7, 0x6b, 0xd4, 0xc6, 0xb6, 0xf8 + } }, + {{ + 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x26, 0x37, 0x05, 0x0c, 0x9f, 0xc0, 0xd4, 0x81, + 0x7e, 0x2d, 0x69, 0xde, 0x87, 0x8a, 0xee, 0x8d + } }, + {{ + 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x11, 0x3e, 0xcb, 0xe4, 0xa4, 0x53, 0x26, 0x9a, + 0x0d, 0xd2, 0x60, 0x69, 0x46, 0x7f, 0xb5, 0xb5 + } }, + {{ + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x97, 0xd0, 0x75, 0x4f, 0xe6, 0x8f, 0x11, 0xb9, + 0xe3, 0x75, 0xd0, 0x70, 0xa6, 0x08, 0xc8, 0x84 + } }, + {{ + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc6, 0xa0, 0xb3, 0xe9, 0x98, 0xd0, 0x50, 0x68, + 0xa5, 0x39, 0x97, 0x78, 0x40, 0x52, 0x00, 0xb4 + } }, + {{ + 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xdf, 0x55, 0x6a, 0x33, 0x43, 0x8d, 0xb8, 0x7b, + 0xc4, 0x1b, 0x17, 0x52, 0xc5, 0x5e, 0x5e, 0x49 + } }, + {{ + 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x90, 0xfb, 0x12, 0x8d, 0x3a, 0x1a, 0xf6, 0xe5, + 0x48, 0x52, 0x1b, 0xb9, 0x62, 0xbf, 0x1f, 0x05 + } }, + {{ + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x26, 0x29, 0x8e, 0x9c, 0x1d, 0xb5, 0x17, 0xc2, + 0x15, 0xfa, 0xdf, 0xb7, 0xd2, 0xa8, 0xd6, 0x91 + } }, + {{ + 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xa6, 0xcb, 0x76, 0x1d, 0x61, 0xf8, 0x29, 0x2d, + 0x0d, 0xf3, 0x93, 0xa2, 0x79, 0xad, 0x03, 0x80 + } }, + {{ + 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x12, 0xac, 0xd8, 0x9b, 0x13, 0xcd, 0x5f, 0x87, + 0x26, 0xe3, 0x4d, 0x44, 0xfd, 0x48, 0x61, 0x08 + } }, + {{ + 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x95, 0xb1, 0x70, 0x3f, 0xc5, 0x7b, 0xa0, 0x9f, + 0xe0, 0xc3, 0x58, 0x0f, 0xeb, 0xdd, 0x7e, 0xd4 + } }, + {{ + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xde, 0x11, 0x72, 0x2d, 0x89, 0x3e, 0x9f, 0x91, + 0x21, 0xc3, 0x81, 0xbe, 0xcc, 0x1d, 0xa5, 0x9a + } }, + {{ + 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x6d, 0x11, 0x4c, 0xcb, 0x27, 0xbf, 0x39, 0x10, + 0x12, 0xe8, 0x97, 0x4c, 0x54, 0x6d, 0x9b, 0xf2 + } }, + {{ + 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x5c, 0xe3, 0x7e, 0x17, 0xeb, 0x46, 0x46, 0xec, + 0xfa, 0xc2, 0x9b, 0x9c, 0xc3, 0x8d, 0x93, 0x40 + } }, + {{ + 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x18, 0xc1, 0xb6, 0xe2, 0x15, 0x71, 0x22, 0x05, + 0x6d, 0x02, 0x43, 0xd8, 0xa1, 0x65, 0xcd, 0xdb + } }, + {{ + 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x99, 0x69, 0x3e, 0x6a, 0x59, 0xd1, 0x36, 0x6c, + 0x74, 0xd8, 0x23, 0x56, 0x2d, 0x7e, 0x14, 0x31 + } }, + {{ + 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x6c, 0x7c, 0x64, 0xdc, 0x84, 0xa8, 0xbb, 0xa7, + 0x58, 0xed, 0x17, 0xeb, 0x02, 0x5a, 0x57, 0xe3 + } }, + {{ + 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xe1, 0x7b, 0xc7, 0x9f, 0x30, 0xea, 0xab, 0x2f, + 0xac, 0x2c, 0xbb, 0xe3, 0x45, 0x8d, 0x68, 0x7a + } }, + {{ + 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x11, 0x14, 0xbc, 0x20, 0x28, 0x00, 0x9b, 0x92, + 0x3f, 0x0b, 0x01, 0x91, 0x5c, 0xe5, 0xe7, 0xc4 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x9c, 0x28, 0x52, 0x4a, 0x16, 0xa1, 0xe1, 0xc1, + 0x45, 0x29, 0x71, 0xca, 0xa8, 0xd1, 0x34, 0x76 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xed, 0x62, 0xe1, 0x63, 0x63, 0x63, 0x83, 0x60, + 0xfd, 0xd6, 0xad, 0x62, 0x11, 0x27, 0x94, 0xf0 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x5a, 0x86, 0x88, 0xf0, 0xb2, 0xa2, 0xc1, 0x62, + 0x24, 0xc1, 0x61, 0x65, 0x8f, 0xfd, 0x40, 0x44 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x23, 0xf7, 0x10, 0x84, 0x2b, 0x9b, 0xb9, 0xc3, + 0x2f, 0x26, 0x64, 0x8c, 0x78, 0x68, 0x07, 0xca + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x44, 0xa9, 0x8b, 0xf1, 0x1e, 0x16, 0x3f, 0x63, + 0x2c, 0x47, 0xec, 0x6a, 0x49, 0x68, 0x3a, 0x89 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x0f, 0x18, 0xaf, 0xf9, 0x42, 0x74, 0x69, 0x6d, + 0x9b, 0x61, 0x84, 0x8b, 0xd5, 0x0a, 0xc5, 0xe5 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x82, 0x40, 0x85, 0x71, 0xc3, 0xe2, 0x42, 0x45, + 0x40, 0x20, 0x7f, 0x83, 0x3b, 0x6d, 0xda, 0x69 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x30, 0x3f, 0xf9, 0x96, 0x94, 0x7f, 0x0c, 0x7d, + 0x1f, 0x43, 0xc8, 0xf3, 0x02, 0x7b, 0x9b, 0x75 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x7d, 0xf4, 0xda, 0xf4, 0xad, 0x29, 0xa3, 0x61, + 0x5a, 0x9b, 0x6e, 0xce, 0x5c, 0x99, 0x51, 0x8a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc7, 0x29, 0x54, 0xa4, 0x8d, 0x07, 0x74, 0xdb, + 0x0b, 0x49, 0x71, 0xc5, 0x26, 0x26, 0x04, 0x15 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x1d, 0xf9, 0xb7, 0x61, 0x12, 0xdc, 0x65, 0x31, + 0xe0, 0x7d, 0x2c, 0xfd, 0xa0, 0x44, 0x11, 0xf0 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x8e, 0x4d, 0x8e, 0x69, 0x91, 0x19, 0xe1, 0xfc, + 0x87, 0x54, 0x5a, 0x64, 0x7f, 0xb1, 0xd3, 0x4f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xe6, 0xc4, 0x80, 0x7a, 0xe1, 0x1f, 0x36, 0xf0, + 0x91, 0xc5, 0x7d, 0x9f, 0xb6, 0x85, 0x48, 0xd1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x8e, 0xbf, 0x73, 0xaa, 0xd4, 0x9c, 0x82, 0x00, + 0x7f, 0x77, 0xa5, 0xc1, 0xcc, 0xec, 0x6a, 0xb4 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x4f, 0xb2, 0x88, 0xcc, 0x20, 0x40, 0x04, 0x90, + 0x01, 0xd2, 0xc7, 0x58, 0x5a, 0xd1, 0x23, 0xfc + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x04, 0x49, 0x71, 0x10, 0xef, 0xb9, 0xdc, 0xeb, + 0x13, 0xe2, 0xb1, 0x3f, 0xb4, 0x46, 0x55, 0x64 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x75, 0x55, 0x0e, 0x6c, 0xb5, 0xa8, 0x8e, 0x49, + 0x63, 0x4c, 0x9a, 0xb6, 0x9e, 0xda, 0x04, 0x30 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb6, 0x76, 0x84, 0x73, 0xce, 0x98, 0x43, 0xea, + 0x66, 0xa8, 0x14, 0x05, 0xdd, 0x50, 0xb3, 0x45 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xcb, 0x2f, 0x43, 0x03, 0x83, 0xf9, 0x08, 0x4e, + 0x03, 0xa6, 0x53, 0x57, 0x1e, 0x06, 0x5d, 0xe6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xff, 0x4e, 0x66, 0xc0, 0x7b, 0xae, 0x3e, 0x79, + 0xfb, 0x7d, 0x21, 0x08, 0x47, 0xa3, 0xb0, 0xba + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x7b, 0x90, 0x78, 0x51, 0x25, 0x50, 0x5f, 0xad, + 0x59, 0xb1, 0x3c, 0x18, 0x6d, 0xd6, 0x6c, 0xe3 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x8b, 0x52, 0x7a, 0x6a, 0xeb, 0xda, 0xec, 0x9e, + 0xae, 0xf8, 0xed, 0xa2, 0xcb, 0x77, 0x83, 0xe5 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x43, 0xfd, 0xaf, 0x53, 0xeb, 0xbc, 0x98, 0x80, + 0xc2, 0x28, 0x61, 0x7d, 0x6a, 0x9b, 0x54, 0x8b + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x53, 0x78, 0x61, 0x04, 0xb9, 0x74, 0x4b, 0x98, + 0xf0, 0x52, 0xc4, 0x6f, 0x1c, 0x85, 0x0d, 0x0b + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xb5, 0xab, 0x30, 0x13, 0xdd, 0x1e, 0x61, 0xdf, + 0x06, 0xcb, 0xaf, 0x34, 0xca, 0x2a, 0xee, 0x78 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x74, 0x70, 0x46, 0x9b, 0xe9, 0x72, 0x30, 0x30, + 0xfd, 0xcc, 0x73, 0xa8, 0xcd, 0x4f, 0xbb, 0x10 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xa3, 0x5a, 0x63, 0xf5, 0x34, 0x3e, 0xbe, 0x9e, + 0xf8, 0x16, 0x7b, 0xcb, 0x48, 0xad, 0x12, 0x2e + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xfd, 0x86, 0x87, 0xf0, 0x75, 0x7a, 0x21, 0x0e, + 0x9f, 0xdf, 0x18, 0x12, 0x04, 0xc3, 0x08, 0x63 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x7a, 0x18, 0x1e, 0x84, 0xbd, 0x54, 0x57, 0xd2, + 0x6a, 0x88, 0xfb, 0xae, 0x96, 0x01, 0x8f, 0xb0 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x65, 0x33, 0x17, 0xb9, 0x36, 0x2b, 0x6f, 0x9b, + 0x9e, 0x1a, 0x58, 0x0e, 0x68, 0xd4, 0x94, 0xb5 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x99, 0x5c, 0x9d, 0xc0, 0xb6, 0x89, 0xf0, 0x3c, + 0x45, 0x86, 0x7b, 0x5f, 0xaa, 0x5c, 0x18, 0xd1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x77, 0xa4, 0xd9, 0x6d, 0x56, 0xdd, 0xa3, 0x98, + 0xb9, 0xaa, 0xbe, 0xcf, 0xc7, 0x57, 0x29, 0xfd + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x84, 0xbe, 0x19, 0xe0, 0x53, 0x63, 0x5f, 0x09, + 0xf2, 0x66, 0x5e, 0x7b, 0xae, 0x85, 0xb4, 0x2d + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x32, 0xcd, 0x65, 0x28, 0x42, 0x92, 0x6a, 0xea, + 0x4a, 0xa6, 0x13, 0x7b, 0xb2, 0xbe, 0x2b, 0x5e + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x49, 0x3d, 0x4a, 0x4f, 0x38, 0xeb, 0xb3, 0x37, + 0xd1, 0x0a, 0xa8, 0x4e, 0x91, 0x71, 0xa5, 0x54 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xd9, 0xbf, 0xf7, 0xff, 0x45, 0x4b, 0x0e, 0xc5, + 0xa4, 0xa2, 0xa6, 0x95, 0x66, 0xe2, 0xcb, 0x84 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x35, 0x35, 0xd5, 0x65, 0xac, 0xe3, 0xf3, 0x1e, + 0xb2, 0x49, 0xba, 0x2c, 0xc6, 0x76, 0x5d, 0x7a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf6, 0x0e, 0x91, 0xfc, 0x32, 0x69, 0xee, 0xcf, + 0x32, 0x31, 0xc6, 0xe9, 0x94, 0x56, 0x97, 0xc6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xab, 0x69, 0xcf, 0xad, 0xf5, 0x1f, 0x8e, 0x60, + 0x4d, 0x9c, 0xc3, 0x71, 0x82, 0xf6, 0x63, 0x5a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x78, 0x66, 0x37, 0x3f, 0x24, 0xa0, 0xb6, 0xed, + 0x56, 0xe0, 0xd9, 0x6f, 0xcd, 0xaf, 0xb8, 0x77 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x1e, 0xa4, 0x48, 0xc2, 0xaa, 0xc9, 0x54, 0xf5, + 0xd8, 0x12, 0xe9, 0xd7, 0x84, 0x94, 0x44, 0x6a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xac, 0xc5, 0x59, 0x9d, 0xd8, 0xac, 0x02, 0x23, + 0x9a, 0x0f, 0xef, 0x4a, 0x36, 0xdd, 0x16, 0x68 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xd8, 0x76, 0x44, 0x68, 0xbb, 0x10, 0x38, 0x28, + 0xcf, 0x7e, 0x14, 0x73, 0xce, 0x89, 0x50, 0x73 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x1b, 0x0d, 0x02, 0x89, 0x36, 0x83, 0xb9, 0xf1, + 0x80, 0x45, 0x8e, 0x4a, 0xa6, 0xb7, 0x39, 0x82 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x96, 0xd9, 0xb0, 0x17, 0xd3, 0x02, 0xdf, 0x41, + 0x0a, 0x93, 0x7d, 0xcd, 0xb8, 0xbb, 0x6e, 0x43 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xef, 0x16, 0x23, 0xcc, 0x44, 0x31, 0x3c, 0xff, + 0x44, 0x0b, 0x15, 0x94, 0xa7, 0xe2, 0x1c, 0xc6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x28, 0x4c, 0xa2, 0xfa, 0x35, 0x80, 0x7b, 0x8b, + 0x0a, 0xe4, 0xd1, 0x9e, 0x11, 0xd7, 0xdb, 0xd7 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf2, 0xe9, 0x76, 0x87, 0x57, 0x55, 0xf9, 0x40, + 0x1d, 0x54, 0xf3, 0x6e, 0x2a, 0x23, 0xa5, 0x94 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xec, 0x19, 0x8a, 0x18, 0xe1, 0x0e, 0x53, 0x24, + 0x03, 0xb7, 0xe2, 0x08, 0x87, 0xc8, 0xdd, 0x80 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x54, 0x5d, 0x50, 0xeb, 0xd9, 0x19, 0xe4, 0xa6, + 0x94, 0x9d, 0x96, 0xad, 0x47, 0xe4, 0x6a, 0x80 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xdb, 0xdf, 0xb5, 0x27, 0x06, 0x0e, 0x0a, 0x71, + 0x00, 0x9c, 0x7b, 0xb0, 0xc6, 0x8f, 0x1d, 0x44 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x9c, 0xfa, 0x13, 0x22, 0xea, 0x33, 0xda, 0x21, + 0x73, 0xa0, 0x24, 0xf2, 0xff, 0x0d, 0x89, 0x6d + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x87, 0x85, 0xb1, 0xa7, 0x5b, 0x0f, 0x3b, 0xd9, + 0x58, 0xdc, 0xd0, 0xe2, 0x93, 0x18, 0xc5, 0x21 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x38, 0xf6, 0x7b, 0x9e, 0x98, 0xe4, 0xa9, 0x7b, + 0x6d, 0xf0, 0x30, 0xa9, 0xfc, 0xdd, 0x01, 0x04 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x19, 0x2a, 0xff, 0xfb, 0x2c, 0x88, 0x0e, 0x82, + 0xb0, 0x59, 0x26, 0xd0, 0xfc, 0x6c, 0x44, 0x8b + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0x6a, 0x79, 0x80, 0xce, 0x7b, 0x10, 0x5c, 0xf5, + 0x30, 0x95, 0x2d, 0x74, 0xda, 0xaf, 0x79, 0x8c + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 + }, { + 0xea, 0x36, 0x95, 0xe1, 0x35, 0x1b, 0x9d, 0x68, + 0x58, 0xbd, 0x95, 0x8c, 0xf5, 0x13, 0xef, 0x6c + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00 + }, { + 0x6d, 0xa0, 0x49, 0x0b, 0xa0, 0xba, 0x03, 0x43, + 0xb9, 0x35, 0x68, 0x1d, 0x2c, 0xce, 0x5b, 0xa1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf0, 0xea, 0x23, 0xaf, 0x08, 0x53, 0x40, 0x11, + 0xc6, 0x00, 0x09, 0xab, 0x29, 0xad, 0xa2, 0xf1 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00 + }, { + 0xff, 0x13, 0x80, 0x6c, 0xf1, 0x9c, 0xc3, 0x87, + 0x21, 0x55, 0x4d, 0x7c, 0x0f, 0xcd, 0xcd, 0x4b + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 + }, { + 0x68, 0x38, 0xaf, 0x1f, 0x4f, 0x69, 0xba, 0xe9, + 0xd8, 0x5d, 0xd1, 0x88, 0xdc, 0xdf, 0x06, 0x88 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00 + }, { + 0x36, 0xcf, 0x44, 0xc9, 0x2d, 0x55, 0x0b, 0xfb, + 0x1e, 0xd2, 0x8e, 0xf5, 0x83, 0xdd, 0xf5, 0xd7 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00 + }, { + 0xd0, 0x6e, 0x31, 0x95, 0xb5, 0x37, 0x6f, 0x10, + 0x9d, 0x5c, 0x4e, 0xc6, 0xc5, 0xd6, 0x2c, 0xed + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00 + }, { + 0xc4, 0x40, 0xde, 0x01, 0x4d, 0x3d, 0x61, 0x07, + 0x07, 0x27, 0x9b, 0x13, 0x24, 0x2a, 0x5c, 0x36 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 + }, { + 0xf0, 0xc5, 0xc6, 0xff, 0xa5, 0xe0, 0xbd, 0x3a, + 0x94, 0xc8, 0x8f, 0x6b, 0x6f, 0x7c, 0x16, 0xb9 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00 + }, { + 0x3e, 0x40, 0xc3, 0x90, 0x1c, 0xd7, 0xef, 0xfc, + 0x22, 0xbf, 0xfc, 0x35, 0xde, 0xe0, 0xb4, 0xd9 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00 + }, { + 0xb6, 0x33, 0x05, 0xc7, 0x2b, 0xed, 0xfa, 0xb9, + 0x73, 0x82, 0xc4, 0x06, 0xd0, 0xc4, 0x9b, 0xc6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00 + }, { + 0x36, 0xbb, 0xaa, 0xb2, 0x2a, 0x6b, 0xd4, 0x92, + 0x5a, 0x99, 0xa2, 0xb4, 0x08, 0xd2, 0xdb, 0xae + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 + }, { + 0x30, 0x7c, 0x5b, 0x8f, 0xcd, 0x05, 0x33, 0xab, + 0x98, 0xbc, 0x51, 0xe2, 0x7a, 0x6c, 0xe4, 0x61 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00 + }, { + 0x82, 0x9c, 0x04, 0xff, 0x4c, 0x07, 0x51, 0x3c, + 0x0b, 0x3e, 0xf0, 0x5c, 0x03, 0xe3, 0x37, 0xb5 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00 + }, { + 0xf1, 0x7a, 0xf0, 0xe8, 0x95, 0xdd, 0xa5, 0xeb, + 0x98, 0xef, 0xc6, 0x80, 0x66, 0xe8, 0x4c, 0x54 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00 + }, { + 0x27, 0x71, 0x67, 0xf3, 0x81, 0x2a, 0xff, 0xf1, + 0xff, 0xac, 0xb4, 0xa9, 0x34, 0x37, 0x9f, 0xc3 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 + }, { + 0x2c, 0xb1, 0xdc, 0x3a, 0x9c, 0x72, 0x97, 0x2e, + 0x42, 0x5a, 0xe2, 0xef, 0x3e, 0xb5, 0x97, 0xcd + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 + }, { + 0x36, 0xae, 0xaa, 0x3a, 0x21, 0x3e, 0x96, 0x8d, + 0x4b, 0x5b, 0x67, 0x9d, 0x3a, 0x2c, 0x97, 0xfe + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00 + }, { + 0x92, 0x41, 0xda, 0xca, 0x4f, 0xdd, 0x03, 0x4a, + 0x82, 0x37, 0x2d, 0xb5, 0x0e, 0x1a, 0x0f, 0x3f + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00 + }, { + 0xc1, 0x45, 0x74, 0xd9, 0xcd, 0x00, 0xcf, 0x2b, + 0x5a, 0x7f, 0x77, 0xe5, 0x3c, 0xd5, 0x78, 0x85 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 + }, { + 0x79, 0x3d, 0xe3, 0x92, 0x36, 0x57, 0x0a, 0xba, + 0x83, 0xab, 0x9b, 0x73, 0x7c, 0xb5, 0x21, 0xc9 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00 + }, { + 0x16, 0x59, 0x1c, 0x0f, 0x27, 0xd6, 0x0e, 0x29, + 0xb8, 0x5a, 0x96, 0xc3, 0x38, 0x61, 0xa7, 0xef + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00 + }, { + 0x44, 0xfb, 0x5c, 0x4d, 0x4f, 0x5c, 0xb7, 0x9b, + 0xe5, 0xc1, 0x74, 0xa3, 0xb1, 0xc9, 0x73, 0x48 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00 + }, { + 0x67, 0x4d, 0x2b, 0x61, 0x63, 0x3d, 0x16, 0x2b, + 0xe5, 0x9d, 0xde, 0x04, 0x22, 0x2f, 0x47, 0x40 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 + }, { + 0xb4, 0x75, 0x0f, 0xf2, 0x63, 0xa6, 0x5e, 0x1f, + 0x9e, 0x92, 0x4c, 0xcf, 0xd9, 0x8f, 0x3e, 0x37 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00 + }, { + 0x62, 0xd0, 0x66, 0x2d, 0x6e, 0xae, 0xdd, 0xed, + 0xeb, 0xae, 0x7f, 0x7e, 0xa3, 0xa4, 0xf6, 0xb6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 + }, { + 0x70, 0xc4, 0x6b, 0xb3, 0x06, 0x92, 0xbe, 0x65, + 0x7f, 0x7e, 0xaa, 0x93, 0xeb, 0xad, 0x98, 0x97 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00 + }, { + 0x32, 0x39, 0x94, 0xcf, 0xb9, 0xda, 0x28, 0x5a, + 0x5d, 0x96, 0x42, 0xe1, 0x75, 0x9b, 0x22, 0x4a + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00 + }, { + 0x1d, 0xbf, 0x57, 0x87, 0x7b, 0x7b, 0x17, 0x38, + 0x5c, 0x85, 0xd0, 0xb5, 0x48, 0x51, 0xe3, 0x71 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00 + }, { + 0xdf, 0xa5, 0xc0, 0x97, 0xcd, 0xc1, 0x53, 0x2a, + 0xc0, 0x71, 0xd5, 0x7b, 0x1d, 0x28, 0xd1, 0xbd + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00 + }, { + 0x3a, 0x0c, 0x53, 0xfa, 0x37, 0x31, 0x1f, 0xc1, + 0x0b, 0xd2, 0xa9, 0x98, 0x1f, 0x51, 0x31, 0x74 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00 + }, { + 0xba, 0x4f, 0x97, 0x0c, 0x0a, 0x25, 0xc4, 0x18, + 0x14, 0xbd, 0xae, 0x2e, 0x50, 0x6b, 0xe3, 0xb4 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 + }, { + 0x2d, 0xce, 0x3a, 0xcb, 0x72, 0x7c, 0xd1, 0x3c, + 0xcd, 0x76, 0xd4, 0x25, 0xea, 0x56, 0xe4, 0xf6 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 + }, { + 0x51, 0x60, 0x47, 0x4d, 0x50, 0x4b, 0x9b, 0x3e, + 0xef, 0xb6, 0x8d, 0x35, 0xf2, 0x45, 0xf4, 0xb3 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 + }, { + 0x41, 0xa8, 0xa9, 0x47, 0x76, 0x66, 0x35, 0xde, + 0xc3, 0x75, 0x53, 0xd9, 0xa6, 0xc0, 0xcb, 0xb7 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0 + }, { + 0x25, 0xd6, 0xcf, 0xe6, 0x88, 0x1f, 0x2b, 0xf4, + 0x97, 0xdd, 0x14, 0xcd, 0x4d, 0xdf, 0x44, 0x5b + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 + }, { + 0x41, 0xc7, 0x8c, 0x13, 0x5e, 0xd9, 0xe9, 0x8c, + 0x09, 0x66, 0x40, 0x64, 0x72, 0x65, 0xda, 0x1e + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8 + }, { + 0x5a, 0x4d, 0x40, 0x4d, 0x89, 0x17, 0xe3, 0x53, + 0xe9, 0x2a, 0x21, 0x07, 0x2c, 0x3b, 0x23, 0x05 + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc + }, { + 0x02, 0xbc, 0x96, 0x84, 0x6b, 0x3f, 0xdc, 0x71, + 0x64, 0x3f, 0x38, 0x4c, 0xd3, 0xcc, 0x3e, 0xaf + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe + }, { + 0x9b, 0xa4, 0xa9, 0x14, 0x3f, 0x4e, 0x5d, 0x40, + 0x48, 0x52, 0x1c, 0x4f, 0x88, 0x77, 0xd8, 0x8e + } }, + {{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }, { + 0xa1, 0xf6, 0x25, 0x8c, 0x87, 0x7d, 0x5f, 0xcd, + 0x89, 0x64, 0x48, 0x45, 0x38, 0xbf, 0xc9, 0x2c + } } + }; + unsigned int i; + + TC_PRINT("AES128 test #4 (NIST variable-key and fixed-text):\n"); + + for (i = 0; i < NUM_OF_FIXED_KEYS; ++i) { + result = var_key_test(i, kat_tbl[i].in, kat_tbl[i].out); + if (result == TC_FAIL) { + break; + } + + } + + TC_END_RESULT(result); + + return result; +} + +/* + * Main task to test AES + */ +int main(void) +{ + int result = TC_PASS; + + TC_START("Performing AES128 tests:"); + + result = test_1(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("AES128 test #1 (NIST key schedule test) failed.\n"); + goto exitTest; + } + result = test_2(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("AES128 test #2 (NIST encryption test) failed.\n"); + goto exitTest; + } + result = test_3(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("AES128 test #3 (NIST fixed-key and variable-text) " + "failed.\n"); + goto exitTest; + } + result = test_4(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("AES128 test #4 (NIST variable-key and fixed-text) " + "failed.\n"); + goto exitTest; + } + + TC_PRINT("All AES128 tests succeeded!\n"); + + exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); + + return result; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_cbc_mode.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_cbc_mode.c new file mode 100644 index 0000000..ffa0a88 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_cbc_mode.c @@ -0,0 +1,177 @@ +/* test_cbc_mode.c - TinyCrypt implementation of some AES-CBC tests */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ +/* + * DESCRIPTION + * This module tests the following AES-CBC Mode routines: + * + * Scenarios tested include: + * - AES128 CBC mode encryption SP 800-38a tests + */ + +#include +#include +#include + +#include +#include +#include + +/* + * NIST test vectors from SP 800-38a: + * + * Block #1 + * Plaintext 6bc1bee22e409f96e93d7e117393172a + * Input Block 6bc0bce12a459991e134741a7f9e1925 + * Output Block 7649abac8119b246cee98e9b12e9197d + * Ciphertext 7649abac8119b246cee98e9b12e9197d + * Block #2 + * Plaintext ae2d8a571e03ac9c9eb76fac45af8e51 + * Input Block d86421fb9f1a1eda505ee1375746972c + * Output Block 5086cb9b507219ee95db113a917678b2 + * Ciphertext 5086cb9b507219ee95db113a917678b2 + * Block #3 + * Plaintext 30c81c46a35ce411e5fbc1191a0a52ef + * Input Block 604ed7ddf32efdff7020d0238b7c2a5d + * Output Block 73bed6b8e3c1743b7116e69e22229516 + * Ciphertext 73bed6b8e3c1743b7116e69e22229516 + * Block #4 + * Plaintext f69f2445df4f9b17ad2b417be66c3710 + * Input Block 8521f2fd3c8eef2cdc3da7e5c44ea206 + * Output Block 3ff1caa1681fac09120eca307586e1a7 + * Ciphertext 3ff1caa1681fac09120eca307586e1a7 + */ +const uint8_t key[16] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, + 0x09, 0xcf, 0x4f, 0x3c +}; + +const uint8_t iv[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f +}; + +const uint8_t plaintext[64] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, + 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, + 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, + 0xe6, 0x6c, 0x37, 0x10 +}; + +const uint8_t ciphertext[80] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, + 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, 0x50, 0x86, 0xcb, 0x9b, + 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, + 0x22, 0x22, 0x95, 0x16, 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, + 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 +}; + +/* + * NIST SP 800-38a CBC Test for encryption and decryption. + */ +int test_1_and_2(void) +{ + struct tc_aes_key_sched_struct a; + uint8_t iv_buffer[16]; + uint8_t encrypted[80]; + uint8_t decrypted[64]; + uint8_t *p; + unsigned int length; + int result = TC_PASS; + + (void)tc_aes128_set_encrypt_key(&a, key); + + (void)memcpy(iv_buffer, iv, TC_AES_BLOCK_SIZE); + + TC_PRINT("CBC test #1 (encryption SP 800-38a tests):\n"); + if (tc_cbc_mode_encrypt(encrypted, sizeof(plaintext) + TC_AES_BLOCK_SIZE, + plaintext, sizeof(plaintext), iv_buffer, &a) == 0) { + TC_ERROR("CBC test #1 (encryption SP 800-38a tests) failed in " + "%s.\n", __func__); + result = TC_FAIL; + goto exitTest1; + } + + result = check_result(1, ciphertext, sizeof(encrypted), encrypted, + sizeof(encrypted)); + TC_END_RESULT(result); + + TC_PRINT("CBC test #2 (decryption SP 800-38a tests):\n"); + (void)tc_aes128_set_decrypt_key(&a, key); + + p = &encrypted[TC_AES_BLOCK_SIZE]; + length = ((unsigned int) sizeof(encrypted)) - TC_AES_BLOCK_SIZE; + + if (tc_cbc_mode_decrypt(decrypted, length - TC_AES_BLOCK_SIZE, p, length, + encrypted, &a) == 0) { + TC_ERROR("CBC test #2 (decryption SP 800-38a tests) failed in. " + "%s\n", __func__); + result = TC_FAIL; + goto exitTest1; + } + + result = check_result(2, plaintext, sizeof(decrypted), decrypted, + sizeof(decrypted)); + +exitTest1: + TC_END_RESULT(result); + return result; +} + +/* + * Main task to test AES + */ +int main(void) +{ + int result = TC_PASS; + + TC_START("Performing AES128 tests:"); + + TC_PRINT("Performing CBC tests:\n"); + result = test_1_and_2(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("CBC test #1 failed.\n"); + goto exitTest; + } + + TC_PRINT("All CBC tests succeeded!\n"); + +exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); + + return result; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_ccm_mode.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_ccm_mode.c new file mode 100644 index 0000000..878edb3 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_ccm_mode.c @@ -0,0 +1,545 @@ +/* test_ccm_mode.c - TinyCrypt AES-CCM tests (RFC 3610 tests) */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ + +/* + * DESCRIPTION + * This module tests the following AES-CCM Mode routines: + * + * Scenarios tested include: + * - AES128 CCM mode encryption RFC 3610 test vector #1 + * - AES128 CCM mode encryption RFC 3610 test vector #2 + * - AES128 CCM mode encryption RFC 3610 test vector #3 + * - AES128 CCM mode encryption RFC 3610 test vector #7 + * - AES128 CCM mode encryption RFC 3610 test vector #8 + * - AES128 CCM mode encryption RFC 3610 test vector #9 + * - AES128 CCM mode encryption No associated data + * - AES128 CCM mode encryption No payload data + */ + +#include +#include +#include + +#include + +#define TC_CCM_MAX_CT_SIZE 50 +#define TC_CCM_MAX_PT_SIZE 25 +#define NUM_NIST_KEYS 16 +#define NONCE_LEN 13 +#define HEADER_LEN 8 +#define M_LEN8 8 +#define M_LEN10 10 +#define DATA_BUF_LEN23 23 +#define DATA_BUF_LEN24 24 +#define DATA_BUF_LEN25 25 +#define EXPECTED_BUF_LEN31 31 +#define EXPECTED_BUF_LEN32 32 +#define EXPECTED_BUF_LEN33 33 +#define EXPECTED_BUF_LEN34 34 +#define EXPECTED_BUF_LEN35 35 + +int do_test(const uint8_t *key, uint8_t *nonce, + size_t nlen, const uint8_t *hdr, + size_t hlen, const uint8_t *data, + size_t dlen, const uint8_t *expected, + size_t elen, const int mlen) +{ + + int result = TC_PASS; + + uint8_t ciphertext[TC_CCM_MAX_CT_SIZE]; + uint8_t decrypted[TC_CCM_MAX_PT_SIZE]; + struct tc_ccm_mode_struct c; + struct tc_aes_key_sched_struct sched; + + tc_aes128_set_encrypt_key(&sched, key); + + result = tc_ccm_config(&c, &sched, nonce, nlen, mlen); + if (result == 0) { + TC_ERROR("CCM config failed in %s.\n", __func__); + + result = TC_FAIL; + goto exitTest1; + } + + result = tc_ccm_generation_encryption(ciphertext, TC_CCM_MAX_CT_SIZE, hdr, + hlen, data, dlen, &c); + if (result == 0) { + TC_ERROR("ccm_encrypt failed in %s.\n", __func__); + + result = TC_FAIL; + goto exitTest1; + } + + + if (memcmp(expected, ciphertext, elen) != 0) { + TC_ERROR("ccm_encrypt produced wrong ciphertext in %s.\n", + __func__); + show_str("\t\tExpected", expected, elen); + show_str("\t\tComputed", ciphertext, elen); + + result = TC_FAIL; + goto exitTest1; + } + + result = tc_ccm_decryption_verification(decrypted, TC_CCM_MAX_PT_SIZE, hdr, + hlen, ciphertext, dlen+mlen, &c); + if (result == 0) { + TC_ERROR("ccm_decrypt failed in %s.\n", __func__); + show_str("\t\tExpected", data, dlen); + show_str("\t\tComputed", decrypted, sizeof(decrypted)); + + result = TC_FAIL; + goto exitTest1; + } + + result = TC_PASS; + +exitTest1: + TC_END_RESULT(result); + return result; +} + +int test_vector_1(void) +{ + int result = TC_PASS; + /* RFC 3610 test vector #1 */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 + }; + const uint8_t hdr[HEADER_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + const uint8_t data[DATA_BUF_LEN23] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e + }; + const uint8_t expected[EXPECTED_BUF_LEN31] = { + 0x58, 0x8c, 0x97, 0x9a, 0x61, 0xc6, 0x63, 0xd2, + 0xf0, 0x66, 0xd0, 0xc2, 0xc0, 0xf9, 0x89, 0x80, + 0x6d, 0x5f, 0x6b, 0x61, 0xda, 0xc3, 0x84, 0x17, + 0xe8, 0xd1, 0x2c, 0xfd, 0xf9, 0x26, 0xe0 + }; + uint16_t mlen = M_LEN8; + + TC_PRINT("%s: Performing CCM test #1 (RFC 3610 test vector #1):\n", + __func__); + + result = do_test(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), expected, sizeof(expected), mlen); + + return result; +} + +int test_vector_2(void) +{ + int result = TC_PASS; + /* RFC 3610 test vector #2 */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 + }; + const uint8_t hdr[HEADER_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + const uint8_t data[DATA_BUF_LEN24] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + const uint8_t expected[EXPECTED_BUF_LEN32] = { + 0x72, 0xc9, 0x1a, 0x36, 0xe1, 0x35, 0xf8, 0xcf, + 0x29, 0x1c, 0xa8, 0x94, 0x08, 0x5c, 0x87, 0xe3, + 0xcc, 0x15, 0xc4, 0x39, 0xc9, 0xe4, 0x3a, 0x3b, + 0xa0, 0x91, 0xd5, 0x6e, 0x10, 0x40, 0x09, 0x16 + }; + uint16_t mlen = M_LEN8; + + TC_PRINT("%s: Performing CCM test #2 (RFC 3610 test vector #2):\n", + __func__); + + result = do_test(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), expected, sizeof(expected), mlen); + + return result; +} + +int test_vector_3(void) +{ + int result = TC_PASS; + /* RFC 3610 test vector #3 */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x05, 0x04, 0x03, 0x02, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 + }; + const uint8_t hdr[HEADER_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + const uint8_t data[DATA_BUF_LEN25] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20 + }; + const uint8_t expected[EXPECTED_BUF_LEN33] = { + 0x51, 0xb1, 0xe5, 0xf4, 0x4a, 0x19, 0x7d, 0x1d, + 0xa4, 0x6b, 0x0f, 0x8e, 0x2d, 0x28, 0x2a, 0xe8, + 0x71, 0xe8, 0x38, 0xbb, 0x64, 0xda, 0x85, 0x96, + 0x57, 0x4a, 0xda, 0xa7, 0x6f, 0xbd, 0x9f, 0xb0, + 0xc5 + }; + uint16_t mlen = M_LEN8; + + TC_PRINT("%s: Performing CCM test #3 (RFC 3610 test vector #3):\n", + __func__); + + result = do_test(key, nonce, sizeof(nonce), hdr, sizeof(hdr), data, + sizeof(data), expected, sizeof(expected), mlen); + + return result; +} + +int test_vector_4(void) +{ + int result = TC_PASS; + /* RFC 3610 test vector #7 */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x09, 0x08, 0x07, 0x06, 0xa0, + 0xa1, 0xa2, 0xa3, 0xa4, 0xa5 + }; + const uint8_t hdr[HEADER_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + const uint8_t data[DATA_BUF_LEN23] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e + }; + const uint8_t expected[EXPECTED_BUF_LEN33] = { + 0x01, 0x35, 0xD1, 0xB2, 0xC9, 0x5F, 0x41, 0xD5, + 0xD1, 0xD4, 0xFE, 0xC1, 0x85, 0xD1, 0x66, 0xB8, + 0x09, 0x4E, 0x99, 0x9D, 0xFE, 0xD9, 0x6C, 0x04, + 0x8C, 0x56, 0x60, 0x2C, 0x97, 0xAC, 0xBB, 0x74, + 0x90 + }; + uint16_t mlen = M_LEN10; + + TC_PRINT("%s: Performing CCM test #4 (RFC 3610 test vector #7):\n", + __func__); + + result = do_test(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), expected, sizeof(expected), mlen); + + return result; +} + +int test_vector_5(void) +{ + int result = TC_PASS; + /* RFC 3610 test vector #8 */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x0A, 0x09, 0x08, 0x07, 0xA0, + 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 + }; + const uint8_t hdr[HEADER_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + const uint8_t data[DATA_BUF_LEN24] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + const uint8_t expected[EXPECTED_BUF_LEN34] = { + 0x7B, 0x75, 0x39, 0x9A, 0xC0, 0x83, 0x1D, 0xD2, + 0xF0, 0xBB, 0xD7, 0x58, 0x79, 0xA2, 0xFD, 0x8F, + 0x6C, 0xAE, 0x6B, 0x6C, 0xD9, 0xB7, 0xDB, 0x24, + 0xC1, 0x7B, 0x44, 0x33, 0xF4, 0x34, 0x96, 0x3F, + 0x34, 0xB4 + }; + uint16_t mlen = M_LEN10; + + TC_PRINT("%s: Performing CCM test #5 (RFC 3610 test vector #8):\n", + __func__); + + result = do_test(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), expected, sizeof(expected), mlen); + + return result; +} + +int test_vector_6(void) +{ + int result = TC_PASS; + /* RFC 3610 test vector #9 */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x0B, 0x0A, 0x09, 0x08, 0xA0, + 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 + }; + const uint8_t hdr[HEADER_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + const uint8_t data[DATA_BUF_LEN25] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20 + }; + const uint8_t expected[EXPECTED_BUF_LEN35] = { + 0x82, 0x53, 0x1a, 0x60, 0xCC, 0x24, 0x94, 0x5a, + 0x4b, 0x82, 0x79, 0x18, 0x1a, 0xb5, 0xc8, 0x4d, + 0xf2, 0x1c, 0xe7, 0xf9, 0xb7, 0x3f, 0x42, 0xe1, + 0x97, 0xea, 0x9c, 0x07, 0xe5, 0x6b, 0x5e, 0xb1, + 0x7e, 0x5f, 0x4e + }; + uint16_t mlen = M_LEN10; + + TC_PRINT("%s: Performing CCM test #6 (RFC 3610 test vector #9):\n", + __func__); + + result = do_test(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), expected, sizeof(expected), mlen); + + return result; +} + +int test_vector_7(void) +{ + int result = TC_PASS; + /* Test based on RFC 3610 test vector #9 but with no associated data */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x0B, 0x0A, 0x09, 0x08, 0xA0, + 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 + }; + uint8_t *hdr = NULL; + + uint8_t data[DATA_BUF_LEN25] = { + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20 + }; + struct tc_ccm_mode_struct c; + struct tc_aes_key_sched_struct sched; + uint8_t decrypted[TC_CCM_MAX_PT_SIZE]; + uint8_t ciphertext[TC_CCM_MAX_CT_SIZE]; + uint16_t mlen = M_LEN10; + + TC_PRINT("%s: Performing CCM test #7 (no associated data):\n", + __func__); + + tc_aes128_set_encrypt_key(&sched, key); + if (tc_ccm_config(&c, &sched, nonce, sizeof(nonce), mlen) == 0) { + TC_ERROR("ccm_config failed in %s.\n", __func__); + + result = TC_FAIL; + goto exitTest1; + } + + result = tc_ccm_generation_encryption(ciphertext, TC_CCM_MAX_CT_SIZE, hdr, + 0, data, sizeof(data), &c); + if (result == 0) { + TC_ERROR("ccm_encryption failed in %s.\n", __func__); + + result = TC_FAIL; + goto exitTest1; + } + + result = tc_ccm_decryption_verification (decrypted, TC_CCM_MAX_PT_SIZE, hdr, + 0, ciphertext, sizeof(data)+mlen, &c); + if (result == 0) { + TC_ERROR("ccm_decrypt failed in %s.\n", __func__); + show_str("\t\tExpected", data, sizeof(data)); + show_str("\t\tComputed", decrypted, sizeof(decrypted)); + + result = TC_FAIL; + goto exitTest1; + } + + result = TC_PASS; + +exitTest1: + TC_END_RESULT(result); + return result; + +} + +int test_vector_8(void) +{ + int result = TC_PASS; + /* Test based on RFC 3610 test vector #9 but with no payload data */ + const uint8_t key[NUM_NIST_KEYS] = { + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF + }; + uint8_t nonce[NONCE_LEN] = { + 0x00, 0x00, 0x00, 0x0B, 0x0A, 0x09, 0x08, 0xA0, + 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 + }; + const uint8_t hdr[8] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + + uint8_t *data = NULL; + + struct tc_ccm_mode_struct c; + struct tc_aes_key_sched_struct sched; + + uint8_t decrypted[TC_CCM_MAX_PT_SIZE]; + uint8_t ciphertext[TC_CCM_MAX_CT_SIZE]; + + uint16_t mlen = M_LEN10; + + TC_PRINT("%s: Performing CCM test #8 (no payload data):\n", __func__); + + tc_aes128_set_encrypt_key(&sched, key); + if (tc_ccm_config(&c, &sched, nonce, sizeof(nonce), mlen) == 0) { + TC_ERROR("CCM config failed in %s.\n", __func__); + + result = TC_FAIL; + goto exitTest1; + } + + result = tc_ccm_generation_encryption(ciphertext, TC_CCM_MAX_CT_SIZE, hdr, + sizeof(hdr), data, 0, &c); + if (result == 0) { + TC_ERROR("ccm_encrypt failed in %s.\n", __func__); + + result = TC_FAIL; + goto exitTest1; + } + + result = tc_ccm_decryption_verification(decrypted, TC_CCM_MAX_PT_SIZE, hdr, + sizeof(hdr), ciphertext, mlen, &c); + if (result == 0) { + TC_ERROR("ccm_decrypt failed in %s.\n", __func__); + show_str("\t\tExpected", data, sizeof(data)); + show_str("\t\tComputed", decrypted, sizeof(decrypted)); + + result = TC_FAIL; + goto exitTest1; + } + + result = TC_PASS; + +exitTest1: + TC_END_RESULT(result); + return result; +} + +/* + * Main task to test CCM + */ +int main(void) +{ + int result = TC_PASS; + + TC_START("Performing CCM tests:"); + + result = test_vector_1(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #1 (RFC 3610 test vector #1) failed.\n"); + goto exitTest; + } + result = test_vector_2(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #2 failed.\n"); + goto exitTest; + } + result = test_vector_3(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #3 failed.\n"); + goto exitTest; + } + result = test_vector_4(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #4 failed.\n"); + goto exitTest; + } + result = test_vector_5(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #5 failed.\n"); + goto exitTest; + } + result = test_vector_6(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #6 failed.\n"); + goto exitTest; + } + result = test_vector_7(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #7 failed.\n"); + goto exitTest; + } + result = test_vector_8(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CCM test #8 (no payload data) failed.\n"); + goto exitTest; + } + + TC_PRINT("All CCM tests succeeded!\n"); + +exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); + + return result; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_cmac_mode.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_cmac_mode.c new file mode 100644 index 0000000..dc757fe --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_cmac_mode.c @@ -0,0 +1,314 @@ +/* test_cmac_mode.c - TinyCrypt AES-CMAC tests (including SP 800-38B tests) */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ +/* + * DESCRIPTION + * This module tests the following AES-CMAC test (including SP 800-38B): + * + * Scenarios tested include: + * - CMAC test #1 (GF(2^128) double)) + * - CMAC test #2 null msg (SP 800-38B test vector #1) + * - CMAC test #3 1 block msg (SP 800-38B test vector #2) + * - CMAC test #4 320 bit msg (SP 800-38B test vector #3) + * - CMAC test #5 512 bit msg (SP 800-38B test vector #4) + */ + +#include +#include +#include +#include + +#include +#include + +#define BUF_LEN 16 + +static void show(const char *label, const uint8_t *s, size_t slen) +{ + unsigned int i; + + TC_PRINT("%s\t", label); + for (i = 0; i < slen; ++i) { + TC_PRINT("%02x", s[i]); + } + TC_PRINT("\n"); +} + +extern void gf_double(uint8_t *out, uint8_t *in); + +static int verify_gf_2_128_double(uint8_t *K1, uint8_t *K2, + struct tc_cmac_struct s) +{ + int result = TC_PASS; + + TC_PRINT("Performing CMAC test #1 (GF(2^128) double):\n"); + + uint8_t zero[BUF_LEN]; + uint8_t L[BUF_LEN]; + const uint8_t l[BUF_LEN] = { + 0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3, + 0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f + }; + const uint8_t k1[BUF_LEN] = { + 0xfb, 0xee, 0xd6, 0x18, 0x35, 0x71, 0x33, 0x66, + 0x7c, 0x85, 0xe0, 0x8f, 0x72, 0x36, 0xa8, 0xde + }; + const uint8_t k2[BUF_LEN] = { + 0xf7, 0xdd, 0xac, 0x30, 0x6a, 0xe2, 0x66, 0xcc, + 0xf9, 0x0b, 0xc1, 0x1e, 0xe4, 0x6d, 0x51, 0x3b + }; + + (void) memset(zero, '\0', sizeof(zero)); + tc_aes_encrypt(L, zero, s.sched); + if (memcmp(L, l, BUF_LEN) != 0) { + TC_ERROR("%s: AES encryption failed\n", __func__); + show("expected L =", l, sizeof(l)); + show("computed L =", L, sizeof(L)); + return TC_FAIL; + } + + gf_double(K1, L); + if (memcmp(K1, k1, BUF_LEN) != 0) { + TC_ERROR("%s: gf_2_128_double failed when msb = 0\n", __func__); + show("expected K1 =", k1, sizeof(k1)); + show("computed K1 =", K1, sizeof(k1)); + return TC_FAIL; + } + + gf_double(K2, K1); + if (memcmp(K2, k2, BUF_LEN) != 0) { + TC_ERROR("%s: gf_2_128_double failed when msb = 1\n", __func__); + show("expected K2 =", k2, sizeof(k2)); + show("computed K2 =", K2, sizeof(k2)); + return TC_FAIL; + } + + TC_END_RESULT(result); + return result; +} + +static int verify_cmac_null_msg(TCCmacState_t s) +{ + int result = TC_PASS; + + TC_PRINT("Performing CMAC test #2 (SP 800-38B test vector #1):\n"); + + const uint8_t tag[BUF_LEN] = { + 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 + }; + uint8_t Tag[BUF_LEN]; + + (void) tc_cmac_init(s); + (void) tc_cmac_update(s, (const uint8_t *) 0, 0); + (void) tc_cmac_final(Tag, s); + + if (memcmp(Tag, tag, BUF_LEN) != 0) { + TC_ERROR("%s: aes_cmac failed with null msg = 1\n", __func__); + show("expected Tag =", tag, sizeof(tag)); + show("computed Tag =", Tag, sizeof(Tag)); + return TC_FAIL; + } + + TC_END_RESULT(result); + return result; +} + +static int verify_cmac_1_block_msg(TCCmacState_t s) +{ + int result = TC_PASS; + + TC_PRINT("Performing CMAC test #3 (SP 800-38B test vector #2):\n"); + + const uint8_t msg[BUF_LEN] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a + }; + const uint8_t tag[BUF_LEN] = { + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c + }; + uint8_t Tag[BUF_LEN]; + + (void) tc_cmac_init(s); + (void) tc_cmac_update(s, msg, sizeof(msg)); + (void) tc_cmac_final(Tag, s); + + if (memcmp(Tag, tag, BUF_LEN) != 0) { + TC_ERROR("%s: aes_cmac failed with 1 block msg\n", __func__); + show("aes_cmac failed with 1 block msg =", msg, sizeof(msg)); + show("expected Tag =", tag, sizeof(tag)); + show("computed Tag =", Tag, sizeof(Tag)); + return TC_FAIL; + } + + TC_END_RESULT(result); + return result; +} + +static int verify_cmac_320_bit_msg(TCCmacState_t s) +{ + int result = TC_PASS; + + TC_PRINT("Performing CMAC test #4 (SP 800-38B test vector #3):\n"); + + const uint8_t msg[40] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 + }; + const uint8_t tag[BUF_LEN] = { + 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 + }; + uint8_t Tag[BUF_LEN]; + + (void) tc_cmac_init(s); + (void) tc_cmac_update(s, msg, sizeof(msg)); + (void) tc_cmac_final(Tag, s); + + if (memcmp(Tag, tag, BUF_LEN) != 0) { + TC_ERROR("%s: aes_cmac failed with 320 bit msg\n", __func__); + show("aes_cmac failed with 320 bit msg =", msg, sizeof(msg)); + show("expected Tag =", tag, sizeof(tag)); + show("computed Tag =", Tag, sizeof(Tag)); + return TC_FAIL; + } + + TC_END_RESULT(result); + return result; +} + +static int verify_cmac_512_bit_msg(TCCmacState_t s) +{ + int result = TC_PASS; + + TC_PRINT("Performing CMAC test #5 (SP 800-38B test vector #4)\n"); + + const uint8_t msg[64] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 + }; + const uint8_t tag[BUF_LEN] = { + 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe + }; + uint8_t Tag[BUF_LEN]; + + (void)tc_cmac_init(s); + (void)tc_cmac_update(s, msg, sizeof(msg)); + (void)tc_cmac_final(Tag, s); + + if (memcmp(Tag, tag, BUF_LEN) != 0) { + TC_ERROR("%s: aes_cmac failed with 512 bit msg\n", __func__); + show("aes_cmac failed with 512 bit msg =", msg, sizeof(msg)); + show("expected Tag =", tag, sizeof(tag)); + show("computed Tag =", Tag, sizeof(Tag)); + return TC_FAIL; + } + + TC_END_RESULT(result); + return result; +} + +/* + * Main task to test CMAC + * effects: returns 1 if all tests pass + * exceptions: returns a negative value if some test fails + */ +int main(void) +{ + + int result = TC_PASS; + + struct tc_cmac_struct state; + struct tc_aes_key_sched_struct sched; + + const uint8_t key[BUF_LEN] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + uint8_t K1[BUF_LEN], K2[BUF_LEN]; + + TC_START("Performing CMAC tests:"); + + (void) tc_cmac_setup(&state, key, &sched); + result = verify_gf_2_128_double(K1, K2, state); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("CMAC test #1 (128 double) failed.\n"); + goto exitTest; + } + (void) tc_cmac_setup(&state, key, &sched); + result = verify_cmac_null_msg(&state); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("CMAC test #2 (null msg) failed.\n"); + goto exitTest; + } + (void) tc_cmac_setup(&state, key, &sched); + result = verify_cmac_1_block_msg(&state); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("CMAC test #3 (1 block msg)failed.\n"); + goto exitTest; + } + (void) tc_cmac_setup(&state, key, &sched); + result = verify_cmac_320_bit_msg(&state); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("CMAC test #4 (320 bit msg) failed.\n"); + goto exitTest; + } + (void) tc_cmac_setup(&state, key, &sched); + result = verify_cmac_512_bit_msg(&state); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("CMAC test #5 (512 bit msg)failed.\n"); + goto exitTest; + } + + TC_PRINT("All CMAC tests succeeded!\n"); + +exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); + + return result; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_mode.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_mode.c new file mode 100644 index 0000000..f323856 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_mode.c @@ -0,0 +1,150 @@ +/* test_ctr_mode.c - TinyCrypt implementation of some AES-CTR tests */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ + +/* + DESCRIPTION + This module tests the following AES-CTR Mode routines: + + Scenarios tested include: + - AES128 CTR mode encryption SP 800-38a tests +*/ + +#include +#include +#include +#include + +#include +#include +#include + +/* + * NIST SP 800-38a CTR Test for encryption and decryption. + */ +unsigned int test_1_and_2(void) +{ + const uint8_t key[16] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, + 0x09, 0xcf, 0x4f, 0x3c + }; + uint8_t ctr[16] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff + }; + const uint8_t plaintext[64] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, + 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, + 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, + 0xe6, 0x6c, 0x37, 0x10 + }; + const uint8_t ciphertext[80] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff, 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, + 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, 0x98, 0x06, 0xf6, 0x6b, + 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, + 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, + 0x0d, 0xb0, 0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, + 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee + }; + struct tc_aes_key_sched_struct sched; + uint8_t out[80]; + uint8_t decrypted[64]; + unsigned int result = TC_PASS; + uint32_t off = 0; + + TC_PRINT("CTR test #1 (encryption SP 800-38a tests):\n"); + (void)tc_aes128_set_encrypt_key(&sched, key); + + (void)memcpy(out, ctr, sizeof(ctr)); + if (tc_ctr_mode(&out[TC_AES_BLOCK_SIZE], sizeof(plaintext), plaintext, + sizeof(plaintext), ctr, &off, &sched) == 0) { + TC_ERROR("CTR test #1 (encryption SP 800-38a tests) failed in %s.\n", __func__); + result = TC_FAIL; + goto exitTest1; + } + + if (off != 0) { + TC_ERROR("CTR test #1 invalid block offset (%u).\n", off); + result = TC_FAIL; + goto exitTest1; + } + result = check_result(1, ciphertext, sizeof(out), out, sizeof(out)); + TC_END_RESULT(result); + + TC_PRINT("CTR test #2 (decryption SP 800-38a tests):\n"); + (void) memcpy(ctr, out, sizeof(ctr)); + off = 0; + if (tc_ctr_mode(decrypted, sizeof(decrypted), &out[TC_AES_BLOCK_SIZE], + sizeof(decrypted), ctr, &off, &sched) == 0) { + TC_ERROR("CTR test #2 (decryption SP 800-38a tests) failed in %s.\n", __func__); + result = TC_FAIL; + goto exitTest1; + } + + if (off != 0) { + TC_ERROR("CTR test #2 invalid block offset (%u).\n", off); + result = TC_FAIL; + goto exitTest1; + } + result = check_result(2, plaintext, sizeof(plaintext), + decrypted, sizeof(plaintext)); + + exitTest1: + TC_END_RESULT(result); + return result; +} + +/* + * Main task to test AES + */ + +int main(void) +{ + unsigned int result = TC_PASS; + + TC_START("Performing AES128-CTR mode tests:"); + + TC_PRINT("Performing CTR tests:\n"); + result = test_1_and_2(); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("CTR test #1 failed.\n"); + goto exitTest; + } + + TC_PRINT("All CTR tests succeeded!\n"); + + exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_prng.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_prng.c new file mode 100644 index 0000000..e95f7f1 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_ctr_prng.c @@ -0,0 +1,565 @@ +/* test_ctr_prng.c - TinyCrypt implementation of some CTR-PRNG tests */ + +/* + * Copyright (c) 2016, Chris Morrison, 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 OWNER 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. + */ + +/* + DESCRIPTION + This module tests the CTR-PRNG routines +*/ + +#include +#include +#include +#include + + +#include +#include +#include + +/* utility function to convert hex character representation to their nibble (4 bit) values */ +static uint8_t nibbleFromChar(char c) +{ + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'a' && c <= 'f') return c - 'a' + 10U; + if(c >= 'A' && c <= 'F') return c - 'A' + 10U; + return 255U; +} + +/* + * Convert a string of characters representing a hex buffer into a series of + * bytes of that real value + */ +uint8_t *hexStringToBytes(char *inhex) +{ + uint8_t *retval; + uint8_t *p; + int len, i; + + len = strlen(inhex) / 2; + retval = (uint8_t *)malloc(len+1); + for(i=0, p = (uint8_t *) inhex; i +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int ecdh_vectors(char **qx_vec, char **qy_vec, char **d_vec, char **z_vec, + int tests, int verbose) +{ + + unsigned int pub[2*NUM_ECC_WORDS]; + unsigned int prv[NUM_ECC_WORDS]; + unsigned int z[NUM_ECC_WORDS]; + unsigned int result = TC_PASS; + + int rc; + unsigned int exp_z[NUM_ECC_WORDS]; + + const struct uECC_Curve_t * curve = uECC_secp256r1(); + + for (int i = 0; i < tests; i++) { + + string2scalar(pub + NUM_ECC_WORDS, NUM_ECC_WORDS, qx_vec[i]); + string2scalar(pub, NUM_ECC_WORDS, qy_vec[i]); + string2scalar(exp_z, NUM_ECC_WORDS, z_vec[i]); + string2scalar(prv, NUM_ECC_WORDS, d_vec[i]); + + uint8_t pub_bytes[2*NUM_ECC_BYTES]; + uECC_vli_nativeToBytes(pub_bytes, 2*NUM_ECC_BYTES, pub); + uint8_t private_bytes[NUM_ECC_BYTES]; + uECC_vli_nativeToBytes(private_bytes, NUM_ECC_BYTES, prv); + uint8_t z_bytes[NUM_ECC_BYTES]; + uECC_vli_nativeToBytes(z_bytes, NUM_ECC_BYTES, exp_z); + + rc = uECC_shared_secret(pub_bytes, private_bytes, z_bytes, curve); + + if (rc == TC_CRYPTO_FAIL) { + TC_ERROR("ECDH failure, exit.\n"); + result = TC_FAIL; + return result;; + } + + uECC_vli_bytesToNative(z, z_bytes, NUM_ECC_BYTES); + + result = check_ecc_result(i, "Z", exp_z, z, NUM_ECC_WORDS, verbose); + if (result == TC_FAIL) { + return result; + } + } + return result; +} + +int cavp_ecdh(bool verbose) +{ + unsigned int result = TC_PASS; + /* + * P-256 + */ + char *d[] = { + "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", + "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5", + "1accfaf1b97712b85a6f54b148985a1bdc4c9bec0bd258cad4b3d603f49f32c8", + "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d", + "59137e38152350b195c9718d39673d519838055ad908dd4757152fd8255c09bf", + "f5f8e0174610a661277979b58ce5c90fee6c9b3bb346a90a7196255e40b132ef", + "3b589af7db03459c23068b64f63f28d3c3c6bc25b5bf76ac05f35482888b5190", + "d8bf929a20ea7436b2461b541a11c80e61d826c0a4c9d322b31dd54e7f58b9c8", + "0f9883ba0ef32ee75ded0d8bda39a5146a29f1f2507b3bd458dbea0b2bb05b4d", + "2beedb04b05c6988f6a67500bb813faf2cae0d580c9253b6339e4a3337bb6c08", + "77c15dcf44610e41696bab758943eff1409333e4d5a11bbe72c8f6c395e9f848", + "42a83b985011d12303db1a800f2610f74aa71cdf19c67d54ce6c9ed951e9093e", + "ceed35507b5c93ead5989119b9ba342cfe38e6e638ba6eea343a55475de2800b", + "43e0e9d95af4dc36483cdd1968d2b7eeb8611fcce77f3a4e7d059ae43e509604", + "b2f3600df3368ef8a0bb85ab22f41fc0e5f4fdd54be8167a5c3cd4b08db04903", + "4002534307f8b62a9bf67ff641ddc60fef593b17c3341239e95bdb3e579bfdc8", + "4dfa12defc60319021b681b3ff84a10a511958c850939ed45635934ba4979147", + "1331f6d874a4ed3bc4a2c6e9c74331d3039796314beee3b7152fcdba5556304e", + "dd5e9f70ae740073ca0204df60763fb6036c45709bf4a7bb4e671412fad65da3", + "5ae026cfc060d55600717e55b8a12e116d1d0df34af831979057607c2d9c2f76", + "b601ac425d5dbf9e1735c5e2d5bdb79ca98b3d5be4a2cfd6f2273f150e064d9d", + "fefb1dda1845312b5fce6b81b2be205af2f3a274f5a212f66c0d9fc33d7ae535", + "334ae0c4693d23935a7e8e043ebbde21e168a7cba3fa507c9be41d7681e049ce", + "2c4bde40214fcc3bfc47d4cf434b629acbe9157f8fd0282540331de7942cf09d", + "85a268f9d7772f990c36b42b0a331adc92b5941de0b862d5d89a347cbf8faab0", + }; + + char *x[] = { + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287", + "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae", + "a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3", + "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed", + "41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922", + "33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792", + "6a9e0c3f916e4e315c91147be571686d90464e8bf981d34a90b6353bca6eeba7", + "a9c0acade55c2a73ead1a86fb0a9713223c82475791cd0e210b046412ce224bb", + "94e94f16a98255fff2b9ac0c9598aac35487b3232d3231bd93b7db7df36f9eb9", + "e099bf2a4d557460b5544430bbf6da11004d127cb5d67f64ab07c94fcdf5274f", + "f75a5fe56bda34f3c1396296626ef012dc07e4825838778a645c8248cff01658", + "2db4540d50230756158abf61d9835712b6486c74312183ccefcaef2797b7674d", + "cd94fc9497e8990750309e9a8534fd114b0a6e54da89c4796101897041d14ecb", + "15b9e467af4d290c417402e040426fe4cf236bae72baa392ed89780dfccdb471", + "49c503ba6c4fa605182e186b5e81113f075bc11dcfd51c932fb21e951eee2fa1", + "19b38de39fdd2f70f7091631a4f75d1993740ba9429162c2a45312401636b29c", + "2c91c61f33adfe9311c942fdbff6ba47020feff416b7bb63cec13faf9b099954", + "a28a2edf58025668f724aaf83a50956b7ac1cfbbff79b08c3bf87dfd2828d767", + "a2ef857a081f9d6eb206a81c4cf78a802bdf598ae380c8886ecd85fdc1ed7644", + "ccd8a2d86bc92f2e01bce4d6922cf7fe1626aed044685e95e2eebd464505f01f", + "c188ffc8947f7301fb7b53e36746097c2134bf9cc981ba74b4e9c4361f595e4e", + "317e1020ff53fccef18bf47bb7f2dd7707fb7b7a7578e04f35b3beed222a0eb6", + "45fb02b2ceb9d7c79d9c2fa93e9c7967c2fa4df5789f9640b24264b1e524fcb1", + "a19ef7bff98ada781842fbfc51a47aff39b5935a1c7d9625c8d323d511c92de6", + "356c5a444c049a52fee0adeb7e5d82ae5aa83030bfff31bbf8ce2096cf161c4b", + }; + + char *y[] = { + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", + "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", + "ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536", + "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4", + "1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328", + "f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f", + "40f9bead39c2f2bcc2602f75b8a73ec7bdffcbcead159d0174c6c4d3c5357f05", + "f6de0afa20e93e078467c053d241903edad734c6b403ba758c2b5ff04c9d4229", + "d8049a43579cfa90b8093a94416cbefbf93386f15b3f6e190b6e3455fedfe69a", + "d9c50dbe70d714edb5e221f4e020610eeb6270517e688ca64fb0e98c7ef8c1c5", + "33bbdf1b1772d8059df568b061f3f1122f28a8d819167c97be448e3dc3fb0c3c", + "62f57f314e3f3495dc4e099012f5e0ba71770f9660a1eada54104cdfde77243e", + "c3def4b5fe04faee0a11932229fff563637bfdee0e79c6deeaf449f85401c5c4", + "cdf4e9170fb904302b8fd93a820ba8cc7ed4efd3a6f2d6b05b80b2ff2aee4e77", + "8af706ff0922d87b3f0c5e4e31d8b259aeb260a9269643ed520a13bb25da5924", + "09aed7232b28e060941741b6828bcdfa2bc49cc844f3773611504f82a390a5ae", + "6cab31b06419e5221fca014fb84ec870622a1b12bab5ae43682aa7ea73ea08d0", + "dfa7bfffd4c766b86abeaf5c99b6e50cb9ccc9d9d00b7ffc7804b0491b67bc03", + "563c4c20419f07bc17d0539fade1855e34839515b892c0f5d26561f97fa04d1a", + "e9ddd583a9635a667777d5b8a8f31b0f79eba12c75023410b54b8567dddc0f38", + "bf7d2f2056e72421ef393f0c0f2b0e00130e3cac4abbcc00286168e85ec55051", + "09420ce5a19d77c6fe1ee587e6a49fbaf8f280e8df033d75403302e5a27db2ae", + "5c6e8ecf1f7d3023893b7b1ca1e4d178972ee2a230757ddc564ffe37f5c5a321", + "e9c184df75c955e02e02e400ffe45f78f339e1afe6d056fb3245f4700ce606ef", + "57d128de8b2a57a094d1a001e572173f96e8866ae352bf29cddaf92fc85b2f92", + }; + + char *Z[] = { + "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b", + "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", + "2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec", + "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024", + "19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62", + "664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3", + "ca342daa50dc09d61be7c196c85e60a80c5cb04931746820be548cdde055679d", + "35aa9b52536a461bfde4e85fc756be928c7de97923f0416c7a3ac8f88b3d4489", + "605c16178a9bc875dcbff54d63fe00df699c03e8a888e9e94dfbab90b25f39b4", + "f96e40a1b72840854bb62bc13c40cc2795e373d4e715980b261476835a092e0b", + "8388fa79c4babdca02a8e8a34f9e43554976e420a4ad273c81b26e4228e9d3a3", + "72877cea33ccc4715038d4bcbdfe0e43f42a9e2c0c3b017fc2370f4b9acbda4a", + "e4e7408d85ff0e0e9c838003f28cdbd5247cdce31f32f62494b70e5f1bc36307", + "ed56bcf695b734142c24ecb1fc1bb64d08f175eb243a31f37b3d9bb4407f3b96", + "bc5c7055089fc9d6c89f83c1ea1ada879d9934b2ea28fcf4e4a7e984b28ad2cf", + "9a4e8e657f6b0e097f47954a63c75d74fcba71a30d83651e3e5a91aa7ccd8343", + "3ca1fc7ad858fb1a6aba232542f3e2a749ffc7203a2374a3f3d3267f1fc97b78", + "1aaabe7ee6e4a6fa732291202433a237df1b49bc53866bfbe00db96a0f58224f", + "430e6a4fba4449d700d2733e557f66a3bf3d50517c1271b1ddae1161b7ac798c", + "1ce9e6740529499f98d1f1d71329147a33df1d05e4765b539b11cf615d6974d3", + "4690e3743c07d643f1bc183636ab2a9cb936a60a802113c49bb1b3f2d0661660", + "30c2261bd0004e61feda2c16aa5e21ffa8d7e7f7dbf6ec379a43b48e4b36aeb0", + "2adae4a138a239dcd93c243a3803c3e4cf96e37fe14e6a9b717be9599959b11c", + "2e277ec30f5ea07d6ce513149b9479b96e07f4b6913b1b5c11305c1444a1bc0b", + "1e51373bd2c6044c129c436e742a55be2a668a85ae08441b6756445df5493857", + }; + + TC_PRINT("Test #1: ECDH"); + TC_PRINT("NIST-p256\n"); + + result = ecdh_vectors(x, y, d, Z, 25, verbose); + if(result == TC_FAIL) { + goto exitTest1; + } + + exitTest1: + TC_END_RESULT(result); + return result; +} + +int cavp_keygen(bool verbose) +{ + unsigned int result = TC_PASS; + /* + * [P-256, B.4.2 Key Pair Generation by Testing Candidates] + */ + char *d[] = { + "c9806898a0334916c860748880a541f093b579a9b1f32934d86c363c39800357", + "710735c8388f48c684a97bd66751cc5f5a122d6b9a96a2dbe73662f78217446d", + "78d5d8b7b3e2c16b3e37e7e63becd8ceff61e2ce618757f514620ada8a11f6e4", + "2a61a0703860585fe17420c244e1de5a6ac8c25146b208ef88ad51ae34c8cb8c", + "01b965b45ff386f28c121c077f1d7b2710acc6b0cb58d8662d549391dcf5a883", + "fac92c13d374c53a085376fe4101618e1e181b5a63816a84a0648f3bdc24e519", + "f257a192dde44227b3568008ff73bcf599a5c45b32ab523b5b21ca582fef5a0a", + "add67e57c42a3d28708f0235eb86885a4ea68e0d8cfd76eb46134c596522abfd", + "4494860fd2c805c5c0d277e58f802cff6d731f76314eb1554142a637a9bc5538", + "d40b07b1ea7b86d4709ef9dc634c61229feb71abd63dc7fc85ef46711a87b210", + }; + + char *x[] = { + "d0720dc691aa80096ba32fed1cb97c2b620690d06de0317b8618d5ce65eb728f", + "f6836a8add91cb182d8d258dda6680690eb724a66dc3bb60d2322565c39e4ab9", + "76711126cbb2af4f6a5fe5665dad4c88d27b6cb018879e03e54f779f203a854e", + "e1aa7196ceeac088aaddeeba037abb18f67e1b55c0a5c4e71ec70ad666fcddc8", + "1f038c5422e88eec9e88b815e8f6b3e50852333fc423134348fc7d79ef8e8a10", + "7258f2ab96fc84ef6ccb33e308cd392d8b568ea635730ceb4ebd72fa870583b9", + "d2e01411817b5512b79bbbe14d606040a4c90deb09e827d25b9f2fc068997872", + "55bed2d9c029b7f230bde934c7124ed52b1330856f13cbac65a746f9175f85d7", + "5190277a0c14d8a3d289292f8a544ce6ea9183200e51aec08440e0c1a463a4e4", + "fbcea7c2827e0e8085d7707b23a3728823ea6f4878b24747fb4fd2842d406c73", + }; + + char *y[] = { + "9681b517b1cda17d0d83d335d9c4a8a9a9b0b1b3c7106d8f3c72bc5093dc275f", + "1f837aa32864870cb8e8d0ac2ff31f824e7beddc4bb7ad72c173ad974b289dc2", + "a26df39960ab5248fd3620fd018398e788bd89a3cea509b352452b69811e6856", + "d7d35bdce6dedc5de98a7ecb27a9cd066a08f586a733b59f5a2cdb54f971d5c8", + "43a047cb20e94b4ffb361ef68952b004c0700b2962e0c0635a70269bc789b849", + "489807ca55bdc29ca5c8fe69b94f227b0345cccdbe89975e75d385cc2f6bb1e2", + "503f138f8bab1df2c4507ff663a1fdf7f710e7adb8e7841eaa902703e314e793", + "32805e311d583b4e007c40668185e85323948e21912b6b0d2cda8557389ae7b0", + "ecd98514821bd5aaf3419ab79b71780569470e4fed3da3c1353b28fe137f36eb", + "2393c85f1f710c5afc115a39ba7e18abe03f19c9d4bb3d47d19468b818efa535", + }; + + TC_PRINT("Test #2: ECC KeyGen "); + TC_PRINT("NIST-p256\n"); + + result = keygen_vectors(d, x, y, 10, verbose); + if(result == TC_FAIL) { + goto exitTest1; + } + + exitTest1: + TC_END_RESULT(result); + return result; +} + +/* Test ecc_make_keys, and also as keygen part of other tests */ +int pkv_vectors(char **qx_vec, char **qy_vec, char **res_vec, int tests, + bool verbose) +{ + + unsigned int pub[2 * NUM_ECC_WORDS]; + uint8_t _public[2 * NUM_ECC_BYTES]; + int rc; + int exp_rc; + char tmp; + unsigned int result = TC_PASS; + const struct uECC_Curve_t * curve = uECC_secp256r1(); + + for (int i = 0; i < tests; i++) { + + if (2 != sscanf(res_vec[i], "%c (%d ", &tmp, &exp_rc)) { + TC_ERROR("Error: failed to parse CAVP response.\n"); + result = TC_FAIL; + goto exitTest1; + } + + if (strlen(qx_vec[i]) > 2 * NUM_ECC_BYTES || + strlen(qy_vec[i]) > 2 * NUM_ECC_BYTES) { + /* invalid input to ECC digit conversion (string2native()) */ + rc = -2; + } else { + string2scalar(pub, NUM_ECC_WORDS, qx_vec[i]); + string2scalar(pub + NUM_ECC_WORDS, NUM_ECC_WORDS, qy_vec[i]); + + uECC_vli_nativeToBytes(_public, NUM_ECC_BYTES, pub); + uECC_vli_nativeToBytes(_public + NUM_ECC_BYTES, NUM_ECC_BYTES, pub+NUM_ECC_WORDS); + + rc = uECC_valid_public_key(_public, curve); + } + + /* + * map to CAVP error codes + * 0 => 0 - success + * -1 => ? - (x,y) = (0,0) (not covered) + * -2 => 1 - out of bounds (pubverify or ecc import) + * -3 => 2 - not on curve + * -4 => ? - public key is the group generator + */ + + if (rc == -3) rc = 2; + if (rc == -2) rc = 1; + + result = check_code(i, res_vec[i], exp_rc, rc, verbose); + if(result == TC_FAIL) { + goto exitTest1; + } + } + + exitTest1: + TC_END_RESULT(result); + return result; +} + +int cavp_pkv(bool verbose) +{ + /* + * [P-256] + */ + char *x[] = { + "e0f7449c5588f24492c338f2bc8f7865f755b958d48edb0f2d0056e50c3fd5b7", + "d17c446237d9df87266ba3a91ff27f45abfdcb77bfd83536e92903efb861a9a9", + "17875397ae87369365656d490e8ce956911bd97607f2aff41b56f6f3a61989826", + "f2d1c0dc0852c3d8a2a2500a23a44813ccce1ac4e58444175b440469ffc12273", + "10b0ca230fff7c04768f4b3d5c75fa9f6c539bea644dffbec5dc796a213061b58", + "2c1052f25360a15062d204a056274e93cbe8fc4c4e9b9561134ad5c15ce525da", + "a40d077a87dae157d93dcccf3fe3aca9c6479a75aa2669509d2ef05c7de6782f", + "2633d398a3807b1895548adbb0ea2495ef4b930f91054891030817df87d4ac0a", + "14bf57f76c260b51ec6bbc72dbd49f02a56eaed070b774dc4bad75a54653c3d56", + "2fa74931ae816b426f484180e517f5050c92decfc8daf756cd91f54d51b302f1", + "f8c6dd3181a76aa0e36c2790bba47041acbe7b1e473ff71eee39a824dc595ff0", + "7a81a7e0b015252928d8b36e4ca37e92fdc328eb25c774b4f872693028c4be38", + }; + + char *y[] = { + "86d7e9255d0f4b6f44fa2cd6f8ba3c0aa828321d6d8cc430ca6284ce1d5b43a0", + "1eabb6a349ce2cd447d777b6739c5fc066add2002d2029052c408d0701066231c", + "980a3c4f61b9692633fbba5ef04c9cb546dd05cdec9fa8428b8849670e2fba92", + "32bfe992831b305d8c37b9672df5d29fcb5c29b4a40534683e3ace23d24647dd", + "f5edf37c11052b75f771b7f9fa050e353e464221fec916684ed45b6fead38205", + "ced9783713a8a2a09eff366987639c625753295d9a85d0f5325e32dedbcada0b", + "503d86b87d743ba20804fd7e7884aa017414a7b5b5963e0d46e3a9611419ddf3", + "d6b2f738e3873cc8364a2d364038ce7d0798bb092e3dd77cbdae7c263ba618d2", + "7a231a23bf8b3aa31d9600d888a0678677a30e573decd3dc56b33f365cc11236", + "5b994346137988c58c14ae2152ac2f6ad96d97decb33099bd8a0210114cd1141", + "9c965f227f281b3072b95b8daf29e88b35284f3574462e268e529bbdc50e9e52", + "08862f7335147261e7b1c3d055f9a316e4cab7daf99cc09d1c647f5dd6e7d5bb", + }; + + char *Result[] = { + "P (0 )", "F (1 - Q_x or Q_y out of range)", + "F (1 - Q_x or Q_y out of range)", "F (2 - Point not on curve)", + "F (1 - Q_x or Q_y out of range)", "P (0 )", + "F (2 - Point not on curve)", "P (0 )", + "F (1 - Q_x or Q_y out of range)", "P (0 )", + "F (2 - Point not on curve)", "F (2 - Point not on curve)", + }; + + TC_PRINT("Test #3: PubKeyVerify "); + TC_PRINT("NIST-p256-SHA2-256\n"); + + return pkv_vectors(x, y, Result, 12, verbose); +} + +int montecarlo_ecdh(int num_tests, bool verbose) +{ + int i; + uint8_t private1[NUM_ECC_BYTES] = {0}; + uint8_t private2[NUM_ECC_BYTES] = {0}; + uint8_t public1[2*NUM_ECC_BYTES] = {0}; + uint8_t public2[2*NUM_ECC_BYTES] = {0}; + uint8_t secret1[NUM_ECC_BYTES] = {0}; + uint8_t secret2[NUM_ECC_BYTES] = {0}; + unsigned int result = TC_PASS; + + const struct uECC_Curve_t * curve = uECC_secp256r1(); + + TC_PRINT("Test #4: Monte Carlo (%d Randomized EC-DH key-exchange) ", num_tests); + TC_PRINT("NIST-p256\n "); + + for (i = 0; i < num_tests; ++i) { + if (verbose) { + TC_PRINT("."); + fflush(stdout); + } + + if (!uECC_make_key(public1, private1, curve) || + !uECC_make_key(public2, private2, curve)) { + TC_ERROR("uECC_make_key() failed\n"); + result = TC_FAIL; + goto exitTest1; + } + + if (!uECC_shared_secret(public2, private1, secret1, curve)) { + TC_ERROR("shared_secret() failed (1)\n"); + result = TC_FAIL; + goto exitTest1;; + } + + if (!uECC_shared_secret(public1, private2, secret2, curve)) { + TC_ERROR("shared_secret() failed (2)\n"); + result = TC_FAIL; + goto exitTest1; + } + + if (memcmp(secret1, secret2, sizeof(secret1)) != 0) { + TC_PRINT("Shared secrets are not identical!\n"); + TC_PRINT("Private key 1 = "); + vli_print_bytes(private1, 32); + TC_PRINT("\nPrivate key 2 = "); + vli_print_bytes(private2, 32); + TC_PRINT("\nPublic key 1 = "); + vli_print_bytes(public1, 64); + TC_PRINT("\nPublic key 2 = "); + vli_print_bytes(public2, 64); + TC_PRINT("\nShared secret 1 = "); + vli_print_bytes(secret1, 32); + TC_PRINT("\nShared secret 2 = "); + vli_print_bytes(secret2, 32); + TC_PRINT("\n"); + } + } + + TC_PRINT("\n"); + + exitTest1: + TC_END_RESULT(result); + return result; +} + +int main() +{ + unsigned int result = TC_PASS; + + TC_START("Performing ECC-DH tests:"); + + /* Setup of the Cryptographically Secure PRNG. */ + uECC_set_rng(&default_CSPRNG); + + bool verbose = true; + + TC_PRINT("Performing cavp_ecdh test:\n"); + result = cavp_ecdh(verbose); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("cavp_ecdh test failed.\n"); + goto exitTest; + } + TC_PRINT("Performing cavp_keygen test:\n"); + result = cavp_keygen(verbose); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("cavp_keygen test failed.\n"); + goto exitTest; + } + TC_PRINT("Performing cavp_pkv test:\n"); + result = cavp_pkv(verbose); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("cavp_pkv failed.\n"); + goto exitTest; + } + TC_PRINT("Performing montecarlo_ecdh test:\n"); + result = montecarlo_ecdh(10, verbose); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("montecarlo_ecdh test failed.\n"); + goto exitTest; + } + + TC_PRINT("All EC-DH tests succeeded!\n"); + + exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_dsa.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_dsa.c new file mode 100644 index 0000000..d1e933c --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_dsa.c @@ -0,0 +1,669 @@ +/* test_ecc_ecdsa.c - TinyCrypt implementation of some EC-DSA tests */ + +/* 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.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + * test_ecc_ecdsa.c -- Implementation of some EC-DSA tests + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Maximum size of message to be signed. */ +#define BUF_SIZE 256 + +int sign_vectors(TCSha256State_t hash, char **d_vec, char **k_vec, + char **msg_vec, char **qx_vec, char **qy_vec, char **r_vec, + char **s_vec, int tests, bool verbose) +{ + + unsigned int k[NUM_ECC_WORDS]; + unsigned int private[NUM_ECC_WORDS]; + uint8_t private_bytes[NUM_ECC_BYTES]; + unsigned int sig[2 * NUM_ECC_WORDS]; + uint8_t sig_bytes[2 * NUM_ECC_BYTES]; + unsigned int digest[TC_SHA256_DIGEST_SIZE / 4]; + uint8_t digest_bytes[TC_SHA256_DIGEST_SIZE]; + unsigned int result = TC_PASS; + + /* expected outputs (converted input vectors) */ + unsigned int exp_r[NUM_ECC_WORDS]; + unsigned int exp_s[NUM_ECC_WORDS]; + + uint8_t msg[BUF_SIZE]; + size_t msglen; + + for (int i = 0; i < tests; i++) { + + /* use keygen test to generate and validate pubkey */ + keygen_vectors(d_vec+i, qx_vec+i, qy_vec+i, 1, false); + string2scalar(private, NUM_ECC_WORDS, d_vec[i]); + uECC_vli_nativeToBytes(private_bytes, NUM_ECC_BYTES, private); + + /* validate ECDSA: hash message, sign digest, check r+s */ + memset(k, 0, NUM_ECC_BYTES); + string2scalar(k, NUM_ECC_WORDS, k_vec[i]); + string2scalar(exp_r, NUM_ECC_WORDS, r_vec[i]); + string2scalar(exp_s, NUM_ECC_WORDS, s_vec[i]); + + msglen = hex2bin(msg, BUF_SIZE, msg_vec[i], strlen(msg_vec[i])); + + if (msglen == false) { + TC_ERROR("failed to import message!\n"); + result = TC_FAIL; + goto exitTest1; + } + + tc_sha256_init(hash); + tc_sha256_update(hash, msg, msglen); + tc_sha256_final(digest_bytes, hash); + + /* if digest larger than ECC scalar, drop the end + * if digest smaller than ECC scalar, zero-pad front */ + int hash_dwords = TC_SHA256_DIGEST_SIZE / 4; + if (NUM_ECC_WORDS < hash_dwords) { + hash_dwords = NUM_ECC_WORDS; + } + + memset(digest, 0, NUM_ECC_BYTES - 4 * hash_dwords); + uECC_vli_bytesToNative(digest + (NUM_ECC_WORDS-hash_dwords), + digest_bytes, TC_SHA256_DIGEST_SIZE); + + if (uECC_sign_with_k(private_bytes, digest_bytes, + sizeof(digest_bytes), k, sig_bytes, uECC_secp256r1()) == 0) { + TC_ERROR("ECDSA_sign failed!\n"); + result = TC_FAIL; + goto exitTest1; + } + + uECC_vli_bytesToNative(sig, sig_bytes, NUM_ECC_BYTES); + uECC_vli_bytesToNative(sig + NUM_ECC_WORDS, sig_bytes+NUM_ECC_BYTES, NUM_ECC_BYTES); + + result = check_ecc_result(i, "sig.r", exp_r, sig, NUM_ECC_WORDS, verbose); + if(result == TC_FAIL) { + goto exitTest1; + } + result = check_ecc_result(i, "sig.s", exp_s, sig + NUM_ECC_WORDS, NUM_ECC_WORDS, verbose); + if(result == TC_FAIL) { + goto exitTest1; + } + } + + exitTest1: + TC_END_RESULT(result); + return result; +} + +int cavp_sign(bool verbose) +{ + + /* + * [P-256,SHA-256] + */ + char *d[] = { + "519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464", + "0f56db78ca460b055c500064824bed999a25aaf48ebb519ac201537b85479813", + "e283871239837e13b95f789e6e1af63bf61c918c992e62bca040d64cad1fc2ef", + "a3d2d3b7596f6592ce98b4bfe10d41837f10027a90d7bb75349490018cf72d07", + "53a0e8a8fe93db01e7ae94e1a9882a102ebd079b3a535827d583626c272d280d", + "4af107e8e2194c830ffb712a65511bc9186a133007855b49ab4b3833aefc4a1d", + "78dfaa09f1076850b3e206e477494cddcfb822aaa0128475053592c48ebaf4ab", + "80e692e3eb9fcd8c7d44e7de9f7a5952686407f90025a1d87e52c7096a62618a", + "5e666c0db0214c3b627a8e48541cc84a8b6fd15f300da4dff5d18aec6c55b881", + "f73f455271c877c4d5334627e37c278f68d143014b0a05aa62f308b2101c5308", + "b20d705d9bd7c2b8dc60393a5357f632990e599a0975573ac67fd89b49187906", + "d4234bebfbc821050341a37e1240efe5e33763cbbb2ef76a1c79e24724e5a5e7", + "b58f5211dff440626bb56d0ad483193d606cf21f36d9830543327292f4d25d8c", + "54c066711cdb061eda07e5275f7e95a9962c6764b84f6f1f3ab5a588e0a2afb1", + "34fa4682bf6cb5b16783adcd18f0e6879b92185f76d7c920409f904f522db4b1", + }; + + char *k[] = { + "94a1bbb14b906a61a280f245f9e93c7f3b4a6247824f5d33b9670787642a68de", + "6d3e71882c3b83b156bb14e0ab184aa9fb728068d3ae9fac421187ae0b2f34c6", + "ad5e887eb2b380b8d8280ad6e5ff8a60f4d26243e0124c2f31a297b5d0835de2", + "24fc90e1da13f17ef9fe84cc96b9471ed1aaac17e3a4bae33a115df4e5834f18", + "5d833e8d24cc7a402d7ee7ec852a3587cddeb48358cea71b0bedb8fabe84e0c4", + "e18f96f84dfa2fd3cdfaec9159d4c338cd54ad314134f0b31e20591fc238d0ab", + "295544dbb2da3da170741c9b2c6551d40af7ed4e891445f11a02b66a5c258a77", + "7c80fd66d62cc076cef2d030c17c0a69c99611549cb32c4ff662475adbe84b22", + "2e7625a48874d86c9e467f890aaa7cd6ebdf71c0102bfdcfa24565d6af3fdce9", + "62f8665fd6e26b3fa069e85281777a9b1f0dfd2c0b9f54a086d0c109ff9fd615", + "72b656f6b35b9ccbc712c9f1f3b1a14cbbebaec41c4bca8da18f492a062d6f6f", + "d926fe10f1bfd9855610f4f5a3d666b1a149344057e35537373372ead8b1a778", + "e158bf4a2d19a99149d9cdb879294ccb7aaeae03d75ddd616ef8ae51a6dc1071", + "646fe933e96c3b8f9f507498e907fdd201f08478d0202c752a7c2cfebf4d061a", + "a6f463ee72c9492bc792fe98163112837aebd07bab7a84aaed05be64db3086f4", + }; + + char *Msg[] = { + "5905238877c77421f73e43ee3da6f2d9e2ccad5fc942dcec0cbd25482935faaf416983fe16" + "5b1a045ee2bcd2e6dca3bdf46c4310a7461f9a37960ca672d3feb5473e253605fb1ddfd280" + "65b53cb5858a8ad28175bf9bd386a5e471ea7a65c17cc934a9d791e91491eb3754d0379979" + "0fe2d308d16146d5c9b0d0debd97d79ce8", + "c35e2f092553c55772926bdbe87c9796827d17024dbb9233a545366e2e5987dd344deb72df" + "987144b8c6c43bc41b654b94cc856e16b96d7a821c8ec039b503e3d86728c494a967d83011" + "a0e090b5d54cd47f4e366c0912bc808fbb2ea96efac88fb3ebec9342738e225f7c7c2b011c" + "e375b56621a20642b4d36e060db4524af1", + "3c054e333a94259c36af09ab5b4ff9beb3492f8d5b4282d16801daccb29f70fe61a0b37ffe" + "f5c04cd1b70e85b1f549a1c4dc672985e50f43ea037efa9964f096b5f62f7ffdf8d6bfb2cc" + "859558f5a393cb949dbd48f269343b5263dcdb9c556eca074f2e98e6d94c2c29a677afaf80" + "6edf79b15a3fcd46e7067b7669f83188ee", + "0989122410d522af64ceb07da2c865219046b4c3d9d99b01278c07ff63eaf1039cb787ae9e" + "2dd46436cc0415f280c562bebb83a23e639e476a02ec8cff7ea06cd12c86dcc3adefbf1a9e" + "9a9b6646c7599ec631b0da9a60debeb9b3e19324977f3b4f36892c8a38671c8e1cc8e50fcd" + "50f9e51deaf98272f9266fc702e4e57c30", + "dc66e39f9bbfd9865318531ffe9207f934fa615a5b285708a5e9c46b7775150e818d7f24d2" + "a123df3672fff2094e3fd3df6fbe259e3989dd5edfcccbe7d45e26a775a5c4329a084f057c" + "42c13f3248e3fd6f0c76678f890f513c32292dd306eaa84a59abe34b16cb5e38d0e885525d" + "10336ca443e1682aa04a7af832b0eee4e7", + "600974e7d8c5508e2c1aab0783ad0d7c4494ab2b4da265c2fe496421c4df238b0be25f2565" + "9157c8a225fb03953607f7df996acfd402f147e37aee2f1693e3bf1c35eab3ae360a2bd91d" + "04622ea47f83d863d2dfecb618e8b8bdc39e17d15d672eee03bb4ce2cc5cf6b217e5faf3f3" + "36fdd87d972d3a8b8a593ba85955cc9d71", + "dfa6cb9b39adda6c74cc8b2a8b53a12c499ab9dee01b4123642b4f11af336a91a5c9ce0520" + "eb2395a6190ecbf6169c4cba81941de8e76c9c908eb843b98ce95e0da29c5d4388040264e0" + "5e07030a577cc5d176387154eabae2af52a83e85c61c7c61da930c9b19e45d7e34c8516dc3" + "c238fddd6e450a77455d534c48a152010b", + "51d2547cbff92431174aa7fc7302139519d98071c755ff1c92e4694b58587ea560f72f32fc" + "6dd4dee7d22bb7387381d0256e2862d0644cdf2c277c5d740fa089830eb52bf79d1e75b859" + "6ecf0ea58a0b9df61e0c9754bfcd62efab6ea1bd216bf181c5593da79f10135a9bc6e164f1" + "854bc8859734341aad237ba29a81a3fc8b", + "558c2ac13026402bad4a0a83ebc9468e50f7ffab06d6f981e5db1d082098065bcff6f21a7a" + "74558b1e8612914b8b5a0aa28ed5b574c36ac4ea5868432a62bb8ef0695d27c1e3ceaf75c7" + "b251c65ddb268696f07c16d2767973d85beb443f211e6445e7fe5d46f0dce70d58a4cd9fe7" + "0688c035688ea8c6baec65a5fc7e2c93e8", + "4d55c99ef6bd54621662c3d110c3cb627c03d6311393b264ab97b90a4b15214a5593ba2510" + "a53d63fb34be251facb697c973e11b665cb7920f1684b0031b4dd370cb927ca7168b0bf8ad" + "285e05e9e31e34bc24024739fdc10b78586f29eff94412034e3b606ed850ec2c1900e8e681" + "51fc4aee5adebb066eb6da4eaa5681378e", + "f8248ad47d97c18c984f1f5c10950dc1404713c56b6ea397e01e6dd925e903b4fadfe2c9e8" + "77169e71ce3c7fe5ce70ee4255d9cdc26f6943bf48687874de64f6cf30a012512e787b8805" + "9bbf561162bdcc23a3742c835ac144cc14167b1bd6727e940540a9c99f3cbb41fb1dcb00d7" + "6dda04995847c657f4c19d303eb09eb48a", + "3b6ee2425940b3d240d35b97b6dcd61ed3423d8e71a0ada35d47b322d17b35ea0472f35edd" + "1d252f87b8b65ef4b716669fc9ac28b00d34a9d66ad118c9d94e7f46d0b4f6c2b2d339fd6b" + "cd351241a387cc82609057048c12c4ec3d85c661975c45b300cb96930d89370a327c98b67d" + "efaa89497aa8ef994c77f1130f752f94a4", + "c5204b81ec0a4df5b7e9fda3dc245f98082ae7f4efe81998dcaa286bd4507ca840a53d21b0" + "1e904f55e38f78c3757d5a5a4a44b1d5d4e480be3afb5b394a5d2840af42b1b4083d40afbf" + "e22d702f370d32dbfd392e128ea4724d66a3701da41ae2f03bb4d91bb946c7969404cb544f" + "71eb7a49eb4c4ec55799bda1eb545143a7", + "72e81fe221fb402148d8b7ab03549f1180bcc03d41ca59d7653801f0ba853add1f6d29edd7" + "f9abc621b2d548f8dbf8979bd16608d2d8fc3260b4ebc0dd42482481d548c7075711b57596" + "49c41f439fad69954956c9326841ea6492956829f9e0dc789f73633b40f6ac77bcae6dfc79" + "30cfe89e526d1684365c5b0be2437fdb01", + "21188c3edd5de088dacc1076b9e1bcecd79de1003c2414c3866173054dc82dde85169baa77" + "993adb20c269f60a5226111828578bcc7c29e6e8d2dae81806152c8ba0c6ada1986a1983eb" + "eec1473a73a04795b6319d48662d40881c1723a706f516fe75300f92408aa1dc6ae4288d20" + "46f23c1aa2e54b7fb6448a0da922bd7f34", + }; + + char *Qx[] = { + "1ccbe91c075fc7f4f033bfa248db8fccd3565de94bbfb12f3c59ff46c271bf83", + "e266ddfdc12668db30d4ca3e8f7749432c416044f2d2b8c10bf3d4012aeffa8a", + "74ccd8a62fba0e667c50929a53f78c21b8ff0c3c737b0b40b1750b2302b0bde8", + "322f80371bf6e044bc49391d97c1714ab87f990b949bc178cb7c43b7c22d89e1", + "1bcec4570e1ec2436596b8ded58f60c3b1ebc6a403bc5543040ba82963057244", + "a32e50be3dae2c8ba3f5e4bdae14cf7645420d425ead94036c22dd6c4fc59e00", + "8bcfe2a721ca6d753968f564ec4315be4857e28bef1908f61a366b1f03c97479", + "a88bc8430279c8c0400a77d751f26c0abc93e5de4ad9a4166357952fe041e767", + "1bc487570f040dc94196c9befe8ab2b6de77208b1f38bdaae28f9645c4d2bc3a", + "b8188bd68701fc396dab53125d4d28ea33a91daf6d21485f4770f6ea8c565dde", + "51f99d2d52d4a6e734484a018b7ca2f895c2929b6754a3a03224d07ae61166ce", + "8fb287f0202ad57ae841aea35f29b2e1d53e196d0ddd9aec24813d64c0922fb7", + "68229b48c2fe19d3db034e4c15077eb7471a66031f28a980821873915298ba76", + "0a7dbb8bf50cb605eb2268b081f26d6b08e012f952c4b70a5a1e6e7d46af98bb", + "105d22d9c626520faca13e7ced382dcbe93498315f00cc0ac39c4821d0d73737", + }; + + char *Qy[] = { + "ce4014c68811f9a21a1fdb2c0e6113e06db7ca93b7404e78dc7ccd5ca89a4ca9", + "bfa86404a2e9ffe67d47c587ef7a97a7f456b863b4d02cfc6928973ab5b1cb39", + "29074e21f3a0ef88b9efdf10d06aa4c295cc1671f758ca0e4cd108803d0f2614", + "3c15d54a5cc6b9f09de8457e873eb3deb1fceb54b0b295da6050294fae7fd999", + "8af62a4c683f096b28558320737bf83b9959a46ad2521004ef74cf85e67494e1", + "d623bf641160c289d6742c6257ae6ba574446dd1d0e74db3aaa80900b78d4ae9", + "0f67576a30b8e20d4232d8530b52fb4c89cbc589ede291e499ddd15fe870ab96", + "2d365a1eef25ead579cc9a069b6abc1b16b81c35f18785ce26a10ba6d1381185", + "ec81602abd8345e71867c8210313737865b8aa186851e1b48eaca140320f5d8f", + "423f058810f277f8fe076f6db56e9285a1bf2c2a1dae145095edd9c04970bc4a", + "4737da963c6ef7247fb88d19f9b0c667cac7fe12837fdab88c66f10d3c14cad1", + "1f6daff1aa2dd2d6d3741623eecb5e7b612997a1039aab2e5cf2de969cfea573", + "303e8ee3742a893f78b810991da697083dd8f11128c47651c27a56740a80c24c", + "f26dd7d799930062480849962ccf5004edcfd307c044f4e8f667c9baa834eeae", + "6c47f3cbbfa97dfcebe16270b8c7d5d3a5900b888c42520d751e8faf3b401ef4", + }; + + char *R[] = { + "f3ac8061b514795b8843e3d6629527ed2afd6b1f6a555a7acabb5e6f79c8c2ac", + "976d3a4e9d23326dc0baa9fa560b7c4e53f42864f508483a6473b6a11079b2db", + "35fb60f5ca0f3ca08542fb3cc641c8263a2cab7a90ee6a5e1583fac2bb6f6bd1", + "d7c562370af617b581c84a2468cc8bd50bb1cbf322de41b7887ce07c0e5884ca", + "18caaf7b663507a8bcd992b836dec9dc5703c080af5e51dfa3a9a7c387182604", + "8524c5024e2d9a73bde8c72d9129f57873bbad0ed05215a372a84fdbc78f2e68", + "c5a186d72df452015480f7f338970bfe825087f05c0088d95305f87aacc9b254", + "9d0c6afb6df3bced455b459cc21387e14929392664bb8741a3693a1795ca6902", + "2f9e2b4e9f747c657f705bffd124ee178bbc5391c86d056717b140c153570fd9", + "1cc628533d0004b2b20e7f4baad0b8bb5e0673db159bbccf92491aef61fc9620", + "9886ae46c1415c3bc959e82b760ad760aab66885a84e620aa339fdf102465c42", + "490efd106be11fc365c7467eb89b8d39e15d65175356775deab211163c2504cb", + "e67a9717ccf96841489d6541f4f6adb12d17b59a6bef847b6183b8fcf16a32eb", + "b53ce4da1aa7c0dc77a1896ab716b921499aed78df725b1504aba1597ba0c64b", + "542c40a18140a6266d6f0286e24e9a7bad7650e72ef0e2131e629c076d962663", + }; + + char *S[] = { + "8bf77819ca05a6b2786c76262bf7371cef97b218e96f175a3ccdda2acc058903", + "1b766e9ceb71ba6c01dcd46e0af462cd4cfa652ae5017d4555b8eeefe36e1932", + "ee59d81bc9db1055cc0ed97b159d8784af04e98511d0a9a407b99bb292572e96", + "b46d9f2d8c4bf83546ff178f1d78937c008d64e8ecc5cbb825cb21d94d670d89", + "77c68928ac3b88d985fb43fb615fb7ff45c18ba5c81af796c613dfa98352d29c", + "d18c2caf3b1072f87064ec5e8953f51301cada03469c640244760328eb5a05cb", + "84a58f9e9d9e735344b316b1aa1ab5185665b85147dc82d92e969d7bee31ca30", + "d7f9ddd191f1f412869429209ee3814c75c72fa46a9cccf804a2f5cc0b7e739f", + "f5413bfd85949da8d83de83ab0d19b2986613e224d1901d76919de23ccd03199", + "880e0bbf82a8cf818ed46ba03cf0fc6c898e36fca36cc7fdb1d2db7503634430", + "2bf3a80bc04faa35ebecc0f4864ac02d349f6f126e0f988501b8d3075409a26c", + "644300fc0da4d40fb8c6ead510d14f0bd4e1321a469e9c0a581464c7186b7aa7", + "9ae6ba6d637706849a6a9fc388cf0232d85c26ea0d1fe7437adb48de58364333", + "d7c246dc7ad0e67700c373edcfdd1c0a0495fc954549ad579df6ed1438840851", + "4f7f65305e24a6bbb5cff714ba8f5a2cee5bdc89ba8d75dcbf21966ce38eb66f", + }; + + struct tc_sha256_state_struct sha256_ctx; + + TC_PRINT("Test #1: ECDSAsign "); + TC_PRINT("NIST-p256, SHA2-256\n"); + + return sign_vectors(&sha256_ctx, d, k, Msg, Qx, Qy, R, S, 15, verbose); +} + +int vrfy_vectors(TCSha256State_t hash, char **msg_vec, char **qx_vec, char **qy_vec, + char **r_vec, char **s_vec, char **res_vec, int tests, bool verbose) +{ + + const struct uECC_Curve_t * curve = uECC_secp256r1(); + unsigned int pub[2 * NUM_ECC_WORDS]; + uint8_t pub_bytes[2 * NUM_ECC_BYTES]; + unsigned int sig[2 * NUM_ECC_WORDS]; + uint8_t sig_bytes[2 * NUM_ECC_BYTES]; + uint8_t digest_bytes[TC_SHA256_DIGEST_SIZE]; + unsigned int digest[TC_SHA256_DIGEST_SIZE / 4]; + unsigned int result = TC_PASS; + + int rc; + int exp_rc; + char tmp; + + uint8_t msg[BUF_SIZE]; + size_t msglen; + + for (int i = 0; i < tests; i++) { + + string2scalar(pub, NUM_ECC_WORDS, qx_vec[i]); + string2scalar(pub + NUM_ECC_WORDS, NUM_ECC_WORDS, qy_vec[i]); + string2scalar(sig, NUM_ECC_WORDS, r_vec[i]); + string2scalar(sig + NUM_ECC_WORDS, NUM_ECC_WORDS, s_vec[i]); + + if (2 != sscanf(res_vec[i], "%c (%d ", &tmp, &exp_rc)) { + TC_ERROR("Error: failed to parse CAVP response.\n"); + result = TC_FAIL; + goto exitTest1; + } + + /* validate ECDSA: hash message, verify r+s */ + msglen = hex2bin(msg, BUF_SIZE, msg_vec[i], strlen(msg_vec[i])); + + if (msglen == false) { + TC_ERROR("failed to import message!\n"); + result = TC_FAIL; + goto exitTest1; + } + + tc_sha256_init(hash); + tc_sha256_update(hash, msg, msglen); + tc_sha256_final(digest_bytes, hash); + + /* if digest larger than ECC scalar, drop the end + * if digest smaller than ECC scalar, zero-pad front */ + int hash_dwords = TC_SHA256_DIGEST_SIZE / 4; + if (NUM_ECC_WORDS < hash_dwords) { + hash_dwords = NUM_ECC_WORDS; + } + + memset(digest, 0, NUM_ECC_BYTES - 4 * hash_dwords); + uECC_vli_bytesToNative(digest + (NUM_ECC_WORDS-hash_dwords), digest_bytes, + TC_SHA256_DIGEST_SIZE); + + uECC_vli_nativeToBytes(pub_bytes, NUM_ECC_BYTES, pub); + uECC_vli_nativeToBytes(pub_bytes + NUM_ECC_BYTES, NUM_ECC_BYTES, + pub + NUM_ECC_WORDS); + + /* adapt return codes to match CAVP error: */ + if (0 != uECC_valid_public_key(pub_bytes, curve)) { + /* error 4 - Q changed */ + rc = 4; + } else { + uECC_vli_nativeToBytes(sig_bytes, NUM_ECC_BYTES, sig); + uECC_vli_nativeToBytes(sig_bytes + NUM_ECC_BYTES, NUM_ECC_BYTES, + sig + NUM_ECC_WORDS); + + rc = uECC_verify(pub_bytes, digest_bytes, sizeof(digest_bytes), sig_bytes, + uECC_secp256r1()); + /* CAVP expects 0 for success, others for fail */ + rc = !rc; + if (exp_rc != 0 && rc != 0) { + /* mimic CAVP code on errors. */ + rc = exp_rc; + } + } + + result = check_code(i, res_vec[i], exp_rc, rc, verbose); + if(result == TC_FAIL) { + goto exitTest1; + } + } + exitTest1: + TC_END_RESULT(result); + return result; +} + +int cavp_verify(bool verbose) +{ + + /* + * [P-256,SHA-256] + */ + char *Msg[] = { + "e4796db5f785f207aa30d311693b3702821dff1168fd2e04c0836825aefd850d9aa60326d8" + "8cde1a23c7745351392ca2288d632c264f197d05cd424a30336c19fd09bb229654f0222fcb" + "881a4b35c290a093ac159ce13409111ff0358411133c24f5b8e2090d6db6558afc36f06ca1" + "f6ef779785adba68db27a409859fc4c4a0", + "069a6e6b93dfee6df6ef6997cd80dd2182c36653cef10c655d524585655462d683877f95ec" + "c6d6c81623d8fac4e900ed0019964094e7de91f1481989ae1873004565789cbf5dc56c62ae" + "dc63f62f3b894c9c6f7788c8ecaadc9bd0e81ad91b2b3569ea12260e93924fdddd3972af52" + "73198f5efda0746219475017557616170e", + "df04a346cf4d0e331a6db78cca2d456d31b0a000aa51441defdb97bbeb20b94d8d746429a3" + "93ba88840d661615e07def615a342abedfa4ce912e562af714959896858af817317a840dcf" + "f85a057bb91a3c2bf90105500362754a6dd321cdd86128cfc5f04667b57aa78c112411e42d" + "a304f1012d48cd6a7052d7de44ebcc01de", + "e1130af6a38ccb412a9c8d13e15dbfc9e69a16385af3c3f1e5da954fd5e7c45fd75e2b8c36" + "699228e92840c0562fbf3772f07e17f1add56588dd45f7450e1217ad239922dd9c32695dc7" + "1ff2424ca0dec1321aa47064a044b7fe3c2b97d03ce470a592304c5ef21eed9f93da56bb23" + "2d1eeb0035f9bf0dfafdcc4606272b20a3", + "73c5f6a67456ae48209b5f85d1e7de7758bf235300c6ae2bdceb1dcb27a7730fb68c950b7f" + "cada0ecc4661d3578230f225a875e69aaa17f1e71c6be5c831f22663bac63d0c7a9635edb0" + "043ff8c6f26470f02a7bc56556f1437f06dfa27b487a6c4290d8bad38d4879b334e341ba09" + "2dde4e4ae694a9c09302e2dbf443581c08", + "666036d9b4a2426ed6585a4e0fd931a8761451d29ab04bd7dc6d0c5b9e38e6c2b263ff6cb8" + "37bd04399de3d757c6c7005f6d7a987063cf6d7e8cb38a4bf0d74a282572bd01d0f41e3fd0" + "66e3021575f0fa04f27b700d5b7ddddf50965993c3f9c7118ed78888da7cb221849b326059" + "2b8e632d7c51e935a0ceae15207bedd548", + "7e80436bce57339ce8da1b5660149a20240b146d108deef3ec5da4ae256f8f894edcbbc57b" + "34ce37089c0daa17f0c46cd82b5a1599314fd79d2fd2f446bd5a25b8e32fcf05b76d644573" + "a6df4ad1dfea707b479d97237a346f1ec632ea5660efb57e8717a8628d7f82af50a4e84b11" + "f21bdff6839196a880ae20b2a0918d58cd", + "1669bfb657fdc62c3ddd63269787fc1c969f1850fb04c933dda063ef74a56ce13e3a649700" + "820f0061efabf849a85d474326c8a541d99830eea8131eaea584f22d88c353965dabcdc4bf" + "6b55949fd529507dfb803ab6b480cd73ca0ba00ca19c438849e2cea262a1c57d8f81cd257f" + "b58e19dec7904da97d8386e87b84948169", + "3fe60dd9ad6caccf5a6f583b3ae65953563446c4510b70da115ffaa0ba04c076115c7043ab" + "8733403cd69c7d14c212c655c07b43a7c71b9a4cffe22c2684788ec6870dc2013f269172c8" + "22256f9e7cc674791bf2d8486c0f5684283e1649576efc982ede17c7b74b214754d70402fb" + "4bb45ad086cf2cf76b3d63f7fce39ac970", + "983a71b9994d95e876d84d28946a041f8f0a3f544cfcc055496580f1dfd4e312a2ad418fe6" + "9dbc61db230cc0c0ed97e360abab7d6ff4b81ee970a7e97466acfd9644f828ffec538abc38" + "3d0e92326d1c88c55e1f46a668a039beaa1be631a89129938c00a81a3ae46d4aecbf9707f7" + "64dbaccea3ef7665e4c4307fa0b0a3075c", + "4a8c071ac4fd0d52faa407b0fe5dab759f7394a5832127f2a3498f34aac287339e043b4ffa" + "79528faf199dc917f7b066ad65505dab0e11e6948515052ce20cfdb892ffb8aa9bf3f1aa5b" + "e30a5bbe85823bddf70b39fd7ebd4a93a2f75472c1d4f606247a9821f1a8c45a6cb80545de" + "2e0c6c0174e2392088c754e9c8443eb5af", + "0a3a12c3084c865daf1d302c78215d39bfe0b8bf28272b3c0b74beb4b7409db0718239de70" + "0785581514321c6440a4bbaea4c76fa47401e151e68cb6c29017f0bce4631290af5ea5e2bf" + "3ed742ae110b04ade83a5dbd7358f29a85938e23d87ac8233072b79c94670ff0959f9c7f45" + "17862ff829452096c78f5f2e9a7e4e9216", + "785d07a3c54f63dca11f5d1a5f496ee2c2f9288e55007e666c78b007d95cc28581dce51f49" + "0b30fa73dc9e2d45d075d7e3a95fb8a9e1465ad191904124160b7c60fa720ef4ef1c5d2998" + "f40570ae2a870ef3e894c2bc617d8a1dc85c3c55774928c38789b4e661349d3f84d2441a3b" + "856a76949b9f1f80bc161648a1cad5588e", + "76f987ec5448dd72219bd30bf6b66b0775c80b394851a43ff1f537f140a6e7229ef8cd72ad" + "58b1d2d20298539d6347dd5598812bc65323aceaf05228f738b5ad3e8d9fe4100fd767c2f0" + "98c77cb99c2992843ba3eed91d32444f3b6db6cd212dd4e5609548f4bb62812a920f6e2bf1" + "581be1ebeebdd06ec4e971862cc42055ca", + "60cd64b2cd2be6c33859b94875120361a24085f3765cb8b2bf11e026fa9d8855dbe435acf7" + "882e84f3c7857f96e2baab4d9afe4588e4a82e17a78827bfdb5ddbd1c211fbc2e6d884cddd" + "7cb9d90d5bf4a7311b83f352508033812c776a0e00c003c7e0d628e50736c7512df0acfa9f" + "2320bd102229f46495ae6d0857cc452a84", + }; + + char *Qx[] = { + "87f8f2b218f49845f6f10eec3877136269f5c1a54736dbdf69f89940cad41555", + "5cf02a00d205bdfee2016f7421807fc38ae69e6b7ccd064ee689fc1a94a9f7d2", + "2ddfd145767883ffbb0ac003ab4a44346d08fa2570b3120dcce94562422244cb", + "e424dc61d4bb3cb7ef4344a7f8957a0c5134e16f7a67c074f82e6e12f49abf3c", + "e0fc6a6f50e1c57475673ee54e3a57f9a49f3328e743bf52f335e3eeaa3d2864", + "a849bef575cac3c6920fbce675c3b787136209f855de19ffe2e8d29b31a5ad86", + "3dfb6f40f2471b29b77fdccba72d37c21bba019efa40c1c8f91ec405d7dcc5df", + "69b7667056e1e11d6caf6e45643f8b21e7a4bebda463c7fdbc13bc98efbd0214", + "bf02cbcf6d8cc26e91766d8af0b164fc5968535e84c158eb3bc4e2d79c3cc682", + "224a4d65b958f6d6afb2904863efd2a734b31798884801fcab5a590f4d6da9de", + "43691c7795a57ead8c5c68536fe934538d46f12889680a9cb6d055a066228369", + "9157dbfcf8cf385f5bb1568ad5c6e2a8652ba6dfc63bc1753edf5268cb7eb596", + "072b10c081a4c1713a294f248aef850e297991aca47fa96a7470abe3b8acfdda", + "09308ea5bfad6e5adf408634b3d5ce9240d35442f7fe116452aaec0d25be8c24", + "2d98ea01f754d34bbc3003df5050200abf445ec728556d7ed7d5c54c55552b6d", + }; + + char *Qy[] = { + "e15f369036f49842fac7a86c8a2b0557609776814448b8f5e84aa9f4395205e9", + "ec530ce3cc5c9d1af463f264d685afe2b4db4b5828d7e61b748930f3ce622a85", + "5f70c7d11ac2b7a435ccfbbae02c3df1ea6b532cc0e9db74f93fffca7c6f9a64", + "970eed7aa2bc48651545949de1dddaf0127e5965ac85d1243d6f60e7dfaee927", + "7f59d689c91e463607d9194d99faf316e25432870816dde63f5d4b373f12f22a", + "bf5fe4f7858f9b805bd8dcc05ad5e7fb889de2f822f3d8b41694e6c55c16b471", + "f22f953f1e395a52ead7f3ae3fc47451b438117b1e04d613bc8555b7d6e6d1bb", + "d3f9b12eb46c7c6fda0da3fc85bc1fd831557f9abc902a3be3cb3e8be7d1aa2f", + "069ba6cb06b49d60812066afa16ecf7b51352f2c03bd93ec220822b1f3dfba03", + "178d51fddada62806f097aa615d33b8f2404e6b1479f5fd4859d595734d6d2b9", + "f8790110b3c3b281aa1eae037d4f1234aff587d903d93ba3af225c27ddc9ccac", + "972570f4313d47fc96f7c02d5594d77d46f91e949808825b3d31f029e8296405", + "9581145cca04a0fb94cedce752c8f0370861916d2a94e7c647c5373ce6a4c8f5", + "f40c93e023ef494b1c3079b2d10ef67f3170740495ce2cc57f8ee4b0618b8ee5", + "9b52672742d637a32add056dfd6d8792f2a33c2e69dafabea09b960bc61e230a", + }; + + char *R[] = { + "d19ff48b324915576416097d2544f7cbdf8768b1454ad20e0baac50e211f23b0", + "dc23d130c6117fb5751201455e99f36f59aba1a6a21cf2d0e7481a97451d6693", + "9913111cff6f20c5bf453a99cd2c2019a4e749a49724a08774d14e4c113edda8", + "bf96b99aa49c705c910be33142017c642ff540c76349b9dab72f981fd9347f4f", + "1d75830cd36f4c9aa181b2c4221e87f176b7f05b7c87824e82e396c88315c407", + "25acc3aa9d9e84c7abf08f73fa4195acc506491d6fc37cb9074528a7db87b9d6", + "548886278e5ec26bed811dbb72db1e154b6f17be70deb1b210107decb1ec2a5a", + "288f7a1cd391842cce21f00e6f15471c04dc182fe4b14d92dc18910879799790", + "f5acb06c59c2b4927fb852faa07faf4b1852bbb5d06840935e849c4d293d1bad", + "87b93ee2fecfda54deb8dff8e426f3c72c8864991f8ec2b3205bb3b416de93d2", + "8acd62e8c262fa50dd9840480969f4ef70f218ebf8ef9584f199031132c6b1ce", + "dfaea6f297fa320b707866125c2a7d5d515b51a503bee817de9faa343cc48eeb", + "09f5483eccec80f9d104815a1be9cc1a8e5b12b6eb482a65c6907b7480cf4f19", + "5cc8aa7c35743ec0c23dde88dabd5e4fcd0192d2116f6926fef788cddb754e73", + "06108e525f845d0155bf60193222b3219c98e3d49424c2fb2a0987f825c17959", + }; + + char *Result[] = { + "F (3 - S changed)", "F (2 - R changed)", "F (4 - Q changed)", + "P (0 )", "P (0 )", "F (2 - R changed)", + "F (4 - Q changed)", "F (1 - Message changed)", "F (3 - S changed)", + "F (2 - R changed)", "F (3 - S changed)", "F (1 - Message changed)", + "F (4 - Q changed)", "F (1 - Message changed)", "P (0 )", + }; + + char *S[] = { + "a3e81e59311cdfff2d4784949f7a2cb50ba6c3a91fa54710568e61aca3e847c6", + "d6ce7708c18dbf35d4f8aa7240922dc6823f2e7058cbc1484fcad1599db5018c", + "9467cd4cd21ecb56b0cab0a9a453b43386845459127a952421f5c6382866c5cc", + "17c55095819089c2e03b9cd415abdf12444e323075d98f31920b9e0f57ec871c", + "cb2acb01dac96efc53a32d4a0d85d0c2e48955214783ecf50a4f0414a319c05a", + "9b21d5b5259ed3f2ef07dfec6cc90d3a37855d1ce122a85ba6a333f307d31537", + "e93bfebd2f14f3d827ca32b464be6e69187f5edbd52def4f96599c37d58eee75", + "247b3c4e89a3bcadfea73c7bfd361def43715fa382b8c3edf4ae15d6e55e9979", + "049dab79c89cc02f1484c437f523e080a75f134917fda752f2d5ca397addfe5d", + "4044a24df85be0cc76f21a4430b75b8e77b932a87f51e4eccbc45c263ebf8f66", + "cfca7ed3d4347fb2a29e526b43c348ae1ce6c60d44f3191b6d8ea3a2d9c92154", + "8f780ad713f9c3e5a4f7fa4c519833dfefc6a7432389b1e4af463961f09764f2", + "a4f90e560c5e4eb8696cb276e5165b6a9d486345dedfb094a76e8442d026378d", + "9c9c045ebaa1b828c32f82ace0d18daebf5e156eb7cbfdc1eff4399a8a900ae7", + "62b5cdd591e5b507e560167ba8f6f7cda74673eb315680cb89ccbc4eec477dce", + }; + + struct tc_sha256_state_struct sha256_ctx; + + printf("Test #2: ECDSAvrfy "); + printf("NIST-p256, SHA2-256\n"); + + return vrfy_vectors(&sha256_ctx, Msg, Qx, Qy, R, S, Result, 15, verbose); +} + +int montecarlo_signverify(int num_tests, bool verbose) +{ + printf("Test #3: Monte Carlo (%d Randomized EC-DSA signatures) ", num_tests); + printf("NIST-p256, SHA2-256\n "); + int i; + uint8_t private[NUM_ECC_BYTES]; + uint8_t public[2*NUM_ECC_BYTES]; + uint8_t hash[NUM_ECC_BYTES]; + unsigned int hash_words[NUM_ECC_WORDS]; + uint8_t sig[2*NUM_ECC_BYTES]; + + const struct uECC_Curve_t * curve = uECC_secp256r1(); + + for (i = 0; i < num_tests; ++i) { + if (verbose) { + TC_PRINT("."); + fflush(stdout); + } + + uECC_generate_random_int(hash_words, curve->n, BITS_TO_WORDS(curve->num_n_bits)); + uECC_vli_nativeToBytes(hash, NUM_ECC_BYTES, hash_words); + + if (!uECC_make_key(public, private, curve)) { + TC_ERROR("uECC_make_key() failed\n"); + return TC_FAIL; + } + + if (!uECC_sign(private, hash, sizeof(hash), sig, curve)) { + TC_ERROR("uECC_sign() failed\n"); + return TC_FAIL; + } + + if (!uECC_verify(public, hash, sizeof(hash), sig, curve)) { + TC_ERROR("uECC_verify() failed\n"); + return TC_FAIL; + } + if (verbose) { + fflush(stdout); + printf("."); + } + } + TC_PRINT("\n"); + return TC_PASS; +} + +int main() +{ + unsigned int result = TC_PASS; + + TC_START("Performing ECC-DSA tests:"); + /* Setup of the Cryptographically Secure PRNG. */ + uECC_set_rng(&default_CSPRNG); + + bool verbose = true; + + TC_PRINT("Performing cavp_sign test:\n"); + result = cavp_sign(verbose); + if (result == TC_FAIL) { /* terminate test */ + TC_ERROR("cavp_sign test failed.\n"); + goto exitTest; + } + TC_PRINT("Performing cavp_verify test:\n"); + result = cavp_verify(verbose); + if (result == TC_FAIL) { + TC_ERROR("cavp_verify test failed.\n"); + goto exitTest; + } + TC_PRINT("Performing montecarlo_signverify test:\n"); + result = montecarlo_signverify(10, verbose); + if (result == TC_FAIL) { + TC_ERROR("montecarlo_signverify test failed.\n"); + goto exitTest; + } + + TC_PRINT("\nAll ECC-DSA tests succeeded.\n"); + + exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_utils.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_utils.c new file mode 100644 index 0000000..5c81eba --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_ecc_utils.c @@ -0,0 +1,271 @@ +/* test_ecc_utils.c - TinyCrypt common functions for ECC tests */ + +/* 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.*/ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + * + * test_ecc_utils.c -- Implementation of some common functions for ECC tests. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int hex2int (char hex) +{ + uint8_t dec; + + if ('0' <= hex && hex <= '9') dec = hex - '0'; + else if ('a' <= hex && hex <= 'f') dec = hex - 'a' + 10; + else if ('A' <= hex && hex <= 'F') dec = hex - 'A' + 10; + else return -1; + + return dec; +} + +/* + * Convert hex string to byte string + * Return number of bytes written to buf, or 0 on error + */ +int hex2bin(uint8_t *buf, const size_t buflen, const char *hex, + const size_t hexlen) +{ + + int dec; + + if (buflen < hexlen / 2 + hexlen % 2) + { + return false; + } + + /* if hexlen is uneven, insert leading zero nibble */ + if (hexlen % 2) + { + dec = hex2int(hex[0]); + if (dec == -1) + return false; + buf[0] = dec; + buf++; + hex++; + } + + /* regular hex conversion */ + for (size_t i = 0; i < hexlen / 2; i++) + { + dec = hex2int(hex[2 * i]); + if (dec == -1) + { + return false; + } + buf[i] = dec << 4; + + dec = hex2int(hex[ 2 * i + 1]); + if (dec == -1) + { + return false; + } + buf[i] += dec; + } + return hexlen / 2 + hexlen % 2; +} + +/* + * Convert hex string to zero-padded nanoECC scalar + */ +void string2scalar(unsigned int *scalar, unsigned int num_word32, char *str) +{ + + unsigned int num_bytes = 4 * num_word32; + uint8_t tmp[num_bytes]; + size_t hexlen = strlen(str); + + int padding; + + if (0 > (padding = 2 * num_bytes - strlen(str))) + { + printf("Error: 2 * num_bytes(%d) < strlen(hex) (%zu)\n", + 2 * num_bytes, strlen(str)); + exit(-1); + } + + memset(tmp, 0, padding / 2); + + if (false == hex2bin(tmp + padding / 2, num_bytes, str, hexlen)) + { + exit(-1); + } + uECC_vli_bytesToNative(scalar, tmp, num_bytes); + +} + +void vli_print_bytes(uint8_t *vli, unsigned int size) +{ + for(unsigned i = 0; i < size; ++i) + { + printf("%02X ", (unsigned)vli[i]); + } +} + +void print_ecc_scalar(const char *label, const unsigned int * p_vli, + unsigned int num_word32) +{ + unsigned int i; + + if (label) { + printf("%s = { ", label); + } + + for(i = 0; i < num_word32 - 1; ++i) { + printf("0x%08lX, ", (unsigned long)p_vli[i]); + } + printf("0x%08lX", (unsigned long)p_vli[i]); + + if (label) { + printf(" };\n"); + } +} + +int check_ecc_result(const int num, const char *name, + const unsigned int *expected, + const unsigned int *computed, + const unsigned int num_word32, const bool verbose) +{ + uint32_t num_bytes = 4 * num_word32; + if (memcmp(computed, expected, num_bytes)) { + TC_PRINT("\n Vector #%02d check %s - FAILURE:\n\n", num, name); + print_ecc_scalar("Expected", expected, num_word32); + print_ecc_scalar("Computed", computed, num_word32); + TC_PRINT("\n"); + return TC_FAIL; + } + if (verbose) { + TC_PRINT(" Vector #%02d check %s - success\n", num, name); + } + return TC_PASS; +} + +int check_code(const int num, const char *name, const int expected, + const int computed, const int verbose) +{ + + if (expected != computed) { + TC_ERROR("\n Vector #%02d check %s - FAILURE:\n", num, name); + TC_ERROR("\n Expected: %d, computed: %d\n\n", expected, computed); + return TC_FAIL; + } + + if (verbose) { + TC_PRINT(" Vector #%02d check %s - success (%d=%d)\n", num, name, + expected, computed); + } + + return TC_PASS; +} + +/* Test ecc_make_keys, and also as keygen part of other tests */ +int keygen_vectors(char **d_vec, char **qx_vec, char **qy_vec, int tests, + bool verbose) +{ + + unsigned int pub[2 * NUM_ECC_WORDS]; + unsigned int d[NUM_ECC_WORDS]; + unsigned int prv[NUM_ECC_WORDS]; + unsigned int result = TC_PASS; + + /* expected outputs (converted input vectors) */ + unsigned int exp_pub[2 * NUM_ECC_WORDS]; + unsigned int exp_prv[NUM_ECC_WORDS]; + + for (int i = 0; i < tests; i++) { + string2scalar(exp_prv, NUM_ECC_WORDS, d_vec[i]); + string2scalar(exp_pub, NUM_ECC_WORDS, qx_vec[i]); + string2scalar(exp_pub + NUM_ECC_WORDS, NUM_ECC_WORDS, qy_vec[i]); + + /* + * Feed prvkey vector as padded random seed into ecc_make_key. + * Internal mod-reduction will be zero-op and generate correct prv/pub + */ + memset(d, 0, NUM_ECC_WORDS); + string2scalar(d, NUM_ECC_WORDS, d_vec[i]); + + uint8_t pub_bytes[2*NUM_ECC_BYTES]; + uint8_t prv_bytes[NUM_ECC_BYTES]; + + uECC_make_key_with_d(pub_bytes, prv_bytes, d, uECC_secp256r1()); + + uECC_vli_bytesToNative(prv, prv_bytes, NUM_ECC_BYTES); + uECC_vli_bytesToNative(pub, pub_bytes, NUM_ECC_BYTES); + uECC_vli_bytesToNative(pub + NUM_ECC_WORDS, pub_bytes + NUM_ECC_BYTES, NUM_ECC_BYTES); + + /* validate correctness of vector conversion and make_key() */ + result = check_ecc_result(i, "prv ", exp_prv, prv, NUM_ECC_WORDS, verbose); + if (result == TC_FAIL) { + return result; + } + result = check_ecc_result(i, "pub.x", exp_pub, pub, NUM_ECC_WORDS, verbose); + if (result == TC_FAIL) { + return result; + } + result = check_ecc_result(i, "pub.y", exp_pub + NUM_ECC_WORDS, pub + NUM_ECC_WORDS, NUM_ECC_WORDS, verbose); + if (result == TC_FAIL) { + return result; + } + } + return result; +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_hmac.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_hmac.c new file mode 100644 index 0000000..a41275e --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_hmac.c @@ -0,0 +1,362 @@ +/* test_hmac.c - TinyCrypt implementation of some HMAC tests */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ + +/* + DESCRIPTION + This module tests the following HMAC routines: + + Scenarios tested include: + - HMAC tests (RFC 4231 test vectors) +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +unsigned int do_hmac_test(TCHmacState_t h, unsigned int testnum, const uint8_t *data, + size_t datalen, const uint8_t *expected, + size_t expectedlen) +{ + uint8_t digest[32]; + unsigned int result = TC_PASS; + + (void)tc_hmac_init(h); + (void)tc_hmac_update(h, data, datalen); + (void)tc_hmac_final(digest, TC_SHA256_DIGEST_SIZE, h); + result = check_result(testnum, expected, expectedlen, + digest, sizeof(digest)); + return result; +} + +/* + * NIST test vectors for encryption. + */ +unsigned int test_1(void) +{ + unsigned int result = TC_PASS; + + TC_PRINT("HMAC %s:\n", __func__); + + const uint8_t key[20] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + const uint8_t data[8] = { + 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 + }; + const uint8_t expected[32] = { + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, + 0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, + 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7 + }; + struct tc_hmac_state_struct h; + + (void)memset(&h, 0x00, sizeof(h)); + (void)tc_hmac_set_key(&h, key, sizeof(key)); + result = do_hmac_test(&h, 1, data, sizeof(data),expected, + sizeof(expected)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_2(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("HMAC %s:\n", __func__); + const uint8_t key[4] = { + 0x4a, 0x65, 0x66, 0x65 + }; + const uint8_t data[28] = { + 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x79, 0x61, 0x20, 0x77, + 0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x3f + }; + const uint8_t expected[32] = { + 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, + 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, + 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 + }; + struct tc_hmac_state_struct h; + + (void)memset(&h, 0x00, sizeof(h)); + (void)tc_hmac_set_key(&h, key, sizeof(key)); + + result = do_hmac_test(&h, 2, data, sizeof(data), expected, + sizeof(expected)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_3(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("HMAC %s:\n", __func__); + const uint8_t key[20] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }; + const uint8_t data[50] = { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd + }; + const uint8_t expected[32] = { + 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb, + 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, + 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe + }; + struct tc_hmac_state_struct h; + + (void)memset(&h, 0x00, sizeof(h)); + (void)tc_hmac_set_key(&h, key, sizeof(key)); + + result = do_hmac_test(&h, 3, data, sizeof(data), expected, + sizeof(expected)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_4(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("HMAC %s:\n", __func__); + + const uint8_t key[25] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19 + }; + const uint8_t data[50] = { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd + }; + const uint8_t expected[32] = { + 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98, + 0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, + 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b + }; + struct tc_hmac_state_struct h; + + (void)memset(&h, 0x00, sizeof(h)); + (void)tc_hmac_set_key(&h, key, sizeof(key)); + + result = do_hmac_test(&h, 4, data, sizeof(data), expected, + sizeof(expected)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_5(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("HMAC %s:\n", __func__); + const uint8_t key[20] = { + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c + }; + const uint8_t data[20] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x57, 0x69, 0x74, 0x68, 0x20, 0x54, 0x72, + 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e + }; + const uint8_t expected[32] = { + 0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0, 0x6e, 0x0c, 0x79, 0x6c, + 0x29, 0x55, 0x55, 0x2b, 0xfa, 0x6f, 0x7c, 0x0a, 0x6a, 0x8a, 0xef, 0x8b, + 0x93, 0xf8, 0x60, 0xaa, 0xb0, 0xcd, 0x20, 0xc5 + }; + struct tc_hmac_state_struct h; + + (void)memset(&h, 0x00, sizeof(h)); + (void)tc_hmac_set_key(&h, key, sizeof(key)); + + result = do_hmac_test(&h, 5, data, sizeof(data), expected, + sizeof(expected)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_6(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("HMAC %s:\n", __func__); + const uint8_t key[131] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }; + const uint8_t data[54] = { + 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x4c, + 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x4b, 0x65, + 0x79, 0x20, 0x2d, 0x20, 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79, + 0x20, 0x46, 0x69, 0x72, 0x73, 0x74 + }; + const uint8_t expected[32] = { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, + 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, + 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54 + }; + struct tc_hmac_state_struct h; + + (void)memset(&h, 0x00, sizeof(h)); + (void)tc_hmac_set_key(&h, key, sizeof(key)); + + result = do_hmac_test(&h, 6, data, sizeof(data), expected, + sizeof(expected)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_7(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("HMAC %s:\n", __func__); + const uint8_t key[131] = { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }; + const uint8_t data[152] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x65, + 0x73, 0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x6c, + 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6b, 0x65, + 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67, + 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6e, 0x65, 0x65, + 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x68, 0x61, 0x73, + 0x68, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x62, + 0x65, 0x69, 0x6e, 0x67, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e + }; + const uint8_t expected[32] = { + 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, + 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, + 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2 + }; + struct tc_hmac_state_struct h; + + (void)memset(&h, 0x00, sizeof(h)); + (void)tc_hmac_set_key(&h, key, sizeof(key)); + + result = do_hmac_test(&h, 7, data, sizeof(data), expected, + sizeof(expected)); + TC_END_RESULT(result); + return result; +} + +/* + * Main task to test AES + */ +int main(void) +{ + unsigned int result = TC_PASS; + + TC_START("Performing HMAC tests (RFC4231 test vectors):"); + + result = test_1(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("HMAC test #1 failed.\n"); + goto exitTest; + } + result = test_2(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("HMAC test #2 failed.\n"); + goto exitTest; + } + result = test_3(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("HMAC test #3 failed.\n"); + goto exitTest; + } + result = test_4(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("HMAC test #4 failed.\n"); + goto exitTest; + } + result = test_5(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("HMAC test #5 failed.\n"); + goto exitTest; + } + result = test_6(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("HMAC #6 test failed.\n"); + goto exitTest; + } + result = test_7(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("HMAC test #7 failed.\n"); + goto exitTest; + } + + TC_PRINT("All HMAC tests succeeded!\n"); + +exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_hmac_prng.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_hmac_prng.c new file mode 100644 index 0000000..b45e984 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_hmac_prng.c @@ -0,0 +1,135 @@ +/* test_hmac_prng.c - TinyCrypt implementation of some HMAC-PRNG tests */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ + +/* + DESCRIPTION + This module tests the following PRNG routines: + + Scenarios tested include: + - HMAC-PRNG init + - HMAC-PRNG reseed + - HMAC-PRNG generate) +*/ + +#include +#include +#include + +#include +#include +#include + +#define TC_DEBUG_MODE 0 + +#ifdef TC_DEBUG_MODE +void show(const char *label, const uint8_t *s, size_t len) +{ + unsigned int i; + printf ("%s = ", label); + for (i = 0; i < (unsigned int) len; ++i) { + printf ("%02x", s[i]); + } + printf ("\n"); +} + +void printBinaryFile(const uint8_t *s, unsigned int slen) +{ + FILE *write_ptr; + write_ptr = fopen("pseudo-random-data.bin","wb"); + fwrite(s, slen, 1, write_ptr); +} +#endif + +/* + * Main task to test AES + */ +int main(void) +{ + uint8_t seed[128]; + struct tc_hmac_prng_struct h; + unsigned int size = (1 << 19); + uint8_t random[size]; + unsigned int i; + unsigned int result = TC_PASS; + + TC_START("Performing HMAC-PRNG tests:"); + TC_PRINT("HMAC-PRNG test#1 (init, reseed, generate):\n"); + + /* Fake seed (replace by a a truly random seed): */ + for (i = 0; i < (unsigned int) sizeof(seed); ++i) { + seed[i] = i; + } + + /* Fake personalization and additional_input (replace by appropriate + * values): * + * e.g.: hostname+timestamp */ + uint8_t *personalization = (uint8_t *) "HOSTNAME"; + uint8_t *additional_input = (uint8_t *) "additional input"; + + TC_PRINT("HMAC-PRNG test#1 (init):\n"); + if (tc_hmac_prng_init(&h, personalization, + sizeof(personalization)) == 0) { + TC_ERROR("HMAC-PRNG initialization failed.\n"); + result = TC_FAIL; + goto exitTest; + } + TC_END_RESULT(result); + + TC_PRINT("HMAC-PRNG test#1 (reseed):\n"); + if (tc_hmac_prng_reseed(&h, seed, sizeof(seed), additional_input, + sizeof(additional_input)) == 0) { + TC_ERROR("HMAC-PRNG reseed failed.\n"); + result = TC_FAIL; + goto exitTest; + } + + TC_END_RESULT(result); + + TC_PRINT("HMAC-PRNG test#1 (generate):\n"); + if (tc_hmac_prng_generate(random, size, &h) < 1) { + TC_ERROR("HMAC-PRNG generate failed.\n"); + result = TC_FAIL; + goto exitTest; + } + TC_END_RESULT(result); + +#ifdef TC_DEBUG_MODE + printBinaryFile(random, size); + show ("Pseudo-random data", random, size); +#endif + + TC_PRINT("All HMAC tests succeeded!\n"); + + exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); +} diff --git a/bootloader/mcuboot/ext/tinycrypt/tests/test_sha256.c b/bootloader/mcuboot/ext/tinycrypt/tests/test_sha256.c new file mode 100644 index 0000000..b309ed1 --- /dev/null +++ b/bootloader/mcuboot/ext/tinycrypt/tests/test_sha256.c @@ -0,0 +1,511 @@ +/* test_sha256.c - TinyCrypt implementation of some SHA-256 tests */ + +/* + * Copyright (C) 2017 by Intel Corporation, 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. + * + * - Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 OWNER 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. + */ + +/* + DESCRIPTION + This module tests the following SHA256 routines: + + Scenarios tested include: + - NIST SHA256 test vectors +*/ + +#include +#include +#include + +#include +#include +#include +#include + +/* + * NIST SHA256 test vector 1. + */ +unsigned int test_1(void) +{ + unsigned int result = TC_PASS; + + TC_PRINT("SHA256 test #1:\n"); + const uint8_t expected[32] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + }; + const char *m = "abc"; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, (const uint8_t *) m, strlen(m)); + (void)tc_sha256_final(digest, &s); + result = check_result(1, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +/* + * NIST SHA256 test vector 2. + */ +unsigned int test_2(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #2:\n"); + const uint8_t expected[32] = { + 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, + 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 + }; + const char *m = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, (const uint8_t *) m, strlen(m)); + (void) tc_sha256_final(digest, &s); + + result = check_result(2, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_3(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #3:\n"); + const uint8_t expected[32] = { + 0x68, 0x32, 0x57, 0x20, 0xaa, 0xbd, 0x7c, 0x82, 0xf3, 0x0f, 0x55, 0x4b, + 0x31, 0x3d, 0x05, 0x70, 0xc9, 0x5a, 0xcc, 0xbb, 0x7d, 0xc4, 0xb5, 0xaa, + 0xe1, 0x12, 0x04, 0xc0, 0x8f, 0xfe, 0x73, 0x2b + }; + const uint8_t m[1] = { 0xbd }; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(3, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_4(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #4:\n"); + const uint8_t expected[32] = { + 0x7a, 0xbc, 0x22, 0xc0, 0xae, 0x5a, 0xf2, 0x6c, 0xe9, 0x3d, 0xbb, 0x94, + 0x43, 0x3a, 0x0e, 0x0b, 0x2e, 0x11, 0x9d, 0x01, 0x4f, 0x8e, 0x7f, 0x65, + 0xbd, 0x56, 0xc6, 0x1c, 0xcc, 0xcd, 0x95, 0x04 + }; + const uint8_t m[4] = { 0xc9, 0x8c, 0x8e, 0x55 }; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(4, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_5(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #5:\n"); + + const uint8_t expected[32] = { + 0x02, 0x77, 0x94, 0x66, 0xcd, 0xec, 0x16, 0x38, 0x11, 0xd0, 0x78, 0x81, + 0x5c, 0x63, 0x3f, 0x21, 0x90, 0x14, 0x13, 0x08, 0x14, 0x49, 0x00, 0x2f, + 0x24, 0xaa, 0x3e, 0x80, 0xf0, 0xb8, 0x8e, 0xf7 + }; + uint8_t m[55]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)memset(m, 0x00, sizeof(m)); + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(5, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_6(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #6:\n"); + const uint8_t expected[32] = { + 0xd4, 0x81, 0x7a, 0xa5, 0x49, 0x76, 0x28, 0xe7, 0xc7, 0x7e, 0x6b, 0x60, + 0x61, 0x07, 0x04, 0x2b, 0xbb, 0xa3, 0x13, 0x08, 0x88, 0xc5, 0xf4, 0x7a, + 0x37, 0x5e, 0x61, 0x79, 0xbe, 0x78, 0x9f, 0xbb + }; + uint8_t m[56]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)memset(m, 0x00, sizeof(m)); + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(6, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_7(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #7:\n"); + const uint8_t expected[32] = { + 0x65, 0xa1, 0x6c, 0xb7, 0x86, 0x13, 0x35, 0xd5, 0xac, 0xe3, 0xc6, 0x07, + 0x18, 0xb5, 0x05, 0x2e, 0x44, 0x66, 0x07, 0x26, 0xda, 0x4c, 0xd1, 0x3b, + 0xb7, 0x45, 0x38, 0x1b, 0x23, 0x5a, 0x17, 0x85 + }; + uint8_t m[57]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)memset(m, 0x00, sizeof(m)); + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(7, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_8(void) +{ + unsigned int result = TC_PASS; + + TC_PRINT("SHA256 test #8:\n"); + const uint8_t expected[32] = { + 0xf5, 0xa5, 0xfd, 0x42, 0xd1, 0x6a, 0x20, 0x30, 0x27, 0x98, 0xef, 0x6e, + 0xd3, 0x09, 0x97, 0x9b, 0x43, 0x00, 0x3d, 0x23, 0x20, 0xd9, 0xf0, 0xe8, + 0xea, 0x98, 0x31, 0xa9, 0x27, 0x59, 0xfb, 0x4b + }; + uint8_t m[64]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)memset(m, 0x00, sizeof(m)); + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(8, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_9(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #9:\n"); + const uint8_t expected[32] = { + 0x54, 0x1b, 0x3e, 0x9d, 0xaa, 0x09, 0xb2, 0x0b, 0xf8, 0x5f, 0xa2, 0x73, + 0xe5, 0xcb, 0xd3, 0xe8, 0x01, 0x85, 0xaa, 0x4e, 0xc2, 0x98, 0xe7, 0x65, + 0xdb, 0x87, 0x74, 0x2b, 0x70, 0x13, 0x8a, 0x53 + }; + uint8_t m[1000]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)memset(m, 0x00, sizeof(m)); + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(9, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_10(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #10:\n"); + const uint8_t expected[32] = { + 0xc2, 0xe6, 0x86, 0x82, 0x34, 0x89, 0xce, 0xd2, 0x01, 0x7f, 0x60, 0x59, + 0xb8, 0xb2, 0x39, 0x31, 0x8b, 0x63, 0x64, 0xf6, 0xdc, 0xd8, 0x35, 0xd0, + 0xa5, 0x19, 0x10, 0x5a, 0x1e, 0xad, 0xd6, 0xe4 + }; + uint8_t m[1000]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)memset(m, 0x41, sizeof(m)); + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(10, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_11(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #11:\n"); + const uint8_t expected[32] = { + 0xf4, 0xd6, 0x2d, 0xde, 0xc0, 0xf3, 0xdd, 0x90, 0xea, 0x13, 0x80, 0xfa, + 0x16, 0xa5, 0xff, 0x8d, 0xc4, 0xc5, 0x4b, 0x21, 0x74, 0x06, 0x50, 0xf2, + 0x4a, 0xfc, 0x41, 0x20, 0x90, 0x35, 0x52, 0xb0 + }; + uint8_t m[1005]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + + (void)memset(m, 0x55, sizeof(m)); + + (void)tc_sha256_init(&s); + tc_sha256_update(&s, m, sizeof(m)); + (void)tc_sha256_final(digest, &s); + + result = check_result(11, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_12(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #12:\n"); + + const uint8_t expected[32] = { + 0xd2, 0x97, 0x51, 0xf2, 0x64, 0x9b, 0x32, 0xff, 0x57, 0x2b, 0x5e, 0x0a, + 0x9f, 0x54, 0x1e, 0xa6, 0x60, 0xa5, 0x0f, 0x94, 0xff, 0x0b, 0xee, 0xdf, + 0xb0, 0xb6, 0x92, 0xb9, 0x24, 0xcc, 0x80, 0x25 + }; + uint8_t m[1000]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + unsigned int i; + + (void)memset(m, 0x00, sizeof(m)); + + (void)tc_sha256_init(&s); + for (i = 0; i < 1000; ++i) { + tc_sha256_update(&s, m, sizeof(m)); + } + (void)tc_sha256_final(digest, &s); + + result = check_result(12, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_13(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #13:\n"); + const uint8_t expected[32] = { + 0x15, 0xa1, 0x86, 0x8c, 0x12, 0xcc, 0x53, 0x95, 0x1e, 0x18, 0x23, 0x44, + 0x27, 0x74, 0x47, 0xcd, 0x09, 0x79, 0x53, 0x6b, 0xad, 0xcc, 0x51, 0x2a, + 0xd2, 0x4c, 0x67, 0xe9, 0xb2, 0xd4, 0xf3, 0xdd + }; + uint8_t m[32768]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + unsigned int i; + + (void)memset(m, 0x5a, sizeof(m)); + + (void)tc_sha256_init(&s); + for (i = 0; i < 16384; ++i) { + tc_sha256_update(&s, m, sizeof(m)); + } + (void)tc_sha256_final(digest, &s); + + result = check_result(13, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +unsigned int test_14(void) +{ + unsigned int result = TC_PASS; + TC_PRINT("SHA256 test #14:\n"); + const uint8_t expected[32] = { + 0x46, 0x1c, 0x19, 0xa9, 0x3b, 0xd4, 0x34, 0x4f, 0x92, 0x15, 0xf5, 0xec, + 0x64, 0x35, 0x70, 0x90, 0x34, 0x2b, 0xc6, 0x6b, 0x15, 0xa1, 0x48, 0x31, + 0x7d, 0x27, 0x6e, 0x31, 0xcb, 0xc2, 0x0b, 0x53 + }; + uint8_t m[32768]; + uint8_t digest[32]; + struct tc_sha256_state_struct s; + unsigned int i; + + (void)memset(m, 0x00, sizeof(m)); + + (void) tc_sha256_init(&s); + for (i = 0; i < 33280; ++i) { + tc_sha256_update(&s, m, sizeof(m)); + } + (void) tc_sha256_final(digest, &s); + + result = check_result(14, expected, sizeof(expected), + digest, sizeof(digest)); + TC_END_RESULT(result); + return result; +} + +/* + * Main task to test AES + */ + +int main(void) +{ + unsigned int result = TC_PASS; + TC_START("Performing SHA256 tests (NIST tests vectors):"); + + result = test_1(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #1 failed.\n"); + goto exitTest; + } + result = test_2(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #2 failed.\n"); + goto exitTest; + } + result = test_3(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #3 failed.\n"); + goto exitTest; + } + result = test_4(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #4 failed.\n"); + goto exitTest; + } + result = test_5(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #5 failed.\n"); + goto exitTest; + } + result = test_6(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #6 failed.\n"); + goto exitTest; + } + result = test_7(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #7 failed.\n"); + goto exitTest; + } + result = test_8(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #8 failed.\n"); + goto exitTest; + } + result = test_9(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #9 failed.\n"); + goto exitTest; + } + result = test_10(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #10 failed.\n"); + goto exitTest; + } + result = test_11(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #11 failed.\n"); + goto exitTest; + } + result = test_12(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #12 failed.\n"); + goto exitTest; + } + /* memory and computation intensive test cases: */ + result = test_13(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #13 failed.\n"); + goto exitTest; + } + result = test_14(); + if (result == TC_FAIL) { + /* terminate test */ + TC_ERROR("SHA256 test #14 failed.\n"); + goto exitTest; + } + + TC_PRINT("All SHA256 tests succeeded!\n"); + +exitTest: + TC_END_RESULT(result); + TC_END_REPORT(result); +} + diff --git a/bootloader/mcuboot/go.mod b/bootloader/mcuboot/go.mod new file mode 100644 index 0000000..8d87521 --- /dev/null +++ b/bootloader/mcuboot/go.mod @@ -0,0 +1,3 @@ +module github.com/mcu-tools/mcuboot + +go 1.14 diff --git a/bootloader/mcuboot/project.yml b/bootloader/mcuboot/project.yml new file mode 100644 index 0000000..37f9ef1 --- /dev/null +++ b/bootloader/mcuboot/project.yml @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +project.name: "mcuboot" + +project.repositories: + - apache-mynewt-core + +# Use github's distribution mechanism for core ASF libraries. +# This provides mirroring automatically for us. +repository.apache-mynewt-core: + type: github + vers: 0-dev + user: apache + repo: mynewt-core diff --git a/bootloader/mcuboot/ptest/.gitignore b/bootloader/mcuboot/ptest/.gitignore new file mode 100644 index 0000000..9409674 --- /dev/null +++ b/bootloader/mcuboot/ptest/.gitignore @@ -0,0 +1,2 @@ +target +.*.swp diff --git a/bootloader/mcuboot/ptest/Cargo.lock b/bootloader/mcuboot/ptest/Cargo.lock new file mode 100644 index 0000000..84f0e48 --- /dev/null +++ b/bootloader/mcuboot/ptest/Cargo.lock @@ -0,0 +1,556 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "4.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi 0.1.19", + "libc", +] + +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptest" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "env_logger", + "failure", + "log", + "num_cpus", + "std-semaphore", + "yaml-rust", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "std-semaphore" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ae9eec00137a8eed469fb4148acd9fc6ac8c3f9b110f52cd34698c8b5bfa0e" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/bootloader/mcuboot/ptest/Cargo.toml b/bootloader/mcuboot/ptest/Cargo.toml new file mode 100644 index 0000000..bb38f93 --- /dev/null +++ b/bootloader/mcuboot/ptest/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ptest" +version = "0.1.0" +authors = ["david.brown"] +edition = "2018" + +[dependencies] +chrono = "0.4" +env_logger = "0.9.0" +failure = "0.1.8" +log = "0.4.17" +num_cpus = "1.13.1" +std-semaphore = "0.1" +yaml-rust = "0.4" + +[dependencies.clap] +version = "4.0" +features = ["derive"] diff --git a/bootloader/mcuboot/ptest/src/main.rs b/bootloader/mcuboot/ptest/src/main.rs new file mode 100644 index 0000000..9a75ccb --- /dev/null +++ b/bootloader/mcuboot/ptest/src/main.rs @@ -0,0 +1,374 @@ +//! Parallel testing. +//! +//! mcuboot simulator is strictly single threaded, as there is a lock around running the C startup +//! code, because it contains numerous global variables. +//! +//! To help speed up testing, the Workflow configuration defines all of the configurations that can +//! be run in parallel. Fortunately, cargo works well this way, and these can be run by simply +//! using subprocess for each particular thread. +//! +//! For now, we assume all of the features are listed under +//! jobs->environment->strategy->matric->features + +use chrono::Local; +use clap::{Parser, Subcommand}; +use log::{debug, error, warn}; +use std::{ + collections::HashSet, + env, + fs::{self, OpenOptions}, + io::{ErrorKind, stdout, Write}, + process::Command, + result, + sync::{ + Arc, + Mutex, + }, + thread, + time::Duration, +}; +use std_semaphore::Semaphore; +use yaml_rust::{ + Yaml, + YamlLoader, +}; + +type Result = result::Result; + +fn main() -> Result<()> { + env_logger::init(); + + let args = Cli::parse(); + + let workflow_text = fs::read_to_string(&args.workflow)?; + let workflow = YamlLoader::load_from_str(&workflow_text)?; + + let ncpus = num_cpus::get(); + let limiter = Arc::new(Semaphore::new(ncpus as isize)); + + let matrix = Matrix::from_yaml(&workflow); + + let matrix = if args.test.len() == 0 { matrix } else { + matrix.only(&args.test) + }; + + match args.command { + Commands::List => { + matrix.show(); + return Ok(()); + } + Commands::Run => (), + } + + let mut children = vec![]; + let state = State::new(matrix.envs.len()); + let st2 = state.clone(); + let _status = thread::spawn(move || { + loop { + thread::sleep(Duration::new(15, 0)); + st2.lock().unwrap().status(); + } + }); + for env in matrix.envs { + let state = state.clone(); + let limiter = limiter.clone(); + + let child = thread::spawn(move || { + let _run = limiter.access(); + state.lock().unwrap().start(&env); + let out = env.run(); + state.lock().unwrap().done(&env, out); + }); + children.push(child); + } + + for child in children { + child.join().unwrap(); + } + + println!(); + + Ok(()) +} + +/// The main Cli. +#[derive(Debug, Parser)] +#[command(name = "ptest")] +#[command(about = "Run MCUboot CI tests stand alone")] +struct Cli { + /// The workflow file to use. + #[arg(short, long, default_value = "../.github/workflows/sim.yaml")] + workflow: String, + + /// The tests to run (defaults to all) + #[arg(short, long)] + test: Vec, + + #[command(subcommand)] + command: Commands, +} + +#[derive(Debug, Subcommand)] +enum Commands { + /// Runs the tests. + Run, + /// List available tests. + List, +} + +/// State, for printing status. +struct State { + running: HashSet, + done: HashSet, + total: usize, +} + +/// Result of a test run. +struct TestResult { + /// Was this run successful. + success: bool, + + /// The captured output. + output: Vec, +} + +impl State { + fn new(total: usize) -> Arc> { + Arc::new(Mutex::new(State { + running: HashSet::new(), + done: HashSet::new(), + total, + })) + } + + fn start(&mut self, fs: &FeatureSet) { + let key = fs.textual(); + if self.running.contains(&key) || self.done.contains(&key) { + warn!("Duplicate: {:?}", key); + } + debug!("Starting: {} ({} running)", key, self.running.len() + 1); + self.running.insert(key); + self.status(); + } + + fn done(&mut self, fs: &FeatureSet, output: Result) { + let key = fs.textual(); + self.running.remove(&key); + self.done.insert(key.clone()); + match output { + Ok(output) => { + if !output.success || log_all() { + // Write the output into a file. + let mut count = 1; + let (mut fd, logname) = loop { + let base = if output.success { "success" } else { "failure" }; + let name = format!("./{}-{:04}.log", base, count); + count += 1; + match OpenOptions::new() + .create_new(true) + .write(true) + .open(&name) + { + Ok(file) => break (file, name), + Err(ref err) if err.kind() == ErrorKind::AlreadyExists => continue, + Err(err) => { + error!("Unable to write log file to current directory: {:?}", err); + return; + } + } + }; + fd.write_all(&output.output).unwrap(); + if !output.success { + error!("Failure {} log:{:?} ({} running)", key, logname, + self.running.len()); + } + } + } + Err(err) => { + error!("Unable to run test {:?} ({:?}", key, err); + } + } + self.status(); + } + + fn status(&self) { + let running = self.running.len(); + let done = self.done.len(); + print!(" {} running ({}/{}/{} done)\r", running, done, running + done, self.total); + stdout().flush().unwrap(); + } +} + +/// The extracted configurations from the workflow config +#[derive(Debug)] +struct Matrix { + envs: Vec, +} + +#[derive(Debug, Eq, Hash, PartialEq)] +struct FeatureSet { + // The environment variable to set. + env: String, + // The successive values to set it to. + values: Vec, +} + +impl Matrix { + fn from_yaml(yaml: &[Yaml]) -> Matrix { + let mut envs = vec![]; + + let mut all_tests = HashSet::new(); + + for y in yaml { + let m = match lookup_matrix(y) { + Some (m) => m, + None => continue, + }; + for elt in m { + let elt = match elt.as_str() { + None => { + warn!("Unexpected yaml: {:?}", elt); + continue; + } + Some(e) => e, + }; + let fset = FeatureSet::decode(elt); + + if false { + // Respect the groupings in the `.workflow.yml` file. + envs.push(fset); + } else { + // Break each test up so we can run more in + // parallel. + let env = fset.env.clone(); + for val in fset.values { + if !all_tests.contains(&val) { + all_tests.insert(val.clone()); + envs.push(FeatureSet { + env: env.clone(), + values: vec![val], + }); + } else { + warn!("Duplicate: {:?}: {:?}", env, val); + } + } + } + } + } + + Matrix { + envs, + } + } + + /// Print out all of the feature sets. + fn show(&self) { + for (i, feature) in self.envs.iter().enumerate() { + println!("{:3}. {}", i + 1, feature.simple_textual()); + } + } + + /// Replace this matrix with one that only has the chosen tests in it. Note + /// that the original order is preserved, not that given in `pick`. + fn only(self, pick: &[usize]) -> Self { + let pick: HashSet = pick.iter().cloned().collect(); + let envs: Vec<_> = self + .envs + .into_iter() + .enumerate() + .filter(|(ind, _)| pick.contains(&(ind + 1))) + .map(|(_, item)| item) + .collect(); + Matrix { envs } + } +} + +impl FeatureSet { + fn decode(text: &str) -> FeatureSet { + // The github workflow is just a space separated set of values. + let values: Vec<_> = text + .split(',') + .map(|s| s.to_string()) + .collect(); + FeatureSet { + env: "MULTI_FEATURES".to_string(), + values, + } + } + + /// Run a test for this given feature set. Output is captured and will be returned if there is + /// an error. Each will be run successively, and the first failure will be returned. + /// Otherwise, it returns None, which means everything worked. + fn run(&self) -> Result { + let mut output = vec![]; + let mut success = true; + for v in &self.values { + let cmdout = Command::new("bash") + .arg("./ci/sim_run.sh") + .current_dir("..") + .env(&self.env, v) + .output()?; + // Grab the output for logging, etc. + writeln!(&mut output, "Test {} {}", + if cmdout.status.success() { "success" } else { "FAILURE" }, + self.textual())?; + writeln!(&mut output, "time: {}", Local::now().to_rfc3339())?; + writeln!(&mut output, "----------------------------------------")?; + writeln!(&mut output, "stdout:")?; + output.extend(&cmdout.stdout); + writeln!(&mut output, "----------------------------------------")?; + writeln!(&mut output, "stderr:")?; + output.extend(&cmdout.stderr); + if !cmdout.status.success() { + success = false; + } + } + Ok(TestResult { success, output }) + } + + /// Convert this feature set into a textual representation + fn textual(&self) -> String { + use std::fmt::Write; + + let mut buf = String::new(); + + write!(&mut buf, "{}:", self.env).unwrap(); + for v in &self.values { + write!(&mut buf, " {}", v).unwrap(); + } + + buf + } + + /// Generate a simpler textual representation, without showing the environment. + fn simple_textual(&self) -> String { + use std::fmt::Write; + + let mut buf = String::new(); + for v in &self.values { + write!(&mut buf, " {}", v).unwrap(); + } + + buf + } +} + +fn lookup_matrix(y: &Yaml) -> Option<&Vec> { + let jobs = Yaml::String("jobs".to_string()); + let environment = Yaml::String("environment".to_string()); + let strategy = Yaml::String("strategy".to_string()); + let matrix = Yaml::String("matrix".to_string()); + let features = Yaml::String("features".to_string()); + y + .as_hash()?.get(&jobs)? + .as_hash()?.get(&environment)? + .as_hash()?.get(&strategy)? + .as_hash()?.get(&matrix)? + .as_hash()?.get(&features)? + .as_vec() +} + +/// Query if we should be logging all tests and not only failures. +fn log_all() -> bool { + env::var("PTEST_LOG_ALL").is_ok() +} diff --git a/bootloader/mcuboot/repository.yml b/bootloader/mcuboot/repository.yml new file mode 100644 index 0000000..f69b449 --- /dev/null +++ b/bootloader/mcuboot/repository.yml @@ -0,0 +1,48 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +repo.name: mcuboot +repo.submodules: "" +repo.versions: + "0.0.0": "main" + "0.9.0": "v0.9.0" + "1.0.0": "v1.0.0" + "1.1.0": "v1.1.0" + "1.2.0": "v1.2.0" + "1.3.0": "v1.3.0" + "1.3.1": "v1.3.1" + "1.4.0": "v1.4.0" + "1.5.0": "v1.5.0" + "1.6.0": "v1.6.0" + "1.7.0": "v1.7.0" + "1.7.1": "v1.7.1" + "1.7.2": "v1.7.2" + "1.8.0": "v1.8.0" + "1.9.0": "v1.9.0" + "1.10.0": "v1.10.0" + "2.0.0": "v2.0.0" + "2.1.0": "v2.1.0" + + "0-dev": "0.0.0" # main + "0-latest": "2.1.0" # latest stable release + "1-latest": "1.11.0" + "2-latest": "2.1.0" + + "1.0-latest": "1.11.0" + "2.0-latest": "2.1.0" diff --git a/bootloader/mcuboot/root-ec-p256-pkcs8.pem b/bootloader/mcuboot/root-ec-p256-pkcs8.pem new file mode 100644 index 0000000..42a1e0e --- /dev/null +++ b/bootloader/mcuboot/root-ec-p256-pkcs8.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg15jVL4MBJDvTVCt+ +Ve1MdGEZALD5UFqCT+Ho7AY7z/GhRANCAAQqy0A86P7tW6RJlaGpHa7o274ZN80U ++y8kVzfllTmI2ZS51lrr183VMIrW/kiySmqBDuXwfYtoNMw6avxTjvrB +-----END PRIVATE KEY----- diff --git a/bootloader/mcuboot/root-ec-p256.pem b/bootloader/mcuboot/root-ec-p256.pem new file mode 100644 index 0000000..2f4accf --- /dev/null +++ b/bootloader/mcuboot/root-ec-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEINeY1S+DASQ701QrflXtTHRhGQCw+VBagk/h6OwGO8/xoAoGCCqGSM49 +AwEHoUQDQgAEKstAPOj+7VukSZWhqR2u6Nu+GTfNFPsvJFc35ZU5iNmUudZa69fN +1TCK1v5IskpqgQ7l8H2LaDTMOmr8U476wQ== +-----END EC PRIVATE KEY----- diff --git a/bootloader/mcuboot/root-ec-p384-pkcs8.pem b/bootloader/mcuboot/root-ec-p384-pkcs8.pem new file mode 100644 index 0000000..4d4894c --- /dev/null +++ b/bootloader/mcuboot/root-ec-p384-pkcs8.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDC8ZQWjooCCaLQJ9DJN +KMyPoUoFcqGluXGu13Zf526RX6TdRhnkExtL1T7fC13n32ChZANiAAQMdsqucjql +6PDU8Ra1Au93oRuTYXjACSZ7O0Cc7kmF4MlP5/K6l2zzgmUULPUMczNNMueb00LM +lVrl4vX0bkXg7SA1XK9SNYHU3JzjniI++z8iENpwAzetqPJI/jpgaaU= +-----END PRIVATE KEY----- diff --git a/bootloader/mcuboot/root-ec-p384.pem b/bootloader/mcuboot/root-ec-p384.pem new file mode 100644 index 0000000..916c800 --- /dev/null +++ b/bootloader/mcuboot/root-ec-p384.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDC8ZQWjooCCaLQJ9DJNKMyPoUoFcqGluXGu13Zf526RX6TdRhnkExtL +1T7fC13n32CgBwYFK4EEACKhZANiAAQMdsqucjql6PDU8Ra1Au93oRuTYXjACSZ7 +O0Cc7kmF4MlP5/K6l2zzgmUULPUMczNNMueb00LMlVrl4vX0bkXg7SA1XK9SNYHU +3JzjniI++z8iENpwAzetqPJI/jpgaaU= +-----END EC PRIVATE KEY----- diff --git a/bootloader/mcuboot/root-ed25519.pem b/bootloader/mcuboot/root-ed25519.pem new file mode 100644 index 0000000..8bc7181 --- /dev/null +++ b/bootloader/mcuboot/root-ed25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEICjJcMmhqKi8d3hoYtLnbQ1DCitXyHz8N5BLy6rIdMvY +-----END PRIVATE KEY----- diff --git a/bootloader/mcuboot/root-rsa-2048.pem b/bootloader/mcuboot/root-rsa-2048.pem new file mode 100644 index 0000000..78c0c34 --- /dev/null +++ b/bootloader/mcuboot/root-rsa-2048.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA0QYIGhhELBjo+/33DaNPH7vuXvmq0ksY01rpbRiAGfnwnDQb +y/O8dNtC54x/EFN+Q14NVyxE0WcIDw27XO7ss5nf4E2EC6p3QWDtFShJpwG0PBDm +aYwvX6xBTZ5cFN/y+M89Hm/nW7q0qciIfkc8lMN3Z1RLqo04NcpiYX634RXbd3PU +vntyIYlpJPv4ZW5kPsgO14XVXErkUw0v/7f98xM5gz+jrtIPp2qd+f64zvoqvq+4 +4PqCN1T0PuEr0NMIWBj2XkzIiIExrV+wghfyimknI/Orhz6TGh3+6PgaJGZZ+Byr +3M5oG2ZkNez6DRGdr1w6p9FnxkfvsUssYuHRyQIDAQABAoIBAEahFCHFK1v/OtLT +eSSZl0Xw2dYr5QXULFpWsOOVUMv2QdB2ZyIehQKziEL3nYPlwpd+82EOa16awwVb +LYF0lnUFvLltV/4dJtjnqJTqnSCamc1mJIVrwiJA8XwJ07GWDuL2G//p7jJ3v05T +nZOV/KmD9xfqSvshZun+LgolqHqcrAa1f4cmuP9C9oqenZryljyfj7piaIZGI0JR +PrJJ5kImYJqRcMgKTyHP4L8nwQ4moMJr6zbfbWxxb5TC7KVZSQ9UKZZ+ZLuy/pkU +Qe4G8XSE0r+R9u4JCg87I1vgHhn8WJSxVX027OVUq5HfOzg2skQBTcExph5V9B2b +onNxd8UCgYEA/32PW+ZwRcdKXMj+QVkxXUd6xkXy7mTXPEaQuOLWZQgrSqAFH1l4 +5/6d099KAJrjM6kR8pKXtz72IIyMHTPm332ghymjKvaEl2XP9sF+f6FmYURar4y6 +8Zh3eivP86+Q/YzOGKwtRSziBMzrAfoIXgtwH8pwIPYLP3zBV4449ZsCgYEA0XC/ +gu2ub5M6EXBnjq9K2d4LlTyAPsIbAcMSwkhOUH4YJFS22qXLYQUA9zM+DUyLCrl/ +PKN2G0HQVgMb4DIbeHv8kXB5oGm5zfbWorWqOomXB3AsI7X8YDMtf/PsZV2amBei +qVskmPJQV21qFyeOcHlT+dHuRb0O0un3dK8RHmsCgYEApDCH4dJ80osZoflVVJ/C +VqTqJOOtFEFgBQ+AUCEPEQyn7aRaxmPUjJsXyKJVx3/ChV+g9hf5Qj1HJXHNVbMW +KwhsEpDSmHimizlV5clBxzntNpMcCHdTaJHILo5bbMqmThugE0ELMsp+UgFzAeky +WWXWX8fUOYqFff5prh/rQQMCgYBQQ8FhT+113Rp37HgDerJY5HvT6afMZV8sQbJC +uqsotepSohShnsBeoihIlF7HgfoXVhepCYwNzh8ll3NrbEiS2BFnO4+hJmOKx3pi +SPTAElLLCvYfiXL6+yII01ZZUpIYj5ZLCR7xbovTtZ7e2M4B1L2WFBoYp+eydO/c +y+rnmQKBgCh0gfyBT/OKPkfKv+Bbt8HcoxgEj+TyH+vYdeTbP9ZSJ6y5utMbPg7z +iLLbJ+9IcSwPCwJSmI+I0Om4xEp4ZblCrzAG7hWvG2NNzxQjmoOOrAANyTvJR/ap +N+UkQA4WrMSKEYyBlRS/hR9Unz31vMc2k9Re0ukWhWh/QksQGDfJ +-----END RSA PRIVATE KEY----- diff --git a/bootloader/mcuboot/root-rsa-3072.pem b/bootloader/mcuboot/root-rsa-3072.pem new file mode 100644 index 0000000..3a3b329 --- /dev/null +++ b/bootloader/mcuboot/root-rsa-3072.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG5AIBAAKCAYEAtCwOmFgQpKdYmXwB3QgqKDQz+JYaNCBdRchxJiXl0pbqe7EV +qqaKYyKLLU6Bc79uFWiMGvTvKo+MIp5xV0veD35y03q4px1ErYcAg1z9cwVyRj+L ++RAA2G7Mhe35Sdt4NoBJOHbdX1QE2ow0pysTJW/RFU+twuGl0k5XDH6cm7pOaLLg +JQKqANO0zC945b5HZx/IbiJsXmG2ms3lqLp6gBMbFy6W7c+zm+Qc6K2n9jpRZl6Z +jofuYCX4jb7OpKjKk2zXv9RzM41EhcxzMAicTbKqWmxve6u3s3zD++fKxPiab8u7 +W4LneugZ/S8RIvt/doxrlKQJT6Vqd1Hrp37ahwbu3L7R6hpAHRv/GrFRfBKw8/aD +AZznDJm/rGhYcqSwWYXuhawqIvTPFQiAHw3QHqCglMj3+mXdUuiWNyMwVzbmnfQM +SgV1H60ByrdtjEN0BgqB8wFi//f1X6/nKw74gbVl3QHZnwcXihjPI26IZZG1e9Ow +La+TZmN0rFrmc947AgMBAAECggGBAJjDTjCvYpUo6r9gXHgbZxslf/dC1b7ivhLf +68gLk/xlRzVPJW7GvElnzZfBm5MXeXAfb8Ofb3WntorXyoPY6NQ8Q4G56PyQnV2A +PNgkrSSsNoMHeFfZ0M2xzCm2Z4rO0fNr/Ckq53HfXCotfKtMo3Q3hZDLOSomhqF1 +GOuYIpMLeVXebJwU0S3YUtBZY+lv1zzBvwBaoYW41coVz6arThhvmqWiNAg49jG0 +TSqfquzj74abqRkned798uvEH5OPJJ6r/7HrjbSan2HfhiQgWebR6NV5xtmAy//2 +iuNqrm9e1Qrinjo1teHP63hzGQbk9HzXdIzMmw83vHOppFmteOCSxJIxJ2h4RlsV +ln/7O5N4jAhdpqu/5AWBlWHefEsWvxfGUwmBdtxF0M4p+Z5qGIIX/WGHx+Eg9qsP +1NhnMhQ8DJjCPNzej7dmsiHcICsI/SJ84Xby5KlG8OJbsYSpzyd4/PD789VXSTK/ +BwIN3IZsWhNw1G7AOfzch86fXNpCoQKBwQDlvyQII6M8etnZMYGFNKDxpcRahrF4 +znENW3IDsm41044oy+qv8fR7NFI+88xcbm/zzQ+oz4FGAcTNfUa1JAII/h1oJx7Q +eAFytlGq41cxnGfVGSemmpEOgZqzWVneFnZ8s/81sq35/OzpQ+pKgTN8SYgjySQb +f5pJECHV3oaX6g/DZcEU6JngYWyG4gAovBph/EXaPn3saHUo7u75yD6HnLxcxa4C +//Zs9NOHmRDxW1AFq6c9qqHjAwiwoWz/1PECgcEAyMKx1WjcdW7AU4TmMoWY7dwc +ginhBzEne7z5hgnabsnFaudRpAix/IBT0G0/lDEBXja+j8V56Jy7xCVLmJi044nj +fzCn0bbtOHOE3BztGP7eWRODN+2u/V9xk6v7y4R7NOLzE7+1/SupaYWW5uQo9Z1v +CamU7MuIXq6b9BS3HD8vS9IpeDQIJKQ9G0NIRN9naLwyfLk0EXBDqE0VlXWusDi8 +B9W5ZQ0l9dY2XLCx8Gx7Foc8UA85EvivtfpC3bXrAoHACywfYXHyNze2LlS0+rhT +d0zbXpecO8a2QrMGuV1M9Lsj96Hq+MFoZTFnKn6KmpgYQ5/eOhRVMgVV/7Qu4xIs +MynAXldArVyYnW52TDwf+l6jwf4mKnjrwuvUjRI0R5OKEYhjScY1pamCD9noo9Ti +nxGoWC0o31l2NEVfj9nxa6PLPnJNUGn7SakTMP/+h/yVv9wXvYQ6dWui/umXn3f4 +annZwx0t2CGAZ04El1x/MW2CV7RAPsR0eOil3IkNFufRAoHAVDtj879oaBkMtr4W ++3GURZBJoc9CbAsSntcd9kAiFsOvgfgGCAXh76hEAjokJ+Aby9S6RYY8bP19xoFD +Y4YGt0U+Xzoh31qZ00qcnuHAFPGyhrsqHggqmII4HBZXsf8m1ny2Mj4IdG2iSfTT +6JIoIU1prispoeSPlfI62sDqRv63sF9AKP/jvsPuI4cqRkNZltcHc88c6ogoyu90 +s93JaoSTV9IzVBOdLrUu39r+/Xn2dvBMvOZ2MuCGkJqs/Wr7AoHBAL1k1bH0+xs3 +sMQjxXyo5CYe5fmi1y8Gnt05kivgvGaGPLIb2lG8JRfIhasO04DrvxnW5EBiap6s +rtAF+MU55/EjXn97fs1TvDRwZ2/SJRCACHepFnXrPMz1dBBe8IG7s8Ai5WTDuVbb +7T4/8fwSrqAnIF7oQwHFwlpDnFGUJLZCJ/+H/jjcn2TgTnGIENTwlSIofhcWT+Ot +GtlLLU4J6+l07vVfueEofoJMfqWjDUpOr3FmnP+K2drZWFnzv/3Rgg== +-----END RSA PRIVATE KEY----- diff --git a/bootloader/mcuboot/samples/mcuboot_config/mcuboot_config.template.h b/bootloader/mcuboot/samples/mcuboot_config/mcuboot_config.template.h new file mode 100644 index 0000000..ce613bd --- /dev/null +++ b/bootloader/mcuboot/samples/mcuboot_config/mcuboot_config.template.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2018 Open Source Foundries Limited + * Copyright (c) 2019-2024 Arm Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_CONFIG_H__ +#define __MCUBOOT_CONFIG_H__ + +/* + * Template configuration file for MCUboot. + * + * When porting MCUboot to a new target, copy it somewhere that your + * include path can find it as mcuboot_config/mcuboot_config.h, and + * make adjustments to suit your platform. + * + * For examples, see: + * + * boot/zephyr/include/mcuboot_config/mcuboot_config.h + * boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h + */ + +/* + * Signature types + * + * You must choose exactly one signature type. + */ + +/* Uncomment for RSA signature support */ +/* #define MCUBOOT_SIGN_RSA */ + +/* Uncomment for ECDSA signatures using curve P-256. */ +/* #define MCUBOOT_SIGN_EC256 */ + +/* + * Public key handling + * + * Choose one or none from the different public key handling options. + */ + +/* Uncomment to use key hash(es) instead of incorporating + * the public key into the code. */ +/* #define MCUBOOT_HW_KEY */ +/* Uncomment to use builtin key(s) instead of incorporating + * the public key into the code. */ +/* #define MCUBOOT_BUILTIN_KEY */ + +/* + * Upgrade mode + * + * The default is to support A/B image swapping with rollback. Other modes + * with simpler code path, which only supports overwriting the existing image + * with the update image or running the newest image directly from its flash + * partition, are also available. + * + * You can enable only one mode at a time from the list below to override + * the default upgrade mode. + */ + +/* Uncomment to enable the overwrite-only code path. */ +/* #define MCUBOOT_OVERWRITE_ONLY */ + +#ifdef MCUBOOT_OVERWRITE_ONLY +/* Uncomment to only erase and overwrite those primary slot sectors needed + * to install the new image, rather than the entire image slot. */ +/* #define MCUBOOT_OVERWRITE_ONLY_FAST */ +#endif + +/* Uncomment to enable the direct-xip code path. */ +/* #define MCUBOOT_DIRECT_XIP */ +/* Uncomment to enable the revert mechanism in direct-xip mode. */ +/* #define MCUBOOT_DIRECT_XIP_REVERT */ + +/* Uncomment to enable the ram-load code path. */ +/* #define MCUBOOT_RAM_LOAD */ + +/* + * Cryptographic settings + * + * You must choose between mbedTLS and Tinycrypt as source of + * cryptographic primitives. Other cryptographic settings are also + * available. + */ + +/* Uncomment to use ARM's mbedTLS cryptographic primitives */ +/* #define MCUBOOT_USE_MBED_TLS */ +/* Uncomment to use Tinycrypt's. */ +/* #define MCUBOOT_USE_TINYCRYPT */ + +/* + * Encrypted images + * + * Uncomment one of the below options (MCUBOOT_ENCRYPT_x) to enable + * encrypted image upgrades. + */ + +/* Uncomment to use RSA-OAEP for key encryption */ +/* #define MCUBOOT_ENCRYPT_RSA */ +/* Uncomment to use AES-KW for key encryption */ +/* #define MCUBOOT_ENCRYPT_KW */ +/* Uncomment to use ECIES-P256 for key encryption */ +/* #define MCUBOOT_ENCRYPT_EC256 */ +/* Uncomment to use ECIES-X25519 for key encryption */ +/* #define MCUBOOT_ENCRYPT_X25519 */ + +/* Uncomment to use a builtin key-encryption key (retrieved from a trusted + * source - if implemented) instead of a key embedded in the bootloader. */ +/* #define MCUBOOT_ENC_BUILTIN_KEY */ + +#if defined(MCUBOOT_ENCRYPT_RSA) || \ + defined(MCUBOOT_ENCRYPT_KW) || \ + defined(MCUBOOT_ENCRYPT_EC256) || \ + defined(MCUBOOT_ENCRYPT_X25519) +#define MCUBOOT_ENC_IMAGES +#endif + +/* + * Always check the signature of the image in the primary slot before booting, + * even if no upgrade was performed. This is recommended if the boot + * time penalty is acceptable. + */ +#define MCUBOOT_VALIDATE_PRIMARY_SLOT + +/* + * Flash abstraction + */ + +/* Uncomment if your flash map API supports flash_area_get_sectors(). + * See the flash APIs for more details. */ +/* #define MCUBOOT_USE_FLASH_AREA_GET_SECTORS */ + +/* Default maximum number of flash sectors per image slot; change + * as desirable. */ +#define MCUBOOT_MAX_IMG_SECTORS 128 + +/* Default number of separately updateable images; change in case of + * multiple images. */ +#define MCUBOOT_IMAGE_NUMBER 1 + +/* + * Logging + */ + +/* + * If logging is enabled the following functions must be defined by the + * platform: + * + * MCUBOOT_LOG_MODULE_REGISTER(domain) + * Register a new log module and add the current C file to it. + * + * MCUBOOT_LOG_MODULE_DECLARE(domain) + * Add the current C file to an existing log module. + * + * MCUBOOT_LOG_ERR(...) + * MCUBOOT_LOG_WRN(...) + * MCUBOOT_LOG_INF(...) + * MCUBOOT_LOG_DBG(...) + * + * The function priority is: + * + * MCUBOOT_LOG_ERR > MCUBOOT_LOG_WRN > MCUBOOT_LOG_INF > MCUBOOT_LOG_DBG + */ +#define MCUBOOT_HAVE_LOGGING 1 + +/* + * Assertions + */ + +/* Uncomment if your platform has its own mcuboot_config/mcuboot_assert.h. + * If so, it must provide an ASSERT macro for use by bootutil. Otherwise, + * "assert" is used. */ +/* #define MCUBOOT_HAVE_ASSERT_H */ + +/* + * Watchdog feeding + */ + +/* This macro might be implemented if the OS / HW watchdog is enabled while + * doing a swap upgrade and the time it takes for a swapping is long enough + * to cause an unwanted reset. If implementing this, the OS main.c must also + * enable the watchdog (if required)! + * + * #define MCUBOOT_WATCHDOG_FEED() + * do { do watchdog feeding here! } while (0) + */ + +/* If a OS ports support single thread mode or is bare-metal then: + * This macro implements call that switches CPU to an idle state, from which + * the CPU may be woken up by, for example, UART transmission event. + * + * Otherwise this macro should be no-op. + */ +#define MCUBOOT_CPU_IDLE() \ + do { \ + } while (0) + +#endif /* __MCUBOOT_CONFIG_H__ */ diff --git a/bootloader/mcuboot/samples/zephyr/.gitignore b/bootloader/mcuboot/samples/zephyr/.gitignore new file mode 100644 index 0000000..9fa3b42 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/.gitignore @@ -0,0 +1,2 @@ +*.bin +test-images.zip diff --git a/bootloader/mcuboot/samples/zephyr/Makefile b/bootloader/mcuboot/samples/zephyr/Makefile new file mode 100644 index 0000000..dd73702 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/Makefile @@ -0,0 +1,295 @@ +########################################################################### +# Sample multi-part application Makefile +# +# Copyright (c) 2017 Linaro Limited +# Copyright (c) 2017 Open Source Foundries Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# This is an example Makefile to demonstrate how to use mcuboot to +# deploy and upgrade images. The image building should work on any +# supported target, however flashing will likely require changes to +# the flash addresses, depending on the partition layout of the device +# in question. +# +# running +# +# make BOARD=frdm_k64f all +# +# should generate three "*.bin" files in this directory: +# +# mcuboot.bin: The bootloader itself +# signed-hello1.bin: A signed sample. +# signed-hello2.bin: An upgrade image, signed and marked for +# upgrade. +# +# "make flash_boot" should flash the bootloader into the flash, +# erasing the rest of the device. If you examine the device at this +# time, you should see a message about the bootloader not being able +# to find a bootable image. +# +# "make flash_hello1" will then flash the first application into the +# "primary slot". This should boot into this app, print a small message, and +# give the zephyr console. +# +# "make flash_hello2" will flash hello2 into the "secondary slot". The +# reset should upgrade and run the new image. Resetting again should +# then revert back to the first app, since we did not mark this image +# as good. + +# Extra .conf fragments to merge into the MCUboot .config, as a +# semicolon-separated list (i.e., a CMake list). +BOOTLOADER_OVERLAY_CONFIG ?= + +BOARD ?= frdm_k64f +SLOT_SIZE ?= 0x60000 +BOOT_ADDR ?= 0x0 +IMG0_ADDR ?= 0x20000 +IMG1_ADDR ?= 0x80000 + +.PHONY: check boot hello1 clean_boot clean_hello1 \ + hello2 clean_hello2 flash_boot flash_hello1 flash_hello2 + +# For signing, use the default RSA demo key, to match the default in +# the mcuboot Makefile. +SIGNING_KEY ?= ../../root-rsa-2048.pem + +# The header size should match that in hello1/prj.conf +# CONFIG_TEXT_SECTION_OFFSET. This value needs to be a power of two +# that is at least as large as the size of the vector table. The +# value given here of 0x200 should be sufficient for any supported +# devices, but it can be made smaller, as long as this value matches +# that used to build the app. +BOOT_HEADER_LEN = 0x200 + +# For upgrades, the signing tool needs to know the device alignment. +# This requirement will be going away soon. +FLASH_ALIGNMENT = 8 + +IMGTOOL = ../../scripts/imgtool.py +ASSEMBLE = ../../scripts/assemble.py +PYOCD = pyocd + +SOURCE_DIRECTORY := $(CURDIR) +BUILD_DIRECTORY := $(CURDIR)/build/$(BOARD) +BUILD_DIR_BOOT := $(BUILD_DIRECTORY)/mcuboot +BUILD_DIR_HELLO1 := $(BUILD_DIRECTORY)/hello1 +BUILD_DIR_HELLO2 := $(BUILD_DIRECTORY)/hello2 + +help: + @echo "make BOARD=" + @echo ": all, boot, hello1, full.bin" + @echo ": frdm_k64f only for now" + +all: boot hello1 hello2 + +full.bin: boot hello1 hello2 + $(ASSEMBLE) -b $(BUILD_DIR_BOOT) \ + -z $(ZEPHYR_BASE) \ + -p signed-hello1.bin \ + -s signed-hello2.bin \ + -o full.bin + +clean: clean_boot clean_hello1 clean_hello2 + @rm -f signed-hello1.bin + @rm -f signed-hello2.bin + @rm -f mcuboot.bin + +boot: check + @rm -f mcuboot.bin + (mkdir -p $(BUILD_DIR_BOOT) && \ + cd $(BUILD_DIR_BOOT) && \ + cmake -DOVERLAY_CONFIG=$(BOOTLOADER_OVERLAY_CONFIG) \ + -G"Ninja" \ + -DBOARD=$(BOARD) \ + $(SOURCE_DIRECTORY)/../../boot/zephyr && \ + ninja) + cp $(BUILD_DIR_BOOT)/zephyr/zephyr.bin mcuboot.bin + +clean_boot: check + rm -rf $(BUILD_DIR_BOOT) + +# Build and sign "hello1". +hello1: check + (mkdir -p $(BUILD_DIR_HELLO1) && \ + cd $(BUILD_DIR_HELLO1) && \ + cmake -DFROM_WHO=hello1 \ + -G"Ninja" \ + -DBOARD=$(BOARD) \ + $(SOURCE_DIRECTORY)/hello-world && \ + ninja) + $(IMGTOOL) sign \ + --key $(SIGNING_KEY) \ + --header-size $(BOOT_HEADER_LEN) \ + --align $(FLASH_ALIGNMENT) \ + --version 1.2 \ + --slot-size $(SLOT_SIZE) \ + $(BUILD_DIR_HELLO1)/zephyr/zephyr.bin \ + signed-hello1.bin + +clean_hello1: check + rm -rf $(BUILD_DIR_HELLO1) + +# Build and sign "hello2". +# This is the same signing command as above, except that it adds the +# "--pad" argument. This will also add the trailer that indicates +# this image is intended to be an upgrade. It should be flashed into +# the secondary slot instead of the primary slot. +hello2: check + (mkdir -p $(BUILD_DIR_HELLO2) && \ + cd $(BUILD_DIR_HELLO2) && \ + cmake -DFROM_WHO=hello2 \ + -G"Ninja" \ + -DBOARD=$(BOARD) \ + $(SOURCE_DIRECTORY)/hello-world && \ + ninja) + $(IMGTOOL) sign \ + --key $(SIGNING_KEY) \ + --header-size $(BOOT_HEADER_LEN) \ + --align $(FLASH_ALIGNMENT) \ + --version 1.2 \ + --slot-size $(SLOT_SIZE) \ + --pad \ + $(BUILD_DIR_HELLO2)/zephyr/zephyr.bin \ + signed-hello2.bin + +clean_hello2: check + rm -rf $(BUILD_DIR_HELLO2) + +# These flash_* targets use pyocd to flash the images. The addresses +# are hardcoded at this time. + +flash_boot: + $(PYOCD) flash -e chip -a $(BOOT_ADDR) mcuboot.bin + +flash_hello1: + $(PYOCD) flash -a $(IMG0_ADDR) signed-hello1.bin + +flash_hello2: + $(PYOCD) flash -a $(IMG1_ADDR) signed-hello2.bin + +flash_full: + $(PYOCD) flash -e chip -a $(BOOT_ADDR) full.bin + +# These test- targets reinvoke make with the configuration set to test +# various configurations. This will generally be followed by using +# the above flash targets. + +# Test a good image, with a good upgrade, using RSA signatures. +# flash_boot: Unable to find bootable image +# flash_hello1: hello1 runs +# flash_hello2: hello2 runs +# reset: hello1 runs +test-good-rsa: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-rsa.conf \ + all + +# Test a good image, with a good upgrade, using ECDSA signatures. +# flash_boot: Unable to find bootable image +# flash_hello1: hello1 runs +# flash_hello2: hello2 runs +# reset: hello1 runs +test-good-ecdsa: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-ecdsa-p256.conf \ + SIGNING_KEY=../../root-ec-p256.pem \ + all + +# Test (with RSA) that overwrite-only works. This should boot, +# upgrade correctly, but not revert once the upgrade has been done. +# flash_boot: Unable to find bootable image +# flash_hello1: hello1 runs +# flash_hello2: hello2 runs +# reset: hello2 runs +test-overwrite: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-upgrade-only.conf \ + all + +# Test that when configured for RSA, a wrong signature in the upgrade +# image will fail to upgrade. +# flash_boot: Unable to find bootable image +# flash_hello1: hello1 runs +# flash_hello2: hello1 runs +# reset: hello1 runs +test-bad-rsa-upgrade: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-rsa.conf \ + boot hello1 + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-rsa.conf \ + SIGNING_KEY=../../root-ec-p256.pem \ + hello2 + +# Test that when configured for ECDSA, a wrong signature in the upgrade +# image will fail to upgrade. +# flash_boot: Unable to find bootable image +# flash_hello1: hello1 runs +# flash_hello2: hello1 runs +# reset: hello1 runs +test-bad-ecdsa-upgrade: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-ecdsa-p256.conf \ + SIGNING_KEY=../../root-ec-p256.pem \ + boot hello1 + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-ecdsa-p256.conf \ + SIGNING_KEY=../../root-rsa-2048.pem \ + hello2 + +# Test that when configured to not validate the primary slot, we still boot, but +# don't upgrade. +# flash_boot: tries to boot and resets +# flash_hello1: hello1 runs +# flash_hello2: hello1 runs +# reset: hello1 runs +test-no-bootcheck: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-skip-primary-slot-validate.conf \ + SIGNING_KEY=../../root-ec-p256.pem \ + all + +# Test a good image, with a wrong-signature upgrade, using RSA signatures. +# flash_boot: Unable to find bootable image +# flash_hello1: hello1 runs +# flash_hello2: hello1 runs +# reset: hello1 runs +test-wrong-rsa: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-rsa.conf \ + boot hello1 + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-rsa.conf \ + SIGNING_KEY=bad-keys/bad-rsa-2048.pem \ + hello2 + +# Test a good image, with a wrong-signature upgrade, using ECDSA signatures. +# flash_boot: Unable to find bootable image +# flash_hello1: hello1 runs +# flash_hello2: hello1 runs +# reset: hello1 runs +test-wrong-ecdsa: clean + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-ecdsa-p256.conf \ + SIGNING_KEY=../../root-ec-p256.pem \ + boot hello1 + $(MAKE) \ + BOOTLOADER_OVERLAY_CONFIG=$(PWD)/overlay-ecdsa-p256.conf \ + SIGNING_KEY=bad-keys/bad-ec-p256.pem \ + hello2 + +check: + @if [ -z "$$ZEPHYR_BASE" ]; then echo "Zephyr environment not set up"; false; fi + @if [ -z "$(BOARD)" ]; then echo "You must specify BOARD="; false; fi diff --git a/bootloader/mcuboot/samples/zephyr/README.md b/bootloader/mcuboot/samples/zephyr/README.md new file mode 100644 index 0000000..d65341e --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/README.md @@ -0,0 +1,24 @@ +# Zephyr sample application. + +In order to successfully deploy an application using MCUboot, it is +necessary to build at least one other binary: the application itself. +It is beyond the scope of this documentation to describe what an +application is able to do, however a working example is certainly +useful. + +Please see the comments in the Makefile in this directory for more +details on how to build and test this application. + +Note that this sample uses the "ninja" build tool, which can be +installed on most systems using the system package manager, e.g., for +a Debian-based distro: + +``` +$ sudo apt-get install ninja +``` + +or in Fedora: + +``` +$ sudo dnf install ninja +``` diff --git a/bootloader/mcuboot/samples/zephyr/bad-keys/README.md b/bootloader/mcuboot/samples/zephyr/bad-keys/README.md new file mode 100644 index 0000000..621ce96 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/bad-keys/README.md @@ -0,0 +1,6 @@ +# Bad keys for testing + +This directory contains some alternate keys that can be used for +testing. Signing the images with either of these keys, but leaving +the demo keys's public keys in the bootloader should result in it not +upgrading, or not booting. diff --git a/bootloader/mcuboot/samples/zephyr/bad-keys/bad-ec-p256.pem b/bootloader/mcuboot/samples/zephyr/bad-keys/bad-ec-p256.pem new file mode 100644 index 0000000..333f41f --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/bad-keys/bad-ec-p256.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEILmqmiH6y3EGhLkTcnNtU7hZ1wnc51MIL53npseRX7vJoAoGCCqGSM49 +AwEHoUQDQgAEcX9ExNjZfsckp6AdutjPjVJsvP6ZZkKfLsGnRpKR+9OpO9/qmJHs +ks+ZXo70SEANjWnNlxKNAVci8aUm8UskLw== +-----END EC PRIVATE KEY----- diff --git a/bootloader/mcuboot/samples/zephyr/bad-keys/bad-rsa-2048.pem b/bootloader/mcuboot/samples/zephyr/bad-keys/bad-rsa-2048.pem new file mode 100644 index 0000000..755f95b --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/bad-keys/bad-rsa-2048.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAzXhCIc6kig0DRWEyKj4N8v/bfvh/RmU73Jo0Z0RAFSLnFrE5 +5W5C9tsdDalhN5HV7xBQCzr7GSW1C6MNvtw/lVr4+x4LvgwYD6kl+9IZc5udPIJ1 +R7aLBP2o4r6rJWtCj9Y6bifsMlVkHCONIHDGhp3bSl+9C2EqfwqJENg3mHYyZcbr +kBfLhILE24T+MhxkPZiI3Ox6XthwT7Is8ZCXF6VCYaK6N8jXAXz8OiEhIpMl3Z3m +DqxaM0ovSXfXvOfMeKN76iI9m8S4JnPbuzu+/5GdSnNbIxoluzXU7e52FoKgthNM +2mArAak5x3IqsStY0qDCODSlNnSha9+jfk8ULwIDAQABAoIBAQC7Z6dx7GdY3vuP +yVIXA1h3vfP2gDKeA3GxCRko4zBL1vTNVsJGx+XeAAYk0suwAp0NGmTXiWlDC4hw +37yGy55W3I3hhQsSwTck+ZOCdqPuNQ4aBadwzEdKOw5SGbRCQe2JAc1zcYhWdFoF +7EspPpNkbxB3apEjkvFOxE42Be/XZrwxig1OQJ9DpOhTeV8+jljg6aSzOuQxdR4G +ZXTzACnb20jnefnwmC5lhZxRWYEXkc96onYIvrDpIMdTzTnTbshJjg5HNPOEEgpF +U1x2iCRB3tN9QHbzdH/XrnRBr9Yy62SUIDXf4LBqin/pRQPodh9ZM7zanrhzsuiy +Tro2WKhRAoGBANoAT73haGrUb9ZSdzpu5qOD0eaQZPCIlubRrT5BCXPS2xVIGe+g +eDaBljdlXDJnKvBqjOVQyU7lrZAIVWgz6qD1HNIQZI30WivXejL4GeoXnAnp/ohh +1QmjMVvlcGe/22jpDmJa3Qgm4leaUxnxxilO/pQlZzzndBP88Df4/hIJAoGBAPFI +xBRq0SdBWhSydeEFvm8n37/icrN+V9f/Ka9tbPZcJaowzqQPrAHtu2CBD1JVyo2G +sKxFy867vCHntFu8zq2KSpCPVyeR+pDvwknr9PDm+DiLScBCGdyKqjBazT5+mHzP +fKk6nRV8uoNXLZQtnqKJzN61bYrnFaOQx6GZM6J3AoGAUHqA9bY7GAUo7FQxU88R +Mhg96wIvYWTrYHbToAHefXXAD1E40e/JsUWRsQ2oRas0fOC49wcl6gx8UInjDb7s +xVL3usz2cjlc+IZpxFs3JeZlYnuRzcNgJFispiJDpul7FHXFK6YjpxjDwldkilVp +NGLHNOXCAQfpIF/mRqOTGBECgYAmb7kMp5d58WcwNN2iYw/bFTcHkkNDZLUJq5Qw +ZfYdqMA3RF8ms3hrNjvLO8P9Eb2angI270dwP2fQ3uBUXNdvvb/zF2KC4zZPMGJ6 +9COo3KJeH5I4Fk+YWl6SJWTct74C4+qv6q5rZdswYQrZuAq1Sc5hC/XPUtCXpdCn +ZYhcMQKBgEHN3ZYe7gOfxR4rqk+OIsAUN9P3zyu6kQzUL1BQ7t7Gwi1dv6+E+BGQ +vGUt4RGG10g3K9twfhOHnwAiruEk6CK2+1G/ZsGCrPIE21KCcucUCPvzUGhintO+ +b+Q5kAeZFg5CB9b616SY/GvlzPraNx5OZfI8nw39U9do7N/yFiKT +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/bootloader/mcuboot/samples/zephyr/build-boot.sh b/bootloader/mcuboot/samples/zephyr/build-boot.sh new file mode 100644 index 0000000..81691e7 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/build-boot.sh @@ -0,0 +1,22 @@ +#! /bin/bash + +# Build the bootloader. + +# In order to build successfully, ZEPHYR_SDK_INSTALL_DIR and +# ZEPHYR_GCC_VARIANT need to be set, as well as zephyr/zephyr-env.sh +# must be sourced. + +die() { + echo error: "$@" + exit 1 +} + +if [ -z "$ZEPHYR_BASE" ]; then + die "Please setup for a Zephyr build before running this script." +fi + +if [ -z "$BOARD" ]; then + die "Please set BOARD to a valid board before running this script." +fi + +make BOARD=${BOARD} -j$(nproc) boot || die "Build mcuboot" diff --git a/bootloader/mcuboot/samples/zephyr/build-hello.sh b/bootloader/mcuboot/samples/zephyr/build-hello.sh new file mode 100644 index 0000000..6920f32 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/build-hello.sh @@ -0,0 +1,22 @@ +#! /bin/bash + +# Build the Sample hello program + +# In order to build successfully, ZEPHYR_SDK_INSTALL_DIR and +# ZEPHYR_GCC_VARIANT need to be set, as well as zephyr/zephyr-env.sh +# must be sourced. + +die() { + echo error: "$@" + exit 1 +} + +if [ -z "$ZEPHYR_BASE" ]; then + die "Please setup for a Zephyr build before running this script." +fi + +if [ -z "$BOARD" ]; then + die "Please set BOARD to a valid board before running this script." +fi + +make BOARD=${BOARD} -j$(nproc) hello1 || die "Build hello1" diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/CMakeLists.txt b/bootloader/mcuboot/samples/zephyr/hello-world/CMakeLists.txt new file mode 100644 index 0000000..3cace48 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/CMakeLists.txt @@ -0,0 +1,26 @@ +# Top-level CMakeLists.txt for the skeleton application. +# +# Copyright (c) 2017 Open Source Foundries Limited +# Copyright (c) 2018 Foundries.io Ltd +# +# SPDX-License-Identifier: Apache-2.0 +# +# This provides a basic application structure suitable for loading by +# mcuboot, which is easy to customize on a per-board basis. It can be +# used as a starting point for new applications. + +cmake_minimum_required(VERSION 3.8) + +# find_package(Zephyr) in order to load application boilerplate: +# https://docs.zephyrproject.org/latest/develop/application/index.html +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(NONE) + +# This string ends up getting printed in the device console +if (NOT DEFINED FROM_WHO) + set(FROM_WHO Zephyr) +endif() + +target_compile_definitions(app PRIVATE "-DMCUBOOT_HELLO_WORLD_FROM=\"${FROM_WHO}\"") + +target_sources(app PRIVATE src/main.c) diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/README.rst b/bootloader/mcuboot/samples/zephyr/hello-world/README.rst new file mode 100644 index 0000000..2c89008 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/README.rst @@ -0,0 +1,6 @@ +This is a "Hello world" skeleton application which can be used as a +starting point for Zephyr application development using mcuboot. + +It includes the configuration "glue" needed to make the application +loadable by mcuboot in addition to a basic Zephyr hello world +application's code. diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/boards/.gitignore b/bootloader/mcuboot/samples/zephyr/hello-world/boards/.gitignore new file mode 100644 index 0000000..27e8bd8 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/boards/.gitignore @@ -0,0 +1 @@ +*-local.conf diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/boards/README.rst b/bootloader/mcuboot/samples/zephyr/hello-world/boards/README.rst new file mode 100644 index 0000000..615c06e --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/boards/README.rst @@ -0,0 +1,2 @@ +You can place per-board configuration here. See the comments in the +CMakeLists.txt for more information. diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/prj.conf b/bootloader/mcuboot/samples/zephyr/hello-world/prj.conf new file mode 100644 index 0000000..12990aa --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/prj.conf @@ -0,0 +1,12 @@ +# Print a banner on the UART on startup. +CONFIG_BOOT_BANNER=y + +# Enable console and printk() +CONFIG_PRINTK=y +CONFIG_STDOUT_CONSOLE=y + +# Enable Zephyr application to be booted by MCUboot +CONFIG_BOOTLOADER_MCUBOOT=y + +# Use the default MCUBoot PEM key file (BOOT_SIGNATURE_KEY_FILE) +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="bootloader/mcuboot/root-rsa-2048.pem" diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/sample.yaml b/bootloader/mcuboot/samples/zephyr/hello-world/sample.yaml new file mode 100644 index 0000000..4241778 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/sample.yaml @@ -0,0 +1,9 @@ +sample: + name: Application Skeleton + description: Basic "hello world" application, but loadable by mcuboot + platforms: all +tests: + - test: + build_only: true + tags: samples tests + min_ram: 16 diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/src/Makefile b/bootloader/mcuboot/samples/zephyr/hello-world/src/Makefile new file mode 100644 index 0000000..00066e1 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/src/Makefile @@ -0,0 +1 @@ +obj-y = main.o diff --git a/bootloader/mcuboot/samples/zephyr/hello-world/src/main.c b/bootloader/mcuboot/samples/zephyr/hello-world/src/main.c new file mode 100644 index 0000000..66443d7 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/hello-world/src/main.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2017 Linaro, Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +void main(void) +{ + printk("Hello World from %s on %s!\n", + MCUBOOT_HELLO_WORLD_FROM, CONFIG_BOARD); +} diff --git a/bootloader/mcuboot/samples/zephyr/mcutests/mcutests.go b/bootloader/mcuboot/samples/zephyr/mcutests/mcutests.go new file mode 100644 index 0000000..e250f98 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/mcutests/mcutests.go @@ -0,0 +1,281 @@ +// Package mcutests +package mcutests // github.com/mcu-tools/mcuboot/samples/zephyr/mcutests + +// The main driver of this consists of a series of tests. Each test +// then contains a series of commands and expect results. +var Tests = []struct { + Name string + ShortName string + Tests []OneTest +}{ + { + Name: "Good RSA", + ShortName: "good-rsa", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-good-rsa"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello2", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello1", + }, + }, + }, + { + Name: "Good ECDSA", + ShortName: "good-ecdsa", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-good-ecdsa"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello2", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello1", + }, + }, + }, + { + Name: "Overwrite", + ShortName: "overwrite", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-overwrite"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello2", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello2", + }, + }, + }, + { + Name: "Bad RSA", + ShortName: "bad-rsa-upgrade", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-bad-rsa-upgrade"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello1", + }, + }, + }, + { + Name: "Bad RSA", + ShortName: "bad-ecdsa-upgrade", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-bad-ecdsa-upgrade"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello1", + }, + }, + }, + { + Name: "No bootcheck", + ShortName: "no-bootcheck", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-no-bootcheck"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello1", + }, + }, + }, + { + Name: "Wrong RSA", + ShortName: "wrong-rsa", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-wrong-rsa"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello1", + }, + }, + }, + { + Name: "Wrong ECDSA", + ShortName: "wrong-ecdsa", + Tests: []OneTest{ + { + Build: [][]string{ + {"make", "test-wrong-ecdsa"}, + }, + Commands: [][]string{ + {"make", "flash_boot"}, + }, + Expect: "Unable to find bootable image", + }, + { + Commands: [][]string{ + {"make", "flash_hello1"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"make", "flash_hello2"}, + }, + Expect: "Hello World from hello1", + }, + { + Commands: [][]string{ + {"pyocd", "commander", "-c", "reset"}, + }, + Expect: "Hello World from hello1", + }, + }, + }, +} + +type OneTest struct { + Build [][]string + Commands [][]string + Expect string +} diff --git a/bootloader/mcuboot/samples/zephyr/overlay-ecdsa-p256.conf b/bootloader/mcuboot/samples/zephyr/overlay-ecdsa-p256.conf new file mode 100644 index 0000000..8bf9512 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/overlay-ecdsa-p256.conf @@ -0,0 +1,4 @@ +# Kconfig overlay for building with ECDSA-P256 signatures +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y +CONFIG_BOOT_SIGNATURE_KEY_FILE="root-ec-p256.pem" +CONFIG_BOOT_SWAP_USING_MOVE=y diff --git a/bootloader/mcuboot/samples/zephyr/overlay-rsa.conf b/bootloader/mcuboot/samples/zephyr/overlay-rsa.conf new file mode 100644 index 0000000..c70349d --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/overlay-rsa.conf @@ -0,0 +1,3 @@ +# Kconfig overlay for building with RSA signatures +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y +CONFIG_BOOT_SWAP_USING_MOVE=y diff --git a/bootloader/mcuboot/samples/zephyr/overlay-skip-primary-slot-validate.conf b/bootloader/mcuboot/samples/zephyr/overlay-skip-primary-slot-validate.conf new file mode 100644 index 0000000..e0c1f98 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/overlay-skip-primary-slot-validate.conf @@ -0,0 +1,4 @@ +# Kconfig overlay for building without validating primary slot. + +# CONFIG_BOOT_VALIDATE_SLOT0 is not set +CONFIG_BOOT_SWAP_USING_MOVE=y diff --git a/bootloader/mcuboot/samples/zephyr/overlay-upgrade-only.conf b/bootloader/mcuboot/samples/zephyr/overlay-upgrade-only.conf new file mode 100644 index 0000000..bdff7ac --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/overlay-upgrade-only.conf @@ -0,0 +1,2 @@ +# Kconfig overlay for building in upgrade-only mode. +CONFIG_BOOT_UPGRADE_ONLY=y diff --git a/bootloader/mcuboot/samples/zephyr/run-tests.go b/bootloader/mcuboot/samples/zephyr/run-tests.go new file mode 100644 index 0000000..f55b0a6 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/run-tests.go @@ -0,0 +1,290 @@ +// +build ignore +// +// Build multiple configurations of MCUboot for Zephyr, making sure +// that they run properly. +// +// Run as: +// +// go run run-tests.go [flags] +// +// Add -help as a flag to get help. See comment below for logIn on +// how to configure terminal output to a file so this program can see +// the output of the Zephyr device. + +package main + +import ( + "archive/zip" + "bufio" + "flag" + "fmt" + "io" + "log" + "os" + "os/exec" + "strings" + "time" + + "github.com/mcu-tools/mcuboot/samples/zephyr/mcutests" +) + +// logIn gives the pathname of the log output from the Zephyr device. +// In order to see the serial output, but still be useful for human +// debugging, the output of the terminal emulator should be teed to a +// file that this program will read from. This can be done with +// something like: +// +// picocom -b 115200 /dev/ttyACM0 | tee /tmp/zephyr.out +// +// Other terminal programs should also have logging options. +var logIn = flag.String("login", "/tmp/zephyr.out", "File name of terminal log from Zephyr device") + +// Output from this test run is written to the given log file. +var logOut = flag.String("logout", "tests.log", "Log file to write to") + +var preBuilt = flag.String("prebuilt", "", "Name of file with prebuilt tests") + +func main() { + err := run() + if err != nil { + log.Fatal(err) + } +} + +func run() error { + flag.Parse() + + lines := make(chan string, 30) + go readLog(lines) + + // Write output to a log file + logFile, err := os.Create(*logOut) + if err != nil { + return err + } + defer logFile.Close() + lg := bufio.NewWriter(logFile) + defer lg.Flush() + + var extractor *Extractor + + if *preBuilt != "" { + // If there are pre-built images, open them. + extractor, err = NewExtractor(*preBuilt) + if err != nil { + return err + } + defer extractor.Close() + } + + for _, group := range mcutests.Tests { + fmt.Printf("Running %q\n", group.Name) + fmt.Fprintf(lg, "-------------------------------------\n") + fmt.Fprintf(lg, "---- Running %q\n", group.Name) + + for _, test := range group.Tests { + if *preBuilt == "" { + // No prebuilt, build the tests + // ourselves. + err = runCommands(test.Build, lg) + if err != nil { + return err + } + } else { + // Extract the build artifacts from + // the zip file. + err = extractor.Extract(group.ShortName) + if err != nil { + return err + } + } + + err = runCommands(test.Commands, lg) + if err != nil { + return err + } + + err = expect(lg, lines, test.Expect) + if err != nil { + return err + } + + fmt.Fprintf(lg, "---- Passed\n") + } + fmt.Printf(" Passed!\n") + } + + return nil +} + +// Run a set of commands +func runCommands(cmds [][]string, lg io.Writer) error { + for _, cmd := range cmds { + fmt.Printf(" %s\n", cmd) + fmt.Fprintf(lg, "---- Run: %s\n", cmd) + err := runCommand(cmd, lg) + if err != nil { + return err + } + } + + return nil +} + +// Run a single command. +func runCommand(cmd []string, lg io.Writer) error { + c := exec.Command(cmd[0], cmd[1:]...) + c.Stdout = lg + c.Stderr = lg + return c.Run() +} + +// Expect the given string. +func expect(lg io.Writer, lines <-chan string, exp string) error { + // Read lines, and if we hit a timeout before seeing our + // expected line, then consider that an error. + fmt.Fprintf(lg, "---- expect: %q\n", exp) + + stopper := time.NewTimer(10 * time.Second) + defer stopper.Stop() +outer: + for { + select { + case line := <-lines: + fmt.Fprintf(lg, "---- target: %q\n", line) + if strings.Contains(line, exp) { + break outer + } + case <-stopper.C: + fmt.Fprintf(lg, "timeout, didn't receive output\n") + return fmt.Errorf("timeout, didn't receive expected string: %q", exp) + } + } + + return nil +} + +// Read things from the log file, discarding everything already there. +func readLog(sink chan<- string) { + file, err := os.Open(*logIn) + if err != nil { + log.Fatal(err) + } + + _, err = file.Seek(0, 2) + if err != nil { + log.Fatal(err) + } + + prefix := "" + for { + // Read lines until EOF, then delay a bit, and do it + // all again. + rd := bufio.NewReader(file) + + for { + line, err := rd.ReadString('\n') + if err == io.EOF { + // A partial line can happen because + // we are racing with the writer. + if line != "" { + prefix = line + } + break + } + if err != nil { + log.Fatal(err) + } + + line = prefix + line + prefix = "" + sink <- line + // fmt.Printf("line: %q\n", line) + } + + // Pause a little + time.Sleep(250 * time.Millisecond) + } +} + +// An Extractor holds an opened Zip file, and is able to extract files +// based on the directory name. +type Extractor struct { + file *os.File + zip *zip.Reader +} + +// NewExtractor returns an Extractor based on the contents of a zip +// file. +func NewExtractor(name string) (*Extractor, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + size, err := f.Seek(0, 2) + if err != nil { + f.Close() + return nil, err + } + + rd, err := zip.NewReader(f, size) + if err != nil { + f.Close() + return nil, err + } + + return &Extractor{ + file: f, + zip: rd, + }, nil +} + +func (e *Extractor) Close() error { + return e.file.Close() +} + +// Extract extracts the files of the given directory name into the +// current directory. These files will overwrite any files of these +// names that already exist (presumably from previous extractions). +func (e *Extractor) Extract(dir string) error { + prefix := dir + "/" + + count := 0 + for _, file := range e.zip.File { + if len(file.Name) > len(prefix) && strings.HasPrefix(file.Name, prefix) { + outName := file.Name[len(prefix):len(file.Name)] + fmt.Printf("->%q\n", outName) + + err := e.single(file, outName) + if err != nil { + return err + } + + count += 1 + } + } + + if count == 0 { + return fmt.Errorf("File for %s missing from archive", dir) + } + + return nil +} + +// single extracts a single file from the zip archive, writing the +// results to a file 'outName'. +func (e *Extractor) single(file *zip.File, outName string) error { + inf, err := file.Open() + if err != nil { + return err + } + + outf, err := os.Create(outName) + if err != nil { + return err + } + defer outf.Close() + + _, err = io.Copy(outf, inf) + return err +} diff --git a/bootloader/mcuboot/samples/zephyr/run-tests.sh b/bootloader/mcuboot/samples/zephyr/run-tests.sh new file mode 100644 index 0000000..cdd89e3 --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/run-tests.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "Please use the new test runner: go run run-tests.go" +exit 1 diff --git a/bootloader/mcuboot/samples/zephyr/test-compile.go b/bootloader/mcuboot/samples/zephyr/test-compile.go new file mode 100644 index 0000000..3acfa3c --- /dev/null +++ b/bootloader/mcuboot/samples/zephyr/test-compile.go @@ -0,0 +1,156 @@ +// +build ignore +// +// Build all of the tests. +// +// Run as: +// +// go run test-compile.go -out name.tar + +package main + +import ( + "archive/zip" + "flag" + "fmt" + "io" + "log" + "os" + "os/exec" + "path" + + "github.com/mcu-tools/mcuboot/samples/zephyr/mcutests" +) + +var outFile = flag.String("out", "test-images.zip", "Name of zip file to put built tests into") + +func main() { + err := run() + if err != nil { + log.Fatal(err) + } +} + +func run() error { + flag.Parse() + + zipper, err := NewBuilds() + if err != nil { + return err + } + defer zipper.Close() + + for _, group := range mcutests.Tests { + fmt.Printf("Compiling %q\n", group.ShortName) + c := exec.Command("make", + fmt.Sprintf("test-%s", group.ShortName)) + // TODO: Should capture the output and show it if + // there is an error. + err = c.Run() + if err != nil { + return err + } + + err = zipper.Capture(group.ShortName) + if err != nil { + return err + } + } + + return nil +} + +// A Builds create a zipfile of the contents of various builds. The +// names will be constructed. +type Builds struct { + // The file being written to. + file *os.File + + // The zip writer writing the data. + zip *zip.Writer +} + +func NewBuilds() (*Builds, error) { + name := *outFile + file, err := os.OpenFile(name, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) + if err != nil { + return nil, err + } + + z := zip.NewWriter(file) + + return &Builds{ + file: file, + zip: z, + }, nil +} + +func (b *Builds) Close() error { + return b.zip.Close() +} + +func (b *Builds) Capture(testName string) error { + // Collect stat information from the test directory, which + // should be close enough to make the zip file meaningful. + info, err := os.Stat(".") + if err != nil { + return err + } + + header, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + header.Name = testName + "/" + + _, err = b.zip.CreateHeader(header) + if err != nil { + return err + } + + for _, name := range []string{ + "mcuboot.bin", + "signed-hello1.bin", + "signed-hello2.bin", + } { + err = b.add(testName, name, name) + if err != nil { + return err + } + } + + return nil +} + +func (b *Builds) add(baseName, zipName, fileName string) error { + inp, err := os.Open(fileName) + if err != nil { + return err + } + defer inp.Close() + + info, err := inp.Stat() + if err != nil { + return err + } + + header, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + header.Name = path.Join(baseName, zipName) + header.Method = zip.Deflate + + wr, err := b.zip.CreateHeader(header) + if err != nil { + return err + } + + _, err = io.Copy(wr, inp) + if err != nil { + return err + } + + return nil +} diff --git a/bootloader/mcuboot/scripts/assemble.py b/bootloader/mcuboot/scripts/assemble.py new file mode 100644 index 0000000..05f9793 --- /dev/null +++ b/bootloader/mcuboot/scripts/assemble.py @@ -0,0 +1,148 @@ +#! /usr/bin/env python3 +# +# Copyright 2017 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Assemble multiple images into a single image that can be flashed on the device. +""" + +import argparse +import errno +import io +import re +import os +import os.path +import pickle +import sys + +def same_keys(a, b): + """Determine if the dicts a and b have the same keys in them""" + for ak in a.keys(): + if ak not in b: + return False + for bk in b.keys(): + if bk not in a: + return False + return True + +offset_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_OFFSET(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") +size_re = re.compile(r"^#define DT_FLASH_AREA_([0-9A-Z_]+)_SIZE(_0)?\s+(0x[0-9a-fA-F]+|[0-9]+)$") + +class Assembly(): + def __init__(self, output, bootdir, edt): + self.find_slots(edt) + try: + os.unlink(output) + except OSError as e: + if e.errno != errno.ENOENT: + raise + self.output = output + + def find_slots(self, edt): + offsets = {} + sizes = {} + + part_nodes = edt.compat2nodes["fixed-partitions"] + for node in part_nodes: + for child in node.children.values(): + if "label" in child.props: + label = child.props["label"].val + offsets[label] = child.regs[0].addr + sizes[label] = child.regs[0].size + + if not same_keys(offsets, sizes): + raise Exception("Inconsistent data in devicetree.h") + + # We care about the mcuboot, image-0, and image-1 partitions. + if 'mcuboot' not in offsets: + raise Exception("Board partition table does not have mcuboot partition") + + if 'image-0' not in offsets: + raise Exception("Board partition table does not have image-0 partition") + + if 'image-1' not in offsets: + raise Exception("Board partition table does not have image-1 partition") + + self.offsets = offsets + self.sizes = sizes + + def add_image(self, source, partition): + with open(self.output, 'ab') as ofd: + pos = ofd.tell() + print("partition {}, pos={}, offset={}".format(partition, pos, self.offsets[partition])) + if pos > self.offsets[partition]: + raise Exception("Partitions not in order, unsupported") + if pos < self.offsets[partition]: + buf = b'\xFF' * (self.offsets[partition] - pos) + ofd.write(buf) + with open(source, 'rb') as rfd: + ibuf = rfd.read() + if len(ibuf) > self.sizes[partition]: + raise Exception("Image {} is too large for partition".format(source)) + ofd.write(ibuf) + +def find_board_name(bootdir): + dot_config = os.path.join(bootdir, "zephyr", ".config") + with open(dot_config, "r") as f: + for line in f: + if line.startswith("CONFIG_BOARD="): + return line.split("=", 1)[1].strip('"') + raise Exception("Expected CONFIG_BOARD line in {}".format(dot_config)) + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument('-b', '--bootdir', required=True, + help='Directory of built bootloader') + parser.add_argument('-p', '--primary', required=True, + help='Signed image file for primary image') + parser.add_argument('-s', '--secondary', + help='Signed image file for secondary image') + parser.add_argument('-o', '--output', required=True, + help='Filename to write full image to') + parser.add_argument('-z', '--zephyr-base', + help='Zephyr base containing the Zephyr repository') + + args = parser.parse_args() + + zephyr_base = args.zephyr_base + if zephyr_base is None: + try: + zephyr_base = os.environ['ZEPHYR_BASE'] + except KeyError: + print('Need to either have ZEPHYR_BASE in environment or pass in -z') + sys.exit(1) + + sys.path.insert(0, os.path.join(zephyr_base, "scripts", "dts", "python-devicetree", "src")) + import devicetree.edtlib + + board = find_board_name(args.bootdir) + + edt_pickle = os.path.join(args.bootdir, "zephyr", "edt.pickle") + with open(edt_pickle, 'rb') as f: + edt = pickle.load(f) + assert isinstance(edt, devicetree.edtlib.EDT) + + output = Assembly(args.output, args.bootdir, edt) + + output.add_image(os.path.join(args.bootdir, 'zephyr', 'zephyr.bin'), 'mcuboot') + output.add_image(args.primary, "image-0") + if args.secondary is not None: + output.add_image(args.secondary, "image-1") + +if __name__ == '__main__': + main() diff --git a/bootloader/mcuboot/scripts/flash.sh b/bootloader/mcuboot/scripts/flash.sh new file mode 100644 index 0000000..7cb5bdb --- /dev/null +++ b/bootloader/mcuboot/scripts/flash.sh @@ -0,0 +1,20 @@ +#! /bin/bash +# +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname $0)/../target.sh + +lscript=/tmp/flash$$.jlink + +cat >$lscript < $gscript < {}; +let + # Nixpkgs has fairly recent versions of the dependencies, so we can + # rely on them without having to build our own derivations. + imgtoolPythonEnv = python37.withPackages ( + _: [ + python37.pkgs.click + python37.pkgs.cryptography + python37.pkgs.intelhex + python37.pkgs.setuptools + python37.pkgs.cbor2 + python37.pkgs.pyyaml + ] + ); +in +myEnvFun { + name = "imgtool"; + + buildInputs = [ imgtoolPythonEnv ]; +} diff --git a/bootloader/mcuboot/scripts/imgtool.py b/bootloader/mcuboot/scripts/imgtool.py new file mode 100644 index 0000000..e29e224 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool.py @@ -0,0 +1,22 @@ +#! /usr/bin/env python3 +# +# Copyright 2017 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from imgtool import main + +if __name__ == '__main__': + main.imgtool() diff --git a/bootloader/mcuboot/scripts/imgtool/__init__.py b/bootloader/mcuboot/scripts/imgtool/__init__.py new file mode 100644 index 0000000..421af3e --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2017-2020 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +imgtool_version = "2.1.0" diff --git a/bootloader/mcuboot/scripts/imgtool/boot_record.py b/bootloader/mcuboot/scripts/imgtool/boot_record.py new file mode 100644 index 0000000..fb98b2e --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/boot_record.py @@ -0,0 +1,52 @@ +# Copyright (c) 2019-2024, Arm Limited. +# Copyright (c) 2020, Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum + +try: + from cbor2 import dumps +except ImportError: + from cbor import dumps + + +class SwComponent(int, Enum): + """ + Software component property IDs specified by + Arm's PSA Attestation API 1.0 document. + """ + TYPE = 1 + MEASUREMENT_VALUE = 2 + VERSION = 4 + SIGNER_ID = 5 + MEASUREMENT_DESCRIPTION = 6 + + +def create_sw_component_data(sw_type, sw_version, sw_measurement_description, + sw_measurement_value, sw_signer_id): + # List of software component properties (Key ID + value) + properties = {SwComponent.TYPE: sw_type, + SwComponent.VERSION: sw_version, + SwComponent.SIGNER_ID: sw_signer_id, + SwComponent.MEASUREMENT_DESCRIPTION: sw_measurement_description, + SwComponent.MEASUREMENT_VALUE: sw_measurement_value, + } + + # Note: The measurement value must be the last item of the property + # list because later it will be modified by the bootloader. + last_key = list(properties.keys())[-1] + assert SwComponent.MEASUREMENT_VALUE == last_key, 'Measurement value is not the last item of the property list' + return dumps(properties) diff --git a/bootloader/mcuboot/scripts/imgtool/dumpinfo.py b/bootloader/mcuboot/scripts/imgtool/dumpinfo.py new file mode 100644 index 0000000..5544657 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/dumpinfo.py @@ -0,0 +1,328 @@ +# Copyright 2023-2024 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Parse and print header, TLV area and trailer information of a signed image. +""" +import os.path +import struct +import sys + +import click +import yaml + +from imgtool import image + +HEADER_ITEMS = ("magic", "load_addr", "hdr_size", "protected_tlv_size", + "img_size", "flags", "version") +TLV_TYPES = dict((value, key) for key, value in image.TLV_VALUES.items()) +BOOT_MAGIC = bytes([ + 0x77, 0xc2, 0x95, 0xf3, + 0x60, 0xd2, 0xef, 0x7f, + 0x35, 0x52, 0x50, 0x0f, + 0x2c, 0xb6, 0x79, 0x80, ]) +BOOT_MAGIC_2 = bytes([ + 0x2d, 0xe1, 0x5d, 0x29, + 0x41, 0x0b, 0x8d, 0x77, + 0x67, 0x9c, 0x11, 0x0f, + 0x1f, 0x8a, ]) +BOOT_MAGIC_SIZE = len(BOOT_MAGIC) +_LINE_LENGTH = 60 +STATUS = { + '0x1': 'SET', + '0x2': 'BAD', + '0x3': 'UNSET', + '0x4': 'ANY', +} + + +def parse_enc(key_field_len): + if key_field_len is not None: + return "(len: {}, if BOOT_SWAP_SAVE_ENCTLV is unset)".format(hex(key_field_len)) + else: + return "Image not encrypted" + + +def parse_size(size_hex): + if size_hex == '0xffffffff': + return "unknown" + return size_hex + " octal: " + str(int(size_hex, 0)) + + +def parse_status(status_hex): + return f"{STATUS[status_hex]} ({status_hex})" if status_hex in STATUS else f"INVALID ({status_hex})" + + +def parse_boot_magic(trailer_magic): + magic = "" + for i in range(BOOT_MAGIC_SIZE): + magic += "{0:#04x} ".format(trailer_magic[i]) + if i == (BOOT_MAGIC_SIZE / 2 - 1): + magic += ("\n" + " ") + return magic + + +def print_in_frame(header_text, content): + sepc = " " + header = "#### " + header_text + sepc + post_header = "#" * (_LINE_LENGTH - len(header)) + print(header + post_header) + + print("|", sepc * (_LINE_LENGTH - 2), "|", sep="") + offset = (_LINE_LENGTH - len(content)) // 2 + pre = "|" + (sepc * (offset - 1)) + post = sepc * (_LINE_LENGTH - len(pre) - len(content) - 1) + "|" + print(pre, content, post, sep="") + print("|", sepc * (_LINE_LENGTH - 2), "|", sep="") + print("#" * _LINE_LENGTH) + + +def print_in_row(row_text): + row_text = "#### " + row_text + " " + fill = "#" * (_LINE_LENGTH - len(row_text)) + print(row_text + fill) + + +def print_tlv_records(tlv_list): + indent = _LINE_LENGTH // 8 + for tlv in tlv_list: + print(" " * indent, "-" * 45) + tlv_type, tlv_length, tlv_data = tlv.keys() + + if tlv[tlv_type] in TLV_TYPES: + print(" " * indent, "{}: {} ({})".format( + tlv_type, TLV_TYPES[tlv[tlv_type]], hex(tlv[tlv_type]))) + else: + print(" " * indent, "{}: {} ({})".format( + tlv_type, "UNKNOWN", hex(tlv[tlv_type]))) + print(" " * indent, "{}: ".format(tlv_length), hex(tlv[tlv_length])) + print(" " * indent, "{}: ".format(tlv_data), end="") + + for j, data in enumerate(tlv[tlv_data]): + print("{0:#04x}".format(data), end=" ") + if ((j + 1) % 8 == 0) and ((j + 1) != len(tlv[tlv_data])): + print("\n", end=" " * (indent + 7)) + print() + + +def dump_imginfo(imgfile, outfile=None, silent=False): + """Parse a signed image binary and print/save the available information.""" + trailer_magic = None + # set to INVALID by default + swap_size = 0x99 + swap_info = 0x99 + copy_done = 0x99 + image_ok = 0x99 + trailer = {} + key_field_len = None + + try: + with open(imgfile, "rb") as f: + b = f.read() + except FileNotFoundError: + raise click.UsageError("Image file not found ({})".format(imgfile)) + + # Parsing the image header + _header = struct.unpack('IIHHIIBBHI', b[:28]) + # Image version consists of the last 4 item ('BBHI') + _version = _header[-4:] + header = {} + for i, key in enumerate(HEADER_ITEMS): + if key == "version": + header[key] = "{}.{}.{}+{}".format(*_version) + else: + header[key] = _header[i] + + # Parsing the TLV area + tlv_area = {"tlv_hdr_prot": {}, + "tlvs_prot": [], + "tlv_hdr": {}, + "tlvs": []} + tlv_off = header["hdr_size"] + header["img_size"] + protected_tlv_size = header["protected_tlv_size"] + + if protected_tlv_size != 0: + _tlv_prot_head = struct.unpack( + 'HH', + b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)]) + tlv_area["tlv_hdr_prot"]["magic"] = _tlv_prot_head[0] + tlv_area["tlv_hdr_prot"]["tlv_tot"] = _tlv_prot_head[1] + tlv_end = tlv_off + tlv_area["tlv_hdr_prot"]["tlv_tot"] + tlv_off += image.TLV_INFO_SIZE + + # Iterating through the protected TLV area + while tlv_off < tlv_end: + tlv_type, tlv_len = struct.unpack( + 'HH', + b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)]) + tlv_off += image.TLV_INFO_SIZE + tlv_data = b[tlv_off:(tlv_off + tlv_len)] + tlv_area["tlvs_prot"].append( + {"type": tlv_type, "len": tlv_len, "data": tlv_data}) + tlv_off += tlv_len + + _tlv_head = struct.unpack('HH', b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)]) + tlv_area["tlv_hdr"]["magic"] = _tlv_head[0] + tlv_area["tlv_hdr"]["tlv_tot"] = _tlv_head[1] + + tlv_end = tlv_off + tlv_area["tlv_hdr"]["tlv_tot"] + tlv_off += image.TLV_INFO_SIZE + + # Iterating through the TLV area + while tlv_off < tlv_end: + tlv_type, tlv_len = struct.unpack( + 'HH', + b[tlv_off:(tlv_off + image.TLV_INFO_SIZE)]) + tlv_off += image.TLV_INFO_SIZE + tlv_data = b[tlv_off:(tlv_off + tlv_len)] + tlv_area["tlvs"].append( + {"type": tlv_type, "len": tlv_len, "data": tlv_data}) + tlv_off += tlv_len + + _img_pad_size = len(b) - tlv_end + + if _img_pad_size: + # Parsing the image trailer + trailer_off = -BOOT_MAGIC_SIZE + trailer_magic = b[trailer_off:] + trailer["magic"] = trailer_magic + max_align = None + if trailer_magic == BOOT_MAGIC: + # The maximum supported write alignment is the default 8 Bytes + max_align = 8 + elif trailer_magic[-len(BOOT_MAGIC_2):] == BOOT_MAGIC_2: + # The alignment value is encoded in the magic field + max_align = int.from_bytes(trailer_magic[:2], "little") + else: + # Invalid magic: the rest of the image trailer cannot be processed. + print("Warning: the trailer magic value is invalid!") + + if max_align is not None: + if max_align > BOOT_MAGIC_SIZE: + trailer_off -= max_align - BOOT_MAGIC_SIZE + # Parsing rest of the trailer fields + trailer_off -= max_align + image_ok = b[trailer_off] + trailer["image_ok"] = image_ok + + trailer_off -= max_align + copy_done = b[trailer_off] + trailer["copy_done"] = copy_done + + trailer_off -= max_align + swap_info = b[trailer_off] + trailer["swap_info"] = swap_info + + trailer_off -= max_align + swap_size = int.from_bytes(b[trailer_off:(trailer_off + 4)], + "little") + trailer["swap_size"] = swap_size + + # Encryption key 0/1 + if ((header["flags"] & image.IMAGE_F["ENCRYPTED_AES128"]) or + (header["flags"] & image.IMAGE_F["ENCRYPTED_AES256"])): + # The image is encrypted + # Estimated value of key_field_len is correct if + # BOOT_SWAP_SAVE_ENCTLV is unset + key_field_len = image.align_up(16, max_align) * 2 + + # Generating output yaml file + if outfile is not None: + imgdata = {"header": header, + "tlv_area": tlv_area, + "trailer": trailer} + with open(outfile, "w") as outf: + # sort_keys - from pyyaml 5.1 + yaml.dump(imgdata, outf, sort_keys=False) + + ############################################################################### + + if silent: + sys.exit(0) + + print("Printing content of signed image:", os.path.basename(imgfile), "\n") + + # Image header + section_name = "Image header (offset: 0x0)" + print_in_row(section_name) + for key, value in header.items(): + if key == "flags": + if not value: + flag_string = hex(value) + else: + flag_string = "" + for flag in image.IMAGE_F.keys(): + if value & image.IMAGE_F[flag]: + if flag_string: + flag_string += ("\n" + (" " * 20)) + flag_string += "{} ({})".format( + flag, hex(image.IMAGE_F[flag])) + value = flag_string + + if not isinstance(value, str): + value = hex(value) + print(key, ":", " " * (19 - len(key)), value, sep="") + print("#" * _LINE_LENGTH) + + # Image payload + _sectionoff = header["hdr_size"] + frame_header_text = "Payload (offset: {})".format(hex(_sectionoff)) + frame_content = "FW image (size: {} Bytes)".format(hex(header["img_size"])) + print_in_frame(frame_header_text, frame_content) + + # TLV area + _sectionoff += header["img_size"] + if protected_tlv_size != 0: + # Protected TLV area + section_name = "Protected TLV area (offset: {})".format(hex(_sectionoff)) + print_in_row(section_name) + print("magic: ", hex(tlv_area["tlv_hdr_prot"]["magic"])) + print("area size:", hex(tlv_area["tlv_hdr_prot"]["tlv_tot"])) + print_tlv_records(tlv_area["tlvs_prot"]) + print("#" * _LINE_LENGTH) + + _sectionoff += protected_tlv_size + section_name = "TLV area (offset: {})".format(hex(_sectionoff)) + print_in_row(section_name) + print("magic: ", hex(tlv_area["tlv_hdr"]["magic"])) + print("area size:", hex(tlv_area["tlv_hdr"]["tlv_tot"])) + print_tlv_records(tlv_area["tlvs"]) + print("#" * _LINE_LENGTH) + + if _img_pad_size: + _sectionoff += tlv_area["tlv_hdr"]["tlv_tot"] + _erased_val = b[_sectionoff] + frame_header_text = "Image padding (offset: {})".format(hex(_sectionoff)) + frame_content = "padding ({})".format(hex(_erased_val)) + print_in_frame(frame_header_text, frame_content) + + # Image trailer + section_name = "Image trailer (offset: unknown)" + print_in_row(section_name) + notice = "(Note: some fields may not be used, depending on the update strategy)\n" + notice = '\n'.join(notice[i:i + _LINE_LENGTH] for i in range(0, len(notice), _LINE_LENGTH)) + print(notice) + print("swap status: (len: unknown)") + print("enc. keys: ", parse_enc(key_field_len)) + print("swap size: ", parse_size(hex(swap_size))) + print("swap_info: ", parse_status(hex(swap_info))) + print("copy_done: ", parse_status(hex(copy_done))) + print("image_ok: ", parse_status(hex(image_ok))) + print("boot magic: ", parse_boot_magic(trailer_magic)) + print() + + footer = "End of Image " + print_in_row(footer) diff --git a/bootloader/mcuboot/scripts/imgtool/image.py b/bootloader/mcuboot/scripts/imgtool/image.py new file mode 100644 index 0000000..9946a25 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/image.py @@ -0,0 +1,880 @@ +# Copyright 2018 Nordic Semiconductor ASA +# Copyright 2017-2020 Linaro Limited +# Copyright 2019-2024 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Image signing and management. +""" + +from . import version as versmod +from .boot_record import create_sw_component_data +import click +import copy +from enum import Enum +import array +from intelhex import IntelHex +import hashlib +import array +import os.path +import struct +from enum import Enum + +import click +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, hmac +from cryptography.hazmat.primitives.asymmetric import ec, padding +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat +from intelhex import IntelHex + +from . import version as versmod, keys +from .boot_record import create_sw_component_data +from .keys import rsa, ecdsa, x25519 + +from collections import namedtuple + +IMAGE_MAGIC = 0x96f3b83d +IMAGE_HEADER_SIZE = 32 +BIN_EXT = "bin" +INTEL_HEX_EXT = "hex" +DEFAULT_MAX_SECTORS = 128 +DEFAULT_MAX_ALIGN = 8 +DEP_IMAGES_KEY = "images" +DEP_VERSIONS_KEY = "versions" +MAX_SW_TYPE_LENGTH = 12 # Bytes + +# Image header flags. +IMAGE_F = { + 'PIC': 0x0000001, + 'ENCRYPTED_AES128': 0x0000004, + 'ENCRYPTED_AES256': 0x0000008, + 'NON_BOOTABLE': 0x0000010, + 'RAM_LOAD': 0x0000020, + 'ROM_FIXED': 0x0000100, + 'COMPRESSED_LZMA1': 0x0000200, + 'COMPRESSED_LZMA2': 0x0000400, + 'COMPRESSED_ARM_THUMB': 0x0000800, +} + +TLV_VALUES = { + 'KEYHASH': 0x01, + 'PUBKEY': 0x02, + 'SHA256': 0x10, + 'SHA384': 0x11, + 'SHA512': 0x12, + 'RSA2048': 0x20, + 'ECDSASIG': 0x22, + 'RSA3072': 0x23, + 'ED25519': 0x24, + 'SIG_PURE': 0x25, + 'ENCRSA2048': 0x30, + 'ENCKW': 0x31, + 'ENCEC256': 0x32, + 'ENCX25519': 0x33, + 'DEPENDENCY': 0x40, + 'SEC_CNT': 0x50, + 'BOOT_RECORD': 0x60, + 'DECOMP_SIZE': 0x70, + 'DECOMP_SHA': 0x71, + 'DECOMP_SIGNATURE': 0x72, +} + +TLV_SIZE = 4 +TLV_INFO_SIZE = 4 +TLV_INFO_MAGIC = 0x6907 +TLV_PROT_INFO_MAGIC = 0x6908 + +TLV_VENDOR_RES_MIN = 0x00a0 +TLV_VENDOR_RES_MAX = 0xfffe + +STRUCT_ENDIAN_DICT = { + 'little': '<', + 'big': '>' +} + +VerifyResult = Enum('VerifyResult', + ['OK', 'INVALID_MAGIC', 'INVALID_TLV_INFO_MAGIC', 'INVALID_HASH', 'INVALID_SIGNATURE', + 'KEY_MISMATCH']) + + +def align_up(num, align): + assert (align & (align - 1) == 0) and align != 0 + return (num + (align - 1)) & ~(align - 1) + + +class TLV(): + def __init__(self, endian, magic=TLV_INFO_MAGIC): + self.magic = magic + self.buf = bytearray() + self.endian = endian + + def __len__(self): + return TLV_INFO_SIZE + len(self.buf) + + def add(self, kind, payload): + """ + Add a TLV record. Kind should be a string found in TLV_VALUES above. + """ + e = STRUCT_ENDIAN_DICT[self.endian] + if isinstance(kind, int): + if not TLV_VENDOR_RES_MIN <= kind <= TLV_VENDOR_RES_MAX: + msg = "Invalid custom TLV type value '0x{:04x}', allowed " \ + "value should be between 0x{:04x} and 0x{:04x}".format( + kind, TLV_VENDOR_RES_MIN, TLV_VENDOR_RES_MAX) + raise click.UsageError(msg) + buf = struct.pack(e + 'HH', kind, len(payload)) + else: + buf = struct.pack(e + 'BBH', TLV_VALUES[kind], 0, len(payload)) + self.buf += buf + self.buf += payload + + def get(self): + if len(self.buf) == 0: + return bytes() + e = STRUCT_ENDIAN_DICT[self.endian] + header = struct.pack(e + 'HH', self.magic, len(self)) + return header + bytes(self.buf) + + +SHAAndAlgT = namedtuple('SHAAndAlgT', ['sha', 'alg']) + +TLV_SHA_TO_SHA_AND_ALG = { + TLV_VALUES['SHA256'] : SHAAndAlgT('256', hashlib.sha256), + TLV_VALUES['SHA384'] : SHAAndAlgT('384', hashlib.sha384), + TLV_VALUES['SHA512'] : SHAAndAlgT('512', hashlib.sha512), +} + + +USER_SHA_TO_ALG_AND_TLV = { + 'auto' : (hashlib.sha256, 'SHA256'), + '256' : (hashlib.sha256, 'SHA256'), + '384' : (hashlib.sha384, 'SHA384'), + '512' : (hashlib.sha512, 'SHA512') +} + + +def is_sha_tlv(tlv): + return tlv in TLV_SHA_TO_SHA_AND_ALG.keys() + + +def tlv_sha_to_sha(tlv): + return TLV_SHA_TO_SHA_AND_ALG[tlv].sha + + +# Auto selecting hash algorithm for type(key) +ALLOWED_KEY_SHA = { + keys.ECDSA384P1 : ['384'], + keys.ECDSA384P1Public : ['384'], + keys.ECDSA256P1 : ['256'], + keys.RSA : ['256'], + # This two are set to 256 for compatibility, the right would be 512 + keys.Ed25519 : ['256', '512'], + keys.X25519 : ['256', '512'] +} + +ALLOWED_PURE_KEY_SHA = { + keys.Ed25519 : ['512'] +} + +ALLOWED_PURE_SIG_TLVS = [ + TLV_VALUES['ED25519'] +] + +def key_and_user_sha_to_alg_and_tlv(key, user_sha, is_pure = False): + """Matches key and user requested sha to sha alogrithm and TLV name. + + The returned tuple will contain hash functions and TVL name. + The function is designed to succeed or completely fail execution, + as providing incorrect pair here basically prevents doing + any more work. + """ + if key is None: + # If key is none, we allow whatever user has selected for sha + return USER_SHA_TO_ALG_AND_TLV[user_sha] + + # If key is not None, then we have to filter hash to only allowed + allowed = None + allowed_key_ssh = ALLOWED_PURE_KEY_SHA if is_pure else ALLOWED_KEY_SHA + try: + allowed = allowed_key_ssh[type(key)] + + except KeyError: + raise click.UsageError("Colud not find allowed hash algorithms for {}" + .format(type(key))) + + # Pure enforces auto, and user selection is ignored + if user_sha == 'auto' or is_pure: + return USER_SHA_TO_ALG_AND_TLV[allowed[0]] + + if user_sha in allowed: + return USER_SHA_TO_ALG_AND_TLV[user_sha] + + raise click.UsageError("Key {} can not be used with --sha {}; allowed sha are one of {}" + .format(key.sig_type(), user_sha, allowed)) + + +def get_digest(tlv_type, hash_region): + sha = TLV_SHA_TO_SHA_AND_ALG[tlv_type].alg() + + sha.update(hash_region) + return sha.digest() + + +def tlv_matches_key_type(tlv_type, key): + """Check if provided key matches to TLV record in the image""" + try: + # We do not need the result here, and the key_and_user_sha_to_alg_and_tlv + # will either succeed finding match or rise exception, so on success we + # return True, on exception we return False. + _, _ = key_and_user_sha_to_alg_and_tlv(key, tlv_sha_to_sha(tlv_type)) + return True + except: + pass + + return False + + +class Image: + + def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, + pad_header=False, pad=False, confirm=False, align=1, + slot_size=0, max_sectors=DEFAULT_MAX_SECTORS, + overwrite_only=False, endian="little", load_addr=0, + rom_fixed=None, erased_val=None, save_enctlv=False, + security_counter=None, max_align=None, + non_bootable=False): + + if load_addr and rom_fixed: + raise click.UsageError("Can not set rom_fixed and load_addr at the same time") + + self.image_hash = None + self.image_size = None + self.signature = None + self.version = version or versmod.decode_version("0") + self.header_size = header_size + self.pad_header = pad_header + self.pad = pad + self.confirm = confirm + self.align = align + self.slot_size = slot_size + self.max_sectors = max_sectors + self.overwrite_only = overwrite_only + self.endian = endian + self.base_addr = None + self.load_addr = 0 if load_addr is None else load_addr + self.rom_fixed = rom_fixed + self.erased_val = 0xff if erased_val is None else int(erased_val, 0) + self.payload = [] + self.infile_data = [] + self.enckey = None + self.save_enctlv = save_enctlv + self.enctlv_len = 0 + self.max_align = max(DEFAULT_MAX_ALIGN, align) if max_align is None else int(max_align) + self.non_bootable = non_bootable + + if self.max_align == DEFAULT_MAX_ALIGN: + self.boot_magic = bytes([ + 0x77, 0xc2, 0x95, 0xf3, + 0x60, 0xd2, 0xef, 0x7f, + 0x35, 0x52, 0x50, 0x0f, + 0x2c, 0xb6, 0x79, 0x80, ]) + else: + lsb = self.max_align & 0x00ff + msb = (self.max_align & 0xff00) >> 8 + align = bytes([msb, lsb]) if self.endian == "big" else bytes([lsb, msb]) + self.boot_magic = align + bytes([0x2d, 0xe1, + 0x5d, 0x29, 0x41, 0x0b, + 0x8d, 0x77, 0x67, 0x9c, + 0x11, 0x0f, 0x1f, 0x8a, ]) + + if security_counter == 'auto': + # Security counter has not been explicitly provided, + # generate it from the version number + self.security_counter = ((self.version.major << 24) + + (self.version.minor << 16) + + self.version.revision) + else: + self.security_counter = security_counter + + def __repr__(self): + return "".format( + self.version, + self.header_size, + self.security_counter, + self.base_addr if self.base_addr is not None else "N/A", + self.load_addr, + self.align, + self.slot_size, + self.max_sectors, + self.overwrite_only, + self.endian, + self.__class__.__name__, + len(self.payload)) + + def load(self, path): + """Load an image from a given file""" + ext = os.path.splitext(path)[1][1:].lower() + try: + if ext == INTEL_HEX_EXT: + ih = IntelHex(path) + self.infile_data = ih.tobinarray() + self.payload = copy.copy(self.infile_data) + self.base_addr = ih.minaddr() + else: + with open(path, 'rb') as f: + self.infile_data = f.read() + self.payload = copy.copy(self.infile_data) + except FileNotFoundError: + raise click.UsageError("Input file not found") + self.image_size = len(self.payload) + + # Add the image header if needed. + if self.pad_header and self.header_size > 0: + if self.base_addr: + # Adjust base_addr for new header + self.base_addr -= self.header_size + self.payload = bytes([self.erased_val] * self.header_size) + \ + self.payload + + self.check_header() + + def load_compressed(self, data, compression_header): + """Load an image from buffer""" + self.payload = compression_header + data + self.image_size = len(self.payload) + + # Add the image header if needed. + if self.pad_header and self.header_size > 0: + if self.base_addr: + # Adjust base_addr for new header + self.base_addr -= self.header_size + self.payload = bytes([self.erased_val] * self.header_size) + \ + self.payload + + self.check_header() + + def save(self, path, hex_addr=None): + """Save an image from a given file""" + ext = os.path.splitext(path)[1][1:].lower() + if ext == INTEL_HEX_EXT: + # input was in binary format, but HEX needs to know the base addr + if self.base_addr is None and hex_addr is None: + raise click.UsageError("No address exists in input file " + "neither was it provided by user") + h = IntelHex() + if hex_addr is not None: + self.base_addr = hex_addr + h.frombytes(bytes=self.payload, offset=self.base_addr) + if self.pad: + trailer_size = self._trailer_size(self.align, self.max_sectors, + self.overwrite_only, + self.enckey, + self.save_enctlv, + self.enctlv_len) + trailer_addr = (self.base_addr + self.slot_size) - trailer_size + if self.confirm and not self.overwrite_only: + magic_align_size = align_up(len(self.boot_magic), + self.max_align) + image_ok_idx = -(magic_align_size + self.max_align) + flag = bytearray([self.erased_val] * self.max_align) + flag[0] = 0x01 # image_ok = 0x01 + h.puts(trailer_addr + trailer_size + image_ok_idx, + bytes(flag)) + h.puts(trailer_addr + (trailer_size - len(self.boot_magic)), + bytes(self.boot_magic)) + h.tofile(path, 'hex') + else: + if self.pad: + self.pad_to(self.slot_size) + with open(path, 'wb') as f: + f.write(self.payload) + + def check_header(self): + if self.header_size > 0 and not self.pad_header: + if any(v != 0 for v in self.payload[0:self.header_size]): + raise click.UsageError("Header padding was not requested and " + "image does not start with zeros") + + def check_trailer(self): + if self.slot_size > 0: + tsize = self._trailer_size(self.align, self.max_sectors, + self.overwrite_only, self.enckey, + self.save_enctlv, self.enctlv_len) + padding = self.slot_size - (len(self.payload) + tsize) + if padding < 0: + msg = "Image size (0x{:x}) + trailer (0x{:x}) exceeds " \ + "requested size 0x{:x}".format( + len(self.payload), tsize, self.slot_size) + raise click.UsageError(msg) + + def ecies_hkdf(self, enckey, plainkey): + if isinstance(enckey, ecdsa.ECDSA256P1Public): + newpk = ec.generate_private_key(ec.SECP256R1(), default_backend()) + shared = newpk.exchange(ec.ECDH(), enckey._get_public()) + else: + newpk = X25519PrivateKey.generate() + shared = newpk.exchange(enckey._get_public()) + derived_key = HKDF( + algorithm=hashes.SHA256(), length=48, salt=None, + info=b'MCUBoot_ECIES_v1', backend=default_backend()).derive(shared) + encryptor = Cipher(algorithms.AES(derived_key[:16]), + modes.CTR(bytes([0] * 16)), + backend=default_backend()).encryptor() + cipherkey = encryptor.update(plainkey) + encryptor.finalize() + mac = hmac.HMAC(derived_key[16:], hashes.SHA256(), + backend=default_backend()) + mac.update(cipherkey) + ciphermac = mac.finalize() + if isinstance(enckey, ecdsa.ECDSA256P1Public): + pubk = newpk.public_key().public_bytes( + encoding=Encoding.X962, + format=PublicFormat.UncompressedPoint) + else: + pubk = newpk.public_key().public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw) + return cipherkey, ciphermac, pubk + + def create(self, key, public_key_format, enckey, dependencies=None, + sw_type=None, custom_tlvs=None, compression_tlvs=None, + compression_type=None, encrypt_keylen=128, clear=False, + fixed_sig=None, pub_key=None, vector_to_sign=None, + user_sha='auto', is_pure=False): + self.enckey = enckey + + # key decides on sha, then pub_key; of both are none default is used + check_key = key if key is not None else pub_key + hash_algorithm, hash_tlv = key_and_user_sha_to_alg_and_tlv(check_key, user_sha, is_pure) + + # Calculate the hash of the public key + if key is not None: + pub = key.get_public_bytes() + sha = hash_algorithm() + sha.update(pub) + pubbytes = sha.digest() + elif pub_key is not None: + if hasattr(pub_key, 'sign'): + print(os.path.basename(__file__) + ": sign the payload") + pub = pub_key.get_public_bytes() + sha = hash_algorithm() + sha.update(pub) + pubbytes = sha.digest() + else: + pubbytes = bytes(hashlib.sha256().digest_size) + + protected_tlv_size = 0 + + if self.security_counter is not None: + # Size of the security counter TLV: header ('HH') + payload ('I') + # = 4 + 4 = 8 Bytes + protected_tlv_size += TLV_SIZE + 4 + + if sw_type is not None: + if len(sw_type) > MAX_SW_TYPE_LENGTH: + msg = "'{}' is too long ({} characters) for sw_type. Its " \ + "maximum allowed length is 12 characters.".format( + sw_type, len(sw_type)) + raise click.UsageError(msg) + + image_version = (str(self.version.major) + '.' + + str(self.version.minor) + '.' + + str(self.version.revision)) + + # The image hash is computed over the image header, the image + # itself and the protected TLV area. However, the boot record TLV + # (which is part of the protected area) should contain this hash + # before it is even calculated. For this reason the script fills + # this field with zeros and the bootloader will insert the right + # value later. + digest = bytes(hash_algorithm().digest_size) + + # Create CBOR encoded boot record + boot_record = create_sw_component_data(sw_type, image_version, + hash_tlv, digest, + pubbytes) + + protected_tlv_size += TLV_SIZE + len(boot_record) + + if dependencies is not None: + # Size of a Dependency TLV = Header ('HH') + Payload('IBBHI') + # = 4 + 12 = 16 Bytes + dependencies_num = len(dependencies[DEP_IMAGES_KEY]) + protected_tlv_size += (dependencies_num * 16) + + if compression_tlvs is not None: + for value in compression_tlvs.values(): + protected_tlv_size += TLV_SIZE + len(value) + if custom_tlvs is not None: + for value in custom_tlvs.values(): + protected_tlv_size += TLV_SIZE + len(value) + + if protected_tlv_size != 0: + # Add the size of the TLV info header + protected_tlv_size += TLV_INFO_SIZE + + # At this point the image is already on the payload + # + # This adds the padding if image is not aligned to the 16 Bytes + # in encrypted mode + if self.enckey is not None: + pad_len = len(self.payload) % 16 + if pad_len > 0: + pad = bytes(16 - pad_len) + if isinstance(self.payload, bytes): + self.payload += pad + else: + self.payload.extend(pad) + + compression_flags = 0x0 + if compression_tlvs is not None: + if compression_type in ["lzma2", "lzma2armthumb"]: + compression_flags = IMAGE_F['COMPRESSED_LZMA2'] + if compression_type == "lzma2armthumb": + compression_flags |= IMAGE_F['COMPRESSED_ARM_THUMB'] + # This adds the header to the payload as well + if encrypt_keylen == 256: + self.add_header(enckey, protected_tlv_size, compression_flags, 256) + else: + self.add_header(enckey, protected_tlv_size, compression_flags) + + prot_tlv = TLV(self.endian, TLV_PROT_INFO_MAGIC) + + # Protected TLVs must be added first, because they are also included + # in the hash calculation + protected_tlv_off = None + if protected_tlv_size != 0: + + e = STRUCT_ENDIAN_DICT[self.endian] + + if self.security_counter is not None: + payload = struct.pack(e + 'I', self.security_counter) + prot_tlv.add('SEC_CNT', payload) + + if sw_type is not None: + prot_tlv.add('BOOT_RECORD', boot_record) + + if dependencies is not None: + for i in range(dependencies_num): + payload = struct.pack( + e + 'B3x' + 'BBHI', + int(dependencies[DEP_IMAGES_KEY][i]), + dependencies[DEP_VERSIONS_KEY][i].major, + dependencies[DEP_VERSIONS_KEY][i].minor, + dependencies[DEP_VERSIONS_KEY][i].revision, + dependencies[DEP_VERSIONS_KEY][i].build + ) + prot_tlv.add('DEPENDENCY', payload) + + if compression_tlvs is not None: + for tag, value in compression_tlvs.items(): + prot_tlv.add(tag, value) + if custom_tlvs is not None: + for tag, value in custom_tlvs.items(): + prot_tlv.add(tag, value) + + protected_tlv_off = len(self.payload) + self.payload += prot_tlv.get() + + tlv = TLV(self.endian) + + # These signature is done over sha of image. In case of + # EC signatures so called Pure algorithm, designated to be run + # over entire message is used with sha of image as message, + # so, for example, in case of ED25519 we have here SHAxxx-ED25519-SHA512. + sha = hash_algorithm() + sha.update(self.payload) + digest = sha.digest() + tlv.add(hash_tlv, digest) + # for external usage + self.image_hash = digest + # Unless pure, we are signing digest. + message = digest + + if is_pure: + # Note that when Pure signature is used, hash TLV is not present. + message = bytes(self.payload) + e = STRUCT_ENDIAN_DICT[self.endian] + sig_pure = struct.pack(e + '?', True) + tlv.add('SIG_PURE', sig_pure) + + if vector_to_sign == 'payload': + # Stop amending data to the image + # Just keep data vector which is expected to be signed + print(os.path.basename(__file__) + ': export payload') + return + elif vector_to_sign == 'digest': + self.payload = digest + print(os.path.basename(__file__) + ': export digest') + return + + if key is not None or fixed_sig is not None: + if public_key_format == 'hash': + tlv.add('KEYHASH', pubbytes) + else: + tlv.add('PUBKEY', pub) + + if key is not None and fixed_sig is None: + # `sign` expects the full image payload (hashing done + # internally), while `sign_digest` expects only the digest + # of the payload + + if hasattr(key, 'sign'): + print(os.path.basename(__file__) + ": sign the payload") + sig = key.sign(bytes(self.payload)) + else: + print(os.path.basename(__file__) + ": sign the digest") + sig = key.sign_digest(message) + tlv.add(key.sig_tlv(), sig) + self.signature = sig + elif fixed_sig is not None and key is None: + tlv.add(pub_key.sig_tlv(), fixed_sig['value']) + self.signature = fixed_sig['value'] + else: + raise click.UsageError("Can not sign using key and provide fixed-signature at the same time") + + # At this point the image was hashed + signed, we can remove the + # protected TLVs from the payload (will be re-added later) + if protected_tlv_off is not None: + self.payload = self.payload[:protected_tlv_off] + + if enckey is not None: + if encrypt_keylen == 256: + plainkey = os.urandom(32) + else: + plainkey = os.urandom(16) + + if isinstance(enckey, rsa.RSAPublic): + cipherkey = enckey._get_public().encrypt( + plainkey, padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None)) + self.enctlv_len = len(cipherkey) + tlv.add('ENCRSA2048', cipherkey) + elif isinstance(enckey, (ecdsa.ECDSA256P1Public, + x25519.X25519Public)): + cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey) + enctlv = pubk + mac + cipherkey + self.enctlv_len = len(enctlv) + if isinstance(enckey, ecdsa.ECDSA256P1Public): + tlv.add('ENCEC256', enctlv) + else: + tlv.add('ENCX25519', enctlv) + + if not clear: + nonce = bytes([0] * 16) + cipher = Cipher(algorithms.AES(plainkey), modes.CTR(nonce), + backend=default_backend()) + encryptor = cipher.encryptor() + img = bytes(self.payload[self.header_size:]) + self.payload[self.header_size:] = \ + encryptor.update(img) + encryptor.finalize() + + self.payload += prot_tlv.get() + self.payload += tlv.get() + + self.check_trailer() + + def get_struct_endian(self): + return STRUCT_ENDIAN_DICT[self.endian] + + def get_signature(self): + return self.signature + + def get_infile_data(self): + return self.infile_data + + def add_header(self, enckey, protected_tlv_size, compression_flags, aes_length=128): + """Install the image header.""" + + flags = 0 + if enckey is not None: + if aes_length == 128: + flags |= IMAGE_F['ENCRYPTED_AES128'] + else: + flags |= IMAGE_F['ENCRYPTED_AES256'] + if self.load_addr != 0: + # Indicates that this image should be loaded into RAM + # instead of run directly from flash. + flags |= IMAGE_F['RAM_LOAD'] + if self.rom_fixed: + flags |= IMAGE_F['ROM_FIXED'] + if self.non_bootable: + flags |= IMAGE_F['NON_BOOTABLE'] + + e = STRUCT_ENDIAN_DICT[self.endian] + fmt = (e + + # type ImageHdr struct { + 'I' + # Magic uint32 + 'I' + # LoadAddr uint32 + 'H' + # HdrSz uint16 + 'H' + # PTLVSz uint16 + 'I' + # ImgSz uint32 + 'I' + # Flags uint32 + 'BBHI' + # Vers ImageVersion + 'I' # Pad1 uint32 + ) # } + assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE + header = struct.pack(fmt, + IMAGE_MAGIC, + self.rom_fixed or self.load_addr, + self.header_size, + protected_tlv_size, # TLV Info header + + # Protected TLVs + len(self.payload) - self.header_size, # ImageSz + flags | compression_flags, + self.version.major, + self.version.minor or 0, + self.version.revision or 0, + self.version.build or 0, + 0) # Pad1 + self.payload = bytearray(self.payload) + self.payload[:len(header)] = header + + def _trailer_size(self, write_size, max_sectors, overwrite_only, enckey, + save_enctlv, enctlv_len): + # NOTE: should already be checked by the argument parser + magic_size = 16 + magic_align_size = align_up(magic_size, self.max_align) + if overwrite_only: + return self.max_align * 2 + magic_align_size + else: + if write_size not in set([1, 2, 4, 8, 16, 32]): + raise click.BadParameter("Invalid alignment: {}".format( + write_size)) + m = DEFAULT_MAX_SECTORS if max_sectors is None else max_sectors + trailer = m * 3 * write_size # status area + if enckey is not None: + if save_enctlv: + # TLV saved by the bootloader is aligned + keylen = align_up(enctlv_len, self.max_align) + else: + keylen = align_up(16, self.max_align) + trailer += keylen * 2 # encryption keys + trailer += self.max_align * 4 # image_ok/copy_done/swap_info/swap_size + trailer += magic_align_size + return trailer + + def pad_to(self, size): + """Pad the image to the given size, with the given flash alignment.""" + tsize = self._trailer_size(self.align, self.max_sectors, + self.overwrite_only, self.enckey, + self.save_enctlv, self.enctlv_len) + padding = size - (len(self.payload) + tsize) + pbytes = bytearray([self.erased_val] * padding) + pbytes += bytearray([self.erased_val] * (tsize - len(self.boot_magic))) + pbytes += self.boot_magic + if self.confirm and not self.overwrite_only: + magic_size = 16 + magic_align_size = align_up(magic_size, self.max_align) + image_ok_idx = -(magic_align_size + self.max_align) + pbytes[image_ok_idx] = 0x01 # image_ok = 0x01 + self.payload += pbytes + + @staticmethod + def verify(imgfile, key): + ext = os.path.splitext(imgfile)[1][1:].lower() + try: + if ext == INTEL_HEX_EXT: + b = IntelHex(imgfile).tobinstr() + else: + with open(imgfile, 'rb') as f: + b = f.read() + except FileNotFoundError: + raise click.UsageError(f"Image file {imgfile} not found") + + magic, _, header_size, _, img_size = struct.unpack('IIHHI', b[:16]) + version = struct.unpack('BBHI', b[20:28]) + + if magic != IMAGE_MAGIC: + return VerifyResult.INVALID_MAGIC, None, None, None + + tlv_off = header_size + img_size + tlv_info = b[tlv_off:tlv_off + TLV_INFO_SIZE] + magic, tlv_tot = struct.unpack('HH', tlv_info) + if magic == TLV_PROT_INFO_MAGIC: + tlv_off += tlv_tot + tlv_info = b[tlv_off:tlv_off + TLV_INFO_SIZE] + magic, tlv_tot = struct.unpack('HH', tlv_info) + + if magic != TLV_INFO_MAGIC: + return VerifyResult.INVALID_TLV_INFO_MAGIC, None, None, None + + # This is set by existence of TLV SIG_PURE + is_pure = False + + prot_tlv_size = tlv_off + hash_region = b[:prot_tlv_size] + tlv_end = tlv_off + tlv_tot + tlv_off += TLV_INFO_SIZE # skip tlv info + + # First scan all TLVs in search of SIG_PURE + while tlv_off < tlv_end: + tlv = b[tlv_off:tlv_off + TLV_SIZE] + tlv_type, _, tlv_len = struct.unpack('BBH', tlv) + if tlv_type == TLV_VALUES['SIG_PURE']: + is_pure = True + break + tlv_off += TLV_SIZE + tlv_len + + digest = None + tlv_off = header_size + img_size + tlv_end = tlv_off + tlv_tot + tlv_off += TLV_INFO_SIZE # skip tlv info + while tlv_off < tlv_end: + tlv = b[tlv_off:tlv_off + TLV_SIZE] + tlv_type, _, tlv_len = struct.unpack('BBH', tlv) + if is_sha_tlv(tlv_type): + if not tlv_matches_key_type(tlv_type, key): + return VerifyResult.KEY_MISMATCH, None, None, None + off = tlv_off + TLV_SIZE + digest = get_digest(tlv_type, hash_region) + if digest == b[off:off + tlv_len]: + if key is None: + return VerifyResult.OK, version, digest, None + else: + return VerifyResult.INVALID_HASH, None, None, None + elif not is_pure and key is not None and tlv_type == TLV_VALUES[key.sig_tlv()]: + off = tlv_off + TLV_SIZE + tlv_sig = b[off:off + tlv_len] + payload = b[:prot_tlv_size] + try: + if hasattr(key, 'verify'): + key.verify(tlv_sig, payload) + else: + key.verify_digest(tlv_sig, digest) + return VerifyResult.OK, version, digest, None + except InvalidSignature: + # continue to next TLV + pass + elif is_pure and key is not None and tlv_type in ALLOWED_PURE_SIG_TLVS: + off = tlv_off + TLV_SIZE + tlv_sig = b[off:off + tlv_len] + try: + key.verify_digest(tlv_sig, hash_region) + return VerifyResult.OK, version, None, tlv_sig + except InvalidSignature: + # continue to next TLV + pass + tlv_off += TLV_SIZE + tlv_len + return VerifyResult.INVALID_SIGNATURE, None, None, None diff --git a/bootloader/mcuboot/scripts/imgtool/keys/__init__.py b/bootloader/mcuboot/scripts/imgtool/keys/__init__.py new file mode 100644 index 0000000..ed2fed5 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/__init__.py @@ -0,0 +1,105 @@ +# Copyright 2017 Linaro Limited +# Copyright 2023 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Cryptographic key management for imgtool. +""" + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, RSAPublicKey) +from cryptography.hazmat.primitives.asymmetric.ec import ( + EllipticCurvePrivateKey, EllipticCurvePublicKey) +from cryptography.hazmat.primitives.asymmetric.ed25519 import ( + Ed25519PrivateKey, Ed25519PublicKey) +from cryptography.hazmat.primitives.asymmetric.x25519 import ( + X25519PrivateKey, X25519PublicKey) + +from .rsa import RSA, RSAPublic, RSAUsageError, RSA_KEY_SIZES +from .ecdsa import (ECDSA256P1, ECDSA256P1Public, + ECDSA384P1, ECDSA384P1Public, ECDSAUsageError) +from .ed25519 import Ed25519, Ed25519Public, Ed25519UsageError +from .x25519 import X25519, X25519Public, X25519UsageError + + +class PasswordRequired(Exception): + """Raised to indicate that the key is password protected, but a + password was not specified.""" + pass + + +def load(path, passwd=None): + """Try loading a key from the given path. + Returns None if the password wasn't specified.""" + with open(path, 'rb') as f: + raw_pem = f.read() + try: + pk = serialization.load_pem_private_key( + raw_pem, + password=passwd, + backend=default_backend()) + # Unfortunately, the crypto library raises unhelpful exceptions, + # so we have to look at the text. + except TypeError as e: + msg = str(e) + if "private key is encrypted" in msg: + return None + raise e + except ValueError: + # This seems to happen if the key is a public key, let's try + # loading it as a public key. + pk = serialization.load_pem_public_key( + raw_pem, + backend=default_backend()) + + if isinstance(pk, RSAPrivateKey): + if pk.key_size not in RSA_KEY_SIZES: + raise Exception("Unsupported RSA key size: " + pk.key_size) + return RSA(pk) + elif isinstance(pk, RSAPublicKey): + if pk.key_size not in RSA_KEY_SIZES: + raise Exception("Unsupported RSA key size: " + pk.key_size) + return RSAPublic(pk) + elif isinstance(pk, EllipticCurvePrivateKey): + if pk.curve.name not in ('secp256r1', 'secp384r1'): + raise Exception("Unsupported EC curve: " + pk.curve.name) + if pk.key_size not in (256, 384): + raise Exception("Unsupported EC size: " + pk.key_size) + if pk.curve.name == 'secp256r1': + return ECDSA256P1(pk) + elif pk.curve.name == 'secp384r1': + return ECDSA384P1(pk) + elif isinstance(pk, EllipticCurvePublicKey): + if pk.curve.name not in ('secp256r1', 'secp384r1'): + raise Exception("Unsupported EC curve: " + pk.curve.name) + if pk.key_size not in (256, 384): + raise Exception("Unsupported EC size: " + pk.key_size) + if pk.curve.name == 'secp256r1': + return ECDSA256P1Public(pk) + elif pk.curve.name == 'secp384r1': + return ECDSA384P1Public(pk) + elif isinstance(pk, Ed25519PrivateKey): + return Ed25519(pk) + elif isinstance(pk, Ed25519PublicKey): + return Ed25519Public(pk) + elif isinstance(pk, X25519PrivateKey): + return X25519(pk) + elif isinstance(pk, X25519PublicKey): + return X25519Public(pk) + else: + raise Exception("Unknown key type: " + str(type(pk))) diff --git a/bootloader/mcuboot/scripts/imgtool/keys/ecdsa.py b/bootloader/mcuboot/scripts/imgtool/keys/ecdsa.py new file mode 100644 index 0000000..52357b1 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/ecdsa.py @@ -0,0 +1,289 @@ +""" +ECDSA key management +""" + +# SPDX-License-Identifier: Apache-2.0 +import os.path +import hashlib + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.hashes import SHA256, SHA384 + +from .general import KeyClass +from .privatebytes import PrivateBytesMixin + + +class ECDSAUsageError(Exception): + pass + + +class ECDSAPublicKey(KeyClass): + """ + Wrapper around an ECDSA public key. + """ + def __init__(self, key): + self.key = key + + def _unsupported(self, name): + raise ECDSAUsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key is embedded into MBUboot in "SubjectPublicKeyInfo" format + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_public_pem(self): + return self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_private_bytes(self, minimal, format): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + +class ECDSAPrivateKey(PrivateBytesMixin): + """ + Wrapper around an ECDSA private key. + """ + def __init__(self, key): + self.key = key + + def _get_public(self): + return self.key.public_key() + + def _build_minimal_ecdsa_privkey(self, der, format): + ''' + Builds a new DER that only includes the EC private key, removing the + public key that is added as an "optional" BITSTRING. + ''' + + if format == serialization.PrivateFormat.OpenSSH: + print(os.path.basename(__file__) + + ': Warning: --minimal is supported only for PKCS8 ' + 'or TraditionalOpenSSL formats') + return bytearray(der) + + EXCEPTION_TEXT = "Error parsing ecdsa key. Please submit an issue!" + if format == serialization.PrivateFormat.PKCS8: + offset_PUB = 68 # where the context specific TLV starts (tag 0xA1) + if der[offset_PUB] != 0xa1: + raise ECDSAUsageError(EXCEPTION_TEXT) + len_PUB = der[offset_PUB + 1] + 2 # + 2 for 0xA1 0x44 bytes + b = bytearray(der[:offset_PUB]) # remove the TLV with the PUB key + offset_SEQ = 29 + if b[offset_SEQ] != 0x30: + raise ECDSAUsageError(EXCEPTION_TEXT) + b[offset_SEQ + 1] -= len_PUB + offset_OCT_STR = 27 + if b[offset_OCT_STR] != 0x04: + raise ECDSAUsageError(EXCEPTION_TEXT) + b[offset_OCT_STR + 1] -= len_PUB + if b[0] != 0x30 or b[1] != 0x81: + raise ECDSAUsageError(EXCEPTION_TEXT) + # as b[1] has bit7 set, the length is on b[2] + b[2] -= len_PUB + if b[2] < 0x80: + del(b[1]) + + elif format == serialization.PrivateFormat.TraditionalOpenSSL: + offset_PUB = 51 + if der[offset_PUB] != 0xA1: + raise ECDSAUsageError(EXCEPTION_TEXT) + len_PUB = der[offset_PUB + 1] + 2 + b = bytearray(der[0:offset_PUB]) + b[1] -= len_PUB + + return b + + _VALID_FORMATS = { + 'pkcs8': serialization.PrivateFormat.PKCS8, + 'openssl': serialization.PrivateFormat.TraditionalOpenSSL + } + _DEFAULT_FORMAT = 'pkcs8' + + def get_private_bytes(self, minimal, format): + format, priv = self._get_private_bytes(minimal, + format, ECDSAUsageError) + if minimal: + priv = self._build_minimal_ecdsa_privkey( + priv, self._VALID_FORMATS[format]) + return priv + + def export_private(self, path, passwd=None): + """Write the private key to the given file, protecting it with ' + 'the optional password.""" + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + +class ECDSA256P1Public(ECDSAPublicKey): + """ + Wrapper around an ECDSA (p256) public key. + """ + def __init__(self, key): + super().__init__(key) + self.key = key + + def shortname(self): + return "ecdsa" + + def sig_type(self): + return "ECDSA256_SHA256" + + def sig_tlv(self): + return "ECDSASIG" + + def sig_len(self): + # Early versions of MCUboot (< v1.5.0) required ECDSA + # signatures to be padded to 72 bytes. Because the DER + # encoding is done with signed integers, the size of the + # signature will vary depending on whether the high bit is set + # in each value. This padding was done in a + # not-easily-reversible way (by just adding zeros). + # + # The signing code no longer requires this padding, and newer + # versions of MCUboot don't require it. But, continue to + # return the total length so that the padding can be done if + # requested. + return 72 + + def verify(self, signature, payload): + # strip possible paddings added during sign + signature = signature[:signature[1] + 2] + k = self.key + if isinstance(self.key, ec.EllipticCurvePrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=payload, + signature_algorithm=ec.ECDSA(SHA256())) + + +class ECDSA256P1(ECDSAPrivateKey, ECDSA256P1Public): + """ + Wrapper around an ECDSA (p256) private key. + """ + def __init__(self, key): + super().__init__(key) + self.key = key + self.pad_sig = False + + @staticmethod + def generate(): + pk = ec.generate_private_key( + ec.SECP256R1(), + backend=default_backend()) + return ECDSA256P1(pk) + + def raw_sign(self, payload): + """Return the actual signature""" + return self.key.sign( + data=payload, + signature_algorithm=ec.ECDSA(SHA256())) + + def sign(self, payload): + sig = self.raw_sign(payload) + if self.pad_sig: + # To make fixed length, pad with one or two zeros. + sig += b'\000' * (self.sig_len() - len(sig)) + return sig + else: + return sig + + +class ECDSA384P1Public(ECDSAPublicKey): + """ + Wrapper around an ECDSA (p384) public key. + """ + def __init__(self, key): + super().__init__(key) + self.key = key + + def shortname(self): + return "ecdsap384" + + def sig_type(self): + return "ECDSA384_SHA384" + + def sig_tlv(self): + return "ECDSASIG" + + def sig_len(self): + # Early versions of MCUboot (< v1.5.0) required ECDSA + # signatures to be padded to a fixed length. Because the DER + # encoding is done with signed integers, the size of the + # signature will vary depending on whether the high bit is set + # in each value. This padding was done in a + # not-easily-reversible way (by just adding zeros). + # + # The signing code no longer requires this padding, and newer + # versions of MCUboot don't require it. But, continue to + # return the total length so that the padding can be done if + # requested. + return 103 + + def verify(self, signature, payload): + # strip possible paddings added during sign + signature = signature[:signature[1] + 2] + k = self.key + if isinstance(self.key, ec.EllipticCurvePrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=payload, + signature_algorithm=ec.ECDSA(SHA384())) + + +class ECDSA384P1(ECDSAPrivateKey, ECDSA384P1Public): + """ + Wrapper around an ECDSA (p384) private key. + """ + + def __init__(self, key): + """key should be an instance of EllipticCurvePrivateKey""" + super().__init__(key) + self.key = key + self.pad_sig = False + + @staticmethod + def generate(): + pk = ec.generate_private_key( + ec.SECP384R1(), + backend=default_backend()) + return ECDSA384P1(pk) + + def raw_sign(self, payload): + """Return the actual signature""" + return self.key.sign( + data=payload, + signature_algorithm=ec.ECDSA(SHA384())) + + def sign(self, payload): + sig = self.raw_sign(payload) + if self.pad_sig: + # To make fixed length, pad with one or two zeros. + sig += b'\000' * (self.sig_len() - len(sig)) + return sig + else: + return sig diff --git a/bootloader/mcuboot/scripts/imgtool/keys/ecdsa_test.py b/bootloader/mcuboot/scripts/imgtool/keys/ecdsa_test.py new file mode 100644 index 0000000..55f6cc4 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/ecdsa_test.py @@ -0,0 +1,120 @@ +""" +Tests for ECDSA keys +""" + +# SPDX-License-Identifier: Apache-2.0 + +import io +import os.path +import sys +import tempfile +import unittest + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.hashes import SHA256 + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +from imgtool.keys import load, ECDSA256P1, ECDSAUsageError + +class EcKeyGeneration(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.TemporaryDirectory() + + def tname(self, base): + return os.path.join(self.test_dir.name, base) + + def tearDown(self): + self.test_dir.cleanup() + + def test_keygen(self): + name1 = self.tname("keygen.pem") + k = ECDSA256P1.generate() + k.export_private(name1, b'secret') + + self.assertIsNone(load(name1)) + + k2 = load(name1, b'secret') + + pubname = self.tname('keygen-pub.pem') + k2.export_public(pubname) + pk2 = load(pubname) + + # We should be able to export the public key from the loaded + # public key, but not the private key. + pk2.export_public(self.tname('keygen-pub2.pem')) + self.assertRaises(ECDSAUsageError, + pk2.export_private, self.tname('keygen-priv2.pem')) + + def test_emit(self): + """Basic sanity check on the code emitters.""" + k = ECDSA256P1.generate() + + pubpem = io.StringIO() + k.emit_public_pem(pubpem) + self.assertIn("BEGIN PUBLIC KEY", pubpem.getvalue()) + self.assertIn("END PUBLIC KEY", pubpem.getvalue()) + + ccode = io.StringIO() + k.emit_c_public(ccode) + self.assertIn("ecdsa_pub_key", ccode.getvalue()) + self.assertIn("ecdsa_pub_key_len", ccode.getvalue()) + + hashccode = io.StringIO() + k.emit_c_public_hash(hashccode) + self.assertIn("ecdsa_pub_key_hash", hashccode.getvalue()) + self.assertIn("ecdsa_pub_key_hash_len", hashccode.getvalue()) + + rustcode = io.StringIO() + k.emit_rust_public(rustcode) + self.assertIn("ECDSA_PUB_KEY", rustcode.getvalue()) + + # raw data - bytes + pubraw = io.BytesIO() + k.emit_raw_public(pubraw) + self.assertTrue(len(pubraw.getvalue()) > 0) + + hashraw = io.BytesIO() + k.emit_raw_public_hash(hashraw) + self.assertTrue(len(hashraw.getvalue()) > 0) + + def test_emit_pub(self): + """Basic sanity check on the code emitters, from public key.""" + pubname = self.tname("public.pem") + k = ECDSA256P1.generate() + k.export_public(pubname) + + k2 = load(pubname) + + ccode = io.StringIO() + k2.emit_c_public(ccode) + self.assertIn("ecdsa_pub_key", ccode.getvalue()) + self.assertIn("ecdsa_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k2.emit_rust_public(rustcode) + self.assertIn("ECDSA_PUB_KEY", rustcode.getvalue()) + + def test_sig(self): + k = ECDSA256P1.generate() + buf = b'This is the message' + sig = k.raw_sign(buf) + + # The code doesn't have any verification, so verify this + # manually. + k.key.public_key().verify( + signature=sig, + data=buf, + signature_algorithm=ec.ECDSA(SHA256())) + + # Modify the message to make sure the signature fails. + self.assertRaises(InvalidSignature, + k.key.public_key().verify, + signature=sig, + data=b'This is thE message', + signature_algorithm=ec.ECDSA(SHA256())) + +if __name__ == '__main__': + unittest.main() diff --git a/bootloader/mcuboot/scripts/imgtool/keys/ed25519.py b/bootloader/mcuboot/scripts/imgtool/keys/ed25519.py new file mode 100644 index 0000000..a9959a6 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/ed25519.py @@ -0,0 +1,111 @@ +""" +ED25519 key management +""" + +# SPDX-License-Identifier: Apache-2.0 + +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ed25519 + +from .general import KeyClass + + +class Ed25519UsageError(Exception): + pass + + +class Ed25519Public(KeyClass): + def __init__(self, key): + self.key = key + + def shortname(self): + return "ed25519" + + def _unsupported(self, name): + raise Ed25519UsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key is embedded into MBUboot in "SubjectPublicKeyInfo" format + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_public_pem(self): + return self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_private_bytes(self, minimal, format): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + def sig_type(self): + return "ED25519" + + def sig_tlv(self): + return "ED25519" + + def sig_len(self): + return 64 + + def verify_digest(self, signature, digest): + """Verify that signature is valid for given digest""" + k = self.key + if isinstance(self.key, ed25519.Ed25519PrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=digest) + + +class Ed25519(Ed25519Public): + """ + Wrapper around an ED25519 private key. + """ + + def __init__(self, key): + """key should be an instance of EllipticCurvePrivateKey""" + self.key = key + + @staticmethod + def generate(): + pk = ed25519.Ed25519PrivateKey.generate() + return Ed25519(pk) + + def _get_public(self): + return self.key.public_key() + + def get_private_bytes(self, minimal, format): + raise Ed25519UsageError("Operation not supported with {} keys".format( + self.shortname())) + + def export_private(self, path, passwd=None): + """ + Write the private key to the given file, protecting it with the + optional password. + """ + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + def sign_digest(self, digest): + """Return the actual signature""" + return self.key.sign(data=digest) diff --git a/bootloader/mcuboot/scripts/imgtool/keys/ed25519_test.py b/bootloader/mcuboot/scripts/imgtool/keys/ed25519_test.py new file mode 100644 index 0000000..ae9ea49 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/ed25519_test.py @@ -0,0 +1,124 @@ +""" +Tests for ECDSA keys +""" + +# SPDX-License-Identifier: Apache-2.0 + +import hashlib +import io +import os.path +import sys +import tempfile +import unittest + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives.asymmetric import ed25519 + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +from imgtool.keys import load, Ed25519, Ed25519UsageError + + +class Ed25519KeyGeneration(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.TemporaryDirectory() + + def tname(self, base): + return os.path.join(self.test_dir.name, base) + + def tearDown(self): + self.test_dir.cleanup() + + def test_keygen(self): + name1 = self.tname("keygen.pem") + k = Ed25519.generate() + k.export_private(name1, b'secret') + + self.assertIsNone(load(name1)) + + k2 = load(name1, b'secret') + + pubname = self.tname('keygen-pub.pem') + k2.export_public(pubname) + pk2 = load(pubname) + + # We should be able to export the public key from the loaded + # public key, but not the private key. + pk2.export_public(self.tname('keygen-pub2.pem')) + self.assertRaises(Ed25519UsageError, + pk2.export_private, self.tname('keygen-priv2.pem')) + + def test_emit(self): + """Basic sanity check on the code emitters.""" + k = Ed25519.generate() + + pubpem = io.StringIO() + k.emit_public_pem(pubpem) + self.assertIn("BEGIN PUBLIC KEY", pubpem.getvalue()) + self.assertIn("END PUBLIC KEY", pubpem.getvalue()) + + ccode = io.StringIO() + k.emit_c_public(ccode) + self.assertIn("ed25519_pub_key", ccode.getvalue()) + self.assertIn("ed25519_pub_key_len", ccode.getvalue()) + + hashccode = io.StringIO() + k.emit_c_public_hash(hashccode) + self.assertIn("ed25519_pub_key_hash", hashccode.getvalue()) + self.assertIn("ed25519_pub_key_hash_len", hashccode.getvalue()) + + rustcode = io.StringIO() + k.emit_rust_public(rustcode) + self.assertIn("ED25519_PUB_KEY", rustcode.getvalue()) + + # raw data - bytes + pubraw = io.BytesIO() + k.emit_raw_public(pubraw) + self.assertTrue(len(pubraw.getvalue()) > 0) + + hashraw = io.BytesIO() + k.emit_raw_public_hash(hashraw) + self.assertTrue(len(hashraw.getvalue()) > 0) + + def test_emit_pub(self): + """Basic sanity check on the code emitters, from public key.""" + pubname = self.tname("public.pem") + k = Ed25519.generate() + k.export_public(pubname) + + k2 = load(pubname) + + ccode = io.StringIO() + k2.emit_c_public(ccode) + self.assertIn("ed25519_pub_key", ccode.getvalue()) + self.assertIn("ed25519_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k2.emit_rust_public(rustcode) + self.assertIn("ED25519_PUB_KEY", rustcode.getvalue()) + + def test_sig(self): + k = Ed25519.generate() + buf = b'This is the message' + sha = hashlib.sha256() + sha.update(buf) + digest = sha.digest() + sig = k.sign_digest(digest) + + # The code doesn't have any verification, so verify this + # manually. + k.key.public_key().verify(signature=sig, data=digest) + + # Modify the message to make sure the signature fails. + sha = hashlib.sha256() + sha.update(b'This is thE message') + new_digest = sha.digest() + self.assertRaises(InvalidSignature, + k.key.public_key().verify, + signature=sig, + data=new_digest) + + +if __name__ == '__main__': + unittest.main() diff --git a/bootloader/mcuboot/scripts/imgtool/keys/general.py b/bootloader/mcuboot/scripts/imgtool/keys/general.py new file mode 100644 index 0000000..4ff700c --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/general.py @@ -0,0 +1,115 @@ +"""General key class.""" + +# SPDX-License-Identifier: Apache-2.0 + +import binascii +import io +import os +import sys +from cryptography.hazmat.primitives.hashes import Hash, SHA256 + +AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */" + + +class FileHandler(object): + def __init__(self, file, *args, **kwargs): + self.file_in = file + self.args = args + self.kwargs = kwargs + + def __enter__(self): + if isinstance(self.file_in, (str, bytes, os.PathLike)): + self.file = open(self.file_in, *self.args, **self.kwargs) + else: + self.file = self.file_in + return self.file + + def __exit__(self, *args): + if self.file != self.file_in: + self.file.close() + + +class KeyClass(object): + def _emit(self, header, trailer, encoded_bytes, indent, file=sys.stdout, + len_format=None): + with FileHandler(file, 'w') as file: + self._emit_to_output(header, trailer, encoded_bytes, indent, + file, len_format) + + def _emit_to_output(self, header, trailer, encoded_bytes, indent, file, + len_format): + print(AUTOGEN_MESSAGE, file=file) + print(header, end='', file=file) + for count, b in enumerate(encoded_bytes): + if count % 8 == 0: + print("\n" + indent, end='', file=file) + else: + print(" ", end='', file=file) + print("0x{:02x},".format(b), end='', file=file) + print("\n" + trailer, file=file) + if len_format is not None: + print(len_format.format(len(encoded_bytes)), file=file) + + def _emit_raw(self, encoded_bytes, file): + with FileHandler(file, 'wb') as file: + try: + # file.buffer is not part of the TextIOBase API + # and may not exist in some implementations. + file.buffer.write(encoded_bytes) + except AttributeError: + # raw binary data, can be for example io.BytesIO + file.write(encoded_bytes) + + def emit_c_public(self, file=sys.stdout): + self._emit( + header="const unsigned char {}_pub_key[] = {{" + .format(self.shortname()), + trailer="};", + encoded_bytes=self.get_public_bytes(), + indent=" ", + len_format="const unsigned int {}_pub_key_len = {{}};" + .format(self.shortname()), + file=file) + + def emit_c_public_hash(self, file=sys.stdout): + digest = Hash(SHA256()) + digest.update(self.get_public_bytes()) + self._emit( + header="const unsigned char {}_pub_key_hash[] = {{" + .format(self.shortname()), + trailer="};", + encoded_bytes=digest.finalize(), + indent=" ", + len_format="const unsigned int {}_pub_key_hash_len = {{}};" + .format(self.shortname()), + file=file) + + def emit_raw_public(self, file=sys.stdout): + self._emit_raw(self.get_public_bytes(), file=file) + + def emit_raw_public_hash(self, file=sys.stdout): + digest = Hash(SHA256()) + digest.update(self.get_public_bytes()) + self._emit_raw(digest.finalize(), file=file) + + def emit_rust_public(self, file=sys.stdout): + self._emit( + header="static {}_PUB_KEY: &[u8] = &[" + .format(self.shortname().upper()), + trailer="];", + encoded_bytes=self.get_public_bytes(), + indent=" ", + file=file) + + def emit_public_pem(self, file=sys.stdout): + with FileHandler(file, 'w') as file: + print(str(self.get_public_pem(), 'utf-8'), file=file, end='') + + def emit_private(self, minimal, format, file=sys.stdout): + self._emit( + header="const unsigned char enc_priv_key[] = {", + trailer="};", + encoded_bytes=self.get_private_bytes(minimal, format), + indent=" ", + len_format="const unsigned int enc_priv_key_len = {};", + file=file) diff --git a/bootloader/mcuboot/scripts/imgtool/keys/privatebytes.py b/bootloader/mcuboot/scripts/imgtool/keys/privatebytes.py new file mode 100644 index 0000000..8027ac8 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/privatebytes.py @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 + +from cryptography.hazmat.primitives import serialization + + +class PrivateBytesMixin(): + def _get_private_bytes(self, minimal, format, exclass): + if format is None: + format = self._DEFAULT_FORMAT + if format not in self._VALID_FORMATS: + raise exclass("{} does not support {}".format( + self.shortname(), format)) + return format, self.key.private_bytes( + encoding=serialization.Encoding.DER, + format=self._VALID_FORMATS[format], + encryption_algorithm=serialization.NoEncryption()) diff --git a/bootloader/mcuboot/scripts/imgtool/keys/rsa.py b/bootloader/mcuboot/scripts/imgtool/keys/rsa.py new file mode 100644 index 0000000..d4793c5 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/rsa.py @@ -0,0 +1,173 @@ +""" +RSA Key management +""" + +# SPDX-License-Identifier: Apache-2.0 + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.padding import PSS, MGF1 +from cryptography.hazmat.primitives.hashes import SHA256 + +from .general import KeyClass +from .privatebytes import PrivateBytesMixin + + +# Sizes that bootutil will recognize +RSA_KEY_SIZES = [2048, 3072] + + +class RSAUsageError(Exception): + pass + + +class RSAPublic(KeyClass): + """The public key can only do a few operations""" + def __init__(self, key): + self.key = key + + def key_size(self): + return self.key.key_size + + def shortname(self): + return "rsa" + + def _unsupported(self, name): + raise RSAUsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key embedded into MCUboot is in PKCS1 format. + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.PKCS1) + + def get_public_pem(self): + return self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_private_bytes(self, minimal, format): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + def sig_type(self): + return "PKCS1_PSS_RSA{}_SHA256".format(self.key_size()) + + def sig_tlv(self): + return"RSA{}".format(self.key_size()) + + def sig_len(self): + return self.key_size() / 8 + + def verify(self, signature, payload): + k = self.key + if isinstance(self.key, rsa.RSAPrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=payload, + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) + + +class RSA(RSAPublic, PrivateBytesMixin): + """ + Wrapper around an RSA key, with imgtool support. + """ + + def __init__(self, key): + """The key should be a private key from cryptography""" + self.key = key + + @staticmethod + def generate(key_size=2048): + if key_size not in RSA_KEY_SIZES: + raise RSAUsageError("Key size {} is not supported by MCUboot" + .format(key_size)) + pk = rsa.generate_private_key( + public_exponent=65537, + key_size=key_size, + backend=default_backend()) + return RSA(pk) + + def _get_public(self): + return self.key.public_key() + + def _build_minimal_rsa_privkey(self, der): + ''' + Builds a new DER that only includes N/E/D/P/Q RSA parameters; + standard DER private bytes provided by OpenSSL also includes + CRT params (DP/DQ/QP) which can be removed. + ''' + OFFSET_N = 7 # N is always located at this offset + b = bytearray(der) + off = OFFSET_N + if b[off + 1] != 0x82: + raise RSAUsageError("Error parsing N while minimizing") + len_N = (b[off + 2] << 8) + b[off + 3] + 4 + off += len_N + if b[off + 1] != 0x03: + raise RSAUsageError("Error parsing E while minimizing") + len_E = b[off + 2] + 4 + off += len_E + if b[off + 1] != 0x82: + raise RSAUsageError("Error parsing D while minimizing") + len_D = (b[off + 2] << 8) + b[off + 3] + 4 + off += len_D + if b[off + 1] != 0x81: + raise RSAUsageError("Error parsing P while minimizing") + len_P = b[off + 2] + 3 + off += len_P + if b[off + 1] != 0x81: + raise RSAUsageError("Error parsing Q while minimizing") + len_Q = b[off + 2] + 3 + off += len_Q + # adjust DER size for removed elements + b[2] = (off - 4) >> 8 + b[3] = (off - 4) & 0xff + return b[:off] + + _VALID_FORMATS = { + 'openssl': serialization.PrivateFormat.TraditionalOpenSSL + } + _DEFAULT_FORMAT = 'openssl' + + def get_private_bytes(self, minimal, format): + _, priv = self._get_private_bytes(minimal, format, RSAUsageError) + if minimal: + priv = self._build_minimal_rsa_privkey(priv) + return priv + + def export_private(self, path, passwd=None): + """Write the private key to the given file, protecting it with the + optional password.""" + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + def sign(self, payload): + # The verification code only allows the salt length to be the + # same as the hash length, 32. + return self.key.sign( + data=payload, + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) diff --git a/bootloader/mcuboot/scripts/imgtool/keys/rsa_test.py b/bootloader/mcuboot/scripts/imgtool/keys/rsa_test.py new file mode 100644 index 0000000..7610106 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/rsa_test.py @@ -0,0 +1,136 @@ +""" +Tests for RSA keys +""" + +# SPDX-License-Identifier: Apache-2.0 + +import io +import os +import sys +import tempfile +import unittest + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives.asymmetric.padding import PSS, MGF1 +from cryptography.hazmat.primitives.hashes import SHA256 + +# Setup sys path so 'imgtool' is in it. +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), + '../..'))) + +from imgtool.keys import load, RSA, RSAUsageError +from imgtool.keys.rsa import RSA_KEY_SIZES + + +class KeyGeneration(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.TemporaryDirectory() + + def tname(self, base): + return os.path.join(self.test_dir.name, base) + + def tearDown(self): + self.test_dir.cleanup() + + def test_keygen(self): + # Try generating a RSA key with non-supported size + with self.assertRaises(RSAUsageError): + RSA.generate(key_size=1024) + + for key_size in RSA_KEY_SIZES: + name1 = self.tname("keygen.pem") + k = RSA.generate(key_size=key_size) + k.export_private(name1, b'secret') + + # Try loading the key without a password. + self.assertIsNone(load(name1)) + + k2 = load(name1, b'secret') + + pubname = self.tname('keygen-pub.pem') + k2.export_public(pubname) + pk2 = load(pubname) + + # We should be able to export the public key from the loaded + # public key, but not the private key. + pk2.export_public(self.tname('keygen-pub2.pem')) + self.assertRaises(RSAUsageError, pk2.export_private, + self.tname('keygen-priv2.pem')) + + def test_emit(self): + """Basic sanity check on the code emitters.""" + for key_size in RSA_KEY_SIZES: + k = RSA.generate(key_size=key_size) + + pubpem = io.StringIO() + k.emit_public_pem(pubpem) + self.assertIn("BEGIN PUBLIC KEY", pubpem.getvalue()) + self.assertIn("END PUBLIC KEY", pubpem.getvalue()) + + ccode = io.StringIO() + k.emit_c_public(ccode) + self.assertIn("rsa_pub_key", ccode.getvalue()) + self.assertIn("rsa_pub_key_len", ccode.getvalue()) + + hashccode = io.StringIO() + k.emit_c_public_hash(hashccode) + self.assertIn("rsa_pub_key_hash", hashccode.getvalue()) + self.assertIn("rsa_pub_key_hash_len", hashccode.getvalue()) + + rustcode = io.StringIO() + k.emit_rust_public(rustcode) + self.assertIn("RSA_PUB_KEY", rustcode.getvalue()) + + # raw data - bytes + pubraw = io.BytesIO() + k.emit_raw_public(pubraw) + self.assertTrue(len(pubraw.getvalue()) > 0) + + hashraw = io.BytesIO() + k.emit_raw_public_hash(hashraw) + self.assertTrue(len(hashraw.getvalue()) > 0) + + def test_emit_pub(self): + """Basic sanity check on the code emitters, from public key.""" + pubname = self.tname("public.pem") + for key_size in RSA_KEY_SIZES: + k = RSA.generate(key_size=key_size) + k.export_public(pubname) + + k2 = load(pubname) + + ccode = io.StringIO() + k2.emit_c_public(ccode) + self.assertIn("rsa_pub_key", ccode.getvalue()) + self.assertIn("rsa_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k2.emit_rust_public(rustcode) + self.assertIn("RSA_PUB_KEY", rustcode.getvalue()) + + def test_sig(self): + for key_size in RSA_KEY_SIZES: + k = RSA.generate(key_size=key_size) + buf = b'This is the message' + sig = k.sign(buf) + + # The code doesn't have any verification, so verify this + # manually. + k.key.public_key().verify( + signature=sig, + data=buf, + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) + + # Modify the message to make sure the signature fails. + self.assertRaises(InvalidSignature, + k.key.public_key().verify, + signature=sig, + data=b'This is thE message', + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) + + +if __name__ == '__main__': + unittest.main() diff --git a/bootloader/mcuboot/scripts/imgtool/keys/x25519.py b/bootloader/mcuboot/scripts/imgtool/keys/x25519.py new file mode 100644 index 0000000..a99cf18 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/keys/x25519.py @@ -0,0 +1,119 @@ +""" +X25519 key management +""" + +# SPDX-License-Identifier: Apache-2.0 + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import x25519 + +from .general import KeyClass +from .privatebytes import PrivateBytesMixin + + +class X25519UsageError(Exception): + pass + + +class X25519Public(KeyClass): + def __init__(self, key): + self.key = key + + def shortname(self): + return "x25519" + + def _unsupported(self, name): + raise X25519UsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key is embedded into MBUboot in "SubjectPublicKeyInfo" format + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_public_pem(self): + return self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_private_bytes(self, minimal, format): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + def sig_type(self): + return "X25519" + + def sig_tlv(self): + return "X25519" + + def sig_len(self): + return 32 + + +class X25519(X25519Public, PrivateBytesMixin): + """ + Wrapper around an X25519 private key. + """ + + def __init__(self, key): + """key should be an instance of EllipticCurvePrivateKey""" + self.key = key + + @staticmethod + def generate(): + pk = x25519.X25519PrivateKey.generate() + return X25519(pk) + + def _get_public(self): + return self.key.public_key() + + _VALID_FORMATS = { + 'pkcs8': serialization.PrivateFormat.PKCS8 + } + _DEFAULT_FORMAT = 'pkcs8' + + def get_private_bytes(self, minimal, format): + _, priv = self._get_private_bytes(minimal, format, + X25519UsageError) + return priv + + def export_private(self, path, passwd=None): + """ + Write the private key to the given file, protecting it with the + optional password. + """ + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + def sign_digest(self, digest): + """Return the actual signature""" + return self.key.sign(data=digest) + + def verify_digest(self, signature, digest): + """Verify that signature is valid for given digest""" + k = self.key + if isinstance(self.key, x25519.X25519PrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=digest) diff --git a/bootloader/mcuboot/scripts/imgtool/main.py b/bootloader/mcuboot/scripts/imgtool/main.py new file mode 100644 index 0000000..03d46c9 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/main.py @@ -0,0 +1,618 @@ +#! /usr/bin/env python3 +# +# Copyright 2017-2020 Linaro Limited +# Copyright 2019-2023 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import click +import getpass +import imgtool.keys as keys +import sys +import struct +import os +import lzma +import hashlib +import base64 +from imgtool import image, imgtool_version +from imgtool.version import decode_version +from imgtool.dumpinfo import dump_imginfo +from .keys import ( + RSAUsageError, ECDSAUsageError, Ed25519UsageError, X25519UsageError) + +comp_default_dictsize=131072 +comp_default_pb=2 +comp_default_lc=3 +comp_default_lp=1 +comp_default_preset=9 + + +MIN_PYTHON_VERSION = (3, 6) +if sys.version_info < MIN_PYTHON_VERSION: + sys.exit("Python %s.%s or newer is required by imgtool." + % MIN_PYTHON_VERSION) + + +def gen_rsa2048(keyfile, passwd): + keys.RSA.generate().export_private(path=keyfile, passwd=passwd) + + +def gen_rsa3072(keyfile, passwd): + keys.RSA.generate(key_size=3072).export_private(path=keyfile, + passwd=passwd) + + +def gen_ecdsa_p256(keyfile, passwd): + keys.ECDSA256P1.generate().export_private(keyfile, passwd=passwd) + + +def gen_ecdsa_p384(keyfile, passwd): + keys.ECDSA384P1.generate().export_private(keyfile, passwd=passwd) + + +def gen_ed25519(keyfile, passwd): + keys.Ed25519.generate().export_private(path=keyfile, passwd=passwd) + + +def gen_x25519(keyfile, passwd): + keys.X25519.generate().export_private(path=keyfile, passwd=passwd) + + +valid_langs = ['c', 'rust'] +valid_hash_encodings = ['lang-c', 'raw'] +valid_encodings = ['lang-c', 'lang-rust', 'pem', 'raw'] +keygens = { + 'rsa-2048': gen_rsa2048, + 'rsa-3072': gen_rsa3072, + 'ecdsa-p256': gen_ecdsa_p256, + 'ecdsa-p384': gen_ecdsa_p384, + 'ed25519': gen_ed25519, + 'x25519': gen_x25519, +} +valid_formats = ['openssl', 'pkcs8'] +valid_sha = [ 'auto', '256', '384', '512' ] + + +def load_signature(sigfile): + with open(sigfile, 'rb') as f: + signature = base64.b64decode(f.read()) + return signature + + +def save_signature(sigfile, sig): + with open(sigfile, 'wb') as f: + signature = base64.b64encode(sig) + f.write(signature) + + +def load_key(keyfile): + # TODO: better handling of invalid pass-phrase + key = keys.load(keyfile) + if key is not None: + return key + passwd = getpass.getpass("Enter key passphrase: ").encode('utf-8') + return keys.load(keyfile, passwd) + + +def get_password(): + while True: + passwd = getpass.getpass("Enter key passphrase: ") + passwd2 = getpass.getpass("Reenter passphrase: ") + if passwd == passwd2: + break + print("Passwords do not match, try again") + + # Password must be bytes, always use UTF-8 for consistent + # encoding. + return passwd.encode('utf-8') + + +@click.option('-p', '--password', is_flag=True, + help='Prompt for password to protect key') +@click.option('-t', '--type', metavar='type', required=True, + type=click.Choice(keygens.keys()), prompt=True, + help='{}'.format('One of: {}'.format(', '.join(keygens.keys())))) +@click.option('-k', '--key', metavar='filename', required=True) +@click.command(help='Generate pub/private keypair') +def keygen(type, key, password): + password = get_password() if password else None + keygens[type](key, password) + + +@click.option('-l', '--lang', metavar='lang', + type=click.Choice(valid_langs), + help='This option is deprecated. Please use the ' + '`--encoding` option. ' + 'Valid langs: {}'.format(', '.join(valid_langs))) +@click.option('-e', '--encoding', metavar='encoding', + type=click.Choice(valid_encodings), + help='Valid encodings: {}'.format(', '.join(valid_encodings))) +@click.option('-k', '--key', metavar='filename', required=True) +@click.option('-o', '--output', metavar='output', required=False, + help='Specify the output file\'s name. \ + The stdout is used if it is not provided.') +@click.command(help='Dump public key from keypair') +def getpub(key, encoding, lang, output): + if encoding and lang: + raise click.UsageError('Please use only one of `--encoding/-e` ' + 'or `--lang/-l`') + elif not encoding and not lang: + # Preserve old behavior defaulting to `c`. If `lang` is removed, + # `default=valid_encodings[0]` should be added to `-e` param. + lang = valid_langs[0] + key = load_key(key) + + if not output: + output = sys.stdout + if key is None: + print("Invalid passphrase") + elif lang == 'c' or encoding == 'lang-c': + key.emit_c_public(file=output) + elif lang == 'rust' or encoding == 'lang-rust': + key.emit_rust_public(file=output) + elif encoding == 'pem': + key.emit_public_pem(file=output) + elif encoding == 'raw': + key.emit_raw_public(file=output) + else: + raise click.UsageError() + + +@click.option('-e', '--encoding', metavar='encoding', + type=click.Choice(valid_hash_encodings), + help='Valid encodings: {}. ' + 'Default value is {}.' + .format(', '.join(valid_hash_encodings), + valid_hash_encodings[0])) +@click.option('-k', '--key', metavar='filename', required=True) +@click.option('-o', '--output', metavar='output', required=False, + help='Specify the output file\'s name. \ + The stdout is used if it is not provided.') +@click.command(help='Dump the SHA256 hash of the public key') +def getpubhash(key, output, encoding): + if not encoding: + encoding = valid_hash_encodings[0] + key = load_key(key) + + if not output: + output = sys.stdout + if key is None: + print("Invalid passphrase") + elif encoding == 'lang-c': + key.emit_c_public_hash(file=output) + elif encoding == 'raw': + key.emit_raw_public_hash(file=output) + else: + raise click.UsageError() + + +@click.option('--minimal', default=False, is_flag=True, + help='Reduce the size of the dumped private key to include only ' + 'the minimum amount of data required to decrypt. This ' + 'might require changes to the build config. Check the docs!' + ) +@click.option('-k', '--key', metavar='filename', required=True) +@click.option('-f', '--format', + type=click.Choice(valid_formats), + help='Valid formats: {}'.format(', '.join(valid_formats)) + ) +@click.command(help='Dump private key from keypair') +def getpriv(key, minimal, format): + key = load_key(key) + if key is None: + print("Invalid passphrase") + try: + key.emit_private(minimal, format) + except (RSAUsageError, ECDSAUsageError, Ed25519UsageError, + X25519UsageError) as e: + raise click.UsageError(e) + + +@click.argument('imgfile') +@click.option('-k', '--key', metavar='filename') +@click.command(help="Check that signed image can be verified by given key") +def verify(key, imgfile): + key = load_key(key) if key else None + ret, version, digest, signature = image.Image.verify(imgfile, key) + if ret == image.VerifyResult.OK: + print("Image was correctly validated") + print("Image version: {}.{}.{}+{}".format(*version)) + if digest: + print("Image digest: {}".format(digest.hex())) + if signature and digest is None: + print("Image signature over image: {}".format(signature.hex())) + return + elif ret == image.VerifyResult.INVALID_MAGIC: + print("Invalid image magic; is this an MCUboot image?") + elif ret == image.VerifyResult.INVALID_TLV_INFO_MAGIC: + print("Invalid TLV info magic; is this an MCUboot image?") + elif ret == image.VerifyResult.INVALID_HASH: + print("Image has an invalid hash") + elif ret == image.VerifyResult.INVALID_SIGNATURE: + print("No signature found for the given key") + elif ret == image.VerifyResult.KEY_MISMATCH: + print("Key type does not match TLV record") + else: + print("Unknown return code: {}".format(ret)) + sys.exit(1) + + +@click.argument('imgfile') +@click.option('-o', '--outfile', metavar='filename', required=False, + help='Save image information to outfile in YAML format') +@click.option('-s', '--silent', default=False, is_flag=True, + help='Do not print image information to output') +@click.command(help='Print header, TLV area and trailer information ' + 'of a signed image') +def dumpinfo(imgfile, outfile, silent): + dump_imginfo(imgfile, outfile, silent) + print("dumpinfo has run successfully") + + +def validate_version(ctx, param, value): + try: + decode_version(value) + return value + except ValueError as e: + raise click.BadParameter("{}".format(e)) + + +def validate_security_counter(ctx, param, value): + if value is not None: + if value.lower() == 'auto': + return 'auto' + else: + try: + return int(value, 0) + except ValueError: + raise click.BadParameter( + "{} is not a valid integer. Please use code literals " + "prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary." + .format(value)) + + +def validate_header_size(ctx, param, value): + min_hdr_size = image.IMAGE_HEADER_SIZE + if value < min_hdr_size: + raise click.BadParameter( + "Minimum value for -H/--header-size is {}".format(min_hdr_size)) + return value + + +def get_dependencies(ctx, param, value): + if value is not None: + versions = [] + images = re.findall(r"\((\d+)", value) + if len(images) == 0: + raise click.BadParameter( + "Image dependency format is invalid: {}".format(value)) + raw_versions = re.findall(r",\s*([0-9.+]+)\)", value) + if len(images) != len(raw_versions): + raise click.BadParameter( + '''There's a mismatch between the number of dependency images + and versions in: {}'''.format(value)) + for raw_version in raw_versions: + try: + versions.append(decode_version(raw_version)) + except ValueError as e: + raise click.BadParameter("{}".format(e)) + dependencies = dict() + dependencies[image.DEP_IMAGES_KEY] = images + dependencies[image.DEP_VERSIONS_KEY] = versions + return dependencies + +def create_lzma2_header(dictsize, pb, lc, lp): + header = bytearray() + for i in range(0, 40): + if dictsize <= ((2 | ((i) & 1)) << int((i) / 2 + 11)): + header.append(i) + break + header.append( ( pb * 5 + lp) * 9 + lc) + return header + +class BasedIntParamType(click.ParamType): + name = 'integer' + + def convert(self, value, param, ctx): + try: + return int(value, 0) + except ValueError: + self.fail('%s is not a valid integer. Please use code literals ' + 'prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary.' + % value, param, ctx) + + +@click.argument('outfile') +@click.argument('infile') +@click.option('--non-bootable', default=False, is_flag=True, + help='Mark the image as non-bootable.') +@click.option('--custom-tlv', required=False, nargs=2, default=[], + multiple=True, metavar='[tag] [value]', + help='Custom TLV that will be placed into protected area. ' + 'Add "0x" prefix if the value should be interpreted as an ' + 'integer, otherwise it will be interpreted as a string. ' + 'Specify the option multiple times to add multiple TLVs.') +@click.option('-R', '--erased-val', type=click.Choice(['0', '0xff']), + required=False, + help='The value that is read back from erased flash.') +@click.option('-x', '--hex-addr', type=BasedIntParamType(), required=False, + help='Adjust address in hex output file.') +@click.option('-L', '--load-addr', type=BasedIntParamType(), required=False, + help='Load address for image when it should run from RAM.') +@click.option('-F', '--rom-fixed', type=BasedIntParamType(), required=False, + help='Set flash address the image is built for.') +@click.option('--save-enctlv', default=False, is_flag=True, + help='When upgrading, save encrypted key TLVs instead of plain ' + 'keys. Enable when BOOT_SWAP_SAVE_ENCTLV config option ' + 'was set.') +@click.option('-E', '--encrypt', metavar='filename', + help='Encrypt image using the provided public key. ' + '(Not supported in direct-xip or ram-load mode.)') +@click.option('--encrypt-keylen', default='128', + type=click.Choice(['128', '256']), + help='When encrypting the image using AES, select a 128 bit or ' + '256 bit key len.') +@click.option('--compression', default='disabled', + type=click.Choice(['disabled', 'lzma2', 'lzma2armthumb']), + help='Enable image compression using specified type. ' + 'Will fall back without image compression automatically ' + 'if the compression increases the image size.') +@click.option('-c', '--clear', required=False, is_flag=True, default=False, + help='Output a non-encrypted image with encryption capabilities,' + 'so it can be installed in the primary slot, and encrypted ' + 'when swapped to the secondary.') +@click.option('-e', '--endian', type=click.Choice(['little', 'big']), + default='little', help="Select little or big endian") +@click.option('--overwrite-only', default=False, is_flag=True, + help='Use overwrite-only instead of swap upgrades') +@click.option('--boot-record', metavar='sw_type', help='Create CBOR encoded ' + 'boot record TLV. The sw_type represents the role of the ' + 'software component (e.g. CoFM for coprocessor firmware). ' + '[max. 12 characters]') +@click.option('-M', '--max-sectors', type=int, + help='When padding allow for this amount of sectors (defaults ' + 'to 128)') +@click.option('--confirm', default=False, is_flag=True, + help='When padding the image, mark it as confirmed (implies ' + '--pad)') +@click.option('--pad', default=False, is_flag=True, + help='Pad image to --slot-size bytes, adding trailer magic') +@click.option('-S', '--slot-size', type=BasedIntParamType(), required=True, + help='Size of the slot. If the slots have different sizes, use ' + 'the size of the secondary slot.') +@click.option('--pad-header', default=False, is_flag=True, + help='Add --header-size zeroed bytes at the beginning of the ' + 'image') +@click.option('-H', '--header-size', callback=validate_header_size, + type=BasedIntParamType(), required=True) +@click.option('--pad-sig', default=False, is_flag=True, + help='Add 0-2 bytes of padding to ECDSA signature ' + '(for mcuboot <1.5)') +@click.option('-d', '--dependencies', callback=get_dependencies, + required=False, help='''Add dependence on another image, format: + "(,), ... "''') +@click.option('-s', '--security-counter', callback=validate_security_counter, + help='Specify the value of security counter. Use the `auto` ' + 'keyword to automatically generate it from the image version.') +@click.option('-v', '--version', callback=validate_version, required=True) +@click.option('--align', type=click.Choice(['1', '2', '4', '8', '16', '32']), + default='1', + required=False, + help='Alignment used by swap update modes.') +@click.option('--max-align', type=click.Choice(['8', '16', '32']), + required=False, + help='Maximum flash alignment. Set if flash alignment of the ' + 'primary and secondary slot differ and any of them is larger ' + 'than 8.') +@click.option('--public-key-format', type=click.Choice(['hash', 'full']), + default='hash', help='In what format to add the public key to ' + 'the image manifest: full key or hash of the key.') +@click.option('-k', '--key', metavar='filename') +@click.option('--fix-sig', metavar='filename', + help='fixed signature for the image. It will be used instead of ' + 'the signature calculated using the public key') +@click.option('--fix-sig-pubkey', metavar='filename', + help='public key relevant to fixed signature') +@click.option('--pure', 'is_pure', is_flag=True, default=False, show_default=True, + help='Expected Pure variant of signature; the Pure variant is ' + 'expected to be signature done over an image rather than hash of ' + 'that image.') +@click.option('--sig-out', metavar='filename', + help='Path to the file to which signature will be written. ' + 'The image signature will be encoded as base64 formatted string') +@click.option('--sha', 'user_sha', type=click.Choice(valid_sha), default='auto', + help='selected sha algorithm to use; defaults to "auto" which is 256 if ' + 'no cryptographic signature is used, or default for signature type') +@click.option('--vector-to-sign', type=click.Choice(['payload', 'digest']), + help='send to OUTFILE the payload or payload''s digest instead ' + 'of complied image. These data can be used for external image ' + 'signing') +@click.command(help='''Create a signed or unsigned image\n + INFILE and OUTFILE are parsed as Intel HEX if the params have + .hex extension, otherwise binary format is used''') +def sign(key, public_key_format, align, version, pad_sig, header_size, + pad_header, slot_size, pad, confirm, max_sectors, overwrite_only, + endian, encrypt_keylen, encrypt, compression, infile, outfile, + dependencies, load_addr, hex_addr, erased_val, save_enctlv, + security_counter, boot_record, custom_tlv, rom_fixed, max_align, + clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, is_pure, + vector_to_sign, non_bootable): + + if confirm: + # Confirmed but non-padded images don't make much sense, because + # otherwise there's no trailer area for writing the confirmed status. + pad = True + img = image.Image(version=decode_version(version), header_size=header_size, + pad_header=pad_header, pad=pad, confirm=confirm, + align=int(align), slot_size=slot_size, + max_sectors=max_sectors, overwrite_only=overwrite_only, + endian=endian, load_addr=load_addr, rom_fixed=rom_fixed, + erased_val=erased_val, save_enctlv=save_enctlv, + security_counter=security_counter, max_align=max_align, + non_bootable=non_bootable) + compression_tlvs = {} + img.load(infile) + key = load_key(key) if key else None + enckey = load_key(encrypt) if encrypt else None + if enckey and key: + if ((isinstance(key, keys.ECDSA256P1) and + not isinstance(enckey, keys.ECDSA256P1Public)) + or (isinstance(key, keys.ECDSA384P1) and + not isinstance(enckey, keys.ECDSA384P1Public)) + or (isinstance(key, keys.RSA) and + not isinstance(enckey, keys.RSAPublic))): + # FIXME + raise click.UsageError("Signing and encryption must use the same " + "type of key") + + if pad_sig and hasattr(key, 'pad_sig'): + key.pad_sig = True + + # Get list of custom protected TLVs from the command-line + custom_tlvs = {} + for tlv in custom_tlv: + tag = int(tlv[0], 0) + if tag in custom_tlvs: + raise click.UsageError('Custom TLV %s already exists.' % hex(tag)) + if tag in image.TLV_VALUES.values(): + raise click.UsageError( + 'Custom TLV %s conflicts with predefined TLV.' % hex(tag)) + + value = tlv[1] + if value.startswith('0x'): + if len(value[2:]) % 2: + raise click.UsageError('Custom TLV length is odd.') + custom_tlvs[tag] = bytes.fromhex(value[2:]) + else: + custom_tlvs[tag] = value.encode('utf-8') + + # Allow signature calculated externally. + raw_signature = load_signature(fix_sig) if fix_sig else None + + baked_signature = None + pub_key = None + + if raw_signature is not None: + if fix_sig_pubkey is None: + raise click.UsageError( + 'public key of the fixed signature is not specified') + + pub_key = load_key(fix_sig_pubkey) + + baked_signature = { + 'value': raw_signature + } + + if is_pure and user_sha != 'auto': + raise click.UsageError( + 'Pure signatures, currently, enforces preferred hash algorithm, ' + 'and forbids sha selection by user.') + + img.create(key, public_key_format, enckey, dependencies, boot_record, + custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear, + baked_signature, pub_key, vector_to_sign, user_sha=user_sha, + is_pure=is_pure) + + if compression in ["lzma2", "lzma2armthumb"]: + compressed_img = image.Image(version=decode_version(version), + header_size=header_size, pad_header=pad_header, + pad=pad, confirm=confirm, align=int(align), + slot_size=slot_size, max_sectors=max_sectors, + overwrite_only=overwrite_only, endian=endian, + load_addr=load_addr, rom_fixed=rom_fixed, + erased_val=erased_val, save_enctlv=save_enctlv, + security_counter=security_counter, max_align=max_align) + compression_filters = [ + {"id": lzma.FILTER_LZMA2, "preset": comp_default_preset, + "dict_size": comp_default_dictsize, "lp": comp_default_lp, + "lc": comp_default_lc} + ] + if compression == "lzma2armthumb": + compression_filters.insert(0, {"id":lzma.FILTER_ARMTHUMB}) + compressed_data = lzma.compress(img.get_infile_data(),filters=compression_filters, + format=lzma.FORMAT_RAW) + uncompressed_size = len(img.get_infile_data()) + compressed_size = len(compressed_data) + print(f"compressed image size: {compressed_size} bytes") + print(f"original image size: {uncompressed_size} bytes") + compression_tlvs["DECOMP_SIZE"] = struct.pack( + img.get_struct_endian() + 'L', img.image_size) + compression_tlvs["DECOMP_SHA"] = img.image_hash + compression_tlvs_size = len(compression_tlvs["DECOMP_SIZE"]) + compression_tlvs_size += len(compression_tlvs["DECOMP_SHA"]) + if img.get_signature(): + compression_tlvs["DECOMP_SIGNATURE"] = img.get_signature() + compression_tlvs_size += len(compression_tlvs["DECOMP_SIGNATURE"]) + if (compressed_size + compression_tlvs_size) < uncompressed_size: + compression_header = create_lzma2_header( + dictsize = comp_default_dictsize, pb = comp_default_pb, + lc = comp_default_lc, lp = comp_default_lp) + compressed_img.load_compressed(compressed_data, compression_header) + compressed_img.base_addr = img.base_addr + compressed_img.create(key, public_key_format, enckey, + dependencies, boot_record, custom_tlvs, compression_tlvs, + compression, int(encrypt_keylen), clear, baked_signature, + pub_key, vector_to_sign) + img = compressed_img + img.save(outfile, hex_addr) + if sig_out is not None: + new_signature = img.get_signature() + save_signature(sig_out, new_signature) + + +class AliasesGroup(click.Group): + + _aliases = { + "create": "sign", + } + + def list_commands(self, ctx): + cmds = [k for k in self.commands] + aliases = [k for k in self._aliases] + return sorted(cmds + aliases) + + def get_command(self, ctx, cmd_name): + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + if cmd_name in self._aliases: + return click.Group.get_command(self, ctx, self._aliases[cmd_name]) + return None + + +@click.command(help='Print imgtool version information') +def version(): + print(imgtool_version) + + +@click.command(cls=AliasesGroup, + context_settings=dict(help_option_names=['-h', '--help'])) +def imgtool(): + pass + + +imgtool.add_command(keygen) +imgtool.add_command(getpub) +imgtool.add_command(getpubhash) +imgtool.add_command(getpriv) +imgtool.add_command(verify) +imgtool.add_command(sign) +imgtool.add_command(version) +imgtool.add_command(dumpinfo) + + +if __name__ == '__main__': + imgtool() diff --git a/bootloader/mcuboot/scripts/imgtool/version.py b/bootloader/mcuboot/scripts/imgtool/version.py new file mode 100644 index 0000000..b803834 --- /dev/null +++ b/bootloader/mcuboot/scripts/imgtool/version.py @@ -0,0 +1,56 @@ +# Copyright 2017 Linaro Limited +# Copyright 2024 Arm Limited +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Semi Semantic Versioning + +Implements a subset of semantic versioning that is supportable by the image +header. +""" +import re +import sys +from collections import namedtuple + +SemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision', + 'build']) + +version_re = re.compile( + r"""^([1-9]\d*|0)(\.([1-9]\d*|0)(\.([1-9]\d*|0)(\+([1-9]\d*|0))?)?)?$""") + + +def decode_version(text): + """Decode the version string, which should be of the form maj.min.rev+build + """ + m = version_re.match(text) + if m: + result = SemiSemVersion( + int(m.group(1)) if m.group(1) else 0, + int(m.group(3)) if m.group(3) else 0, + int(m.group(5)) if m.group(5) else 0, + int(m.group(7)) if m.group(7) else 0) + return result + else: + msg = "Invalid version number, should be maj.min.rev+build with later " + msg += "parts optional" + raise ValueError(msg) + + +if __name__ == '__main__': + if len(sys.argv) > 1: + print(decode_version(sys.argv[1])) + else: + print("Requires an argument, e.g. '1.0.0'") diff --git a/bootloader/mcuboot/scripts/jgdb.sh b/bootloader/mcuboot/scripts/jgdb.sh new file mode 100644 index 0000000..dc59020 --- /dev/null +++ b/bootloader/mcuboot/scripts/jgdb.sh @@ -0,0 +1,8 @@ +#! /bin/bash +# +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname $0)/../target.sh + +# Start the jlink gdb server +JLinkGDBServer -if swd -device $SOC -speed auto diff --git a/bootloader/mcuboot/scripts/jl.sh b/bootloader/mcuboot/scripts/jl.sh new file mode 100644 index 0000000..abc6dab --- /dev/null +++ b/bootloader/mcuboot/scripts/jl.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# +# SPDX-License-Identifier: Apache-2.0 + +source $(dirname $0)/../target.sh + +JLinkExe -speed auto -si SWD -device $SOC diff --git a/bootloader/mcuboot/scripts/mcubin.bt b/bootloader/mcuboot/scripts/mcubin.bt new file mode 100644 index 0000000..e2ec361 --- /dev/null +++ b/bootloader/mcuboot/scripts/mcubin.bt @@ -0,0 +1,135 @@ +// Copyright (C) 2019, Linaro Ltd +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is a Binary Template file for the 010 Editor +// (http://www.sweetscape.com/010editor/) to allow it to show the +// structure of an MCUboot image. + +LittleEndian(); + +struct ENTRY { + uint32 id; + uint32 offset; + uint32 size; + uint32 pad; +}; + +// The simulator writes the partition table at the beginning of the +// image, so that we can tell where the partitions are. If you are +// trying to view an image captured from a device, you can either +// construct a synthetic partition table in the file, or change code +// described below to hardcode one. +struct PTABLE { + uchar pheader[8]; + if (ptable.pheader != "mcuboot\0") { + // NOTE: Put code here to hard code a partition table, and + // continue. + Warning("Invalid magic on ptable header"); + return -1; + } else { + uint32 count; + struct ENTRY entries[count]; + } +}; + +struct PTABLE ptable; + +struct IMAGE_VERSION { + uchar major; + uchar minor; + uint16 revision; + uint32 build_num; +}; + +struct IHDR { + uint32 magic ; + uint32 load_addr ; + uint16 hdr_size ; + uint16 protect_size ; + uint32 img_size ; + uint32 flags; + struct IMAGE_VERSION ver; + uint32 _pad1; +}; + +struct TLV_HDR { + uint16 magic; + uint16 tlv_tot; +}; + +struct TLV { + uchar type ; + uchar pad; + uint16 len; + + switch (type) { + case 0x01: // keyhash + uchar keyhash[len]; + break; + case 0x40: // dependency + if (len != 12) { + Warning("Invalid dependency size"); + return -1; + } + uchar image_id; + uchar pad1; + uint16 pad2; + struct IMAGE_VERSION version; + break; + default: + // Other, just consume the data. + uchar data[len]; + } +}; + +local int i; +local int epos; + +for (i = 0; i < ptable.count; i++) { + FSeek(ptable.entries[i].offset); + switch (ptable.entries[i].id) { + case 1: + case 2: + case 4: + case 5: + struct IMAGE { + struct IHDR ihdr; + + if (ihdr.magic == 0x96f3b83d) { + uchar payload[ihdr.img_size]; + + epos = FTell(); + struct TLV_HDR tlv_hdr; + + if (tlv_hdr.magic == 0x6907) { + epos += tlv_hdr.tlv_tot; + while (FTell() < epos) { + struct TLV tlv; + } + } + } + // uchar block[ptable.entries[i].size]; + } image; + break; + case 3: + struct SCRATCH { + uchar data[ptable.entries[i].size]; + } scratch; + break; + default: + break; + } +} diff --git a/bootloader/mcuboot/scripts/requirements.txt b/bootloader/mcuboot/scripts/requirements.txt new file mode 100644 index 0000000..33b11bb --- /dev/null +++ b/bootloader/mcuboot/scripts/requirements.txt @@ -0,0 +1,7 @@ +cryptography>=40.0.0 +intelhex +click +cbor2 +setuptools +pyyaml +pytest diff --git a/bootloader/mcuboot/scripts/setup.py b/bootloader/mcuboot/scripts/setup.py new file mode 100644 index 0000000..c97de95 --- /dev/null +++ b/bootloader/mcuboot/scripts/setup.py @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: Apache-2.0 + +import setuptools + +from imgtool import imgtool_version + +setuptools.setup( + name="imgtool", + version=imgtool_version, + author="The MCUboot committers", + author_email="mcuboot@groups.io", + description=("MCUboot's image signing and key management"), + license="Apache Software License", + url="http://github.com/mcu-tools/mcuboot", + packages=setuptools.find_packages(), + python_requires='>=3.6', + install_requires=[ + 'cryptography>=40.0.0', + 'intelhex>=2.2.1', + 'click', + 'cbor2', + 'pyyaml', + ], + entry_points={ + "console_scripts": ["imgtool=imgtool.main:imgtool"] + }, + classifiers=[ + "Programming Language :: Python :: 3", + "Development Status :: 4 - Beta", + "Topic :: Software Development :: Build Tools", + "License :: OSI Approved :: Apache Software License", + ], +) diff --git a/bootloader/mcuboot/scripts/tests/conftest.py b/bootloader/mcuboot/scripts/tests/conftest.py new file mode 100644 index 0000000..797bf25 --- /dev/null +++ b/bootloader/mcuboot/scripts/tests/conftest.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +# List of tests expected to fail for some reason +XFAILED_TESTS = { + "tests/test_keys.py::test_getpriv[openssl-ed25519]", + "tests/test_keys.py::test_getpriv[openssl-x25519]", + "tests/test_keys.py::test_getpriv[pkcs8-rsa-2048]", + "tests/test_keys.py::test_getpriv[pkcs8-rsa-3072]", + "tests/test_keys.py::test_getpriv[pkcs8-ed25519]", + "tests/test_keys.py::test_getpub[pem-ed25519]", + "tests/test_keys.py::test_sign_verify[x25519]", +} + + +def pytest_runtest_setup(item): + if item.nodeid in XFAILED_TESTS: + pytest.xfail() diff --git a/bootloader/mcuboot/scripts/tests/test_commands.py b/bootloader/mcuboot/scripts/tests/test_commands.py new file mode 100644 index 0000000..458e4e2 --- /dev/null +++ b/bootloader/mcuboot/scripts/tests/test_commands.py @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from click.testing import CliRunner +from imgtool.main import imgtool +from imgtool import imgtool_version + +# all available imgtool commands +COMMANDS = [ + "create", + "dumpinfo", + "getpriv", + "getpub", + "getpubhash", + "keygen", + "sign", + "verify", + "version", +] + + +def test_new_command(): + """Check that no new commands had been added, + so that tests would be updated in such case""" + for cmd in imgtool.commands: + assert cmd in COMMANDS + + +def test_help(): + """Simple test for the imgtool's help option, + mostly just to see that it can be started""" + runner = CliRunner() + + result_short = runner.invoke(imgtool, ["-h"]) + assert result_short.exit_code == 0 + + result_long = runner.invoke(imgtool, ["--help"]) + assert result_long.exit_code == 0 + assert result_short.output == result_long.output + + # by default help should be also produced + result_empty = runner.invoke(imgtool) + assert result_empty.exit_code == 0 + assert result_empty.output == result_short.output + + +def test_version(): + """Check that some version info is produced""" + runner = CliRunner() + + result = runner.invoke(imgtool, ["version"]) + assert result.exit_code == 0 + assert result.output == imgtool_version + "\n" + + result_help = runner.invoke(imgtool, ["version", "-h"]) + assert result_help.exit_code == 0 + assert result_help.output != result.output + + +def test_unknown(): + """Check that unknown command will be handled""" + runner = CliRunner() + + result = runner.invoke(imgtool, ["unknown"]) + assert result.exit_code != 0 + + +@pytest.mark.parametrize("command", COMMANDS) +def test_cmd_help(command): + """Check that all commands have some help""" + runner = CliRunner() + + result_short = runner.invoke(imgtool, [command, "-h"]) + assert result_short.exit_code == 0 + + result_long = runner.invoke(imgtool, [command, "--help"]) + assert result_long.exit_code == 0 + + assert result_short.output == result_long.output + + +@pytest.mark.parametrize("command1", COMMANDS) +@pytest.mark.parametrize("command2", COMMANDS) +def test_cmd_dif_help(command1, command2): + """Check that all commands have some different help""" + runner = CliRunner() + + result_general = runner.invoke(imgtool, "--help") + assert result_general.exit_code == 0 + + result_cmd1 = runner.invoke(imgtool, [command1, "--help"]) + assert result_cmd1.exit_code == 0 + assert result_cmd1.output != result_general.output + + if command1 != command2: + result_cmd2 = runner.invoke(imgtool, [command2, "--help"]) + assert result_cmd2.exit_code == 0 + + assert result_cmd1.output != result_cmd2.output diff --git a/bootloader/mcuboot/scripts/tests/test_keys.py b/bootloader/mcuboot/scripts/tests/test_keys.py new file mode 100644 index 0000000..a812ba2 --- /dev/null +++ b/bootloader/mcuboot/scripts/tests/test_keys.py @@ -0,0 +1,253 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +import subprocess +from click.testing import CliRunner +from imgtool import main as imgtool_main +from imgtool.main import imgtool + +# all supported key types for 'keygen' +KEY_TYPES = [*imgtool_main.keygens] +KEY_ENCODINGS = [*imgtool_main.valid_encodings] +PUB_HASH_ENCODINGS = [*imgtool_main.valid_hash_encodings] +PVT_KEY_FORMATS = [*imgtool_main.valid_formats] + +OPENSSL_KEY_TYPES = { + "rsa-2048": "Private-Key: (2048 bit, 2 primes)", + "rsa-3072": "Private-Key: (3072 bit, 2 primes)", + "ecdsa-p256": "Private-Key: (256 bit)", + "ecdsa-p384": "Private-Key: (384 bit)", + "ed25519": "ED25519 Private-Key:", + "x25519": "X25519 Private-Key:", +} + +GEN_KEY_EXT = ".key" +GEN_ANOTHER_KEY_EXT = ".another.key" +PUB_KEY_EXT = ".pub" +PUB_KEY_HASH_EXT = ".pubhash" + + +def tmp_name(tmp_path, key_type, suffix=""): + return tmp_path / (key_type + suffix) + + +@pytest.fixture(scope="session") +def tmp_path_persistent(tmp_path_factory): + return tmp_path_factory.mktemp("keys") + + +@pytest.mark.parametrize("key_type", KEY_TYPES) +def test_keygen(key_type, tmp_path_persistent): + """Generate keys by imgtool""" + + runner = CliRunner() + + gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + + assert not gen_key.exists() + result = runner.invoke( + imgtool, ["keygen", "--key", str(gen_key), "--type", key_type] + ) + assert result.exit_code == 0 + assert gen_key.exists() + assert gen_key.stat().st_size > 0 + + # another key + gen_key2 = tmp_name(tmp_path_persistent, key_type, GEN_ANOTHER_KEY_EXT) + + assert str(gen_key2) != str(gen_key) + + assert not gen_key2.exists() + result = runner.invoke( + imgtool, ["keygen", "--key", str(gen_key2), "--type", key_type] + ) + assert result.exit_code == 0 + assert gen_key2.exists() + assert gen_key2.stat().st_size > 0 + + # content must be different + assert gen_key.read_bytes() != gen_key2.read_bytes() + + +@pytest.mark.parametrize("key_type", KEY_TYPES) +def test_keygen_type(key_type, tmp_path_persistent): + """Check generated keys""" + assert key_type in OPENSSL_KEY_TYPES + + gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + + result = subprocess.run( + ["openssl", "pkey", "-in", str(gen_key), "-check", "-noout", "-text"], + capture_output=True, + text=True, + ) + assert result.returncode == 0 + assert "Key is valid" in result.stdout + assert OPENSSL_KEY_TYPES[key_type] in result.stdout + + +@pytest.mark.parametrize("key_type", KEY_TYPES) +@pytest.mark.parametrize("format", PVT_KEY_FORMATS) +def test_getpriv(key_type, format, tmp_path_persistent): + """Get private key""" + runner = CliRunner() + + gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + + result = runner.invoke( + imgtool, + [ + "getpriv", + "--key", + str(gen_key), + "--format", + format, + ], + ) + assert result.exit_code == 0 + + +@pytest.mark.parametrize("key_type", KEY_TYPES) +@pytest.mark.parametrize("encoding", KEY_ENCODINGS) +def test_getpub(key_type, encoding, tmp_path_persistent): + """Get public key""" + runner = CliRunner() + + gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT + + "." + encoding) + + assert not pub_key.exists() + result = runner.invoke( + imgtool, + [ + "getpub", + "--key", + str(gen_key), + "--output", + str(pub_key), + "--encoding", + encoding, + ], + ) + assert result.exit_code == 0 + assert pub_key.exists() + assert pub_key.stat().st_size > 0 + + +@pytest.mark.parametrize("key_type", KEY_TYPES) +@pytest.mark.parametrize("encoding", PUB_HASH_ENCODINGS) +def test_getpubhash(key_type, encoding, tmp_path_persistent): + """Get the hash of the public key""" + runner = CliRunner() + + gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + pub_key_hash = tmp_name( + tmp_path_persistent, key_type, PUB_KEY_HASH_EXT + "." + encoding + ) + + assert not pub_key_hash.exists() + result = runner.invoke( + imgtool, + [ + "getpubhash", + "--key", + str(gen_key), + "--output", + str(pub_key_hash), + "--encoding", + encoding, + ], + ) + assert result.exit_code == 0 + assert pub_key_hash.exists() + assert pub_key_hash.stat().st_size > 0 + + +@pytest.mark.parametrize("key_type", KEY_TYPES) +def test_sign_verify(key_type, tmp_path_persistent): + """Test basic sign and verify""" + runner = CliRunner() + + gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT) + wrong_key = tmp_name(tmp_path_persistent, key_type, GEN_ANOTHER_KEY_EXT) + image = tmp_name(tmp_path_persistent, "image", "bin") + image_signed = tmp_name(tmp_path_persistent, "image", "signed") + + with image.open("wb") as f: + f.write(b"\x00" * 1024) + + # not all required arguments are provided + result = runner.invoke( + imgtool, + [ + "sign", + "--key", + str(gen_key), + str(image), + str(image_signed), + ], + ) + assert result.exit_code != 0 + assert not image_signed.exists() + + result = runner.invoke( + imgtool, + [ + "sign", + "--key", + str(gen_key), + "--align", + "16", + "--version", + "1.0.0", + "--header-size", + "0x400", + "--slot-size", + "0x10000", + "--pad-header", + str(image), + str(image_signed), + ], + ) + assert result.exit_code == 0 + assert image_signed.exists() + assert image_signed.stat().st_size > 0 + + # original key can be used to verify a signed image + result = runner.invoke( + imgtool, + [ + "verify", + "--key", + str(gen_key), + str(image_signed), + ], + ) + assert result.exit_code == 0 + + # 'another' key is not valid to verify a signed image + result = runner.invoke( + imgtool, + [ + "verify", + "--key", + str(wrong_key), + str(image_signed), + ], + ) + assert result.exit_code != 0 + image_signed.unlink() diff --git a/bootloader/mcuboot/sim/.gitignore b/bootloader/mcuboot/sim/.gitignore new file mode 100644 index 0000000..9409674 --- /dev/null +++ b/bootloader/mcuboot/sim/.gitignore @@ -0,0 +1,2 @@ +target +.*.swp diff --git a/bootloader/mcuboot/sim/Cargo.toml b/bootloader/mcuboot/sim/Cargo.toml new file mode 100644 index 0000000..7cef823 --- /dev/null +++ b/bootloader/mcuboot/sim/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "bootsim" +version = "0.1.0" +authors = ["David Brown "] +edition = "2021" + +[features] +default = [] + +sig-rsa = ["mcuboot-sys/sig-rsa"] +sig-rsa3072 = ["mcuboot-sys/sig-rsa3072"] +sig-ecdsa = ["mcuboot-sys/sig-ecdsa"] +sig-ecdsa-mbedtls = ["mcuboot-sys/sig-ecdsa-mbedtls"] +sig-ecdsa-psa = ["mcuboot-sys/sig-ecdsa-psa", "mcuboot-sys/psa-crypto-api"] +sig-p384 = ["mcuboot-sys/sig-p384"] +sig-ed25519 = ["mcuboot-sys/sig-ed25519"] +overwrite-only = ["mcuboot-sys/overwrite-only"] +swap-move = ["mcuboot-sys/swap-move"] +validate-primary-slot = ["mcuboot-sys/validate-primary-slot"] +enc-rsa = ["mcuboot-sys/enc-rsa"] +enc-aes256-rsa = ["mcuboot-sys/enc-aes256-rsa"] +enc-kw = ["mcuboot-sys/enc-kw"] +enc-aes256-kw = ["mcuboot-sys/enc-aes256-kw"] +enc-ec256 = ["mcuboot-sys/enc-ec256"] +enc-ec256-mbedtls = ["mcuboot-sys/enc-ec256-mbedtls"] +enc-aes256-ec256 = ["mcuboot-sys/enc-aes256-ec256"] +enc-x25519 = ["mcuboot-sys/enc-x25519"] +enc-aes256-x25519 = ["mcuboot-sys/enc-aes256-x25519"] +bootstrap = ["mcuboot-sys/bootstrap"] +multiimage = ["mcuboot-sys/multiimage"] +ram-load = ["mcuboot-sys/ram-load"] +direct-xip = ["mcuboot-sys/direct-xip"] +downgrade-prevention = ["mcuboot-sys/downgrade-prevention"] +max-align-32 = ["mcuboot-sys/max-align-32"] +hw-rollback-protection = ["mcuboot-sys/hw-rollback-protection"] + +[dependencies] +byteorder = "1.4" +libc = "0.2" +rand = { version = "0.8", features = ["small_rng"] } +docopt = "1.1.0" +serde = "1.0" +serde_derive = "1.0" +log = "0.4" +env_logger = "0.9" +simflash = { path = "simflash" } +mcuboot-sys = { path = "mcuboot-sys" } +ring = "0.16.11" +untrusted = "0.9" +pem = "1.0" +cipher = "0.3" +aes = { version = "0.7.4", features = ["ctr"] } +base64 = "0.13.0" +typenum = "1.13.0" diff --git a/bootloader/mcuboot/sim/README.rst b/bootloader/mcuboot/sim/README.rst new file mode 100644 index 0000000..fb9012b --- /dev/null +++ b/bootloader/mcuboot/sim/README.rst @@ -0,0 +1,61 @@ +MCUboot simulator +################# + +This is a small simulator designed to exercise the mcuboot upgrade +code, specifically testing untimely reset scenarios to make sure the +code is robust. + +Prerequisites +============= + +The simulator is written in Rust_, and you will need to install it to +build it. The installation_ page describes this process. The +simulator can be built with the stable release of Rust. + +.. _Rust: https://www.rust-lang.org/ + +.. _installation: https://www.rust-lang.org/en-US/install.html + +Dependent code +-------------- + +The simulator depends on some external modules. These are stored as +submodules within git. To fetch these dependencies the first time:: + + $ git submodule update --init --recursive + +will clone and check out these trees in the appropriate place. + +Testing +======= + +The tests are written as unit tests in Rust, and can be built and run +automatically:: + + $ cargo test + +this should download and compile the necessary dependencies, compile +the relevant modules from mcuboot, build the simulator, and run the +tests. + +There are several different features you can test. For example, +testing RSA signatures can be done with:: + + $ cargo test --features sig-rsa + +For a complete list of features, see Cargo.toml. + +Debugging +========= + +If the simulator indicates a failure, you can turn on additional +logging by setting ``RUST_LOG=warn`` or ``RUST_LOG=error`` in the +environment:: + + $ RUST_LOG=warn ./target/release/bootsim run ... + +It is also possible to run specific tests, for example:: + + $ cargo test -- basic_revert + +which will run only the `basic_revert` test. diff --git a/bootloader/mcuboot/sim/mcuboot-sys/.gitignore b/bootloader/mcuboot/sim/mcuboot-sys/.gitignore new file mode 100644 index 0000000..03314f7 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/.gitignore @@ -0,0 +1 @@ +Cargo.lock diff --git a/bootloader/mcuboot/sim/mcuboot-sys/Cargo.toml b/bootloader/mcuboot/sim/mcuboot-sys/Cargo.toml new file mode 100644 index 0000000..ab97bbf --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/Cargo.toml @@ -0,0 +1,104 @@ +[package] +name = "mcuboot-sys" +version = "0.1.0" +authors = ["David Brown "] +description = "A simple wrapper around the mcuboot code." +build = "build.rs" +publish = false +edition = "2021" + +[features] +# By default, build with simplistic signature verification. +default = [] + +# Verify RSA signatures. Note that at this time, the C code will not +# compile with both sig-rsa and sig-ecdsa enabled. +sig-rsa = [] + +# Verify RSA-3072 signatures. +sig-rsa3072 = [] + +# Verify ECDSA (secp256r1) signatures. +sig-ecdsa = [] + +# Verify ECDSA (secp256r1) signatures using mbed TLS +sig-ecdsa-mbedtls = [] + +# Verify ECDSA (p256 or p384) signatures using PSA Crypto API +sig-ecdsa-psa = [] + +# Enable P384 Curve support (instead of P256) for PSA Crypto +sig-p384 = [] + +# Verify ED25519 signatures. +sig-ed25519 = [] + +# Overwrite only upgrade +overwrite-only = [] + +swap-move = [] + +# Disable validation of the primary slot +validate-primary-slot = [] + +# Encrypt image in the secondary slot using RSA-OAEP-2048 +enc-rsa = [] + +# Encrypt image in the secondary slot using AES-256-CTR and RSA-OAEP-2048 +enc-aes256-rsa = [] + +# Encrypt image in the secondary slot using AES-KW-128 +enc-kw = [] + +# Encrypt image in the secondary slot using AES-256-CTR and AES-KW-256 +enc-aes256-kw = [] + +# Encrypt image in the secondary slot using ECIES-P256 +enc-ec256 = [] + +# Encrypt image in the secondary slot using AES-256-CTR and ECIES-P256 +enc-aes256-ec256 = [] + +# Encrypt image in the secondary slot using ECIES-P256 using Mbed TLS +enc-ec256-mbedtls = [] + +# Encrypt image in the secondary slot using ECIES-X25519 +enc-x25519 = [] + +# Encrypt image in the secondary slot using AES-256-CTR and ECIES-X25519 +enc-aes256-x25519 = [] + +# Allow bootstrapping an empty/invalid primary slot from a valid secondary slot +bootstrap = [] + +# Support multiple images (currently 2 instead of 1). +multiimage = [] + +# Support simulation of ram-loading. No swaps are performed, and the +# image is copied to RAM before loading it. +ram-load = [] + +# Support simulation of direct XIP. No swaps are performed, the image +# is directly executed out of whichever partition contains the most +# appropriate image. +direct-xip = [] + +# Check (in software) against version downgrades. +downgrade-prevention = [] + +# Support images with 32-byte maximum write alignment value. +max-align-32 = [] + +# Enable hardware rollback protection +hw-rollback-protection = [] + +# Enable the PSA Crypto APIs where supported for cryptography related operations. +psa-crypto-api = [] + +[build-dependencies] +cc = "1.0.25" + +[dependencies] +libc = "0.2" +log = "0.4" +simflash = { path = "../simflash" } diff --git a/bootloader/mcuboot/sim/mcuboot-sys/build.rs b/bootloader/mcuboot/sim/mcuboot-sys/build.rs new file mode 100644 index 0000000..ea17d80 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/build.rs @@ -0,0 +1,532 @@ +// Build mcuboot as a library, based on the requested features. + +extern crate cc; + +use std::collections::BTreeSet; +use std::env; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; + +fn main() { + // Feature flags. + let psa_crypto_api = env::var("CARGO_FEATURE_PSA_CRYPTO_API").is_ok(); + let sig_rsa = env::var("CARGO_FEATURE_SIG_RSA").is_ok(); + let sig_rsa3072 = env::var("CARGO_FEATURE_SIG_RSA3072").is_ok(); + let sig_ecdsa = env::var("CARGO_FEATURE_SIG_ECDSA").is_ok(); + let sig_ecdsa_mbedtls = env::var("CARGO_FEATURE_SIG_ECDSA_MBEDTLS").is_ok(); + let sig_ecdsa_psa = env::var("CARGO_FEATURE_SIG_ECDSA_PSA").is_ok(); + let sig_p384 = env::var("CARGO_FEATURE_SIG_P384").is_ok(); + let sig_ed25519 = env::var("CARGO_FEATURE_SIG_ED25519").is_ok(); + let overwrite_only = env::var("CARGO_FEATURE_OVERWRITE_ONLY").is_ok(); + let swap_move = env::var("CARGO_FEATURE_SWAP_MOVE").is_ok(); + let validate_primary_slot = + env::var("CARGO_FEATURE_VALIDATE_PRIMARY_SLOT").is_ok(); + let enc_rsa = env::var("CARGO_FEATURE_ENC_RSA").is_ok(); + let enc_aes256_rsa = env::var("CARGO_FEATURE_ENC_AES256_RSA").is_ok(); + let enc_kw = env::var("CARGO_FEATURE_ENC_KW").is_ok(); + let enc_aes256_kw = env::var("CARGO_FEATURE_ENC_AES256_KW").is_ok(); + let enc_ec256 = env::var("CARGO_FEATURE_ENC_EC256").is_ok(); + let enc_ec256_mbedtls = env::var("CARGO_FEATURE_ENC_EC256_MBEDTLS").is_ok(); + let enc_aes256_ec256 = env::var("CARGO_FEATURE_ENC_AES256_EC256").is_ok(); + let enc_x25519 = env::var("CARGO_FEATURE_ENC_X25519").is_ok(); + let enc_aes256_x25519 = env::var("CARGO_FEATURE_ENC_AES256_X25519").is_ok(); + let bootstrap = env::var("CARGO_FEATURE_BOOTSTRAP").is_ok(); + let multiimage = env::var("CARGO_FEATURE_MULTIIMAGE").is_ok(); + let downgrade_prevention = env::var("CARGO_FEATURE_DOWNGRADE_PREVENTION").is_ok(); + let ram_load = env::var("CARGO_FEATURE_RAM_LOAD").is_ok(); + let direct_xip = env::var("CARGO_FEATURE_DIRECT_XIP").is_ok(); + let max_align_32 = env::var("CARGO_FEATURE_MAX_ALIGN_32").is_ok(); + let hw_rollback_protection = env::var("CARGO_FEATURE_HW_ROLLBACK_PROTECTION").is_ok(); + + let mut conf = CachedBuild::new(); + conf.conf.define("__BOOTSIM__", None); + conf.conf.define("MCUBOOT_HAVE_LOGGING", None); + conf.conf.define("MCUBOOT_USE_FLASH_AREA_GET_SECTORS", None); + conf.conf.define("MCUBOOT_HAVE_ASSERT_H", None); + conf.conf.define("MCUBOOT_MAX_IMG_SECTORS", Some("128")); + + if max_align_32 { + conf.conf.define("MCUBOOT_BOOT_MAX_ALIGN", Some("32")); + } else { + conf.conf.define("MCUBOOT_BOOT_MAX_ALIGN", Some("8")); + } + + conf.conf.define("MCUBOOT_IMAGE_NUMBER", Some(if multiimage { "2" } else { "1" })); + + if downgrade_prevention && !overwrite_only { + panic!("Downgrade prevention requires overwrite only"); + } + + if bootstrap { + conf.conf.define("MCUBOOT_BOOTSTRAP", None); + conf.conf.define("MCUBOOT_OVERWRITE_ONLY_FAST", None); + } + + if validate_primary_slot { + conf.conf.define("MCUBOOT_VALIDATE_PRIMARY_SLOT", None); + } + + if downgrade_prevention { + conf.conf.define("MCUBOOT_DOWNGRADE_PREVENTION", None); + } + + if ram_load { + conf.conf.define("MCUBOOT_RAM_LOAD", None); + } + + if direct_xip { + conf.conf.define("MCUBOOT_DIRECT_XIP", None); + } + + if hw_rollback_protection { + conf.conf.define("MCUBOOT_HW_ROLLBACK_PROT", None); + conf.file("csupport/security_cnt.c"); + } + + // Currently no more than one sig type can be used simultaneously. + if vec![sig_rsa, sig_rsa3072, sig_ecdsa, sig_ed25519].iter() + .fold(0, |sum, &v| sum + v as i32) > 1 { + panic!("mcuboot does not support more than one sig type at the same time"); + } + + if psa_crypto_api { + if sig_ecdsa || enc_ec256 || enc_x25519 || + enc_aes256_ec256 || sig_ecdsa_mbedtls || enc_aes256_x25519 || + enc_kw || enc_aes256_kw { + conf.file("csupport/psa_crypto_init_stub.c"); + } else { + conf.conf.define("MCUBOOT_USE_PSA_CRYPTO", None); + conf.file("../../ext/mbedtls/library/aes.c"); + conf.file("../../ext/mbedtls/library/aesni.c"); + conf.file("../../ext/mbedtls/library/aria.c"); + conf.file("../../ext/mbedtls/library/asn1write.c"); + conf.file("../../ext/mbedtls/library/base64.c"); + conf.file("../../ext/mbedtls/library/camellia.c"); + conf.file("../../ext/mbedtls/library/ccm.c"); + conf.file("../../ext/mbedtls/library/chacha20.c"); + conf.file("../../ext/mbedtls/library/chachapoly.c"); + conf.file("../../ext/mbedtls/library/cipher.c"); + conf.file("../../ext/mbedtls/library/cipher_wrap.c"); + conf.file("../../ext/mbedtls/library/ctr_drbg.c"); + conf.file("../../ext/mbedtls/library/des.c"); + conf.file("../../ext/mbedtls/library/ecdsa.c"); + conf.file("../../ext/mbedtls/library/ecp.c"); + conf.file("../../ext/mbedtls/library/ecp_curves.c"); + conf.file("../../ext/mbedtls/library/entropy.c"); + conf.file("../../ext/mbedtls/library/entropy_poll.c"); + conf.file("../../ext/mbedtls/library/gcm.c"); + conf.file("../../ext/mbedtls/library/md5.c"); + conf.file("../../ext/mbedtls/library/nist_kw.c"); + conf.file("../../ext/mbedtls/library/oid.c"); + conf.file("../../ext/mbedtls/library/pem.c"); + conf.file("../../ext/mbedtls/library/pk.c"); + conf.file("../../ext/mbedtls/library/pkcs5.c"); + conf.file("../../ext/mbedtls/library/pkcs12.c"); + conf.file("../../ext/mbedtls/library/pkparse.c"); + conf.file("../../ext/mbedtls/library/pk_wrap.c"); + conf.file("../../ext/mbedtls/library/pkwrite.c"); + conf.file("../../ext/mbedtls/library/poly1305.c"); + conf.file("../../ext/mbedtls/library/psa_crypto.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_cipher.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_client.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_driver_wrappers.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_ecp.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_hash.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_mac.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_rsa.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_slot_management.c"); + conf.file("../../ext/mbedtls/library/psa_crypto_storage.c"); + conf.file("../../ext/mbedtls/library/psa_its_file.c"); + conf.file("../../ext/mbedtls/library/ripemd160.c"); + conf.file("../../ext/mbedtls/library/rsa_alt_helpers.c"); + conf.file("../../ext/mbedtls/library/sha1.c"); + conf.file("../../ext/mbedtls/library/sha512.c"); + conf.file("../../ext/mbedtls/tests/src/random.c"); + conf.conf.include("../../ext/mbedtls/library"); + } + + conf.conf.include("../../ext/mbedtls/tests/include/"); + conf.file("../../ext/mbedtls/tests/src/fake_external_rng_for_test.c"); + } + + if sig_rsa || sig_rsa3072 { + conf.conf.define("MCUBOOT_SIGN_RSA", None); + // The Kconfig style defines must be added here as well because + // they are used internally by "config-rsa.h" + if sig_rsa { + conf.conf.define("MCUBOOT_SIGN_RSA_LEN", "2048"); + conf.conf.define("CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN", "2048"); + } else { + conf.conf.define("MCUBOOT_SIGN_RSA_LEN", "3072"); + conf.conf.define("CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN", "3072"); + } + conf.conf.define("MCUBOOT_USE_MBED_TLS", None); + + conf.conf.include("../../ext/mbedtls/include"); + conf.file("../../ext/mbedtls/library/sha256.c"); + conf.file("csupport/keys.c"); + + conf.file("../../ext/mbedtls/library/rsa.c"); + conf.file("../../ext/mbedtls/library/bignum.c"); + conf.file("../../ext/mbedtls/library/platform.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + conf.file("../../ext/mbedtls/library/md.c"); + + } else if sig_ecdsa { + conf.conf.define("MCUBOOT_SIGN_EC256", None); + conf.conf.define("MCUBOOT_USE_TINYCRYPT", None); + + if !enc_kw { + conf.conf.include("../../ext/mbedtls/include"); + } + conf.conf.include("../../ext/tinycrypt/lib/include"); + + conf.file("csupport/keys.c"); + + conf.file("../../ext/tinycrypt/lib/source/utils.c"); + conf.file("../../ext/tinycrypt/lib/source/sha256.c"); + conf.file("../../ext/tinycrypt/lib/source/ecc.c"); + conf.file("../../ext/tinycrypt/lib/source/ecc_dsa.c"); + conf.file("../../ext/tinycrypt/lib/source/ecc_platform_specific.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + } else if sig_ecdsa_mbedtls { + conf.conf.define("MCUBOOT_SIGN_EC256", None); + conf.conf.define("MCUBOOT_USE_MBED_TLS", None); + + conf.conf.include("../../ext/mbedtls/include"); + conf.file("../../ext/mbedtls/library/sha256.c"); + conf.file("csupport/keys.c"); + + conf.file("../../ext/mbedtls/library/asn1parse.c"); + conf.file("../../ext/mbedtls/library/bignum.c"); + conf.file("../../ext/mbedtls/library/ecdsa.c"); + conf.file("../../ext/mbedtls/library/ecp.c"); + conf.file("../../ext/mbedtls/library/ecp_curves.c"); + conf.file("../../ext/mbedtls/library/platform.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + } else if sig_ecdsa_psa { + conf.conf.include("../../ext/mbedtls/include"); + + if sig_p384 { + conf.conf.define("MCUBOOT_SIGN_EC384", None); + conf.file("../../ext/mbedtls/library/sha512.c"); + } else { + conf.conf.define("MCUBOOT_SIGN_EC256", None); + conf.file("../../ext/mbedtls/library/sha256.c"); + } + + conf.file("csupport/keys.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + conf.file("../../ext/mbedtls/library/bignum.c"); + conf.file("../../ext/mbedtls/library/ecp.c"); + conf.file("../../ext/mbedtls/library/ecp_curves.c"); + conf.file("../../ext/mbedtls/library/platform.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + } else if sig_ed25519 { + conf.conf.define("MCUBOOT_SIGN_ED25519", None); + conf.conf.define("MCUBOOT_USE_TINYCRYPT", None); + + conf.conf.include("../../ext/tinycrypt/lib/include"); + conf.conf.include("../../ext/tinycrypt-sha512/lib/include"); + conf.conf.include("../../ext/mbedtls/include"); + conf.file("../../ext/tinycrypt/lib/source/sha256.c"); + conf.file("../../ext/tinycrypt-sha512/lib/source/sha512.c"); + conf.file("../../ext/tinycrypt/lib/source/utils.c"); + conf.file("csupport/keys.c"); + conf.file("../../ext/fiat/src/curve25519.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + } else if !enc_ec256 && !enc_x25519 { + // No signature type, only sha256 validation. The default + // configuration file bundled with mbedTLS is sufficient. + // When using ECIES-P256 rely on Tinycrypt. + conf.conf.define("MCUBOOT_USE_MBED_TLS", None); + conf.conf.include("../../ext/mbedtls/include"); + conf.file("../../ext/mbedtls/library/sha256.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + } + + if overwrite_only { + conf.conf.define("MCUBOOT_OVERWRITE_ONLY", None); + } + + if swap_move { + conf.conf.define("MCUBOOT_SWAP_USING_MOVE", None); + } else if !overwrite_only && !direct_xip && !ram_load { + conf.conf.define("CONFIG_BOOT_SWAP_USING_SCRATCH", None); + conf.conf.define("MCUBOOT_SWAP_USING_SCRATCH", None); + } + + if enc_rsa || enc_aes256_rsa { + if enc_aes256_rsa { + conf.conf.define("MCUBOOT_AES_256", None); + } + conf.conf.define("MCUBOOT_ENCRYPT_RSA", None); + conf.conf.define("MCUBOOT_ENC_IMAGES", None); + conf.conf.define("MCUBOOT_USE_MBED_TLS", None); + + conf.file("../../boot/bootutil/src/encrypted.c"); + conf.file("csupport/keys.c"); + + conf.conf.include("../../ext/mbedtls/include"); + conf.conf.include("../../ext/mbedtls/library"); + conf.file("../../ext/mbedtls/library/sha256.c"); + + conf.file("../../ext/mbedtls/library/platform.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/rsa.c"); + conf.file("../../ext/mbedtls/library/rsa_alt_helpers.c"); + conf.file("../../ext/mbedtls/library/md.c"); + conf.file("../../ext/mbedtls/library/aes.c"); + conf.file("../../ext/mbedtls/library/bignum.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + } + + if enc_kw || enc_aes256_kw { + if enc_aes256_kw { + conf.conf.define("MCUBOOT_AES_256", None); + } + conf.conf.define("MCUBOOT_ENCRYPT_KW", None); + conf.conf.define("MCUBOOT_ENC_IMAGES", None); + + conf.file("../../boot/bootutil/src/encrypted.c"); + conf.file("csupport/keys.c"); + + if sig_rsa || sig_rsa3072 { + conf.file("../../ext/mbedtls/library/sha256.c"); + } + + /* Simulator uses Mbed-TLS to wrap keys */ + conf.conf.include("../../ext/mbedtls/include"); + conf.file("../../ext/mbedtls/library/platform.c"); + conf.conf.include("../../ext/mbedtls/library"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/nist_kw.c"); + conf.file("../../ext/mbedtls/library/cipher.c"); + conf.file("../../ext/mbedtls/library/cipher_wrap.c"); + conf.file("../../ext/mbedtls/library/aes.c"); + + if sig_ecdsa { + conf.conf.define("MCUBOOT_USE_TINYCRYPT", None); + + conf.conf.include("../../ext/tinycrypt/lib/include"); + + conf.file("../../ext/tinycrypt/lib/source/utils.c"); + conf.file("../../ext/tinycrypt/lib/source/sha256.c"); + conf.file("../../ext/tinycrypt/lib/source/aes_encrypt.c"); + conf.file("../../ext/tinycrypt/lib/source/aes_decrypt.c"); + conf.file("../../ext/tinycrypt/lib/source/ctr_mode.c"); + } + + if sig_ed25519 { + panic!("ed25519 does not support image encryption with KW yet"); + } + } + + if enc_ec256 { + conf.conf.define("MCUBOOT_ENCRYPT_EC256", None); + conf.conf.define("MCUBOOT_ENC_IMAGES", None); + conf.conf.define("MCUBOOT_USE_TINYCRYPT", None); + conf.conf.define("MCUBOOT_SWAP_SAVE_ENCTLV", None); + + conf.file("../../boot/bootutil/src/encrypted.c"); + conf.file("csupport/keys.c"); + + conf.conf.include("../../ext/mbedtls/include"); + conf.conf.include("../../ext/tinycrypt/lib/include"); + + /* FIXME: fail with other signature schemes ? */ + + conf.file("../../ext/tinycrypt/lib/source/utils.c"); + conf.file("../../ext/tinycrypt/lib/source/sha256.c"); + conf.file("../../ext/tinycrypt/lib/source/ecc.c"); + conf.file("../../ext/tinycrypt/lib/source/ecc_dsa.c"); + conf.file("../../ext/tinycrypt/lib/source/ecc_platform_specific.c"); + + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + + conf.file("../../ext/tinycrypt/lib/source/aes_encrypt.c"); + conf.file("../../ext/tinycrypt/lib/source/aes_decrypt.c"); + conf.file("../../ext/tinycrypt/lib/source/ctr_mode.c"); + conf.file("../../ext/tinycrypt/lib/source/hmac.c"); + conf.file("../../ext/tinycrypt/lib/source/ecc_dh.c"); + } else if enc_ec256_mbedtls || enc_aes256_ec256 { + if enc_aes256_ec256 { + conf.conf.define("MCUBOOT_AES_256", None); + } + conf.conf.define("MCUBOOT_ENCRYPT_EC256", None); + conf.conf.define("MCUBOOT_ENC_IMAGES", None); + conf.conf.define("MCUBOOT_USE_MBED_TLS", None); + conf.conf.define("MCUBOOT_SWAP_SAVE_ENCTLV", None); + + conf.conf.include("../../ext/mbedtls/include"); + + conf.file("../../boot/bootutil/src/encrypted.c"); + conf.file("../../ext/mbedtls/library/sha256.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + conf.file("../../ext/mbedtls/library/bignum.c"); + conf.file("../../ext/mbedtls/library/ecdh.c"); + conf.file("../../ext/mbedtls/library/md.c"); + conf.file("../../ext/mbedtls/library/aes.c"); + conf.file("../../ext/mbedtls/library/ecp.c"); + conf.file("../../ext/mbedtls/library/ecp_curves.c"); + conf.file("../../ext/mbedtls/library/platform.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("csupport/keys.c"); + } + + if enc_x25519 { + conf.conf.define("MCUBOOT_ENCRYPT_X25519", None); + conf.conf.define("MCUBOOT_ENC_IMAGES", None); + conf.conf.define("MCUBOOT_USE_TINYCRYPT", None); + conf.conf.define("MCUBOOT_SWAP_SAVE_ENCTLV", None); + + conf.file("../../boot/bootutil/src/encrypted.c"); + conf.file("csupport/keys.c"); + + conf.conf.include("../../ext/mbedtls/include"); + conf.conf.include("../../ext/tinycrypt/lib/include"); + conf.conf.include("../../ext/tinycrypt-sha512/lib/include"); + + conf.file("../../ext/fiat/src/curve25519.c"); + + conf.file("../../ext/tinycrypt/lib/source/utils.c"); + conf.file("../../ext/tinycrypt/lib/source/sha256.c"); + + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + + conf.file("../../ext/tinycrypt/lib/source/aes_encrypt.c"); + conf.file("../../ext/tinycrypt/lib/source/aes_decrypt.c"); + conf.file("../../ext/tinycrypt/lib/source/ctr_mode.c"); + conf.file("../../ext/tinycrypt/lib/source/hmac.c"); + } + + else if enc_aes256_x25519 { + conf.conf.define("MCUBOOT_AES_256", None); + conf.conf.define("MCUBOOT_ENCRYPT_X25519", None); + conf.conf.define("MCUBOOT_ENC_IMAGES", None); + conf.conf.define("MCUBOOT_USE_MBED_TLS", None); + conf.conf.define("MCUBOOT_SWAP_SAVE_ENCTLV", None); + + conf.file("../../boot/bootutil/src/encrypted.c"); + conf.file("csupport/keys.c"); + + conf.conf.include("../../ext/mbedtls/include"); + conf.file("../../ext/fiat/src/curve25519.c"); + conf.file("../../ext/mbedtls/library/asn1parse.c"); + conf.file("../../ext/mbedtls/library/platform.c"); + conf.file("../../ext/mbedtls/library/platform_util.c"); + conf.file("../../ext/mbedtls/library/aes.c"); + conf.file("../../ext/mbedtls/library/sha256.c"); + conf.file("../../ext/mbedtls/library/md.c"); + conf.file("../../ext/mbedtls/library/sha512.c"); + } + + if sig_rsa && enc_kw { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } else if sig_rsa || sig_rsa3072 || enc_rsa || enc_aes256_rsa { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } else if sig_ecdsa_mbedtls || enc_ec256_mbedtls || enc_aes256_ec256 { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } else if (sig_ecdsa || enc_ec256) && !enc_kw { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } else if sig_ed25519 || enc_x25519 { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } else if enc_kw || enc_aes256_kw { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } else if enc_aes256_x25519 { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } else if sig_ecdsa_psa { + conf.conf.define("MBEDTLS_CONFIG_FILE", Some("")); + } + + conf.file("../../boot/bootutil/src/image_validate.c"); + if sig_rsa || sig_rsa3072 { + conf.file("../../boot/bootutil/src/image_rsa.c"); + } else if sig_ecdsa || sig_ecdsa_mbedtls || sig_ecdsa_psa { + conf.file("../../boot/bootutil/src/image_ecdsa.c"); + } else if sig_ed25519 { + conf.file("../../boot/bootutil/src/image_ed25519.c"); + } + + conf.file("../../boot/bootutil/src/loader.c"); + conf.file("../../boot/bootutil/src/swap_misc.c"); + conf.file("../../boot/bootutil/src/swap_scratch.c"); + conf.file("../../boot/bootutil/src/swap_move.c"); + conf.file("../../boot/bootutil/src/caps.c"); + conf.file("../../boot/bootutil/src/bootutil_misc.c"); + conf.file("../../boot/bootutil/src/bootutil_public.c"); + conf.file("../../boot/bootutil/src/tlv.c"); + conf.file("../../boot/bootutil/src/fault_injection_hardening.c"); + conf.file("csupport/run.c"); + conf.conf.include("../../boot/bootutil/include"); + conf.conf.include("csupport"); + conf.conf.debug(true); + conf.conf.flag("-Wall"); + conf.conf.flag("-Werror"); + + // FIXME: travis-ci still uses gcc 4.8.4 which defaults to std=gnu90. + // It has incomplete std=c11 and std=c99 support but std=c99 was checked + // to build correctly so leaving it here to updated in the future... + conf.conf.flag("-std=c99"); + + conf.conf.compile("libbootutil.a"); + + walk_dir("../../boot").unwrap(); + walk_dir("../../ext/tinycrypt/lib/source").unwrap(); + walk_dir("../../ext/mbedtls-asn1").unwrap(); + walk_dir("csupport").unwrap(); + walk_dir("../../ext/mbedtls/include").unwrap(); + walk_dir("../../ext/mbedtls/library").unwrap(); +} + +// Output the names of all files within a directory so that Cargo knows when to rebuild. +fn walk_dir>(path: P) -> io::Result<()> { + for ent in fs::read_dir(path.as_ref())? { + let ent = ent?; + let p = ent.path(); + if p.is_dir() { + walk_dir(p)?; + } else { + // Note that non-utf8 names will fail. + let name = p.to_str().unwrap(); + if name.ends_with(".c") || name.ends_with(".h") { + println!("cargo:rerun-if-changed={}", name); + } + } + } + + Ok(()) +} + +/// Wrap the cc::Build type so that we can make sure that files are only added a single time. +/// Other methods can be passed through as needed. +struct CachedBuild { + conf: cc::Build, + seen: BTreeSet, +} + +impl CachedBuild { + fn new() -> CachedBuild { + CachedBuild { + conf: cc::Build::new(), + seen: BTreeSet::new(), + } + } + + /// Works like `file` in the Build, but doesn't add a file if the same path has already been + /// given. + fn file>(&mut self, p: P) -> &mut CachedBuild { + let p = p.as_ref(); + if !self.seen.contains(p) { + self.conf.file(p); + self.seen.insert(p.to_owned()); + } + self + } +} diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/bootsim.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/bootsim.h new file mode 100644 index 0000000..36e4028 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/bootsim.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BOOTSIM_ +#define H_BOOTSIM_ + +#include "mcuboot_config/mcuboot_assert.h" + +#endif diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-add-psa-crypto.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-add-psa-crypto.h new file mode 100644 index 0000000..d825be1 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-add-psa-crypto.h @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Arm Limited + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_ADD_PSA_CRYPTO_H +#define MCUBOOT_MBEDTLS_CONFIG_ADD_PSA_CRYPTO_H + +#include "mbedtls/build_info.h" + +/* Enable PSA Crypto Core without support for the permanent storage + * Don't define MBEDTLS_PSA_CRYPTO_STORAGE_C to make sure that support + * for permanent keys is not enabled, as it is not usually required during boot + */ +#define MBEDTLS_PSA_CRYPTO_C +#define MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + +#if defined(MCUBOOT_ENCRYPT_RSA) || defined(MCUBOOT_SIGN_RSA) + #define MBEDTLS_PK_C + #define MBEDTLS_CTR_DRBG_C + #define MBEDTLS_CIPHER_C + #define MBEDTLS_ENTROPY_C + #define MBEDTLS_PK_PARSE_C + #define MBEDTLS_PK_WRITE_C +#endif /* MCUBOOT_ENCRYPT_RSA || MCUBOOT_SIGN_RSA */ + +#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) + #define MBEDTLS_PLATFORM_FREE_MACRO free + #define MBEDTLS_PLATFORM_CALLOC_MACRO calloc +#endif /* MCUBOOT_ENCRYPT_EC256 || MCUBOOT_ENCRYPT_X25519 */ + +#if !defined(MCUBOOT_ENCRYPT_X25519) + #define MBEDTLS_PSA_BUILTIN_CIPHER 1 +#endif /* MCUBOOT_ENCRYPT_X25519 */ + +#if defined(MCUBOOT_ENCRYPT_KW) + #define MBEDTLS_PSA_CRYPTO_CONFIG + #define MBEDTLS_POLY1305_C +#endif /* MCUBOOT_ENCRYPT_KW */ + +#if MBEDTLS_VERSION_NUMBER == 0x03000000 +/* This PSA define is available only with more recent versions of 3.x */ +#define PSA_KEY_ID_NULL ((psa_key_id_t)0) // not overly happy with this being here +#endif /* MBEDTLS_VERSION_NUMBER == 0x03000000 */ + +#endif /* MCUBOOT_MBEDTLS_CONFIG_ADD_PSA_CRYPTO_H */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-asn1.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-asn1.h new file mode 100644 index 0000000..b9d70df --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-asn1.h @@ -0,0 +1,44 @@ +/* + * Configuration of mbedTLS containing only the ASN.1 parser. + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MBEDTLS_CONFIG_ASN1_H +#define MBEDTLS_CONFIG_ASN1_H + +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +// #define MBEDTLS_ASN1_WRITE_C +// #define MBEDTLS_BIGNUM_C +// #define MBEDTLS_MD_C +// #define MBEDTLS_OID_C +// #define MBEDTLS_SHA256_C + +#endif /* MBEDTLS_CONFIG_ASN1_H */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec-psa.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec-psa.h new file mode 100644 index 0000000..5dbb495 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec-psa.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Arm Limited + */ + +#ifndef MCUBOOT_PSA_CRYPTO_CONFIG_ECDSA +#define MCUBOOT_PSA_CRYPTO_CONFIG_ECDSA + +#if defined(MCUBOOT_USE_PSA_CRYPTO) +#include "config-add-psa-crypto.h" +#endif + +#define MBEDTLS_ECP_C +#define MBEDTLS_ECP_NIST_OPTIM +#define MBEDTLS_ECDSA_C + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_AES_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#if defined(MCUBOOT_SIGN_EC384) +#define MBEDTLS_SHA384_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#else +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#endif /* MCUBOOT_SIGN_EC384 */ + +#endif /* MCUBOOT_PSA_CRYPTO_CONFIG_ECDSA */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec.h new file mode 100644 index 0000000..2ce3b8f --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ec.h @@ -0,0 +1,95 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_ECDSA +#define MCUBOOT_MBEDTLS_CONFIG_ECDSA + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ECDH_C + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_NIST_OPTIM +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C + +/* Bring in support for x509. */ +#define MBEDTLS_X509_USE_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_X509_CRT_PARSE_C + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_ECP_MAX_BITS 256 + +#define MBEDTLS_MPI_MAX_SIZE 32 + +#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +/* If encryption is being used, also enable the features needed for + * that. */ +#if defined(MCUBOOT_ENC_IMAGES) +#define MBEDTLS_CIPHER_MODE_CTR +#define MBEDTLS_CIPHER_C +#define MBEDTLS_NIST_KW_C +#endif /* MCUBOOT_ENC_IMAGES */ + +#endif /* MCUBOOT_MBEDTLS_CONFIG_ECDSA */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ed25519.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ed25519.h new file mode 100644 index 0000000..09b3c21 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-ed25519.h @@ -0,0 +1,77 @@ +/* + * Configuration of mbedTLS containing only the ASN.1 parser. + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - ed25519 signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_ED25519 +#define MCUBOOT_MBEDTLS_CONFIG_ED25519 + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_CIPHER_MODE_CTR + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_AES_C + +/* Save RAM by adjusting to our exact needs */ +//#define MBEDTLS_ECP_MAX_BITS 2048 + +#define MBEDTLS_MPI_MAX_SIZE 64 + +//#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +#endif /* MCUBOOT_MBEDTLS_CONFIG_RSA */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-kw.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-kw.h new file mode 100644 index 0000000..116eb40 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-kw.h @@ -0,0 +1,65 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2021, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_KW +#define MCUBOOT_MBEDTLS_CONFIG_KW + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#define MBEDTLS_ASN1_PARSE_C + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_CIPHER_MODE_CTR + +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_NIST_KW_C + +#endif /* MCUBOOT_MBEDTLS_CONFIG_KW */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa-kw.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa-kw.h new file mode 100644 index 0000000..e06debf --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa-kw.h @@ -0,0 +1,84 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2023, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA signature verification + NIST Keywrapping support + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_RSA_KW +#define MCUBOOT_MBEDTLS_CONFIG_RSA_KW + +#if defined(MCUBOOT_USE_PSA_CRYPTO) +#include "config-add-psa-crypto.h" +#endif /* defined(MCUBOOT_USE_PSA_CRYPTO) */ + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_RSA_C +#define MBEDTLS_PKCS1_V21 + +#define MBEDTLS_CIPHER_MODE_CTR + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_NIST_KW_C + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_ECP_MAX_BITS 2048 +#define MBEDTLS_MPI_MAX_SIZE 256 + +#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +#endif /* MCUBOOT_MBEDTLS_CONFIG_RSA_KW */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa.h new file mode 100644 index 0000000..6f174f0 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/config-rsa.h @@ -0,0 +1,85 @@ +/* + * Minimal configuration for using TLS in the bootloader + * + * Copyright (C) 2006-2023, ARM Limited, All Rights Reserved + * Copyright (C) 2016, Linaro Ltd + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * Minimal configuration for using TLS in the bootloader + * + * - RSA or ECDSA signature verification + */ + +#ifndef MCUBOOT_MBEDTLS_CONFIG_RSA +#define MCUBOOT_MBEDTLS_CONFIG_RSA + +#if defined(MCUBOOT_USE_PSA_CRYPTO) +#include "config-add-psa-crypto.h" +#endif + +#ifdef CONFIG_MCUBOOT_SERIAL +/* Mcuboot uses mbedts-base64 for serial protocol encoding. */ +#define MBEDTLS_BASE64_C +#endif + +/* System support */ +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_MEMORY_BUFFER_ALLOC_C +#define MBEDTLS_NO_PLATFORM_ENTROPY +#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/* STD functions */ +#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +#define MBEDTLS_PLATFORM_EXIT_ALT +#define MBEDTLS_PLATFORM_PRINTF_ALT +#define MBEDTLS_PLATFORM_SNPRINTF_ALT + +#if !defined(CONFIG_ARM) +#define MBEDTLS_HAVE_ASM +#endif + +#define MBEDTLS_RSA_C +#define MBEDTLS_PKCS1_V21 + +#define MBEDTLS_CIPHER_MODE_CTR + +/* mbed TLS modules */ +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_AES_C + +#if (CONFIG_BOOT_SIGNATURE_TYPE_RSA_LEN == 3072) +#define MBEDTLS_MPI_MAX_SIZE 384 +#else +#define MBEDTLS_MPI_MAX_SIZE 256 +#endif + +#define MBEDTLS_SSL_MAX_CONTENT_LEN 1024 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +#endif /* MCUBOOT_MBEDTLS_CONFIG_RSA */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/devicetree.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/devicetree.h new file mode 100644 index 0000000..434e4ee --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/devicetree.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This file mocks zephyr's flash map / DT macro */ + +#ifndef __DEVICETREE_H__ +#define __DEVICETREE_H__ + +#define FLASH_AREA_ID(x) FLASH_AREA_ID_##x + +#define FLASH_AREA_ID_image_0 1 +#define FLASH_AREA_ID_image_1 2 +#define FLASH_AREA_ID_image_scratch 3 +#define FLASH_AREA_ID_image_2 4 +#define FLASH_AREA_ID_image_3 5 + +#endif /*__DEVICETREE_H__*/ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/flash_map_backend/flash_map_backend.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/flash_map_backend/flash_map_backend.h new file mode 100644 index 0000000..6783544 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/flash_map_backend/flash_map_backend.h @@ -0,0 +1,38 @@ +#ifndef __FLASH_MAP_BACKEND_H__ +#define __FLASH_MAP_BACKEND_H__ + +#include + +static inline uint32_t flash_area_get_off(const struct flash_area *fa) +{ + return (uint32_t)fa->fa_off; +} + +static inline uint32_t flash_area_get_size(const struct flash_area *fa) +{ + return (uint32_t)fa->fa_size; +} + +static inline uint32_t flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +static inline uint32_t flash_sector_get_size(const struct flash_sector *fs) +{ + return fs->fs_size; +} + +#define FLASH_DEVICE_ID 0 + +static inline uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + return fa->fa_device_id; +} + +static inline uint8_t flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +#endif /* __FLASH_MAP_BACKEND_H__*/ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/keys.c b/bootloader/mcuboot/sim/mcuboot-sys/csupport/keys.c new file mode 100644 index 0000000..82a746b --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/keys.c @@ -0,0 +1,330 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +#include + +#if defined(MCUBOOT_SIGN_RSA) +#if MCUBOOT_SIGN_RSA_LEN == 2048 +#define HAVE_KEYS +const unsigned char root_pub_der[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd1, 0x06, 0x08, + 0x1a, 0x18, 0x44, 0x2c, 0x18, 0xe8, 0xfb, 0xfd, 0xf7, 0x0d, 0xa3, 0x4f, + 0x1f, 0xbb, 0xee, 0x5e, 0xf9, 0xaa, 0xd2, 0x4b, 0x18, 0xd3, 0x5a, 0xe9, + 0x6d, 0x18, 0x80, 0x19, 0xf9, 0xf0, 0x9c, 0x34, 0x1b, 0xcb, 0xf3, 0xbc, + 0x74, 0xdb, 0x42, 0xe7, 0x8c, 0x7f, 0x10, 0x53, 0x7e, 0x43, 0x5e, 0x0d, + 0x57, 0x2c, 0x44, 0xd1, 0x67, 0x08, 0x0f, 0x0d, 0xbb, 0x5c, 0xee, 0xec, + 0xb3, 0x99, 0xdf, 0xe0, 0x4d, 0x84, 0x0b, 0xaa, 0x77, 0x41, 0x60, 0xed, + 0x15, 0x28, 0x49, 0xa7, 0x01, 0xb4, 0x3c, 0x10, 0xe6, 0x69, 0x8c, 0x2f, + 0x5f, 0xac, 0x41, 0x4d, 0x9e, 0x5c, 0x14, 0xdf, 0xf2, 0xf8, 0xcf, 0x3d, + 0x1e, 0x6f, 0xe7, 0x5b, 0xba, 0xb4, 0xa9, 0xc8, 0x88, 0x7e, 0x47, 0x3c, + 0x94, 0xc3, 0x77, 0x67, 0x54, 0x4b, 0xaa, 0x8d, 0x38, 0x35, 0xca, 0x62, + 0x61, 0x7e, 0xb7, 0xe1, 0x15, 0xdb, 0x77, 0x73, 0xd4, 0xbe, 0x7b, 0x72, + 0x21, 0x89, 0x69, 0x24, 0xfb, 0xf8, 0x65, 0x6e, 0x64, 0x3e, 0xc8, 0x0e, + 0xd7, 0x85, 0xd5, 0x5c, 0x4a, 0xe4, 0x53, 0x0d, 0x2f, 0xff, 0xb7, 0xfd, + 0xf3, 0x13, 0x39, 0x83, 0x3f, 0xa3, 0xae, 0xd2, 0x0f, 0xa7, 0x6a, 0x9d, + 0xf9, 0xfe, 0xb8, 0xce, 0xfa, 0x2a, 0xbe, 0xaf, 0xb8, 0xe0, 0xfa, 0x82, + 0x37, 0x54, 0xf4, 0x3e, 0xe1, 0x2b, 0xd0, 0xd3, 0x08, 0x58, 0x18, 0xf6, + 0x5e, 0x4c, 0xc8, 0x88, 0x81, 0x31, 0xad, 0x5f, 0xb0, 0x82, 0x17, 0xf2, + 0x8a, 0x69, 0x27, 0x23, 0xf3, 0xab, 0x87, 0x3e, 0x93, 0x1a, 0x1d, 0xfe, + 0xe8, 0xf8, 0x1a, 0x24, 0x66, 0x59, 0xf8, 0x1c, 0xab, 0xdc, 0xce, 0x68, + 0x1b, 0x66, 0x64, 0x35, 0xec, 0xfa, 0x0d, 0x11, 0x9d, 0xaf, 0x5c, 0x3a, + 0xa7, 0xd1, 0x67, 0xc6, 0x47, 0xef, 0xb1, 0x4b, 0x2c, 0x62, 0xe1, 0xd1, + 0xc9, 0x02, 0x03, 0x01, 0x00, 0x01 +}; +const unsigned int root_pub_der_len = 270; +#elif MCUBOOT_SIGN_RSA_LEN == 3072 +#define HAVE_KEYS +const unsigned char root_pub_der[] = { + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, + 0x00, 0xb4, 0x2c, 0x0e, 0x98, 0x58, 0x10, 0xa4, + 0xa7, 0x58, 0x99, 0x7c, 0x01, 0xdd, 0x08, 0x2a, + 0x28, 0x34, 0x33, 0xf8, 0x96, 0x1a, 0x34, 0x20, + 0x5d, 0x45, 0xc8, 0x71, 0x26, 0x25, 0xe5, 0xd2, + 0x96, 0xea, 0x7b, 0xb1, 0x15, 0xaa, 0xa6, 0x8a, + 0x63, 0x22, 0x8b, 0x2d, 0x4e, 0x81, 0x73, 0xbf, + 0x6e, 0x15, 0x68, 0x8c, 0x1a, 0xf4, 0xef, 0x2a, + 0x8f, 0x8c, 0x22, 0x9e, 0x71, 0x57, 0x4b, 0xde, + 0x0f, 0x7e, 0x72, 0xd3, 0x7a, 0xb8, 0xa7, 0x1d, + 0x44, 0xad, 0x87, 0x00, 0x83, 0x5c, 0xfd, 0x73, + 0x05, 0x72, 0x46, 0x3f, 0x8b, 0xf9, 0x10, 0x00, + 0xd8, 0x6e, 0xcc, 0x85, 0xed, 0xf9, 0x49, 0xdb, + 0x78, 0x36, 0x80, 0x49, 0x38, 0x76, 0xdd, 0x5f, + 0x54, 0x04, 0xda, 0x8c, 0x34, 0xa7, 0x2b, 0x13, + 0x25, 0x6f, 0xd1, 0x15, 0x4f, 0xad, 0xc2, 0xe1, + 0xa5, 0xd2, 0x4e, 0x57, 0x0c, 0x7e, 0x9c, 0x9b, + 0xba, 0x4e, 0x68, 0xb2, 0xe0, 0x25, 0x02, 0xaa, + 0x00, 0xd3, 0xb4, 0xcc, 0x2f, 0x78, 0xe5, 0xbe, + 0x47, 0x67, 0x1f, 0xc8, 0x6e, 0x22, 0x6c, 0x5e, + 0x61, 0xb6, 0x9a, 0xcd, 0xe5, 0xa8, 0xba, 0x7a, + 0x80, 0x13, 0x1b, 0x17, 0x2e, 0x96, 0xed, 0xcf, + 0xb3, 0x9b, 0xe4, 0x1c, 0xe8, 0xad, 0xa7, 0xf6, + 0x3a, 0x51, 0x66, 0x5e, 0x99, 0x8e, 0x87, 0xee, + 0x60, 0x25, 0xf8, 0x8d, 0xbe, 0xce, 0xa4, 0xa8, + 0xca, 0x93, 0x6c, 0xd7, 0xbf, 0xd4, 0x73, 0x33, + 0x8d, 0x44, 0x85, 0xcc, 0x73, 0x30, 0x08, 0x9c, + 0x4d, 0xb2, 0xaa, 0x5a, 0x6c, 0x6f, 0x7b, 0xab, + 0xb7, 0xb3, 0x7c, 0xc3, 0xfb, 0xe7, 0xca, 0xc4, + 0xf8, 0x9a, 0x6f, 0xcb, 0xbb, 0x5b, 0x82, 0xe7, + 0x7a, 0xe8, 0x19, 0xfd, 0x2f, 0x11, 0x22, 0xfb, + 0x7f, 0x76, 0x8c, 0x6b, 0x94, 0xa4, 0x09, 0x4f, + 0xa5, 0x6a, 0x77, 0x51, 0xeb, 0xa7, 0x7e, 0xda, + 0x87, 0x06, 0xee, 0xdc, 0xbe, 0xd1, 0xea, 0x1a, + 0x40, 0x1d, 0x1b, 0xff, 0x1a, 0xb1, 0x51, 0x7c, + 0x12, 0xb0, 0xf3, 0xf6, 0x83, 0x01, 0x9c, 0xe7, + 0x0c, 0x99, 0xbf, 0xac, 0x68, 0x58, 0x72, 0xa4, + 0xb0, 0x59, 0x85, 0xee, 0x85, 0xac, 0x2a, 0x22, + 0xf4, 0xcf, 0x15, 0x08, 0x80, 0x1f, 0x0d, 0xd0, + 0x1e, 0xa0, 0xa0, 0x94, 0xc8, 0xf7, 0xfa, 0x65, + 0xdd, 0x52, 0xe8, 0x96, 0x37, 0x23, 0x30, 0x57, + 0x36, 0xe6, 0x9d, 0xf4, 0x0c, 0x4a, 0x05, 0x75, + 0x1f, 0xad, 0x01, 0xca, 0xb7, 0x6d, 0x8c, 0x43, + 0x74, 0x06, 0x0a, 0x81, 0xf3, 0x01, 0x62, 0xff, + 0xf7, 0xf5, 0x5f, 0xaf, 0xe7, 0x2b, 0x0e, 0xf8, + 0x81, 0xb5, 0x65, 0xdd, 0x01, 0xd9, 0x9f, 0x07, + 0x17, 0x8a, 0x18, 0xcf, 0x23, 0x6e, 0x88, 0x65, + 0x91, 0xb5, 0x7b, 0xd3, 0xb0, 0x2d, 0xaf, 0x93, + 0x66, 0x63, 0x74, 0xac, 0x5a, 0xe6, 0x73, 0xde, + 0x3b, 0x02, 0x03, 0x01, 0x00, 0x01, +}; +const unsigned int root_pub_der_len = 398; +#endif +#elif defined(MCUBOOT_SIGN_EC256) || \ + defined(MCUBOOT_SIGN_EC384) +#define HAVE_KEYS +#ifndef MCUBOOT_SIGN_EC384 +const unsigned char root_pub_der[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x2a, 0xcb, 0x40, 0x3c, 0xe8, + 0xfe, 0xed, 0x5b, 0xa4, 0x49, 0x95, 0xa1, 0xa9, + 0x1d, 0xae, 0xe8, 0xdb, 0xbe, 0x19, 0x37, 0xcd, + 0x14, 0xfb, 0x2f, 0x24, 0x57, 0x37, 0xe5, 0x95, + 0x39, 0x88, 0xd9, 0x94, 0xb9, 0xd6, 0x5a, 0xeb, + 0xd7, 0xcd, 0xd5, 0x30, 0x8a, 0xd6, 0xfe, 0x48, + 0xb2, 0x4a, 0x6a, 0x81, 0x0e, 0xe5, 0xf0, 0x7d, + 0x8b, 0x68, 0x34, 0xcc, 0x3a, 0x6a, 0xfc, 0x53, + 0x8e, 0xfa, 0xc1, }; +const unsigned int root_pub_der_len = 91; +#else /* MCUBOOT_SIGN_EC384 */ +const unsigned char root_pub_der[] = { + 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, + 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, + 0x0c, 0x76, 0xca, 0xae, 0x72, 0x3a, 0xa5, 0xe8, + 0xf0, 0xd4, 0xf1, 0x16, 0xb5, 0x02, 0xef, 0x77, + 0xa1, 0x1b, 0x93, 0x61, 0x78, 0xc0, 0x09, 0x26, + 0x7b, 0x3b, 0x40, 0x9c, 0xee, 0x49, 0x85, 0xe0, + 0xc9, 0x4f, 0xe7, 0xf2, 0xba, 0x97, 0x6c, 0xf3, + 0x82, 0x65, 0x14, 0x2c, 0xf5, 0x0c, 0x73, 0x33, + 0x4d, 0x32, 0xe7, 0x9b, 0xd3, 0x42, 0xcc, 0x95, + 0x5a, 0xe5, 0xe2, 0xf5, 0xf4, 0x6e, 0x45, 0xe0, + 0xed, 0x20, 0x35, 0x5c, 0xaf, 0x52, 0x35, 0x81, + 0xd4, 0xdc, 0x9c, 0xe3, 0x9e, 0x22, 0x3e, 0xfb, + 0x3f, 0x22, 0x10, 0xda, 0x70, 0x03, 0x37, 0xad, + 0xa8, 0xf2, 0x48, 0xfe, 0x3a, 0x60, 0x69, 0xa5, +}; +const unsigned int root_pub_der_len = 120; +#endif /* MCUBOOT_SIGN_EC384 */ +#elif defined(MCUBOOT_SIGN_ED25519) +#define HAVE_KEYS +const unsigned char root_pub_der[] = { + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x70, 0x03, 0x21, 0x00, 0xd4, 0xb3, 0x1b, 0xa4, + 0x9a, 0x3a, 0xdd, 0x3f, 0x82, 0x5d, 0x10, 0xca, + 0x7f, 0x31, 0xb5, 0x0b, 0x0d, 0xe8, 0x7f, 0x37, + 0xcc, 0xc4, 0x9f, 0x1a, 0x40, 0x3a, 0x5c, 0x13, + 0x20, 0xff, 0xb4, 0xe0, +}; +const unsigned int root_pub_der_len = 44; +#endif + +#if defined(HAVE_KEYS) +const struct bootutil_key bootutil_keys[] = { + { + .key = root_pub_der, + .len = &root_pub_der_len, + }, +}; +const int bootutil_key_cnt = 1; +#endif + +#if defined(MCUBOOT_ENCRYPT_RSA) +unsigned char enc_key[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xb4, 0x26, 0x14, 0x49, 0x3d, 0x16, 0x13, 0x3a, 0x6d, 0x9c, 0x84, 0xa9, + 0x8b, 0x6a, 0x10, 0x20, 0x61, 0xef, 0x48, 0x04, 0xa4, 0x4b, 0x24, 0xf3, + 0x00, 0x32, 0xac, 0x22, 0xe0, 0x30, 0x27, 0x70, 0x18, 0xe5, 0x55, 0xc8, + 0xb8, 0x05, 0x34, 0x03, 0xb0, 0xf8, 0xa5, 0x96, 0xd2, 0x48, 0x58, 0xef, + 0x70, 0xb0, 0x09, 0xdb, 0xe3, 0x58, 0x62, 0xef, 0x99, 0x63, 0x01, 0xb2, + 0x89, 0xc4, 0xb3, 0xf6, 0x9e, 0x62, 0xbf, 0x4d, 0xc2, 0x8a, 0xd0, 0xc9, + 0x4d, 0x43, 0xa3, 0xd8, 0xe5, 0x1d, 0xec, 0x62, 0x63, 0x08, 0xe2, 0x20, + 0xa5, 0xfc, 0x78, 0xd0, 0x3e, 0x74, 0xc8, 0xa4, 0x1b, 0x36, 0xad, 0x7b, + 0xf5, 0x06, 0xae, 0x4d, 0x51, 0x9b, 0x40, 0xce, 0x30, 0x4f, 0x6c, 0xea, + 0xf9, 0xe9, 0x74, 0xea, 0x06, 0xee, 0x9c, 0xe4, 0x14, 0x68, 0x20, 0xb9, + 0x3d, 0xe7, 0x11, 0x14, 0x8b, 0x25, 0xa3, 0xff, 0x4c, 0x8a, 0xf3, 0x53, + 0xee, 0x6b, 0x3e, 0xef, 0x34, 0xcd, 0x6a, 0x3f, 0x62, 0x68, 0xc0, 0xff, + 0x78, 0x4c, 0xb0, 0xc3, 0xe6, 0x96, 0x61, 0xfc, 0x1f, 0x18, 0xf1, 0x7a, + 0x82, 0xe2, 0x8f, 0x35, 0xa8, 0x2b, 0x86, 0x16, 0xa4, 0x46, 0xfb, 0xac, + 0x7e, 0x41, 0xdb, 0x02, 0x05, 0x91, 0x6d, 0xdf, 0xc1, 0xde, 0x13, 0x95, + 0x9c, 0xf9, 0x9e, 0x5e, 0x72, 0xba, 0xa7, 0x25, 0x93, 0xfb, 0xdc, 0xe8, + 0xab, 0x86, 0x45, 0x88, 0x47, 0x2d, 0xed, 0xee, 0xee, 0x97, 0x9e, 0xce, + 0x5d, 0x9b, 0x04, 0x04, 0x40, 0x7c, 0xcb, 0x7c, 0x3d, 0x2c, 0x74, 0xab, + 0xa4, 0xcc, 0x64, 0xa3, 0x5c, 0x95, 0x3d, 0xd4, 0xa2, 0xdc, 0x92, 0xb2, + 0xc8, 0x18, 0xcb, 0xf9, 0x00, 0x39, 0x81, 0x8f, 0x8f, 0x40, 0xc2, 0xdf, + 0x99, 0x29, 0xac, 0x8a, 0xc2, 0x3b, 0xd8, 0xa4, 0xf2, 0xad, 0xaf, 0x74, + 0xc0, 0x11, 0xc7, 0x99, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x42, 0x47, 0x80, 0x4f, 0x31, 0xda, 0x5d, 0x58, 0xb1, 0xdb, 0x54, + 0x33, 0xcc, 0xc7, 0x49, 0x07, 0xa1, 0x00, 0x98, 0x4e, 0x9c, 0xe3, 0xc8, + 0xc4, 0x5e, 0xde, 0x45, 0xd6, 0xcf, 0x04, 0xe8, 0x7d, 0xa5, 0xab, 0x3a, + 0xd4, 0x8e, 0x5f, 0xdb, 0xb3, 0x3f, 0xf9, 0x3b, 0x73, 0x32, 0x0a, 0xcc, + 0x2d, 0xcc, 0x17, 0xf8, 0x88, 0x9e, 0x2c, 0x76, 0xba, 0x10, 0x85, 0x0c, + 0xaa, 0xd3, 0x65, 0x3b, 0x91, 0x10, 0xd4, 0xe3, 0xed, 0x88, 0x15, 0xea, + 0x9b, 0x25, 0x82, 0x2d, 0x56, 0x2f, 0x75, 0xc2, 0xf2, 0xaf, 0xdd, 0x24, + 0xd5, 0x3e, 0x3c, 0x95, 0x76, 0x88, 0x84, 0x0f, 0x0d, 0xd1, 0xb5, 0x5c, + 0x3e, 0xae, 0xf7, 0xb6, 0x49, 0x5c, 0x2c, 0xf2, 0xba, 0xe9, 0xab, 0x4f, + 0x37, 0x64, 0x9b, 0x30, 0x18, 0xaa, 0x54, 0x40, 0x04, 0xea, 0x3d, 0x25, + 0x4d, 0x02, 0x29, 0x71, 0x6f, 0x4d, 0x82, 0x9b, 0xc3, 0x44, 0x2a, 0x9d, + 0x0c, 0x98, 0xd3, 0xc8, 0x15, 0x0d, 0x04, 0x93, 0x60, 0x30, 0xc7, 0x5e, + 0x79, 0xea, 0x53, 0x9d, 0xc0, 0x0e, 0x81, 0xac, 0x90, 0xbc, 0x9e, 0x1e, + 0xd2, 0x28, 0x0f, 0x10, 0xf5, 0x1f, 0xdf, 0x38, 0x7f, 0x8a, 0x90, 0x8d, + 0x49, 0x07, 0x7d, 0x78, 0xcb, 0xa7, 0xef, 0x92, 0x6d, 0x3b, 0x13, 0x95, + 0x9b, 0xba, 0x83, 0xc6, 0xb3, 0x71, 0x25, 0x27, 0x07, 0x99, 0x54, 0x82, + 0x3d, 0xec, 0xc5, 0xf8, 0xb4, 0xa0, 0x38, 0x7a, 0x59, 0x6a, 0x0b, 0xca, + 0x69, 0x6c, 0x17, 0xa4, 0x18, 0xe0, 0xb4, 0xaa, 0x89, 0x99, 0x8f, 0xcb, + 0x71, 0x34, 0x09, 0x1b, 0x6e, 0xe6, 0x87, 0x00, 0xb5, 0xba, 0x70, 0x8a, + 0x29, 0x3d, 0x9a, 0x06, 0x18, 0x2d, 0x66, 0x5e, 0x61, 0x37, 0xeb, 0xdd, + 0x5e, 0xc8, 0x28, 0x92, 0x05, 0x30, 0xfd, 0xb8, 0x65, 0xb1, 0x7f, 0xbf, + 0x2d, 0x55, 0x12, 0x91, 0xc1, 0x02, 0x81, 0x81, 0x00, 0xda, 0x65, 0xda, + 0x38, 0x7c, 0x18, 0xfb, 0x00, 0x11, 0x60, 0xeb, 0x37, 0x65, 0xb8, 0x83, + 0x62, 0x88, 0xc4, 0x3a, 0x4e, 0x64, 0x6a, 0xf3, 0x3e, 0x4e, 0xc0, 0x34, + 0x19, 0x8a, 0xcb, 0x4a, 0xca, 0x2f, 0x5d, 0x50, 0x7a, 0xac, 0xf7, 0x9e, + 0x87, 0x5a, 0xfc, 0x4d, 0x49, 0xd7, 0xf9, 0x21, 0xf5, 0x0b, 0x6f, 0x57, + 0x41, 0x3d, 0x8f, 0xb8, 0xec, 0x7f, 0xcc, 0x92, 0x09, 0xbe, 0xd3, 0xa4, + 0xc3, 0x14, 0x85, 0x21, 0x5d, 0x05, 0xa3, 0xaa, 0x20, 0xf6, 0x62, 0x44, + 0x50, 0x03, 0x5e, 0x53, 0x4a, 0xcd, 0x6a, 0xb6, 0x65, 0x8e, 0x4e, 0x4b, + 0x3f, 0x25, 0xc6, 0x16, 0x31, 0xf5, 0x99, 0x13, 0x77, 0x42, 0xda, 0xdc, + 0x70, 0x4d, 0x65, 0xb0, 0x99, 0x0f, 0xdf, 0x5a, 0xb1, 0x45, 0xf0, 0xb9, + 0x8e, 0xa0, 0xae, 0x4f, 0x4d, 0x65, 0x09, 0x84, 0xb5, 0x38, 0x29, 0xbf, + 0x69, 0xe0, 0x88, 0x1f, 0x27, 0x02, 0x81, 0x81, 0x00, 0xd3, 0x2a, 0x59, + 0xec, 0x28, 0xc3, 0x0d, 0x4f, 0x92, 0x96, 0xca, 0x67, 0x94, 0xfc, 0x2e, + 0xa6, 0x86, 0x68, 0x45, 0x53, 0x92, 0xcc, 0x86, 0x7f, 0x8a, 0xe1, 0x5d, + 0xe8, 0x1d, 0x9e, 0xbb, 0x1e, 0x00, 0x26, 0x1d, 0x80, 0x12, 0xff, 0x9c, + 0x11, 0x0a, 0xbd, 0xa6, 0xc3, 0x8d, 0x48, 0xda, 0xfc, 0x10, 0xf7, 0x7a, + 0x16, 0x07, 0x15, 0xa0, 0x3a, 0xd3, 0x94, 0xfb, 0x52, 0x87, 0x39, 0xee, + 0xe7, 0xc4, 0x26, 0x49, 0x16, 0xc6, 0xc0, 0x83, 0x25, 0xbf, 0x6a, 0x4e, + 0x8c, 0x0b, 0x10, 0x85, 0x66, 0xab, 0x7e, 0xae, 0xac, 0x4c, 0x69, 0x3c, + 0x44, 0xeb, 0xcd, 0xe9, 0xf6, 0x64, 0x8b, 0x4a, 0xd8, 0x6a, 0x4d, 0x6d, + 0x47, 0xa9, 0xb8, 0x55, 0x72, 0xc1, 0xfd, 0xf4, 0x81, 0x4c, 0x66, 0xbe, + 0x49, 0xf2, 0x75, 0x4f, 0x80, 0xf1, 0x20, 0x38, 0xb8, 0x6a, 0x1b, 0x75, + 0x41, 0x30, 0x0f, 0x1b, 0x3f, 0x02, 0x81, 0x80, 0x09, 0x35, 0xfa, 0x7a, + 0x1f, 0x61, 0xbe, 0x54, 0x46, 0x67, 0x5c, 0x04, 0x3e, 0x1a, 0x06, 0x10, + 0x85, 0xcc, 0x20, 0xd9, 0x65, 0x8a, 0xcd, 0x2f, 0x77, 0x8a, 0xcb, 0xa7, + 0xb8, 0x1e, 0xd2, 0xcc, 0xac, 0x2a, 0xb7, 0x56, 0x35, 0x2d, 0x4c, 0x56, + 0x51, 0x14, 0x0a, 0xfe, 0x6e, 0x49, 0x67, 0x91, 0x3a, 0x26, 0x3b, 0xfb, + 0xd8, 0x68, 0xd3, 0x57, 0xc6, 0x1c, 0x0e, 0x9c, 0xb2, 0x9b, 0xa2, 0x7b, + 0x47, 0xc6, 0x45, 0x9d, 0xf2, 0xba, 0xf0, 0x55, 0xeb, 0x8e, 0x41, 0x6b, + 0x4e, 0x79, 0x0f, 0xf2, 0x3b, 0xaf, 0xa0, 0x79, 0xb0, 0x02, 0xc5, 0x51, + 0xa8, 0x7a, 0x2e, 0x3d, 0x75, 0x2a, 0x3b, 0x93, 0xf0, 0x11, 0xe2, 0xf2, + 0x29, 0x91, 0x7c, 0x5d, 0x38, 0x3a, 0x27, 0x4d, 0x0a, 0xb2, 0x18, 0x61, + 0x57, 0x8d, 0x82, 0x72, 0xb5, 0x2c, 0x2d, 0x98, 0xa7, 0x01, 0xbb, 0xbc, + 0xef, 0x67, 0x4e, 0x49, 0x02, 0x81, 0x81, 0x00, 0xb2, 0x70, 0x53, 0x54, + 0x70, 0x8d, 0x82, 0xad, 0xff, 0x1d, 0x55, 0x24, 0x7a, 0x8d, 0x2f, 0x8e, + 0xa0, 0x7d, 0x74, 0x37, 0xcf, 0x10, 0xed, 0x86, 0xd1, 0x80, 0xe7, 0xad, + 0xc1, 0x79, 0xe4, 0x7c, 0xd1, 0x7b, 0x63, 0xea, 0x5a, 0x23, 0x8d, 0x6a, + 0x09, 0x3d, 0x81, 0xb2, 0x35, 0xad, 0x9e, 0xfe, 0xea, 0x07, 0x76, 0x2f, + 0x2f, 0x05, 0x63, 0x44, 0xd2, 0x8e, 0x4e, 0x61, 0xca, 0xcb, 0x75, 0xca, + 0x7b, 0xc2, 0x2e, 0x79, 0x04, 0xb2, 0xa1, 0x20, 0x40, 0xc4, 0x40, 0x63, + 0xae, 0xe5, 0xe3, 0x14, 0x83, 0x4e, 0xa5, 0xa4, 0x0b, 0x5d, 0xd2, 0x04, + 0x1b, 0x8f, 0x01, 0x69, 0xa8, 0x44, 0xdc, 0x96, 0x4c, 0x1d, 0xe9, 0x7e, + 0x69, 0x38, 0xcf, 0x5c, 0x0d, 0xf9, 0xdf, 0xa7, 0x73, 0x3c, 0x4f, 0x08, + 0x85, 0xce, 0x03, 0xc4, 0xdd, 0xfd, 0x70, 0x70, 0xc5, 0x99, 0x36, 0x58, + 0x43, 0x98, 0x40, 0x59, 0x02, 0x81, 0x81, 0x00, 0xd5, 0xaa, 0xfb, 0xec, + 0x8d, 0xc6, 0xdd, 0xfa, 0x2b, 0x5a, 0x24, 0xd0, 0xda, 0x58, 0xbd, 0x87, + 0x92, 0x1a, 0x29, 0x62, 0x13, 0x1d, 0x4b, 0x79, 0x1b, 0xbe, 0x79, 0x7d, + 0xad, 0x79, 0xca, 0x17, 0x75, 0xda, 0xe8, 0x32, 0xe8, 0xa0, 0x9e, 0xa8, + 0x77, 0x53, 0xac, 0x38, 0xd6, 0xeb, 0xe6, 0x22, 0x65, 0xc4, 0xaa, 0x4c, + 0xc8, 0xd0, 0x33, 0x1a, 0x1e, 0xbe, 0xbd, 0x73, 0x09, 0x4a, 0xfa, 0x85, + 0x5c, 0xf3, 0x0c, 0x9c, 0x81, 0x56, 0x30, 0xa7, 0xf7, 0x9b, 0xf4, 0x92, + 0x9c, 0x6b, 0x93, 0x6a, 0x00, 0x33, 0xdc, 0x2f, 0x54, 0x1e, 0x78, 0xd4, + 0x97, 0xec, 0x24, 0xa2, 0xdb, 0x3d, 0x03, 0x33, 0x09, 0xb2, 0x2c, 0x03, + 0x05, 0x40, 0xde, 0x52, 0xf2, 0x9b, 0xfa, 0x00, 0x8d, 0x4b, 0xfe, 0x5b, + 0x9b, 0x9c, 0x73, 0xad, 0xfb, 0x7a, 0x00, 0x42, 0x62, 0x9e, 0xa0, 0x95, + 0x55, 0x50, 0x32, 0x87 +}; +static unsigned int enc_key_len = 1192; +const struct bootutil_key bootutil_enc_key = { + .key = enc_key, + .len = &enc_key_len, +}; +#endif + +#if defined(MCUBOOT_ENCRYPT_KW) +#if defined(MCUBOOT_AES_256) +unsigned char enc_key[] = { + 0xE4, 0x5C, 0x51, 0x46, 0xD2, 0x1C, 0x82, 0x35, 0xCC, 0x1A, 0x19, 0xAF, + 0xA1, 0xF2, 0xAA, 0x20, 0xC8, 0x8C, 0x7F, 0x40, 0x6C, 0xDB, 0x22, 0xAA, + 0x6A, 0xB5, 0xCB, 0xAA, 0xF8, 0xB1, 0x5B, 0xB4 +}; +static unsigned int enc_key_len = 32; +#else +unsigned char enc_key[] = { + 0xd1, 0x5a, 0x04, 0x95, 0xc4, 0xc2, 0xa8, 0xff, 0x30, 0x78, 0xce, 0x49, + 0xb5, 0xfc, 0xb2, 0xdd +}; +static unsigned int enc_key_len = 16; +#endif +const struct bootutil_key bootutil_enc_key = { + .key = enc_key, + .len = &enc_key_len, +}; +#endif + +#if defined(MCUBOOT_ENCRYPT_EC256) +unsigned char enc_key[] = { + 0x30, 0x81, 0x43, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x04, 0x29, 0x30, 0x27, 0x02, 0x01, 0x01, 0x04, 0x20, + 0xf6, 0x1e, 0x51, 0x9d, 0xf8, 0xfa, 0xdd, 0xa1, 0xb7, 0xd9, 0xa9, 0x64, + 0x64, 0x3b, 0x54, 0xd0, 0x3d, 0xd0, 0x1f, 0xe5, 0x78, 0xd9, 0x17, 0x98, + 0xa5, 0x28, 0xca, 0xcc, 0x6b, 0x67, 0x9e, 0x06, 0xa1, 0x44, + +}; +static unsigned int enc_key_len = 70; +const struct bootutil_key bootutil_enc_key = { + .key = enc_key, + .len = &enc_key_len, +}; +#endif + +#if defined(MCUBOOT_ENCRYPT_X25519) +unsigned char enc_key[] = { + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, + 0x04, 0x22, 0x04, 0x20, 0x28, 0x80, 0x2f, 0xef, 0xef, 0x82, 0x95, 0x50, + 0xf1, 0x41, 0x93, 0x03, 0x6c, 0x1b, 0xb9, 0x49, 0x6c, 0x51, 0xe5, 0x26, + 0x87, 0x8f, 0x77, 0x07, 0xf8, 0xb4, 0x1f, 0x04, 0x45, 0x6d, 0x84, 0x4f, +}; +static unsigned int enc_key_len = 48; +const struct bootutil_key bootutil_enc_key = { + .key = enc_key, + .len = &enc_key_len, +}; +#endif diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_assert.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_assert.h new file mode 100644 index 0000000..3d33d24 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_assert.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __MCUBOOT_ASSERT_H__ +#define __MCUBOOT_ASSERT_H__ + +#include +void sim_assert(int, const char *test, const char *, unsigned int, const char *); +#define ASSERT(x) sim_assert((x), #x, __FILE__, __LINE__, __func__) + +#endif /* __MCUBOOT_ASSERT_H__ */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_config.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_config.h new file mode 100644 index 0000000..97a0ac5 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_config.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_CONFIG_H__ +#define __MCUBOOT_CONFIG_H__ + +/* + * This file is included by the simulator, but we don't want to + * define almost anything here. + * + * Instead of using mcuboot_config.h, the simulator adds MCUBOOT_xxx + * configuration flags to the compiler command lines based on the + * values of environment variables. However, the file still must + * exist, or bootutil won't build. + */ + +#define MCUBOOT_WATCHDOG_FEED() \ + do { \ + } while (0) + +#define MCUBOOT_CPU_IDLE() \ + do { \ + } while (0) + +#endif /* __MCUBOOT_CONFIG_H__ */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_logging.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_logging.h new file mode 100644 index 0000000..bb4ff30 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/mcuboot_config/mcuboot_logging.h @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __MCUBOOT_LOGGING_H__ +#define __MCUBOOT_LOGGING_H__ + +#include + +#define MCUBOOT_LOG_LEVEL_OFF 0 +#define MCUBOOT_LOG_LEVEL_ERROR 1 +#define MCUBOOT_LOG_LEVEL_WARNING 2 +#define MCUBOOT_LOG_LEVEL_INFO 3 +#define MCUBOOT_LOG_LEVEL_DEBUG 4 +#define MCUBOOT_LOG_LEVEL_SIM 5 /* RUST_LOG=trace */ + +/* + * The compiled log level determines the maximum level that can be + * printed. Messages at or below this level can be printed, provided + * they are also enabled through the Rust logging system, such as by + * setting RUST_LOG to bootsim::api=info. + */ +#ifndef MCUBOOT_LOG_LEVEL +#define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_DEBUG +#endif + +#define MCUBOOT_LOG_MODULE_DECLARE(domain) /* ignore */ +#define MCUBOOT_LOG_MODULE_REGISTER(domain) /* ignore */ + +int sim_log_enabled(int level); + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_ERROR +#define MCUBOOT_LOG_ERR(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_ERROR)) { \ + fprintf(stderr, "[ERR] " _fmt "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_ERR(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_WARNING +#define MCUBOOT_LOG_WRN(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_WARNING)) { \ + fprintf(stderr, "[WRN] " _fmt "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_WRN(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_INFO +#define MCUBOOT_LOG_INF(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_INFO)) { \ + fprintf(stderr, "[INF] " _fmt "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_INF(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_DEBUG +#define MCUBOOT_LOG_DBG(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_DEBUG)) { \ + fprintf(stderr, "[DBG] " _fmt "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_DBG(...) IGNORE(__VA_ARGS__) +#endif + +#if MCUBOOT_LOG_LEVEL >= MCUBOOT_LOG_LEVEL_SIM +#define MCUBOOT_LOG_SIM(_fmt, ...) \ + do { \ + if (sim_log_enabled(MCUBOOT_LOG_LEVEL_SIM)) { \ + fprintf(stderr, "[SIM] " _fmt "\n", ##__VA_ARGS__); \ + fflush(stderr); \ + } \ + } while (0) +#else +#define MCUBOOT_LOG_SIM(...) IGNORE(__VA_ARGS__) +#endif + +#endif /* __MCUBOOT_LOGGING_H__ */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_heap.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_heap.h new file mode 100644 index 0000000..dfe162a --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_heap.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_OS_HEAP_ +#define H_OS_HEAP_ + +#endif diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_malloc.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_malloc.h new file mode 100644 index 0000000..4139d4a --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/os/os_malloc.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_OS_MALLOC_ +#define H_OS_MALLOC_ + +#endif diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/psa_crypto_init_stub.c b/bootloader/mcuboot/sim/mcuboot-sys/csupport/psa_crypto_init_stub.c new file mode 100644 index 0000000..1362ac3 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/psa_crypto_init_stub.c @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Arm Limited + */ + +/* This file, and the methods within are required when PSA Crypto API is enabled + * (--features psa-crypto-api), but the selected combination of features does + * not rely on any PSA Crypto APIs, and will not be adding any of them to the build. + */ + +#include + +int psa_crypto_init() +{ + BOOT_LOG_SIM("psa_crypto_init() is being stubbed.\n"); + return 0; +} + +void mbedtls_test_enable_insecure_external_rng(){ + BOOT_LOG_SIM("mbedtls_test_enable_insecure_external_rng() is being stubbed.\n"); +} diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/run.c b/bootloader/mcuboot/sim/mcuboot-sys/csupport/run.c new file mode 100644 index 0000000..9868996 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/run.c @@ -0,0 +1,543 @@ +/* Run the boot image. */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../../../boot/bootutil/src/bootutil_priv.h" +#include "bootsim.h" + +#ifdef MCUBOOT_ENCRYPT_RSA +#include "mbedtls/rsa.h" +#include "mbedtls/asn1.h" +#endif + +#ifdef MCUBOOT_ENCRYPT_KW +#include "mbedtls/nist_kw.h" +#endif + +#define BOOT_LOG_LEVEL BOOT_LOG_LEVEL_ERROR +#include +#include "bootutil/crypto/common.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +struct area_desc; +extern struct area_desc *sim_get_flash_areas(void); +extern void sim_set_flash_areas(struct area_desc *areas); +extern void sim_reset_flash_areas(void); + +struct sim_context; +extern struct sim_context *sim_get_context(void); +extern void sim_set_context(struct sim_context *ctx); +extern void sim_reset_context(void); + +extern int sim_flash_erase(uint8_t flash_id, uint32_t offset, uint32_t size); +extern int sim_flash_read(uint8_t flash_id, uint32_t offset, uint8_t *dest, + uint32_t size); +extern int sim_flash_write(uint8_t flash_id, uint32_t offset, const uint8_t *src, + uint32_t size); +extern uint32_t sim_flash_align(uint8_t flash_id); +extern uint8_t sim_flash_erased_val(uint8_t flash_id); + +struct sim_context { + int flash_counter; + int jumped; + uint8_t c_asserts; + uint8_t c_catch_asserts; + jmp_buf boot_jmpbuf; +}; + +#ifdef MCUBOOT_ENCRYPT_RSA +static int +parse_pubkey(mbedtls_rsa_context *ctx, uint8_t **p, uint8_t *end) +{ + int rc; + size_t len; + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return -1; + } + + if (*p + len != end) { + return -2; + } + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return -3; + } + + *p += len; + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_BIT_STRING)) != 0) { + return -4; + } + + if (**p != MBEDTLS_ASN1_PRIMITIVE) { + return -5; + } + + *p += 1; + + if ((rc = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return -6; + } + + if (mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(N)) != 0) { + return -7; + } + + if (mbedtls_asn1_get_mpi(p, end, &ctx->MBEDTLS_CONTEXT_MEMBER(E)) != 0) { + return -8; + } + + ctx->MBEDTLS_CONTEXT_MEMBER(len) = mbedtls_mpi_size(&ctx->MBEDTLS_CONTEXT_MEMBER(N)); + + if (*p != end) { + return -9; + } + + if (mbedtls_rsa_check_pubkey(ctx) != 0) { + return -10; + } + + return 0; +} + +static int +fake_rng(void *p_rng, unsigned char *output, size_t len) +{ + size_t i; + + (void)p_rng; + for (i = 0; i < len; i++) { + output[i] = (char)i; + } + + return 0; +} +#endif + +int mbedtls_platform_set_calloc_free(void * (*calloc_func)(size_t, size_t), + void (*free_func)(void *)); + +int rsa_oaep_encrypt_(const uint8_t *pubkey, unsigned pubkey_len, + const uint8_t *seckey, unsigned seckey_len, + uint8_t *encbuf) +{ +#ifdef MCUBOOT_ENCRYPT_RSA + mbedtls_rsa_context ctx; + uint8_t *cp; + uint8_t *cpend; + int rc; + + mbedtls_platform_set_calloc_free(calloc, free); + +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + mbedtls_rsa_init(&ctx); + mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); +#else + mbedtls_rsa_init(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); +#endif + + cp = (uint8_t *)pubkey; + cpend = cp + pubkey_len; + + rc = parse_pubkey(&ctx, &cp, cpend); + if (rc) { + goto done; + } + +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + rc = mbedtls_rsa_rsaes_oaep_encrypt(&ctx, fake_rng, NULL, + NULL, 0, seckey_len, seckey, encbuf); +#else + rc = mbedtls_rsa_rsaes_oaep_encrypt(&ctx, fake_rng, NULL, MBEDTLS_RSA_PUBLIC, + NULL, 0, seckey_len, seckey, encbuf); +#endif + if (rc) { + goto done; + } + +done: + mbedtls_rsa_free(&ctx); + return rc; + +#else + (void)pubkey; + (void)pubkey_len; + (void)seckey; + (void)seckey_len; + (void)encbuf; + return 0; +#endif +} + +int kw_encrypt_(const uint8_t *kek, const uint8_t *seckey, uint8_t *encbuf) +{ +#ifdef MCUBOOT_ENCRYPT_KW +#ifdef MCUBOOT_AES_256 + int key_len = 256; + int out_size = 40; + int in_len = 32; +#else + int key_len = 128; + int out_size = 24; + int in_len = 16; +#endif + mbedtls_nist_kw_context kw; + size_t olen; + int rc; + + mbedtls_platform_set_calloc_free(calloc, free); + + mbedtls_nist_kw_init(&kw); + + rc = mbedtls_nist_kw_setkey(&kw, MBEDTLS_CIPHER_ID_AES, kek, key_len, 1); + if (rc) { + goto done; + } + + rc = mbedtls_nist_kw_wrap(&kw, MBEDTLS_KW_MODE_KW, seckey, in_len, encbuf, + &olen, out_size); + +done: + mbedtls_nist_kw_free(&kw); + return rc; + +#else + (void)kek; + (void)seckey; + (void)encbuf; + return 0; +#endif +} + +uint32_t flash_area_align(const struct flash_area *area) +{ + return sim_flash_align(area->fa_device_id); +} + +uint8_t flash_area_erased_val(const struct flash_area *area) +{ + return sim_flash_erased_val(area->fa_device_id); +} + +struct area { + struct flash_area whole; + struct flash_area *areas; + uint32_t num_areas; + uint8_t id; +}; + +struct area_desc { + struct area slots[16]; + uint32_t num_slots; +}; + +int invoke_boot_go(struct sim_context *ctx, struct area_desc *adesc, + struct boot_rsp *rsp, int image_id) +{ + int res; + struct boot_loader_state *state; + +#if defined(MCUBOOT_SIGN_RSA) || \ + (defined(MCUBOOT_SIGN_EC256) && defined(MCUBOOT_USE_MBED_TLS)) ||\ + (defined(MCUBOOT_ENCRYPT_EC256) && defined(MCUBOOT_USE_MBED_TLS)) ||\ + (defined(MCUBOOT_ENCRYPT_X25519) && defined(MCUBOOT_USE_MBED_TLS)) + mbedtls_platform_set_calloc_free(calloc, free); +#endif + + state = malloc(sizeof(struct boot_loader_state)); + + sim_set_flash_areas(adesc); + sim_set_context(ctx); + + if (setjmp(ctx->boot_jmpbuf) == 0) { + boot_state_clear(state); + +#if BOOT_IMAGE_NUMBER > 1 + if (image_id >= 0) { + memset(state->img_mask, 1, sizeof(state->img_mask)); + state->img_mask[image_id] = 0; + } +#else + (void) image_id; +#endif /* BOOT_IMAGE_NUMBER > 1 */ + + res = context_boot_go(state, rsp); + sim_reset_flash_areas(); + sim_reset_context(); + free(state); + /* printf("boot_go off: %d (0x%08x)\n", res, rsp.br_image_off); */ + return res; + } else { + sim_reset_flash_areas(); + sim_reset_context(); + free(state); + return -0x13579; + } +} + +void *os_malloc(size_t size) +{ + // printf("os_malloc 0x%x bytes\n", size); + return malloc(size); +} + +int flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + switch (slot) { + case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); + case 1: return FLASH_AREA_IMAGE_SECONDARY(image_index); + case 2: return FLASH_AREA_IMAGE_SCRATCH; + } + + printf("Image flash area ID not found\n"); + return -1; /* flash_area_open will fail on that */ +} + +int flash_area_open(uint8_t id, const struct flash_area **area) +{ + uint32_t i; + struct area_desc *flash_areas; + + flash_areas = sim_get_flash_areas(); + for (i = 0; i < flash_areas->num_slots; i++) { + if (flash_areas->slots[i].id == id) + break; + } + if (i == flash_areas->num_slots) { + printf("Unsupported area\n"); + abort(); + } + + /* Unsure if this is right, just returning the first area. */ + *area = &flash_areas->slots[i].whole; + return 0; +} + +void flash_area_close(const struct flash_area *area) +{ + (void)area; +} + +/* + * Read/write/erase. Offset is relative from beginning of flash area. + */ +int flash_area_read(const struct flash_area *area, uint32_t off, void *dst, + uint32_t len) +{ + BOOT_LOG_SIM("%s: area=%d, off=%x, len=%x", + __func__, area->fa_id, off, len); + return sim_flash_read(area->fa_device_id, area->fa_off + off, dst, len); +} + +int flash_area_write(const struct flash_area *area, uint32_t off, const void *src, + uint32_t len) +{ + BOOT_LOG_SIM("%s: area=%d, off=%x, len=%x", __func__, + area->fa_id, off, len); + struct sim_context *ctx = sim_get_context(); + if (--(ctx->flash_counter) == 0) { + ctx->jumped++; + longjmp(ctx->boot_jmpbuf, 1); + } + return sim_flash_write(area->fa_device_id, area->fa_off + off, src, len); +} + +int flash_area_erase(const struct flash_area *area, uint32_t off, uint32_t len) +{ + BOOT_LOG_SIM("%s: area=%d, off=%x, len=%x", __func__, + area->fa_id, off, len); + struct sim_context *ctx = sim_get_context(); + if (--(ctx->flash_counter) == 0) { + ctx->jumped++; + longjmp(ctx->boot_jmpbuf, 1); + } + return sim_flash_erase(area->fa_device_id, area->fa_off + off, len); +} + +int flash_area_to_sectors(int idx, int *cnt, struct flash_area *ret) +{ + uint32_t i; + struct area *slot; + struct area_desc *flash_areas; + + flash_areas = sim_get_flash_areas(); + for (i = 0; i < flash_areas->num_slots; i++) { + if (flash_areas->slots[i].id == idx) + break; + } + if (i == flash_areas->num_slots) { + printf("Unsupported area\n"); + abort(); + } + + slot = &flash_areas->slots[i]; + + if (slot->num_areas > (uint32_t)*cnt) { + printf("Too many areas in slot\n"); + abort(); + } + + *cnt = slot->num_areas; + memcpy(ret, slot->areas, slot->num_areas * sizeof(struct flash_area)); + + return 0; +} + +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors) +{ + uint32_t i; + struct area *slot; + struct area_desc *flash_areas; + + flash_areas = sim_get_flash_areas(); + for (i = 0; i < flash_areas->num_slots; i++) { + if (flash_areas->slots[i].id == fa_id) + break; + } + if (i == flash_areas->num_slots) { + printf("Unsupported area\n"); + abort(); + } + + slot = &flash_areas->slots[i]; + + if (slot->num_areas > *count) { + printf("Too many areas in slot\n"); + abort(); + } + + for (i = 0; i < slot->num_areas; i++) { + sectors[i].fs_off = slot->areas[i].fa_off - + slot->whole.fa_off; + sectors[i].fs_size = slot->areas[i].fa_size; + } + *count = slot->num_areas; + + return 0; +} + +int flash_area_id_to_multi_image_slot(int image_index, int area_id) +{ + if (area_id == FLASH_AREA_IMAGE_PRIMARY(image_index)) { + return 0; + } + if (area_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) { + return 1; + } + + printf("Unsupported image area ID\n"); + abort(); +} + +int flash_area_id_from_image_slot(int slot) { + /* For single image cases, just use the first image. */ + return flash_area_id_from_multi_image_slot(0, slot); +} + +int flash_area_sector_from_off(uint32_t off, struct flash_sector *sector) +{ + uint32_t i, sec_off, sec_size; + struct area *slot; + struct area_desc *flash_areas; + + flash_areas = sim_get_flash_areas(); + for (i = 0; i < flash_areas->num_slots; i++) { + if (flash_areas->slots[i].id == FLASH_AREA_ID(image_0)) + break; + } + + if (i == flash_areas->num_slots) { + printf("Unsupported area\n"); + abort(); + } + + slot = &flash_areas->slots[i]; + + for (i = 0; i < slot->num_areas; i++) { + sec_off = slot->areas[i].fa_off - slot->whole.fa_off; + sec_size = slot->areas[i].fa_size; + + if (off >= sec_off && off < (sec_off + sec_size)) { + sector->fs_off = sec_off; + sector->fs_size = sec_size; + break; + } + } + + return (i < slot->num_areas) ? 0 : -1; +} + +int flash_area_get_sector(const struct flash_area *fa, uint32_t off, + struct flash_sector *sector) +{ + uint32_t i, sec_off, sec_size; + struct area *slot; + struct area_desc *flash_areas; + + flash_areas = sim_get_flash_areas(); + for (i = 0; i < flash_areas->num_slots; i++) { + if (&flash_areas->slots[i].whole == fa) + break; + } + + if (i == flash_areas->num_slots) { + printf("Unsupported area\n"); + abort(); + } + + slot = &flash_areas->slots[i]; + + for (i = 0; i < slot->num_areas; i++) { + sec_off = slot->areas[i].fa_off - slot->whole.fa_off; + sec_size = slot->areas[i].fa_size; + + if (off >= sec_off && off < (sec_off + sec_size)) { + sector->fs_off = sec_off; + sector->fs_size = sec_size; + break; + } + } + + return (i < slot->num_areas) ? 0 : -1; +} + +void sim_assert(int x, const char *assertion, const char *file, unsigned int line, const char *function) +{ + if (!(x)) { + struct sim_context *ctx = sim_get_context(); + if (ctx->c_catch_asserts) { + ctx->c_asserts++; + } else { + BOOT_LOG_ERR("%s:%d: %s: Assertion `%s' failed.", file, line, function, assertion); + + /* NOTE: if the assert below is triggered, the place where it was originally + * asserted is printed by the message above... + */ + assert(x); + } + } +} + +uint32_t boot_max_align(void) +{ + return BOOT_MAX_ALIGN; +} + +uint32_t boot_magic_sz(void) +{ + return BOOT_MAGIC_ALIGN_SIZE; +} diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/security_cnt.c b/bootloader/mcuboot/sim/mcuboot-sys/csupport/security_cnt.c new file mode 100644 index 0000000..0f79d8d --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/security_cnt.c @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Arm Limited + */ + +#include "bootutil/security_cnt.h" +#include "mcuboot_config/mcuboot_logging.h" +#include "bootutil/fault_injection_hardening.h" + +/* + * Since the simulator is executing unit tests in parallel, + * the storage area where the security counter values reside + * has to be managed per thread from Rust's side. + */ +#ifdef MCUBOOT_HW_ROLLBACK_PROT + +int sim_set_nv_counter_for_image(uint32_t image_index, uint32_t security_counter_value); + +int sim_get_nv_counter_for_image(uint32_t image_index, uint32_t* data); + +fih_ret boot_nv_security_counter_init(void) { + return FIH_SUCCESS; +} + +fih_ret boot_nv_security_counter_get(uint32_t image_id, fih_int *security_cnt) { + uint32_t counter = 0; + FIH_DECLARE(fih_rc, FIH_FAILURE); + fih_rc = fih_ret_encode_zero_equality(sim_get_nv_counter_for_image(image_id, &counter)); + + MCUBOOT_LOG_INF("Read security counter value (%d) for image: %d\n", counter, image_id); + *security_cnt = fih_int_encode(counter); + + FIH_RET(fih_rc); +} + +int32_t boot_nv_security_counter_update(uint32_t image_id, uint32_t img_security_cnt) { + MCUBOOT_LOG_INF("Writing security counter value (%d) for image: %d\n", img_security_cnt, image_id); + + return sim_set_nv_counter_for_image(image_id, img_security_cnt); +} + +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/storage/flash_map.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/storage/flash_map.h new file mode 100644 index 0000000..1c54597 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/storage/flash_map.h @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_UTIL_FLASH_MAP_ +#define H_UTIL_FLASH_MAP_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * Provides abstraction of flash regions for type of use. + * I.e. dude where's my image? + * + * System will contain a map which contains flash areas. Every + * region will contain flash identifier, offset within flash and length. + * + * 1. This system map could be in a file within filesystem (Initializer + * must know/figure out where the filesystem is at). + * 2. Map could be at fixed location for project (compiled to code) + * 3. Map could be at specific place in flash (put in place at mfg time). + * + * Note that the map you use must be valid for BSP it's for, + * match the linker scripts when platform executes from flash, + * and match the target offset specified in download script. + */ +#include + +/** + * @brief Structure describing an area on a flash device. + * + * Multiple flash devices may be available in the system, each of + * which may have its own areas. For this reason, flash areas track + * which flash device they are part of. + */ +struct flash_area { + /** + * This flash area's ID; unique in the system. + */ + uint8_t fa_id; + + /** + * ID of the flash device this area is a part of. + */ + uint8_t fa_device_id; + + uint16_t pad16; + + /** + * This area's offset, relative to the beginning of its flash + * device's storage. + */ + uint32_t fa_off; + + /** + * This area's size, in bytes. + */ + uint32_t fa_size; +}; + +/** + * @brief Structure describing a sector within a flash area. + * + * Each sector has an offset relative to the start of its flash area + * (NOT relative to the start of its flash device), and a size. A + * flash area may contain sectors with different sizes. + */ +struct flash_sector { + /** + * Offset of this sector, from the start of its flash area (not device). + */ + uint32_t fs_off; + + /** + * Size of this sector, in bytes. + */ + uint32_t fs_size; +}; + +/* + * Retrieve a memory-mapped flash device's base address. + * + * On success, the address will be stored in the value pointed to by + * ret. + * + * Returns 0 on success, or an error code on failure. + */ +int flash_device_base(uint8_t fd_id, uintptr_t *ret); + +/* + * Start using flash area. + */ +int flash_area_open(uint8_t id, const struct flash_area **); + +void flash_area_close(const struct flash_area *); + +/* + * Read/write/erase. Offset is relative from beginning of flash area. + */ +int flash_area_read(const struct flash_area *, uint32_t off, void *dst, + uint32_t len); +int flash_area_write(const struct flash_area *, uint32_t off, const void *src, + uint32_t len); +int flash_area_erase(const struct flash_area *, uint32_t off, uint32_t len); + +/* + * Alignment restriction for flash writes. + */ +uint32_t flash_area_align(const struct flash_area *); + +/* + * What is value is read from erased flash bytes. + */ +uint8_t flash_area_erased_val(const struct flash_area *); + +/* + * Given flash area ID, return info about sectors within the area. + */ +int flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors); + + +/* Retrieve the flash sector a given offset belongs to. + * + * Returns 0 on success, or an error code on failure. + */ +int flash_area_sector_from_off(uint32_t off, struct flash_sector *sector); + +/* Retrieve the flash sector a given offset, within flash area. + * + * @param fa flash area. + * @param off offset of sector. + * @param sector pointer to structure for obtained information. + * Returns 0 on success, or an error code on failure. + */ +int flash_area_get_sector(const struct flash_area *fa, uint32_t off, + struct flash_sector *sector); + +/* + * Similar to flash_area_get_sectors(), but return the values in an + * array of struct flash_area instead. + */ +__attribute__((deprecated)) +int flash_area_to_sectors(int idx, int *cnt, struct flash_area *ret); + +int flash_area_id_from_image_slot(int slot); +int flash_area_id_from_multi_image_slot(int image_index, int slot); +int flash_area_id_to_multi_image_slot(int image_index, int area_id); + +#ifdef __cplusplus +} +#endif + +#endif /* H_UTIL_FLASH_MAP_ */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/csupport/sysflash/sysflash.h b/bootloader/mcuboot/sim/mcuboot-sys/csupport/sysflash/sysflash.h new file mode 100644 index 0000000..7f723a4 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/csupport/sysflash/sysflash.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SYSFLASH_H__ +#define __SYSFLASH_H__ + +#include +#include + +#if (MCUBOOT_IMAGE_NUMBER == 1) +/* + * NOTE: the definition below returns the same values for true/false on + * purpose, to avoid having to mark x as non-used by all callers when + * running in single image mode. + */ +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_ID(image_0) : \ + FLASH_AREA_ID(image_0)) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_ID(image_1) : \ + FLASH_AREA_ID(image_1)) +#elif (MCUBOOT_IMAGE_NUMBER == 2) +/* MCUBoot currently supports only up to 2 updateable firmware images. + * If the number of the current image is greater than MCUBOOT_IMAGE_NUMBER - 1 + * then a dummy value will be assigned to the flash area macros. + */ +#define FLASH_AREA_IMAGE_PRIMARY(x) (((x) == 0) ? \ + FLASH_AREA_ID(image_0) : \ + ((x) == 1) ? \ + FLASH_AREA_ID(image_2) : \ + 255) +#define FLASH_AREA_IMAGE_SECONDARY(x) (((x) == 0) ? \ + FLASH_AREA_ID(image_1) : \ + ((x) == 1) ? \ + FLASH_AREA_ID(image_3) : \ + 255) +#else +#error "Image slot and flash area mapping is not defined" +#endif + +#define FLASH_AREA_IMAGE_SCRATCH FLASH_AREA_ID(image_scratch) + +#endif /* __SYSFLASH_H__ */ diff --git a/bootloader/mcuboot/sim/mcuboot-sys/src/api.rs b/bootloader/mcuboot/sim/mcuboot-sys/src/api.rs new file mode 100644 index 0000000..0a098ff --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/src/api.rs @@ -0,0 +1,390 @@ +// Copyright (c) 2017-2021 Linaro LTD +// Copyright (c) 2018-2019 JUUL Labs +// Copyright (c) 2023 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +//! HAL api for MyNewt applications + +use crate::area::CAreaDesc; +use log::{Level, log_enabled, warn}; +use simflash::{Result, Flash, FlashPtr}; +use std::{ + cell::RefCell, + collections::HashMap, + mem, + ptr, + slice, +}; + +/// A FlashMap maintain a table of [device_id -> Flash trait] +pub type FlashMap = HashMap; + +pub struct FlashParamsStruct { + align: u32, + erased_val: u8, +} + +pub type FlashParams = HashMap; + +/// The `boot_rsp` structure used by boot_go. +#[repr(C)] +#[derive(Debug)] +pub struct BootRsp { + pub br_hdr: *const ImageHeader, + pub flash_dev_id: u8, + pub image_off: u32, +} + +// TODO: Don't duplicate this image header declaration. +#[repr(C)] +#[derive(Debug)] +pub struct ImageHeader { + magic: u32, + load_addr: u32, + hdr_size: u16, + protect_tlv_size: u16, + img_size: u32, + flags: u32, + ver: ImageVersion, + _pad2: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct ImageVersion { + pub major: u8, + pub minor: u8, + pub revision: u16, + pub build_num: u32, +} + +pub struct CAreaDescPtr { + pub ptr: *const CAreaDesc, +} + +pub struct FlashContext { + flash_map: FlashMap, + flash_params: FlashParams, + flash_areas: CAreaDescPtr, +} + +impl FlashContext { + pub fn new() -> FlashContext { + FlashContext { + flash_map: HashMap::new(), + flash_params: HashMap::new(), + flash_areas: CAreaDescPtr{ptr: ptr::null()}, + } + } +} + +impl Default for FlashContext { + fn default() -> FlashContext { + FlashContext { + flash_map: HashMap::new(), + flash_params: HashMap::new(), + flash_areas: CAreaDescPtr{ptr: ptr::null()}, + } + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct CSimContext { + pub flash_counter: libc::c_int, + pub jumped: libc::c_int, + pub c_asserts: u8, + pub c_catch_asserts: u8, + // NOTE: Always leave boot_jmpbuf declaration at the end; this should + // store a "jmp_buf" which is arch specific and not defined by libc crate. + // The size below is enough to store data on a x86_64 machine. + pub boot_jmpbuf: [u64; 48], +} + +impl Default for CSimContext { + fn default() -> Self { + CSimContext { + flash_counter: 0, + jumped: 0, + c_asserts: 0, + c_catch_asserts: 0, + boot_jmpbuf: [0; 48], + } + } +} + +pub struct CSimContextPtr { + pub ptr: *const CSimContext, +} + +impl CSimContextPtr { + pub fn new() -> CSimContextPtr { + CSimContextPtr { + ptr: ptr::null(), + } + } +} + +impl Default for CSimContextPtr { + fn default() -> CSimContextPtr { + CSimContextPtr { + ptr: ptr::null(), + } + } +} + +/// This struct describes the RAM layout of the current device. It will be stashed, per test +/// thread, and queried by the C code. +#[repr(C)] +#[derive(Debug, Default)] +pub struct BootsimRamInfo { + pub start: u32, + pub size: u32, + pub base: usize, +} + +/// This struct stores the non-volatile security counter per image. It will be stored per test thread, +/// and the C code will set / get the values here. +#[repr(C)] +#[derive(Debug, Default)] +pub struct NvCounterStorage { + pub storage: Vec, +} + +impl NvCounterStorage { + pub fn new() -> Self { + let count = if cfg!(feature = "multiimage") { + 2 + } else { + 1 + }; + Self { + storage: vec![0; count] + } + } +} + +thread_local! { + pub static THREAD_CTX: RefCell = RefCell::new(FlashContext::new()); + pub static SIM_CTX: RefCell = RefCell::new(CSimContextPtr::new()); + pub static RAM_CTX: RefCell = RefCell::new(BootsimRamInfo::default()); + pub static NV_COUNTER_CTX: RefCell = RefCell::new(NvCounterStorage::new()); +} + +/// Set the flash device to be used by the simulation. The pointer is unsafely stashed away. +/// +/// # Safety +/// +/// This uses mem::transmute to stash a Rust pointer into a C value to +/// retrieve later. It should be safe to use this. +pub fn set_flash(dev_id: u8, dev: &mut dyn Flash) { + THREAD_CTX.with(|ctx| { + ctx.borrow_mut().flash_params.insert(dev_id, FlashParamsStruct { + align: dev.align() as u32, + erased_val: dev.erased_val(), + }); + unsafe { + let dev: &'static mut dyn Flash = mem::transmute(dev); + ctx.borrow_mut().flash_map.insert( + dev_id, FlashPtr{ptr: dev as *mut dyn Flash}); + } + }); +} + +pub fn clear_flash(dev_id: u8) { + THREAD_CTX.with(|ctx| { + ctx.borrow_mut().flash_map.remove(&dev_id); + }); +} + +// This isn't meant to call directly, but by a wrapper. + +#[no_mangle] +pub extern "C" fn sim_get_flash_areas() -> *const CAreaDesc { + THREAD_CTX.with(|ctx| { + ctx.borrow().flash_areas.ptr + }) +} + +#[no_mangle] +pub extern "C" fn sim_set_flash_areas(areas: *const CAreaDesc) { + THREAD_CTX.with(|ctx| { + ctx.borrow_mut().flash_areas.ptr = areas; + }); +} + +#[no_mangle] +pub extern "C" fn sim_reset_flash_areas() { + THREAD_CTX.with(|ctx| { + ctx.borrow_mut().flash_areas.ptr = ptr::null(); + }); +} + +#[no_mangle] +pub extern "C" fn sim_get_context() -> *const CSimContext { + SIM_CTX.with(|ctx| { + ctx.borrow().ptr + }) +} + +#[no_mangle] +pub extern "C" fn sim_set_context(ptr: *const CSimContext) { + SIM_CTX.with(|ctx| { + ctx.borrow_mut().ptr = ptr; + }); +} + +#[no_mangle] +pub extern "C" fn sim_reset_context() { + SIM_CTX.with(|ctx| { + ctx.borrow_mut().ptr = ptr::null(); + }); +} + +#[no_mangle] +pub extern "C" fn bootsim_get_ram_info() -> *const BootsimRamInfo { + RAM_CTX.with(|ctx| { + if ctx.borrow().base == 0 { + // Option is messier to get a pointer out of, so just check if the base has been set to + // anything. + panic!("ram info not set, but being used"); + } + ctx.as_ptr() + }) +} + +/// Store a copy of this RAM info. +pub fn set_ram_info(info: BootsimRamInfo) { + RAM_CTX.with(|ctx| { + ctx.replace(info); + }); +} + +/// Clear out the ram info. +pub fn clear_ram_info() { + RAM_CTX.with(|ctx| { + ctx.borrow_mut().base = 0; + }); +} + +#[no_mangle] +pub extern "C" fn sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int { + let mut rc: libc::c_int = -19; + THREAD_CTX.with(|ctx| { + if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) { + let dev = unsafe { &mut *(flash.ptr) }; + rc = map_err(dev.erase(offset as usize, size as usize)); + } + }); + rc +} + +#[no_mangle] +pub extern "C" fn sim_flash_read(dev_id: u8, offset: u32, dest: *mut u8, size: u32) -> libc::c_int { + let mut rc: libc::c_int = -19; + THREAD_CTX.with(|ctx| { + if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) { + let mut buf: &mut[u8] = unsafe { slice::from_raw_parts_mut(dest, size as usize) }; + let dev = unsafe { &mut *(flash.ptr) }; + rc = map_err(dev.read(offset as usize, &mut buf)); + } + }); + rc +} + +#[no_mangle] +pub extern "C" fn sim_flash_write(dev_id: u8, offset: u32, src: *const u8, size: u32) -> libc::c_int { + let mut rc: libc::c_int = -19; + THREAD_CTX.with(|ctx| { + if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) { + let buf: &[u8] = unsafe { slice::from_raw_parts(src, size as usize) }; + let dev = unsafe { &mut *(flash.ptr) }; + rc = map_err(dev.write(offset as usize, &buf)); + } + }); + rc +} + +#[no_mangle] +pub extern "C" fn sim_flash_align(id: u8) -> u32 { + THREAD_CTX.with(|ctx| { + ctx.borrow().flash_params.get(&id).unwrap().align + }) +} + +#[no_mangle] +pub extern "C" fn sim_flash_erased_val(id: u8) -> u8 { + THREAD_CTX.with(|ctx| { + ctx.borrow().flash_params.get(&id).unwrap().erased_val + }) +} + +fn map_err(err: Result<()>) -> libc::c_int { + match err { + Ok(()) => 0, + Err(e) => { + warn!("{}", e); + -1 + }, + } +} + +/// Called by C code to determine if we should log at this level. Levels are defined in +/// bootutil/bootutil_log.h. This makes the logging from the C code controlled by bootsim::api, so +/// for example, it can be enabled with something like: +/// RUST_LOG=bootsim::api=info cargo run --release runall +/// or +/// RUST_LOG=bootsim=info cargo run --release runall +#[no_mangle] +pub extern "C" fn sim_log_enabled(level: libc::c_int) -> libc::c_int { + let res = match level { + 1 => log_enabled!(Level::Error), + 2 => log_enabled!(Level::Warn), + 3 => log_enabled!(Level::Info), + 4 => log_enabled!(Level::Debug), + 5 => log_enabled!(Level::Trace), // log level == SIM + _ => false, + }; + if res { + 1 + } else { + 0 + } +} + +#[no_mangle] +pub extern "C" fn sim_set_nv_counter_for_image(image_index: u32, security_counter_value: u32) -> libc::c_int { + let mut rc = 0; + NV_COUNTER_CTX.with(|ctx| { + let mut counter_storage = ctx.borrow_mut(); + if image_index as usize >= counter_storage.storage.len() { + rc = -1; + return; + } + if counter_storage.storage[image_index as usize] > security_counter_value { + rc = -2; + warn!("Failed to set security counter value ({}) for image index {}", security_counter_value, image_index); + return; + } + + counter_storage.storage[image_index as usize] = security_counter_value; + }); + + return rc; +} + +#[no_mangle] +pub extern "C" fn sim_get_nv_counter_for_image(image_index: u32, security_counter_value: *mut u32) -> libc::c_int { + let mut rc = 0; + NV_COUNTER_CTX.with(|ctx| { + let counter_storage = ctx.borrow(); + if image_index as usize >= counter_storage.storage.len() { + rc = -1; + return; + } + unsafe { *security_counter_value = counter_storage.storage[image_index as usize] }; + + }); + return rc; +} diff --git a/bootloader/mcuboot/sim/mcuboot-sys/src/area.rs b/bootloader/mcuboot/sim/mcuboot-sys/src/area.rs new file mode 100644 index 0000000..1ebb405 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/src/area.rs @@ -0,0 +1,210 @@ +// Copyright (c) 2017-2021 Linaro LTD +// Copyright (c) 2018-2019 JUUL Labs +// Copyright (c) 2019 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +//! Describe flash areas. + +use simflash::{Flash, SimFlash, Sector}; +use std::ptr; +use std::collections::HashMap; +use std::borrow::BorrowMut; + +/// Structure to build up the boot area table. +#[derive(Debug, Default, Clone)] +pub struct AreaDesc { + areas: Vec>, + whole: Vec, + sectors: HashMap>, +} + +impl AreaDesc { + pub fn new() -> AreaDesc { + AreaDesc { + areas: vec![], + whole: vec![], + sectors: HashMap::new(), + } + } + + pub fn add_flash_sectors(&mut self, id: u8, flash: &SimFlash) { + self.sectors.insert(id, flash.sector_iter().collect()); + } + + /// Add a slot to the image. The slot must align with erasable units in the flash device. + /// Panics if the description is not valid. There are also bootloader assumptions that the + /// slots are PRIMARY_SLOT, SECONDARY_SLOT, and SCRATCH in that order. + pub fn add_image(&mut self, base: usize, len: usize, id: FlashId, dev_id: u8) { + let nid = id as usize; + let orig_base = base; + let orig_len = len; + let mut base = base; + let mut len = len; + + while nid > self.areas.len() { + self.areas.push(vec![]); + self.whole.push(Default::default()); + } + + if nid != self.areas.len() { + panic!("Flash areas not added in order"); + } + + let mut area = vec![]; + + for sector in &self.sectors[&dev_id] { + if len == 0 { + break; + }; + if base > sector.base + sector.size - 1 { + continue; + } + if sector.base != base { + panic!("Image does not start on a sector boundary"); + } + + area.push(FlashArea { + flash_id: id, + device_id: dev_id, + pad16: 0, + off: sector.base as u32, + size: sector.size as u32, + }); + + base += sector.size; + len -= sector.size; + } + + if len != 0 { + panic!("Image goes past end of device"); + } + + self.areas.push(area); + self.whole.push(FlashArea { + flash_id: id, + device_id: dev_id, + pad16: 0, + off: orig_base as u32, + size: orig_len as u32, + }); + } + + // Add a simple slot to the image. This ignores the device layout, and just adds the area as a + // single unit. It assumes that the image lines up with image boundaries. This tests + // configurations where the partition table uses larger sectors than the underlying flash + // device. + pub fn add_simple_image(&mut self, base: usize, len: usize, id: FlashId, dev_id: u8) { + let area = vec![FlashArea { + flash_id: id, + device_id: dev_id, + pad16: 0, + off: base as u32, + size: len as u32, + }]; + + self.areas.push(area); + self.whole.push(FlashArea { + flash_id: id, + device_id: dev_id, + pad16: 0, + off: base as u32, + size: len as u32, + }); + } + + // Look for the image with the given ID, and return its offset, size and + // device id. Returns None if the area is not present. + pub fn find(&self, id: FlashId) -> Option<(usize, usize, u8)> { + for area in &self.whole { + // FIXME: should we ensure id is not duplicated over multiple devices? + if area.flash_id == id { + return Some((area.off as usize, area.size as usize, area.device_id)); + } + } + None + } + + pub fn get_c(&self) -> Box { + let mut areas_box: Box = Box::new(Default::default()); + let areas: &mut CAreaDesc = areas_box.borrow_mut(); + + assert_eq!(self.areas.len(), self.whole.len()); + + for (i, area) in self.areas.iter().enumerate() { + if !area.is_empty() { + areas.slots[i].areas = &area[0]; + areas.slots[i].whole = self.whole[i].clone(); + areas.slots[i].num_areas = area.len() as u32; + areas.slots[i].id = area[0].flash_id; + } + } + + areas.num_slots = self.areas.len() as u32; + + areas_box + } + + /// Return an iterator over all `FlashArea`s present. + pub fn iter_areas(&self) -> impl Iterator { + self.whole.iter() + } +} + +/// The area descriptor, C format. +#[repr(C)] +#[derive(Debug, Default)] +pub struct CAreaDesc { + slots: [CArea; 16], + num_slots: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct CArea { + whole: FlashArea, + areas: *const FlashArea, + num_areas: u32, + // FIXME: is this not already available on whole/areas? + id: FlashId, +} + +impl Default for CArea { + fn default() -> CArea { + CArea { + areas: ptr::null(), + whole: Default::default(), + id: FlashId::BootLoader, + num_areas: 0, + } + } +} + +/// Flash area map. +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[allow(dead_code)] +pub enum FlashId { + BootLoader = 0, + Image0 = 1, + Image1 = 2, + ImageScratch = 3, + Image2 = 4, + Image3 = 5, +} + +impl Default for FlashId { + fn default() -> FlashId { + FlashId::BootLoader + } +} + +#[repr(C)] +#[derive(Debug, Clone, Default)] +pub struct FlashArea { + pub flash_id: FlashId, + pub device_id: u8, + pad16: u16, + pub off: u32, + pub size: u32, +} diff --git a/bootloader/mcuboot/sim/mcuboot-sys/src/c.rs b/bootloader/mcuboot/sim/mcuboot-sys/src/c.rs new file mode 100644 index 0000000..9d2a321 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/src/c.rs @@ -0,0 +1,226 @@ +// Copyright (c) 2017-2021 Linaro LTD +// Copyright (c) 2017-2019 JUUL Labs +// Copyright (c) 2019-2023 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +//! Interface wrappers to C API entering to the bootloader + +use crate::area::AreaDesc; +use simflash::SimMultiFlash; +use crate::api; + +#[allow(unused)] +use std::sync::Once; + +use std::borrow::Borrow; + +/// The result of an invocation of `boot_go`. This is intentionally opaque so that we can provide +/// accessors for everything we need from this. +#[derive(Debug)] +pub enum BootGoResult { + /// This run was stopped by the flash simulation mechanism. + Stopped, + /// The bootloader ran to completion with the following data. + Normal { + result: i32, + asserts: u8, + + resp: api::BootRsp, + }, +} + +impl BootGoResult { + /// Was this run interrupted. + pub fn interrupted(&self) -> bool { + matches!(self, BootGoResult::Stopped) + } + + /// Was this boot run successful (returned 0) + pub fn success(&self) -> bool { + matches!(self, BootGoResult::Normal { result: 0, .. }) + } + + /// Success, but also no asserts. + pub fn success_no_asserts(&self) -> bool { + matches!(self, BootGoResult::Normal { + result: 0, + asserts: 0, + .. + }) + } + + /// Get the asserts count. An interrupted run will be considered to have no asserts. + pub fn asserts(&self) -> u8 { + match self { + BootGoResult::Normal { asserts, .. } => *asserts, + _ => 0, + } + } + + /// Retrieve the 'resp' field that is filled in. + pub fn resp(&self) -> Option<&api::BootRsp> { + match self { + BootGoResult::Normal { resp, .. } => Some(resp), + _ => None, + } + } +} + +/// Invoke the bootloader on this flash device. +pub fn boot_go(multiflash: &mut SimMultiFlash, areadesc: &AreaDesc, + counter: Option<&mut i32>, image_index: Option, + catch_asserts: bool) -> BootGoResult { + init_crypto(); + + for (&dev_id, flash) in multiflash.iter_mut() { + api::set_flash(dev_id, flash); + } + let mut sim_ctx = api::CSimContext { + flash_counter: match counter { + None => 0, + Some(ref c) => **c as libc::c_int + }, + c_catch_asserts: if catch_asserts { 1 } else { 0 }, + .. Default::default() + }; + let mut rsp = api::BootRsp { + br_hdr: std::ptr::null(), + flash_dev_id: 0, + image_off: 0, + }; + let result: i32 = unsafe { + let adesc = areadesc.get_c(); + match image_index { + None => raw::invoke_boot_go(&mut sim_ctx as *mut _, + adesc.borrow() as *const _, + &mut rsp as *mut _, -1) as i32, + Some(i) => raw::invoke_boot_go(&mut sim_ctx as *mut _, + adesc.borrow() as *const _, + &mut rsp as *mut _, + i as i32) as i32 + } + }; + let asserts = sim_ctx.c_asserts; + if let Some(c) = counter { + *c = sim_ctx.flash_counter; + } + for &dev_id in multiflash.keys() { + api::clear_flash(dev_id); + } + if result == -0x13579 { + BootGoResult::Stopped + } else { + BootGoResult::Normal { result, asserts, resp: rsp } + } +} + +pub fn boot_trailer_sz(align: u32) -> u32 { + unsafe { raw::boot_trailer_sz(align) } +} + +pub fn boot_status_sz(align: u32) -> u32 { + unsafe { raw::boot_status_sz(align) } +} + +pub fn boot_magic_sz() -> usize { + unsafe { raw::boot_magic_sz() as usize } +} + +pub fn boot_max_align() -> usize { + unsafe { raw::boot_max_align() as usize } +} + +pub fn rsa_oaep_encrypt(pubkey: &[u8], seckey: &[u8]) -> Result<[u8; 256], &'static str> { + unsafe { + let mut encbuf: [u8; 256] = [0; 256]; + if raw::rsa_oaep_encrypt_(pubkey.as_ptr(), pubkey.len() as u32, + seckey.as_ptr(), seckey.len() as u32, + encbuf.as_mut_ptr()) == 0 { + return Ok(encbuf); + } + Err("Failed to encrypt buffer") + } +} + +pub fn kw_encrypt(kek: &[u8], seckey: &[u8], keylen: u32) -> Result, &'static str> { + unsafe { + let mut encbuf = vec![0u8; 24]; + if keylen == 32 { + encbuf = vec![0u8; 40]; + } + if raw::kw_encrypt_(kek.as_ptr(), seckey.as_ptr(), encbuf.as_mut_ptr()) == 0 { + return Ok(encbuf); + } + Err("Failed to encrypt buffer") + } +} + +pub fn set_security_counter(image_index: u32, security_counter_value: u32) { + api::sim_set_nv_counter_for_image(image_index, security_counter_value); +} + +pub fn get_security_counter(image_index: u32) -> u32 { + let mut counter_val: u32 = 0; + api::sim_get_nv_counter_for_image(image_index, &mut counter_val as *mut u32); + return counter_val; +} + +mod raw { + use crate::area::CAreaDesc; + use crate::api::{BootRsp, CSimContext}; + + extern "C" { + // This generates a warning about `CAreaDesc` not being foreign safe. There doesn't appear to + // be any way to get rid of this warning. See https://github.com/rust-lang/rust/issues/34798 + // for information and tracking. + pub fn invoke_boot_go(sim_ctx: *mut CSimContext, areadesc: *const CAreaDesc, + rsp: *mut BootRsp, image_index: libc::c_int) -> libc::c_int; + + pub fn boot_trailer_sz(min_write_sz: u32) -> u32; + pub fn boot_status_sz(min_write_sz: u32) -> u32; + + pub fn boot_magic_sz() -> u32; + pub fn boot_max_align() -> u32; + + pub fn rsa_oaep_encrypt_(pubkey: *const u8, pubkey_len: libc::c_uint, + seckey: *const u8, seckey_len: libc::c_uint, + encbuf: *mut u8) -> libc::c_int; + + pub fn kw_encrypt_(kek: *const u8, seckey: *const u8, + encbuf: *mut u8) -> libc::c_int; + + #[allow(unused)] + pub fn psa_crypto_init() -> u32; + + #[allow(unused)] + pub fn mbedtls_test_enable_insecure_external_rng(); + } +} + +#[allow(unused)] +static PSA_INIT_SYNC: Once = Once::new(); + +#[allow(unused)] +static MBEDTLS_EXTERNAL_RNG_ENABLE_SYNC: Once = Once::new(); + +#[cfg(feature = "psa-crypto-api")] +fn init_crypto() { + PSA_INIT_SYNC.call_once(|| { + assert_eq!(unsafe { raw::psa_crypto_init() }, 0); + }); + + /* The PSA APIs require properly initialisation of the entropy subsystem + * The configuration adds the option MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG when the + * psa-crypto-api feature is enabled. As a result the tests use the implementation + * of the test external rng that needs to be initialised before being able to use it + */ + MBEDTLS_EXTERNAL_RNG_ENABLE_SYNC.call_once(|| { + unsafe { raw::mbedtls_test_enable_insecure_external_rng() } + }); +} + +#[cfg(not(feature = "psa-crypto-api"))] +fn init_crypto() { + // When the feature is not enabled, the init is just empty +} diff --git a/bootloader/mcuboot/sim/mcuboot-sys/src/lib.rs b/bootloader/mcuboot/sim/mcuboot-sys/src/lib.rs new file mode 100644 index 0000000..bc3fc49 --- /dev/null +++ b/bootloader/mcuboot/sim/mcuboot-sys/src/lib.rs @@ -0,0 +1,52 @@ +// Copyright (c) 2017-2019 Linaro LTD +// +// SPDX-License-Identifier: Apache-2.0 + +mod area; +pub mod c; + +// The API needs to be public, even though it isn't intended to be called by Rust code, but the +// functions are exported to C code. +pub mod api; + +pub use crate::area::{AreaDesc, FlashId}; + +/// For testing the ram load feature, we need to emulate a block of RAM and be able to pass that +/// down to the C code. The call down to boot_go should go through this object so that the buffer +/// itself is managed properly. +pub struct RamBlock { + ram: Vec, + offset: u32, // 32-bit offset. +} + +impl RamBlock { + pub fn new(size: u32, offset: u32) -> RamBlock { + RamBlock { + ram: vec![0; size as usize], + offset: offset, + } + } + + /// Borrow the RAM buffer, with 'offset' being the beginning of the buffer. + pub fn borrow(&self) -> &[u8] { + &self.ram + } + + /// Borrow a piece of the ram, with 'offset' being the beginning of the buffer. + pub fn borrow_part(&self, base: usize, size: usize) -> &[u8] { + &self.ram[base..base+size] + } + + pub fn invoke(&self, act: F) -> R + where F: FnOnce() -> R + { + api::set_ram_info(api::BootsimRamInfo { + start: self.offset, + size: self.ram.len() as u32, + base: &self.ram[0] as *const u8 as usize - self.offset as usize, + }); + let result = act(); + api::clear_ram_info(); + result + } +} diff --git a/bootloader/mcuboot/sim/simflash/.gitignore b/bootloader/mcuboot/sim/simflash/.gitignore new file mode 100644 index 0000000..03314f7 --- /dev/null +++ b/bootloader/mcuboot/sim/simflash/.gitignore @@ -0,0 +1 @@ +Cargo.lock diff --git a/bootloader/mcuboot/sim/simflash/Cargo.toml b/bootloader/mcuboot/sim/simflash/Cargo.toml new file mode 100644 index 0000000..e745a0c --- /dev/null +++ b/bootloader/mcuboot/sim/simflash/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "simflash" +version = "0.1.0" +authors = ["David Brown "] +edition = "2021" + +[dependencies] +rand = "0.8" +log = "0.4" +thiserror = "1.0" diff --git a/bootloader/mcuboot/sim/simflash/src/lib.rs b/bootloader/mcuboot/sim/simflash/src/lib.rs new file mode 100644 index 0000000..4c49ccb --- /dev/null +++ b/bootloader/mcuboot/sim/simflash/src/lib.rs @@ -0,0 +1,377 @@ +// Copyright (c) 2017-2021 Linaro LTD +// Copyright (c) 2017-2018 JUUL Labs +// +// SPDX-License-Identifier: Apache-2.0 + +//! A flash simulator +//! +//! This module is capable of simulating the type of NOR flash commonly used in microcontrollers. +//! These generally can be written as individual bytes, but must be erased in larger units. + +mod pdump; + +use crate::pdump::HexDump; +use log::info; +use rand::{ + self, + distributions::Standard, + Rng, +}; +use std::{ + collections::HashMap, + fs::File, + io::{self, Write}, + iter::Enumerate, + path::Path, + slice, +}; +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Error, Debug)] +pub enum FlashError { + #[error("Offset out of bounds: {0}")] + OutOfBounds(String), + #[error("Invalid write: {0}")] + Write(String), + #[error("Write failed by chance: {0}")] + SimulatedFail(String), + #[error("{0}")] + Io(#[from] io::Error), +} + +// Transition from error-chain. +macro_rules! bail { + ($item:expr) => (return Err($item.into());) +} + +pub struct FlashPtr { + pub ptr: *mut dyn Flash, +} +unsafe impl Send for FlashPtr {} + +pub trait Flash { + fn erase(&mut self, offset: usize, len: usize) -> Result<()>; + fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()>; + fn read(&self, offset: usize, data: &mut [u8]) -> Result<()>; + + fn add_bad_region(&mut self, offset: usize, len: usize, rate: f32) -> Result<()>; + fn reset_bad_regions(&mut self); + + fn set_verify_writes(&mut self, enable: bool); + + fn sector_iter(&self) -> SectorIter<'_>; + fn device_size(&self) -> usize; + + fn align(&self) -> usize; + fn erased_val(&self) -> u8; +} + +fn ebounds>(message: T) -> FlashError { + FlashError::OutOfBounds(message.as_ref().to_owned()) +} + +#[allow(dead_code)] +fn ewrite>(message: T) -> FlashError { + FlashError::Write(message.as_ref().to_owned()) +} + +#[allow(dead_code)] +fn esimulatedwrite>(message: T) -> FlashError { + FlashError::SimulatedFail(message.as_ref().to_owned()) +} + +/// An emulated flash device. It is represented as a block of bytes, and a list of the sector +/// mappings. +#[derive(Clone)] +pub struct SimFlash { + data: Vec, + write_safe: Vec, + sectors: Vec, + bad_region: Vec<(usize, usize, f32)>, + // Alignment required for writes. + align: usize, + verify_writes: bool, + erased_val: u8, +} + +impl SimFlash { + /// Given a sector size map, construct a flash device for that. + pub fn new(sectors: Vec, align: usize, erased_val: u8) -> SimFlash { + // Verify that the alignment is a positive power of two. + assert!(align > 0); + assert!(align & (align - 1) == 0); + + let total = sectors.iter().sum(); + SimFlash { + data: vec![erased_val; total], + write_safe: vec![true; total], + sectors, + bad_region: Vec::new(), + align, + verify_writes: true, + erased_val, + } + } + + #[allow(dead_code)] + pub fn dump(&self) { + self.data.dump(); + } + + /// Dump this image to the given file. + #[allow(dead_code)] + pub fn write_file>(&self, path: P) -> Result<()> { + let mut fd = File::create(path)?; + fd.write_all(&self.data)?; + Ok(()) + } + + // Scan the sector map, and return the base and offset within a sector for this given byte. + // Returns None if the value is outside of the device. + fn get_sector(&self, offset: usize) -> Option<(usize, usize)> { + let mut offset = offset; + for (sector, &size) in self.sectors.iter().enumerate() { + if offset < size { + return Some((sector, offset)); + } + offset -= size; + } + None + } + +} + +pub type SimMultiFlash = HashMap; + +impl Flash for SimFlash { + /// The flash drivers tend to erase beyond the bounds of the given range. Instead, we'll be + /// strict, and make sure that the passed arguments are exactly at a sector boundary, otherwise + /// return an error. + fn erase(&mut self, offset: usize, len: usize) -> Result<()> { + let (_start, slen) = self.get_sector(offset).ok_or_else(|| ebounds("start"))?; + let (end, elen) = self.get_sector(offset + len - 1).ok_or_else(|| ebounds("end"))?; + + if slen != 0 { + bail!(ebounds("offset not at start of sector")); + } + if elen != self.sectors[end] - 1 { + bail!(ebounds("end not at start of sector")); + } + + for x in &mut self.data[offset .. offset + len] { + *x = self.erased_val; + } + + for x in &mut self.write_safe[offset .. offset + len] { + *x = true; + } + + Ok(()) + } + + /// We restrict to only allowing writes of values that are: + /// + /// 1. being written to for the first time + /// 2. being written to after being erased + /// + /// This emulates a flash device which starts out erased, with the + /// added restriction that repeated writes to the same location + /// are disallowed, even if they would be safe to do. + fn write(&mut self, offset: usize, payload: &[u8]) -> Result<()> { + for &(off, len, rate) in &self.bad_region { + if offset >= off && (offset + payload.len()) <= (off + len) { + let mut rng = rand::thread_rng(); + let samp: f32 = rng.sample(Standard); + if samp < rate { + bail!(esimulatedwrite( + format!("Ignoring write to {:#x}-{:#x}", off, off + len))); + } + } + } + + if offset + payload.len() > self.data.len() { + panic!("Write outside of device"); + } + + // Verify the alignment (which must be a power of two). + if offset & (self.align - 1) != 0 { + panic!("Misaligned write address"); + } + + if payload.len() & (self.align - 1) != 0 { + panic!("Write length not multiple of alignment"); + } + + for (i, x) in &mut self.write_safe[offset .. offset + payload.len()].iter_mut().enumerate() { + if self.verify_writes && !(*x) { + panic!("Write to unerased location at 0x{:x}", offset + i); + } + *x = false; + } + + let sub = &mut self.data[offset .. offset + payload.len()]; + sub.copy_from_slice(payload); + Ok(()) + } + + /// Read is simple. + fn read(&self, offset: usize, data: &mut [u8]) -> Result<()> { + if offset + data.len() > self.data.len() { + bail!(ebounds("Read outside of device")); + } + + let sub = &self.data[offset .. offset + data.len()]; + data.copy_from_slice(sub); + Ok(()) + } + + /// Adds a new flash bad region. Writes to this area fail with a chance + /// given by `rate`. + fn add_bad_region(&mut self, offset: usize, len: usize, rate: f32) -> Result<()> { + if !(0.0..=1.0).contains(&rate) { + bail!(ebounds("Invalid rate")); + } + + info!("Adding new bad region {:#x}-{:#x}", offset, offset + len); + self.bad_region.push((offset, len, rate)); + + Ok(()) + } + + fn reset_bad_regions(&mut self) { + self.bad_region.clear(); + } + + fn set_verify_writes(&mut self, enable: bool) { + self.verify_writes = enable; + } + + /// An iterator over each sector in the device. + fn sector_iter(&self) -> SectorIter<'_> { + SectorIter { + iter: self.sectors.iter().enumerate(), + base: 0, + } + } + + fn device_size(&self) -> usize { + self.data.len() + } + + fn align(&self) -> usize { + self.align + } + + fn erased_val(&self) -> u8 { + self.erased_val + } +} + +/// It is possible to iterate over the sectors in the device, each element returning this. +#[derive(Debug, Clone)] +pub struct Sector { + /// Which sector is this, starting from 0. + pub num: usize, + /// The offset, in bytes, of the start of this sector. + pub base: usize, + /// The length, in bytes, of this sector. + pub size: usize, +} + +pub struct SectorIter<'a> { + iter: Enumerate>, + base: usize, +} + +impl<'a> Iterator for SectorIter<'a> { + type Item = Sector; + + fn next(&mut self) -> Option { + match self.iter.next() { + None => None, + Some((num, &size)) => { + let base = self.base; + self.base += size; + Some(Sector { + num, + base, + size, + }) + } + } + } +} + +#[cfg(test)] +mod test { + use super::{Flash, FlashError, SimFlash, Result, Sector}; + + #[test] + fn test_flash() { + for &erased_val in &[0, 0xff] { + // NXP-style, uniform sectors. + let mut f1 = SimFlash::new(vec![4096usize; 256], 1, erased_val); + test_device(&mut f1, erased_val); + + // STM style, non-uniform sectors. + let mut f2 = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024, + 128 * 1024, 128 * 1024, 128 * 1024], 1, erased_val); + test_device(&mut f2, erased_val); + } + } + + fn test_device(flash: &mut dyn Flash, erased_val: u8) { + let sectors: Vec = flash.sector_iter().collect(); + + flash.erase(0, sectors[0].size).unwrap(); + let flash_size = flash.device_size(); + flash.erase(0, flash_size).unwrap(); + assert!(flash.erase(0, sectors[0].size - 1).is_bounds()); + + // Verify that write and erase do something. + flash.write(0, &[0x55]).unwrap(); + let mut buf = [0xAA; 4]; + flash.read(0, &mut buf).unwrap(); + assert_eq!(buf, [0x55, erased_val, erased_val, erased_val]); + + flash.erase(0, sectors[0].size).unwrap(); + flash.read(0, &mut buf).unwrap(); + assert_eq!(buf, [erased_val; 4]); + + // Program the first and last byte of each sector, verify that has been done, and then + // erase to verify the erase boundaries. + for sector in §ors { + let byte = [(sector.num & 127) as u8]; + flash.write(sector.base, &byte).unwrap(); + flash.write(sector.base + sector.size - 1, &byte).unwrap(); + } + + // Verify the above + let mut buf = Vec::new(); + for sector in §ors { + let byte = (sector.num & 127) as u8; + buf.resize(sector.size, 0); + flash.read(sector.base, &mut buf).unwrap(); + assert_eq!(buf.first(), Some(&byte)); + assert_eq!(buf.last(), Some(&byte)); + assert!(buf[1..buf.len()-1].iter().all(|&x| x == erased_val)); + } + } + + // Helper checks for the result type. + trait EChecker { + fn is_bounds(&self) -> bool; + } + + impl EChecker for Result { + + fn is_bounds(&self) -> bool { + match *self { + Err(FlashError::OutOfBounds(_)) => true, + _ => false, + } + } + } +} diff --git a/bootloader/mcuboot/sim/simflash/src/pdump.rs b/bootloader/mcuboot/sim/simflash/src/pdump.rs new file mode 100644 index 0000000..b2f403f --- /dev/null +++ b/bootloader/mcuboot/sim/simflash/src/pdump.rs @@ -0,0 +1,80 @@ +// Copyright (c) 2017,2021 Linaro LTD +// +// SPDX-License-Identifier: Apache-2.0 + +// Printable hexdump. + +pub trait HexDump { + // Output the data value in hex. + fn dump(&self); +} + +struct Dumper { + hex: String, + ascii: String, + count: usize, + total_count: usize, +} + +impl Dumper { + fn new() -> Dumper { + Dumper { + hex: String::with_capacity(49), + ascii: String::with_capacity(16), + count: 0, + total_count: 0, + } + } + + fn add_byte(&mut self, ch: u8) { + if self.count == 16 { + self.ship(); + } + if self.count == 8 { + self.hex.push(' '); + } + self.hex.push_str(&format!(" {:02x}", ch)[..]); + self.ascii.push(if (b' '..=b'~').contains(&ch) { + ch as char + } else { + '.' + }); + self.count += 1; + } + + fn ship(&mut self) { + if self.count == 0 { + return; + } + + println!("{:06x} {:-49} |{}|", self.total_count, self.hex, self.ascii); + + self.hex.clear(); + self.ascii.clear(); + self.total_count += 16; + self.count = 0; + } +} + +impl<'a> HexDump for &'a [u8] { + fn dump(&self) { + let mut dump = Dumper::new(); + for ch in self.iter() { + dump.add_byte(*ch); + } + dump.ship(); + } +} + +impl HexDump for Vec { + fn dump(&self) { + (&self[..]).dump() + } +} + +#[test] +fn samples() { + "Hello".as_bytes().dump(); + "This is a much longer string".as_bytes().dump(); + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f".as_bytes().dump(); +} diff --git a/bootloader/mcuboot/sim/src/caps.rs b/bootloader/mcuboot/sim/src/caps.rs new file mode 100644 index 0000000..d8dd068 --- /dev/null +++ b/bootloader/mcuboot/sim/src/caps.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2017-2021 Linaro LTD +// Copyright (c) 2019 JUUL Labs +// Copyright (c) 2019-2023 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +// Query the bootloader's capabilities. + +#[repr(u32)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[allow(unused)] +pub enum Caps { + RSA2048 = (1 << 0), + /* reserved (1 << 1) */ + EcdsaP256 = (1 << 2), + SwapUsingScratch = (1 << 3), + OverwriteUpgrade = (1 << 4), + EncRsa = (1 << 5), + EncKw = (1 << 6), + ValidatePrimarySlot = (1 << 7), + RSA3072 = (1 << 8), + Ed25519 = (1 << 9), + EncEc256 = (1 << 10), + SwapUsingMove = (1 << 11), + DowngradePrevention = (1 << 12), + EncX25519 = (1 << 13), + Bootstrap = (1 << 14), + Aes256 = (1 << 15), + RamLoad = (1 << 16), + DirectXip = (1 << 17), + HwRollbackProtection = (1 << 18), + EcdsaP384 = (1 << 19), +} + +impl Caps { + pub fn present(self) -> bool { + let caps = unsafe { bootutil_get_caps() }; + (caps as u32) & (self as u32) != 0 + } + + /// Does this build have ECDSA of some type enabled for signatures. + pub fn has_ecdsa() -> bool { + Caps::EcdsaP256.present() || Caps::EcdsaP384.present() + } + + /// Query for the number of images that have been configured into this + /// MCUboot build. + pub fn get_num_images() -> usize { + (unsafe { bootutil_get_num_images() }) as usize + } + + /// Query if this configuration performs some kind of upgrade by writing to flash. + pub fn modifies_flash() -> bool { + // All other configurations perform upgrades by writing to flash. + !(Self::RamLoad.present() || Self::DirectXip.present()) + } +} + +extern "C" { + fn bootutil_get_caps() -> Caps; + fn bootutil_get_num_images() -> u32; +} diff --git a/bootloader/mcuboot/sim/src/depends.rs b/bootloader/mcuboot/sim/src/depends.rs new file mode 100644 index 0000000..229b3e2 --- /dev/null +++ b/bootloader/mcuboot/sim/src/depends.rs @@ -0,0 +1,187 @@ +// Copyright (c) 2019-2021 Linaro LTD +// +// SPDX-License-Identifier: Apache-2.0 + +//! Support and tests related to the dependency management for multi-image +//! support. + +use crate::image::ImageVersion; + +pub trait Depender { + /// Generate a version for this particular image. The slot indicates + /// which slot this is being put in. + fn my_version(&self, offset: usize, slot: usize) -> ImageVersion; + + /// Return dependencies for this image/slot combination. + fn my_deps(&self, offset: usize, slot: usize) -> Vec; + + /// Return the image ID of the other version. + fn other_id(&self) -> u8; +} + +/// A boring image is used when we aren't testing dependencies. There will +/// be meaningful version numbers. The size field is the image number we +/// are. +pub struct BoringDep { + number: usize, + test: DepTest, +} + +impl BoringDep { + pub fn new(number: usize, test: &DepTest) -> BoringDep { + BoringDep { + number, + test: test.clone(), + } + } +} + +impl Depender for BoringDep { + fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion { + let slot = if self.test.downgrade { + 1 - slot + } else { + slot + }; + ImageVersion::new_synthetic(self.number as u8, slot as u8, 0) + } + + fn my_deps(&self, _offset: usize, _slot: usize) -> Vec { + vec![] + } + + fn other_id(&self) -> u8 { + 0 + } +} + +/// An individual test of the dependency mechanism describes one of the +/// possibilities for the dependency information for each image, and what +/// the expected outcome is. +#[derive(Clone, Debug)] +pub struct DepTest { + /// What kinds of dependency should be installed in the image. + pub depends: [DepType; 2], + + /// What is the expected outcome of the upgrade. + pub upgrades: [UpgradeInfo; 2], + + /// Should this be considered a downgrade (cause the version number to + /// decrease). + pub downgrade: bool, +} + +/// Describes the various types of dependency information that can be +/// provided. +#[derive(Clone, Debug)] +pub enum DepType { + /// Do not include dependency information + Nothing, + /// Provide dependency information that matches the other image. + Correct, + /// Provide a dependency that matches the old version of the other + /// image. + OldCorrect, + /// Provide dependency information describing something newer than the + /// other image. + Newer, + /// Don't provide an upgrade image at all for this image + NoUpgrade, +} + +/// Describes what our expectation is for an upgrade. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum UpgradeInfo { + /// The current version should be held. + Held, + /// The image should be upgraded + Upgraded, +} + +/// A "test" that gives no dependency information. +pub static NO_DEPS: DepTest = DepTest { + depends: [DepType::Nothing, DepType::Nothing], + upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded], + downgrade: false, +}; + +/// A "test" with no dependency information, and the images marked as a +/// downgrade. +pub static REV_DEPS: DepTest = DepTest { + depends: [DepType::Nothing, DepType::Nothing], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Held], + downgrade: true, +}; + +/// A PairDep describes the dependencies between two pairs. +pub struct PairDep { + /// The image number of this image. + number: usize, + + test: DepTest, +} + +impl PairDep { + pub fn new(total_image: usize, my_image: usize, deps: &DepTest) -> PairDep { + if total_image != 2 { + panic!("PairDep only works when there are two images"); + } + + PairDep { + number: my_image, + test: deps.clone(), + } + } +} + +impl Depender for PairDep { + fn my_version(&self, _offset: usize, slot: usize) -> ImageVersion { + let slot = if self.test.downgrade { + 1 - slot + } else { + slot + }; + ImageVersion::new_synthetic(self.number as u8, slot as u8, 0) + } + + fn my_deps(&self, _offset: usize, slot: usize) -> Vec { + // For now, don't put any dependencies in slot zero. They could be + // added here if we someday implement checking these. + if slot == 0 { + vec![] + } else { + match self.test.depends[self.number] { + DepType::Nothing => vec![], + DepType::NoUpgrade => panic!("Shouldn't get to this point"), + DepType::Correct => vec![ + ImageVersion::new_synthetic(self.other_id(), slot as u8, 0) + ], + DepType::OldCorrect => vec![ + ImageVersion::new_synthetic(self.other_id(), 0, 0) + ], + DepType::Newer => vec![ + ImageVersion::new_synthetic(self.other_id(), slot as u8, 1) + ], + } + } + } + + fn other_id(&self) -> u8 { + (1 - self.number) as u8 + } +} + +impl ImageVersion { + /// Generate an image version based on some key information. The image + /// number influences the major version number (by an arbitrary factor), + /// and the slot affects the major number on the build_number. The minor + /// number can also be given to force numbers to be different. + fn new_synthetic(image_id: u8, slot: u8, minor: u8) -> ImageVersion { + ImageVersion { + major: image_id * 20 + slot, + minor, + revision: 1, + build_num: slot as u32, + } + } +} diff --git a/bootloader/mcuboot/sim/src/ecdsa_pub_key-rs.txt b/bootloader/mcuboot/sim/src/ecdsa_pub_key-rs.txt new file mode 100644 index 0000000..3d86436 --- /dev/null +++ b/bootloader/mcuboot/sim/src/ecdsa_pub_key-rs.txt @@ -0,0 +1,32 @@ +static ECDSA256_PUB_KEY: &[u8] = &[ + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x2a, 0xcb, 0x40, 0x3c, 0xe8, + 0xfe, 0xed, 0x5b, 0xa4, 0x49, 0x95, 0xa1, 0xa9, + 0x1d, 0xae, 0xe8, 0xdb, 0xbe, 0x19, 0x37, 0xcd, + 0x14, 0xfb, 0x2f, 0x24, 0x57, 0x37, 0xe5, 0x95, + 0x39, 0x88, 0xd9, 0x94, 0xb9, 0xd6, 0x5a, 0xeb, + 0xd7, 0xcd, 0xd5, 0x30, 0x8a, 0xd6, 0xfe, 0x48, + 0xb2, 0x4a, 0x6a, 0x81, 0x0e, 0xe5, 0xf0, 0x7d, + 0x8b, 0x68, 0x34, 0xcc, 0x3a, 0x6a, 0xfc, 0x53, + 0x8e, 0xfa, 0xc1, +]; + +static ECDSAP384_PUB_KEY: &[u8] = &[ + 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, + 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, + 0x0c, 0x76, 0xca, 0xae, 0x72, 0x3a, 0xa5, 0xe8, + 0xf0, 0xd4, 0xf1, 0x16, 0xb5, 0x02, 0xef, 0x77, + 0xa1, 0x1b, 0x93, 0x61, 0x78, 0xc0, 0x09, 0x26, + 0x7b, 0x3b, 0x40, 0x9c, 0xee, 0x49, 0x85, 0xe0, + 0xc9, 0x4f, 0xe7, 0xf2, 0xba, 0x97, 0x6c, 0xf3, + 0x82, 0x65, 0x14, 0x2c, 0xf5, 0x0c, 0x73, 0x33, + 0x4d, 0x32, 0xe7, 0x9b, 0xd3, 0x42, 0xcc, 0x95, + 0x5a, 0xe5, 0xe2, 0xf5, 0xf4, 0x6e, 0x45, 0xe0, + 0xed, 0x20, 0x35, 0x5c, 0xaf, 0x52, 0x35, 0x81, + 0xd4, 0xdc, 0x9c, 0xe3, 0x9e, 0x22, 0x3e, 0xfb, + 0x3f, 0x22, 0x10, 0xda, 0x70, 0x03, 0x37, 0xad, + 0xa8, 0xf2, 0x48, 0xfe, 0x3a, 0x60, 0x69, 0xa5, +]; diff --git a/bootloader/mcuboot/sim/src/ed25519_pub_key-rs.txt b/bootloader/mcuboot/sim/src/ed25519_pub_key-rs.txt new file mode 100644 index 0000000..8abce32 --- /dev/null +++ b/bootloader/mcuboot/sim/src/ed25519_pub_key-rs.txt @@ -0,0 +1,8 @@ +static ED25519_PUB_KEY: &[u8] = &[ + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x70, 0x03, 0x21, 0x00, 0xd4, 0xb3, 0x1b, 0xa4, + 0x9a, 0x3a, 0xdd, 0x3f, 0x82, 0x5d, 0x10, 0xca, + 0x7f, 0x31, 0xb5, 0x0b, 0x0d, 0xe8, 0x7f, 0x37, + 0xcc, 0xc4, 0x9f, 0x1a, 0x40, 0x3a, 0x5c, 0x13, + 0x20, 0xff, 0xb4, 0xe0, +]; diff --git a/bootloader/mcuboot/sim/src/image.rs b/bootloader/mcuboot/sim/src/image.rs new file mode 100644 index 0000000..4cd6488 --- /dev/null +++ b/bootloader/mcuboot/sim/src/image.rs @@ -0,0 +1,2332 @@ +// Copyright (c) 2019-2021 Linaro LTD +// Copyright (c) 2019-2020 JUUL Labs +// Copyright (c) 2019-2023 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +use byteorder::{ + LittleEndian, WriteBytesExt, +}; +use log::{ + Level::Info, + error, + info, + log_enabled, + warn, +}; +use rand::{ + Rng, RngCore, SeedableRng, + rngs::SmallRng, +}; +use std::{ + collections::{BTreeMap, HashSet}, io::{Cursor, Write}, mem, rc::Rc, slice +}; +use aes::{ + Aes128, + Aes128Ctr, + Aes256, + Aes256Ctr, + NewBlockCipher, +}; +use cipher::{ + FromBlockCipher, + generic_array::GenericArray, + StreamCipher, + }; + +use simflash::{Flash, SimFlash, SimMultiFlash}; +use mcuboot_sys::{c, AreaDesc, FlashId, RamBlock}; +use crate::{ + ALL_DEVICES, + DeviceName, +}; +use crate::caps::Caps; +use crate::depends::{ + BoringDep, + Depender, + DepTest, + DepType, + NO_DEPS, + PairDep, + UpgradeInfo, +}; +use crate::tlv::{ManifestGen, TlvGen, TlvFlags}; +use crate::utils::align_up; +use typenum::{U32, U16}; + +/// For testing, use a non-zero offset for the ram-load, to make sure the offset is getting used +/// properly, but the value is not really that important. +const RAM_LOAD_ADDR: u32 = 1024; + +/// A builder for Images. This describes a single run of the simulator, +/// capturing the configuration of a particular set of devices, including +/// the flash simulator(s) and the information about the slots. +#[derive(Clone)] +pub struct ImagesBuilder { + flash: SimMultiFlash, + areadesc: Rc, + slots: Vec<[SlotInfo; 2]>, + ram: RamData, +} + +/// Images represents the state of a simulation for a given set of images. +/// The flash holds the state of the simulated flash, whereas primaries +/// and upgrades hold the expected contents of these images. +pub struct Images { + flash: SimMultiFlash, + areadesc: Rc, + images: Vec, + total_count: Option, + ram: RamData, +} + +/// When doing multi-image, there is an instance of this information for +/// each of the images. Single image there will be one of these. +struct OneImage { + slots: [SlotInfo; 2], + primaries: ImageData, + upgrades: ImageData, +} + +/// The Rust-side representation of an image. For unencrypted images, this +/// is just the unencrypted payload. For encrypted images, we store both +/// the encrypted and the plaintext. +struct ImageData { + size: usize, + plain: Vec, + cipher: Option>, +} + +/// For the RamLoad test cases, we need a contiguous area of RAM to load these images into. For +/// multi-image builds, these may not correspond with the offsets. This has to be computed early, +/// before images are built, because each image contains the offset where the image is to be loaded +/// in the header, which is contained within the signature. +#[derive(Clone, Debug)] +struct RamData { + places: BTreeMap, + total: u32, +} + +/// Every slot is indexed by this key. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +struct SlotKey { + dev_id: u8, + base_off: usize, +} + +#[derive(Clone, Debug)] +struct SlotPlace { + offset: u32, + size: u32, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ImageManipulation { + None, + BadSignature, + WrongOffset, + IgnoreRamLoadFlag, + /// True to use same address, + /// false to overlap by 1 byte + OverlapImages(bool), + CorruptHigherVersionImage, +} + + +impl ImagesBuilder { + /// Construct a new image builder for the given device. Returns + /// Some(builder) if is possible to test this configuration, or None if + /// not possible (for example, if there aren't enough image slots). + pub fn new(device: DeviceName, align: usize, erased_val: u8) -> Result { + let (flash, areadesc, unsupported_caps) = Self::make_device(device, align, erased_val); + + for cap in unsupported_caps { + if cap.present() { + return Err(format!("unsupported {:?}", cap)); + } + } + + let num_images = Caps::get_num_images(); + + let mut slots = Vec::with_capacity(num_images); + for image in 0..num_images { + // This mapping must match that defined in + // `boot/zephyr/include/sysflash/sysflash.h`. + let id0 = match image { + 0 => FlashId::Image0, + 1 => FlashId::Image2, + _ => panic!("More than 2 images not supported"), + }; + let (primary_base, primary_len, primary_dev_id) = match areadesc.find(id0) { + Some(info) => info, + None => return Err("insufficient partitions".to_string()), + }; + let id1 = match image { + 0 => FlashId::Image1, + 1 => FlashId::Image3, + _ => panic!("More than 2 images not supported"), + }; + let (secondary_base, secondary_len, secondary_dev_id) = match areadesc.find(id1) { + Some(info) => info, + None => return Err("insufficient partitions".to_string()), + }; + + let offset_from_end = c::boot_magic_sz() + c::boot_max_align() * 4; + + // Construct a primary image. + let primary = SlotInfo { + base_off: primary_base as usize, + trailer_off: primary_base + primary_len - offset_from_end, + len: primary_len as usize, + dev_id: primary_dev_id, + index: 0, + }; + + // And an upgrade image. + let secondary = SlotInfo { + base_off: secondary_base as usize, + trailer_off: secondary_base + secondary_len - offset_from_end, + len: secondary_len as usize, + dev_id: secondary_dev_id, + index: 1, + }; + + slots.push([primary, secondary]); + } + + let ram = RamData::new(&slots); + + Ok(ImagesBuilder { + flash, + areadesc, + slots, + ram, + }) + } + + pub fn each_device(f: F) + where F: Fn(Self) + { + for &dev in ALL_DEVICES { + for &align in test_alignments() { + for &erased_val in &[0, 0xff] { + match Self::new(dev, align, erased_val) { + Ok(run) => f(run), + Err(msg) => warn!("Skipping {}: {}", dev, msg), + } + } + } + } + } + + /// Construct an `Images` that doesn't expect an upgrade to happen. + pub fn make_no_upgrade_image(self, deps: &DepTest, img_manipulation: ImageManipulation) -> Images { + let num_images = self.num_images(); + let mut flash = self.flash; + let ram = self.ram.clone(); // TODO: Avoid this clone. + let mut higher_version_corrupted = false; + let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { + let dep: Box = if num_images > 1 { + Box::new(PairDep::new(num_images, image_num, deps)) + } else { + Box::new(BoringDep::new(image_num, deps)) + }; + + let (primaries,upgrades) = if img_manipulation == ImageManipulation::CorruptHigherVersionImage && !higher_version_corrupted { + higher_version_corrupted = true; + let prim = install_image(&mut flash, &slots[0], + maximal(42784), &ram, &*dep, ImageManipulation::None, Some(0)); + let upgr = match deps.depends[image_num] { + DepType::NoUpgrade => install_no_image(), + _ => install_image(&mut flash, &slots[1], + maximal(46928), &ram, &*dep, ImageManipulation::BadSignature, Some(0)) + }; + (prim, upgr) + } else { + let prim = install_image(&mut flash, &slots[0], + maximal(42784), &ram, &*dep, img_manipulation, Some(0)); + let upgr = match deps.depends[image_num] { + DepType::NoUpgrade => install_no_image(), + _ => install_image(&mut flash, &slots[1], + maximal(46928), &ram, &*dep, img_manipulation, Some(0)) + }; + (prim, upgr) + }; + OneImage { + slots, + primaries, + upgrades, + }}).collect(); + install_ptable(&mut flash, &self.areadesc); + Images { + flash, + areadesc: self.areadesc, + images, + total_count: None, + ram: self.ram, + } + } + + pub fn make_image(self, deps: &DepTest, permanent: bool) -> Images { + let mut images = self.make_no_upgrade_image(deps, ImageManipulation::None); + for image in &images.images { + mark_upgrade(&mut images.flash, &image.slots[1]); + } + + // The count is meaningless if no flash operations are performed. + if !Caps::modifies_flash() { + return images; + } + + // upgrades without fails, counts number of flash operations + let total_count = match images.run_basic_upgrade(permanent) { + Some(v) => v, + None => + if deps.upgrades.iter().any(|u| *u == UpgradeInfo::Held) { + 0 + } else { + panic!("Unable to perform basic upgrade"); + } + }; + + images.total_count = Some(total_count); + images + } + + pub fn make_bad_secondary_slot_image(self) -> Images { + let mut bad_flash = self.flash; + let ram = self.ram.clone(); // TODO: Avoid this clone. + let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { + let dep = BoringDep::new(image_num, &NO_DEPS); + let primaries = install_image(&mut bad_flash, &slots[0], + maximal(32784), &ram, &dep, ImageManipulation::None, Some(0)); + let upgrades = install_image(&mut bad_flash, &slots[1], + maximal(41928), &ram, &dep, ImageManipulation::BadSignature, Some(0)); + OneImage { + slots, + primaries, + upgrades, + }}).collect(); + Images { + flash: bad_flash, + areadesc: self.areadesc, + images, + total_count: None, + ram: self.ram, + } + } + + pub fn make_oversized_secondary_slot_image(self) -> Images { + let mut bad_flash = self.flash; + let ram = self.ram.clone(); // TODO: Avoid this clone. + let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { + let dep = BoringDep::new(image_num, &NO_DEPS); + let primaries = install_image(&mut bad_flash, &slots[0], + maximal(32784), &ram, &dep, ImageManipulation::None, Some(0)); + let upgrades = install_image(&mut bad_flash, &slots[1], + ImageSize::Oversized, &ram, &dep, ImageManipulation::None, Some(0)); + OneImage { + slots, + primaries, + upgrades, + }}).collect(); + Images { + flash: bad_flash, + areadesc: self.areadesc, + images, + total_count: None, + ram: self.ram, + } + } + + pub fn make_erased_secondary_image(self) -> Images { + let mut flash = self.flash; + let ram = self.ram.clone(); // TODO: Avoid this clone. + let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { + let dep = BoringDep::new(image_num, &NO_DEPS); + let primaries = install_image(&mut flash, &slots[0], + maximal(32784), &ram, &dep,ImageManipulation::None, Some(0)); + let upgrades = install_no_image(); + OneImage { + slots, + primaries, + upgrades, + }}).collect(); + Images { + flash, + areadesc: self.areadesc, + images, + total_count: None, + ram: self.ram, + } + } + + pub fn make_bootstrap_image(self) -> Images { + let mut flash = self.flash; + let ram = self.ram.clone(); // TODO: Avoid this clone. + let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { + let dep = BoringDep::new(image_num, &NO_DEPS); + let primaries = install_no_image(); + let upgrades = install_image(&mut flash, &slots[1], + maximal(32784), &ram, &dep, ImageManipulation::None, Some(0)); + OneImage { + slots, + primaries, + upgrades, + }}).collect(); + Images { + flash, + areadesc: self.areadesc, + images, + total_count: None, + ram: self.ram, + } + } + + pub fn make_oversized_bootstrap_image(self) -> Images { + let mut flash = self.flash; + let ram = self.ram.clone(); // TODO: Avoid this clone. + let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { + let dep = BoringDep::new(image_num, &NO_DEPS); + let primaries = install_no_image(); + let upgrades = install_image(&mut flash, &slots[1], + ImageSize::Oversized, &ram, &dep, ImageManipulation::None, Some(0)); + OneImage { + slots, + primaries, + upgrades, + }}).collect(); + Images { + flash, + areadesc: self.areadesc, + images, + total_count: None, + ram: self.ram, + } + } + + /// If security_cnt is None then do not add a security counter TLV, otherwise add the specified value. + pub fn make_image_with_security_counter(self, security_cnt: Option) -> Images { + let mut flash = self.flash; + let ram = self.ram.clone(); // TODO: Avoid this clone. + let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { + let dep = BoringDep::new(image_num, &NO_DEPS); + let primaries = install_image(&mut flash, &slots[0], + maximal(32784), &ram, &dep, ImageManipulation::None, security_cnt); + let upgrades = install_image(&mut flash, &slots[1], + maximal(41928), &ram, &dep, ImageManipulation::None, security_cnt.map(|v| v + 1)); + OneImage { + slots, + primaries, + upgrades, + }}).collect(); + Images { + flash, + areadesc: self.areadesc, + images, + total_count: None, + ram: self.ram, + } + } + + /// Build the Flash and area descriptor for a given device. + pub fn make_device(device: DeviceName, align: usize, erased_val: u8) -> (SimMultiFlash, Rc, &'static [Caps]) { + match device { + DeviceName::Stm32f4 => { + // STM style flash. Large sectors, with a large scratch area. + // The flash layout as described is not present in any real STM32F4 device, but it + // serves to exercise support for sectors of varying sizes inside a single slot, + // as long as they are compatible in both slots and all fit in the scratch. + let dev = SimFlash::new(vec![16 * 1024, 16 * 1024, 16 * 1024, 16 * 1024, 64 * 1024, + 32 * 1024, 32 * 1024, 64 * 1024, + 32 * 1024, 32 * 1024, 64 * 1024, + 128 * 1024], + align as usize, erased_val); + let dev_id = 0; + let mut areadesc = AreaDesc::new(); + areadesc.add_flash_sectors(dev_id, &dev); + areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id); + areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id); + areadesc.add_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id); + + let mut flash = SimMultiFlash::new(); + flash.insert(dev_id, dev); + (flash, Rc::new(areadesc), &[Caps::SwapUsingMove]) + } + DeviceName::K64f => { + // NXP style flash. Small sectors, one small sector for scratch. + let dev = SimFlash::new(vec![4096; 128], align as usize, erased_val); + + let dev_id = 0; + let mut areadesc = AreaDesc::new(); + areadesc.add_flash_sectors(dev_id, &dev); + areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id); + areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id); + areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id); + + let mut flash = SimMultiFlash::new(); + flash.insert(dev_id, dev); + (flash, Rc::new(areadesc), &[]) + } + DeviceName::K64fBig => { + // Simulating an STM style flash on top of an NXP style flash. Underlying flash device + // uses small sectors, but we tell the bootloader they are large. + let dev = SimFlash::new(vec![4096; 128], align as usize, erased_val); + + let dev_id = 0; + let mut areadesc = AreaDesc::new(); + areadesc.add_flash_sectors(dev_id, &dev); + areadesc.add_simple_image(0x020000, 0x020000, FlashId::Image0, dev_id); + areadesc.add_simple_image(0x040000, 0x020000, FlashId::Image1, dev_id); + areadesc.add_simple_image(0x060000, 0x020000, FlashId::ImageScratch, dev_id); + + let mut flash = SimMultiFlash::new(); + flash.insert(dev_id, dev); + (flash, Rc::new(areadesc), &[Caps::SwapUsingMove]) + } + DeviceName::Nrf52840 => { + // Simulating the flash on the nrf52840 with partitions set up so that the scratch size + // does not divide into the image size. + let dev = SimFlash::new(vec![4096; 128], align as usize, erased_val); + + let dev_id = 0; + let mut areadesc = AreaDesc::new(); + areadesc.add_flash_sectors(dev_id, &dev); + areadesc.add_image(0x008000, 0x034000, FlashId::Image0, dev_id); + areadesc.add_image(0x03c000, 0x034000, FlashId::Image1, dev_id); + areadesc.add_image(0x070000, 0x00d000, FlashId::ImageScratch, dev_id); + + let mut flash = SimMultiFlash::new(); + flash.insert(dev_id, dev); + (flash, Rc::new(areadesc), &[]) + } + DeviceName::Nrf52840UnequalSlots => { + let dev = SimFlash::new(vec![4096; 128], align as usize, erased_val); + + let dev_id = 0; + let mut areadesc = AreaDesc::new(); + areadesc.add_flash_sectors(dev_id, &dev); + areadesc.add_image(0x008000, 0x03c000, FlashId::Image0, dev_id); + areadesc.add_image(0x044000, 0x03b000, FlashId::Image1, dev_id); + + let mut flash = SimMultiFlash::new(); + flash.insert(dev_id, dev); + (flash, Rc::new(areadesc), &[Caps::SwapUsingScratch, Caps::OverwriteUpgrade]) + } + DeviceName::Nrf52840SpiFlash => { + // Simulate nrf52840 with external SPI flash. The external SPI flash + // has a larger sector size so for now store scratch on that flash. + let dev0 = SimFlash::new(vec![4096; 128], align as usize, erased_val); + let dev1 = SimFlash::new(vec![8192; 64], align as usize, erased_val); + + let mut areadesc = AreaDesc::new(); + areadesc.add_flash_sectors(0, &dev0); + areadesc.add_flash_sectors(1, &dev1); + + areadesc.add_image(0x008000, 0x068000, FlashId::Image0, 0); + areadesc.add_image(0x000000, 0x068000, FlashId::Image1, 1); + areadesc.add_image(0x068000, 0x018000, FlashId::ImageScratch, 1); + + let mut flash = SimMultiFlash::new(); + flash.insert(0, dev0); + flash.insert(1, dev1); + (flash, Rc::new(areadesc), &[Caps::SwapUsingMove]) + } + DeviceName::K64fMulti => { + // NXP style flash, but larger, to support multiple images. + let dev = SimFlash::new(vec![4096; 256], align as usize, erased_val); + + let dev_id = 0; + let mut areadesc = AreaDesc::new(); + areadesc.add_flash_sectors(dev_id, &dev); + areadesc.add_image(0x020000, 0x020000, FlashId::Image0, dev_id); + areadesc.add_image(0x040000, 0x020000, FlashId::Image1, dev_id); + areadesc.add_image(0x060000, 0x001000, FlashId::ImageScratch, dev_id); + areadesc.add_image(0x080000, 0x020000, FlashId::Image2, dev_id); + areadesc.add_image(0x0a0000, 0x020000, FlashId::Image3, dev_id); + + let mut flash = SimMultiFlash::new(); + flash.insert(dev_id, dev); + (flash, Rc::new(areadesc), &[]) + } + } + } + + pub fn num_images(&self) -> usize { + self.slots.len() + } +} + +impl Images { + /// A simple upgrade without forced failures. + /// + /// Returns the number of flash operations which can later be used to + /// inject failures at chosen steps. Returns None if it was unable to + /// count the operations in a basic upgrade. + pub fn run_basic_upgrade(&self, permanent: bool) -> Option { + let (flash, total_count) = self.try_upgrade(None, permanent); + info!("Total flash operation count={}", total_count); + + if !self.verify_images(&flash, 0, 1) { + warn!("Image mismatch after first boot"); + None + } else { + Some(total_count) + } + } + + pub fn run_bootstrap(&self) -> bool { + let mut flash = self.flash.clone(); + let mut fails = 0; + + if Caps::Bootstrap.present() { + info!("Try bootstraping image in the primary"); + + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed first boot"); + fails += 1; + } + + if !self.verify_images(&flash, 0, 1) { + warn!("Image in the first slot was not bootstrapped"); + fails += 1; + } + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + } + + if fails > 0 { + error!("Expected trailer on secondary slot to be erased"); + } + + fails > 0 + } + + pub fn run_oversized_bootstrap(&self) -> bool { + let mut flash = self.flash.clone(); + let mut fails = 0; + + if Caps::Bootstrap.present() { + info!("Try bootstraping image in the primary"); + + let boot_result = c::boot_go(&mut flash, &self.areadesc, None, None, false).interrupted(); + + if boot_result { + warn!("Failed first boot"); + fails += 1; + } + + if self.verify_images(&flash, 0, 1) { + warn!("Image in the first slot was not bootstrapped"); + fails += 1; + } + + if self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + } + + if fails > 0 { + error!("Expected trailer on secondary slot to be erased"); + } + + fails > 0 + } + + + /// Test a simple upgrade, with dependencies given, and verify that the + /// image does as is described in the test. + pub fn run_check_deps(&self, deps: &DepTest) -> bool { + if !Caps::modifies_flash() { + return false; + } + + let (flash, _) = self.try_upgrade(None, true); + + self.verify_dep_images(&flash, deps) + } + + fn is_swap_upgrade(&self) -> bool { + Caps::SwapUsingScratch.present() || Caps::SwapUsingMove.present() + } + + pub fn run_basic_revert(&self) -> bool { + if Caps::OverwriteUpgrade.present() || !Caps::modifies_flash() { + return false; + } + + let mut fails = 0; + + // FIXME: this test would also pass if no swap is ever performed??? + if self.is_swap_upgrade() { + for count in 2 .. 5 { + info!("Try revert: {}", count); + let flash = self.try_revert(count); + if !self.verify_images(&flash, 0, 0) { + error!("Revert failure on count {}", count); + fails += 1; + } + } + } + + fails > 0 + } + + pub fn run_perm_with_fails(&self) -> bool { + if !Caps::modifies_flash() { + return false; + } + + let mut fails = 0; + let total_flash_ops = self.total_count.unwrap(); + + if skip_slow_test() { + return false; + } + + // Let's try an image halfway through. + for i in 1 .. total_flash_ops { + info!("Try interruption at {}", i); + let (flash, count) = self.try_upgrade(Some(i), true); + info!("Second boot, count={}", count); + if !self.verify_images(&flash, 0, 1) { + warn!("FAIL at step {} of {}", i, total_flash_ops); + fails += 1; + } + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + + if !self.verify_trailers(&flash, 1, BOOT_MAGIC_UNSET, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the secondary slot"); + fails += 1; + } + + if self.is_swap_upgrade() && !self.verify_images(&flash, 1, 0) { + warn!("Secondary slot FAIL at step {} of {}", + i, total_flash_ops); + fails += 1; + } + } + + if fails > 0 { + error!("{} out of {} failed {:.2}%", fails, total_flash_ops, + fails as f32 * 100.0 / total_flash_ops as f32); + } + + fails > 0 + } + + pub fn run_perm_with_random_fails(&self, total_fails: usize) -> bool { + if !Caps::modifies_flash() { + return false; + } + + let mut fails = 0; + let total_flash_ops = self.total_count.unwrap(); + let (flash, total_counts) = self.try_random_fails(total_flash_ops, total_fails); + info!("Random interruptions at reset points={:?}", total_counts); + + let primary_slot_ok = self.verify_images(&flash, 0, 1); + let secondary_slot_ok = if self.is_swap_upgrade() { + // TODO: This result is ignored. + self.verify_images(&flash, 1, 0) + } else { + true + }; + if !primary_slot_ok || !secondary_slot_ok { + error!("Image mismatch after random interrupts: primary slot={} \ + secondary slot={}", + if primary_slot_ok { "ok" } else { "fail" }, + if secondary_slot_ok { "ok" } else { "fail" }); + fails += 1; + } + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + error!("Mismatched trailer for the primary slot"); + fails += 1; + } + if !self.verify_trailers(&flash, 1, BOOT_MAGIC_UNSET, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + error!("Mismatched trailer for the secondary slot"); + fails += 1; + } + + if fails > 0 { + error!("Error testing perm upgrade with {} fails", total_fails); + } + + fails > 0 + } + + pub fn run_revert_with_fails(&self) -> bool { + if Caps::OverwriteUpgrade.present() || !Caps::modifies_flash() { + return false; + } + + let mut fails = 0; + + if skip_slow_test() { + return false; + } + + if self.is_swap_upgrade() { + for i in 1 .. self.total_count.unwrap() { + info!("Try interruption at {}", i); + if self.try_revert_with_fail_at(i) { + error!("Revert failed at interruption {}", i); + fails += 1; + } + } + } + + fails > 0 + } + + pub fn run_norevert(&self) -> bool { + if Caps::OverwriteUpgrade.present() || !Caps::modifies_flash() { + return false; + } + + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try norevert"); + + // First do a normal upgrade... + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed first boot"); + fails += 1; + } + + //FIXME: copy_done is written by boot_go, is it ok if no copy + // was ever done? + + if !self.verify_images(&flash, 0, 1) { + warn!("Primary slot image verification FAIL"); + fails += 1; + } + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_UNSET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + if !self.verify_trailers(&flash, 1, BOOT_MAGIC_UNSET, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the secondary slot"); + fails += 1; + } + + // Marks image in the primary slot as permanent, + // no revert should happen... + self.mark_permanent_upgrades(&mut flash, 0); + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed second boot"); + fails += 1; + } + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + if !self.verify_images(&flash, 0, 1) { + warn!("Failed image verification"); + fails += 1; + } + + if fails > 0 { + error!("Error running upgrade without revert"); + } + + fails > 0 + } + + // Test taht too big upgrade image will be rejected + pub fn run_oversizefail_upgrade(&self) -> bool { + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try upgrade image with to big size"); + + // Only perform this test if an upgrade is expected to happen. + if !Caps::modifies_flash() { + info!("Skipping upgrade image with bad signature"); + return false; + } + + self.mark_upgrades(&mut flash, 0); + self.mark_permanent_upgrades(&mut flash, 0); + self.mark_upgrades(&mut flash, 1); + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_UNSET) { + warn!("1. Mismatched trailer for the primary slot"); + fails += 1; + } + + // Run the bootloader... + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed first boot"); + fails += 1; + } + + // State should not have changed + if !self.verify_images(&flash, 0, 0) { + warn!("Failed image verification"); + fails += 1; + } + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_UNSET) { + warn!("2. Mismatched trailer for the primary slot"); + fails += 1; + } + + if fails > 0 { + error!("Expected an upgrade failure when image has to big size"); + } + + fails > 0 + } + + // Test that an upgrade is rejected. Assumes that the image was build + // such that the upgrade is instead a downgrade. + pub fn run_nodowngrade(&self) -> bool { + if !Caps::DowngradePrevention.present() { + return false; + } + + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try no downgrade"); + + // First, do a normal upgrade. + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed first boot"); + fails += 1; + } + + if !self.verify_images(&flash, 0, 0) { + warn!("Failed verification after downgrade rejection"); + fails += 1; + } + + if fails > 0 { + error!("Error testing downgrade rejection"); + } + + fails > 0 + } + + // Tests a new image written to the primary slot that already has magic and + // image_ok set while there is no image on the secondary slot, so no revert + // should ever happen... + pub fn run_norevert_newimage(&self) -> bool { + if !Caps::modifies_flash() { + info!("Skipping run_norevert_newimage, as configuration doesn't modify flash"); + return false; + } + + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try non-revert on imgtool generated image"); + + self.mark_upgrades(&mut flash, 0); + + // This simulates writing an image created by imgtool to + // the primary slot + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + + // Run the bootloader... + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed first boot"); + fails += 1; + } + + // State should not have changed + if !self.verify_images(&flash, 0, 0) { + warn!("Failed image verification"); + fails += 1; + } + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + if !self.verify_trailers(&flash, 1, BOOT_MAGIC_UNSET, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the secondary slot"); + fails += 1; + } + + if fails > 0 { + error!("Expected a non revert with new image"); + } + + fails > 0 + } + + // Tests a new image written to the primary slot that already has magic and + // image_ok set while there is no image on the secondary slot, so no revert + // should ever happen... + pub fn run_signfail_upgrade(&self) -> bool { + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try upgrade image with bad signature"); + + // Only perform this test if an upgrade is expected to happen. + if !Caps::modifies_flash() { + info!("Skipping upgrade image with bad signature"); + return false; + } + + self.mark_upgrades(&mut flash, 0); + self.mark_permanent_upgrades(&mut flash, 0); + self.mark_upgrades(&mut flash, 1); + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + + // Run the bootloader... + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed first boot"); + fails += 1; + } + + // State should not have changed + if !self.verify_images(&flash, 0, 0) { + warn!("Failed image verification"); + fails += 1; + } + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + + if fails > 0 { + error!("Expected an upgrade failure when image has bad signature"); + } + + fails > 0 + } + + // Should detect there is a leftover trailer in an otherwise erased + // secondary slot and erase its trailer. + pub fn run_secondary_leftover_trailer(&self) -> bool { + if !Caps::modifies_flash() { + return false; + } + + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try with a leftover trailer in the secondary; must be erased"); + + // Add a trailer on the secondary slot + self.mark_permanent_upgrades(&mut flash, 1); + self.mark_upgrades(&mut flash, 1); + + // Run the bootloader... + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed first boot"); + fails += 1; + } + + // State should not have changed + if !self.verify_images(&flash, 0, 0) { + warn!("Failed image verification"); + fails += 1; + } + if !self.verify_trailers(&flash, 1, BOOT_MAGIC_UNSET, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the secondary slot"); + fails += 1; + } + + if fails > 0 { + error!("Expected trailer on secondary slot to be erased"); + } + + fails > 0 + } + + fn trailer_sz(&self, align: usize) -> usize { + c::boot_trailer_sz(align as u32) as usize + } + + fn status_sz(&self, align: usize) -> usize { + c::boot_status_sz(align as u32) as usize + } + + /// This test runs a simple upgrade with no fails in the images, but + /// allowing for fails in the status area. This should run to the end + /// and warn that write fails were detected... + pub fn run_with_status_fails_complete(&self) -> bool { + if !Caps::ValidatePrimarySlot.present() || !Caps::modifies_flash() { + return false; + } + + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try swap with status fails"); + + self.mark_permanent_upgrades(&mut flash, 1); + self.mark_bad_status_with_rate(&mut flash, 0, 1.0); + + let result = c::boot_go(&mut flash, &self.areadesc, None, None, true); + if !result.success() { + warn!("Failed!"); + fails += 1; + } + + // Failed writes to the marked "bad" region don't assert anymore. + // Any detected assert() is happening in another part of the code. + if result.asserts() != 0 { + warn!("At least one assert() was called"); + fails += 1; + } + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot"); + fails += 1; + } + + if !self.verify_images(&flash, 0, 1) { + warn!("Failed image verification"); + fails += 1; + } + + info!("validate primary slot enabled; \ + re-run of boot_go should just work"); + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Failed!"); + fails += 1; + } + + if fails > 0 { + error!("Error running upgrade with status write fails"); + } + + fails > 0 + } + + /// This test runs a simple upgrade with no fails in the images, but + /// allowing for fails in the status area. This should run to the end + /// and warn that write fails were detected... + pub fn run_with_status_fails_with_reset(&self) -> bool { + if Caps::OverwriteUpgrade.present() || !Caps::modifies_flash() { + false + } else if Caps::ValidatePrimarySlot.present() { + + let mut flash = self.flash.clone(); + let mut fails = 0; + let mut count = self.total_count.unwrap() / 2; + + //info!("count={}\n", count); + + info!("Try interrupted swap with status fails"); + + self.mark_permanent_upgrades(&mut flash, 1); + self.mark_bad_status_with_rate(&mut flash, 0, 0.5); + + // Should not fail, writing to bad regions does not assert + let asserts = c::boot_go(&mut flash, &self.areadesc, + Some(&mut count), None, true).asserts(); + if asserts != 0 { + warn!("At least one assert() was called"); + fails += 1; + } + + self.reset_bad_status(&mut flash, 0); + + info!("Resuming an interrupted swap operation"); + let asserts = c::boot_go(&mut flash, &self.areadesc, None, None, + true).asserts(); + + // This might throw no asserts, for large sector devices, where + // a single failure writing is indistinguishable from no failure, + // or throw a single assert for small sector devices that fail + // multiple times... + if asserts > 1 { + warn!("Expected single assert validating the primary slot, \ + more detected {}", asserts); + fails += 1; + } + + if fails > 0 { + error!("Error running upgrade with status write fails"); + } + + fails > 0 + } else { + let mut flash = self.flash.clone(); + let mut fails = 0; + + info!("Try interrupted swap with status fails"); + + self.mark_permanent_upgrades(&mut flash, 1); + self.mark_bad_status_with_rate(&mut flash, 0, 1.0); + + // This is expected to fail while writing to bad regions... + let asserts = c::boot_go(&mut flash, &self.areadesc, None, None, + true).asserts(); + if asserts == 0 { + warn!("No assert() detected"); + fails += 1; + } + + fails > 0 + } + } + + /// Test the direct XIP configuration. With this mode, flash images are never moved, and the + /// bootloader merely selects which partition is the proper one to boot. + pub fn run_direct_xip(&self) -> bool { + if !Caps::DirectXip.present() { + return false; + } + + // Clone the flash so we can tell if unchanged. + let mut flash = self.flash.clone(); + + let result = c::boot_go(&mut flash, &self.areadesc, None, None, true); + + // Ensure the boot was successful. + let resp = if let Some(resp) = result.resp() { + resp + } else { + panic!("Boot didn't return a valid result"); + }; + + // This configuration should always try booting from the first upgrade slot. + if let Some((offset, _, dev_id)) = self.areadesc.find(FlashId::Image1) { + assert_eq!(offset, resp.image_off as usize); + assert_eq!(dev_id, resp.flash_dev_id); + } else { + panic!("Unable to find upgrade image"); + } + false + } + + /// Test the ram-loading. + pub fn run_ram_load(&self) -> bool { + if !Caps::RamLoad.present() { + return false; + } + + // Clone the flash so we can tell if unchanged. + let mut flash = self.flash.clone(); + + // Setup ram based on the ram configuration we determined earlier for the images. + let ram = RamBlock::new(self.ram.total - RAM_LOAD_ADDR, RAM_LOAD_ADDR); + + // println!("Ram: {:#?}", self.ram); + + // Verify that the images area loaded into this. + let result = ram.invoke(|| c::boot_go(&mut flash, &self.areadesc, None, + None, true)); + if !result.success() { + error!("Failed to execute ram-load"); + return true; + } + + // Verify each image. + for image in &self.images { + let place = self.ram.lookup(&image.slots[0]); + let ram_image = ram.borrow_part(place.offset as usize - RAM_LOAD_ADDR as usize, + place.size as usize); + let src_sz = image.upgrades.size(); + if src_sz > ram_image.len() { + error!("Image ended up too large, nonsensical"); + return true; + } + let src_image = &image.upgrades.plain[0..src_sz]; + let ram_image = &ram_image[0..src_sz]; + if ram_image != src_image { + error!("Image not loaded correctly"); + return true; + } + + } + + return false; + } + + /// Test the split ram-loading. + pub fn run_split_ram_load(&self) -> bool { + if !Caps::RamLoad.present() { + return false; + } + + // Clone the flash so we can tell if unchanged. + let mut flash = self.flash.clone(); + + // Setup ram based on the ram configuration we determined earlier for the images. + let ram = RamBlock::new(self.ram.total - RAM_LOAD_ADDR, RAM_LOAD_ADDR); + + for (idx, _image) in (&self.images).iter().enumerate() { + // Verify that the images area loaded into this. + let result = ram.invoke(|| c::boot_go(&mut flash, &self.areadesc, + None, Some(idx as i32), true)); + if !result.success() { + error!("Failed to execute ram-load"); + return true; + } + } + + // Verify each image. + for image in &self.images { + let place = self.ram.lookup(&image.slots[0]); + let ram_image = ram.borrow_part(place.offset as usize - RAM_LOAD_ADDR as usize, + place.size as usize); + let src_sz = image.upgrades.size(); + if src_sz > ram_image.len() { + error!("Image ended up too large, nonsensical"); + return true; + } + let src_image = &image.upgrades.plain[0..src_sz]; + let ram_image = &ram_image[0..src_sz]; + if ram_image != src_image { + error!("Image not loaded correctly"); + return true; + } + + } + + return false; + } + + pub fn run_hw_rollback_prot(&self) -> bool { + if !Caps::HwRollbackProtection.present() { + return false; + } + + let mut flash = self.flash.clone(); + + // set the "stored" security counter to a fixed value. + c::set_security_counter(0, 30); + + let result = c::boot_go(&mut flash, &self.areadesc, None, None, true); + + if result.success() { + warn!("Successful boot when it did not suppose to happen!"); + return true; + } + let counter_val = c::get_security_counter(0); + if counter_val != 30 { + warn!("Counter was changed when it did not suppose to!"); + return true; + } + + false + } + + pub fn run_ram_load_boot_with_result(&self, expected_result: bool) -> bool { + if !Caps::RamLoad.present() { + return false; + } + // Clone the flash so we can tell if unchanged. + let mut flash = self.flash.clone(); + + // Create RAM config. + let ram = RamBlock::new(self.ram.total - RAM_LOAD_ADDR, RAM_LOAD_ADDR); + + // Run the bootloader, and verify that it couldn't run to completion. + let result = ram.invoke(|| c::boot_go(&mut flash, &self.areadesc, None, + None, true)); + + if result.success() != expected_result { + error!("RAM load boot result was not of the expected value! (was: {}, expected: {})", result.success(), expected_result); + return true; + } + + false + } + + /// Adds a new flash area that fails statistically + fn mark_bad_status_with_rate(&self, flash: &mut SimMultiFlash, slot: usize, + rate: f32) { + if Caps::OverwriteUpgrade.present() { + return; + } + + // Set this for each image. + for image in &self.images { + let dev_id = &image.slots[slot].dev_id; + let dev = flash.get_mut(&dev_id).unwrap(); + let align = dev.align(); + let off = &image.slots[slot].base_off; + let len = &image.slots[slot].len; + let status_off = off + len - self.trailer_sz(align); + + // Mark the status area as a bad area + let _ = dev.add_bad_region(status_off, self.status_sz(align), rate); + } + } + + fn reset_bad_status(&self, flash: &mut SimMultiFlash, slot: usize) { + if !Caps::ValidatePrimarySlot.present() { + return; + } + + for image in &self.images { + let dev_id = &image.slots[slot].dev_id; + let dev = flash.get_mut(&dev_id).unwrap(); + dev.reset_bad_regions(); + + // Disabling write verification the only assert triggered by + // boot_go should be checking for integrity of status bytes. + dev.set_verify_writes(false); + } + } + + /// Test a boot, optionally stopping after 'n' flash options. Returns a count + /// of the number of flash operations done total. + fn try_upgrade(&self, stop: Option, permanent: bool) -> (SimMultiFlash, i32) { + // Clone the flash to have a new copy. + let mut flash = self.flash.clone(); + + if permanent { + self.mark_permanent_upgrades(&mut flash, 1); + } + + let mut counter = stop.unwrap_or(0); + + let (first_interrupted, count) = match c::boot_go(&mut flash, + &self.areadesc, + Some(&mut counter), + None, false) { + x if x.interrupted() => (true, stop.unwrap()), + x if x.success() => (false, -counter), + x => panic!("Unknown return: {:?}", x), + }; + + counter = 0; + if first_interrupted { + // fl.dump(); + match c::boot_go(&mut flash, &self.areadesc, Some(&mut counter), + None, false) { + x if x.interrupted() => panic!("Shouldn't stop again"), + x if x.success() => (), + x => panic!("Unknown return: {:?}", x), + } + } + + (flash, count - counter) + } + + fn try_revert(&self, count: usize) -> SimMultiFlash { + let mut flash = self.flash.clone(); + + // fl.write_file("image0.bin").unwrap(); + for i in 0 .. count { + info!("Running boot pass {}", i + 1); + assert!(c::boot_go(&mut flash, &self.areadesc, None, None, false).success_no_asserts()); + } + flash + } + + fn try_revert_with_fail_at(&self, stop: i32) -> bool { + let mut flash = self.flash.clone(); + let mut fails = 0; + + let mut counter = stop; + if !c::boot_go(&mut flash, &self.areadesc, Some(&mut counter), None, + false).interrupted() { + warn!("Should have stopped test at interruption point"); + fails += 1; + } + + // In a multi-image setup, copy done might be set if any number of + // images was already successfully swapped. + if !self.verify_trailers_loose(&flash, 0, None, None, BOOT_FLAG_UNSET) { + warn!("copy_done should be unset"); + fails += 1; + } + + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Should have finished test upgrade"); + fails += 1; + } + + if !self.verify_images(&flash, 0, 1) { + warn!("Image in the primary slot before revert is invalid at stop={}", + stop); + fails += 1; + } + if !self.verify_images(&flash, 1, 0) { + warn!("Image in the secondary slot before revert is invalid at stop={}", + stop); + fails += 1; + } + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_UNSET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot before revert"); + fails += 1; + } + if !self.verify_trailers(&flash, 1, BOOT_MAGIC_UNSET, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the secondary slot before revert"); + fails += 1; + } + + // Do Revert + let mut counter = stop; + if !c::boot_go(&mut flash, &self.areadesc, Some(&mut counter), None, + false).interrupted() { + warn!("Should have stopped revert at interruption point"); + fails += 1; + } + + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Should have finished revert upgrade"); + fails += 1; + } + + if !self.verify_images(&flash, 0, 0) { + warn!("Image in the primary slot after revert is invalid at stop={}", + stop); + fails += 1; + } + if !self.verify_images(&flash, 1, 1) { + warn!("Image in the secondary slot after revert is invalid at stop={}", + stop); + fails += 1; + } + + if !self.verify_trailers(&flash, 0, BOOT_MAGIC_GOOD, + BOOT_FLAG_SET, BOOT_FLAG_SET) { + warn!("Mismatched trailer for the primary slot after revert"); + fails += 1; + } + if !self.verify_trailers(&flash, 1, BOOT_MAGIC_UNSET, + BOOT_FLAG_UNSET, BOOT_FLAG_UNSET) { + warn!("Mismatched trailer for the secondary slot after revert"); + fails += 1; + } + + if !c::boot_go(&mut flash, &self.areadesc, None, None, false).success() { + warn!("Should have finished 3rd boot"); + fails += 1; + } + + if !self.verify_images(&flash, 0, 0) { + warn!("Image in the primary slot is invalid on 1st boot after revert"); + fails += 1; + } + if !self.verify_images(&flash, 1, 1) { + warn!("Image in the secondary slot is invalid on 1st boot after revert"); + fails += 1; + } + + fails > 0 + } + + + fn try_random_fails(&self, total_ops: i32, count: usize) -> (SimMultiFlash, Vec) { + let mut flash = self.flash.clone(); + + self.mark_permanent_upgrades(&mut flash, 1); + + let mut rng = rand::thread_rng(); + let mut resets = vec![0i32; count]; + let mut remaining_ops = total_ops; + for reset in &mut resets { + let reset_counter = rng.gen_range(1 ..= remaining_ops / 2); + let mut counter = reset_counter; + match c::boot_go(&mut flash, &self.areadesc, Some(&mut counter), + None, false) { + x if x.interrupted() => (), + x => panic!("Unknown return: {:?}", x), + } + remaining_ops -= reset_counter; + *reset = reset_counter; + } + + match c::boot_go(&mut flash, &self.areadesc, None, None, false) { + x if x.interrupted() => panic!("Should not be have been interrupted!"), + x if x.success() => (), + x => panic!("Unknown return: {:?}", x), + } + + (flash, resets) + } + + /// Verify the image in the given flash device, the specified slot + /// against the expected image. + fn verify_images(&self, flash: &SimMultiFlash, slot: usize, against: usize) -> bool { + self.images.iter().all(|image| { + verify_image(flash, &image.slots[slot], + match against { + 0 => &image.primaries, + 1 => &image.upgrades, + _ => panic!("Invalid 'against'") + }) + }) + } + + /// Verify the images, according to the dependency test. + fn verify_dep_images(&self, flash: &SimMultiFlash, deps: &DepTest) -> bool { + for (image_num, (image, upgrade)) in self.images.iter().zip(deps.upgrades.iter()).enumerate() { + info!("Upgrade: slot:{}, {:?}", image_num, upgrade); + if !verify_image(flash, &image.slots[0], + match upgrade { + UpgradeInfo::Upgraded => &image.upgrades, + UpgradeInfo::Held => &image.primaries, + }) { + error!("Failed to upgrade properly: image: {}, upgrade: {:?}", image_num, upgrade); + return true; + } + } + + false + } + + /// Verify that at least one of the trailers of the images have the + /// specified values. + fn verify_trailers_loose(&self, flash: &SimMultiFlash, slot: usize, + magic: Option, image_ok: Option, + copy_done: Option) -> bool { + self.images.iter().any(|image| { + verify_trailer(flash, &image.slots[slot], + magic, image_ok, copy_done) + }) + } + + /// Verify that the trailers of the images have the specified + /// values. + fn verify_trailers(&self, flash: &SimMultiFlash, slot: usize, + magic: Option, image_ok: Option, + copy_done: Option) -> bool { + self.images.iter().all(|image| { + verify_trailer(flash, &image.slots[slot], + magic, image_ok, copy_done) + }) + } + + /// Mark each of the images for permanent upgrade. + fn mark_permanent_upgrades(&self, flash: &mut SimMultiFlash, slot: usize) { + for image in &self.images { + mark_permanent_upgrade(flash, &image.slots[slot]); + } + } + + /// Mark each of the images for permanent upgrade. + fn mark_upgrades(&self, flash: &mut SimMultiFlash, slot: usize) { + for image in &self.images { + mark_upgrade(flash, &image.slots[slot]); + } + } + + /// Dump out the flash image(s) to one or more files for debugging + /// purposes. The names will be written as either "{prefix}.mcubin" or + /// "{prefix}-001.mcubin" depending on how many images there are. + pub fn debug_dump(&self, prefix: &str) { + for (id, fdev) in &self.flash { + let name = if self.flash.len() == 1 { + format!("{}.mcubin", prefix) + } else { + format!("{}-{:>0}.mcubin", prefix, id) + }; + fdev.write_file(&name).unwrap(); + } + } +} + +impl RamData { + // TODO: This is not correct. The second slot of each image should be at the same address as + // the primary. + fn new(slots: &[[SlotInfo; 2]]) -> RamData { + let mut addr = RAM_LOAD_ADDR; + let mut places = BTreeMap::new(); + // println!("Setup:-------------"); + for imgs in slots { + for si in imgs { + // println!("Setup: si: {:?}", si); + let offset = addr; + let size = si.len as u32; + places.insert(SlotKey { + dev_id: si.dev_id, + base_off: si.base_off, + }, SlotPlace { offset, size }); + // println!(" load: offset: {}, size: {}", offset, size); + } + addr += imgs[0].len as u32; + } + RamData { + places, + total: addr, + } + } + + /// Lookup the ram data associated with a given flash partition. We just panic if not present, + /// because all slots used should be in the map. + fn lookup(&self, slot: &SlotInfo) -> &SlotPlace { + self.places.get(&SlotKey{dev_id: slot.dev_id, base_off: slot.base_off}) + .expect("RamData should contain all slots") + } +} + +/// Show the flash layout. +#[allow(dead_code)] +fn show_flash(flash: &dyn Flash) { + println!("---- Flash configuration ----"); + for sector in flash.sector_iter() { + println!(" {:3}: 0x{:08x}, 0x{:08x}", + sector.num, sector.base, sector.size); + } + println!(); +} + +#[derive(Debug)] +enum ImageSize { + /// Make the image the specified given size. + Given(usize), + /// Make the image as large as it can be for the partition/device. + Largest, + /// Make the image quite larger than it can be for the partition/device/ + Oversized, +} + +#[cfg(not(feature = "max-align-32"))] +fn tralier_estimation(dev: &dyn Flash) -> usize { + c::boot_trailer_sz(dev.align() as u32) as usize +} + +#[cfg(feature = "max-align-32")] +fn tralier_estimation(dev: &dyn Flash) -> usize { + + let sector_size = dev.sector_iter().next().unwrap().size as u32; + + align_up(c::boot_trailer_sz(dev.align() as u32), sector_size) as usize +} + +fn image_largest_trailer(dev: &dyn Flash) -> usize { + // Using the header size we know, the trailer size, and the slot size, we can compute + // the largest image possible. + let trailer = if Caps::OverwriteUpgrade.present() { + // This computation is incorrect, and we need to figure out the correct size. + // c::boot_status_sz(dev.align() as u32) as usize + 16 + 4 * dev.align() + } else if Caps::SwapUsingMove.present() { + let sector_size = dev.sector_iter().next().unwrap().size as u32; + align_up(c::boot_trailer_sz(dev.align() as u32), sector_size) as usize + } else if Caps::SwapUsingScratch.present() { + tralier_estimation(dev) + } else { + panic!("The maximum image size can't be calculated.") + }; + + trailer +} + +/// Install a "program" into the given image. This fakes the image header, or at least all of the +/// fields used by the given code. Returns a copy of the image that was written. +fn install_image(flash: &mut SimMultiFlash, slot: &SlotInfo, len: ImageSize, + ram: &RamData, + deps: &dyn Depender, img_manipulation: ImageManipulation, security_counter:Option) -> ImageData { + let offset = slot.base_off; + let slot_len = slot.len; + let dev_id = slot.dev_id; + let dev = flash.get_mut(&dev_id).unwrap(); + + let mut tlv: Box = Box::new(make_tlv()); + if img_manipulation == ImageManipulation::IgnoreRamLoadFlag { + tlv.set_ignore_ram_load_flag(); + } + + tlv.set_security_counter(security_counter); + + + // Add the dependencies early to the tlv. + for dep in deps.my_deps(offset, slot.index) { + tlv.add_dependency(deps.other_id(), &dep); + } + + const HDR_SIZE: usize = 32; + let place = ram.lookup(&slot); + let load_addr = if Caps::RamLoad.present() { + match img_manipulation { + ImageManipulation::WrongOffset => u32::MAX, + ImageManipulation::OverlapImages(true) => RAM_LOAD_ADDR, + ImageManipulation::OverlapImages(false) => place.offset - 1, + _ => place.offset + } + } else { + 0 + }; + + let len = match len { + ImageSize::Given(size) => size, + ImageSize::Largest => { + let trailer = image_largest_trailer(dev); + let tlv_len = tlv.estimate_size(); + info!("slot: 0x{:x}, HDR: 0x{:x}, trailer: 0x{:x}", + slot_len, HDR_SIZE, trailer); + slot_len - HDR_SIZE - trailer - tlv_len + }, + ImageSize::Oversized => { + let trailer = image_largest_trailer(dev); + let tlv_len = tlv.estimate_size(); + info!("slot: 0x{:x}, HDR: 0x{:x}, trailer: 0x{:x}", + slot_len, HDR_SIZE, trailer); + // the overflow size is rougly estimated to work for all + // configurations. It might be precise if tlv_len will be maked precise. + slot_len - HDR_SIZE - trailer - tlv_len + dev.align()*4 + } + + }; + + // Generate a boot header. Note that the size doesn't include the header. + let header = ImageHeader { + magic: tlv.get_magic(), + load_addr, + hdr_size: HDR_SIZE as u16, + protect_tlv_size: tlv.protect_size(), + img_size: len as u32, + flags: tlv.get_flags(), + ver: deps.my_version(offset, slot.index), + _pad2: 0, + }; + + let mut b_header = [0; HDR_SIZE]; + b_header[..32].clone_from_slice(header.as_raw()); + assert_eq!(b_header.len(), HDR_SIZE); + + tlv.add_bytes(&b_header); + + // The core of the image itself is just pseudorandom data. + let mut b_img = vec![0; len]; + splat(&mut b_img, offset); + + // Add some information at the start of the payload to make it easier + // to see what it is. This will fail if the image itself is too small. + { + let mut wr = Cursor::new(&mut b_img); + writeln!(&mut wr, "offset: {:#x}, dev_id: {:#x}, slot_info: {:?}", + offset, dev_id, slot).unwrap(); + writeln!(&mut wr, "version: {:?}", deps.my_version(offset, slot.index)).unwrap(); + } + + // TLV signatures work over plain image + tlv.add_bytes(&b_img); + + // Generate encrypted images + let flag = TlvFlags::ENCRYPTED_AES128 as u32 | TlvFlags::ENCRYPTED_AES256 as u32; + let is_encrypted = (tlv.get_flags() & flag) != 0; + let mut b_encimg = vec![]; + if is_encrypted { + let flag = TlvFlags::ENCRYPTED_AES256 as u32; + let aes256 = (tlv.get_flags() & flag) == flag; + tlv.generate_enc_key(); + let enc_key = tlv.get_enc_key(); + let nonce = GenericArray::from_slice(&[0; 16]); + b_encimg = b_img.clone(); + if aes256 { + let key: &GenericArray = GenericArray::from_slice(enc_key.as_slice()); + let block = Aes256::new(&key); + let mut cipher = Aes256Ctr::from_block_cipher(block, &nonce); + cipher.apply_keystream(&mut b_encimg); + } else { + let key: &GenericArray = GenericArray::from_slice(enc_key.as_slice()); + let block = Aes128::new(&key); + let mut cipher = Aes128Ctr::from_block_cipher(block, &nonce); + cipher.apply_keystream(&mut b_encimg); + } + } + + // Build the TLV itself. + if img_manipulation == ImageManipulation::BadSignature { + tlv.corrupt_sig(); + } + let mut b_tlv = tlv.make_tlv(); + + let mut buf = vec![]; + buf.append(&mut b_header.to_vec()); + buf.append(&mut b_img); + buf.append(&mut b_tlv.clone()); + + // Pad the buffer to a multiple of the flash alignment. + let align = dev.align(); + let image_sz = buf.len(); + while buf.len() % align != 0 { + buf.push(dev.erased_val()); + } + + let mut encbuf = vec![]; + if is_encrypted { + encbuf.append(&mut b_header.to_vec()); + encbuf.append(&mut b_encimg); + encbuf.append(&mut b_tlv); + + while encbuf.len() % align != 0 { + encbuf.push(dev.erased_val()); + } + } + + // Since images are always non-encrypted in the primary slot, we first write + // an encrypted image, re-read to use for verification, erase + flash + // un-encrypted. In the secondary slot the image is written un-encrypted, + // and if encryption is requested, it follows an erase + flash encrypted. + // + // In the case of ram-load when encryption is enabled both slots have to + // be encrypted so in the event when the image is in the primary slot + // the verification will fail as the image is not encrypted. + if slot.index == 0 && !Caps::RamLoad.present() { + let enc_copy: Option>; + + if is_encrypted { + dev.write(offset, &encbuf).unwrap(); + + let mut enc = vec![0u8; encbuf.len()]; + dev.read(offset, &mut enc).unwrap(); + + enc_copy = Some(enc); + + dev.erase(offset, slot_len).unwrap(); + } else { + enc_copy = None; + } + + dev.write(offset, &buf).unwrap(); + + let mut copy = vec![0u8; buf.len()]; + dev.read(offset, &mut copy).unwrap(); + + ImageData { + size: image_sz, + plain: copy, + cipher: enc_copy, + } + } else { + + dev.write(offset, &buf).unwrap(); + + let mut copy = vec![0u8; buf.len()]; + dev.read(offset, &mut copy).unwrap(); + + let enc_copy: Option>; + + if is_encrypted { + dev.erase(offset, slot_len).unwrap(); + + dev.write(offset, &encbuf).unwrap(); + + let mut enc = vec![0u8; encbuf.len()]; + dev.read(offset, &mut enc).unwrap(); + + enc_copy = Some(enc); + } else { + enc_copy = None; + } + + ImageData { + size: image_sz, + plain: copy, + cipher: enc_copy, + } + } +} + +/// Install no image. This is used when no upgrade happens. +fn install_no_image() -> ImageData { + ImageData { + size: 0, + plain: vec![], + cipher: None, + } +} + +/// Construct a TLV generator based on how MCUboot is currently configured. The returned +/// ManifestGen will generate the appropriate entries based on this configuration. +fn make_tlv() -> TlvGen { + let aes_key_size = if Caps::Aes256.present() { 256 } else { 128 }; + + if Caps::EncKw.present() { + if Caps::RSA2048.present() { + TlvGen::new_rsa_kw(aes_key_size) + } else if Caps::EcdsaP256.present() { + TlvGen::new_ecdsa_kw(aes_key_size) + } else { + TlvGen::new_enc_kw(aes_key_size) + } + } else if Caps::EncRsa.present() { + if Caps::RSA2048.present() { + TlvGen::new_sig_enc_rsa(aes_key_size) + } else { + TlvGen::new_enc_rsa(aes_key_size) + } + } else if Caps::EncEc256.present() { + if Caps::EcdsaP256.present() { + TlvGen::new_ecdsa_ecies_p256(aes_key_size) + } else { + TlvGen::new_ecies_p256(aes_key_size) + } + } else if Caps::EncX25519.present() { + if Caps::Ed25519.present() { + TlvGen::new_ed25519_ecies_x25519(aes_key_size) + } else { + TlvGen::new_ecies_x25519(aes_key_size) + } + } else { + // The non-encrypted configuration. + if Caps::RSA2048.present() { + TlvGen::new_rsa_pss() + } else if Caps::RSA3072.present() { + TlvGen::new_rsa3072_pss() + } else if Caps::EcdsaP256.present() || Caps::EcdsaP384.present() { + TlvGen::new_ecdsa() + } else if Caps::Ed25519.present() { + TlvGen::new_ed25519() + } else if Caps::HwRollbackProtection.present() { + TlvGen::new_sec_cnt() + } else { + TlvGen::new_hash_only() + } + } +} + +impl ImageData { + /// Find the image contents for the given slot. This assumes that slot 0 + /// is unencrypted, and slot 1 is encrypted. + fn find(&self, slot: usize) -> &Vec { + let encrypted = Caps::EncRsa.present() || Caps::EncKw.present() || + Caps::EncEc256.present() || Caps::EncX25519.present(); + match (encrypted, slot) { + (false, _) => &self.plain, + (true, 0) => &self.plain, + (true, 1) => self.cipher.as_ref().expect("Invalid image"), + _ => panic!("Invalid slot requested"), + } + } + + fn size(&self) -> usize { + self.size + } +} + +/// Verify that given image is present in the flash at the given offset. +fn verify_image(flash: &SimMultiFlash, slot: &SlotInfo, images: &ImageData) -> bool { + let image = images.find(slot.index); + let buf = image.as_slice(); + let dev_id = slot.dev_id; + + let mut copy = vec![0u8; buf.len()]; + let offset = slot.base_off; + let dev = flash.get(&dev_id).unwrap(); + dev.read(offset, &mut copy).unwrap(); + + if buf != ©[..] { + for i in 0 .. buf.len() { + if buf[i] != copy[i] { + info!("First failure for slot{} at {:#x} ({:#x} within) {:#x}!={:#x}", + slot.index, offset + i, i, buf[i], copy[i]); + break; + } + } + false + } else { + true + } +} + +fn verify_trailer(flash: &SimMultiFlash, slot: &SlotInfo, + magic: Option, image_ok: Option, + copy_done: Option) -> bool { + if Caps::OverwriteUpgrade.present() { + return true; + } + + let offset = slot.trailer_off + c::boot_max_align(); + let dev_id = slot.dev_id; + let mut copy = vec![0u8; c::boot_magic_sz() + c::boot_max_align() * 3]; + let mut failed = false; + + let dev = flash.get(&dev_id).unwrap(); + let erased_val = dev.erased_val(); + dev.read(offset, &mut copy).unwrap(); + + failed |= match magic { + Some(v) => { + let magic_off = (c::boot_max_align() * 3) + (c::boot_magic_sz() - MAGIC.len()); + if v == 1 && ©[magic_off..] != MAGIC { + warn!("\"magic\" mismatch at {:#x}", offset); + true + } else if v == 3 { + let expected = [erased_val; 16]; + if copy[magic_off..] != expected { + warn!("\"magic\" mismatch at {:#x}", offset); + true + } else { + false + } + } else { + false + } + }, + None => false, + }; + + failed |= match image_ok { + Some(v) => { + let image_ok_off = c::boot_max_align() * 2; + if (v == 1 && copy[image_ok_off] != v) || (v == 3 && copy[image_ok_off] != erased_val) { + warn!("\"image_ok\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[image_ok_off]); + true + } else { + false + } + }, + None => false, + }; + + failed |= match copy_done { + Some(v) => { + let copy_done_off = c::boot_max_align(); + if (v == 1 && copy[copy_done_off] != v) || (v == 3 && copy[copy_done_off] != erased_val) { + warn!("\"copy_done\" mismatch at {:#x} v={} val={:#x}", offset, v, copy[copy_done_off]); + true + } else { + false + } + }, + None => false, + }; + + !failed +} + +/// Install a partition table. This is a simplified partition table that +/// we write at the beginning of flash so make it easier for external tools +/// to analyze these images. +fn install_ptable(flash: &mut SimMultiFlash, areadesc: &AreaDesc) { + let ids: HashSet = areadesc.iter_areas().map(|area| area.device_id).collect(); + for &id in &ids { + // If there are any partitions in this device that start at 0, and + // aren't marked as the BootLoader partition, avoid adding the + // partition table. This makes it harder to view the image, but + // avoids messing up images already written. + let skip_ptable = areadesc + .iter_areas() + .any(|area| { + area.device_id == id && + area.off == 0 && + area.flash_id != FlashId::BootLoader + }); + if skip_ptable { + if log_enabled!(Info) { + let special: Vec = areadesc.iter_areas() + .filter(|area| area.device_id == id && area.off == 0) + .map(|area| area.flash_id) + .collect(); + info!("Skipping partition table: {:?}", special); + } + break; + } + + let mut buf: Vec = vec![]; + write!(&mut buf, "mcuboot\0").unwrap(); + + // Iterate through all of the partitions in that device, and encode + // into the table. + let count = areadesc.iter_areas().filter(|area| area.device_id == id).count(); + buf.write_u32::(count as u32).unwrap(); + + for area in areadesc.iter_areas().filter(|area| area.device_id == id) { + buf.write_u32::(area.flash_id as u32).unwrap(); + buf.write_u32::(area.off).unwrap(); + buf.write_u32::(area.size).unwrap(); + buf.write_u32::(0).unwrap(); + } + + let dev = flash.get_mut(&id).unwrap(); + + // Pad to alignment. + while buf.len() % dev.align() != 0 { + buf.push(0); + } + + dev.write(0, &buf).unwrap(); + } +} + +/// The image header +#[repr(C)] +#[derive(Debug)] +pub struct ImageHeader { + magic: u32, + load_addr: u32, + hdr_size: u16, + protect_tlv_size: u16, + img_size: u32, + flags: u32, + ver: ImageVersion, + _pad2: u32, +} + +impl AsRaw for ImageHeader {} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct ImageVersion { + pub major: u8, + pub minor: u8, + pub revision: u16, + pub build_num: u32, +} + +#[derive(Clone, Debug)] +pub struct SlotInfo { + pub base_off: usize, + pub trailer_off: usize, + pub len: usize, + // Which slot within this device. + pub index: usize, + pub dev_id: u8, +} + +#[cfg(not(feature = "max-align-32"))] +const MAGIC: &[u8] = &[0x77, 0xc2, 0x95, 0xf3, + 0x60, 0xd2, 0xef, 0x7f, + 0x35, 0x52, 0x50, 0x0f, + 0x2c, 0xb6, 0x79, 0x80]; + +#[cfg(feature = "max-align-32")] +const MAGIC: &[u8] = &[0x20, 0x00, 0x2d, 0xe1, + 0x5d, 0x29, 0x41, 0x0b, + 0x8d, 0x77, 0x67, 0x9c, + 0x11, 0x0f, 0x1f, 0x8a]; + +// Replicates defines found in bootutil.h +const BOOT_MAGIC_GOOD: Option = Some(1); +const BOOT_MAGIC_UNSET: Option = Some(3); + +const BOOT_FLAG_SET: Option = Some(1); +const BOOT_FLAG_UNSET: Option = Some(3); + +/// Write out the magic so that the loader tries doing an upgrade. +pub fn mark_upgrade(flash: &mut SimMultiFlash, slot: &SlotInfo) { + let dev = flash.get_mut(&slot.dev_id).unwrap(); + let align = dev.align(); + let offset = slot.trailer_off + c::boot_max_align() * 4; + if offset % align != 0 || MAGIC.len() % align != 0 { + // The write size is larger than the magic value. Fill a buffer + // with the erased value, put the MAGIC in it, and write it in its + // entirety. + let mut buf = vec![dev.erased_val(); c::boot_max_align()]; + let magic_off = (offset % align) + (c::boot_magic_sz() - MAGIC.len()); + buf[magic_off..].copy_from_slice(MAGIC); + dev.write(offset - (offset % align), &buf).unwrap(); + } else { + dev.write(offset, MAGIC).unwrap(); + } +} + +/// Writes the image_ok flag which, guess what, tells the bootloader +/// the this image is ok (not a test, and no revert is to be performed). +fn mark_permanent_upgrade(flash: &mut SimMultiFlash, slot: &SlotInfo) { + // Overwrite mode always is permanent, and only the magic is used in + // the trailer. To avoid problems with large write sizes, don't try to + // set anything in this case. + if Caps::OverwriteUpgrade.present() { + return; + } + + let dev = flash.get_mut(&slot.dev_id).unwrap(); + let align = dev.align(); + let mut ok = vec![dev.erased_val(); align]; + ok[0] = 1u8; + let off = slot.trailer_off + c::boot_max_align() * 3; + dev.write(off, &ok).unwrap(); +} + +// Drop some pseudo-random gibberish onto the data. +fn splat(data: &mut [u8], seed: usize) { + let mut seed_block = [0u8; 32]; + let mut buf = Cursor::new(&mut seed_block[..]); + buf.write_u32::(0x135782ea).unwrap(); + buf.write_u32::(0x92184728).unwrap(); + buf.write_u32::(data.len() as u32).unwrap(); + buf.write_u32::(seed as u32).unwrap(); + let mut rng: SmallRng = SeedableRng::from_seed(seed_block); + rng.fill_bytes(data); +} + +/// Return a read-only view into the raw bytes of this object +trait AsRaw : Sized { + fn as_raw(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self as *const _ as *const u8, + mem::size_of::()) } + } +} + +/// Determine whether it makes sense to test this configuration with a maximally-sized image. +/// Returns an ImageSize representing the best size to test, possibly just with the given size. +fn maximal(size: usize) -> ImageSize { + if Caps::OverwriteUpgrade.present() || + Caps::SwapUsingMove.present() + { + ImageSize::Given(size) + } else { + ImageSize::Largest + } +} + +pub fn show_sizes() { + // This isn't panic safe. + for min in &[1, 2, 4, 8] { + let msize = c::boot_trailer_sz(*min); + println!("{:2}: {} (0x{:x})", min, msize, msize); + } +} + +#[cfg(not(feature = "max-align-32"))] +fn test_alignments() -> &'static [usize] { + &[1, 2, 4, 8] +} + +#[cfg(feature = "max-align-32")] +fn test_alignments() -> &'static [usize] { + &[32] +} + +/// For testing, some of the tests are quite slow. This will query for an +/// environment variable `MCUBOOT_SKIP_SLOW_TESTS`, which can be set to avoid +/// running these tests. +fn skip_slow_test() -> bool { + if let Ok(_) = std::env::var("MCUBOOT_SKIP_SLOW_TESTS") { + true + } else { + false + } +} diff --git a/bootloader/mcuboot/sim/src/lib.rs b/bootloader/mcuboot/sim/src/lib.rs new file mode 100644 index 0000000..fe43e46 --- /dev/null +++ b/bootloader/mcuboot/sim/src/lib.rs @@ -0,0 +1,231 @@ +// Copyright (c) 2017-2019 Linaro LTD +// Copyright (c) 2017-2019 JUUL Labs +// Copyright (c) 2019-2023 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +use docopt::Docopt; +use log::{warn, error}; +use std::{ + fmt, + process, +}; +use serde_derive::Deserialize; + +mod caps; +mod depends; +mod image; +mod tlv; +mod utils; +pub mod testlog; + +pub use crate::{ + depends::{ + DepTest, + DepType, + UpgradeInfo, + NO_DEPS, + REV_DEPS, + }, + image::{ + ImagesBuilder, + Images, + ImageManipulation, + show_sizes, + }, +}; + +const USAGE: &str = " +Mcuboot simulator + +Usage: + bootsim sizes + bootsim run --device TYPE [--align SIZE] + bootsim runall + bootsim (--help | --version) + +Options: + -h, --help Show this message + --version Version + --device TYPE MCU to simulate + Valid values: stm32f4, k64f + --align SIZE Flash write alignment +"; + +#[derive(Debug, Deserialize)] +struct Args { + flag_device: Option, + flag_align: Option, + cmd_sizes: bool, + cmd_run: bool, + cmd_runall: bool, +} + +#[derive(Copy, Clone, Debug, Deserialize)] +pub enum DeviceName { + Stm32f4, K64f, K64fBig, K64fMulti, Nrf52840, Nrf52840SpiFlash, + Nrf52840UnequalSlots, +} + +pub static ALL_DEVICES: &[DeviceName] = &[ + DeviceName::Stm32f4, + DeviceName::K64f, + DeviceName::K64fBig, + DeviceName::K64fMulti, + DeviceName::Nrf52840, + DeviceName::Nrf52840SpiFlash, + DeviceName::Nrf52840UnequalSlots, +]; + +impl fmt::Display for DeviceName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = match *self { + DeviceName::Stm32f4 => "stm32f4", + DeviceName::K64f => "k64f", + DeviceName::K64fBig => "k64fbig", + DeviceName::K64fMulti => "k64fmulti", + DeviceName::Nrf52840 => "nrf52840", + DeviceName::Nrf52840SpiFlash => "Nrf52840SpiFlash", + DeviceName::Nrf52840UnequalSlots => "Nrf52840UnequalSlots", + }; + f.write_str(name) + } +} + +#[derive(Debug)] +struct AlignArg(usize); + +struct AlignArgVisitor; + +impl<'de> serde::de::Visitor<'de> for AlignArgVisitor { + type Value = AlignArg; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("1, 2, 4 or 8") + } + + fn visit_u32(self, n: u32) -> Result + where E: serde::de::Error + { + Ok(match n { + 1 | 2 | 4 | 8 => AlignArg(n as usize), + n => { + let err = format!("Could not deserialize '{}' as alignment", n); + return Err(E::custom(err)); + } + }) + } +} + +impl<'de> serde::de::Deserialize<'de> for AlignArg { + fn deserialize(d: D) -> Result + where D: serde::de::Deserializer<'de> + { + d.deserialize_u8(AlignArgVisitor) + } +} + +pub fn main() { + let args: Args = Docopt::new(USAGE) + .and_then(|d| d.deserialize()) + .unwrap_or_else(|e| e.exit()); + // println!("args: {:#?}", args); + + if args.cmd_sizes { + show_sizes(); + return; + } + + let mut status = RunStatus::new(); + if args.cmd_run { + + let align = args.flag_align.map(|x| x.0).unwrap_or(1); + + + let device = match args.flag_device { + None => panic!("Missing mandatory device argument"), + Some(dev) => dev, + }; + + status.run_single(device, align, 0xff); + } + + if args.cmd_runall { + for &dev in ALL_DEVICES { + for &align in &[1, 2, 4, 8] { + for &erased_val in &[0, 0xff] { + status.run_single(dev, align, erased_val); + } + } + } + } + + if status.failures > 0 { + error!("{} Tests ran with {} failures", status.failures + status.passes, status.failures); + process::exit(1); + } else { + error!("{} Tests ran successfully", status.passes); + process::exit(0); + } +} + +#[derive(Default)] +pub struct RunStatus { + failures: usize, + passes: usize, +} + +impl RunStatus { + pub fn new() -> RunStatus { + RunStatus { + failures: 0, + passes: 0, + } + } + + pub fn run_single(&mut self, device: DeviceName, align: usize, erased_val: u8) { + warn!("Running on device {} with alignment {}", device, align); + + let run = match ImagesBuilder::new(device, align, erased_val) { + Ok(builder) => builder, + Err(msg) => { + warn!("Skipping {}: {}", device, msg); + return; + } + }; + + let mut failed = false; + + // Creates a badly signed image in the secondary slot to check that + // it is not upgraded to + let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image(); + + failed |= bad_secondary_slot_image.run_signfail_upgrade(); + + let images = run.clone().make_no_upgrade_image(&NO_DEPS, ImageManipulation::None); + failed |= images.run_norevert_newimage(); + + let images = run.make_image(&NO_DEPS, true); + + failed |= images.run_basic_revert(); + failed |= images.run_revert_with_fails(); + failed |= images.run_perm_with_fails(); + failed |= images.run_perm_with_random_fails(5); + failed |= images.run_norevert(); + + failed |= images.run_with_status_fails_complete(); + failed |= images.run_with_status_fails_with_reset(); + + //show_flash(&flash); + + if failed { + self.failures += 1; + } else { + self.passes += 1; + } + } + + pub fn failures(&self) -> usize { + self.failures + } +} diff --git a/bootloader/mcuboot/sim/src/main.rs b/bootloader/mcuboot/sim/src/main.rs new file mode 100644 index 0000000..85036a8 --- /dev/null +++ b/bootloader/mcuboot/sim/src/main.rs @@ -0,0 +1,10 @@ +// Copyright (c) 2017-2019 Linaro LTD +// Copyright (c) 2017-2019 JUUL Labs +// +// SPDX-License-Identifier: Apache-2.0 + +fn main() { + env_logger::init(); + + bootsim::main(); +} diff --git a/bootloader/mcuboot/sim/src/rsa3072_pub_key-rs.txt b/bootloader/mcuboot/sim/src/rsa3072_pub_key-rs.txt new file mode 100644 index 0000000..16ca30d --- /dev/null +++ b/bootloader/mcuboot/sim/src/rsa3072_pub_key-rs.txt @@ -0,0 +1,53 @@ +/* Autogenerated by imgtool.py, do not edit. */ +static RSA3072_PUB_KEY: &[u8] = &[ + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, + 0x00, 0xb4, 0x2c, 0x0e, 0x98, 0x58, 0x10, 0xa4, + 0xa7, 0x58, 0x99, 0x7c, 0x01, 0xdd, 0x08, 0x2a, + 0x28, 0x34, 0x33, 0xf8, 0x96, 0x1a, 0x34, 0x20, + 0x5d, 0x45, 0xc8, 0x71, 0x26, 0x25, 0xe5, 0xd2, + 0x96, 0xea, 0x7b, 0xb1, 0x15, 0xaa, 0xa6, 0x8a, + 0x63, 0x22, 0x8b, 0x2d, 0x4e, 0x81, 0x73, 0xbf, + 0x6e, 0x15, 0x68, 0x8c, 0x1a, 0xf4, 0xef, 0x2a, + 0x8f, 0x8c, 0x22, 0x9e, 0x71, 0x57, 0x4b, 0xde, + 0x0f, 0x7e, 0x72, 0xd3, 0x7a, 0xb8, 0xa7, 0x1d, + 0x44, 0xad, 0x87, 0x00, 0x83, 0x5c, 0xfd, 0x73, + 0x05, 0x72, 0x46, 0x3f, 0x8b, 0xf9, 0x10, 0x00, + 0xd8, 0x6e, 0xcc, 0x85, 0xed, 0xf9, 0x49, 0xdb, + 0x78, 0x36, 0x80, 0x49, 0x38, 0x76, 0xdd, 0x5f, + 0x54, 0x04, 0xda, 0x8c, 0x34, 0xa7, 0x2b, 0x13, + 0x25, 0x6f, 0xd1, 0x15, 0x4f, 0xad, 0xc2, 0xe1, + 0xa5, 0xd2, 0x4e, 0x57, 0x0c, 0x7e, 0x9c, 0x9b, + 0xba, 0x4e, 0x68, 0xb2, 0xe0, 0x25, 0x02, 0xaa, + 0x00, 0xd3, 0xb4, 0xcc, 0x2f, 0x78, 0xe5, 0xbe, + 0x47, 0x67, 0x1f, 0xc8, 0x6e, 0x22, 0x6c, 0x5e, + 0x61, 0xb6, 0x9a, 0xcd, 0xe5, 0xa8, 0xba, 0x7a, + 0x80, 0x13, 0x1b, 0x17, 0x2e, 0x96, 0xed, 0xcf, + 0xb3, 0x9b, 0xe4, 0x1c, 0xe8, 0xad, 0xa7, 0xf6, + 0x3a, 0x51, 0x66, 0x5e, 0x99, 0x8e, 0x87, 0xee, + 0x60, 0x25, 0xf8, 0x8d, 0xbe, 0xce, 0xa4, 0xa8, + 0xca, 0x93, 0x6c, 0xd7, 0xbf, 0xd4, 0x73, 0x33, + 0x8d, 0x44, 0x85, 0xcc, 0x73, 0x30, 0x08, 0x9c, + 0x4d, 0xb2, 0xaa, 0x5a, 0x6c, 0x6f, 0x7b, 0xab, + 0xb7, 0xb3, 0x7c, 0xc3, 0xfb, 0xe7, 0xca, 0xc4, + 0xf8, 0x9a, 0x6f, 0xcb, 0xbb, 0x5b, 0x82, 0xe7, + 0x7a, 0xe8, 0x19, 0xfd, 0x2f, 0x11, 0x22, 0xfb, + 0x7f, 0x76, 0x8c, 0x6b, 0x94, 0xa4, 0x09, 0x4f, + 0xa5, 0x6a, 0x77, 0x51, 0xeb, 0xa7, 0x7e, 0xda, + 0x87, 0x06, 0xee, 0xdc, 0xbe, 0xd1, 0xea, 0x1a, + 0x40, 0x1d, 0x1b, 0xff, 0x1a, 0xb1, 0x51, 0x7c, + 0x12, 0xb0, 0xf3, 0xf6, 0x83, 0x01, 0x9c, 0xe7, + 0x0c, 0x99, 0xbf, 0xac, 0x68, 0x58, 0x72, 0xa4, + 0xb0, 0x59, 0x85, 0xee, 0x85, 0xac, 0x2a, 0x22, + 0xf4, 0xcf, 0x15, 0x08, 0x80, 0x1f, 0x0d, 0xd0, + 0x1e, 0xa0, 0xa0, 0x94, 0xc8, 0xf7, 0xfa, 0x65, + 0xdd, 0x52, 0xe8, 0x96, 0x37, 0x23, 0x30, 0x57, + 0x36, 0xe6, 0x9d, 0xf4, 0x0c, 0x4a, 0x05, 0x75, + 0x1f, 0xad, 0x01, 0xca, 0xb7, 0x6d, 0x8c, 0x43, + 0x74, 0x06, 0x0a, 0x81, 0xf3, 0x01, 0x62, 0xff, + 0xf7, 0xf5, 0x5f, 0xaf, 0xe7, 0x2b, 0x0e, 0xf8, + 0x81, 0xb5, 0x65, 0xdd, 0x01, 0xd9, 0x9f, 0x07, + 0x17, 0x8a, 0x18, 0xcf, 0x23, 0x6e, 0x88, 0x65, + 0x91, 0xb5, 0x7b, 0xd3, 0xb0, 0x2d, 0xaf, 0x93, + 0x66, 0x63, 0x74, 0xac, 0x5a, 0xe6, 0x73, 0xde, + 0x3b, 0x02, 0x03, 0x01, 0x00, 0x01, +]; diff --git a/bootloader/mcuboot/sim/src/rsa_pub_key-rs.txt b/bootloader/mcuboot/sim/src/rsa_pub_key-rs.txt new file mode 100644 index 0000000..f6466f8 --- /dev/null +++ b/bootloader/mcuboot/sim/src/rsa_pub_key-rs.txt @@ -0,0 +1,37 @@ +/* Autogenerated by imgtool.py, do not edit. */ +static RSA_PUB_KEY: &[u8] = &[ + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xd1, 0x06, 0x08, 0x1a, 0x18, 0x44, 0x2c, + 0x18, 0xe8, 0xfb, 0xfd, 0xf7, 0x0d, 0xa3, 0x4f, + 0x1f, 0xbb, 0xee, 0x5e, 0xf9, 0xaa, 0xd2, 0x4b, + 0x18, 0xd3, 0x5a, 0xe9, 0x6d, 0x18, 0x80, 0x19, + 0xf9, 0xf0, 0x9c, 0x34, 0x1b, 0xcb, 0xf3, 0xbc, + 0x74, 0xdb, 0x42, 0xe7, 0x8c, 0x7f, 0x10, 0x53, + 0x7e, 0x43, 0x5e, 0x0d, 0x57, 0x2c, 0x44, 0xd1, + 0x67, 0x08, 0x0f, 0x0d, 0xbb, 0x5c, 0xee, 0xec, + 0xb3, 0x99, 0xdf, 0xe0, 0x4d, 0x84, 0x0b, 0xaa, + 0x77, 0x41, 0x60, 0xed, 0x15, 0x28, 0x49, 0xa7, + 0x01, 0xb4, 0x3c, 0x10, 0xe6, 0x69, 0x8c, 0x2f, + 0x5f, 0xac, 0x41, 0x4d, 0x9e, 0x5c, 0x14, 0xdf, + 0xf2, 0xf8, 0xcf, 0x3d, 0x1e, 0x6f, 0xe7, 0x5b, + 0xba, 0xb4, 0xa9, 0xc8, 0x88, 0x7e, 0x47, 0x3c, + 0x94, 0xc3, 0x77, 0x67, 0x54, 0x4b, 0xaa, 0x8d, + 0x38, 0x35, 0xca, 0x62, 0x61, 0x7e, 0xb7, 0xe1, + 0x15, 0xdb, 0x77, 0x73, 0xd4, 0xbe, 0x7b, 0x72, + 0x21, 0x89, 0x69, 0x24, 0xfb, 0xf8, 0x65, 0x6e, + 0x64, 0x3e, 0xc8, 0x0e, 0xd7, 0x85, 0xd5, 0x5c, + 0x4a, 0xe4, 0x53, 0x0d, 0x2f, 0xff, 0xb7, 0xfd, + 0xf3, 0x13, 0x39, 0x83, 0x3f, 0xa3, 0xae, 0xd2, + 0x0f, 0xa7, 0x6a, 0x9d, 0xf9, 0xfe, 0xb8, 0xce, + 0xfa, 0x2a, 0xbe, 0xaf, 0xb8, 0xe0, 0xfa, 0x82, + 0x37, 0x54, 0xf4, 0x3e, 0xe1, 0x2b, 0xd0, 0xd3, + 0x08, 0x58, 0x18, 0xf6, 0x5e, 0x4c, 0xc8, 0x88, + 0x81, 0x31, 0xad, 0x5f, 0xb0, 0x82, 0x17, 0xf2, + 0x8a, 0x69, 0x27, 0x23, 0xf3, 0xab, 0x87, 0x3e, + 0x93, 0x1a, 0x1d, 0xfe, 0xe8, 0xf8, 0x1a, 0x24, + 0x66, 0x59, 0xf8, 0x1c, 0xab, 0xdc, 0xce, 0x68, + 0x1b, 0x66, 0x64, 0x35, 0xec, 0xfa, 0x0d, 0x11, + 0x9d, 0xaf, 0x5c, 0x3a, 0xa7, 0xd1, 0x67, 0xc6, + 0x47, 0xef, 0xb1, 0x4b, 0x2c, 0x62, 0xe1, 0xd1, + 0xc9, 0x02, 0x03, 0x01, 0x00, 0x01, +]; diff --git a/bootloader/mcuboot/sim/src/testlog.rs b/bootloader/mcuboot/sim/src/testlog.rs new file mode 100644 index 0000000..77619e4 --- /dev/null +++ b/bootloader/mcuboot/sim/src/testlog.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2017 Linaro LTD +// Copyright (c) 2019 JUUL Labs +// +// SPDX-License-Identifier: Apache-2.0 + +//! Logging support for the test framework. +//! +//! https://stackoverflow.com/questions/30177845/how-to-initialize-the-logger-for-integration-tests +//! +//! The test framework runs the tests, possibly simultaneously, and in various orders. This helper +//! function, which should be called at the beginning of each test, will setup logging for all of +//! the tests. + +use std::sync::Once; + +static INIT: Once = Once::new(); + +/// Setup the logging system. Intended to be called at the beginning of each test. +pub fn setup() { + INIT.call_once(|| { + env_logger::init(); + }); +} diff --git a/bootloader/mcuboot/sim/src/tlv.rs b/bootloader/mcuboot/sim/src/tlv.rs new file mode 100644 index 0000000..ce6876e --- /dev/null +++ b/bootloader/mcuboot/sim/src/tlv.rs @@ -0,0 +1,841 @@ +// Copyright (c) 2017-2021 Linaro LTD +// Copyright (c) 2017-2020 JUUL Labs +// Copyright (c) 2021-2023 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +//! TLV Support +//! +//! mcuboot images are followed immediately by a list of TLV items that contain integrity +//! information about the image. Their generation is made a little complicated because the size of +//! the TLV block is in the image header, which is included in the hash. Since some signatures can +//! vary in size, we just make them the largest size possible. +//! +//! Because of this header, we have to make two passes. The first pass will compute the size of +//! the TLV, and the second pass will build the data for the TLV. + +use byteorder::{ + LittleEndian, WriteBytesExt, +}; +use cipher::FromBlockCipher; +use crate::caps::Caps; +use crate::image::ImageVersion; +use log::info; +use ring::{digest, rand, agreement, hkdf, hmac}; +use ring::rand::SecureRandom; +use ring::signature::{ + RsaKeyPair, + RSA_PSS_SHA256, + EcdsaKeyPair, + ECDSA_P256_SHA256_ASN1_SIGNING, + Ed25519KeyPair, + ECDSA_P384_SHA384_ASN1_SIGNING, +}; +use aes::{ + Aes128, + Aes128Ctr, + Aes256, + Aes256Ctr, + NewBlockCipher +}; +use cipher::{ + generic_array::GenericArray, + StreamCipher, +}; +use mcuboot_sys::c; +use typenum::{U16, U32}; + +#[repr(u16)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[allow(dead_code)] // TODO: For now +pub enum TlvKinds { + KEYHASH = 0x01, + SHA256 = 0x10, + SHA384 = 0x11, + RSA2048 = 0x20, + ECDSASIG = 0x22, + RSA3072 = 0x23, + ED25519 = 0x24, + ENCRSA2048 = 0x30, + ENCKW = 0x31, + ENCEC256 = 0x32, + ENCX25519 = 0x33, + DEPENDENCY = 0x40, + SECCNT = 0x50, +} + +#[allow(dead_code, non_camel_case_types)] +pub enum TlvFlags { + PIC = 0x01, + NON_BOOTABLE = 0x02, + ENCRYPTED_AES128 = 0x04, + ENCRYPTED_AES256 = 0x08, + RAM_LOAD = 0x20, +} + +/// A generator for manifests. The format of the manifest can be either a +/// traditional "TLV" or a SUIT-style manifest. +pub trait ManifestGen { + /// Retrieve the header magic value for this manifest type. + fn get_magic(&self) -> u32; + + /// Retrieve the flags value for this particular manifest type. + fn get_flags(&self) -> u32; + + /// Retrieve the number of bytes of this manifest that is "protected". + /// This field is stored in the outside image header instead of the + /// manifest header. + fn protect_size(&self) -> u16; + + /// Add a dependency on another image. + fn add_dependency(&mut self, id: u8, version: &ImageVersion); + + /// Add a sequence of bytes to the payload that the manifest is + /// protecting. + fn add_bytes(&mut self, bytes: &[u8]); + + /// Set an internal flag indicating that the next `make_tlv` should + /// corrupt the signature. + fn corrupt_sig(&mut self); + + /// Estimate the size of the TLV. This can be called before the payload is added (but after + /// other information is added). Some of the signature algorithms can generate variable sized + /// data, and therefore, this can slightly overestimate the size. + fn estimate_size(&self) -> usize; + + /// Construct the manifest for this payload. + fn make_tlv(self: Box) -> Vec; + + /// Generate a new encryption random key + fn generate_enc_key(&mut self); + + /// Return the current encryption key + fn get_enc_key(&self) -> Vec; + + /// Set the security counter to the specified value. + fn set_security_counter(&mut self, security_cnt: Option); + + /// Sets the ignore_ram_load_flag so that can be validated when it is missing, + /// it will not load successfully. + fn set_ignore_ram_load_flag(&mut self); +} + +#[derive(Debug, Default)] +pub struct TlvGen { + flags: u32, + kinds: Vec, + payload: Vec, + dependencies: Vec, + enc_key: Vec, + /// Should this signature be corrupted. + gen_corrupted: bool, + security_cnt: Option, + /// Ignore RAM_LOAD flag + ignore_ram_load_flag: bool, +} + +#[derive(Debug)] +struct Dependency { + id: u8, + version: ImageVersion, +} + +impl TlvGen { + /// Construct a new tlv generator that will only contain a hash of the data. + #[allow(dead_code)] + pub fn new_hash_only() -> TlvGen { + TlvGen { + kinds: vec![TlvKinds::SHA256], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_rsa_pss() -> TlvGen { + TlvGen { + kinds: vec![TlvKinds::SHA256, TlvKinds::RSA2048], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_rsa3072_pss() -> TlvGen { + TlvGen { + kinds: vec![TlvKinds::SHA256, TlvKinds::RSA3072], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_ecdsa() -> TlvGen { + let hash_kind = if cfg!(feature = "sig-p384") { + TlvKinds::SHA384 + } else { + TlvKinds::SHA256 + }; + TlvGen { + kinds: vec![hash_kind, TlvKinds::ECDSASIG], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_ed25519() -> TlvGen { + TlvGen { + kinds: vec![TlvKinds::SHA256, TlvKinds::ED25519], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_enc_rsa(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::ENCRSA2048], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_sig_enc_rsa(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::RSA2048, TlvKinds::ENCRSA2048], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_enc_kw(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::ENCKW], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_rsa_kw(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::RSA2048, TlvKinds::ENCKW], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_ecdsa_kw(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::ECDSASIG, TlvKinds::ENCKW], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_ecies_p256(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::ENCEC256], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_ecdsa_ecies_p256(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::ECDSASIG, TlvKinds::ENCEC256], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_ecies_x25519(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::ENCX25519], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_ed25519_ecies_x25519(aes_key_size: u32) -> TlvGen { + let flag = if aes_key_size == 256 { + TlvFlags::ENCRYPTED_AES256 as u32 + } else { + TlvFlags::ENCRYPTED_AES128 as u32 + }; + TlvGen { + flags: flag, + kinds: vec![TlvKinds::SHA256, TlvKinds::ED25519, TlvKinds::ENCX25519], + ..Default::default() + } + } + + #[allow(dead_code)] + pub fn new_sec_cnt() -> TlvGen { + TlvGen { + kinds: vec![TlvKinds::SHA256, TlvKinds::SECCNT], + ..Default::default() + } + } + +} + +impl ManifestGen for TlvGen { + fn get_magic(&self) -> u32 { + 0x96f3b83d + } + + /// Retrieve the header flags for this configuration. This can be called at any time. + fn get_flags(&self) -> u32 { + // For the RamLoad case, add in the flag for this feature. + if Caps::RamLoad.present() && !self.ignore_ram_load_flag { + self.flags | (TlvFlags::RAM_LOAD as u32) + } else { + self.flags + } + } + + /// Add bytes to the covered hash. + fn add_bytes(&mut self, bytes: &[u8]) { + self.payload.extend_from_slice(bytes); + } + + fn protect_size(&self) -> u16 { + let mut size = 0; + if !self.dependencies.is_empty() || (Caps::HwRollbackProtection.present() && self.security_cnt.is_some()) { + // include the TLV area header. + size += 4; + // add space for each dependency. + size += (self.dependencies.len() as u16) * (4 + std::mem::size_of::() as u16); + if Caps::HwRollbackProtection.present() && self.security_cnt.is_some() { + size += 4 + 4; + } + } + size + } + + fn add_dependency(&mut self, id: u8, version: &ImageVersion) { + self.dependencies.push(Dependency { + id, + version: version.clone(), + }); + } + + fn corrupt_sig(&mut self) { + self.gen_corrupted = true; + } + + fn estimate_size(&self) -> usize { + // Begin the estimate with the 4 byte header. + let mut estimate = 4; + // A very poor estimate. + + // Estimate the size of the image hash. + if self.kinds.contains(&TlvKinds::SHA256) { + estimate += 4 + 32; + } else if self.kinds.contains(&TlvKinds::SHA384) { + estimate += 4 + 48; + } + + // Add an estimate in for each of the signature algorithms. + if self.kinds.contains(&TlvKinds::RSA2048) { + estimate += 4 + 32; // keyhash + estimate += 4 + 256; // RSA2048 + } + if self.kinds.contains(&TlvKinds::RSA3072) { + estimate += 4 + 32; // keyhash + estimate += 4 + 384; // RSA3072 + } + if self.kinds.contains(&TlvKinds::ED25519) { + estimate += 4 + 32; // keyhash + estimate += 4 + 64; // ED25519 signature. + } + if self.kinds.contains(&TlvKinds::ECDSASIG) { + // ECDSA signatures are encoded as ASN.1 with the x and y values + // stored as signed integers. As such, the size can vary by 2 bytes, + // if for example the 256-bit value has the high bit, it takes an + // extra 0 byte to avoid it being seen as a negative number. + if self.kinds.contains(&TlvKinds::SHA384) { + estimate += 4 + 48; // SHA384 + estimate += 4 + 104; // ECDSA384 (varies) + } else { + estimate += 4 + 32; // SHA256 + estimate += 4 + 72; // ECDSA256 (varies) + } + } + + // Estimate encryption. + let flag = TlvFlags::ENCRYPTED_AES256 as u32; + let aes256 = (self.get_flags() & flag) == flag; + + if self.kinds.contains(&TlvKinds::ENCRSA2048) { + estimate += 4 + 256; + } + if self.kinds.contains(&TlvKinds::ENCKW) { + estimate += 4 + if aes256 { 40 } else { 24 }; + } + if self.kinds.contains(&TlvKinds::ENCEC256) { + estimate += 4 + if aes256 { 129 } else { 113 }; + } + if self.kinds.contains(&TlvKinds::ENCX25519) { + estimate += 4 + if aes256 { 96 } else { 80 }; + } + + // Gather the size of the protected TLV area. + estimate += self.protect_size() as usize; + + estimate + } + + /// Compute the TLV given the specified block of data. + fn make_tlv(self: Box) -> Vec { + let size_estimate = self.estimate_size(); + + let mut protected_tlv: Vec = vec![]; + + if self.protect_size() > 0 { + protected_tlv.push(0x08); + protected_tlv.push(0x69); + let size = self.protect_size(); + protected_tlv.write_u16::(size).unwrap(); + for dep in &self.dependencies { + protected_tlv.write_u16::(TlvKinds::DEPENDENCY as u16).unwrap(); + protected_tlv.write_u16::(12).unwrap(); + + // The dependency. + protected_tlv.push(dep.id); + protected_tlv.push(0); + protected_tlv.write_u16::(0).unwrap(); + protected_tlv.push(dep.version.major); + protected_tlv.push(dep.version.minor); + protected_tlv.write_u16::(dep.version.revision).unwrap(); + protected_tlv.write_u32::(dep.version.build_num).unwrap(); + } + + // Security counter has to be at the protected TLV area also + if Caps::HwRollbackProtection.present() && self.security_cnt.is_some() { + protected_tlv.write_u16::(TlvKinds::SECCNT as u16).unwrap(); + protected_tlv.write_u16::(std::mem::size_of::() as u16).unwrap(); + protected_tlv.write_u32::(self.security_cnt.unwrap() as u32).unwrap(); + } + + assert_eq!(size, protected_tlv.len() as u16, "protected TLV length incorrect"); + } + + // Ring does the signature itself, which means that it must be + // given a full, contiguous payload. Although this does help from + // a correct usage perspective, it is fairly stupid from an + // efficiency view. If this is shown to be a performance issue + // with the tests, the protected data could be appended to the + // payload, and then removed after the signature is done. For now, + // just make a signed payload. + let mut sig_payload = self.payload.clone(); + sig_payload.extend_from_slice(&protected_tlv); + + let mut result: Vec = vec![]; + + // add back signed payload + result.extend_from_slice(&protected_tlv); + + // add non-protected payload + let npro_pos = result.len(); + result.push(0x07); + result.push(0x69); + // Placeholder for the size. + result.write_u16::(0).unwrap(); + + if self.kinds.iter().any(|v| v == &TlvKinds::SHA256 || v == &TlvKinds::SHA384) { + // If a signature is not requested, corrupt the hash we are + // generating. But, if there is a signature, output the + // correct hash. We want the hash test to pass so that the + // signature verification can be validated. + let mut corrupt_hash = self.gen_corrupted; + for k in &[TlvKinds::RSA2048, TlvKinds::RSA3072, + TlvKinds::ED25519, TlvKinds::ECDSASIG] + { + if self.kinds.contains(k) { + corrupt_hash = false; + break; + } + } + + if corrupt_hash { + sig_payload[0] ^= 1; + } + let (hash,hash_size,tlv_kind) = if self.kinds.contains(&TlvKinds::SHA256) + { + let hash = digest::digest(&digest::SHA256, &sig_payload); + (hash,32,TlvKinds::SHA256) + } + else { + let hash = digest::digest(&digest::SHA384, &sig_payload); + (hash,48,TlvKinds::SHA384) + }; + let hash = hash.as_ref(); + + assert!(hash.len() == hash_size); + result.write_u16::(tlv_kind as u16).unwrap(); + result.write_u16::(hash_size as u16).unwrap(); + result.extend_from_slice(hash); + + // Undo the corruption. + if corrupt_hash { + sig_payload[0] ^= 1; + } + + } + + if self.gen_corrupted { + // Corrupt what is signed by modifying the input to the + // signature code. + sig_payload[0] ^= 1; + } + + if self.kinds.contains(&TlvKinds::RSA2048) || + self.kinds.contains(&TlvKinds::RSA3072) { + + let is_rsa2048 = self.kinds.contains(&TlvKinds::RSA2048); + + // Output the hash of the public key. + let hash = if is_rsa2048 { + digest::digest(&digest::SHA256, RSA_PUB_KEY) + } else { + digest::digest(&digest::SHA256, RSA3072_PUB_KEY) + }; + let hash = hash.as_ref(); + + assert!(hash.len() == 32); + result.write_u16::(TlvKinds::KEYHASH as u16).unwrap(); + result.write_u16::(32).unwrap(); + result.extend_from_slice(hash); + + // For now assume PSS. + let key_bytes = if is_rsa2048 { + pem::parse(include_bytes!("../../root-rsa-2048.pem").as_ref()).unwrap() + } else { + pem::parse(include_bytes!("../../root-rsa-3072.pem").as_ref()).unwrap() + }; + assert_eq!(key_bytes.tag, "RSA PRIVATE KEY"); + let key_pair = RsaKeyPair::from_der(&key_bytes.contents).unwrap(); + let rng = rand::SystemRandom::new(); + let mut signature = vec![0; key_pair.public_modulus_len()]; + if is_rsa2048 { + assert_eq!(signature.len(), 256); + } else { + assert_eq!(signature.len(), 384); + } + key_pair.sign(&RSA_PSS_SHA256, &rng, &sig_payload, &mut signature).unwrap(); + + if is_rsa2048 { + result.write_u16::(TlvKinds::RSA2048 as u16).unwrap(); + } else { + result.write_u16::(TlvKinds::RSA3072 as u16).unwrap(); + } + result.write_u16::(signature.len() as u16).unwrap(); + result.extend_from_slice(&signature); + } + + if self.kinds.contains(&TlvKinds::ECDSASIG) { + let rng = rand::SystemRandom::new(); + let (signature, keyhash, keyhash_size) = if self.kinds.contains(&TlvKinds::SHA384) { + let keyhash = digest::digest(&digest::SHA384, ECDSAP384_PUB_KEY); + let key_bytes = pem::parse(include_bytes!("../../root-ec-p384-pkcs8.pem").as_ref()).unwrap(); + let sign_algo = &ECDSA_P384_SHA384_ASN1_SIGNING; + let key_pair = EcdsaKeyPair::from_pkcs8(sign_algo, &key_bytes.contents).unwrap(); + (key_pair.sign(&rng, &sig_payload).unwrap(), keyhash, 48) + } else { + let keyhash = digest::digest(&digest::SHA256, ECDSA256_PUB_KEY); + let key_bytes = pem::parse(include_bytes!("../../root-ec-p256-pkcs8.pem").as_ref()).unwrap(); + let sign_algo = &ECDSA_P256_SHA256_ASN1_SIGNING; + let key_pair = EcdsaKeyPair::from_pkcs8(sign_algo, &key_bytes.contents).unwrap(); + (key_pair.sign(&rng, &sig_payload).unwrap(), keyhash, 32) + }; + + // Write public key + let keyhash_slice = keyhash.as_ref(); + assert!(keyhash_slice.len() == keyhash_size); + result.write_u16::(TlvKinds::KEYHASH as u16).unwrap(); + result.write_u16::(keyhash_size as u16).unwrap(); + result.extend_from_slice(keyhash_slice); + + // Write signature + result.write_u16::(TlvKinds::ECDSASIG as u16).unwrap(); + let signature = signature.as_ref().to_vec(); + result.write_u16::(signature.len() as u16).unwrap(); + result.extend_from_slice(&signature); + } + + if self.kinds.contains(&TlvKinds::ED25519) { + let keyhash = digest::digest(&digest::SHA256, ED25519_PUB_KEY); + let keyhash = keyhash.as_ref(); + + assert!(keyhash.len() == 32); + result.write_u16::(TlvKinds::KEYHASH as u16).unwrap(); + result.write_u16::(32).unwrap(); + result.extend_from_slice(keyhash); + + let hash = digest::digest(&digest::SHA256, &sig_payload); + let hash = hash.as_ref(); + assert!(hash.len() == 32); + + let key_bytes = pem::parse(include_bytes!("../../root-ed25519.pem").as_ref()).unwrap(); + assert_eq!(key_bytes.tag, "PRIVATE KEY"); + + let key_pair = Ed25519KeyPair::from_seed_and_public_key( + &key_bytes.contents[16..48], &ED25519_PUB_KEY[12..44]).unwrap(); + let signature = key_pair.sign(&hash); + + result.write_u16::(TlvKinds::ED25519 as u16).unwrap(); + + let signature = signature.as_ref().to_vec(); + result.write_u16::(signature.len() as u16).unwrap(); + result.extend_from_slice(signature.as_ref()); + } + + if self.kinds.contains(&TlvKinds::ENCRSA2048) { + let key_bytes = pem::parse(include_bytes!("../../enc-rsa2048-pub.pem") + .as_ref()).unwrap(); + assert_eq!(key_bytes.tag, "PUBLIC KEY"); + + let cipherkey = self.get_enc_key(); + let cipherkey = cipherkey.as_slice(); + let encbuf = match c::rsa_oaep_encrypt(&key_bytes.contents, cipherkey) { + Ok(v) => v, + Err(_) => panic!("Failed to encrypt secret key"), + }; + + assert!(encbuf.len() == 256); + result.write_u16::(TlvKinds::ENCRSA2048 as u16).unwrap(); + result.write_u16::(256).unwrap(); + result.extend_from_slice(&encbuf); + } + + if self.kinds.contains(&TlvKinds::ENCKW) { + let flag = TlvFlags::ENCRYPTED_AES256 as u32; + let aes256 = (self.get_flags() & flag) == flag; + let key_bytes = if aes256 { + base64::decode( + include_str!("../../enc-aes256kw.b64").trim()).unwrap() + } else { + base64::decode( + include_str!("../../enc-aes128kw.b64").trim()).unwrap() + }; + let cipherkey = self.get_enc_key(); + let cipherkey = cipherkey.as_slice(); + let keylen = if aes256 { 32 } else { 16 }; + let encbuf = match c::kw_encrypt(&key_bytes, cipherkey, keylen) { + Ok(v) => v, + Err(_) => panic!("Failed to encrypt secret key"), + }; + + let size = if aes256 { 40 } else { 24 }; + assert!(encbuf.len() == size); + result.write_u16::(TlvKinds::ENCKW as u16).unwrap(); + result.write_u16::(size as u16).unwrap(); + result.extend_from_slice(&encbuf); + } + + if self.kinds.contains(&TlvKinds::ENCEC256) || self.kinds.contains(&TlvKinds::ENCX25519) { + let key_bytes = if self.kinds.contains(&TlvKinds::ENCEC256) { + pem::parse(include_bytes!("../../enc-ec256-pub.pem").as_ref()).unwrap() + } else { + pem::parse(include_bytes!("../../enc-x25519-pub.pem").as_ref()).unwrap() + }; + assert_eq!(key_bytes.tag, "PUBLIC KEY"); + let rng = rand::SystemRandom::new(); + let alg = if self.kinds.contains(&TlvKinds::ENCEC256) { + &agreement::ECDH_P256 + } else { + &agreement::X25519 + }; + let pk = match agreement::EphemeralPrivateKey::generate(alg, &rng) { + Ok(v) => v, + Err(_) => panic!("Failed to generate ephemeral keypair"), + }; + + let pubk = match pk.compute_public_key() { + Ok(pubk) => pubk, + Err(_) => panic!("Failed computing ephemeral public key"), + }; + + let peer_pubk = if self.kinds.contains(&TlvKinds::ENCEC256) { + agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, &key_bytes.contents[26..]) + } else { + agreement::UnparsedPublicKey::new(&agreement::X25519, &key_bytes.contents[12..]) + }; + + #[derive(Debug, PartialEq)] + struct OkmLen(T); + + impl hkdf::KeyType for OkmLen { + fn len(&self) -> usize { + self.0 + } + } + + let flag = TlvFlags::ENCRYPTED_AES256 as u32; + let aes256 = (self.get_flags() & flag) == flag; + + let derived_key = match agreement::agree_ephemeral( + pk, &peer_pubk, ring::error::Unspecified, |shared| { + let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]); + let prk = salt.extract(&shared); + let okm_len = if aes256 { 64 } else { 48 }; + let okm = match prk.expand(&[b"MCUBoot_ECIES_v1"], OkmLen(okm_len)) { + Ok(okm) => okm, + Err(_) => panic!("Failed building HKDF OKM"), + }; + let mut buf = if aes256 { vec![0u8; 64] } else { vec![0u8; 48] }; + match okm.fill(&mut buf) { + Ok(_) => Ok(buf), + Err(_) => panic!("Failed generating HKDF output"), + } + }, + ) { + Ok(v) => v, + Err(_) => panic!("Failed building HKDF"), + }; + + let nonce = GenericArray::from_slice(&[0; 16]); + let mut cipherkey = self.get_enc_key(); + if aes256 { + let key: &GenericArray = GenericArray::from_slice(&derived_key[..32]); + let block = Aes256::new(&key); + let mut cipher = Aes256Ctr::from_block_cipher(block, &nonce); + cipher.apply_keystream(&mut cipherkey); + } else { + let key: &GenericArray = GenericArray::from_slice(&derived_key[..16]); + let block = Aes128::new(&key); + let mut cipher = Aes128Ctr::from_block_cipher(block, &nonce); + cipher.apply_keystream(&mut cipherkey); + } + + let size = if aes256 { 32 } else { 16 }; + let key = hmac::Key::new(hmac::HMAC_SHA256, &derived_key[size..]); + let tag = hmac::sign(&key, &cipherkey); + + let mut buf = vec![]; + buf.append(&mut pubk.as_ref().to_vec()); + buf.append(&mut tag.as_ref().to_vec()); + buf.append(&mut cipherkey); + + if self.kinds.contains(&TlvKinds::ENCEC256) { + let size = if aes256 { 129 } else { 113 }; + assert!(buf.len() == size); + result.write_u16::(TlvKinds::ENCEC256 as u16).unwrap(); + result.write_u16::(size as u16).unwrap(); + } else { + let size = if aes256 { 96 } else { 80 }; + assert!(buf.len() == size); + result.write_u16::(TlvKinds::ENCX25519 as u16).unwrap(); + result.write_u16::(size as u16).unwrap(); + } + result.extend_from_slice(&buf); + } + + // Patch the size back into the TLV header. + let size = (result.len() - npro_pos) as u16; + let mut size_buf = &mut result[npro_pos + 2 .. npro_pos + 4]; + size_buf.write_u16::(size).unwrap(); + + // ECDSA is stored as an ASN.1 integer. For a 128-bit value, this maximally results in 33 + // bytes of storage for each of the two values. If the high bit is zero, it will take 32 + // bytes, if the top 8 bits are zero, it will take 31 bits, and so on. The smaller size + // will occur with decreasing likelihood. We'll allow this to get a bit smaller, hopefully + // allowing the tests to pass with false failures rare. For this case, we'll handle up to + // the top 16 bits of both numbers being all zeros (1 in 2^32). + if !Caps::has_ecdsa() { + if size_estimate != result.len() { + panic!("Incorrect size estimate: {} (actual {})", size_estimate, result.len()); + } + } else { + if size_estimate < result.len() || size_estimate > result.len() + 6 { + panic!("Incorrect size estimate: {} (actual {})", size_estimate, result.len()); + } + } + if size_estimate != result.len() { + log::warn!("Size off: {} actual {}", size_estimate, result.len()); + } + + result + } + + fn generate_enc_key(&mut self) { + let rng = rand::SystemRandom::new(); + let flag = TlvFlags::ENCRYPTED_AES256 as u32; + let aes256 = (self.get_flags() & flag) == flag; + let mut buf = if aes256 { + vec![0u8; 32] + } else { + vec![0u8; 16] + }; + if rng.fill(&mut buf).is_err() { + panic!("Error generating encrypted key"); + } + info!("New encryption key: {:02x?}", buf); + self.enc_key = buf; + } + + fn get_enc_key(&self) -> Vec { + if self.enc_key.len() != 32 && self.enc_key.len() != 16 { + panic!("No random key was generated"); + } + self.enc_key.clone() + } + + fn set_security_counter(&mut self, security_cnt: Option) { + self.security_cnt = security_cnt; + } + + fn set_ignore_ram_load_flag(&mut self) { + self.ignore_ram_load_flag = true; + } +} + +include!("rsa_pub_key-rs.txt"); +include!("rsa3072_pub_key-rs.txt"); +include!("ecdsa_pub_key-rs.txt"); +include!("ed25519_pub_key-rs.txt"); diff --git a/bootloader/mcuboot/sim/src/utils.rs b/bootloader/mcuboot/sim/src/utils.rs new file mode 100644 index 0000000..abbac3c --- /dev/null +++ b/bootloader/mcuboot/sim/src/utils.rs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD +// +// SPDX-License-Identifier: Apache-2.0 + +//! Utility functions used throughout MCUboot + +pub fn align_up(num: u32, align: u32) -> u32 { + assert!(align.is_power_of_two()); + + (num + (align - 1)) & !(align - 1) +} diff --git a/bootloader/mcuboot/sim/tests/core.rs b/bootloader/mcuboot/sim/tests/core.rs new file mode 100644 index 0000000..ee941fe --- /dev/null +++ b/bootloader/mcuboot/sim/tests/core.rs @@ -0,0 +1,201 @@ +// Copyright (c) 2017-2021 Linaro LTD +// Copyright (c) 2017-2019 JUUL Labs +// Copyright (c) 2021-2023 Arm Limited +// +// SPDX-License-Identifier: Apache-2.0 + +//! Core tests +//! +//! Run the existing testsuite as a Rust unit test. + +use bootsim::{ + DepTest, DepType, UpgradeInfo, + ImagesBuilder, + Images, + NO_DEPS, + REV_DEPS, + testlog, + ImageManipulation +}; +use std::{ + env, + sync::atomic::{AtomicUsize, Ordering}, +}; + +/// A single test, after setting up logging and such. Within the $body, +/// $arg will be bound to each device. +macro_rules! test_shell { + ($name:ident, $arg: ident, $body:expr) => { + #[test] + fn $name() { + testlog::setup(); + ImagesBuilder::each_device(|$arg| { + $body; + }); + } + } +} + +/// A typical test calls a particular constructor, and runs a given test on +/// that constructor. +macro_rules! sim_test { + ($name:ident, $maker:ident($($margs:expr),*), $test:ident($($targs:expr),*)) => { + test_shell!($name, r, { + let image = r.$maker($($margs),*); + dump_image(&image, stringify!($name)); + assert!(!image.$test($($targs),*)); + }); + }; +} + +sim_test!(bad_secondary_slot, make_bad_secondary_slot_image(), run_signfail_upgrade()); +sim_test!(secondary_trailer_leftover, make_erased_secondary_image(), run_secondary_leftover_trailer()); +sim_test!(bootstrap, make_bootstrap_image(), run_bootstrap()); +sim_test!(oversized_bootstrap, make_oversized_bootstrap_image(), run_oversized_bootstrap()); +sim_test!(norevert_newimage, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_norevert_newimage()); +sim_test!(basic_revert, make_image(&NO_DEPS, true), run_basic_revert()); +sim_test!(revert_with_fails, make_image(&NO_DEPS, false), run_revert_with_fails()); +sim_test!(perm_with_fails, make_image(&NO_DEPS, true), run_perm_with_fails()); +sim_test!(perm_with_random_fails, make_image(&NO_DEPS, true), run_perm_with_random_fails(5)); +sim_test!(norevert, make_image(&NO_DEPS, true), run_norevert()); + +#[cfg(not(feature = "max-align-32"))] +sim_test!(oversized_secondary_slot, make_oversized_secondary_slot_image(), run_oversizefail_upgrade()); + +sim_test!(status_write_fails_complete, make_image(&NO_DEPS, true), run_with_status_fails_complete()); +sim_test!(status_write_fails_with_reset, make_image(&NO_DEPS, true), run_with_status_fails_with_reset()); +sim_test!(downgrade_prevention, make_image(&REV_DEPS, true), run_nodowngrade()); + +sim_test!(direct_xip_first, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_direct_xip()); +sim_test!(ram_load_first, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_ram_load()); +sim_test!(ram_load_split, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_split_ram_load()); +sim_test!(hw_prot_failed_security_cnt_check, make_image_with_security_counter(Some(0)), run_hw_rollback_prot()); +sim_test!(hw_prot_missing_security_cnt, make_image_with_security_counter(None), run_hw_rollback_prot()); +sim_test!(ram_load_out_of_bounds, make_no_upgrade_image(&NO_DEPS, ImageManipulation::WrongOffset), run_ram_load_boot_with_result(false)); +sim_test!(ram_load_missing_header_flag, make_no_upgrade_image(&NO_DEPS, ImageManipulation::IgnoreRamLoadFlag), run_ram_load_boot_with_result(false)); +sim_test!(ram_load_failed_validation, make_no_upgrade_image(&NO_DEPS, ImageManipulation::BadSignature), run_ram_load_boot_with_result(false)); +sim_test!(ram_load_corrupt_higher_version_image, make_no_upgrade_image(&NO_DEPS, ImageManipulation::CorruptHigherVersionImage), run_ram_load_boot_with_result(true)); + +#[cfg(feature = "multiimage")] +sim_test!(ram_load_overlapping_images_same_base, make_no_upgrade_image(&NO_DEPS, ImageManipulation::OverlapImages(true)), run_ram_load_boot_with_result(false)); +#[cfg(feature = "multiimage")] +sim_test!(ram_load_overlapping_images_offset, make_no_upgrade_image(&NO_DEPS, ImageManipulation::OverlapImages(false)), run_ram_load_boot_with_result(false)); + +// Test various combinations of incorrect dependencies. +test_shell!(dependency_combos, r, { + // Only test setups with two images. + if r.num_images() != 2 { + return; + } + + for dep in TEST_DEPS { + let image = r.clone().make_image(&dep, true); + dump_image(&image, "dependency_combos"); + assert!(!image.run_check_deps(&dep)); + } +}); + +/// These are the variants of dependencies we will test. +pub static TEST_DEPS: &[DepTest] = &[ + // A sanity test, no dependencies should upgrade. + DepTest { + depends: [DepType::Nothing, DepType::Nothing], + upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded], + downgrade: false, + }, + + // If all of the dependencies are met, we should also upgrade. + DepTest { + depends: [DepType::Correct, DepType::Correct], + upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Upgraded], + downgrade: false, + }, + + // If none of the dependencies are met, the images should be held. + DepTest { + depends: [DepType::Newer, DepType::Newer], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Held], + downgrade: false, + }, + + // If the first image is not met, we should hold back on the + // dependencies (it is not well defined what the correct behavior is + // here, it could also be correct to upgrade only the second image). + DepTest { + depends: [DepType::Newer, DepType::Correct], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Held], + downgrade: false, + }, + + // Test the variant in the other direction. + DepTest { + depends: [DepType::Correct, DepType::Newer], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Held], + downgrade: false, + }, + + // Test where only the first image is upgraded, and there are no + // dependencies. + DepTest { + depends: [DepType::Nothing, DepType::NoUpgrade], + upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Held], + downgrade: false, + }, + + // Test one image with a valid dependency on the first image. + DepTest { + depends: [DepType::OldCorrect, DepType::NoUpgrade], + upgrades: [UpgradeInfo::Upgraded, UpgradeInfo::Held], + downgrade: false, + }, + + // Test one image with an invalid dependency on the first image. + DepTest { + depends: [DepType::Newer, DepType::NoUpgrade], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Held], + downgrade: false, + }, + + // Test where only the second image is upgraded, and there are no + // dependencies. + DepTest { + depends: [DepType::NoUpgrade, DepType::Nothing], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Upgraded], + downgrade: false, + }, + + // Test one image with a valid dependency on the second image. + DepTest { + depends: [DepType::NoUpgrade, DepType::OldCorrect], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Upgraded], + downgrade: false, + }, + + // Test one image with an invalid dependency on the second image. + DepTest { + depends: [DepType::NoUpgrade, DepType::Newer], + upgrades: [UpgradeInfo::Held, UpgradeInfo::Held], + downgrade: false, + }, +]; + +/// Counter for the image number. +static IMAGE_NUMBER: AtomicUsize = AtomicUsize::new(0); + +/// Dump an image if that makes sense. The name is the name of the test +/// being run. If the MCUBOT_DEBUG_DUMP environment variable contains, in +/// one of its comma separate strings a substring of this name, then this +/// image will be dumped. As a special case, we will dump everything if +/// this environment variable is set to all. +fn dump_image(image: &Images, name: &str) { + if let Ok(request) = env::var("MCUBOOT_DEBUG_DUMP") { + if request.split(',').any(|req| { + req == "all" || name.contains(req) + }) { + let count = IMAGE_NUMBER.fetch_add(1, Ordering::SeqCst); + let full_name = format!("{}-{:04}", name, count); + log::info!("Dump {:?}", full_name); + image.debug_dump(&full_name); + } + } +} diff --git a/bootloader/mcuboot/testplan/mynewt/Makefile b/bootloader/mcuboot/testplan/mynewt/Makefile new file mode 100644 index 0000000..86e7b9c --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/Makefile @@ -0,0 +1,91 @@ +BLINKY := k64f_blinky +BLINKY2 := k64f_blinky2 + +SLINKY := k64f_slinky +SLINKY2 := k64f_slinky2 + +BOOT_RSA := k64f_boot_rsa +BOOT_RSA_PSS := k64f_boot_rsa_pss +BOOT_EC := k64f_boot_ec +BOOT_EC256 := k64f_boot_ec256 +BOOT_RSA_EC := k64f_boot_rsa_ec +BOOT_RSA_VALIDATE0 := k64f_boot_rsa_validate0 +BOOT_RSA_NOSWAP := k64f_boot_rsa_noswap + +RSA_KEY_1 := key_rsa.pem +RSA_KEY_2 := key_rsa_2.pem + +BLINKY2_IMG := bin/targets/$(BLINKY2)/app/apps/blinky/blinky.img + +FLASH_ERASE := pyocd-flashtool -ce + +NEWTMGR_CONN := --conn k64f +NEWTMGR_IMG := newtmgr $(NEWTMGR_CONN) image + +all: build-apps build-mcuboot + +build-blinky: + @echo "* Building blinky for the primary slot... \c" + @newt build -q $(BLINKY) + @echo "ok" + +build-blinky2: + @echo "* Building blinky2 for the secondary slot... \c" + @newt build -q $(BLINKY2) + @echo "ok" + +build-slinky: + @echo "* Building slinky for the primary slot... \c" + @newt build -q $(SLINKY) + @echo "ok" + +build-slinky2: + @echo "* Building slinky2 for the secondary slot... \c" + @newt build -q $(SLINKY2) + @echo "ok" + +build-boot-rsa: + @echo "* Building mcuboot with RSA... \c" + @newt build -q $(BOOT_RSA) + @echo "ok" + +build-boot-rsa-pss: + @echo "* Building mcuboot with RSA/PSS... \c" + @newt build -q $(BOOT_RSA_PSS) + @echo "ok" + +build-boot-ec: + @echo "* Building mcuboot with EC... \c" + @newt build -q $(BOOT_EC) + @echo "ok" + +build-boot-ec256: + @echo "* Building mcuboot with EC256... \c" + @newt build -q $(BOOT_EC256) + @echo "ok" + +# FIXME: multi-key signing does not work yet... +build-boot-rsa-ec: + @echo "* Building mcuboot with RSA + EC... \c" + @newt build -q $(BOOT_RSA_EC) + @echo "ok" + +build-boot-rsa-validate0: + @echo "* Building mcuboot with primary slot validation... \c" + @newt build -q $(BOOT_RSA_VALIDATE0) + @echo "ok" + +build-boot-rsa-noswap: + @echo "* Building mcuboot with overwrite only upgrade... \c" + @newt build -q $(BOOT_RSA_NOSWAP) + @echo "ok" + +build-apps: build-blinky build-blinky2 build-slinky build-slinky2 + +build-mcuboot: build-boot-rsa build-boot-rsa-pss build-boot-ec \ + build-boot-ec256 build-boot-rsa-validate0 build-boot-rsa-noswap + +clean: + rm -rf bin/ + +.PHONY: all clean diff --git a/bootloader/mcuboot/testplan/mynewt/apps/blinky/pkg.yml b/bootloader/mcuboot/testplan/mynewt/apps/blinky/pkg.yml new file mode 100644 index 0000000..f6f8b8b --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/apps/blinky/pkg.yml @@ -0,0 +1,35 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/blinky +pkg.type: app +pkg.description: Basic example application which blinks an LED. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@mcuboot/boot/bootutil" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/hw/hal" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/mgmt/smp" + - "@apache-mynewt-core/mgmt/smp/transport/smp_shell" + - "@apache-mynewt-core/mgmt/imgmgr" + - "@apache-mynewt-core/sys/log/stub" diff --git a/bootloader/mcuboot/testplan/mynewt/apps/blinky/src/main.c b/bootloader/mcuboot/testplan/mynewt/apps/blinky/src/main.c new file mode 100644 index 0000000..c02cb6d --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/apps/blinky/src/main.c @@ -0,0 +1,78 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#include "sysinit/sysinit.h" +#include "os/os.h" +#include "bsp/bsp.h" +#include "hal/hal_gpio.h" +#ifdef ARCH_sim +#include "mcu/mcu_sim.h" +#endif + +#define BLINKY_PRIO (8) +#define BLINKY_STACK_SIZE OS_STACK_ALIGN(128) +static struct os_task task1; +static volatile int g_task1_loops; + +/* For LED toggling */ +int g_led_pin; + +static void +blinky_handler(void *arg) +{ + while (1) { + ++g_task1_loops; + + os_time_delay(OS_TICKS_PER_SEC / MYNEWT_VAL(BLINKY_TICKS_PER_SEC)); + + /* Toggle the LED */ + hal_gpio_toggle(g_led_pin); + } +} + +int +main(int argc, char **argv) +{ + os_stack_t *pstack; + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + sysinit(); + + g_led_pin = LED_BLINK_PIN; + hal_gpio_init_out(g_led_pin, 1); + + pstack = malloc(sizeof(os_stack_t) * BLINKY_STACK_SIZE); + assert(pstack); + + os_task_init(&task1, "blinky", blinky_handler, NULL, + BLINKY_PRIO, OS_WAIT_FOREVER, pstack, BLINKY_STACK_SIZE); + + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} + diff --git a/bootloader/mcuboot/testplan/mynewt/apps/blinky/syscfg.yml b/bootloader/mcuboot/testplan/mynewt/apps/blinky/syscfg.yml new file mode 100644 index 0000000..e99da2b --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/apps/blinky/syscfg.yml @@ -0,0 +1,8 @@ +syscfg.defs: + BLINKY_TICKS_PER_SEC: + value: 1 + +syscfg.vals: + REBOOT_LOG_ENTRY_COUNT: 0 + REBOOT_LOG_CONSOLE: 0 + SHELL_TASK: 1 diff --git a/bootloader/mcuboot/testplan/mynewt/apps/slinky/pkg.yml b/bootloader/mcuboot/testplan/mynewt/apps/slinky/pkg.yml new file mode 100644 index 0000000..96f8eb7 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/apps/slinky/pkg.yml @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/slinky +pkg.type: app +pkg.description: "Example application which uses a variety of mynewt features." +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@mcuboot/boot/bootutil" + - "@apache-mynewt-core/test/flash_test" + - "@apache-mynewt-core/mgmt/imgmgr" + - "@apache-mynewt-core/mgmt/smp" + - "@apache-mynewt-core/mgmt/smp/transport/smp_shell" + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/shell" + - "@apache-mynewt-core/sys/config" + - "@apache-mynewt-core/sys/console/full" + - "@apache-mynewt-core/sys/id" + - "@apache-mynewt-core/sys/log/full" + - "@apache-mynewt-core/sys/stats/full" diff --git a/bootloader/mcuboot/testplan/mynewt/apps/slinky/src/main.c b/bootloader/mcuboot/testplan/mynewt/apps/slinky/src/main.c new file mode 100644 index 0000000..4da3eeb --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/apps/slinky/src/main.c @@ -0,0 +1,296 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "syscfg/syscfg.h" +#include "sysinit/sysinit.h" +#include "sysflash/sysflash.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "flash_map_backend/flash_map_backend.h" +#include +#if MYNEWT_VAL(SPLIT_LOADER) +#include "split/split.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ARCH_sim +#include +#endif + +/* Task 1 */ +#define TASK1_PRIO (8) +#define TASK1_STACK_SIZE OS_STACK_ALIGN(192) +#define MAX_CBMEM_BUF 600 +static struct os_task task1; +static volatile int g_task1_loops; + +/* Task 2 */ +#define TASK2_PRIO (9) +#define TASK2_STACK_SIZE OS_STACK_ALIGN(64) +static struct os_task task2; + +static struct log my_log; + +static volatile int g_task2_loops; + +/* Global test semaphore */ +static struct os_sem g_test_sem; + +/* For LED toggling */ +static int g_led_pin; + +STATS_SECT_START(gpio_stats) +STATS_SECT_ENTRY(toggles) +STATS_SECT_END + +static STATS_SECT_DECL(gpio_stats) g_stats_gpio_toggle; + +static STATS_NAME_START(gpio_stats) +STATS_NAME(gpio_stats, toggles) +STATS_NAME_END(gpio_stats) + +static char *test_conf_get(int argc, char **argv, char *val, int max_len); +static int test_conf_set(int argc, char **argv, char *val); +static int test_conf_commit(void); +static int test_conf_export(void (*export_func)(char *name, char *val), + enum conf_export_tgt tgt); + +static struct conf_handler test_conf_handler = { + .ch_name = "test", + .ch_get = test_conf_get, + .ch_set = test_conf_set, + .ch_commit = test_conf_commit, + .ch_export = test_conf_export +}; + +static uint8_t test8; +static uint8_t test8_shadow; +static char test_str[32]; +static uint32_t cbmem_buf[MAX_CBMEM_BUF]; +static struct cbmem cbmem; + +static char * +test_conf_get(int argc, char **argv, char *buf, int max_len) +{ + if (argc == 1) { + if (!strcmp(argv[0], "8")) { + return conf_str_from_value(CONF_INT8, &test8, buf, max_len); + } else if (!strcmp(argv[0], "str")) { + return test_str; + } + } + return NULL; +} + +static int +test_conf_set(int argc, char **argv, char *val) +{ + if (argc == 1) { + if (!strcmp(argv[0], "8")) { + return CONF_VALUE_SET(val, CONF_INT8, test8_shadow); + } else if (!strcmp(argv[0], "str")) { + return CONF_VALUE_SET(val, CONF_STRING, test_str); + } + } + return OS_ENOENT; +} + +static int +test_conf_commit(void) +{ + test8 = test8_shadow; + + return 0; +} + +static int +test_conf_export(void (*func)(char *name, char *val), enum conf_export_tgt tgt) +{ + char buf[4]; + + conf_str_from_value(CONF_INT8, &test8, buf, sizeof(buf)); + func("test/8", buf); + func("test/str", test_str); + return 0; +} + +static void +task1_handler(void *arg) +{ + struct os_task *t; + int prev_pin_state, curr_pin_state; + struct image_version ver; + + /* Set the led pin for the E407 devboard */ + g_led_pin = LED_BLINK_PIN; + hal_gpio_init_out(g_led_pin, 1); + + if (imgr_my_version(&ver) == 0) { + console_printf("\nSlinky %u.%u.%u.%u\n", + ver.iv_major, ver.iv_minor, ver.iv_revision, + (unsigned int)ver.iv_build_num); + } else { + console_printf("\nSlinky\n"); + } + + while (1) { + t = os_sched_get_current_task(); + assert(t->t_func == task1_handler); + + ++g_task1_loops; + + /* Wait one second */ + os_time_delay(OS_TICKS_PER_SEC / MYNEWT_VAL(BLINKY_TICKS_PER_SEC)); + + /* Toggle the LED */ + prev_pin_state = hal_gpio_read(g_led_pin); + curr_pin_state = hal_gpio_toggle(g_led_pin); + LOG_INFO(&my_log, LOG_MODULE_DEFAULT, "GPIO toggle from %u to %u", + prev_pin_state, curr_pin_state); + STATS_INC(g_stats_gpio_toggle, toggles); + + /* Release semaphore to task 2 */ + os_sem_release(&g_test_sem); + } +} + +static void +task2_handler(void *arg) +{ + struct os_task *t; + + while (1) { + /* just for debug; task 2 should be the running task */ + t = os_sched_get_current_task(); + assert(t->t_func == task2_handler); + + /* Increment # of times we went through task loop */ + ++g_task2_loops; + + /* Wait for semaphore from ISR */ + os_sem_pend(&g_test_sem, OS_TIMEOUT_NEVER); + } +} + +/** + * init_tasks + * + * Called by main.c after sysinit(). This function performs initializations + * that are required before tasks are running. + * + * @return int 0 success; error otherwise. + */ +static void +init_tasks(void) +{ + os_stack_t *pstack; + + /* Initialize global test semaphore */ + os_sem_init(&g_test_sem, 0); + + pstack = malloc(sizeof(os_stack_t)*TASK1_STACK_SIZE); + assert(pstack); + + os_task_init(&task1, "task1", task1_handler, NULL, + TASK1_PRIO, OS_WAIT_FOREVER, pstack, TASK1_STACK_SIZE); + + pstack = malloc(sizeof(os_stack_t)*TASK2_STACK_SIZE); + assert(pstack); + + os_task_init(&task2, "task2", task2_handler, NULL, + TASK2_PRIO, OS_WAIT_FOREVER, pstack, TASK2_STACK_SIZE); +} + +int read_random_data(void); + +/** + * main + * + * The main task for the project. This function initializes the packages, calls + * init_tasks to initialize additional tasks (and possibly other objects), + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(int argc, char **argv) +{ + int rc; + +#ifdef ARCH_sim + mcu_sim_parse_args(argc, argv); +#endif + + sysinit(); + + rc = conf_register(&test_conf_handler); + assert(rc == 0); + + cbmem_init(&cbmem, cbmem_buf, MAX_CBMEM_BUF); + log_register("log", &my_log, &log_cbmem_handler, &cbmem, LOG_SYSLEVEL); + + stats_init(STATS_HDR(g_stats_gpio_toggle), + STATS_SIZE_INIT_PARMS(g_stats_gpio_toggle, STATS_SIZE_32), + STATS_NAME_INIT_PARMS(gpio_stats)); + + stats_register("gpio_toggle", STATS_HDR(g_stats_gpio_toggle)); + + conf_load(); + + reboot_start(hal_reset_cause()); + + (void) read_random_data(); + + init_tasks(); + + /* If this app is acting as the loader in a split image setup, jump into + * the second stage application instead of starting the OS. + */ +#if MYNEWT_VAL(SPLIT_LOADER) + { + void *entry; + rc = split_app_go(&entry, true); + if(rc == 0) { + hal_system_restart(entry); + } + } +#endif + + /* + * As the last thing, process events from default event queue. + */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } +} diff --git a/bootloader/mcuboot/testplan/mynewt/apps/slinky/src/random_data.c b/bootloader/mcuboot/testplan/mynewt/apps/slinky/src/random_data.c new file mode 100644 index 0000000..cc8c4c7 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/apps/slinky/src/random_data.c @@ -0,0 +1,21354 @@ +#include +static const unsigned char random_bin[] = { + 0x12, 0x3d, 0x29, 0x6d, 0x1e, 0x9a, 0xbf, 0x19, 0xdb, 0x98, 0x7e, 0x80, + 0xb6, 0x2e, 0x25, 0x13, 0xb5, 0x5a, 0x0e, 0xdc, 0x5a, 0x3e, 0xf5, 0x9a, + 0xf1, 0x8a, 0x37, 0xbd, 0x82, 0x3e, 0x99, 0x5d, 0x23, 0x7e, 0x44, 0xd5, + 0x09, 0xdd, 0x2c, 0xfa, 0x51, 0xfb, 0xa4, 0x2e, 0x2b, 0x63, 0x19, 0xd4, + 0x00, 0x1b, 0x4e, 0x53, 0x24, 0xef, 0xdb, 0xa4, 0xd1, 0xe1, 0x0d, 0xa8, + 0xf6, 0x90, 0x03, 0x21, 0x1e, 0xda, 0x94, 0x1f, 0x67, 0xb6, 0xd8, 0x18, + 0x8a, 0x9c, 0xae, 0x4a, 0x9d, 0xd1, 0x09, 0xff, 0x8a, 0xb3, 0x83, 0xeb, + 0x8f, 0x25, 0x5b, 0x4e, 0xad, 0x8c, 0x4d, 0x79, 0x0e, 0x92, 0x67, 0x2a, + 0x4c, 0x71, 0xaf, 0xfb, 0x85, 0xc5, 0x18, 0x30, 0x74, 0x13, 0x13, 0xbf, + 0x09, 0x21, 0xc7, 0x27, 0xef, 0x50, 0x8c, 0x43, 0xc4, 0x41, 0x10, 0xb2, + 0xac, 0x09, 0x9f, 0x43, 0xa3, 0x58, 0xbd, 0xde, 0x85, 0x61, 0x58, 0x65, + 0x5e, 0x0d, 0x71, 0xfa, 0x39, 0xbd, 0x9a, 0x7d, 0xe6, 0x1f, 0x4c, 0x72, + 0x11, 0xb3, 0x1e, 0x81, 0xf8, 0x38, 0x7f, 0x05, 0x67, 0xb1, 0x57, 0x66, + 0x54, 0x5c, 0xcd, 0xc6, 0x69, 0x80, 0x0b, 0xd1, 0x2c, 0xa4, 0xd4, 0x8c, + 0x6d, 0xd7, 0x63, 0x6b, 0x66, 0xa2, 0xc3, 0x46, 0xca, 0x81, 0x3a, 0xaa, + 0xb0, 0x16, 0x40, 0x7e, 0xad, 0xe0, 0x62, 0x39, 0x35, 0x98, 0xed, 0x63, + 0x9b, 0x7a, 0x22, 0xfd, 0xb9, 0x1a, 0x05, 0x59, 0x49, 0xb5, 0x12, 0x20, + 0xbf, 0x9e, 0x3f, 0xe5, 0x50, 0x6e, 0x4d, 0x58, 0xb9, 0xfc, 0x46, 0xd5, + 0x6f, 0x86, 0xc3, 0x52, 0xd7, 0x70, 0x5f, 0xc1, 0x46, 0xf9, 0x88, 0x82, + 0x7d, 0x7b, 0xa1, 0xe9, 0x71, 0xe2, 0xff, 0x7a, 0x44, 0x88, 0x6d, 0x51, + 0x4b, 0x98, 0x37, 0x36, 0xf7, 0xcd, 0x55, 0x93, 0x40, 0x40, 0x91, 0xf0, + 0x6e, 0x9a, 0xb8, 0xa9, 0xa0, 0xa6, 0xaa, 0xd8, 0x0d, 0xc4, 0x84, 0x1e, + 0xbe, 0x70, 0x7a, 0xb8, 0xa6, 0x09, 0x38, 0x13, 0x0b, 0xb2, 0x56, 0x67, + 0xbe, 0x21, 0x61, 0x7d, 0xc6, 0x5d, 0xe5, 0xfe, 0xd8, 0x54, 0xe0, 0xc5, + 0x42, 0xed, 0x76, 0x7f, 0xd5, 0x42, 0x38, 0x8e, 0x83, 0x60, 0xc1, 0x4c, + 0xe6, 0x7e, 0xa4, 0x6e, 0x6b, 0x04, 0xd4, 0x38, 0xe0, 0x6f, 0xfe, 0xe0, + 0xd2, 0x79, 0xd6, 0xe9, 0x3c, 0xa9, 0xb6, 0x0d, 0x2a, 0xc3, 0x3f, 0x35, + 0xa6, 0x95, 0xb5, 0x62, 0xc4, 0xfb, 0x48, 0x89, 0x7c, 0x17, 0x65, 0xb9, + 0xa2, 0x4e, 0xe2, 0x09, 0xd2, 0xcd, 0xcf, 0x87, 0xb4, 0x7f, 0x17, 0x42, + 0xcc, 0x3a, 0x89, 0x38, 0x4e, 0x6d, 0xbf, 0x66, 0xec, 0xa1, 0x65, 0xea, + 0xd3, 0x1d, 0x19, 0x42, 0xb2, 0xc0, 0x81, 0x27, 0x10, 0xd9, 0x3f, 0x6c, + 0x42, 0x3e, 0xcd, 0x16, 0x8b, 0x35, 0x43, 0xea, 0x3b, 0x43, 0x95, 0x25, + 0x81, 0x80, 0x45, 0x25, 0xe1, 0x15, 0x36, 0xa7, 0x4e, 0x70, 0x02, 0x16, + 0xa8, 0x79, 0x88, 0xf3, 0x52, 0x06, 0x17, 0x9d, 0xa1, 0x4d, 0x4d, 0x67, + 0xdf, 0x2b, 0x25, 0xe3, 0xf2, 0xf0, 0x10, 0x21, 0x3c, 0x7a, 0xc6, 0xb1, + 0xa7, 0xa4, 0x9d, 0x58, 0x64, 0x4c, 0xed, 0xa3, 0x27, 0x91, 0xba, 0x6a, + 0xc3, 0x27, 0x47, 0xd7, 0x16, 0x72, 0x52, 0x83, 0xac, 0xd9, 0x85, 0x47, + 0x06, 0x5d, 0x76, 0xd9, 0x62, 0x89, 0x49, 0xca, 0x7e, 0x67, 0x6a, 0x99, + 0xa8, 0x2a, 0xf6, 0x57, 0xb1, 0x42, 0x13, 0x74, 0xc3, 0x13, 0xe3, 0x91, + 0x38, 0x0c, 0x07, 0xb3, 0xbf, 0xd4, 0x05, 0xb4, 0xd8, 0x90, 0x65, 0x4b, + 0x10, 0x08, 0xe6, 0xa1, 0x76, 0x09, 0x46, 0x4e, 0xe4, 0xda, 0xc2, 0xaa, + 0x7b, 0xab, 0xcf, 0x83, 0xe4, 0x19, 0x5e, 0x4b, 0xf5, 0x7a, 0x32, 0x6c, + 0x25, 0x8d, 0xd8, 0x58, 0x61, 0x02, 0x1a, 0x0b, 0x8d, 0xc8, 0x80, 0xd3, + 0x7b, 0xba, 0xb2, 0x5f, 0xd3, 0xe9, 0xc9, 0x4c, 0x15, 0xe5, 0x76, 0xfe, + 0x3a, 0x1f, 0x26, 0x2b, 0xe7, 0x49, 0x5f, 0xdc, 0x6b, 0xab, 0xd4, 0x3b, + 0x69, 0x84, 0x75, 0x75, 0x65, 0x3a, 0xcb, 0xd7, 0xea, 0x20, 0xda, 0xf8, + 0x4c, 0xd1, 0x78, 0x08, 0xdf, 0x67, 0x22, 0x0f, 0xb2, 0xa9, 0x32, 0x27, + 0xca, 0x17, 0x89, 0x7b, 0x16, 0x9f, 0x48, 0x4f, 0xc4, 0x59, 0x48, 0x8e, + 0xe5, 0x26, 0x3a, 0x19, 0x50, 0x44, 0xad, 0x69, 0xed, 0x58, 0x1a, 0xbb, + 0x72, 0x9c, 0x4c, 0x48, 0x7c, 0x1b, 0x42, 0x13, 0x03, 0x3f, 0xb6, 0xb5, + 0x37, 0xca, 0x97, 0xf1, 0x82, 0x68, 0xdd, 0xf8, 0x22, 0x63, 0xf0, 0x80, + 0xf2, 0x7b, 0xee, 0xb9, 0x5f, 0x6d, 0x15, 0xed, 0xa5, 0x26, 0xd1, 0x3f, + 0xb1, 0xfd, 0x9a, 0x7e, 0xe9, 0x5b, 0x46, 0x04, 0x63, 0xe5, 0xb5, 0x1b, + 0x25, 0x5b, 0x65, 0x17, 0x03, 0x55, 0x3a, 0x3f, 0x23, 0xde, 0x55, 0x56, + 0xcd, 0xcc, 0x92, 0xc1, 0x26, 0xbf, 0x7f, 0x6b, 0x2c, 0x9e, 0xed, 0x05, + 0xd7, 0x7f, 0x8d, 0x37, 0xe2, 0x55, 0x15, 0x72, 0x8a, 0xa7, 0x30, 0x84, + 0x7d, 0x9c, 0x36, 0x95, 0x2d, 0x6a, 0x0f, 0xfe, 0x6b, 0x85, 0x0c, 0x6e, + 0xdc, 0x2b, 0x78, 0x75, 0xd8, 0x65, 0xe4, 0xae, 0x8b, 0xca, 0x71, 0x92, + 0x63, 0xe3, 0xd4, 0x49, 0xa2, 0xa1, 0xde, 0x87, 0xfe, 0x6f, 0xb5, 0x26, + 0x80, 0xfd, 0xda, 0x02, 0xfc, 0xf1, 0xc0, 0x4d, 0xda, 0xf4, 0x58, 0x98, + 0x9f, 0x41, 0xcc, 0xc9, 0xcf, 0xbb, 0x85, 0xe8, 0x83, 0x6e, 0x2a, 0xc0, + 0x01, 0xaf, 0x8c, 0x98, 0x26, 0xd8, 0x08, 0x62, 0x40, 0x0c, 0x8c, 0xfd, + 0x0a, 0xd5, 0x1e, 0xdc, 0xa9, 0x72, 0x07, 0xf9, 0x75, 0x9e, 0x22, 0xa2, + 0xfd, 0x8c, 0x61, 0xee, 0xb9, 0x9b, 0x6a, 0x95, 0x73, 0xaa, 0xe6, 0x97, + 0x68, 0x07, 0x91, 0xc4, 0x46, 0x05, 0x6d, 0x2a, 0x8b, 0x45, 0x71, 0x20, + 0x90, 0x3b, 0x31, 0x3c, 0xac, 0xc4, 0x51, 0x31, 0xb1, 0x95, 0x0e, 0x70, + 0x1a, 0x28, 0x31, 0x14, 0x13, 0x5c, 0xb7, 0x1f, 0x60, 0x0b, 0x21, 0xc8, + 0x62, 0x00, 0x42, 0x5a, 0xbe, 0x72, 0xe7, 0xb1, 0x4b, 0x05, 0xe0, 0xe8, + 0xcc, 0x0e, 0xdb, 0xbb, 0xc3, 0x6b, 0x9c, 0x98, 0xa2, 0x7c, 0x64, 0xdf, + 0xaa, 0x5f, 0xdd, 0x77, 0x15, 0xe6, 0x54, 0x1b, 0x64, 0xe6, 0xf0, 0x7b, + 0x22, 0xdd, 0x7b, 0x4d, 0x35, 0xb5, 0x08, 0x69, 0xe8, 0x3e, 0xaa, 0x0c, + 0xf3, 0x2e, 0x40, 0xa2, 0x3a, 0x61, 0x61, 0x45, 0xa4, 0x0f, 0xea, 0xca, + 0xa2, 0x42, 0x35, 0xac, 0x03, 0x55, 0x93, 0x0e, 0x4d, 0x04, 0x71, 0xf4, + 0xa5, 0xb8, 0xf7, 0xe1, 0xe3, 0x20, 0xbe, 0xb9, 0xbf, 0x4e, 0xab, 0x64, + 0xd6, 0x37, 0x2f, 0x8b, 0xec, 0xfb, 0x88, 0x57, 0xbb, 0x8e, 0xcf, 0xc9, + 0xc7, 0xe0, 0x3b, 0xbf, 0xe4, 0x05, 0x63, 0xc3, 0xe7, 0x41, 0xa6, 0x2e, + 0xab, 0x02, 0x75, 0xc3, 0x39, 0x02, 0x0a, 0xec, 0xac, 0xd3, 0x00, 0xb2, + 0xd0, 0xf2, 0x72, 0x76, 0xb1, 0xdb, 0x69, 0x2f, 0xb9, 0x22, 0x68, 0x60, + 0x85, 0x12, 0x0c, 0x44, 0x1c, 0x14, 0xbb, 0xc6, 0x0e, 0x71, 0xaa, 0x44, + 0xa4, 0xe4, 0x65, 0x92, 0x52, 0x1e, 0x17, 0x4e, 0xe1, 0xfe, 0x42, 0x15, + 0x6e, 0x77, 0x5c, 0x73, 0x30, 0x0b, 0xb3, 0x68, 0xee, 0x6b, 0x69, 0x86, + 0x44, 0x6e, 0xb4, 0x90, 0x80, 0xa5, 0x04, 0xce, 0xc2, 0x6b, 0x42, 0x55, + 0xf2, 0xc7, 0x4f, 0x19, 0x26, 0x32, 0xc2, 0x23, 0x3b, 0x2f, 0x4e, 0x10, + 0x0c, 0x8d, 0x23, 0x52, 0x35, 0xc5, 0x03, 0x38, 0x47, 0xf0, 0x57, 0x90, + 0x84, 0x40, 0x1e, 0x24, 0xa3, 0x5a, 0x6f, 0xc1, 0x10, 0x87, 0x5e, 0x93, + 0x3e, 0x30, 0xb1, 0x3d, 0x2e, 0x5e, 0xd1, 0xad, 0xd8, 0x71, 0xaf, 0xd1, + 0xd7, 0x17, 0x82, 0x54, 0x5a, 0xe8, 0xc4, 0x55, 0xee, 0xd9, 0xc6, 0x42, + 0x1e, 0xea, 0x76, 0xfd, 0x68, 0xcf, 0xb3, 0x7b, 0x60, 0x66, 0x12, 0xec, + 0x44, 0xab, 0x0c, 0x22, 0xd3, 0x94, 0x97, 0x06, 0x0d, 0xd1, 0xac, 0xd1, + 0x77, 0x10, 0x17, 0xfd, 0x04, 0x24, 0xc4, 0x19, 0x1c, 0x10, 0x2d, 0xfb, + 0x03, 0xa5, 0xe7, 0xf4, 0x3d, 0x4a, 0xff, 0xf5, 0x55, 0x30, 0xf7, 0x9b, + 0xf4, 0x38, 0x57, 0xc6, 0xa8, 0x70, 0x72, 0xc6, 0xe0, 0x79, 0x4c, 0xb5, + 0xc6, 0x3a, 0xdb, 0x8f, 0x4d, 0x2a, 0x70, 0x1d, 0xba, 0x05, 0x20, 0x76, + 0x40, 0xc3, 0xc0, 0xdc, 0xf9, 0x47, 0x9c, 0x66, 0x36, 0x13, 0xe4, 0xc5, + 0xf8, 0x80, 0x5e, 0xdf, 0x28, 0xbc, 0x00, 0x87, 0x77, 0x00, 0x75, 0x55, + 0x2f, 0x53, 0x4d, 0x4d, 0x65, 0xb5, 0xaa, 0x91, 0x23, 0x60, 0x89, 0xfe, + 0x8d, 0x2d, 0x54, 0x70, 0x45, 0x71, 0xab, 0x17, 0xc3, 0x2d, 0x97, 0x9c, + 0x3f, 0xe9, 0x81, 0xda, 0x27, 0x15, 0x2c, 0x7a, 0xc3, 0x9e, 0x9c, 0x2c, + 0x99, 0x1b, 0xee, 0x68, 0xdf, 0x5f, 0x59, 0x97, 0xc6, 0x89, 0x05, 0x8a, + 0x3c, 0x19, 0xe9, 0x2e, 0x3c, 0x32, 0x71, 0x1f, 0xf8, 0x08, 0xdb, 0xc9, + 0xad, 0xc0, 0xbe, 0xfa, 0x56, 0x95, 0x2b, 0x90, 0x3e, 0x97, 0x38, 0x8e, + 0xb8, 0xf9, 0xed, 0xcb, 0x41, 0x32, 0x28, 0x0c, 0x78, 0xca, 0x4b, 0x49, + 0xce, 0x1d, 0xc0, 0xbb, 0x60, 0x60, 0x24, 0x26, 0xb8, 0x47, 0x36, 0xe1, + 0x7e, 0xed, 0xac, 0x59, 0xb0, 0x02, 0x09, 0x0e, 0x56, 0xce, 0x7e, 0x6b, + 0x91, 0xbe, 0x3b, 0xe7, 0xd4, 0x8d, 0x65, 0x48, 0x87, 0x8f, 0x75, 0xa5, + 0x4a, 0xac, 0xcb, 0x52, 0x10, 0x83, 0xfa, 0xdd, 0x44, 0x18, 0xc9, 0x80, + 0x7f, 0xaa, 0xed, 0x8e, 0x77, 0x7c, 0x9a, 0x91, 0x4f, 0x77, 0xc4, 0xcb, + 0x23, 0x2b, 0x5f, 0x3f, 0x92, 0x93, 0x0a, 0xf0, 0x62, 0xc5, 0x11, 0x92, + 0x52, 0x8a, 0xda, 0x31, 0xee, 0x95, 0x01, 0x79, 0xf3, 0x79, 0x77, 0x1f, + 0x0a, 0xef, 0x5d, 0x6f, 0x0a, 0xce, 0x86, 0xa7, 0xb2, 0xd3, 0x57, 0x48, + 0xd4, 0x2f, 0x7f, 0x45, 0xf8, 0xea, 0xbc, 0x48, 0xb9, 0x9e, 0xce, 0x71, + 0xbf, 0xa8, 0x16, 0x73, 0x32, 0x9b, 0x42, 0x86, 0xfb, 0x8f, 0x6f, 0x9b, + 0xd7, 0x18, 0x22, 0x94, 0x0f, 0xcc, 0x91, 0x71, 0xd4, 0x90, 0x5c, 0xd0, + 0x79, 0x05, 0x7e, 0x53, 0xfd, 0x3d, 0x18, 0x5b, 0x34, 0xd7, 0xe2, 0xca, + 0xfc, 0xe6, 0xd6, 0x8d, 0x49, 0xa0, 0xca, 0x63, 0xfb, 0x31, 0x4d, 0xcb, + 0xe2, 0x01, 0x56, 0x0b, 0xdb, 0x8d, 0x4f, 0xa2, 0x19, 0x9d, 0xd2, 0xec, + 0x95, 0x53, 0x7b, 0x99, 0xf4, 0xfb, 0x27, 0xbe, 0xbd, 0x1e, 0xbb, 0x87, + 0x7d, 0x89, 0x3b, 0xf9, 0xef, 0xde, 0x5e, 0x85, 0xc8, 0x8d, 0xa0, 0x2c, + 0x9f, 0x3c, 0xea, 0x61, 0x91, 0xaa, 0x4e, 0x15, 0xdb, 0x81, 0x6e, 0x9f, + 0xd6, 0x47, 0x6a, 0x7c, 0x6c, 0xe2, 0x9e, 0x0e, 0xa7, 0xcd, 0x88, 0x21, + 0x6f, 0x73, 0x82, 0xfa, 0xf2, 0x4a, 0x39, 0xe4, 0x6c, 0x1e, 0x21, 0xf6, + 0x63, 0x13, 0x39, 0x7f, 0x18, 0x7a, 0x02, 0x94, 0x8d, 0x5d, 0x09, 0xbf, + 0x52, 0xdf, 0xe8, 0x09, 0x40, 0xac, 0x5e, 0x4c, 0xc9, 0xa3, 0x71, 0x4d, + 0x4c, 0xcb, 0x42, 0x23, 0x42, 0xe1, 0x52, 0x4a, 0x0f, 0xe8, 0x3e, 0xf2, + 0x4e, 0x54, 0xaa, 0x38, 0x89, 0xd5, 0x6b, 0x3e, 0xae, 0x89, 0x0c, 0x5e, + 0x16, 0x17, 0x5b, 0x6c, 0xdf, 0x0b, 0xc9, 0x6e, 0x84, 0x05, 0x6b, 0xe1, + 0xe8, 0x18, 0x0a, 0x7c, 0xc9, 0x42, 0xa1, 0xf1, 0xfd, 0x48, 0xee, 0x13, + 0x1e, 0x16, 0x5f, 0x62, 0xf8, 0x88, 0x00, 0x18, 0x3b, 0x6d, 0x13, 0x9a, + 0xcd, 0x43, 0x9f, 0xc5, 0xeb, 0x4c, 0x3d, 0xe2, 0xbb, 0xde, 0x48, 0x77, + 0x21, 0x65, 0x34, 0xc3, 0x08, 0xe6, 0x0f, 0x91, 0x4c, 0xc8, 0xf6, 0xac, + 0xfe, 0x51, 0xe4, 0xed, 0x9d, 0xa9, 0x1d, 0x6c, 0x75, 0x60, 0x56, 0xb0, + 0xf2, 0x7b, 0xa7, 0x92, 0xa9, 0x7c, 0x20, 0x02, 0x3e, 0x91, 0x1c, 0x10, + 0xd9, 0x04, 0xa9, 0x0e, 0x5c, 0x28, 0x9c, 0x39, 0x71, 0x4f, 0xa7, 0xf9, + 0x14, 0x12, 0x76, 0x33, 0x02, 0xcc, 0x20, 0xf0, 0x84, 0xed, 0xa4, 0xa5, + 0xf0, 0x4d, 0x44, 0xcd, 0xa7, 0x38, 0x59, 0x60, 0xfd, 0x83, 0xa1, 0x2c, + 0x0c, 0xec, 0x20, 0x28, 0x12, 0xc9, 0xa4, 0x87, 0x71, 0x2f, 0x9a, 0x70, + 0x22, 0xd7, 0xc7, 0x0a, 0x85, 0x79, 0xc2, 0x95, 0x16, 0x26, 0xc0, 0x6a, + 0xad, 0x31, 0x9a, 0x7c, 0x0e, 0x93, 0x5d, 0x9f, 0xef, 0xd4, 0xe0, 0x8b, + 0x5d, 0xfe, 0xff, 0xc9, 0x6c, 0x15, 0x64, 0x77, 0xc3, 0x2d, 0xf9, 0x0b, + 0x03, 0xca, 0x78, 0x37, 0xe5, 0x4d, 0x97, 0x9f, 0xfe, 0xae, 0xb0, 0x0f, + 0x87, 0x65, 0x20, 0xfc, 0x32, 0xd4, 0xe7, 0x9d, 0xdf, 0xa8, 0x71, 0xa7, + 0x88, 0x7a, 0xdb, 0x2f, 0x32, 0x97, 0xc5, 0x42, 0xb7, 0x1d, 0xd0, 0x9a, + 0xdc, 0x34, 0x64, 0x50, 0xa2, 0x17, 0x94, 0x98, 0xb1, 0xb6, 0x75, 0xb8, + 0xe0, 0x97, 0x2d, 0x89, 0x3e, 0x9c, 0x9e, 0x20, 0x22, 0xc4, 0xbb, 0xef, + 0xe4, 0xa9, 0xb3, 0x8f, 0x57, 0xa3, 0xf0, 0x06, 0xf2, 0xc2, 0x2d, 0x1a, + 0x20, 0x4f, 0x88, 0xfc, 0x3c, 0xc6, 0x7a, 0x93, 0xbb, 0x39, 0x81, 0x96, + 0x3f, 0xf2, 0xf9, 0x7d, 0xd3, 0x14, 0x8f, 0x0e, 0x0a, 0x00, 0xd7, 0xc9, + 0xa1, 0xc4, 0x01, 0x84, 0x5f, 0x3c, 0x79, 0xac, 0x1b, 0xdd, 0xfe, 0x99, + 0x79, 0xa0, 0x09, 0x95, 0x73, 0x4a, 0x42, 0x0a, 0x57, 0x71, 0x5e, 0x7a, + 0x0d, 0x9b, 0x53, 0x98, 0x7a, 0x13, 0x64, 0x40, 0xe6, 0x3a, 0x55, 0xab, + 0xe4, 0xf9, 0x75, 0x53, 0x6c, 0xf7, 0x62, 0xa1, 0xd7, 0x00, 0x14, 0x10, + 0xa9, 0xa7, 0xac, 0x48, 0x98, 0x75, 0xd5, 0x8a, 0xc9, 0x2d, 0x5b, 0x29, + 0x70, 0x70, 0xfa, 0xca, 0xc8, 0x35, 0x37, 0x03, 0x62, 0xe7, 0xe6, 0xba, + 0x76, 0xdd, 0xcc, 0xf6, 0x9f, 0x5f, 0xc6, 0x49, 0xed, 0x78, 0xf8, 0x87, + 0x47, 0x74, 0xe7, 0x91, 0x91, 0x2d, 0xdb, 0xf0, 0x9a, 0xbe, 0x0d, 0xb3, + 0x9c, 0xd9, 0xb3, 0xa0, 0xc3, 0xbc, 0x76, 0x43, 0xdd, 0xd3, 0xba, 0x73, + 0x98, 0x0f, 0xe7, 0x9a, 0x5f, 0x58, 0x8d, 0x79, 0xc3, 0x91, 0x2c, 0x0c, + 0x8b, 0x06, 0x06, 0xe2, 0x54, 0xf9, 0x51, 0xdb, 0x03, 0xac, 0x15, 0xb7, + 0x16, 0xb4, 0x88, 0x47, 0x69, 0x14, 0x11, 0xe1, 0xd3, 0x2a, 0x07, 0xa3, + 0x0c, 0x00, 0xb9, 0xb6, 0x2a, 0x77, 0xbe, 0x73, 0x36, 0x50, 0x4a, 0x8f, + 0x71, 0xb0, 0x1c, 0x1c, 0x4e, 0xbb, 0xe8, 0xdd, 0x96, 0xaf, 0xa1, 0x40, + 0xc6, 0x3d, 0xa2, 0x8d, 0xa5, 0x3b, 0xe9, 0x1a, 0x5c, 0xc0, 0x9a, 0x1f, + 0x01, 0x70, 0x5e, 0xdb, 0xd7, 0x4b, 0x75, 0x92, 0x2f, 0x7e, 0x19, 0x55, + 0xec, 0xa6, 0xe4, 0x61, 0x3b, 0x25, 0x75, 0x50, 0x59, 0xd4, 0x7e, 0xd9, + 0x5a, 0x75, 0x87, 0xcd, 0x94, 0x5e, 0x9b, 0x3d, 0x0a, 0xb8, 0xc9, 0x75, + 0x83, 0x98, 0x16, 0x43, 0x77, 0x07, 0x1a, 0xb4, 0x2e, 0x4a, 0xad, 0xa8, + 0xcc, 0x6f, 0x3e, 0xa5, 0x4b, 0x43, 0xca, 0xb4, 0x0a, 0x2e, 0xdc, 0x5a, + 0x57, 0xa0, 0x59, 0x64, 0x0a, 0xdd, 0xef, 0x83, 0x02, 0xb5, 0xdb, 0x78, + 0x59, 0xef, 0xc3, 0xed, 0xea, 0x70, 0x87, 0x08, 0xee, 0x2b, 0xf4, 0x5d, + 0x1e, 0x24, 0x94, 0x08, 0xa0, 0x3e, 0xe1, 0x87, 0x9f, 0xd2, 0x52, 0xce, + 0x53, 0xc2, 0x20, 0x9b, 0x25, 0x1d, 0xee, 0xb2, 0x52, 0x85, 0xaa, 0x4f, + 0x57, 0x88, 0x1d, 0x16, 0x71, 0xd3, 0x54, 0xaf, 0xbb, 0xb6, 0x92, 0xd1, + 0x1d, 0x28, 0xb3, 0x6f, 0x61, 0xd4, 0x9a, 0xa2, 0xd6, 0x69, 0xd6, 0x77, + 0xcb, 0xac, 0x33, 0x7a, 0x30, 0xc4, 0x39, 0xbc, 0xf3, 0xe5, 0xdc, 0x87, + 0x6d, 0x49, 0xe6, 0xbb, 0xa3, 0x19, 0xa6, 0xb2, 0x0a, 0x8b, 0xe9, 0xea, + 0xda, 0x06, 0xe1, 0xc6, 0xee, 0x8e, 0xa4, 0x7b, 0xfd, 0x53, 0xae, 0xa0, + 0x65, 0x00, 0x76, 0xa1, 0x2c, 0xb1, 0xb6, 0x86, 0xcd, 0x5e, 0xae, 0xef, + 0x5a, 0x93, 0x0e, 0x42, 0xdc, 0xfa, 0x0c, 0x31, 0xad, 0x3d, 0x05, 0x67, + 0x33, 0x13, 0x51, 0x3c, 0x5e, 0x0b, 0xfe, 0x3f, 0x28, 0x1a, 0x15, 0xca, + 0x84, 0xac, 0xfd, 0x9c, 0xcf, 0xe4, 0xfa, 0xc0, 0x7e, 0x91, 0x49, 0x76, + 0x30, 0x2e, 0xcf, 0x19, 0x5b, 0xfb, 0xb1, 0xa0, 0xa6, 0x39, 0x6b, 0x49, + 0x1e, 0xf1, 0x79, 0x58, 0x18, 0xf5, 0x42, 0xf4, 0x14, 0x1f, 0xa6, 0x24, + 0x1e, 0xaf, 0x11, 0x27, 0xf5, 0x99, 0x11, 0xc2, 0x6a, 0xa0, 0x05, 0x12, + 0x0b, 0x27, 0x09, 0xa1, 0x36, 0xb3, 0x02, 0x9d, 0x7e, 0x42, 0xee, 0x72, + 0xc1, 0x5f, 0x8b, 0xa8, 0x19, 0xa1, 0x28, 0xa2, 0x60, 0x5a, 0x71, 0xa7, + 0x64, 0x6c, 0x05, 0xe4, 0x40, 0x1f, 0x66, 0xff, 0xcc, 0x5e, 0xe5, 0x15, + 0x92, 0x2c, 0xad, 0xeb, 0x5b, 0x8e, 0x58, 0xe0, 0x80, 0xf4, 0x93, 0x87, + 0x54, 0xad, 0x6a, 0x40, 0x81, 0x15, 0x1f, 0x39, 0xe0, 0xb7, 0xd2, 0x52, + 0x77, 0x05, 0x81, 0x1f, 0x07, 0x3f, 0xa2, 0x4a, 0x40, 0xd6, 0x70, 0xc0, + 0x1a, 0xd7, 0xcf, 0x02, 0x69, 0x6f, 0x1a, 0xf0, 0x34, 0x4e, 0x7f, 0x22, + 0x9e, 0xd5, 0x64, 0x6d, 0x95, 0x06, 0x0d, 0x8a, 0xd9, 0xae, 0x2c, 0x5c, + 0x85, 0x49, 0x3f, 0x1b, 0xfe, 0x60, 0xa4, 0xdb, 0xac, 0xf7, 0x7b, 0x45, + 0x24, 0xce, 0xf1, 0xcb, 0x92, 0x29, 0xfe, 0xb7, 0xf8, 0xe5, 0xa0, 0x36, + 0x53, 0x16, 0x10, 0xb6, 0x59, 0xb3, 0x09, 0x65, 0x25, 0xc4, 0xa8, 0x0c, + 0x94, 0x57, 0xa8, 0xd5, 0xa6, 0x41, 0xc7, 0xcd, 0xa7, 0xc3, 0x7e, 0x0a, + 0x65, 0x05, 0x42, 0x2c, 0x9f, 0x5e, 0x93, 0x1d, 0x01, 0x5a, 0xe3, 0x43, + 0x22, 0x92, 0x5c, 0xd9, 0xae, 0x13, 0xb0, 0x9a, 0x68, 0xc0, 0xcc, 0x3b, + 0xbc, 0xb5, 0xbe, 0xf9, 0x8d, 0xec, 0xb2, 0x13, 0xa6, 0x12, 0x9a, 0xf0, + 0x73, 0xbb, 0x36, 0x3f, 0x15, 0xc0, 0x1c, 0xb0, 0xac, 0xb4, 0x5a, 0xcc, + 0x27, 0x8f, 0x2f, 0x36, 0x4f, 0xe6, 0x2a, 0xf9, 0xfd, 0x77, 0x1d, 0x6d, + 0x4a, 0x89, 0xa8, 0xf2, 0x9c, 0x67, 0x98, 0xc9, 0x3d, 0x07, 0xc0, 0x49, + 0x5d, 0x44, 0x85, 0x84, 0x5e, 0x65, 0x3d, 0xd7, 0x1f, 0xcf, 0x50, 0x25, + 0x19, 0x8b, 0x0f, 0xc3, 0x64, 0x88, 0xe2, 0x56, 0xbb, 0xd2, 0xdb, 0x1a, + 0x6a, 0x72, 0xbf, 0xb8, 0xd3, 0x82, 0x09, 0xae, 0x86, 0x33, 0xc9, 0x58, + 0xb2, 0xb5, 0xce, 0x37, 0x6a, 0xe4, 0x29, 0x8a, 0xab, 0x09, 0x9e, 0xf4, + 0xab, 0x25, 0x79, 0xe3, 0x7e, 0x9a, 0x35, 0xc1, 0x9c, 0x17, 0xef, 0x13, + 0xac, 0x86, 0x38, 0xf7, 0xa9, 0x85, 0xb9, 0x2a, 0x42, 0x57, 0xbe, 0x08, + 0x35, 0x8b, 0x5c, 0x4d, 0xdd, 0xac, 0xda, 0xa9, 0xa1, 0xe7, 0x72, 0xa9, + 0x0d, 0x7d, 0x04, 0xb3, 0x11, 0x8e, 0x17, 0x69, 0x0a, 0x5e, 0xd7, 0xdc, + 0xac, 0x68, 0xaa, 0x81, 0xe7, 0xaa, 0xb2, 0xfc, 0x6c, 0xff, 0x2c, 0xdb, + 0x55, 0x06, 0xe4, 0x22, 0xd6, 0xd0, 0xab, 0xad, 0x32, 0xb0, 0x8a, 0x40, + 0x24, 0xf7, 0x69, 0xe5, 0x98, 0x6b, 0x28, 0x5a, 0xec, 0x2c, 0x8f, 0x64, + 0x07, 0xf7, 0xe8, 0xc4, 0x73, 0xac, 0x3a, 0xe8, 0xd0, 0xf9, 0xdb, 0x3c, + 0xea, 0x5a, 0x0b, 0xdc, 0x8e, 0xc7, 0x7b, 0xdd, 0xba, 0xc3, 0x49, 0x41, + 0x09, 0xd3, 0x42, 0xbc, 0xa5, 0xd2, 0x4e, 0x94, 0x1d, 0x6a, 0x7d, 0xcf, + 0xdd, 0xe5, 0x01, 0x98, 0x1f, 0x64, 0xf2, 0xc2, 0xb5, 0xda, 0x97, 0x01, + 0xff, 0x1d, 0x07, 0xfa, 0xc1, 0xb1, 0xe3, 0x22, 0x45, 0x52, 0x8c, 0x29, + 0x46, 0xb9, 0x45, 0xea, 0x04, 0x2e, 0xf7, 0x62, 0x01, 0x7d, 0x14, 0x64, + 0x43, 0xd1, 0xf1, 0xc7, 0x30, 0xfa, 0x2e, 0x73, 0xde, 0x69, 0xfa, 0x31, + 0x90, 0x7e, 0x41, 0xdc, 0x9b, 0x3d, 0x96, 0x28, 0xe1, 0xb0, 0x78, 0x3f, + 0x7a, 0x6a, 0xe2, 0xb3, 0x7d, 0x5f, 0x63, 0xb9, 0xfb, 0x98, 0x30, 0xb0, + 0xd5, 0x77, 0xf2, 0xbf, 0x61, 0x63, 0x73, 0x52, 0xdf, 0x94, 0x18, 0x8b, + 0xb8, 0x03, 0xa2, 0x7e, 0xc5, 0x8f, 0x15, 0x52, 0x1f, 0x20, 0xfb, 0x86, + 0xe0, 0x9c, 0x61, 0xfc, 0x5e, 0x2f, 0x47, 0x87, 0x71, 0x52, 0xe6, 0x3e, + 0x48, 0x5b, 0x2f, 0x46, 0x51, 0x9e, 0x92, 0xa2, 0x34, 0x3d, 0xc0, 0xe5, + 0xb1, 0xea, 0x1c, 0x55, 0x18, 0x2e, 0x3b, 0xb4, 0x83, 0x94, 0x1c, 0x0a, + 0x01, 0xeb, 0xc6, 0x42, 0x5d, 0xe3, 0x46, 0x95, 0x72, 0x08, 0x1f, 0x06, + 0xea, 0xf7, 0xa8, 0x7e, 0x1f, 0x11, 0xf4, 0xc9, 0x0e, 0x51, 0x30, 0x67, + 0x1d, 0x8b, 0x11, 0xe5, 0xb9, 0xe0, 0xe6, 0xd9, 0xfb, 0xc7, 0x05, 0x9c, + 0xbc, 0x45, 0x0b, 0x46, 0x57, 0x82, 0xf1, 0x92, 0x3d, 0xa3, 0x30, 0x14, + 0x15, 0xe8, 0x2c, 0xfe, 0x02, 0xe5, 0x16, 0x85, 0x59, 0x96, 0x8a, 0x6b, + 0xd7, 0x45, 0xac, 0xe9, 0xf6, 0x84, 0x80, 0x1b, 0xf3, 0x30, 0x74, 0xe6, + 0x4b, 0x85, 0xb8, 0x47, 0xe2, 0xa3, 0xac, 0x72, 0xbe, 0x6d, 0x45, 0xe0, + 0x3a, 0x4a, 0x69, 0xc0, 0xb7, 0xfa, 0xae, 0x4f, 0x74, 0xa6, 0x21, 0x46, + 0xa1, 0xcd, 0x4a, 0xe3, 0x80, 0x57, 0x7b, 0xd5, 0xd8, 0x73, 0x2e, 0x56, + 0xe7, 0xf9, 0xf9, 0x35, 0xb6, 0xb8, 0x7b, 0x31, 0x27, 0x46, 0xbc, 0x02, + 0xa5, 0x70, 0xb8, 0x90, 0xe8, 0xdd, 0x80, 0x93, 0xbb, 0x93, 0xf9, 0xae, + 0x1d, 0xbc, 0xcb, 0x82, 0xb8, 0x82, 0x86, 0x62, 0xa2, 0x83, 0x78, 0x98, + 0x6e, 0x69, 0xf6, 0xdd, 0x38, 0x89, 0x61, 0xf7, 0x76, 0xc9, 0x0b, 0x51, + 0x43, 0xd9, 0x98, 0x8a, 0xf4, 0x78, 0x28, 0xc4, 0x84, 0xae, 0x35, 0x87, + 0x55, 0x3c, 0xe2, 0xbd, 0xb5, 0xa5, 0x96, 0xc7, 0x06, 0x92, 0x85, 0x75, + 0x1b, 0xba, 0x4c, 0x7c, 0x2a, 0x7f, 0x15, 0x8e, 0x01, 0x17, 0xfe, 0xe6, + 0x8a, 0xf3, 0x60, 0xd2, 0xdc, 0x3d, 0x25, 0x47, 0x63, 0x29, 0x1d, 0x72, + 0x59, 0x47, 0x7c, 0xee, 0x14, 0x77, 0xa1, 0xfe, 0x47, 0x5a, 0x71, 0x39, + 0x03, 0xff, 0x4e, 0xc5, 0x84, 0x85, 0x65, 0x10, 0x17, 0x45, 0xa5, 0xb4, + 0x7c, 0xbb, 0x46, 0xcb, 0x5d, 0xdc, 0x27, 0x1e, 0xe4, 0xaf, 0x71, 0xa6, + 0x4a, 0xa5, 0xac, 0xb3, 0xe0, 0xab, 0xcd, 0xa5, 0xd8, 0x0b, 0x0e, 0x52, + 0xdf, 0x03, 0xa2, 0x54, 0x7e, 0x4a, 0xc1, 0x6d, 0x5a, 0xfe, 0x44, 0xf7, + 0xd8, 0xed, 0xa8, 0x08, 0x2a, 0x5d, 0xae, 0x6e, 0x32, 0x46, 0x16, 0x8e, + 0x86, 0xa9, 0xda, 0x1e, 0x65, 0xfb, 0x28, 0xf9, 0x99, 0x37, 0x95, 0x99, + 0x48, 0xe7, 0x88, 0xd4, 0xfd, 0xe2, 0x3f, 0x70, 0xb1, 0x3d, 0x9b, 0x39, + 0x47, 0x7f, 0xf4, 0xa3, 0xab, 0xfd, 0x64, 0xfb, 0x7b, 0x7b, 0xee, 0x37, + 0x4d, 0xfa, 0x26, 0x99, 0x2a, 0xeb, 0x29, 0x3b, 0xae, 0x45, 0xb3, 0x9e, + 0x42, 0xb4, 0x81, 0x21, 0x4c, 0xb5, 0x7d, 0x1d, 0xea, 0x62, 0xec, 0x13, + 0xcb, 0x7f, 0x8e, 0x50, 0xe5, 0x36, 0x80, 0x15, 0x3c, 0x2a, 0x98, 0x65, + 0x7b, 0x20, 0xd0, 0xc3, 0x84, 0x76, 0xc0, 0xc4, 0xba, 0xee, 0x59, 0x6c, + 0xb0, 0xf0, 0x92, 0x83, 0x51, 0x80, 0xb1, 0xcd, 0x3a, 0xf9, 0x63, 0x44, + 0x2c, 0x46, 0xaa, 0x03, 0xb8, 0x79, 0x15, 0x2f, 0x3c, 0x3a, 0x1d, 0xf6, + 0xe1, 0x8f, 0xda, 0xc8, 0xb2, 0x3d, 0x3e, 0xef, 0x8c, 0xa7, 0x5f, 0x09, + 0x5c, 0x08, 0x52, 0xd8, 0x10, 0xd5, 0xcd, 0xef, 0x58, 0xe1, 0xad, 0x26, + 0xc8, 0x06, 0xbc, 0xee, 0xc7, 0xe0, 0x03, 0x54, 0xd2, 0x40, 0x99, 0x19, + 0x43, 0x65, 0x4f, 0x77, 0x08, 0xa5, 0xeb, 0x01, 0x41, 0xaf, 0xab, 0x75, + 0xc7, 0xaa, 0xa0, 0xfc, 0x85, 0x27, 0x0f, 0xf9, 0x50, 0x32, 0xb1, 0x70, + 0x25, 0x89, 0xac, 0xe8, 0x71, 0xb3, 0x4e, 0x8c, 0x9c, 0x7c, 0x8c, 0x70, + 0xea, 0x1c, 0xca, 0xe6, 0x0d, 0xa4, 0x62, 0x25, 0xec, 0xc0, 0x51, 0xfe, + 0x91, 0x35, 0x63, 0x74, 0x84, 0xe7, 0xfb, 0x68, 0xb0, 0xdb, 0x4a, 0xb7, + 0xb6, 0x32, 0xaf, 0xe3, 0x54, 0x04, 0x4f, 0xd6, 0x0a, 0x3f, 0xf8, 0x4f, + 0x35, 0xff, 0x4f, 0xf5, 0xd8, 0xb5, 0x48, 0x98, 0x75, 0xa1, 0xfe, 0x00, + 0x22, 0x43, 0x85, 0x4a, 0x33, 0x0d, 0x29, 0x98, 0x54, 0xb4, 0x57, 0x1f, + 0x44, 0xbe, 0xee, 0x53, 0x5e, 0x0d, 0xdc, 0x8a, 0x76, 0xd8, 0xce, 0x52, + 0x90, 0x77, 0x19, 0xa7, 0xf3, 0xb3, 0x81, 0x85, 0xaf, 0x2e, 0x51, 0xbe, + 0x85, 0xd3, 0x42, 0xe0, 0x69, 0x13, 0xb3, 0x28, 0xef, 0x11, 0x1f, 0xfb, + 0x61, 0x11, 0x6f, 0x33, 0xf9, 0xca, 0x7a, 0x65, 0x33, 0xbd, 0x15, 0x2a, + 0xe3, 0x0b, 0x39, 0x18, 0xbe, 0x7d, 0x7d, 0xe4, 0x2f, 0xc4, 0x30, 0x0a, + 0x60, 0x63, 0x6e, 0x87, 0x52, 0x25, 0xc8, 0x17, 0xa0, 0x75, 0xa3, 0x07, + 0x4d, 0x62, 0x59, 0x24, 0x9d, 0x34, 0xd7, 0xb6, 0xcf, 0x23, 0x21, 0x5a, + 0xec, 0xf2, 0x59, 0x0c, 0xba, 0xd0, 0xcb, 0x55, 0xe8, 0xa9, 0x5c, 0x45, + 0x06, 0xb2, 0x17, 0x6f, 0xdc, 0xdc, 0x49, 0x84, 0xac, 0x94, 0xd8, 0x95, + 0xca, 0x4b, 0x5b, 0x16, 0x24, 0xdd, 0x08, 0x21, 0xb1, 0x09, 0xac, 0x82, + 0xc9, 0x49, 0x4d, 0x70, 0xfd, 0xbe, 0xf7, 0x59, 0x82, 0x0a, 0xaf, 0x1a, + 0xa2, 0x25, 0xaa, 0x5a, 0x18, 0xe5, 0x2c, 0x8a, 0x67, 0xef, 0x80, 0x86, + 0x4c, 0x6a, 0x5f, 0x21, 0x40, 0x58, 0xc4, 0x77, 0x27, 0x24, 0x4e, 0x2a, + 0x30, 0xf2, 0x75, 0x38, 0xaa, 0xf7, 0xc0, 0x42, 0x0b, 0x70, 0x22, 0xe0, + 0xaf, 0x32, 0xd9, 0xd0, 0x8c, 0x2c, 0xb5, 0xd6, 0xee, 0x8d, 0x22, 0x38, + 0x39, 0xf3, 0x72, 0x18, 0x65, 0x03, 0x10, 0xf8, 0xc7, 0xdd, 0xd4, 0x3d, + 0xb4, 0x20, 0x74, 0xb0, 0xca, 0x95, 0xb5, 0x6b, 0xf2, 0xc4, 0x8f, 0xb8, + 0x36, 0x43, 0x8c, 0x1b, 0x5e, 0x88, 0xf9, 0x9d, 0x07, 0x01, 0x10, 0x89, + 0x87, 0xf8, 0x82, 0x28, 0x36, 0x34, 0xf8, 0x39, 0x87, 0x71, 0x1f, 0xc1, + 0x8a, 0x2e, 0x33, 0x6c, 0x30, 0xc3, 0xba, 0x2a, 0x04, 0x92, 0xd5, 0xe6, + 0xa4, 0xe6, 0x34, 0xe3, 0x3c, 0x99, 0x16, 0xed, 0xb0, 0xf0, 0xb4, 0x5f, + 0xe8, 0x1a, 0x98, 0xf7, 0x0b, 0xaf, 0xae, 0x17, 0x5c, 0xc9, 0x3c, 0xb7, + 0xa0, 0x52, 0xf6, 0xd4, 0x4e, 0xdc, 0xc8, 0xe8, 0xa9, 0xef, 0x53, 0xc1, + 0x5f, 0x7d, 0x00, 0x43, 0xc8, 0x73, 0x08, 0x49, 0x2e, 0xa9, 0x87, 0xd1, + 0x91, 0x23, 0x70, 0xec, 0xd4, 0x9d, 0x20, 0xa5, 0xce, 0x85, 0xf0, 0x30, + 0x82, 0xe1, 0xf6, 0xb6, 0x02, 0x6a, 0x3a, 0x35, 0x9b, 0xd2, 0x8a, 0x48, + 0x97, 0x9c, 0x1d, 0x03, 0x72, 0xa6, 0xe6, 0xbc, 0xd9, 0x39, 0x64, 0xf4, + 0x0d, 0x9d, 0x27, 0xcb, 0x5f, 0x52, 0x19, 0x92, 0xed, 0x46, 0xaa, 0x06, + 0xf7, 0xf4, 0xc6, 0x2b, 0x4a, 0xd1, 0xd3, 0x51, 0x80, 0x2b, 0x7f, 0xf6, + 0x78, 0x23, 0xa9, 0x3d, 0x5f, 0x29, 0x2c, 0x71, 0x89, 0xd9, 0x4e, 0x18, + 0xa7, 0x16, 0x14, 0x1a, 0x3b, 0xb2, 0x25, 0xd0, 0x32, 0x88, 0xf8, 0x6f, + 0x2b, 0x4b, 0x0b, 0x11, 0xab, 0x84, 0xda, 0xc3, 0x69, 0x18, 0xca, 0x43, + 0x70, 0xfb, 0x08, 0x2f, 0x8e, 0x14, 0xfb, 0xc4, 0xe7, 0xa3, 0xa1, 0x8c, + 0xce, 0xd4, 0xcd, 0xd7, 0xee, 0xd5, 0x96, 0xf7, 0x53, 0xc6, 0x4c, 0x42, + 0xfb, 0x49, 0xce, 0x03, 0x0d, 0x79, 0x36, 0xa3, 0x88, 0xc8, 0xde, 0x54, + 0xc6, 0xd4, 0x64, 0xf7, 0xd2, 0x82, 0x12, 0x50, 0xb0, 0xff, 0xdb, 0x42, + 0x3b, 0xc2, 0x0f, 0xe3, 0xe4, 0x8d, 0x13, 0x36, 0xc5, 0x94, 0x11, 0xed, + 0xfe, 0x79, 0xb0, 0x9a, 0xb3, 0x37, 0xd8, 0x3d, 0x27, 0xf1, 0x64, 0xa4, + 0xaf, 0xde, 0x57, 0x23, 0x94, 0xb0, 0xc3, 0xd8, 0x22, 0x70, 0xf0, 0x8f, + 0x10, 0x25, 0xdb, 0x27, 0xa9, 0x37, 0x5e, 0x31, 0x8e, 0x8b, 0xa9, 0x3c, + 0x0f, 0x08, 0x37, 0x25, 0x9f, 0x83, 0x58, 0xf2, 0x56, 0x1b, 0xe0, 0xd3, + 0x23, 0x29, 0xf6, 0x7e, 0x2b, 0x04, 0x2a, 0xd3, 0x82, 0xd1, 0xf1, 0x23, + 0x86, 0xbc, 0x74, 0x99, 0x0a, 0x2b, 0x74, 0x73, 0xdf, 0x2a, 0x36, 0xac, + 0x30, 0xea, 0x3a, 0xa3, 0xea, 0xa5, 0xb7, 0x84, 0x7a, 0x13, 0x08, 0x1d, + 0x89, 0x3f, 0xae, 0x0a, 0xcf, 0x8b, 0x31, 0xb8, 0xe8, 0xa3, 0xb2, 0x70, + 0xc5, 0x75, 0x7e, 0x66, 0x4e, 0x25, 0xfb, 0x2c, 0x35, 0xff, 0x56, 0xa5, + 0x3c, 0x1b, 0x80, 0x68, 0x5e, 0xdd, 0x17, 0x61, 0xda, 0x10, 0x2b, 0x4d, + 0x3f, 0x32, 0x7e, 0x0c, 0x01, 0xb2, 0x6b, 0xaa, 0x31, 0xb1, 0x2e, 0xde, + 0x69, 0x09, 0x40, 0x00, 0x48, 0x97, 0xab, 0x75, 0x2d, 0x0d, 0x77, 0xcf, + 0xfd, 0xf4, 0x16, 0xb5, 0x47, 0xc9, 0xd6, 0x99, 0x0e, 0xca, 0xcc, 0x3c, + 0x22, 0x44, 0xa9, 0x81, 0xc4, 0xc6, 0x0b, 0x71, 0x15, 0x2c, 0x58, 0x93, + 0x78, 0x43, 0xfe, 0x59, 0xde, 0x73, 0x4c, 0x76, 0xd4, 0x2a, 0x6c, 0xf4, + 0x4e, 0x68, 0xa4, 0x47, 0x6a, 0x7e, 0x47, 0x8b, 0x28, 0x84, 0x0e, 0xe9, + 0x7f, 0xfa, 0x49, 0x2a, 0x83, 0x76, 0x17, 0x57, 0xff, 0x17, 0x50, 0xda, + 0x85, 0xa5, 0xff, 0xdc, 0x8e, 0xd3, 0x56, 0x94, 0x14, 0x0c, 0x41, 0x76, + 0x84, 0xf7, 0xfd, 0x13, 0x32, 0x81, 0x6c, 0x43, 0x36, 0x03, 0x6c, 0x5c, + 0xa1, 0x5e, 0x66, 0x75, 0x55, 0xf9, 0x4a, 0xd3, 0x20, 0x95, 0x29, 0x76, + 0x32, 0x75, 0x9e, 0x0b, 0xd7, 0x9d, 0x6a, 0x54, 0x05, 0x7e, 0xbe, 0xa4, + 0x9b, 0xa9, 0x37, 0x14, 0x79, 0x6f, 0x42, 0x92, 0x28, 0x69, 0x3d, 0x4f, + 0xbe, 0xe5, 0x17, 0x81, 0x9f, 0xde, 0xbb, 0x4e, 0xba, 0xd4, 0xd2, 0x6b, + 0xab, 0x28, 0x2e, 0xe8, 0xac, 0xec, 0x41, 0x87, 0xb7, 0x78, 0x50, 0x17, + 0x0a, 0xe3, 0xcc, 0xa8, 0x42, 0x98, 0x4d, 0x6a, 0x7b, 0xf0, 0xc4, 0x71, + 0x8d, 0x74, 0x1b, 0x30, 0xb3, 0xc7, 0x54, 0x2c, 0x8d, 0xe9, 0x9a, 0x6e, + 0x18, 0x40, 0xe3, 0x54, 0xc0, 0x56, 0xb7, 0x5c, 0x14, 0x2f, 0xbb, 0xb4, + 0x6c, 0x4d, 0x12, 0x4c, 0x8c, 0xaf, 0x5d, 0xcc, 0x31, 0xc2, 0xc2, 0xb7, + 0x25, 0xa9, 0x04, 0x41, 0x12, 0xbc, 0xa8, 0x5b, 0x3e, 0xed, 0xe1, 0x48, + 0x09, 0x6c, 0x21, 0x09, 0x9c, 0x80, 0x61, 0xc9, 0xe7, 0x53, 0x9b, 0xa0, + 0x1c, 0x45, 0xc9, 0x44, 0x4c, 0x2f, 0xaa, 0x73, 0x9f, 0x25, 0x63, 0x1d, + 0x05, 0x47, 0x33, 0xa4, 0x97, 0x47, 0xb8, 0x7c, 0x7b, 0x64, 0xc5, 0xa5, + 0xcc, 0x64, 0x2c, 0xb2, 0xaa, 0xee, 0x10, 0x2e, 0x3c, 0xe0, 0xb2, 0x94, + 0xb0, 0x84, 0x10, 0xb8, 0xf8, 0xe3, 0xb4, 0x88, 0x49, 0x6c, 0x1d, 0x3f, + 0x32, 0xd3, 0x8d, 0xcf, 0xa2, 0x34, 0x79, 0x7b, 0xb9, 0x48, 0x89, 0x2f, + 0x04, 0x0c, 0x72, 0x3f, 0xf9, 0x1b, 0xd8, 0xe6, 0x00, 0xae, 0x66, 0xb8, + 0x76, 0xad, 0x25, 0xd1, 0xc1, 0xb1, 0x2f, 0xc5, 0xf2, 0x8f, 0xb9, 0xe6, + 0x34, 0x33, 0x98, 0xdf, 0xf3, 0x0e, 0xa5, 0x6f, 0x3e, 0x00, 0x73, 0xf2, + 0x8a, 0x53, 0xc0, 0x1b, 0x5b, 0xd3, 0x6c, 0x42, 0xd2, 0x2a, 0xb8, 0x95, + 0xb0, 0x03, 0x50, 0x87, 0xc6, 0x02, 0x43, 0xe4, 0x5e, 0xdb, 0x9b, 0x9d, + 0xb7, 0x1a, 0x7c, 0x71, 0x34, 0xfa, 0x8e, 0xe3, 0xae, 0x43, 0x11, 0x03, + 0x31, 0xba, 0xe5, 0x21, 0xdc, 0xba, 0x96, 0x34, 0x87, 0x0c, 0xc8, 0x6e, + 0x3e, 0xbc, 0x8e, 0x4b, 0x7f, 0xcb, 0x37, 0xe5, 0x8b, 0xac, 0x83, 0xb2, + 0x12, 0xca, 0x73, 0x62, 0xd8, 0x89, 0xab, 0x70, 0x5c, 0x31, 0x59, 0xcc, + 0x49, 0x98, 0x4a, 0xd3, 0xec, 0xcf, 0x63, 0xa1, 0x2f, 0x6b, 0x68, 0xcf, + 0x9f, 0xee, 0x63, 0xe9, 0x54, 0xa8, 0xcf, 0x2e, 0x5a, 0x29, 0x1a, 0x85, + 0xf9, 0x50, 0xac, 0x61, 0x19, 0xd7, 0xfd, 0x8d, 0x6d, 0xf1, 0xa2, 0x28, + 0x33, 0x3e, 0x80, 0xbc, 0xab, 0xfe, 0x0d, 0x84, 0x05, 0xab, 0xf7, 0x98, + 0x86, 0x99, 0x7a, 0x8a, 0x6a, 0x93, 0x72, 0x21, 0x7a, 0x4b, 0x11, 0x19, + 0xe4, 0x66, 0x7e, 0xb8, 0x7d, 0x6d, 0xa1, 0xce, 0x4a, 0x94, 0xb1, 0xb1, + 0xb2, 0x09, 0x28, 0x89, 0x1e, 0x7c, 0xf3, 0xa0, 0x50, 0xe4, 0x2c, 0x1d, + 0x38, 0x83, 0xac, 0x6c, 0x79, 0x20, 0xaf, 0xa2, 0xbc, 0xb0, 0x2e, 0x4d, + 0x5c, 0xfc, 0x74, 0x4e, 0xa1, 0x5e, 0x0f, 0x92, 0x40, 0x9e, 0x2a, 0x53, + 0x8b, 0x97, 0x0d, 0xe5, 0x54, 0x68, 0x39, 0xd8, 0x37, 0xb4, 0x76, 0x1d, + 0x5e, 0xc6, 0x77, 0x2d, 0x3c, 0x6a, 0xb5, 0xe7, 0x2b, 0x21, 0x7c, 0xd4, + 0x94, 0xed, 0xeb, 0xc8, 0x24, 0xd2, 0x57, 0xe5, 0x5b, 0x0b, 0x2d, 0xb2, + 0x52, 0x5f, 0x9c, 0x93, 0xe0, 0x38, 0x2a, 0x0f, 0xa1, 0xbe, 0xeb, 0xd7, + 0x71, 0x2f, 0xc3, 0x27, 0x1c, 0x58, 0x93, 0xcb, 0xf5, 0x5b, 0xaa, 0x38, + 0x78, 0x54, 0x7a, 0xbe, 0xf7, 0x7c, 0xa7, 0x4d, 0x6b, 0x11, 0x54, 0x85, + 0x6f, 0x88, 0x1e, 0x2f, 0x43, 0x90, 0x4b, 0x75, 0x56, 0xd2, 0xf7, 0xca, + 0x6d, 0x7e, 0x77, 0x4d, 0x87, 0x29, 0xcc, 0xe3, 0x30, 0x64, 0x2d, 0xd1, + 0x5b, 0x24, 0x4b, 0x5d, 0xe9, 0x14, 0x5b, 0xdb, 0x5a, 0xd1, 0xc7, 0x41, + 0xf8, 0x56, 0x9a, 0x5d, 0x50, 0x9b, 0xac, 0xe0, 0xb1, 0xec, 0x5d, 0x95, + 0x85, 0x28, 0x92, 0x5e, 0x0e, 0x43, 0x3b, 0x38, 0x83, 0x33, 0x45, 0xf9, + 0x72, 0x72, 0xa9, 0x27, 0xa7, 0xdd, 0x32, 0xec, 0x65, 0xef, 0xf6, 0xfc, + 0x21, 0x66, 0x3f, 0xe6, 0xee, 0xeb, 0x0a, 0xde, 0xe4, 0x37, 0x47, 0xb0, + 0x29, 0x56, 0xb4, 0x6e, 0x5c, 0x13, 0xdb, 0x3f, 0xe5, 0x28, 0x41, 0x85, + 0xfc, 0x7b, 0x87, 0x83, 0x75, 0xf4, 0x5b, 0xd9, 0xb6, 0x9a, 0x03, 0x8d, + 0x8a, 0x84, 0xe7, 0x2b, 0x06, 0xa0, 0x17, 0x4a, 0x75, 0x0b, 0x28, 0xa6, + 0xfa, 0x3e, 0xed, 0x51, 0xcc, 0x44, 0xb2, 0xa3, 0x45, 0x23, 0x59, 0xee, + 0x14, 0xb2, 0x01, 0x5a, 0x3f, 0x98, 0x70, 0x6e, 0x5e, 0xf9, 0x32, 0xed, + 0x15, 0x05, 0x6d, 0xc6, 0xa1, 0xa2, 0x59, 0xfe, 0x3e, 0xec, 0xac, 0xa8, + 0xd5, 0xf4, 0xca, 0x2b, 0xb8, 0xfa, 0xcb, 0xea, 0x34, 0x3b, 0x69, 0x0d, + 0x07, 0xcb, 0x7b, 0x9c, 0x5d, 0x00, 0x00, 0x17, 0x14, 0x05, 0x9b, 0x88, + 0xcc, 0x54, 0x2f, 0xae, 0x0e, 0x1a, 0xc9, 0xc3, 0x65, 0x72, 0xa1, 0x21, + 0x8a, 0x86, 0xde, 0xe6, 0x26, 0xeb, 0x85, 0xef, 0xa5, 0xce, 0x8c, 0xfd, + 0xbc, 0x56, 0x8d, 0xb2, 0x39, 0x46, 0x54, 0x92, 0xb4, 0x06, 0x0d, 0x72, + 0x4b, 0xf9, 0xa8, 0x1a, 0xfb, 0x8a, 0xdb, 0x93, 0xf9, 0x77, 0xd7, 0xb0, + 0x83, 0xb8, 0x6b, 0x6b, 0xee, 0x8a, 0xc2, 0xe6, 0x56, 0x7b, 0xf5, 0x08, + 0x3d, 0x8c, 0xad, 0xd3, 0xbc, 0x8a, 0xee, 0x67, 0xd9, 0xbd, 0x6e, 0x6e, + 0xd9, 0xe1, 0xec, 0xf3, 0xef, 0x2e, 0xe7, 0xe4, 0x4b, 0x19, 0x2a, 0xfe, + 0x39, 0xe3, 0xdd, 0x80, 0xfa, 0xf5, 0xc4, 0x5b, 0x5c, 0xd5, 0x70, 0xc1, + 0xc2, 0xa7, 0x7e, 0x5f, 0x07, 0xd4, 0x25, 0xe2, 0xb9, 0x52, 0x0e, 0x95, + 0x0d, 0x7a, 0x8b, 0xae, 0x77, 0x29, 0x8c, 0xa6, 0xa4, 0x3a, 0x59, 0x71, + 0x6f, 0xd8, 0x80, 0xbe, 0xf0, 0xf6, 0x40, 0x7a, 0x43, 0x56, 0xff, 0x4b, + 0x80, 0xd2, 0x64, 0xe3, 0x65, 0xe6, 0x1b, 0x04, 0xa9, 0xdf, 0x16, 0x2c, + 0xfe, 0x0f, 0x0d, 0xd3, 0xe6, 0x96, 0xbb, 0x0c, 0x66, 0xca, 0x09, 0x5d, + 0xf1, 0x42, 0x98, 0xa2, 0xd8, 0x9c, 0x98, 0x55, 0x36, 0xad, 0xf6, 0x80, + 0x90, 0x2c, 0xdd, 0x62, 0x87, 0x89, 0x7c, 0x62, 0x86, 0x1f, 0x4b, 0x3a, + 0xb3, 0x3e, 0xc4, 0xfc, 0x4b, 0x09, 0x14, 0x7c, 0x25, 0x94, 0x26, 0x9d, + 0x9d, 0xe6, 0x63, 0xeb, 0x66, 0xb3, 0xf5, 0x09, 0x97, 0x0f, 0xa0, 0x09, + 0x85, 0xec, 0x83, 0x3b, 0x15, 0x56, 0xca, 0x27, 0xeb, 0x7e, 0xb2, 0x0f, + 0x21, 0xc4, 0xa5, 0xed, 0x4f, 0x72, 0x53, 0x81, 0x8d, 0x0a, 0x81, 0x18, + 0x65, 0xac, 0xaf, 0x49, 0x32, 0x54, 0x77, 0xbd, 0x12, 0x22, 0x85, 0x5f, + 0x66, 0x78, 0x13, 0x7b, 0x7e, 0xaa, 0x0e, 0xf4, 0x29, 0x83, 0xf0, 0x4c, + 0x98, 0x5a, 0x69, 0xd3, 0xd0, 0x8f, 0xc7, 0x87, 0x4e, 0x4b, 0x65, 0xfc, + 0xa2, 0x88, 0x78, 0x98, 0x1d, 0x83, 0x4d, 0xe0, 0x8b, 0x77, 0xbf, 0x6b, + 0x3f, 0x85, 0x93, 0xbd, 0x33, 0x1a, 0x5f, 0x57, 0x37, 0xd2, 0x27, 0x99, + 0xf6, 0xcc, 0x53, 0xd4, 0x07, 0x71, 0x06, 0xb4, 0xa1, 0x07, 0xab, 0xaf, + 0x4b, 0xa0, 0xbd, 0x87, 0xa8, 0xb5, 0xfc, 0xbc, 0x00, 0x58, 0x66, 0xd8, + 0xf0, 0xd1, 0x78, 0xbb, 0x5b, 0x29, 0xc1, 0x2e, 0x67, 0x80, 0x52, 0x70, + 0x78, 0xfa, 0xe7, 0x1b, 0x82, 0x86, 0x6c, 0x20, 0x5a, 0x29, 0x9c, 0xc7, + 0x7f, 0x0b, 0x83, 0xa6, 0xcc, 0x95, 0x22, 0xd3, 0xde, 0xee, 0xbe, 0x9c, + 0x70, 0xaf, 0xa9, 0xa0, 0xb8, 0x63, 0xad, 0x22, 0x0e, 0x97, 0x17, 0x51, + 0xb8, 0xb5, 0x21, 0xe9, 0x35, 0x0b, 0xae, 0xe1, 0xe1, 0xc7, 0xd6, 0xf6, + 0xe3, 0x7d, 0xac, 0xc8, 0xf3, 0x07, 0x7e, 0x19, 0x5c, 0xd4, 0xef, 0x8d, + 0x9f, 0x1d, 0x17, 0xb6, 0x03, 0xda, 0x20, 0xc4, 0x65, 0x46, 0x91, 0xdf, + 0xde, 0xf3, 0x77, 0xd0, 0x60, 0x79, 0x4c, 0x58, 0x98, 0x63, 0x98, 0x92, + 0xdf, 0x07, 0x4a, 0x66, 0x69, 0xc9, 0xcf, 0xbc, 0x74, 0x20, 0x50, 0x0c, + 0xb7, 0x16, 0x1c, 0x95, 0x31, 0x99, 0xbb, 0x44, 0x0b, 0x81, 0x8e, 0xd6, + 0x14, 0x58, 0xb9, 0x86, 0xf2, 0x46, 0x0b, 0x10, 0x19, 0xfe, 0x70, 0x98, + 0xe9, 0x65, 0xe2, 0x93, 0x43, 0xc1, 0x54, 0xbf, 0x60, 0xf3, 0x3d, 0xbf, + 0x3e, 0xa9, 0x61, 0x25, 0x91, 0xd9, 0x67, 0x33, 0x17, 0xaa, 0x27, 0x22, + 0x2f, 0xcc, 0x28, 0x37, 0x3b, 0xa1, 0xc0, 0x09, 0x7a, 0x72, 0xae, 0x4b, + 0x72, 0xed, 0x55, 0x9c, 0x92, 0xdc, 0x22, 0xe6, 0x29, 0x66, 0x65, 0xb8, + 0x9b, 0xf3, 0x39, 0x97, 0x79, 0x19, 0xc4, 0xba, 0xaa, 0xe2, 0xf8, 0x97, + 0x3f, 0x48, 0xb4, 0x9d, 0x38, 0x0b, 0xbb, 0x50, 0x79, 0x26, 0x8f, 0x7b, + 0xa5, 0xef, 0x19, 0xd3, 0xbf, 0xd3, 0x42, 0xd2, 0xca, 0x70, 0xab, 0x5c, + 0x89, 0x68, 0x34, 0x9f, 0x0e, 0x2a, 0x8f, 0xb3, 0xa4, 0x3a, 0x4c, 0x10, + 0x9b, 0xfb, 0xf7, 0x75, 0xd6, 0x22, 0xab, 0x97, 0x8b, 0x0f, 0x2e, 0x7c, + 0xa0, 0x3e, 0x71, 0x95, 0xfc, 0x03, 0x6e, 0xd9, 0xa4, 0x49, 0x7f, 0x12, + 0xf3, 0x5a, 0xcc, 0x15, 0x37, 0xa8, 0x57, 0x0e, 0x23, 0x0e, 0xcc, 0x8f, + 0xe1, 0x28, 0xca, 0x28, 0x19, 0xc2, 0x50, 0xe7, 0xb6, 0xe1, 0x9a, 0xda, + 0xa0, 0x4a, 0x20, 0xb3, 0xa4, 0xa3, 0x60, 0x84, 0x1d, 0xb2, 0xcb, 0xec, + 0x63, 0x41, 0xf3, 0x20, 0x31, 0x98, 0x99, 0x90, 0x18, 0xa9, 0x99, 0xd8, + 0xc1, 0x58, 0x39, 0xa2, 0x80, 0x10, 0x44, 0xc5, 0x50, 0x9d, 0x1d, 0x2d, + 0x0c, 0x06, 0xd7, 0x20, 0x01, 0xce, 0xf7, 0x02, 0xa5, 0xc8, 0x66, 0x38, + 0xad, 0xc3, 0x9a, 0x7d, 0x42, 0xfc, 0xd2, 0x6f, 0x3f, 0x0d, 0xef, 0xc4, + 0xf5, 0x27, 0xd8, 0x57, 0x76, 0xbc, 0x2d, 0x1d, 0xb4, 0xff, 0x92, 0xac, + 0x00, 0x06, 0x9b, 0xc3, 0x12, 0xb5, 0x62, 0x50, 0x99, 0x72, 0x09, 0xc9, + 0x25, 0xdd, 0x6b, 0x1d, 0x83, 0x23, 0x1b, 0xa7, 0xe3, 0xe6, 0xfa, 0x4e, + 0x22, 0xca, 0x63, 0x72, 0x1b, 0x14, 0xdb, 0x4d, 0x54, 0xc5, 0x70, 0x0c, + 0x32, 0xf3, 0x8f, 0xb0, 0xe8, 0x6f, 0x0f, 0xc7, 0x1e, 0xfa, 0x32, 0x09, + 0x98, 0xc0, 0x02, 0x70, 0xab, 0xd7, 0xc8, 0xb0, 0x3b, 0xc5, 0xb1, 0x20, + 0x59, 0x9d, 0x95, 0x2d, 0x6c, 0x9b, 0x2d, 0xf2, 0x2a, 0x3f, 0x92, 0x7b, + 0xac, 0x41, 0x65, 0xec, 0xe0, 0x7d, 0x6a, 0x80, 0xd2, 0xee, 0x15, 0x12, + 0x26, 0x34, 0xfc, 0x77, 0x95, 0x84, 0xc6, 0xff, 0xcc, 0xbd, 0x52, 0x1f, + 0xbb, 0xb2, 0x9e, 0x56, 0xac, 0xd8, 0x39, 0x4f, 0x09, 0xbf, 0x8b, 0x6f, + 0x0a, 0x10, 0xc7, 0x74, 0x60, 0x48, 0xbd, 0x2f, 0xb6, 0x50, 0x17, 0xac, + 0x84, 0x39, 0xdf, 0x31, 0xb8, 0x7d, 0x6c, 0xaf, 0x5c, 0x7d, 0x6e, 0x4d, + 0x31, 0x56, 0x93, 0xf0, 0x67, 0x17, 0x2c, 0x07, 0x39, 0x1b, 0x7c, 0x48, + 0xc8, 0x49, 0x4e, 0x15, 0x2d, 0x0f, 0xe0, 0xdf, 0xc7, 0x94, 0xb6, 0x08, + 0x4c, 0x26, 0xc3, 0xd8, 0xc9, 0xe2, 0x62, 0x51, 0x86, 0xf8, 0xe0, 0xcd, + 0x10, 0x8e, 0x26, 0xec, 0x32, 0x44, 0x3b, 0xc6, 0x0c, 0x8c, 0x48, 0xa6, + 0xd5, 0x72, 0x53, 0x9c, 0x89, 0x84, 0x85, 0xd3, 0x19, 0xbe, 0xdc, 0x15, + 0x0a, 0x3b, 0x45, 0xe6, 0x1b, 0x9a, 0x6c, 0xdb, 0xbf, 0x33, 0x94, 0xf5, + 0xcb, 0x60, 0xdf, 0x68, 0xb8, 0x50, 0x06, 0xdc, 0xfc, 0x6d, 0x6e, 0xc3, + 0x88, 0x43, 0xe8, 0x90, 0x51, 0x3a, 0x79, 0x7c, 0xe9, 0xc3, 0x00, 0xd2, + 0x04, 0xb9, 0xbc, 0xcb, 0xe1, 0xbc, 0x1e, 0x8f, 0x4c, 0xef, 0x2c, 0x12, + 0xff, 0xbc, 0x3c, 0xad, 0x47, 0xe5, 0x75, 0x1c, 0x55, 0x24, 0xbc, 0x0e, + 0x05, 0x30, 0x01, 0xd5, 0xb6, 0xc7, 0x30, 0x8a, 0xfe, 0x85, 0x72, 0xd3, + 0x07, 0x77, 0xd7, 0x52, 0x33, 0x4c, 0x6b, 0x20, 0x23, 0x26, 0xfb, 0x23, + 0xc8, 0x39, 0x4b, 0x47, 0x5e, 0xe9, 0xfb, 0xbc, 0x6c, 0x69, 0xe4, 0xfd, + 0x30, 0x02, 0x05, 0x97, 0xfd, 0xaa, 0x98, 0x51, 0x7f, 0x93, 0x9e, 0x81, + 0x8a, 0xdc, 0x7c, 0x73, 0xe3, 0xe1, 0x25, 0xa2, 0x72, 0x2b, 0x3e, 0x99, + 0x1d, 0xe6, 0x96, 0x95, 0x66, 0xb4, 0xbc, 0x4c, 0x6c, 0x18, 0x01, 0x57, + 0x6f, 0x93, 0x07, 0x41, 0xa8, 0x6f, 0xc8, 0xff, 0xcc, 0x77, 0xeb, 0x5b, + 0x9c, 0xf3, 0xdb, 0x27, 0x36, 0x45, 0xa5, 0xc5, 0x3e, 0x87, 0x74, 0x81, + 0x2f, 0xfb, 0x36, 0x3f, 0xea, 0xcb, 0x33, 0xa1, 0xe9, 0xb4, 0xcd, 0xea, + 0x46, 0x3e, 0x12, 0x70, 0x0d, 0x1f, 0x42, 0xde, 0xcb, 0xf4, 0x05, 0x91, + 0x21, 0xaf, 0x54, 0x68, 0x2a, 0xd2, 0x08, 0x9b, 0x5a, 0xe5, 0x08, 0x97, + 0x40, 0x42, 0xce, 0x52, 0x82, 0x04, 0x34, 0xf6, 0x2e, 0xbb, 0x98, 0x56, + 0x17, 0x39, 0x53, 0x7a, 0x4c, 0xdf, 0xa5, 0x0c, 0x4d, 0x4a, 0xa1, 0xdc, + 0x62, 0x61, 0xc5, 0xca, 0xb5, 0x3a, 0x13, 0xe0, 0x51, 0x5e, 0x2d, 0xfc, + 0x9c, 0x0b, 0xf8, 0x09, 0x95, 0x00, 0x7e, 0x8f, 0x40, 0xa1, 0x88, 0xb8, + 0x50, 0xc0, 0xa6, 0x4f, 0xa5, 0xd4, 0x4e, 0x18, 0x15, 0xa9, 0x3c, 0x11, + 0x8f, 0x2a, 0x1b, 0x9b, 0xeb, 0x8c, 0xf7, 0xf4, 0xe5, 0xa5, 0xf4, 0xc6, + 0x0f, 0x15, 0xd2, 0x73, 0x20, 0x00, 0x03, 0xa9, 0xfa, 0x82, 0xd1, 0xdc, + 0xd9, 0x6c, 0x76, 0x89, 0x57, 0xe8, 0xf7, 0xf8, 0xee, 0x9c, 0x56, 0xe7, + 0x15, 0xac, 0xfc, 0x28, 0xb3, 0x73, 0x9f, 0x7a, 0x63, 0x50, 0xce, 0x17, + 0x9c, 0xaf, 0xa8, 0xd8, 0x7f, 0xee, 0xc3, 0x4e, 0xd7, 0x9b, 0x9e, 0xc4, + 0x76, 0x8f, 0x8e, 0x67, 0x63, 0x13, 0xdb, 0xea, 0x9f, 0x33, 0xc1, 0x05, + 0xb2, 0x85, 0x9e, 0x16, 0x06, 0x58, 0x28, 0xd4, 0xf7, 0xcd, 0xc6, 0x77, + 0xc9, 0x83, 0x94, 0x59, 0x11, 0xb2, 0x86, 0xc3, 0x16, 0x74, 0x81, 0xb6, + 0x8e, 0x92, 0x4e, 0x33, 0x80, 0x54, 0x3d, 0x3d, 0x2c, 0xb0, 0x70, 0xad, + 0x08, 0x5c, 0x35, 0x49, 0x00, 0x63, 0x00, 0xed, 0xd3, 0x9c, 0xc9, 0x86, + 0xf6, 0x87, 0x81, 0x36, 0xb7, 0x9b, 0xeb, 0xdc, 0xd4, 0x5a, 0x5a, 0x1e, + 0xdf, 0x6a, 0xa0, 0x94, 0x06, 0x4b, 0x23, 0xcf, 0xdd, 0x8f, 0x1a, 0x47, + 0x0e, 0x53, 0x90, 0x05, 0xe3, 0x24, 0x7e, 0x9e, 0xee, 0x69, 0xcf, 0xb3, + 0xbe, 0x23, 0xdd, 0x05, 0xbc, 0x07, 0x13, 0x64, 0xc5, 0x2b, 0x7a, 0xbb, + 0x9b, 0x93, 0x4f, 0x28, 0xb7, 0xf2, 0x8c, 0xe0, 0x39, 0x06, 0x79, 0x73, + 0x5d, 0x7e, 0xb5, 0xdb, 0x79, 0xfc, 0xf3, 0xb1, 0xcd, 0x4e, 0xa3, 0xd7, + 0xf7, 0x48, 0x95, 0x97, 0x43, 0x2c, 0xaf, 0x30, 0xca, 0x4f, 0x33, 0x0d, + 0x66, 0x9e, 0x12, 0xa6, 0xc3, 0xbe, 0xda, 0x2f, 0x42, 0xf2, 0xa8, 0xa4, + 0xaf, 0x64, 0x93, 0x3b, 0x93, 0xd8, 0x0e, 0x2a, 0x25, 0x34, 0x44, 0xf8, + 0xef, 0xb0, 0x34, 0x4e, 0xcf, 0x0d, 0xc9, 0xcd, 0x82, 0x9e, 0x07, 0xca, + 0xa4, 0x17, 0x4b, 0x7e, 0x1f, 0x80, 0xbf, 0x04, 0xee, 0x54, 0x65, 0xec, + 0x01, 0xe4, 0xb1, 0x5a, 0x57, 0xf9, 0x34, 0xf3, 0x2f, 0x04, 0x3f, 0xdd, + 0xce, 0x45, 0x16, 0x30, 0x82, 0xc7, 0x0f, 0x83, 0xf3, 0xb4, 0x45, 0x08, + 0xb9, 0xa6, 0xeb, 0x67, 0x5d, 0xaa, 0x83, 0xf8, 0xc7, 0x6a, 0x3e, 0x8f, + 0xc5, 0x2e, 0xef, 0x0a, 0x27, 0xda, 0xb4, 0x81, 0x4e, 0x29, 0xbe, 0x96, + 0xa8, 0xed, 0xc2, 0x7f, 0xc9, 0xf6, 0x45, 0x4d, 0x89, 0x4a, 0xee, 0x32, + 0x12, 0x1c, 0xf3, 0x72, 0x7f, 0x2e, 0x32, 0x14, 0x40, 0x6b, 0x2d, 0x53, + 0xa0, 0xec, 0x2e, 0xed, 0xbc, 0xab, 0xe4, 0x8a, 0x94, 0xdf, 0x52, 0xfa, + 0x1c, 0x02, 0xe1, 0xce, 0x06, 0xd4, 0x6f, 0x12, 0xc2, 0xf9, 0x87, 0xcc, + 0x47, 0x40, 0xff, 0x59, 0x9b, 0x46, 0x7f, 0x42, 0xcd, 0x6c, 0x3d, 0x2a, + 0x5c, 0x91, 0x2d, 0x7b, 0xba, 0x24, 0xac, 0x90, 0x75, 0x2c, 0xe0, 0xdc, + 0x1c, 0x23, 0x29, 0xd0, 0x5f, 0xfd, 0x76, 0x4b, 0x74, 0x29, 0x82, 0xa8, + 0xc9, 0xb0, 0x78, 0x26, 0x13, 0xc2, 0x14, 0x6f, 0xd2, 0x95, 0x29, 0xa4, + 0xc8, 0x43, 0x07, 0x5c, 0xd6, 0xcb, 0x47, 0x17, 0x9f, 0x73, 0xbd, 0x58, + 0xb0, 0xd3, 0x23, 0x0e, 0x37, 0x5f, 0x97, 0x09, 0xb3, 0xb3, 0xd0, 0x5c, + 0xe7, 0xcb, 0x02, 0xf7, 0x7d, 0xe5, 0x85, 0xbf, 0x51, 0x6f, 0xd5, 0x42, + 0xa3, 0x25, 0x78, 0x9c, 0x29, 0x29, 0x49, 0xcc, 0x56, 0x5b, 0xd4, 0x2b, + 0x04, 0x7e, 0xcd, 0x95, 0x6e, 0x70, 0xf3, 0x82, 0xab, 0x30, 0x4e, 0xa0, + 0x36, 0xce, 0xb4, 0x11, 0x29, 0x17, 0x83, 0x8d, 0xe3, 0xf4, 0x2a, 0x78, + 0x44, 0xa3, 0x60, 0x41, 0xa1, 0x30, 0xa9, 0x03, 0xe9, 0x07, 0xa3, 0xf4, + 0xea, 0xd6, 0xbb, 0x8a, 0xfa, 0x6b, 0xda, 0xdf, 0x8d, 0x83, 0x41, 0x11, + 0xdb, 0x75, 0xad, 0x62, 0xeb, 0x6f, 0x4c, 0xdf, 0xcf, 0x8e, 0xb5, 0x85, + 0x48, 0x8f, 0x3b, 0x92, 0x9a, 0x7c, 0xe6, 0xd8, 0x13, 0x21, 0x8c, 0xa2, + 0x31, 0xea, 0xe1, 0x86, 0xb7, 0x62, 0xe4, 0x71, 0x93, 0xd1, 0x0d, 0x98, + 0xdf, 0x6f, 0x49, 0xb0, 0x98, 0xb2, 0x51, 0xd1, 0xc1, 0xd6, 0x4b, 0xa3, + 0x75, 0xa4, 0x7c, 0x70, 0x77, 0xd7, 0x80, 0x4e, 0x18, 0x45, 0x07, 0x5e, + 0x41, 0xb0, 0x44, 0x9d, 0xbd, 0x67, 0x21, 0xe7, 0x04, 0xf4, 0x1d, 0x81, + 0xf8, 0x79, 0x37, 0xdc, 0xdb, 0x30, 0xb1, 0x94, 0xe1, 0xab, 0xf0, 0x0d, + 0x24, 0xa1, 0x46, 0x15, 0xd7, 0xf8, 0xaa, 0xee, 0x3f, 0xa0, 0xce, 0xc8, + 0x39, 0x38, 0xc2, 0x15, 0xad, 0x6f, 0x64, 0x92, 0xa8, 0x15, 0x16, 0x36, + 0x71, 0x2d, 0x02, 0xf6, 0x66, 0xff, 0x9f, 0xb1, 0xe3, 0x7e, 0xf1, 0xfb, + 0x15, 0x2c, 0x59, 0x1c, 0xbf, 0x81, 0x48, 0x24, 0xd4, 0x11, 0xfc, 0xbd, + 0x85, 0xf4, 0x89, 0x83, 0xa9, 0xd7, 0x9e, 0x8c, 0x91, 0x27, 0x91, 0xdb, + 0xe9, 0x97, 0x27, 0x55, 0x5a, 0x49, 0xee, 0xba, 0xba, 0x2e, 0x24, 0x70, + 0x16, 0x0c, 0x83, 0xae, 0x84, 0x14, 0x13, 0x09, 0x91, 0x00, 0xec, 0xbb, + 0xc8, 0x93, 0xb6, 0x02, 0x2a, 0xaa, 0x52, 0x07, 0x6b, 0xd5, 0x89, 0xb5, + 0x93, 0xc8, 0x3c, 0xd5, 0xa6, 0xc5, 0x8e, 0xf9, 0x7d, 0x43, 0xd9, 0x43, + 0xab, 0x93, 0xca, 0x0e, 0x61, 0x41, 0x2e, 0xcc, 0xe5, 0xdb, 0x85, 0x02, + 0xa8, 0x9c, 0x5c, 0xef, 0x2d, 0x38, 0x43, 0x16, 0xe4, 0xae, 0x70, 0x35, + 0x18, 0xcf, 0xe7, 0x33, 0x4a, 0xeb, 0x72, 0xf4, 0x9d, 0xb2, 0xc4, 0x49, + 0xe1, 0xd9, 0x0f, 0x68, 0x94, 0xa4, 0x5e, 0x8b, 0x31, 0x4b, 0xd8, 0xf3, + 0xc7, 0x02, 0xc9, 0x8d, 0x40, 0xb6, 0x9d, 0xc9, 0x6f, 0x20, 0xd2, 0x89, + 0xec, 0x30, 0x27, 0xb2, 0x11, 0x48, 0x35, 0x3d, 0x54, 0x0d, 0xae, 0xb3, + 0xf5, 0xf7, 0x73, 0xac, 0xd4, 0xe9, 0xa1, 0xa2, 0x6f, 0x37, 0xfe, 0x83, + 0xaf, 0xae, 0x93, 0xd5, 0x67, 0xff, 0xe8, 0x47, 0x64, 0x3f, 0x43, 0x75, + 0x54, 0x44, 0x64, 0x31, 0x14, 0x7b, 0xd1, 0x34, 0x5c, 0xb8, 0x8f, 0x12, + 0xb2, 0xb5, 0x29, 0x6b, 0x32, 0xa8, 0xa2, 0x14, 0xb7, 0x05, 0x27, 0x0f, + 0x22, 0x05, 0x15, 0xb2, 0x72, 0xe0, 0xd5, 0x31, 0xd3, 0x15, 0x23, 0x7f, + 0xf5, 0x44, 0x8a, 0x05, 0x7f, 0xc9, 0x25, 0x54, 0x60, 0x00, 0x3c, 0xb7, + 0x0d, 0x84, 0x73, 0x26, 0x02, 0x51, 0xac, 0xae, 0x64, 0xec, 0xc2, 0x6b, + 0xe1, 0x52, 0xfe, 0x77, 0x96, 0xb2, 0x76, 0x67, 0xe5, 0x4b, 0x75, 0xb9, + 0xd3, 0x7b, 0xaa, 0x75, 0x5a, 0xd5, 0xe7, 0x24, 0x60, 0xcf, 0xc5, 0x02, + 0x48, 0xf0, 0xc3, 0x10, 0xeb, 0x7e, 0x98, 0x43, 0xaa, 0x9a, 0x4b, 0xdd, + 0xe4, 0x8a, 0x8e, 0xef, 0x46, 0x2e, 0xf8, 0xcb, 0x32, 0xac, 0xd3, 0x07, + 0x60, 0xda, 0x88, 0xb3, 0xd4, 0xce, 0xe1, 0x2c, 0xf8, 0xc3, 0xd0, 0x93, + 0x46, 0xa7, 0xd4, 0x55, 0x0e, 0xf1, 0xb9, 0x3f, 0x13, 0x3d, 0xea, 0xc7, + 0xfe, 0x82, 0x93, 0x4d, 0x87, 0x6f, 0xec, 0x0a, 0x8d, 0xc0, 0x22, 0x3f, + 0xe8, 0x4c, 0xd3, 0xdc, 0x28, 0x3f, 0xd5, 0x5e, 0xba, 0x57, 0xd8, 0x59, + 0x62, 0x66, 0xa6, 0xd8, 0xc5, 0xb6, 0xd5, 0x37, 0x1a, 0x74, 0x91, 0xe7, + 0x53, 0x75, 0x5c, 0x3f, 0xbf, 0x9c, 0x4f, 0x89, 0xf3, 0xde, 0x07, 0x95, + 0x4e, 0xfb, 0x33, 0xcb, 0xc3, 0x2d, 0x81, 0x32, 0xfa, 0xc7, 0xe3, 0x56, + 0x4b, 0x26, 0x8f, 0xed, 0x5d, 0x41, 0x3e, 0x60, 0x71, 0xe9, 0x7a, 0x5c, + 0xe7, 0x2a, 0xf5, 0x25, 0x7a, 0x0f, 0x49, 0x87, 0x48, 0x4a, 0x9a, 0xb5, + 0x3e, 0x03, 0x00, 0xc1, 0x66, 0xd8, 0x8c, 0x3a, 0x57, 0xcb, 0x80, 0xb5, + 0x6d, 0x74, 0x85, 0x23, 0x91, 0xff, 0x99, 0x21, 0xc4, 0xa8, 0xc6, 0x52, + 0x1c, 0xc5, 0x29, 0xae, 0xab, 0xc3, 0x05, 0xe5, 0xcd, 0x62, 0xcd, 0x5b, + 0x62, 0xc0, 0xc0, 0xdd, 0x53, 0x8e, 0x89, 0x23, 0x42, 0xa6, 0x3c, 0x87, + 0xd0, 0x63, 0x91, 0x27, 0xb9, 0x7d, 0x8d, 0x93, 0x44, 0xfa, 0x26, 0x4a, + 0xd9, 0x7f, 0x26, 0x1c, 0x3b, 0xa9, 0x69, 0x7f, 0x2a, 0x04, 0x8d, 0x1e, + 0x27, 0x42, 0x29, 0xb8, 0x69, 0xcd, 0x49, 0xa0, 0x6c, 0xcc, 0xd5, 0x18, + 0x05, 0xd9, 0x14, 0x29, 0x26, 0x29, 0x21, 0xa0, 0x31, 0xb8, 0xbd, 0xad, + 0xae, 0x1d, 0x20, 0x63, 0x81, 0x0f, 0x74, 0xc0, 0xe4, 0x60, 0xb1, 0x51, + 0x6a, 0xc7, 0x80, 0x6b, 0x0f, 0x6b, 0x87, 0x7d, 0x5e, 0xd5, 0x0c, 0x0a, + 0x1f, 0x3f, 0xec, 0x73, 0x9d, 0x0c, 0x14, 0xb0, 0x3e, 0x6e, 0x04, 0xe8, + 0xa4, 0x5d, 0x02, 0x7d, 0x00, 0xfe, 0x07, 0xef, 0xe8, 0x8d, 0x83, 0x5d, + 0xcc, 0x58, 0xc2, 0x99, 0x5c, 0x1e, 0x9e, 0x56, 0xac, 0x0d, 0xb9, 0xfc, + 0x04, 0xa6, 0xf9, 0x22, 0xa5, 0xba, 0x05, 0x09, 0x8d, 0x4a, 0x28, 0x04, + 0x7a, 0x53, 0x3f, 0x8f, 0x1b, 0xe5, 0x34, 0x4d, 0x7d, 0x2c, 0xd8, 0x73, + 0x1c, 0x3e, 0x09, 0x0d, 0xff, 0x14, 0xfc, 0xed, 0x15, 0xfb, 0x9d, 0x1e, + 0xb2, 0xd3, 0x89, 0xb1, 0xdb, 0x30, 0xf3, 0x85, 0xeb, 0x6c, 0x5f, 0x69, + 0xa5, 0x1a, 0x50, 0xa6, 0xcb, 0x06, 0x69, 0xf5, 0x31, 0x86, 0xb3, 0xb1, + 0x11, 0x0d, 0x53, 0x9d, 0xe9, 0x2a, 0x20, 0xf0, 0x5f, 0xf6, 0x4c, 0x37, + 0x9e, 0x68, 0xe7, 0xc7, 0xde, 0x99, 0x34, 0x96, 0xaa, 0x5f, 0xfc, 0x47, + 0x7c, 0x5d, 0x46, 0x26, 0xc3, 0xa4, 0xe3, 0xe7, 0xba, 0x6e, 0xfb, 0x4b, + 0x5c, 0xc9, 0xd7, 0x2b, 0xa4, 0xef, 0x1c, 0xc0, 0xba, 0x61, 0x35, 0x2c, + 0xfb, 0x84, 0xf2, 0xc7, 0x56, 0x2c, 0x73, 0x88, 0x71, 0xa2, 0x75, 0xa1, + 0xbe, 0x24, 0x11, 0x4c, 0x2b, 0x11, 0x3d, 0xac, 0xf8, 0xd9, 0x38, 0xe5, + 0x15, 0x0d, 0xc4, 0x42, 0x8a, 0x4e, 0x09, 0x76, 0x51, 0xbb, 0xfe, 0xba, + 0x34, 0x0e, 0xb9, 0x4e, 0x59, 0x66, 0x02, 0x90, 0xe6, 0x83, 0x49, 0x10, + 0x8f, 0x2b, 0x95, 0x27, 0x8c, 0xf0, 0xa7, 0xa4, 0x69, 0x52, 0x34, 0x44, + 0x6a, 0x71, 0x37, 0x24, 0x5e, 0x1c, 0x89, 0x6d, 0x32, 0xa5, 0x38, 0x43, + 0xb5, 0x00, 0xbf, 0x30, 0x26, 0x6a, 0xbd, 0x72, 0xf0, 0x8f, 0x77, 0x3e, + 0xc9, 0x1d, 0x49, 0xc5, 0x2b, 0xac, 0xb1, 0xbe, 0x0e, 0x86, 0x93, 0x67, + 0xec, 0xd9, 0xef, 0x23, 0x9e, 0x09, 0xbf, 0xe0, 0x5d, 0x08, 0x1d, 0xdf, + 0xec, 0x10, 0xe3, 0x24, 0xe0, 0xce, 0x64, 0x95, 0xa8, 0x62, 0xd5, 0x9f, + 0xde, 0x77, 0x9f, 0x38, 0xa6, 0x2b, 0x68, 0xe7, 0xb3, 0xb2, 0x1b, 0x8b, + 0x3f, 0x95, 0x2b, 0x17, 0x04, 0x2f, 0xd0, 0x74, 0x98, 0x11, 0x73, 0x4a, + 0xe1, 0xa7, 0xb4, 0xd3, 0xdd, 0x77, 0x5c, 0x07, 0x8d, 0xa9, 0xdc, 0xc4, + 0x32, 0xfa, 0x19, 0x07, 0x9b, 0xb5, 0xbe, 0xc8, 0x75, 0x2e, 0x3a, 0xdb, + 0xb2, 0xc2, 0x4b, 0x11, 0x09, 0xad, 0xbf, 0x44, 0xb7, 0x52, 0x49, 0x23, + 0x09, 0x37, 0xbc, 0x18, 0xbe, 0x81, 0xd6, 0xee, 0x9a, 0x3b, 0xcb, 0x9e, + 0x4e, 0xd2, 0x23, 0x5f, 0x54, 0x20, 0xc6, 0x7f, 0xc2, 0x4c, 0x1a, 0xdc, + 0xdf, 0x1b, 0xec, 0xc4, 0xb6, 0x6f, 0x6e, 0x39, 0xeb, 0x22, 0xfd, 0xa8, + 0x4d, 0x7c, 0xf1, 0xfe, 0xee, 0x4d, 0xd8, 0x63, 0xc1, 0xdb, 0xed, 0x1c, + 0x58, 0xb5, 0xa4, 0x4c, 0x3e, 0x72, 0xa4, 0xff, 0x80, 0x72, 0xd7, 0x01, + 0xc2, 0xde, 0x6b, 0xa0, 0x2b, 0x5e, 0x25, 0x27, 0xf6, 0x2c, 0x96, 0x23, + 0xc3, 0x12, 0xc6, 0xc1, 0x79, 0x8b, 0x06, 0x71, 0x23, 0xd4, 0x66, 0xae, + 0x2a, 0xcd, 0xb2, 0x65, 0x67, 0x3d, 0x39, 0x7f, 0xed, 0x0d, 0x8a, 0x01, + 0x69, 0xd7, 0x40, 0xe3, 0xcd, 0x37, 0x64, 0x7e, 0x3c, 0x23, 0xda, 0xea, + 0x8b, 0xcf, 0x60, 0x34, 0x5e, 0x33, 0x20, 0x7c, 0xae, 0xba, 0xed, 0xf3, + 0xfa, 0xba, 0x52, 0x4b, 0x67, 0x82, 0x65, 0x24, 0x05, 0xe1, 0x59, 0xce, + 0xcb, 0xcb, 0xfc, 0x2e, 0xe9, 0x44, 0x6f, 0xc6, 0xab, 0xbc, 0x7f, 0xba, + 0x38, 0xfe, 0x64, 0x72, 0x36, 0xf5, 0x43, 0x69, 0x95, 0xd4, 0xc7, 0xc5, + 0x8b, 0x75, 0x21, 0x49, 0x8f, 0x7a, 0xa5, 0x57, 0x7f, 0x9e, 0x10, 0x0b, + 0x2c, 0x03, 0x92, 0x94, 0x13, 0xfb, 0x62, 0xe9, 0x85, 0xc3, 0x43, 0x5a, + 0x45, 0xe5, 0x87, 0x89, 0x09, 0xc8, 0x56, 0xfb, 0xef, 0x85, 0x7b, 0x68, + 0xfa, 0x02, 0x99, 0x25, 0xe9, 0xcb, 0xe3, 0x9d, 0xd4, 0xdb, 0x9c, 0xf0, + 0x9f, 0x9b, 0x22, 0xff, 0xcd, 0xe1, 0x6f, 0x26, 0x60, 0x68, 0x8b, 0x97, + 0x3c, 0x44, 0x94, 0xc9, 0xcd, 0x57, 0x87, 0x07, 0xa6, 0xbc, 0x8f, 0x3e, + 0xdc, 0xe0, 0xa4, 0x78, 0x20, 0xbc, 0xc2, 0x55, 0xc0, 0x6d, 0xf8, 0xed, + 0x1d, 0x39, 0xba, 0x4c, 0xeb, 0xdb, 0x18, 0x02, 0x72, 0x75, 0x11, 0x44, + 0x26, 0xf7, 0x50, 0x04, 0x77, 0xee, 0x26, 0x16, 0x07, 0x2b, 0x4a, 0xc3, + 0xa4, 0xdf, 0x26, 0xc4, 0xc5, 0x1c, 0x89, 0x9d, 0xd5, 0xc6, 0x8b, 0x8d, + 0xc4, 0x77, 0x06, 0x3c, 0xea, 0xd1, 0x3c, 0x76, 0x90, 0xe7, 0x11, 0xc0, + 0xaa, 0x9b, 0x6d, 0x77, 0x99, 0x47, 0x26, 0x18, 0xf7, 0x2c, 0xb1, 0x50, + 0x58, 0x1a, 0xf9, 0xfb, 0x8b, 0x00, 0x93, 0xfe, 0x59, 0x5e, 0x09, 0xf9, + 0xc9, 0xce, 0x08, 0x43, 0xff, 0xf7, 0x29, 0xc0, 0xbe, 0x00, 0x75, 0x83, + 0x8e, 0xfc, 0x34, 0x8b, 0x39, 0xee, 0xaa, 0x9a, 0x3d, 0x58, 0x53, 0xee, + 0x91, 0x3c, 0x85, 0x1f, 0x0f, 0x4a, 0x50, 0x09, 0x84, 0xd0, 0xcb, 0x39, + 0x05, 0x0b, 0xa6, 0xc9, 0xf7, 0x56, 0x7a, 0x71, 0xe2, 0xa5, 0x07, 0xa0, + 0xaf, 0xb6, 0x34, 0xcf, 0xb5, 0x84, 0xe6, 0xc1, 0xf7, 0x45, 0xb0, 0xdf, + 0x8a, 0xb6, 0x94, 0x9d, 0x9f, 0xf4, 0xb9, 0xdf, 0x59, 0x73, 0xa1, 0x54, + 0x2c, 0xe1, 0x89, 0x74, 0x11, 0x97, 0x94, 0x16, 0x77, 0x3f, 0x09, 0xc4, + 0xe7, 0x03, 0x3c, 0x45, 0x89, 0xd4, 0x66, 0x55, 0x1c, 0x3f, 0xb1, 0xfc, + 0xf0, 0x19, 0x7d, 0x84, 0x8e, 0xc8, 0x79, 0xee, 0x14, 0x11, 0x3d, 0xd7, + 0xc4, 0x1f, 0xc5, 0x80, 0xf0, 0xb3, 0x25, 0xa8, 0xdc, 0xf9, 0xfb, 0xcb, + 0x49, 0xf1, 0xd3, 0xff, 0xa3, 0x04, 0x5b, 0x0b, 0xeb, 0x97, 0xd4, 0xe0, + 0x5c, 0x38, 0xc2, 0xac, 0x2d, 0x41, 0x3b, 0x1e, 0x8b, 0x9b, 0x81, 0xc7, + 0xe8, 0x4e, 0xc6, 0xc5, 0xfd, 0x14, 0x46, 0x70, 0x97, 0xcc, 0x73, 0x23, + 0xcd, 0x4b, 0x94, 0xcc, 0x07, 0xd6, 0x2d, 0x76, 0x1b, 0x64, 0xc5, 0x3e, + 0x7c, 0x33, 0x55, 0x6e, 0x7c, 0x00, 0x48, 0x8c, 0x5b, 0x67, 0xc4, 0x03, + 0x27, 0x18, 0x46, 0xf9, 0xa8, 0x80, 0x24, 0xf6, 0xfd, 0x25, 0x15, 0x13, + 0x24, 0x1e, 0xf4, 0x2e, 0x4d, 0x7d, 0x31, 0xdc, 0x15, 0x09, 0xdc, 0x83, + 0x95, 0x1a, 0x9a, 0x0f, 0xfb, 0xad, 0x96, 0x1b, 0x81, 0x7e, 0xa7, 0x1b, + 0x32, 0x52, 0xa7, 0x6c, 0x3e, 0x1f, 0x22, 0x25, 0x80, 0xd5, 0x13, 0x03, + 0x96, 0x52, 0x90, 0x83, 0x24, 0x85, 0xb2, 0x41, 0xa0, 0xd5, 0xd2, 0x9f, + 0x4c, 0xa6, 0x1a, 0xd3, 0x5b, 0xdc, 0x7f, 0x8a, 0xa0, 0x87, 0x16, 0x2c, + 0x3c, 0x93, 0x5b, 0xc1, 0x7f, 0x5b, 0x8a, 0xd0, 0xa9, 0x54, 0xdf, 0x4b, + 0x72, 0xaa, 0xb5, 0x21, 0xb7, 0x7c, 0x10, 0x4a, 0x3a, 0x2c, 0xd5, 0xab, + 0xdb, 0x93, 0xa2, 0xf8, 0xac, 0x73, 0x97, 0x18, 0xc8, 0x1e, 0x0b, 0x29, + 0xf6, 0x02, 0x2d, 0x33, 0x46, 0x10, 0x4d, 0x0e, 0x52, 0x2f, 0x7b, 0xd1, + 0xb2, 0x35, 0x08, 0x47, 0x7e, 0x9a, 0x9e, 0x8e, 0xab, 0xb3, 0xc0, 0x19, + 0xa9, 0xe3, 0x0f, 0xdb, 0xd2, 0xb8, 0xa7, 0xa2, 0x9e, 0xa1, 0x3a, 0x6b, + 0x61, 0xc7, 0xa7, 0x4a, 0xde, 0x8b, 0x0b, 0x59, 0x11, 0x75, 0xac, 0xd5, + 0x34, 0x9c, 0x78, 0x3d, 0xdd, 0xce, 0x01, 0xac, 0x44, 0x9a, 0xa3, 0x33, + 0xcb, 0xde, 0x28, 0x11, 0xc4, 0x25, 0xa1, 0x70, 0xb8, 0x5b, 0x58, 0xd1, + 0x45, 0xf2, 0x3c, 0xdf, 0x2c, 0x8a, 0xe6, 0x3d, 0x90, 0xcf, 0x54, 0xcc, + 0x07, 0xdc, 0x42, 0x52, 0x20, 0x90, 0xd0, 0xcc, 0xd9, 0x4e, 0xdf, 0xdc, + 0x12, 0x17, 0x40, 0xb5, 0x24, 0x38, 0x28, 0xca, 0xf3, 0xa6, 0x3d, 0x97, + 0x2c, 0xf6, 0xf3, 0x73, 0x90, 0x66, 0x60, 0xaf, 0xec, 0xb4, 0x97, 0x77, + 0x56, 0x3c, 0x95, 0x4a, 0x16, 0x4e, 0x10, 0xcf, 0xb2, 0x6e, 0x6e, 0xe9, + 0x6c, 0xb4, 0xc8, 0xcc, 0xde, 0x2e, 0x50, 0xca, 0xa3, 0x77, 0xe5, 0xa5, + 0x18, 0xf1, 0x41, 0x99, 0x05, 0x1f, 0x4e, 0x27, 0x70, 0x07, 0xd9, 0x94, + 0x99, 0x33, 0x08, 0x07, 0xa7, 0x47, 0x48, 0x1f, 0xed, 0x16, 0xfd, 0xa3, + 0xc4, 0x4c, 0x93, 0xde, 0x2b, 0xcd, 0xe5, 0x29, 0xe1, 0x70, 0x71, 0x52, + 0x97, 0x6c, 0x8a, 0x33, 0x41, 0x1b, 0xf6, 0xd3, 0x28, 0x8f, 0x28, 0x5b, + 0x4e, 0xc4, 0xe2, 0xdf, 0x6b, 0xe3, 0x28, 0x48, 0xf4, 0x56, 0x5c, 0x87, + 0x45, 0x3a, 0xaa, 0xa0, 0xbe, 0xe4, 0xae, 0x2a, 0x45, 0x82, 0x6b, 0x10, + 0x6e, 0xf9, 0xf3, 0xaf, 0x64, 0x85, 0xdd, 0xf4, 0x83, 0x84, 0x88, 0xe3, + 0x91, 0x3e, 0x72, 0x79, 0x75, 0x0d, 0x81, 0x08, 0xed, 0x57, 0x1a, 0x0a, + 0x99, 0x93, 0x8a, 0x67, 0x96, 0x41, 0x5e, 0x3e, 0x81, 0xcf, 0xa5, 0x93, + 0xa4, 0xa4, 0xb0, 0xbb, 0x00, 0xf8, 0xbc, 0x6a, 0x0c, 0x0c, 0x8e, 0x23, + 0x25, 0xc5, 0x0f, 0x5a, 0x6e, 0x73, 0xd6, 0xad, 0xa7, 0xf4, 0x97, 0x80, + 0x92, 0xf2, 0x0a, 0xd5, 0xb9, 0x0b, 0x0e, 0x5e, 0x02, 0xcd, 0x5e, 0xa3, + 0xe5, 0x28, 0xe6, 0x5b, 0x71, 0x18, 0xdb, 0xe9, 0x5a, 0x7a, 0xa2, 0x92, + 0x56, 0x85, 0x48, 0xbf, 0xc8, 0x54, 0xee, 0x98, 0x95, 0xd3, 0x46, 0xe7, + 0xc3, 0x5c, 0x88, 0x6b, 0xbe, 0x06, 0x4e, 0x88, 0x92, 0xb0, 0x78, 0xf2, + 0xff, 0xe9, 0xf0, 0x39, 0x86, 0x55, 0x0a, 0xcd, 0x70, 0x2b, 0x6d, 0xa5, + 0xdc, 0xeb, 0x58, 0x21, 0xf6, 0x7f, 0xef, 0x52, 0x71, 0x92, 0x1b, 0x98, + 0x43, 0xb1, 0x0e, 0x39, 0xc0, 0xc0, 0xa9, 0xe8, 0x4c, 0xd2, 0x82, 0x4a, + 0x6a, 0xbe, 0x4f, 0x7f, 0xc5, 0xb1, 0x2c, 0x44, 0x86, 0x01, 0x20, 0x97, + 0x3e, 0x3c, 0x1e, 0xcd, 0x5f, 0x8b, 0x32, 0x39, 0x19, 0xa5, 0x4d, 0x9f, + 0xca, 0x5a, 0x72, 0x60, 0x9a, 0x3a, 0x29, 0x97, 0xb6, 0x49, 0x71, 0x80, + 0xf3, 0xc0, 0xc3, 0xe7, 0x16, 0xb3, 0x24, 0xa1, 0x17, 0xdf, 0xfb, 0xc8, + 0x9d, 0x3b, 0xd2, 0xc4, 0xd4, 0xdf, 0x5d, 0x16, 0x74, 0x81, 0xa4, 0x72, + 0xc6, 0x3a, 0x20, 0x3c, 0x03, 0x29, 0x10, 0x39, 0x61, 0xbd, 0xe3, 0xd1, + 0x9f, 0x8a, 0x8e, 0xd2, 0x09, 0xac, 0x14, 0x2e, 0x69, 0x70, 0xc2, 0xae, + 0x70, 0x0f, 0x1b, 0x81, 0xda, 0x50, 0x18, 0xeb, 0x9c, 0x5c, 0x6f, 0x4c, + 0x1a, 0x0f, 0xd6, 0x8c, 0x77, 0xe6, 0xa0, 0x03, 0xd4, 0x89, 0xee, 0x2a, + 0x72, 0xa1, 0xda, 0x8a, 0x4c, 0x40, 0x9c, 0x43, 0xfb, 0x98, 0x63, 0x5e, + 0x69, 0xfc, 0x42, 0x20, 0x3a, 0x0c, 0xeb, 0x4d, 0x1e, 0xc8, 0xdb, 0x1e, + 0x74, 0x2c, 0xbb, 0x76, 0x72, 0xa6, 0x4e, 0x6f, 0x30, 0xe4, 0x5d, 0xe7, + 0x90, 0xa9, 0x78, 0x59, 0x39, 0xa7, 0xf6, 0x66, 0x5a, 0xa7, 0xbf, 0x90, + 0xef, 0x09, 0xb0, 0x34, 0xbe, 0x72, 0x6c, 0x28, 0x07, 0xbb, 0x45, 0xa1, + 0xcd, 0x16, 0xf0, 0x39, 0x7f, 0xe2, 0x33, 0xfe, 0x96, 0x26, 0xa9, 0x93, + 0x6e, 0x48, 0xba, 0xf8, 0x08, 0xb3, 0xc9, 0xed, 0x9c, 0x6f, 0xa3, 0xd3, + 0x94, 0x3e, 0x78, 0x9f, 0x02, 0x99, 0x2d, 0x7e, 0xee, 0x34, 0xe6, 0x45, + 0x23, 0x6f, 0x1c, 0xf8, 0x9c, 0x99, 0x6c, 0xa1, 0x9b, 0x31, 0xf0, 0xe3, + 0x27, 0x55, 0x54, 0x4b, 0x57, 0x81, 0xcb, 0x9c, 0x4a, 0xbb, 0x94, 0xc1, + 0x80, 0xed, 0xd3, 0x41, 0xde, 0xa5, 0x94, 0x92, 0x44, 0x6d, 0x3b, 0x59, + 0xde, 0xa8, 0x13, 0x1d, 0xb0, 0x7e, 0x22, 0xb6, 0x68, 0x74, 0xc4, 0x40, + 0x84, 0x3d, 0x68, 0x98, 0x15, 0x09, 0x57, 0x37, 0x6e, 0x1f, 0x5f, 0xcc, + 0x47, 0xda, 0xc4, 0xf1, 0xd9, 0xc4, 0x51, 0xe6, 0xf6, 0x13, 0xc6, 0x91, + 0x6b, 0x27, 0x36, 0x5b, 0xbe, 0xdf, 0x65, 0x93, 0xd8, 0x79, 0x4a, 0xdc, + 0xe6, 0xa8, 0x0b, 0xcf, 0x5a, 0xa8, 0x53, 0xbd, 0xf3, 0x18, 0x64, 0xf1, + 0xd2, 0x66, 0xbb, 0x90, 0x0c, 0x45, 0x2c, 0x1a, 0x70, 0x75, 0xd5, 0x53, + 0x6d, 0xcc, 0x39, 0xb7, 0xb9, 0xcf, 0x4a, 0x6d, 0x45, 0x70, 0x26, 0xc8, + 0x3d, 0x80, 0x85, 0x74, 0x0b, 0x69, 0x1f, 0x33, 0x3d, 0x96, 0x57, 0x00, + 0x04, 0xc5, 0x5d, 0x79, 0xa4, 0x4f, 0xd7, 0x9d, 0xa5, 0x37, 0x15, 0xd1, + 0xb7, 0x54, 0x09, 0xa6, 0x05, 0x7f, 0x29, 0xc0, 0xe1, 0x78, 0xcb, 0x98, + 0x2a, 0x71, 0x86, 0xaf, 0xdb, 0x5e, 0x51, 0x73, 0x66, 0xca, 0x3e, 0xd3, + 0xc0, 0x29, 0x50, 0x91, 0x15, 0xd6, 0xdc, 0x07, 0x57, 0xac, 0x5a, 0x4e, + 0xad, 0x2e, 0xe5, 0x6f, 0xc9, 0x0e, 0xf3, 0x1b, 0x4d, 0x63, 0xed, 0x9f, + 0x1e, 0xdf, 0x26, 0x6f, 0x5d, 0xf1, 0x2b, 0x14, 0xed, 0xa1, 0x59, 0x36, + 0xe4, 0x4f, 0x9b, 0x63, 0x73, 0x70, 0x6e, 0xca, 0x69, 0x15, 0xcb, 0x47, + 0x05, 0xd4, 0x10, 0xbd, 0xce, 0x6c, 0xc0, 0xfc, 0xea, 0x3c, 0xfe, 0x15, + 0x13, 0xff, 0x49, 0x77, 0x0a, 0xf8, 0x0f, 0x98, 0xb5, 0x63, 0x35, 0x75, + 0x16, 0xdd, 0x9b, 0xb2, 0xd7, 0x2c, 0x50, 0x13, 0x25, 0x7e, 0x86, 0xb7, + 0x76, 0xb3, 0xbf, 0xa1, 0x56, 0xdb, 0xd7, 0x6c, 0x59, 0x07, 0x65, 0xf8, + 0xc0, 0x6e, 0x99, 0xe1, 0x7d, 0x81, 0x55, 0x4d, 0x62, 0xe7, 0x5d, 0xd8, + 0xff, 0xd3, 0x4e, 0xaa, 0x4d, 0xa5, 0x72, 0x2d, 0x98, 0x9b, 0x0c, 0x7c, + 0x94, 0x0f, 0xdd, 0x2c, 0x4f, 0x49, 0xbb, 0x45, 0xce, 0xb2, 0xfb, 0x08, + 0xaf, 0xdf, 0x1f, 0x8d, 0xf8, 0xd4, 0xf9, 0xe4, 0xee, 0xf6, 0xa7, 0x6e, + 0x9e, 0x1b, 0xa1, 0xf3, 0x90, 0x22, 0xe4, 0x91, 0x56, 0xf5, 0x86, 0x51, + 0x68, 0xfa, 0x9e, 0x31, 0xcf, 0xac, 0xb8, 0xb1, 0x87, 0x07, 0xba, 0xc0, + 0x68, 0x86, 0xb1, 0xd0, 0xcc, 0x99, 0x2c, 0xaf, 0x42, 0x93, 0x1f, 0x87, + 0x17, 0x98, 0x2a, 0xea, 0x79, 0x63, 0x55, 0x74, 0xd2, 0x5c, 0x94, 0x13, + 0x80, 0x2c, 0x0c, 0x6b, 0xff, 0x91, 0x16, 0xaa, 0xc0, 0xdb, 0x7e, 0x81, + 0x3f, 0x19, 0xfe, 0xa1, 0x50, 0xb6, 0xf9, 0xfe, 0xcb, 0xec, 0x60, 0xa1, + 0x9b, 0x4c, 0x71, 0x51, 0xea, 0x65, 0x65, 0xb1, 0x4c, 0x79, 0xd1, 0x8e, + 0x94, 0x08, 0xce, 0xa3, 0xd2, 0x81, 0xb0, 0xaf, 0x47, 0x8c, 0xef, 0xef, + 0xdc, 0x69, 0x17, 0xa7, 0x28, 0x31, 0x20, 0x4d, 0x0f, 0x33, 0x07, 0x2d, + 0x68, 0x49, 0xcd, 0x80, 0xeb, 0x24, 0x39, 0xe2, 0xbf, 0xc4, 0xc8, 0x20, + 0x89, 0xac, 0x20, 0x40, 0xba, 0x1e, 0xac, 0x64, 0x41, 0xdf, 0x58, 0x17, + 0xc5, 0x33, 0xd1, 0x3f, 0xac, 0xc1, 0xb2, 0xc2, 0xf8, 0xf9, 0x0d, 0x08, + 0xd3, 0x2d, 0x19, 0x32, 0x12, 0x4d, 0x06, 0x88, 0xa4, 0x35, 0x6d, 0x06, + 0xf7, 0x75, 0x7f, 0x9a, 0x09, 0x4a, 0xbb, 0xa6, 0x94, 0xbc, 0xb2, 0xf6, + 0x43, 0x0a, 0x31, 0x4c, 0x0f, 0xe1, 0x95, 0x26, 0xed, 0xfd, 0xe0, 0x7e, + 0xa2, 0xfe, 0x37, 0x6f, 0x34, 0x8c, 0x6c, 0xaf, 0x1d, 0x7c, 0x83, 0x9b, + 0xbe, 0x36, 0x05, 0xf4, 0xcf, 0xdb, 0xbc, 0xb1, 0x7c, 0x32, 0x80, 0x9c, + 0x19, 0xd0, 0x2e, 0x5d, 0xd6, 0x7b, 0xeb, 0xfa, 0x10, 0xac, 0x5b, 0x0c, + 0xb4, 0xeb, 0xbb, 0x5a, 0xc8, 0x21, 0x46, 0xbb, 0x00, 0x02, 0x27, 0x0a, + 0xa5, 0x1e, 0xd9, 0xac, 0x1d, 0xa8, 0x59, 0xf7, 0xa0, 0x32, 0x74, 0x9c, + 0x1c, 0x8b, 0x1d, 0x55, 0x4a, 0xe7, 0xd2, 0x58, 0x8b, 0xba, 0x03, 0xe2, + 0xd3, 0x54, 0xb2, 0xa7, 0x8a, 0x8a, 0x9d, 0x9a, 0x77, 0x88, 0x88, 0x80, + 0x4a, 0xd6, 0x2d, 0xbd, 0x5b, 0x83, 0x80, 0x5e, 0xf6, 0x28, 0x5d, 0x24, + 0xe7, 0x5c, 0x2e, 0x7a, 0xde, 0xf4, 0x5a, 0x22, 0xd0, 0xbb, 0x8d, 0x10, + 0xe8, 0x9b, 0xa0, 0x07, 0xaf, 0xb5, 0xf0, 0x31, 0x28, 0x24, 0x53, 0x39, + 0x57, 0xdc, 0xa4, 0x47, 0x5d, 0x4b, 0x30, 0xca, 0xdc, 0x65, 0x03, 0x35, + 0x4f, 0xe4, 0xc1, 0xd7, 0x74, 0x5b, 0xe2, 0x9d, 0x72, 0x9e, 0x42, 0xe1, + 0xcc, 0xd6, 0xe2, 0x5c, 0x37, 0x60, 0x72, 0x66, 0xcb, 0x36, 0xdc, 0x70, + 0x68, 0x1a, 0xe6, 0x2a, 0xc0, 0xda, 0xae, 0x9f, 0xfe, 0xfd, 0xf4, 0x09, + 0xd4, 0xf4, 0x12, 0x52, 0x44, 0xfb, 0x9e, 0x74, 0xe4, 0x15, 0x2c, 0x81, + 0xb0, 0x48, 0x03, 0xb6, 0x35, 0x43, 0x1f, 0xeb, 0xae, 0xac, 0xc7, 0xc2, + 0x83, 0x58, 0x41, 0x1c, 0x0a, 0x23, 0x03, 0x8b, 0x0d, 0xa5, 0xae, 0xf3, + 0x87, 0x00, 0xa7, 0x89, 0x35, 0x51, 0x22, 0x08, 0xb6, 0x0a, 0x56, 0x9b, + 0xf2, 0xdf, 0x08, 0xb9, 0x20, 0x07, 0x71, 0x7d, 0xa7, 0x0c, 0x1b, 0xb8, + 0xb0, 0x68, 0x10, 0x1b, 0x6f, 0x58, 0x91, 0x91, 0x61, 0x31, 0x62, 0xe1, + 0x3c, 0x52, 0x49, 0x77, 0x34, 0x79, 0xe9, 0x9c, 0x08, 0x22, 0x0f, 0x4e, + 0xbf, 0x95, 0x30, 0x8b, 0x5c, 0x23, 0xba, 0x57, 0x74, 0xc7, 0x9a, 0x94, + 0x17, 0xb3, 0x9b, 0xce, 0xe0, 0x47, 0xc9, 0x28, 0xd3, 0xe9, 0x01, 0x72, + 0xa9, 0x20, 0x80, 0x7f, 0x29, 0x07, 0xb9, 0x44, 0x02, 0x18, 0xe7, 0xe7, + 0xcb, 0xbf, 0xac, 0x08, 0xc2, 0x61, 0xff, 0xce, 0x1c, 0x44, 0x70, 0xc6, + 0x7a, 0xfc, 0x57, 0x7b, 0xe8, 0xc0, 0x86, 0xb4, 0x7c, 0x6f, 0x82, 0x2c, + 0xbf, 0xf0, 0xb4, 0x1e, 0x9c, 0x95, 0xde, 0x91, 0xeb, 0x62, 0x0c, 0x42, + 0xb9, 0xdb, 0x03, 0x66, 0xca, 0x9f, 0x11, 0xd9, 0x71, 0xdf, 0x78, 0xde, + 0x3d, 0xb0, 0xe2, 0xc9, 0x96, 0x38, 0x9d, 0x73, 0x69, 0x72, 0xb9, 0x08, + 0xdc, 0xb4, 0x1e, 0xb9, 0x4b, 0x36, 0x96, 0x8f, 0xea, 0xe6, 0x43, 0xb2, + 0x15, 0xff, 0xc1, 0xdb, 0x1e, 0x16, 0xa0, 0x04, 0x5d, 0x5f, 0x4a, 0x91, + 0xd9, 0xd1, 0x71, 0x9a, 0x9a, 0xf3, 0xb2, 0xff, 0x5a, 0x8c, 0x20, 0xbf, + 0xda, 0xc4, 0xfe, 0x6c, 0x49, 0xb0, 0x62, 0xf4, 0x94, 0xf7, 0x11, 0x73, + 0x63, 0xab, 0x49, 0xe2, 0x0a, 0x09, 0xfa, 0xbe, 0x2e, 0x4b, 0x98, 0x31, + 0x78, 0x27, 0x38, 0x92, 0x80, 0x09, 0x51, 0xaf, 0x18, 0x17, 0x81, 0xed, + 0x39, 0xcd, 0x7a, 0x76, 0x76, 0xc2, 0xe0, 0x40, 0x0e, 0x20, 0x9a, 0x7c, + 0x30, 0x84, 0xeb, 0xc8, 0x85, 0xae, 0x34, 0xfc, 0xfe, 0xa2, 0x47, 0x79, + 0x60, 0x95, 0x64, 0x86, 0x82, 0xa2, 0x8e, 0x0b, 0x64, 0x89, 0xb3, 0xd9, + 0x19, 0x90, 0xb7, 0x6a, 0x5b, 0x28, 0xc1, 0x5d, 0x6a, 0x62, 0x7a, 0x74, + 0xcc, 0xb6, 0x9c, 0xa4, 0xf2, 0xe8, 0xdf, 0x01, 0xa6, 0xa8, 0xb8, 0x3a, + 0x45, 0x98, 0xdc, 0x23, 0xcd, 0x48, 0x2f, 0x20, 0x09, 0x1c, 0x19, 0xe9, + 0x87, 0xb1, 0xa7, 0xb4, 0x51, 0xd5, 0xdb, 0x10, 0xc4, 0x92, 0x2e, 0x07, + 0xa5, 0xc5, 0xf8, 0xa3, 0x77, 0x73, 0x1a, 0x96, 0x80, 0xe3, 0x58, 0xdd, + 0x15, 0xe1, 0xb7, 0xf5, 0x0f, 0xad, 0xb4, 0x76, 0xe9, 0xea, 0xea, 0x7e, + 0x10, 0xed, 0x2b, 0x45, 0xac, 0xe8, 0xb8, 0xb8, 0x68, 0x7b, 0x4c, 0x61, + 0x0a, 0x72, 0x2e, 0x6c, 0x65, 0x2a, 0x5f, 0x15, 0x17, 0xe7, 0xec, 0xa7, + 0x3f, 0xc2, 0x47, 0xf3, 0x60, 0x38, 0xb9, 0xd9, 0x06, 0x84, 0x0b, 0xeb, + 0xc3, 0xe4, 0x2a, 0xb2, 0x4e, 0xb3, 0x79, 0x91, 0x38, 0x41, 0x37, 0x35, + 0xdc, 0x96, 0xf3, 0x2e, 0xff, 0x6b, 0xf4, 0x02, 0x62, 0x77, 0xb1, 0xad, + 0x6f, 0x58, 0xe5, 0xd2, 0xeb, 0xd8, 0xc8, 0x5e, 0xf0, 0x43, 0xf7, 0x61, + 0x71, 0x61, 0xd8, 0x4c, 0x7d, 0xc3, 0xa4, 0xc4, 0xf4, 0x51, 0x0d, 0xc2, + 0x45, 0xa3, 0xc2, 0x53, 0x7c, 0xbe, 0x34, 0xda, 0xa5, 0xa9, 0x81, 0xc6, + 0x5c, 0x0a, 0x1f, 0xdf, 0x99, 0xb7, 0x40, 0xb7, 0xc6, 0x0a, 0x0f, 0xb7, + 0xbb, 0x33, 0xf9, 0x61, 0xbb, 0x34, 0xef, 0x9e, 0xbf, 0x86, 0x5d, 0x09, + 0x92, 0xa8, 0x9f, 0x6c, 0xfc, 0x1e, 0x05, 0xb1, 0xc3, 0x38, 0xba, 0x2d, + 0xc2, 0x5c, 0xef, 0x1d, 0x3a, 0xbc, 0x1a, 0x30, 0xe9, 0x0f, 0x67, 0xf8, + 0x4d, 0x93, 0xc5, 0xe7, 0x7b, 0x22, 0xfc, 0x7c, 0xd2, 0xcd, 0x4c, 0xaf, + 0x8b, 0x7a, 0x00, 0xd5, 0xf4, 0x2e, 0x48, 0xd5, 0x94, 0xcd, 0x9b, 0xe7, + 0x95, 0x49, 0x98, 0x3a, 0x10, 0x15, 0xff, 0xf3, 0xb6, 0x61, 0x4a, 0x05, + 0x66, 0xf9, 0xbe, 0xaa, 0x31, 0x1d, 0x53, 0x0d, 0x84, 0x2d, 0x5a, 0xde, + 0x50, 0x21, 0xc3, 0x10, 0x6f, 0x25, 0x54, 0x5e, 0xd3, 0x53, 0x89, 0xd2, + 0x9f, 0xd2, 0xe6, 0xa3, 0x05, 0x48, 0xfe, 0xa9, 0xfc, 0x3d, 0x25, 0x1c, + 0x19, 0x50, 0x0e, 0x1c, 0x31, 0x80, 0x9d, 0xa1, 0xdc, 0x57, 0xc5, 0x68, + 0xe7, 0xbb, 0x64, 0x87, 0xf3, 0xc4, 0xba, 0x7a, 0x3b, 0x66, 0xd2, 0x3e, + 0x13, 0x88, 0xf6, 0x84, 0x39, 0xb6, 0x45, 0x65, 0x61, 0xad, 0xb8, 0x6e, + 0x9a, 0x45, 0x90, 0xcd, 0xfc, 0xb3, 0xe0, 0x73, 0xa0, 0x12, 0x44, 0x1d, + 0x00, 0x3f, 0xd9, 0x64, 0x98, 0xf4, 0x01, 0xba, 0x9d, 0xfd, 0xa0, 0x89, + 0x6b, 0xd0, 0x40, 0x10, 0x39, 0x3f, 0x3d, 0x1d, 0x43, 0x33, 0xba, 0x67, + 0x68, 0x4c, 0x45, 0x9c, 0xe2, 0xcb, 0x21, 0x74, 0xb3, 0x76, 0xce, 0xe2, + 0xfe, 0x48, 0x29, 0xc9, 0x95, 0x7e, 0x36, 0xae, 0xad, 0x14, 0xec, 0x20, + 0x8c, 0x4c, 0x7e, 0xf0, 0xab, 0x9a, 0xf4, 0x94, 0xab, 0x39, 0xbb, 0xeb, + 0x39, 0x9c, 0x38, 0xf9, 0x82, 0xf0, 0x87, 0x4c, 0xe5, 0x65, 0x67, 0x16, + 0xc5, 0x14, 0x0a, 0xa9, 0x51, 0x67, 0xe2, 0x60, 0x68, 0xdf, 0x2e, 0x5d, + 0x07, 0x49, 0x7a, 0x89, 0x0d, 0x96, 0x37, 0xe5, 0x53, 0x20, 0xab, 0x87, + 0x88, 0x4a, 0x75, 0x56, 0xef, 0xee, 0xb8, 0xe0, 0x02, 0x7b, 0x5e, 0xe4, + 0xf1, 0xcc, 0x6a, 0xd1, 0x77, 0x01, 0x84, 0xa5, 0xae, 0xb5, 0x47, 0xf4, + 0x9c, 0x3a, 0x8e, 0x71, 0xc8, 0x30, 0x2c, 0x0f, 0xcd, 0x39, 0xbc, 0x52, + 0x1d, 0xc4, 0x41, 0xde, 0xfd, 0x6d, 0x77, 0xb3, 0x8a, 0xcf, 0xff, 0x74, + 0x94, 0x44, 0xb9, 0x77, 0xf5, 0x09, 0xc5, 0x3b, 0x14, 0x1a, 0x5b, 0x80, + 0x8a, 0xa7, 0x34, 0x91, 0xf0, 0xd6, 0x94, 0xb4, 0x47, 0x93, 0x6d, 0x46, + 0x4e, 0x75, 0xd5, 0x87, 0x4c, 0xdf, 0x73, 0xab, 0xaa, 0x21, 0x4b, 0x1e, + 0xa8, 0x00, 0x39, 0x85, 0x88, 0xd8, 0x20, 0x3d, 0x4c, 0x16, 0x9b, 0x67, + 0x5e, 0xbd, 0x44, 0x2f, 0x95, 0x62, 0x09, 0x00, 0x35, 0x55, 0x54, 0xc8, + 0x54, 0x98, 0xe9, 0x45, 0x68, 0x5b, 0x3f, 0xc5, 0xa3, 0x3f, 0x01, 0x0a, + 0xa1, 0x7e, 0xa9, 0x2b, 0x95, 0xe9, 0xda, 0xf4, 0x9c, 0x6a, 0x24, 0xd1, + 0xa4, 0x7f, 0xb9, 0x6f, 0x50, 0x13, 0x5b, 0x1a, 0x42, 0xbe, 0x90, 0xca, + 0x1b, 0x33, 0x95, 0x99, 0xbb, 0x4d, 0x33, 0xb2, 0xab, 0xe5, 0xae, 0xa2, + 0x4e, 0x26, 0x96, 0x28, 0x68, 0x81, 0x80, 0xe7, 0x70, 0x66, 0x2b, 0x3b, + 0x09, 0xf3, 0xc6, 0x2a, 0x49, 0xdf, 0x7f, 0xd3, 0xd3, 0x16, 0xeb, 0xd3, + 0xb8, 0xd8, 0x36, 0x74, 0x05, 0x3e, 0x50, 0x38, 0xcb, 0x31, 0xd0, 0x00, + 0x0d, 0xab, 0x26, 0xbc, 0x46, 0x72, 0x8b, 0x99, 0x9a, 0xa9, 0xec, 0xd7, + 0x07, 0xb6, 0x2a, 0xb7, 0x71, 0x02, 0x72, 0xeb, 0x8f, 0x48, 0x52, 0xf4, + 0xba, 0x99, 0xd6, 0xf9, 0xc7, 0x79, 0xf8, 0x40, 0xbd, 0x7f, 0x61, 0x1b, + 0x3d, 0x32, 0x09, 0x7e, 0xb1, 0x29, 0x38, 0x29, 0xa2, 0xed, 0x60, 0x47, + 0xfb, 0x1c, 0xfd, 0xa4, 0x80, 0x22, 0x27, 0xb8, 0xff, 0x83, 0x8c, 0x92, + 0x61, 0x96, 0x02, 0xf1, 0x90, 0x27, 0x53, 0x1c, 0x84, 0xe6, 0xf4, 0xd2, + 0xb2, 0x0e, 0x48, 0xdf, 0x36, 0x1b, 0xe2, 0xfd, 0x59, 0x67, 0x49, 0x6c, + 0x3b, 0xd3, 0xac, 0x95, 0xb4, 0x9a, 0x13, 0x41, 0x35, 0xa7, 0xdc, 0x4b, + 0xb5, 0x3f, 0x93, 0xc3, 0xc1, 0x46, 0xd4, 0xd0, 0x7e, 0xf1, 0x6e, 0x9a, + 0x50, 0x05, 0xcd, 0x4a, 0xce, 0x0c, 0x14, 0x01, 0x9f, 0xdb, 0x5e, 0x8d, + 0x41, 0xa8, 0x6b, 0x32, 0xcd, 0x32, 0x7f, 0x51, 0xb6, 0x9a, 0x96, 0x00, + 0xe3, 0x3a, 0x62, 0x99, 0x96, 0x43, 0x76, 0x0e, 0xaf, 0x2d, 0x6b, 0xc0, + 0xb8, 0xb7, 0x14, 0x8a, 0xee, 0x39, 0xd8, 0xdc, 0xfc, 0xf1, 0x2a, 0x82, + 0x14, 0xe8, 0x65, 0x4b, 0x5b, 0x10, 0x87, 0x81, 0x7b, 0xa3, 0x1a, 0x12, + 0xde, 0x12, 0xf3, 0x94, 0xa2, 0xe5, 0xf6, 0x88, 0xa0, 0x83, 0xd7, 0xa7, + 0xfb, 0xfe, 0xce, 0x5b, 0x8d, 0xd8, 0x06, 0xa4, 0xa8, 0x2b, 0xf8, 0x7c, + 0x4f, 0x99, 0x7a, 0xf2, 0xf5, 0xc8, 0x99, 0x5b, 0xe0, 0x99, 0x2b, 0x71, + 0x85, 0x9f, 0xcf, 0x35, 0xbf, 0x48, 0x84, 0x53, 0x04, 0xda, 0x84, 0x90, + 0x84, 0x4c, 0x27, 0x23, 0xba, 0xe2, 0x90, 0x63, 0xcc, 0x42, 0x2e, 0x9c, + 0x22, 0x26, 0xbb, 0x2c, 0x43, 0x64, 0x20, 0xee, 0xc2, 0x1e, 0x63, 0x74, + 0x8d, 0xc8, 0x9f, 0xcc, 0x7b, 0x89, 0x1d, 0x5e, 0x3a, 0x3d, 0x42, 0x02, + 0x1b, 0x62, 0xd1, 0xe6, 0x52, 0xd2, 0xd5, 0x67, 0xd9, 0xad, 0x9b, 0xbd, + 0x8c, 0x4e, 0x23, 0x63, 0x4c, 0x77, 0xac, 0xfe, 0x3d, 0x48, 0x5d, 0x1c, + 0x59, 0x49, 0x1a, 0x85, 0x7e, 0x3f, 0x6b, 0x7f, 0xad, 0x09, 0x8f, 0x70, + 0x01, 0xb5, 0xc6, 0x85, 0xac, 0x54, 0xe5, 0x09, 0xec, 0x02, 0x96, 0x47, + 0x7d, 0x26, 0xf8, 0x91, 0xbe, 0x3b, 0xaf, 0xf7, 0x89, 0x3a, 0xb0, 0x97, + 0x20, 0xfd, 0x46, 0xd1, 0x55, 0xf0, 0x7a, 0xdf, 0xb1, 0x8e, 0xf4, 0x79, + 0x96, 0x2f, 0x60, 0x53, 0x1e, 0xc6, 0xe5, 0x56, 0xed, 0xe7, 0x9f, 0x15, + 0x73, 0x42, 0x40, 0xe2, 0x27, 0xc0, 0x93, 0xc2, 0x84, 0x8c, 0x4c, 0x28, + 0x14, 0xdd, 0x49, 0x4c, 0x10, 0xd1, 0x7f, 0x96, 0x95, 0x3a, 0x77, 0x69, + 0x4a, 0x5a, 0xef, 0xf2, 0x39, 0x82, 0xfe, 0xae, 0xdb, 0x1d, 0x0f, 0x06, + 0xf6, 0x6d, 0x7b, 0x6e, 0x5b, 0x10, 0xce, 0xfc, 0xd1, 0xc6, 0xaa, 0xc8, + 0x13, 0xfa, 0xcb, 0x71, 0x95, 0x8d, 0x49, 0x56, 0xd6, 0x69, 0x37, 0x02, + 0x72, 0xed, 0xbd, 0xd7, 0x07, 0x2d, 0xae, 0xb6, 0xda, 0xff, 0x69, 0xae, + 0x34, 0xda, 0x47, 0x25, 0xe8, 0x87, 0xfc, 0x56, 0x0a, 0xb5, 0xa8, 0x9d, + 0xad, 0xf4, 0xa8, 0x36, 0xd0, 0x0c, 0x17, 0x2d, 0xb7, 0x45, 0x31, 0x1d, + 0x80, 0x9d, 0xf6, 0x63, 0x80, 0x9c, 0x42, 0x73, 0xc0, 0x60, 0xf0, 0x91, + 0x1d, 0x1f, 0x7f, 0x68, 0x84, 0x02, 0xf1, 0x32, 0x15, 0x6e, 0x37, 0xdf, + 0x8d, 0xca, 0x4a, 0xe3, 0xc6, 0x6f, 0x2c, 0x71, 0x96, 0x56, 0xb8, 0x05, + 0x0e, 0x2b, 0xa2, 0x62, 0x62, 0xf8, 0xc9, 0x67, 0x82, 0x20, 0x2b, 0x62, + 0xe6, 0x3b, 0xf1, 0xda, 0xbb, 0xe8, 0x63, 0xa3, 0x83, 0x74, 0xd9, 0xba, + 0xdc, 0xfa, 0x3e, 0xd8, 0x8e, 0x6b, 0xdd, 0x1c, 0x38, 0x3f, 0x12, 0x9c, + 0x37, 0x4d, 0x90, 0xa3, 0x89, 0xc0, 0xd2, 0xfc, 0xbf, 0x12, 0x96, 0x57, + 0x3d, 0xd6, 0x12, 0x95, 0xcf, 0x00, 0xba, 0x3b, 0x92, 0x3b, 0xb9, 0x51, + 0x26, 0x67, 0x64, 0x00, 0x48, 0xb5, 0xbd, 0x19, 0xa4, 0xc5, 0x3c, 0xfd, + 0x32, 0x5f, 0x0b, 0x5b, 0x99, 0x8b, 0x04, 0x5c, 0xf1, 0xff, 0xe6, 0xd5, + 0x2f, 0x82, 0xbc, 0xa1, 0x3e, 0x23, 0x1b, 0x84, 0x98, 0x04, 0x86, 0x5d, + 0x75, 0xd3, 0x85, 0xc7, 0x85, 0xb5, 0x51, 0xdf, 0xc8, 0x21, 0x0d, 0x4d, + 0xf8, 0xf6, 0xe7, 0x17, 0xae, 0x7e, 0xe4, 0x6c, 0x6a, 0xad, 0xf5, 0xee, + 0x5a, 0x1c, 0x56, 0xbe, 0x6f, 0x42, 0x87, 0x3c, 0x51, 0xc6, 0x71, 0x21, + 0x08, 0xe8, 0x98, 0xf9, 0x17, 0xcd, 0x0d, 0x7b, 0xee, 0x37, 0xc6, 0x6e, + 0x32, 0xfd, 0xae, 0xe5, 0x55, 0xf9, 0xda, 0xc4, 0x4b, 0xf8, 0xb1, 0x7a, + 0xa3, 0x34, 0xb6, 0xac, 0xf2, 0x4f, 0x4f, 0xc5, 0xf5, 0x8b, 0x73, 0xe5, + 0xf9, 0xa0, 0x05, 0xad, 0xbe, 0xa6, 0x97, 0x79, 0x58, 0xed, 0xfc, 0x3e, + 0xac, 0x08, 0xb4, 0x5d, 0x04, 0xf9, 0xd3, 0x32, 0x3c, 0x0b, 0xb5, 0x6e, + 0x9e, 0xdc, 0xbe, 0x92, 0xe1, 0x8f, 0x98, 0x69, 0x30, 0xcc, 0x15, 0x19, + 0x78, 0x98, 0xd1, 0x93, 0xe4, 0x5f, 0x27, 0x8b, 0x45, 0x02, 0xc0, 0x43, + 0xf3, 0xf2, 0xaa, 0x32, 0xef, 0xec, 0x99, 0x53, 0x18, 0x59, 0xda, 0xf5, + 0xf4, 0xd3, 0x0c, 0x60, 0x68, 0x7c, 0x66, 0x3f, 0x99, 0xfc, 0x0b, 0x1c, + 0x89, 0x5a, 0x1d, 0xdd, 0x9f, 0x81, 0x98, 0x62, 0x69, 0x1a, 0x84, 0x1f, + 0x0a, 0x44, 0x01, 0x92, 0x7a, 0xfe, 0xeb, 0xbd, 0xc3, 0x08, 0xb7, 0x16, + 0x72, 0x0f, 0x73, 0x45, 0x28, 0x36, 0x07, 0x8d, 0xfa, 0x03, 0xf2, 0x27, + 0x17, 0xbc, 0x8e, 0xcb, 0x0b, 0x49, 0xc0, 0x31, 0x5d, 0xbf, 0xe9, 0x06, + 0x14, 0x99, 0x45, 0x5c, 0xe0, 0xc8, 0x1b, 0x16, 0x5f, 0x6b, 0xc4, 0x85, + 0x4f, 0x84, 0x14, 0x11, 0x58, 0xde, 0xda, 0x3e, 0x8b, 0x81, 0x8a, 0x9d, + 0x69, 0x88, 0x10, 0x85, 0x87, 0x7c, 0xc2, 0x1c, 0x67, 0xa3, 0x5d, 0x53, + 0xf4, 0x41, 0xce, 0x92, 0x8c, 0x6d, 0x83, 0x38, 0x21, 0xdc, 0x10, 0x0a, + 0x76, 0x8b, 0x4a, 0x89, 0x84, 0x55, 0x9b, 0x17, 0x5e, 0x8a, 0xb0, 0x13, + 0xf2, 0xfb, 0xe4, 0x5b, 0x98, 0x99, 0xbd, 0x93, 0x8e, 0xee, 0xe7, 0x03, + 0x13, 0xee, 0x4c, 0xbf, 0xd4, 0x0b, 0x3d, 0x01, 0x01, 0xb7, 0xdb, 0x3a, + 0xac, 0xa3, 0x6a, 0xf7, 0x16, 0x1e, 0xde, 0x79, 0xc4, 0x58, 0x3b, 0x79, + 0x7e, 0xf9, 0x72, 0x96, 0xd7, 0x2a, 0x55, 0xf2, 0xda, 0x15, 0x71, 0x2a, + 0xaa, 0xd9, 0xcd, 0xfc, 0xb6, 0xe3, 0xc9, 0x70, 0xb6, 0x53, 0x0a, 0x47, + 0x3a, 0x95, 0x4d, 0x1d, 0x5d, 0x84, 0x02, 0x8a, 0xd6, 0x44, 0x6e, 0x39, + 0x22, 0xb5, 0x96, 0xa4, 0x13, 0x8d, 0xd7, 0xd7, 0x08, 0x75, 0x8f, 0xf0, + 0xe2, 0xae, 0x2a, 0xc5, 0x40, 0x2b, 0x83, 0x50, 0x4e, 0x50, 0x4f, 0x75, + 0x40, 0xbd, 0xf3, 0xd9, 0x93, 0x04, 0x13, 0x58, 0xa5, 0x5c, 0xcd, 0xe4, + 0x76, 0xdf, 0x89, 0x90, 0x9d, 0x2a, 0x93, 0xd0, 0x5c, 0x0c, 0xaa, 0x0d, + 0xb7, 0x6e, 0x47, 0xd6, 0x87, 0x7b, 0x69, 0x7e, 0xde, 0x02, 0x4c, 0xaa, + 0x19, 0xc6, 0x43, 0x7c, 0xd8, 0x32, 0xa7, 0xab, 0x26, 0xcf, 0x82, 0x4d, + 0xbc, 0xcc, 0xa3, 0xfb, 0xa7, 0x75, 0xe7, 0x41, 0x44, 0xab, 0xf0, 0x92, + 0x79, 0x36, 0xd9, 0xec, 0xec, 0xa3, 0xb1, 0xa4, 0x0e, 0x8d, 0x5b, 0xda, + 0xf8, 0x8d, 0xb5, 0x3f, 0x91, 0xd8, 0xb0, 0xc4, 0x76, 0xb2, 0xa9, 0xcd, + 0x05, 0xaa, 0x6e, 0x16, 0x4a, 0x0a, 0x50, 0x9a, 0xa3, 0xfb, 0x1b, 0x68, + 0xd3, 0xf2, 0xfe, 0xa3, 0x37, 0x47, 0x03, 0xb6, 0xe4, 0x11, 0xa6, 0xb9, + 0x0b, 0x7d, 0x10, 0x6f, 0xfc, 0xd1, 0x0e, 0x13, 0xdf, 0x23, 0x34, 0x4a, + 0xdd, 0xe4, 0x1c, 0x7b, 0xe1, 0x24, 0x7b, 0xc8, 0xef, 0x42, 0x5d, 0x99, + 0xe6, 0xe1, 0xce, 0x33, 0xa9, 0xb8, 0xd5, 0x16, 0x90, 0x10, 0x2b, 0xa0, + 0x18, 0x87, 0x58, 0xec, 0x4a, 0xcb, 0x51, 0x2b, 0xa8, 0xe5, 0xde, 0xd5, + 0x3d, 0xc7, 0x98, 0xcc, 0xec, 0x99, 0x68, 0x4b, 0x3c, 0xae, 0x15, 0xfa, + 0x1a, 0x05, 0xe3, 0x75, 0x8a, 0x55, 0x65, 0x45, 0xbb, 0x56, 0x96, 0x9a, + 0xb0, 0x81, 0xb9, 0xe9, 0xfa, 0x2b, 0xfa, 0x6f, 0xdd, 0x4e, 0x1d, 0xac, + 0x6c, 0x5a, 0xb0, 0x97, 0x81, 0x2c, 0x86, 0xd4, 0x4d, 0xaa, 0x07, 0x0f, + 0x6c, 0x34, 0x1d, 0x16, 0x04, 0x3a, 0x48, 0x0a, 0xf8, 0xe8, 0x83, 0xa2, + 0x2c, 0x1b, 0xa4, 0xd2, 0xa4, 0x18, 0xe6, 0x61, 0x26, 0x54, 0x05, 0xf8, + 0xf0, 0x45, 0x52, 0x45, 0x38, 0x1b, 0xb2, 0xb8, 0x8d, 0x2d, 0x49, 0x12, + 0x22, 0xbf, 0xfd, 0x70, 0xbc, 0x7f, 0xa6, 0x11, 0xaf, 0xbc, 0x7a, 0xf4, + 0xd6, 0x80, 0xe4, 0xb1, 0x61, 0x82, 0x9f, 0x54, 0x0c, 0x9b, 0x74, 0x39, + 0x5b, 0x9b, 0x43, 0x76, 0xff, 0xb7, 0xb2, 0x65, 0x43, 0x9d, 0x8e, 0x1f, + 0x22, 0x73, 0x16, 0x94, 0x16, 0x4f, 0xeb, 0x65, 0x47, 0x08, 0xe8, 0xbf, + 0x78, 0x15, 0x95, 0x79, 0x06, 0x66, 0xbe, 0x1f, 0xfa, 0xd0, 0x15, 0xbc, + 0xa7, 0xd1, 0xef, 0xb2, 0x42, 0xf0, 0x7b, 0x4c, 0xdb, 0x7d, 0x87, 0x52, + 0x82, 0xde, 0x27, 0x1e, 0x0d, 0x3b, 0x9f, 0xd9, 0x7e, 0x26, 0xd7, 0x24, + 0x6b, 0x32, 0x22, 0x9f, 0x86, 0xb8, 0xc5, 0x19, 0x96, 0xc6, 0x1e, 0x42, + 0x34, 0xf9, 0x39, 0x20, 0x6b, 0x36, 0x76, 0x90, 0x1a, 0xe0, 0x04, 0x59, + 0xc4, 0x1f, 0xdd, 0x8a, 0xf1, 0x94, 0x18, 0x87, 0xec, 0xd0, 0x0e, 0x75, + 0xb2, 0x7b, 0xaa, 0x8e, 0x07, 0x6d, 0xc3, 0x63, 0xc1, 0xac, 0x67, 0xac, + 0x23, 0x33, 0x7b, 0xfc, 0x6d, 0xf3, 0x7a, 0xdc, 0xed, 0x99, 0x9a, 0x4e, + 0xff, 0x6e, 0x4a, 0x2a, 0x6a, 0xe9, 0x9d, 0x27, 0x7a, 0xc1, 0x0a, 0xbf, + 0xcc, 0x60, 0xb7, 0x39, 0xdc, 0xaa, 0x06, 0x2f, 0x3d, 0xb9, 0xca, 0x90, + 0x87, 0x8d, 0xc5, 0x69, 0x33, 0xa6, 0x50, 0x30, 0x52, 0x4e, 0x7e, 0xae, + 0x7e, 0x30, 0x49, 0x7b, 0xd9, 0xed, 0x72, 0x25, 0x5c, 0x79, 0xa8, 0x22, + 0x2e, 0x13, 0xf7, 0x4d, 0xac, 0x9f, 0x1b, 0xbb, 0x69, 0x56, 0x5e, 0x54, + 0x12, 0x29, 0x6d, 0x4f, 0xd6, 0x12, 0xbc, 0x44, 0x54, 0xd6, 0xcf, 0xf5, + 0x55, 0x2b, 0xa1, 0x91, 0xe4, 0x5c, 0xdd, 0x3e, 0x7f, 0xc6, 0x3f, 0x22, + 0x38, 0xef, 0x14, 0xae, 0x1e, 0xef, 0x68, 0x91, 0xcb, 0xbc, 0x27, 0x11, + 0xd9, 0x7e, 0x5f, 0x94, 0x07, 0x34, 0x23, 0xa0, 0x25, 0xa7, 0x3b, 0xa7, + 0x3d, 0xed, 0x77, 0xad, 0x0f, 0xce, 0xba, 0x9b, 0x54, 0x90, 0x78, 0x3c, + 0x85, 0x8d, 0xbe, 0xfb, 0x38, 0x01, 0x42, 0xf3, 0xbf, 0x5a, 0xd9, 0x8c, + 0x38, 0x60, 0x93, 0x1f, 0x09, 0x17, 0xed, 0x57, 0x7d, 0x17, 0xdb, 0xcb, + 0xa0, 0x55, 0x0d, 0x33, 0x9f, 0xd1, 0x9a, 0x6f, 0xf7, 0xe0, 0x02, 0x33, + 0x0e, 0x0c, 0x83, 0x76, 0x62, 0xc0, 0x48, 0x47, 0xbe, 0x49, 0xf7, 0xd3, + 0xf5, 0x52, 0xe8, 0x90, 0x8a, 0xa0, 0x2e, 0x40, 0xc6, 0x07, 0xc7, 0x30, + 0x2a, 0x8b, 0x24, 0x8d, 0xa5, 0x1a, 0x90, 0x10, 0x02, 0x64, 0x23, 0x66, + 0xe9, 0x12, 0xef, 0x91, 0xc7, 0x45, 0x24, 0x99, 0x43, 0x49, 0xb5, 0x77, + 0x97, 0x93, 0x0c, 0x24, 0x84, 0xa5, 0xcc, 0xb7, 0xb7, 0x4f, 0xc1, 0x66, + 0x3d, 0xf7, 0x36, 0x28, 0xd8, 0x28, 0x5f, 0xfe, 0x98, 0xbe, 0x30, 0x33, + 0x6d, 0xff, 0xc1, 0x76, 0x0c, 0x97, 0xa1, 0x60, 0x4f, 0xa2, 0x29, 0x77, + 0x99, 0xcb, 0xeb, 0x53, 0x4f, 0xd5, 0xc2, 0x59, 0x85, 0x30, 0xa0, 0x86, + 0xad, 0x6c, 0xd2, 0x3e, 0x0a, 0x5a, 0x25, 0xb6, 0x75, 0xd0, 0x61, 0x05, + 0xcc, 0x97, 0x13, 0x0c, 0x0e, 0x88, 0xea, 0x06, 0x8e, 0x0a, 0x85, 0x5e, + 0xdc, 0x78, 0x2b, 0x3a, 0xec, 0xf8, 0x06, 0xc8, 0x40, 0xaf, 0xc3, 0x9e, + 0x90, 0xd8, 0xb4, 0x55, 0x91, 0x13, 0xf6, 0x8d, 0xb2, 0xd7, 0x22, 0x03, + 0xd9, 0xc5, 0xb7, 0x0d, 0x4c, 0xcc, 0xdc, 0x38, 0xed, 0x67, 0x03, 0xf2, + 0x2b, 0xeb, 0xdf, 0xc9, 0x93, 0xe9, 0x0c, 0x5a, 0x12, 0x9d, 0x67, 0xfd, + 0xdc, 0x0f, 0x07, 0x63, 0xf4, 0x27, 0xc6, 0x4a, 0x93, 0xfb, 0x00, 0xd6, + 0x3e, 0x04, 0x47, 0x61, 0x32, 0xce, 0xd4, 0x0f, 0xbc, 0x11, 0xfc, 0x37, + 0x01, 0x41, 0xf5, 0x44, 0x36, 0x94, 0xfd, 0xa5, 0xed, 0x81, 0xf3, 0x95, + 0x2b, 0x72, 0x3b, 0x9f, 0x82, 0x80, 0xf8, 0x6b, 0xb1, 0x78, 0x03, 0x50, + 0xb7, 0xbf, 0xf9, 0x90, 0x5e, 0x45, 0xd5, 0x0c, 0xab, 0x18, 0x9b, 0x44, + 0xe6, 0xd9, 0xfd, 0xcb, 0xdc, 0x27, 0xcd, 0x6d, 0xe6, 0x87, 0xc1, 0xbb, + 0xb3, 0xf1, 0x68, 0x15, 0x8c, 0x9f, 0xd5, 0xe4, 0x0f, 0xb4, 0xf3, 0xa8, + 0x2a, 0x3c, 0x9e, 0x49, 0x50, 0x8a, 0x20, 0x3d, 0x89, 0x78, 0xd4, 0xca, + 0xdd, 0x41, 0x6b, 0x8c, 0x52, 0x7f, 0xcd, 0x78, 0xbb, 0x8d, 0x84, 0xeb, + 0x55, 0x16, 0xf0, 0x7a, 0xa7, 0x5d, 0xad, 0x8f, 0x75, 0x0a, 0x77, 0x68, + 0x57, 0x1d, 0xb7, 0xdf, 0xa5, 0x4a, 0x4f, 0xbb, 0x5b, 0xa7, 0xc4, 0x49, + 0xa5, 0x8d, 0x9c, 0xbd, 0x41, 0xc4, 0x14, 0x44, 0xa9, 0x0e, 0xaf, 0xae, + 0x62, 0x31, 0x1b, 0x80, 0xc5, 0xf0, 0xaa, 0x4e, 0x66, 0xa4, 0xde, 0x2e, + 0x55, 0x67, 0xd6, 0x32, 0x2b, 0xf3, 0x22, 0xda, 0x0b, 0xc8, 0xb7, 0xf3, + 0xf4, 0x6d, 0x64, 0x82, 0x9f, 0x34, 0x4e, 0xae, 0xad, 0x8a, 0xd2, 0x32, + 0xd9, 0x5b, 0x42, 0x93, 0x0a, 0x2b, 0x5f, 0xd1, 0xd9, 0xdb, 0x75, 0x21, + 0xc9, 0x35, 0x7c, 0xf1, 0x3c, 0x99, 0xca, 0xb4, 0x5a, 0x6d, 0xe5, 0x5e, + 0x19, 0xdf, 0xa7, 0x1e, 0x4d, 0x20, 0x95, 0x46, 0x9d, 0xdf, 0x2e, 0x7a, + 0xbc, 0xc5, 0xa7, 0x4e, 0xdc, 0x90, 0x2e, 0x7a, 0x70, 0x51, 0x4b, 0x70, + 0xae, 0xe4, 0x3d, 0x21, 0xe9, 0x45, 0x7e, 0xe9, 0xeb, 0x9d, 0x25, 0x91, + 0x86, 0x54, 0x20, 0x88, 0x63, 0x64, 0xc5, 0xc3, 0x2e, 0x9b, 0x39, 0x30, + 0x8d, 0x27, 0x78, 0x21, 0xdb, 0x5a, 0x2a, 0x70, 0xbf, 0x7a, 0x96, 0x43, + 0xd2, 0x94, 0xd1, 0xb1, 0xe5, 0xee, 0xec, 0x3a, 0x64, 0x30, 0xbd, 0xa7, + 0xec, 0x68, 0x82, 0xf5, 0x9a, 0xa7, 0xb3, 0x1a, 0x09, 0x02, 0x81, 0x5b, + 0x9f, 0x5b, 0x75, 0xad, 0xd4, 0x87, 0xe8, 0x17, 0xab, 0x39, 0xb3, 0xe3, + 0x95, 0x51, 0x2b, 0xa2, 0xcb, 0x34, 0x06, 0x18, 0x27, 0x13, 0x07, 0x67, + 0xe8, 0x73, 0x55, 0xb7, 0x7d, 0xaa, 0x76, 0x58, 0x5e, 0x23, 0x84, 0x62, + 0x5b, 0x65, 0x9e, 0x2b, 0x95, 0x2e, 0x21, 0xea, 0xc6, 0x6e, 0xab, 0x2a, + 0x31, 0x43, 0x49, 0x23, 0xc6, 0xe5, 0xb1, 0xc5, 0xdb, 0x3a, 0x37, 0x75, + 0xc7, 0xfd, 0x9d, 0x02, 0xd7, 0x66, 0x00, 0x80, 0xa5, 0x13, 0xf8, 0x05, + 0xa1, 0xa9, 0x57, 0x08, 0xbb, 0x61, 0x21, 0x04, 0x07, 0x45, 0xbc, 0x1d, + 0x4c, 0xb3, 0x58, 0xcc, 0x4b, 0x23, 0xcd, 0x1f, 0x6b, 0x82, 0x8c, 0x21, + 0x80, 0x32, 0x9f, 0x67, 0x80, 0xbf, 0x1b, 0xc8, 0xcc, 0x66, 0xe9, 0x9f, + 0x30, 0x5d, 0x31, 0xf3, 0xc5, 0x28, 0x8c, 0xd1, 0xdf, 0x3c, 0xc6, 0x1e, + 0x60, 0xa2, 0x57, 0x5e, 0x37, 0x5c, 0x05, 0xb9, 0x26, 0x0e, 0x5d, 0xf2, + 0x4d, 0x58, 0x0e, 0x7a, 0xf6, 0x4e, 0xd1, 0xd1, 0x0c, 0x0a, 0x16, 0x69, + 0xd7, 0x8d, 0xb6, 0x75, 0xe1, 0x48, 0xb6, 0xd5, 0x9b, 0xbe, 0x8d, 0x07, + 0x1a, 0x29, 0x53, 0x14, 0x44, 0xf9, 0x19, 0x53, 0xa9, 0x64, 0xb1, 0x77, + 0xc5, 0x99, 0xf6, 0x87, 0x27, 0xac, 0xcb, 0x10, 0x98, 0x2a, 0xc7, 0x86, + 0x1d, 0x7f, 0xbd, 0xce, 0xc5, 0x35, 0x63, 0xff, 0x8c, 0x64, 0x5c, 0xe9, + 0xf4, 0xc1, 0xad, 0x8d, 0x57, 0xb6, 0xb6, 0xa8, 0xe0, 0x36, 0xe3, 0x5c, + 0xc3, 0x3a, 0x12, 0xe9, 0xa8, 0x28, 0x43, 0x5f, 0x90, 0x93, 0xb5, 0x03, + 0x50, 0x12, 0x27, 0x6b, 0x9c, 0xaa, 0xe5, 0x44, 0xc4, 0x88, 0x86, 0xa9, + 0x27, 0x9b, 0x22, 0xfd, 0x4a, 0x20, 0xe8, 0x3a, 0xc7, 0xd5, 0xab, 0xa6, + 0xc1, 0xab, 0x86, 0x4b, 0xdf, 0xfb, 0x63, 0x01, 0xab, 0xdb, 0x53, 0xd9, + 0x1a, 0xed, 0x9c, 0x95, 0x14, 0xea, 0x21, 0x6a, 0xd0, 0xf8, 0x86, 0xec, + 0xb8, 0x70, 0xc3, 0xad, 0x4d, 0xe9, 0x01, 0xfd, 0xe8, 0x6c, 0x88, 0x61, + 0x27, 0x7d, 0x8e, 0xdc, 0x68, 0xfe, 0x0a, 0x52, 0x24, 0x3c, 0xc2, 0x98, + 0xb5, 0x29, 0x18, 0x74, 0x17, 0x32, 0x9e, 0xcb, 0x93, 0xdc, 0x6a, 0x18, + 0x03, 0x91, 0x4a, 0xc2, 0x23, 0x2d, 0x26, 0xe9, 0xde, 0x7c, 0xcd, 0x18, + 0x69, 0x30, 0x0f, 0x6e, 0x46, 0x87, 0xcf, 0x8c, 0x29, 0xce, 0x0d, 0x1c, + 0xb9, 0xe1, 0x1c, 0x32, 0x58, 0xd9, 0x08, 0x69, 0x21, 0x91, 0x56, 0x3d, + 0x73, 0x02, 0xa4, 0x75, 0x77, 0x79, 0x4b, 0x79, 0x5a, 0x40, 0xb3, 0xc3, + 0x24, 0x90, 0xd0, 0x19, 0x9e, 0x25, 0xf8, 0xd4, 0x58, 0x4b, 0x27, 0xd4, + 0xf0, 0xf2, 0xa1, 0x4c, 0x54, 0xdb, 0x18, 0x73, 0x8e, 0xb4, 0x9f, 0xe2, + 0x93, 0x2a, 0x41, 0xb7, 0x79, 0x58, 0x92, 0xe5, 0xd8, 0x73, 0xeb, 0x28, + 0xf8, 0x09, 0x8a, 0xc4, 0xcf, 0x82, 0x24, 0x4c, 0xa8, 0xfa, 0xb2, 0x2d, + 0x25, 0x80, 0x43, 0xdc, 0xe6, 0x54, 0xf8, 0xec, 0x79, 0xe6, 0xe9, 0x20, + 0xac, 0xf6, 0x9f, 0xaf, 0x1e, 0xa3, 0x3f, 0xef, 0xd0, 0x21, 0x70, 0xd0, + 0x72, 0xf1, 0xfe, 0x56, 0x8d, 0x6b, 0xdd, 0x93, 0x60, 0x3e, 0x91, 0xed, + 0x51, 0x9f, 0x2c, 0x44, 0x44, 0x96, 0x50, 0xc0, 0x1b, 0xc4, 0xf0, 0xcb, + 0x32, 0x0d, 0xd2, 0xae, 0x84, 0x5c, 0xff, 0x4f, 0xd9, 0xb5, 0x43, 0xcd, + 0xb9, 0x6b, 0x05, 0xad, 0x4a, 0x6a, 0xa2, 0xeb, 0x71, 0x14, 0xd0, 0x15, + 0xae, 0x82, 0x34, 0xbd, 0x97, 0xf5, 0x75, 0x3c, 0x6d, 0x0d, 0xd0, 0x43, + 0xa8, 0xb8, 0xb1, 0xce, 0x09, 0xe0, 0x41, 0x44, 0x42, 0x03, 0xcb, 0x8b, + 0xc2, 0x1c, 0xf7, 0xb2, 0x77, 0x82, 0xd5, 0x88, 0x0c, 0x4d, 0x8d, 0x58, + 0x79, 0xe2, 0x56, 0xc9, 0x1d, 0xab, 0xfd, 0x5f, 0xbc, 0x51, 0x42, 0x3d, + 0xb7, 0x1f, 0xba, 0x1d, 0x73, 0x3e, 0xbd, 0x8f, 0x02, 0x9e, 0x41, 0x87, + 0x1b, 0xbe, 0x2f, 0x54, 0x87, 0xd6, 0xaf, 0xd0, 0x12, 0xee, 0xe5, 0x05, + 0xbc, 0x17, 0x75, 0x00, 0x39, 0x9d, 0xd7, 0x18, 0xc1, 0x10, 0x22, 0x84, + 0xae, 0xc9, 0x56, 0xba, 0xb5, 0x43, 0x41, 0x8c, 0xaa, 0x22, 0x87, 0x51, + 0xc9, 0xae, 0x8d, 0x32, 0xc4, 0xf5, 0xc3, 0x20, 0xa8, 0xc9, 0xe7, 0x97, + 0xa7, 0x38, 0xe0, 0xe7, 0x54, 0xfd, 0xeb, 0x71, 0xcc, 0xf0, 0x13, 0x2e, + 0x0a, 0x96, 0xaa, 0xd8, 0x74, 0xa5, 0x8f, 0xad, 0x9d, 0xe8, 0x14, 0x52, + 0x3d, 0x79, 0x69, 0x6f, 0x4c, 0x2c, 0xa1, 0x7c, 0xb4, 0x47, 0x56, 0x32, + 0x81, 0x31, 0xda, 0x6b, 0x0b, 0x74, 0x63, 0x2e, 0x20, 0x54, 0x12, 0x7f, + 0x3e, 0xb0, 0x6d, 0x42, 0x5d, 0x81, 0x87, 0xe0, 0x34, 0x88, 0x6f, 0x09, + 0x8f, 0xff, 0x64, 0x94, 0x71, 0x3e, 0xc9, 0x97, 0xa7, 0x7d, 0x76, 0x33, + 0x62, 0x51, 0xf5, 0x4a, 0x72, 0xb8, 0x04, 0x05, 0x17, 0x91, 0xa0, 0x2b, + 0x94, 0x35, 0x25, 0x52, 0xc0, 0x1e, 0xa3, 0x67, 0xa4, 0x5b, 0x17, 0xb2, + 0x09, 0xb1, 0x95, 0x82, 0x58, 0x65, 0xbc, 0xda, 0x51, 0xa4, 0xf9, 0x54, + 0x16, 0xd2, 0x4f, 0x10, 0xa5, 0x2f, 0x38, 0x92, 0x16, 0xb6, 0xd4, 0xbf, + 0xa7, 0xf2, 0xaf, 0xc8, 0x68, 0xcd, 0x47, 0x89, 0x5b, 0x6d, 0xbc, 0x68, + 0xa0, 0x1a, 0xc3, 0xe5, 0x7b, 0x99, 0x0d, 0x06, 0xac, 0xa3, 0xa1, 0xcf, + 0x6f, 0x11, 0x86, 0x9b, 0x56, 0xe0, 0x7d, 0x0f, 0xde, 0xbd, 0x31, 0x23, + 0x22, 0x73, 0x04, 0x60, 0x41, 0x1e, 0xfe, 0xf5, 0xbe, 0xe4, 0xec, 0x4f, + 0x14, 0x0c, 0x1f, 0x7f, 0xde, 0x11, 0xb4, 0x8a, 0x76, 0x09, 0x83, 0x54, + 0xd9, 0x68, 0x20, 0xb1, 0xe6, 0x2a, 0x33, 0x36, 0x0a, 0x94, 0x11, 0x38, + 0x2b, 0x7c, 0x54, 0x92, 0xfe, 0x74, 0xe2, 0x66, 0x0c, 0xb4, 0x72, 0xd8, + 0x3f, 0x57, 0xfc, 0x8b, 0xe5, 0x4f, 0x70, 0x63, 0x27, 0x62, 0x8c, 0x66, + 0x9a, 0x3b, 0x9c, 0xdd, 0xc8, 0x1c, 0x8e, 0x46, 0x47, 0x36, 0x5f, 0x36, + 0x89, 0xf3, 0xe7, 0x73, 0x7e, 0xc1, 0x7b, 0x3e, 0x16, 0x6f, 0x76, 0x40, + 0x24, 0x80, 0x07, 0x7e, 0x45, 0x66, 0xf7, 0x51, 0xde, 0xb1, 0x51, 0x32, + 0xb4, 0x9e, 0xf6, 0xc0, 0x2c, 0x22, 0x58, 0xc2, 0xb3, 0xf0, 0xd2, 0x6c, + 0xda, 0xa1, 0x54, 0x9c, 0x72, 0xc7, 0x83, 0x31, 0x67, 0x13, 0x37, 0x2b, + 0x58, 0x3e, 0xa7, 0xdc, 0x16, 0x6f, 0xff, 0x70, 0x1a, 0xd5, 0x2b, 0xba, + 0x02, 0x41, 0x8a, 0xd1, 0x5a, 0x19, 0xb2, 0x9a, 0x28, 0xbd, 0x9c, 0x0e, + 0xed, 0x8b, 0xa1, 0xa4, 0x67, 0x83, 0x49, 0x12, 0xbb, 0x73, 0xd4, 0x8d, + 0x89, 0x48, 0x9b, 0xe6, 0xfe, 0xc9, 0x4a, 0x41, 0xca, 0x8c, 0x43, 0xb0, + 0x0d, 0x12, 0x42, 0x58, 0x93, 0xd2, 0x14, 0x35, 0x07, 0x46, 0x47, 0x46, + 0x34, 0xfd, 0x60, 0x4b, 0x23, 0xe8, 0xdc, 0x95, 0x19, 0x95, 0x04, 0x52, + 0xb6, 0xca, 0x89, 0x84, 0xdf, 0x1c, 0x71, 0x4d, 0xd5, 0x58, 0x62, 0x14, + 0x62, 0x05, 0x1f, 0xb0, 0x0e, 0xf1, 0x32, 0x69, 0x9d, 0x65, 0x2f, 0xf1, + 0x18, 0x95, 0xbb, 0x4b, 0xff, 0x19, 0x52, 0xf0, 0xc1, 0x49, 0x18, 0xe3, + 0x84, 0x08, 0x9a, 0xf8, 0x69, 0xfe, 0x13, 0xa6, 0x20, 0x79, 0xaf, 0x22, + 0xb3, 0x32, 0x0f, 0x3d, 0x61, 0xb4, 0xd3, 0xf3, 0xb5, 0xd8, 0x6c, 0x50, + 0x0f, 0x82, 0x3a, 0x4b, 0x84, 0x85, 0xa7, 0x8f, 0xc5, 0x21, 0xd2, 0x7e, + 0x84, 0xbe, 0x2a, 0xec, 0xb5, 0x88, 0x05, 0xb2, 0x21, 0x13, 0x0b, 0x6b, + 0x1a, 0x95, 0xb3, 0xd9, 0xe5, 0x24, 0xde, 0xe6, 0x18, 0x27, 0x97, 0x71, + 0x31, 0xb4, 0x15, 0x59, 0xfc, 0x25, 0x20, 0x5f, 0xef, 0xea, 0x96, 0x9f, + 0x54, 0x79, 0x06, 0xd3, 0x19, 0xe0, 0xcd, 0x64, 0x05, 0x4a, 0x17, 0x14, + 0xa3, 0x11, 0x94, 0xc7, 0xc1, 0x94, 0x34, 0x85, 0xc3, 0xa6, 0x3f, 0xf9, + 0xd9, 0x9f, 0xc8, 0xfe, 0x2c, 0xb2, 0x2c, 0x70, 0x4a, 0x7d, 0xef, 0xb8, + 0x0c, 0xfd, 0x85, 0x77, 0x5d, 0x5e, 0x2c, 0x20, 0x50, 0x69, 0x8d, 0xdc, + 0x6d, 0xa7, 0x01, 0x30, 0xcf, 0x32, 0x7a, 0x04, 0x18, 0xbc, 0x31, 0x65, + 0x36, 0x29, 0x8e, 0xc3, 0xbd, 0xc8, 0xb6, 0xc4, 0xc1, 0x48, 0xc8, 0x6f, + 0x4e, 0x27, 0xc2, 0xb8, 0xff, 0x19, 0x8b, 0x5a, 0x04, 0x8b, 0x66, 0x57, + 0x11, 0x73, 0xd6, 0xeb, 0xf9, 0xb1, 0x81, 0x42, 0xb0, 0x35, 0xb9, 0x71, + 0x40, 0xae, 0xca, 0x46, 0xe4, 0xfe, 0x75, 0x88, 0xb9, 0x8e, 0xf1, 0x85, + 0x77, 0x7b, 0x33, 0x0c, 0x33, 0x55, 0x2b, 0xaf, 0x85, 0x3b, 0x97, 0xac, + 0x61, 0x76, 0x92, 0xda, 0xa5, 0xc7, 0xda, 0xfa, 0x29, 0xc3, 0x4a, 0x6f, + 0x72, 0x58, 0x6b, 0x77, 0x13, 0x25, 0xae, 0x8e, 0x46, 0x34, 0xc6, 0x12, + 0x93, 0x8b, 0x51, 0x6b, 0xab, 0x2f, 0x42, 0xc2, 0x38, 0x2a, 0xc8, 0x32, + 0x63, 0x99, 0xaa, 0x4c, 0xbc, 0xec, 0x48, 0xe5, 0x61, 0xc8, 0x21, 0x02, + 0x89, 0xff, 0x5a, 0x62, 0xea, 0x06, 0xd2, 0x6e, 0xcb, 0x0d, 0xeb, 0x86, + 0x37, 0x1f, 0x09, 0x65, 0x30, 0xb9, 0x31, 0x99, 0x26, 0x22, 0x6b, 0x86, + 0x09, 0xf6, 0x87, 0xdb, 0x27, 0x54, 0xa7, 0x9b, 0xf6, 0x4c, 0xb7, 0xe8, + 0x19, 0x6f, 0x96, 0x5c, 0x7e, 0x0c, 0x15, 0x9e, 0x4a, 0xd7, 0x3a, 0x3a, + 0xe2, 0xcc, 0x38, 0xe7, 0x16, 0x0c, 0x2b, 0xe7, 0x6e, 0xff, 0x6f, 0x64, + 0x26, 0x26, 0x08, 0x96, 0x19, 0x43, 0x75, 0x99, 0x19, 0x1d, 0xfa, 0xa2, + 0x18, 0xd6, 0xdb, 0xc1, 0x41, 0xae, 0xaf, 0xb4, 0x97, 0x49, 0x49, 0xca, + 0x1a, 0xf5, 0x1d, 0x57, 0x6a, 0x9b, 0x88, 0x0d, 0xf4, 0xe4, 0xa1, 0xb9, + 0xb8, 0x9d, 0x10, 0x2d, 0x2d, 0x28, 0x8e, 0x52, 0x7c, 0x5e, 0x30, 0xf4, + 0xfe, 0x2e, 0x49, 0xff, 0x5a, 0xa9, 0x05, 0xf8, 0x99, 0x7d, 0x73, 0x80, + 0x6c, 0x26, 0x5e, 0x23, 0xcb, 0xad, 0x64, 0xbd, 0xbb, 0xfb, 0xb8, 0x6a, + 0x57, 0xc5, 0x73, 0x8b, 0xac, 0x03, 0xad, 0xe3, 0xf2, 0x12, 0xfb, 0x31, + 0x8f, 0x09, 0x52, 0x34, 0x8d, 0xab, 0x59, 0xe2, 0x56, 0x41, 0xfc, 0x89, + 0x5f, 0x58, 0x6e, 0xd5, 0xa1, 0xf7, 0xd9, 0xd5, 0x83, 0xb4, 0x3a, 0xf9, + 0xab, 0x86, 0x05, 0xa3, 0xb6, 0xe7, 0x02, 0x29, 0x85, 0x14, 0x4a, 0x81, + 0x21, 0x9e, 0xb2, 0xcd, 0xe3, 0xe6, 0xa8, 0xc5, 0x16, 0x69, 0xe2, 0x99, + 0x6d, 0x12, 0x22, 0x0b, 0x3c, 0xa8, 0xfa, 0x39, 0x3b, 0x3d, 0xf3, 0xfa, + 0x24, 0x60, 0x4d, 0x59, 0xe0, 0x6d, 0x99, 0x5a, 0x8e, 0x12, 0xd9, 0x34, + 0x6d, 0x4e, 0x45, 0x36, 0xcc, 0xa7, 0x05, 0xae, 0x7b, 0xfd, 0xeb, 0xcf, + 0xb1, 0xa9, 0xca, 0xce, 0x73, 0xb4, 0x4b, 0x48, 0x21, 0xca, 0x74, 0x79, + 0x31, 0xb6, 0xda, 0x13, 0xdc, 0xa4, 0x40, 0x4d, 0x70, 0x88, 0xed, 0xa2, + 0xc0, 0x36, 0x92, 0xf7, 0x40, 0x23, 0x19, 0x58, 0x30, 0xca, 0xd6, 0xde, + 0xb3, 0xef, 0x6b, 0xeb, 0x43, 0xa8, 0x18, 0x92, 0x27, 0xa5, 0xb0, 0xb8, + 0x36, 0x3e, 0xc8, 0x20, 0x4d, 0xc0, 0xa0, 0x78, 0x24, 0x45, 0x8a, 0x40, + 0xa3, 0x3b, 0x95, 0xd2, 0xed, 0x23, 0x47, 0xde, 0x8a, 0x19, 0x82, 0x82, + 0x62, 0x62, 0x19, 0xa7, 0xca, 0x90, 0xbc, 0x26, 0xd9, 0xce, 0x83, 0xd9, + 0x37, 0x11, 0xef, 0x18, 0xe8, 0x42, 0x23, 0x0b, 0xfb, 0x52, 0x87, 0x87, + 0x13, 0xe0, 0xc3, 0xa0, 0xb9, 0x04, 0x09, 0x3c, 0x41, 0x6a, 0x06, 0xbf, + 0xf2, 0x25, 0x5a, 0x17, 0x88, 0x93, 0x79, 0x52, 0x38, 0xd9, 0x73, 0x3f, + 0xfe, 0x69, 0xd9, 0xd3, 0xc9, 0x47, 0x1a, 0x14, 0x86, 0xdc, 0x17, 0x25, + 0x13, 0x9e, 0x2a, 0xad, 0x64, 0x05, 0x5e, 0xff, 0x0d, 0xee, 0x73, 0x7d, + 0xeb, 0x3d, 0xf3, 0x40, 0x62, 0x1c, 0x8e, 0xc7, 0x60, 0x43, 0xd8, 0x81, + 0x30, 0x13, 0xe4, 0x64, 0x74, 0x04, 0x56, 0x2c, 0xfd, 0xa9, 0x01, 0x7d, + 0x42, 0x85, 0xec, 0x62, 0xf8, 0x51, 0x0f, 0xad, 0xae, 0xf3, 0x66, 0x6c, + 0x0a, 0x8c, 0x97, 0x47, 0xb3, 0xad, 0x0a, 0x7b, 0x00, 0x47, 0x44, 0x11, + 0x77, 0xcd, 0x55, 0xc0, 0x85, 0xb5, 0xd0, 0xb0, 0x33, 0xc5, 0x32, 0xe9, + 0x5e, 0x52, 0xc7, 0xd2, 0x42, 0xc1, 0xd6, 0xed, 0x54, 0xac, 0x1f, 0x7c, + 0x7a, 0xf0, 0x8f, 0x20, 0x3a, 0x77, 0xff, 0xcd, 0x14, 0xac, 0x90, 0xbf, + 0x9f, 0xd8, 0x25, 0x2e, 0x00, 0x2f, 0xae, 0x9e, 0xb9, 0x12, 0xc5, 0x85, + 0x11, 0x05, 0x74, 0xeb, 0x3a, 0x69, 0x2c, 0x09, 0x7c, 0x7f, 0x05, 0xa7, + 0x24, 0x3e, 0xf6, 0x25, 0xe3, 0x09, 0xc3, 0x0c, 0x39, 0x72, 0x06, 0x5c, + 0x8d, 0x64, 0x3e, 0xde, 0xf0, 0xed, 0xcb, 0x46, 0xee, 0x8e, 0x6c, 0x34, + 0xac, 0x2b, 0x55, 0xe7, 0xaf, 0x69, 0xfc, 0x23, 0xc3, 0x47, 0x90, 0x4b, + 0x0c, 0xab, 0x06, 0x07, 0xed, 0x97, 0xeb, 0x36, 0x0f, 0x5c, 0x5f, 0xaa, + 0x67, 0x3c, 0xc5, 0xca, 0x4a, 0x45, 0x2d, 0x2a, 0x14, 0xc5, 0x87, 0x5e, + 0xce, 0x39, 0x3d, 0x62, 0xbd, 0x9a, 0xf1, 0xea, 0x9a, 0xdb, 0x15, 0x0f, + 0x91, 0x81, 0xff, 0x83, 0xe7, 0x42, 0xd5, 0xf2, 0x2e, 0x27, 0xf0, 0x54, + 0x7e, 0xa4, 0x87, 0xf4, 0xb9, 0x17, 0xbd, 0xe1, 0x7e, 0xd1, 0xfa, 0xcb, + 0x93, 0x92, 0x5e, 0xf2, 0x4f, 0xd3, 0xc8, 0x2d, 0xb2, 0x4c, 0xaf, 0x7a, + 0xe3, 0x15, 0x9d, 0x89, 0x3f, 0x2f, 0x16, 0x77, 0x28, 0x51, 0xe8, 0x3a, + 0xfa, 0x19, 0xc4, 0xc7, 0xbd, 0xca, 0x75, 0x37, 0x35, 0x2b, 0xb6, 0xf7, + 0xd2, 0x3d, 0xbc, 0xe4, 0x08, 0x03, 0x02, 0x1f, 0xa5, 0x71, 0x4c, 0x70, + 0x08, 0x34, 0x7f, 0x00, 0x4b, 0x49, 0x6e, 0x57, 0x6c, 0x59, 0xf1, 0x67, + 0xbe, 0x03, 0x21, 0xe4, 0xdb, 0xfc, 0x7c, 0xdc, 0x9a, 0xdd, 0x53, 0x40, + 0x57, 0x6a, 0xc7, 0x83, 0x06, 0x44, 0xed, 0x72, 0xc2, 0x89, 0xc1, 0x58, + 0xe5, 0x5b, 0x00, 0x93, 0xfa, 0x4f, 0x84, 0x64, 0xe1, 0xba, 0x15, 0x66, + 0x6f, 0x91, 0x9a, 0x1e, 0x37, 0x3b, 0xdb, 0x58, 0x83, 0x1c, 0x6a, 0x31, + 0x4c, 0x32, 0xef, 0xee, 0xd4, 0x6e, 0xfe, 0x7e, 0xbd, 0xdf, 0x82, 0x80, + 0x0a, 0x23, 0xa9, 0x08, 0xc6, 0x34, 0x48, 0xe7, 0x2b, 0xd7, 0x89, 0xc1, + 0x9b, 0xd0, 0x02, 0x29, 0xe2, 0x2d, 0x1b, 0x11, 0x69, 0x55, 0x30, 0x4f, + 0x2c, 0xe9, 0x2b, 0xf3, 0x8a, 0xd6, 0xbb, 0xe0, 0x80, 0x3f, 0xc2, 0xac, + 0xdd, 0x1d, 0x73, 0x25, 0x9d, 0xbc, 0x11, 0x8c, 0xa4, 0xf1, 0x9e, 0x6d, + 0xac, 0x1c, 0x07, 0xbc, 0x1d, 0x9b, 0x36, 0xfc, 0xe7, 0x46, 0xd5, 0xe2, + 0x7f, 0xbf, 0x83, 0xe1, 0x70, 0xe6, 0xa1, 0x16, 0x94, 0xac, 0xda, 0x70, + 0x3d, 0x04, 0x80, 0x17, 0x95, 0x10, 0xe2, 0x8b, 0xe4, 0x8d, 0xf6, 0x13, + 0xe3, 0xe8, 0x24, 0xd3, 0x7e, 0x44, 0x96, 0xd1, 0xf3, 0xe0, 0x7e, 0x24, + 0x6d, 0xc7, 0x3c, 0x30, 0xff, 0x03, 0x7f, 0xe0, 0xfb, 0xe3, 0xaf, 0xaa, + 0x53, 0x85, 0x98, 0xff, 0xd8, 0x31, 0x98, 0xf6, 0x2c, 0x86, 0x8d, 0x1f, + 0x7d, 0xd8, 0x6c, 0x17, 0x22, 0x6f, 0xd8, 0x73, 0x04, 0x3b, 0x62, 0x96, + 0x82, 0x48, 0x62, 0x23, 0xb9, 0x76, 0xed, 0xd5, 0x70, 0x57, 0x07, 0x82, + 0x77, 0x28, 0x23, 0xd2, 0xe0, 0x65, 0x04, 0xb1, 0x60, 0x3f, 0x75, 0xcb, + 0x8a, 0x0f, 0x0d, 0xfa, 0x87, 0xa9, 0x0d, 0x0e, 0xc1, 0x5b, 0x20, 0x4d, + 0x39, 0xc6, 0x30, 0x37, 0x6a, 0x6c, 0x42, 0x91, 0x24, 0x54, 0xe0, 0xeb, + 0x17, 0x23, 0x43, 0x35, 0x7b, 0x73, 0x18, 0x6c, 0x75, 0xa4, 0x1f, 0x75, + 0xc0, 0x2a, 0xf2, 0x52, 0x1f, 0xdf, 0xbd, 0x2e, 0x3d, 0xbf, 0x00, 0x5f, + 0xc9, 0x01, 0x8a, 0xc3, 0x96, 0x9c, 0x78, 0xac, 0x90, 0xba, 0x0a, 0x10, + 0x0f, 0x06, 0x83, 0xd4, 0x6f, 0x0a, 0xc7, 0x1d, 0x14, 0x21, 0x27, 0xe6, + 0xc8, 0x0c, 0xc6, 0x67, 0x77, 0xa8, 0x87, 0x94, 0x1b, 0xb0, 0x0b, 0xee, + 0xf8, 0xb8, 0xea, 0x64, 0x7f, 0xd6, 0x7f, 0xfd, 0x20, 0x77, 0xe1, 0x97, + 0xae, 0xa1, 0x70, 0xca, 0x15, 0x64, 0xcc, 0x25, 0xc2, 0x20, 0x40, 0x19, + 0x12, 0x32, 0x0d, 0xb1, 0x64, 0xaf, 0x33, 0x9d, 0xd3, 0x5b, 0xea, 0x41, + 0x21, 0xe4, 0xd1, 0xca, 0x11, 0x5c, 0x45, 0x86, 0xdb, 0xab, 0x39, 0xa5, + 0x85, 0xaa, 0xa5, 0x60, 0x5e, 0x6d, 0xf5, 0x1e, 0xab, 0x2c, 0xc7, 0x5f, + 0x34, 0x6d, 0x9d, 0xab, 0x3b, 0x2e, 0x36, 0xd1, 0xb1, 0xca, 0xec, 0xc4, + 0x9f, 0x9b, 0x8c, 0xd3, 0x70, 0xe9, 0x34, 0xcc, 0x07, 0x89, 0xa6, 0x09, + 0xae, 0x31, 0xd4, 0x83, 0x80, 0xfd, 0x5e, 0xa4, 0x17, 0x99, 0xef, 0x2c, + 0xce, 0xb0, 0xf4, 0x23, 0x4f, 0x4f, 0x74, 0xcf, 0xf6, 0x43, 0xf0, 0x71, + 0x28, 0x57, 0x08, 0xc7, 0x35, 0xb9, 0x51, 0xb3, 0x21, 0x6a, 0x3c, 0xec, + 0xf3, 0xb2, 0x56, 0xef, 0x01, 0x13, 0xbf, 0x62, 0x7d, 0xae, 0xee, 0x3a, + 0x6c, 0x9c, 0x49, 0xea, 0xb9, 0x4c, 0xe4, 0xef, 0x37, 0x49, 0xd0, 0x36, + 0x27, 0xea, 0xdc, 0x65, 0xa8, 0xf2, 0x83, 0xac, 0xa7, 0xe6, 0x10, 0x37, + 0xc2, 0x61, 0xf8, 0x3b, 0x44, 0xa7, 0xc3, 0x14, 0x39, 0x05, 0x6c, 0xf5, + 0x9f, 0x3b, 0xc9, 0x0b, 0x7f, 0xf4, 0x62, 0x79, 0xc6, 0x84, 0xbf, 0x08, + 0x39, 0xc0, 0xe2, 0x63, 0xbe, 0x98, 0x25, 0xdb, 0xe7, 0x6a, 0xd3, 0xe1, + 0xfb, 0xdd, 0x7c, 0xf1, 0xea, 0x39, 0x33, 0xf5, 0x5b, 0x43, 0x87, 0xf0, + 0x43, 0xdc, 0x04, 0x02, 0x6e, 0xc9, 0xcd, 0xd1, 0x44, 0x09, 0x8e, 0x59, + 0x9b, 0x5a, 0x26, 0x18, 0x55, 0x25, 0x85, 0xbb, 0x95, 0x22, 0xaf, 0xeb, + 0xc8, 0xe1, 0xbc, 0x87, 0x55, 0x4b, 0x75, 0xe6, 0x3b, 0x35, 0x55, 0xa2, + 0x05, 0x53, 0x4a, 0x18, 0x6e, 0x4a, 0x91, 0x73, 0x2f, 0xaf, 0x15, 0xac, + 0xcd, 0x32, 0xbf, 0xbc, 0xd7, 0xda, 0xe5, 0x9f, 0x41, 0xbd, 0xc6, 0xc8, + 0x6e, 0xc2, 0xbd, 0xf8, 0x84, 0x25, 0x9b, 0xf0, 0x62, 0x20, 0x21, 0x5e, + 0x9f, 0x5f, 0xdd, 0xb5, 0x49, 0x1f, 0xcb, 0x8f, 0xa1, 0x64, 0x23, 0x0a, + 0xa7, 0x66, 0x7b, 0x71, 0x10, 0x7e, 0x6f, 0x28, 0x16, 0x45, 0x7a, 0xdc, + 0x80, 0xdc, 0x9b, 0x35, 0xa5, 0x2f, 0xcf, 0x22, 0x8c, 0xe3, 0xb3, 0x8b, + 0xa1, 0xf5, 0x3f, 0xcb, 0x3a, 0xd1, 0xb9, 0xee, 0xd5, 0x5f, 0xc8, 0x19, + 0x00, 0x38, 0x9c, 0x74, 0x32, 0x8a, 0xdc, 0x2e, 0x37, 0x0d, 0x63, 0x71, + 0x45, 0xa2, 0x0f, 0xc7, 0xee, 0x25, 0xe2, 0xe0, 0x61, 0xd5, 0x96, 0xa5, + 0x4a, 0x7d, 0xc0, 0x8c, 0x6e, 0x12, 0x0a, 0x2e, 0xfb, 0x55, 0x81, 0x9c, + 0x0a, 0xb8, 0xe1, 0x48, 0x70, 0x3c, 0xa5, 0x41, 0xd7, 0x1b, 0x35, 0xc3, + 0x9b, 0x99, 0x8a, 0x0c, 0xc8, 0x07, 0xba, 0x10, 0x44, 0x20, 0xa2, 0x5b, + 0xc6, 0x63, 0x0b, 0x7a, 0x66, 0x7e, 0x1a, 0x9f, 0x2c, 0x26, 0x44, 0x27, + 0x29, 0x66, 0xfc, 0x31, 0x60, 0x58, 0xd1, 0x5b, 0xae, 0x39, 0xb6, 0x10, + 0xf1, 0xe9, 0x68, 0xba, 0xd7, 0xbf, 0x74, 0x92, 0x3b, 0xc8, 0x9c, 0x73, + 0x34, 0xd6, 0x79, 0x50, 0x21, 0x73, 0x41, 0x2b, 0xa6, 0xe0, 0x64, 0xd0, + 0xa4, 0x74, 0x54, 0x56, 0x85, 0xe8, 0x7c, 0x72, 0x69, 0x8c, 0x62, 0x7d, + 0x19, 0xfa, 0xde, 0x89, 0xe1, 0x82, 0x09, 0x6f, 0xc0, 0x57, 0xe4, 0x24, + 0xa6, 0x27, 0x5a, 0xbc, 0x61, 0x7b, 0xc4, 0x04, 0xe8, 0xfc, 0x0e, 0x76, + 0x4a, 0x75, 0x14, 0x9e, 0x95, 0x38, 0x74, 0xae, 0xc9, 0x90, 0xdc, 0xe4, + 0x6e, 0xf7, 0xe5, 0x1a, 0x05, 0xaf, 0x9f, 0xc9, 0xc1, 0x5b, 0x72, 0x90, + 0x6d, 0x64, 0x2f, 0x80, 0x92, 0xf3, 0xdf, 0x83, 0xa4, 0x91, 0xed, 0xcd, + 0x85, 0x0f, 0x27, 0x74, 0xc7, 0x86, 0x76, 0x3d, 0xfd, 0x77, 0x82, 0xc8, + 0xa4, 0xd8, 0x8f, 0x5a, 0x89, 0xa4, 0xac, 0xa9, 0x21, 0xae, 0x54, 0xd2, + 0x46, 0xd8, 0x1c, 0xbb, 0x3b, 0x90, 0x92, 0xc6, 0x26, 0xbb, 0xf2, 0xfa, + 0x69, 0xc5, 0xc9, 0x1c, 0x68, 0x59, 0xc1, 0x43, 0xfe, 0x1d, 0xed, 0x37, + 0xee, 0xac, 0xb1, 0x31, 0xa2, 0xb9, 0x0f, 0xe2, 0x7c, 0x5d, 0xd8, 0x32, + 0x17, 0xd1, 0xf6, 0x20, 0xf4, 0x75, 0xa1, 0xcc, 0xe7, 0x38, 0x2d, 0x9e, + 0x8a, 0xb2, 0x4c, 0x99, 0xab, 0xed, 0xd1, 0xce, 0xc8, 0x83, 0x48, 0x37, + 0xbd, 0x22, 0x39, 0xf9, 0xeb, 0x3f, 0x6a, 0x3b, 0xb5, 0xfa, 0x27, 0x7d, + 0xc0, 0x36, 0x2d, 0x3a, 0x04, 0x01, 0x64, 0xf4, 0x38, 0xd8, 0x1b, 0x63, + 0xc5, 0x3a, 0x28, 0x84, 0x59, 0x07, 0xe5, 0xc5, 0xd2, 0xd5, 0xe7, 0x91, + 0x84, 0x43, 0x7f, 0xa8, 0xeb, 0xc5, 0x94, 0x2a, 0xb3, 0x15, 0x0d, 0x28, + 0xf3, 0x37, 0xdb, 0xc7, 0x80, 0x8f, 0x3d, 0x43, 0x5b, 0xc7, 0x85, 0x0b, + 0x79, 0x1e, 0x2c, 0x7c, 0xbe, 0x28, 0x1d, 0x5d, 0x15, 0x90, 0x48, 0x3a, + 0x64, 0xf3, 0x21, 0x00, 0xf8, 0x2d, 0x0e, 0xb7, 0x6d, 0x77, 0x06, 0x27, + 0x7d, 0xe4, 0x0d, 0x6e, 0x15, 0x9f, 0x28, 0x88, 0x93, 0xc8, 0x17, 0xd2, + 0x9b, 0xc7, 0x0c, 0x6a, 0x6a, 0x63, 0x90, 0xd2, 0x96, 0xfd, 0x91, 0xeb, + 0xfd, 0x14, 0x92, 0xb9, 0x50, 0x47, 0x86, 0xe3, 0x58, 0xf9, 0x97, 0xcb, + 0x80, 0xbc, 0xc7, 0x5e, 0xbf, 0x94, 0x4c, 0x2d, 0x6f, 0x13, 0x50, 0xd3, + 0x25, 0x58, 0x81, 0x0d, 0xcc, 0x4f, 0x0d, 0x28, 0x4d, 0x82, 0xc0, 0x16, + 0x42, 0x18, 0x58, 0x69, 0xa3, 0x42, 0xd3, 0x20, 0x99, 0x35, 0xb7, 0x9a, + 0xd4, 0x16, 0x15, 0xdb, 0xeb, 0x80, 0xfc, 0xdb, 0x06, 0x23, 0x60, 0xc9, + 0xc5, 0xbb, 0xbd, 0x4f, 0xca, 0xff, 0x9b, 0x80, 0xae, 0x8c, 0xeb, 0x0c, + 0x04, 0xc0, 0xfb, 0x20, 0x28, 0x68, 0x46, 0xa9, 0xcf, 0xb2, 0xec, 0x31, + 0xb1, 0x4c, 0xca, 0x06, 0x03, 0x38, 0x99, 0x9f, 0x9a, 0xb3, 0x40, 0x56, + 0xec, 0x9d, 0x11, 0xc0, 0x77, 0x11, 0x5a, 0xa1, 0x6b, 0x51, 0x70, 0x00, + 0x5c, 0x42, 0x13, 0xe9, 0xc4, 0x94, 0x4e, 0x18, 0xe7, 0x3a, 0xd5, 0xae, + 0x2d, 0x0b, 0x3d, 0x4f, 0xcd, 0xc7, 0xd6, 0x14, 0x05, 0x49, 0xea, 0x09, + 0x99, 0x0e, 0x14, 0xd6, 0x39, 0x20, 0xbc, 0x3e, 0xb9, 0x8f, 0xee, 0x0c, + 0xd4, 0x12, 0x36, 0x7e, 0x71, 0x2b, 0x05, 0x74, 0x2b, 0x77, 0x36, 0x8b, + 0x2c, 0xc9, 0xe3, 0x43, 0xbc, 0x63, 0xf3, 0x05, 0xce, 0xd3, 0x35, 0x4f, + 0xb7, 0x91, 0x39, 0x4a, 0x1d, 0x10, 0x87, 0xf3, 0xdc, 0x67, 0xa5, 0xa6, + 0x0f, 0x04, 0xd7, 0x7e, 0x1c, 0xc8, 0x26, 0x07, 0xc8, 0xb3, 0x81, 0x28, + 0xe4, 0x62, 0x99, 0x48, 0x89, 0xad, 0x1a, 0x7b, 0x17, 0xf4, 0x26, 0xc4, + 0x2a, 0xa2, 0xd5, 0xe3, 0xa8, 0x9a, 0xf0, 0x07, 0x28, 0x67, 0xa6, 0x0b, + 0x6d, 0x20, 0x8a, 0x7f, 0xd1, 0x18, 0x1c, 0x1d, 0x8b, 0xcf, 0x18, 0xbc, + 0x5b, 0x97, 0x94, 0x03, 0xd1, 0xe0, 0xe5, 0x82, 0x6a, 0x87, 0xa2, 0x06, + 0xa1, 0x4d, 0xe4, 0x3c, 0xa0, 0x5d, 0xf8, 0xaa, 0x01, 0xcc, 0xa9, 0xf2, + 0x25, 0xbf, 0x17, 0x5b, 0xc2, 0xcb, 0x58, 0x61, 0xdf, 0xb6, 0x7b, 0x77, + 0xb0, 0x04, 0x10, 0x2e, 0x21, 0xfe, 0x46, 0x73, 0xe6, 0xc8, 0x69, 0x88, + 0xc6, 0x5c, 0xb4, 0x31, 0x04, 0x95, 0x7c, 0xb5, 0x2d, 0x23, 0x45, 0x14, + 0x25, 0xea, 0xfc, 0xe7, 0xe8, 0x71, 0x7e, 0xd4, 0x4e, 0x06, 0x20, 0x9b, + 0xae, 0x4b, 0x20, 0x7e, 0x55, 0x0a, 0x7d, 0x92, 0xcc, 0xdf, 0xbf, 0xbe, + 0x57, 0x5e, 0x33, 0xba, 0xcf, 0x4b, 0x79, 0x47, 0x52, 0x53, 0x41, 0xa1, + 0x25, 0x4e, 0x9c, 0x12, 0x61, 0xcc, 0x40, 0x67, 0xda, 0xc7, 0x8b, 0x2d, + 0x18, 0xd6, 0x90, 0x08, 0x19, 0x05, 0x2c, 0x72, 0xc7, 0x67, 0xb8, 0x99, + 0x17, 0x3e, 0x23, 0x0a, 0xe8, 0x9a, 0x97, 0x54, 0xdf, 0x3a, 0x6c, 0x81, + 0x8c, 0xcd, 0xf7, 0x1a, 0x46, 0xf7, 0x4b, 0xfd, 0x76, 0x25, 0x03, 0x2a, + 0xe1, 0x67, 0xbc, 0x51, 0xfc, 0x2a, 0xd4, 0x68, 0xeb, 0x48, 0x4c, 0x11, + 0x1e, 0xfa, 0xbb, 0x4b, 0xe1, 0x75, 0x39, 0xb3, 0x78, 0x1b, 0x1a, 0x9a, + 0x08, 0xd9, 0xe8, 0xdc, 0xf2, 0xd1, 0x0c, 0x65, 0x65, 0x7a, 0x67, 0x1c, + 0x5b, 0x45, 0xd7, 0x60, 0x8d, 0xdf, 0x41, 0x37, 0x4e, 0x9d, 0x26, 0xc3, + 0x45, 0xe7, 0x29, 0xc4, 0x66, 0x7c, 0xb1, 0x90, 0x5f, 0xea, 0xc8, 0xf4, + 0x99, 0x62, 0xb0, 0x20, 0x5a, 0x81, 0x09, 0x7d, 0x05, 0xb1, 0x54, 0x1f, + 0x39, 0xbe, 0x13, 0x91, 0x06, 0x18, 0x3c, 0x0f, 0x3c, 0xfe, 0x36, 0x6f, + 0x9a, 0x95, 0xa8, 0x7b, 0x78, 0x93, 0x91, 0xb5, 0x67, 0x1b, 0x21, 0x4b, + 0x77, 0x46, 0x53, 0x1c, 0xf2, 0x10, 0xce, 0xd9, 0xe6, 0x26, 0x1d, 0xd9, + 0xdc, 0x5e, 0x38, 0xc2, 0x6e, 0xab, 0x70, 0xf8, 0xeb, 0x80, 0xbe, 0x8c, + 0x7a, 0x05, 0x9e, 0x2d, 0x3d, 0x31, 0xb7, 0x1f, 0x6d, 0x94, 0xb7, 0xaa, + 0x6c, 0x65, 0x07, 0x68, 0x83, 0xc2, 0x6f, 0xd7, 0x3c, 0xd5, 0xd2, 0x31, + 0xed, 0xd1, 0x84, 0x59, 0x1f, 0xf7, 0x76, 0x6e, 0x68, 0x15, 0x29, 0xa0, + 0x5b, 0x02, 0xd2, 0x18, 0xcd, 0x64, 0x43, 0xff, 0x42, 0x71, 0x46, 0x15, + 0x64, 0x1a, 0xd4, 0x3d, 0xc3, 0xec, 0x78, 0x2a, 0x6d, 0xdb, 0x61, 0x1f, + 0x6b, 0x32, 0x2c, 0xfd, 0x82, 0x41, 0x48, 0xc5, 0x46, 0x15, 0x9f, 0x0a, + 0xec, 0xd4, 0x5b, 0x91, 0xf0, 0x57, 0x76, 0x1d, 0x7c, 0x62, 0xff, 0x2b, + 0xc8, 0x40, 0xd0, 0xe5, 0x65, 0x0b, 0x20, 0xa5, 0x6e, 0x87, 0xbe, 0xb6, + 0x03, 0xed, 0x19, 0xee, 0x10, 0xf1, 0xea, 0x49, 0x61, 0x67, 0x73, 0x08, + 0xde, 0xff, 0xcb, 0x61, 0x45, 0x4b, 0xef, 0xd1, 0xa7, 0xd1, 0x87, 0xad, + 0xf7, 0x02, 0xc8, 0xd6, 0x01, 0xd1, 0x9b, 0xf6, 0x3e, 0x9f, 0x85, 0x97, + 0x99, 0x2e, 0x58, 0x75, 0xe1, 0xa5, 0x06, 0x40, 0x5b, 0xfd, 0x52, 0xbd, + 0x33, 0x55, 0xb0, 0x40, 0x64, 0x89, 0xeb, 0x78, 0x16, 0xc4, 0xc7, 0x5f, + 0xf5, 0x93, 0x4b, 0x58, 0x73, 0x94, 0x09, 0x5e, 0x07, 0x1d, 0xb6, 0x3a, + 0xb9, 0x8b, 0x97, 0x9b, 0xc6, 0x19, 0xc6, 0xf8, 0x53, 0xb5, 0xb1, 0x05, + 0xf5, 0xf7, 0xf3, 0x0b, 0xf3, 0x02, 0xee, 0x3b, 0xc6, 0x5a, 0xe7, 0xdd, + 0x20, 0xa2, 0x93, 0x8d, 0xc9, 0x08, 0xfe, 0x6f, 0x4a, 0x28, 0xfb, 0x23, + 0xc7, 0x8d, 0xa0, 0x09, 0x69, 0x19, 0xd9, 0xa8, 0x8f, 0x65, 0xad, 0x7d, + 0x17, 0xdc, 0xd2, 0xfd, 0xfc, 0x0c, 0x17, 0xad, 0x4e, 0x59, 0x06, 0xb4, + 0x2f, 0xe2, 0xcd, 0xa1, 0x05, 0x4c, 0xbc, 0xcb, 0x12, 0xc0, 0xb3, 0x7e, + 0x9b, 0x8d, 0x1b, 0xb6, 0x12, 0xcc, 0xef, 0xf4, 0x99, 0x3c, 0x0e, 0x41, + 0x17, 0x51, 0xa4, 0x2e, 0x18, 0x48, 0xc9, 0x86, 0x51, 0xb8, 0x21, 0xac, + 0x8c, 0x76, 0xf7, 0x76, 0x70, 0x82, 0xc7, 0x7d, 0x83, 0x8f, 0xbd, 0x03, + 0x76, 0x55, 0xf3, 0xcd, 0x6d, 0xb7, 0x69, 0x9b, 0x26, 0xc6, 0x07, 0xb9, + 0x9a, 0xc9, 0x38, 0xa1, 0x1c, 0x59, 0xc7, 0x83, 0x7e, 0x55, 0x71, 0x9f, + 0x29, 0x0f, 0xfb, 0xc1, 0x0a, 0xf1, 0x0c, 0x65, 0xc3, 0x5a, 0x81, 0xdb, + 0xc3, 0x20, 0xab, 0x11, 0x38, 0x19, 0x29, 0x18, 0x1e, 0x1d, 0xf5, 0x28, + 0xe3, 0x48, 0x2b, 0x2b, 0x0d, 0x84, 0x87, 0xa3, 0x8d, 0xcd, 0x95, 0x92, + 0x5d, 0x80, 0x85, 0xc7, 0xf2, 0x1a, 0x1d, 0x32, 0x08, 0x30, 0x2a, 0x32, + 0x99, 0xa8, 0xcb, 0x53, 0xf1, 0xbd, 0x5d, 0x07, 0x08, 0x80, 0xa3, 0x94, + 0x85, 0x2c, 0x59, 0x8b, 0x39, 0x6c, 0x69, 0xed, 0x60, 0xfd, 0xbb, 0xc2, + 0x52, 0xce, 0x39, 0xf8, 0xca, 0x8b, 0x2c, 0xa2, 0xac, 0x09, 0x86, 0xd3, + 0xd7, 0x26, 0x1c, 0xcc, 0x65, 0xb9, 0x16, 0x74, 0xa9, 0x8d, 0xc6, 0x10, + 0xd7, 0xd1, 0x5f, 0x83, 0x43, 0xb4, 0x66, 0x9f, 0xae, 0xf3, 0x50, 0x4c, + 0x34, 0xcd, 0x72, 0x40, 0x80, 0xef, 0x02, 0xab, 0x36, 0x1e, 0x88, 0x59, + 0x37, 0x3d, 0xcf, 0x6f, 0xc3, 0xca, 0x71, 0x57, 0xc0, 0x25, 0x96, 0x4f, + 0xec, 0x82, 0x6a, 0x6c, 0xa9, 0xcc, 0x67, 0xd2, 0x48, 0xc7, 0xe9, 0x92, + 0xb2, 0x6e, 0x9a, 0x4c, 0xab, 0xd6, 0x90, 0x9f, 0x9c, 0x3c, 0x60, 0x61, + 0x6f, 0x61, 0xd8, 0xd0, 0x0a, 0x56, 0x28, 0xcf, 0xff, 0x46, 0x7e, 0x1f, + 0x83, 0xbe, 0x87, 0xbb, 0x10, 0xf2, 0x93, 0xbd, 0xbb, 0x1e, 0x0c, 0x7e, + 0xf5, 0xcf, 0xf3, 0x7c, 0xf0, 0x5a, 0xf2, 0xf3, 0xf8, 0x0e, 0xc4, 0xad, + 0x90, 0xd4, 0x85, 0x3c, 0xd8, 0x20, 0xa0, 0x5b, 0x32, 0xe0, 0xbd, 0xa6, + 0xf5, 0xd7, 0x94, 0x0c, 0x1c, 0xe2, 0xd2, 0x23, 0x4b, 0x16, 0xbf, 0x52, + 0x61, 0xd5, 0xec, 0x2a, 0xe6, 0x6a, 0xa5, 0xad, 0xeb, 0x05, 0xbc, 0xb5, + 0xf0, 0xb3, 0x19, 0x5e, 0x37, 0x1b, 0xc5, 0xe6, 0xe0, 0x36, 0x58, 0x01, + 0x6c, 0x84, 0x41, 0xe2, 0x52, 0x06, 0xf4, 0x38, 0x1d, 0xa6, 0x8e, 0x58, + 0xa7, 0x6b, 0xa8, 0xfa, 0x78, 0x2d, 0x2c, 0x8c, 0x65, 0x98, 0x1c, 0x03, + 0xcc, 0x01, 0xf6, 0xb5, 0xee, 0x5d, 0x92, 0xa5, 0x54, 0x3c, 0x70, 0x56, + 0x4a, 0x84, 0x4b, 0xad, 0xbf, 0x37, 0x06, 0x7e, 0x2b, 0x31, 0x9f, 0xc0, + 0x94, 0xd9, 0xcb, 0x9c, 0x46, 0xab, 0x53, 0x83, 0x53, 0xa4, 0x32, 0x29, + 0x2b, 0xc8, 0xf3, 0xb1, 0xc2, 0x57, 0x90, 0x6a, 0x47, 0x5b, 0x8b, 0xd7, + 0xff, 0x97, 0x8d, 0x0e, 0x00, 0x43, 0x13, 0xba, 0xb7, 0x93, 0xe8, 0xda, + 0x3f, 0x56, 0x2d, 0x19, 0x89, 0x99, 0x8f, 0x3a, 0x38, 0x11, 0x8a, 0xb6, + 0x61, 0x3f, 0xbb, 0xb7, 0xb6, 0xab, 0xb3, 0x67, 0x3d, 0x54, 0xb1, 0xf4, + 0x10, 0x55, 0x22, 0xdb, 0xc6, 0xfc, 0x9f, 0x77, 0xde, 0xfa, 0x27, 0x96, + 0x4e, 0x3c, 0x57, 0x27, 0x55, 0x54, 0x0c, 0x2a, 0xb9, 0x33, 0x28, 0xce, + 0x09, 0x34, 0x8c, 0xad, 0xf2, 0x5e, 0x07, 0xc4, 0xfb, 0x8c, 0xf6, 0x04, + 0xf2, 0x7e, 0xb4, 0x6d, 0xb4, 0x7d, 0xb2, 0x0e, 0x78, 0x99, 0x90, 0xf9, + 0x58, 0xa4, 0x23, 0x53, 0xff, 0x3e, 0xe0, 0x73, 0x9e, 0x21, 0x40, 0xe6, + 0x46, 0xe8, 0x38, 0xd8, 0x8d, 0x1f, 0xb6, 0x8a, 0x3d, 0xe3, 0x89, 0xb0, + 0xe2, 0xd0, 0xdf, 0x11, 0x03, 0x63, 0xd2, 0xf5, 0x60, 0xa2, 0xa1, 0x98, + 0x58, 0xe3, 0xc6, 0x98, 0x5f, 0x1c, 0xae, 0xf9, 0xa2, 0xab, 0x50, 0xd2, + 0xee, 0x94, 0x07, 0xc3, 0x69, 0x48, 0x95, 0x71, 0x07, 0xe8, 0x69, 0xff, + 0xb7, 0xfe, 0xe6, 0xd9, 0x46, 0x4d, 0xc1, 0x8d, 0x9e, 0xe9, 0x5b, 0xa1, + 0x45, 0x32, 0x8f, 0xbd, 0x9b, 0x18, 0x79, 0x57, 0x34, 0x54, 0x48, 0xf2, + 0x32, 0xdb, 0x5a, 0xfb, 0x7a, 0xac, 0xe2, 0xdc, 0xc0, 0x19, 0x43, 0x9b, + 0x69, 0x27, 0x54, 0x8c, 0x4b, 0x4e, 0x49, 0x44, 0x09, 0x32, 0x86, 0xdf, + 0x69, 0xb1, 0xf0, 0x39, 0x6b, 0x32, 0xad, 0xe9, 0x34, 0x2e, 0x0c, 0x4b, + 0xee, 0x52, 0xb0, 0x5a, 0x79, 0x03, 0x12, 0xfe, 0xec, 0x7e, 0xf0, 0x18, + 0xa7, 0x85, 0x01, 0x50, 0xdd, 0xd2, 0xbc, 0x5b, 0xea, 0x8b, 0xfc, 0x86, + 0xdf, 0x8a, 0x22, 0xed, 0x4b, 0x50, 0x25, 0xcf, 0x54, 0xc9, 0x1c, 0x6e, + 0x28, 0x42, 0xaa, 0xfd, 0x63, 0x98, 0x34, 0x77, 0x46, 0x0b, 0x0f, 0xb9, + 0xd8, 0xc4, 0x1e, 0x0e, 0xed, 0x17, 0xa3, 0x92, 0x7f, 0x4f, 0xe9, 0xb4, + 0x67, 0x70, 0xef, 0x67, 0x3d, 0x93, 0x2f, 0x66, 0x1f, 0xe2, 0x5a, 0x7f, + 0x55, 0x77, 0x9e, 0x1c, 0x60, 0xf6, 0x25, 0x8f, 0x8b, 0x71, 0xd6, 0xb3, + 0xa5, 0x1f, 0xc8, 0x91, 0xec, 0x0b, 0x1e, 0xcb, 0x69, 0x5a, 0xf3, 0x12, + 0x9a, 0x64, 0x6a, 0xcd, 0x54, 0x0b, 0xd1, 0x54, 0xfc, 0xa5, 0x97, 0xfd, + 0x59, 0x4a, 0xe8, 0xed, 0xdf, 0x29, 0x8f, 0xc7, 0x34, 0xfa, 0x44, 0xfa, + 0xad, 0xe6, 0x41, 0xbd, 0x53, 0xfe, 0xb2, 0xdf, 0x8a, 0x21, 0xd1, 0x6b, + 0x55, 0xb8, 0x38, 0x77, 0xb8, 0x2b, 0xa2, 0xf7, 0x55, 0x61, 0xc5, 0xce, + 0xd5, 0xc4, 0xe7, 0x7e, 0xe6, 0x04, 0x78, 0x35, 0xcf, 0xde, 0xcb, 0xfc, + 0x7b, 0xbd, 0xbf, 0xa7, 0xd2, 0x08, 0xeb, 0x01, 0x7d, 0x2a, 0xbd, 0x6a, + 0xe5, 0x37, 0x03, 0x73, 0x52, 0x5a, 0xfd, 0x97, 0x96, 0xec, 0x5b, 0xaf, + 0x2b, 0xa7, 0xd8, 0xc3, 0x5d, 0x5e, 0x6c, 0x7a, 0xa6, 0xb2, 0x21, 0xb3, + 0x27, 0x26, 0x2e, 0x3b, 0x4c, 0xb0, 0xa9, 0xa5, 0x1b, 0x69, 0x1f, 0xe6, + 0x13, 0x69, 0x31, 0xc8, 0x0e, 0xe2, 0x9c, 0x06, 0x5a, 0x45, 0x86, 0xbc, + 0x48, 0x22, 0x1a, 0xd9, 0x67, 0x9a, 0x8c, 0x54, 0x7d, 0x6f, 0xbd, 0x32, + 0x14, 0xce, 0x08, 0x2b, 0x41, 0x29, 0x69, 0xaa, 0xe6, 0x60, 0x4b, 0x30, + 0x59, 0x96, 0x60, 0x31, 0x4f, 0xa1, 0xc5, 0x87, 0xef, 0x7b, 0xc9, 0x42, + 0xe7, 0x6e, 0x8e, 0xdf, 0x10, 0xd1, 0x2e, 0x79, 0xa1, 0x3a, 0x0d, 0xc7, + 0xc1, 0x60, 0x77, 0x1f, 0xa7, 0xa4, 0xfd, 0x44, 0x04, 0xde, 0xf4, 0x38, + 0x41, 0x10, 0xe8, 0x88, 0x4a, 0xc5, 0x9a, 0xc6, 0xd2, 0x5b, 0xf1, 0x73, + 0x71, 0x8b, 0x25, 0xa4, 0xf5, 0x9f, 0x62, 0x9e, 0xc7, 0xd8, 0xa8, 0x74, + 0xe3, 0x3f, 0xe5, 0x7b, 0x34, 0x1b, 0xa7, 0x0e, 0x53, 0xa5, 0x63, 0x93, + 0xb3, 0x86, 0x67, 0xcd, 0xd1, 0x7b, 0xf7, 0xb2, 0x23, 0x72, 0x07, 0xf0, + 0xd0, 0xd3, 0xb8, 0x50, 0x7d, 0xbd, 0xeb, 0x3b, 0xaf, 0xcb, 0xef, 0x97, + 0x2b, 0x0c, 0xdc, 0x1f, 0x9d, 0x26, 0x52, 0x7d, 0x62, 0xe1, 0x60, 0x87, + 0x6c, 0x6a, 0xbe, 0x7e, 0xa5, 0x66, 0x15, 0x6e, 0xb8, 0x42, 0xbc, 0x3e, + 0x05, 0x70, 0xfd, 0x56, 0x51, 0xc0, 0x38, 0x89, 0x6c, 0xae, 0x72, 0xc6, + 0x50, 0x69, 0xc2, 0x6b, 0x00, 0x80, 0x7b, 0x87, 0x6a, 0xfc, 0x67, 0x58, + 0x3b, 0x05, 0x62, 0xf0, 0x79, 0x18, 0x97, 0x8b, 0xda, 0x35, 0x79, 0x9c, + 0xcf, 0xa8, 0x58, 0xfb, 0x7b, 0x5e, 0x18, 0x6e, 0x65, 0x94, 0xa1, 0x34, + 0x93, 0x0c, 0x35, 0xb2, 0x65, 0x50, 0x34, 0xd0, 0xbb, 0xf1, 0x79, 0x4a, + 0x41, 0x07, 0x77, 0x6e, 0xca, 0xff, 0x52, 0x54, 0x6d, 0x94, 0x18, 0x34, + 0xef, 0x68, 0x87, 0x1e, 0x21, 0x69, 0xee, 0x97, 0xab, 0xd6, 0xb0, 0x33, + 0xdf, 0x00, 0xf1, 0x70, 0x2f, 0x34, 0x29, 0xcb, 0xdd, 0x30, 0x63, 0x03, + 0x79, 0xff, 0xdc, 0x62, 0xbb, 0xeb, 0x0e, 0x03, 0xd6, 0xd1, 0xea, 0x3d, + 0x28, 0xa5, 0x59, 0xa0, 0xda, 0x3b, 0xf3, 0xf0, 0x22, 0xa4, 0xd2, 0x19, + 0x9d, 0xe6, 0x3b, 0x1a, 0x56, 0x4c, 0x0e, 0x90, 0x56, 0xe4, 0xd4, 0x31, + 0x3d, 0x28, 0x7f, 0x13, 0x02, 0xc4, 0x26, 0x77, 0x8a, 0x94, 0xec, 0x0a, + 0x8f, 0x91, 0x78, 0x95, 0xeb, 0x8a, 0x7e, 0x80, 0x8b, 0x0a, 0xa3, 0x3b, + 0x82, 0xd5, 0x08, 0xd4, 0x68, 0xa8, 0x2e, 0x22, 0x93, 0x1b, 0x51, 0x85, + 0x78, 0xcf, 0xff, 0x90, 0x0a, 0xfd, 0x31, 0x01, 0x60, 0x3a, 0x8f, 0x1c, + 0x5e, 0x67, 0x32, 0x97, 0x29, 0xc8, 0x0b, 0x9d, 0x05, 0x99, 0x37, 0x6b, + 0xf4, 0xb8, 0xd5, 0x7b, 0x09, 0xd2, 0xc2, 0x3f, 0x8f, 0xc1, 0xaf, 0xbb, + 0xef, 0x3a, 0x1f, 0xed, 0xb9, 0x22, 0xbf, 0x3c, 0x9c, 0x00, 0xfc, 0xbd, + 0x12, 0xba, 0x4b, 0xd2, 0xa7, 0x1c, 0x23, 0x69, 0x92, 0x7e, 0x31, 0x8b, + 0x8c, 0x42, 0xd7, 0x44, 0xcd, 0x8a, 0x96, 0xdb, 0xad, 0x16, 0x36, 0xe6, + 0xd2, 0x45, 0x8e, 0xf2, 0x46, 0xe7, 0xc3, 0x96, 0xdd, 0xe3, 0xac, 0x47, + 0x8c, 0xd2, 0xc6, 0xb8, 0x9d, 0x41, 0xa2, 0xaa, 0xd9, 0x48, 0xaf, 0x7b, + 0x20, 0x5f, 0x71, 0x5f, 0x00, 0x2f, 0x87, 0x98, 0x3b, 0x81, 0x85, 0xe2, + 0x4f, 0x4a, 0xfe, 0x1f, 0x6c, 0xe4, 0x0c, 0x6b, 0xb8, 0xb4, 0x08, 0x5c, + 0xd0, 0x42, 0x71, 0x76, 0x6c, 0xca, 0x16, 0x91, 0x38, 0x1d, 0xb8, 0x44, + 0xcd, 0xd1, 0xad, 0x17, 0x8b, 0x04, 0xc9, 0xb5, 0x5a, 0xf6, 0x9e, 0xde, + 0x5b, 0xe2, 0x9d, 0xe8, 0x21, 0x27, 0xf7, 0xba, 0xe3, 0x6c, 0x0e, 0x2c, + 0xb8, 0x51, 0xb8, 0x73, 0x2c, 0x52, 0xc1, 0x04, 0x1d, 0xab, 0x3d, 0x7d, + 0x3b, 0x15, 0x34, 0xeb, 0xfd, 0x0b, 0x51, 0x91, 0x76, 0x22, 0x58, 0x6d, + 0x49, 0x60, 0x6a, 0x6b, 0xa4, 0xe2, 0xd2, 0x98, 0xee, 0x99, 0x17, 0x96, + 0x61, 0xa5, 0x9d, 0x20, 0xd2, 0x0f, 0xd6, 0x1b, 0xae, 0xc6, 0x1c, 0xf5, + 0xaa, 0x38, 0xee, 0x04, 0xd5, 0x36, 0x25, 0x72, 0x76, 0x43, 0x49, 0xb9, + 0x5b, 0xd2, 0xcd, 0x79, 0x7a, 0x4d, 0x84, 0x3e, 0x10, 0x44, 0xac, 0xb5, + 0xe7, 0x4d, 0x8c, 0x20, 0x4d, 0xc2, 0x5a, 0xb7, 0x01, 0xf5, 0xdb, 0xc2, + 0xb3, 0x1c, 0xa8, 0x53, 0xdb, 0xb7, 0x9d, 0x72, 0x19, 0xad, 0x97, 0x3f, + 0xc4, 0x6f, 0xd7, 0x57, 0x5e, 0x29, 0xa7, 0x10, 0x16, 0xeb, 0x35, 0x2e, + 0x40, 0xf5, 0x04, 0x83, 0x36, 0x25, 0x97, 0xa5, 0x15, 0xf6, 0x70, 0xc0, + 0x2c, 0xdf, 0xa9, 0xf2, 0x00, 0x84, 0x02, 0x89, 0x2e, 0x92, 0x41, 0x47, + 0xe0, 0x1b, 0x50, 0xb3, 0x44, 0x61, 0xac, 0xe7, 0x9d, 0x25, 0xab, 0xbb, + 0x59, 0x9a, 0xd4, 0xae, 0x9f, 0xaf, 0xef, 0x08, 0x52, 0x6d, 0x5e, 0xc3, + 0xad, 0x9f, 0x8b, 0x1a, 0x79, 0x6a, 0xca, 0x00, 0x6b, 0xa4, 0x73, 0x3d, + 0xff, 0xe9, 0xe3, 0xbf, 0xeb, 0x44, 0x75, 0xdc, 0x3b, 0x8a, 0xa3, 0x3c, + 0x62, 0x2d, 0x66, 0xa1, 0x52, 0x61, 0x73, 0x09, 0xb4, 0x65, 0x04, 0x13, + 0x14, 0xc3, 0xc4, 0xf6, 0x01, 0xa9, 0x86, 0x15, 0xf4, 0x19, 0x50, 0x6e, + 0xae, 0x2a, 0x0c, 0x09, 0x60, 0x9e, 0x66, 0x4f, 0xac, 0x29, 0x5f, 0x43, + 0x30, 0x6f, 0xf1, 0x38, 0x47, 0x6c, 0x83, 0x1f, 0xfb, 0xeb, 0xfb, 0x8c, + 0xc0, 0x24, 0x47, 0x8b, 0x63, 0x4e, 0x7d, 0x5f, 0xc1, 0x26, 0x55, 0xf8, + 0x9c, 0xca, 0x7b, 0x14, 0x42, 0xc2, 0x0f, 0xce, 0x1a, 0x41, 0x5d, 0x0a, + 0x51, 0xbb, 0x3e, 0xe3, 0x18, 0x4d, 0xc6, 0x01, 0xd9, 0x11, 0x11, 0x98, + 0x02, 0x14, 0xe5, 0xbf, 0xc5, 0x34, 0x6b, 0xa4, 0x62, 0x3d, 0x64, 0x91, + 0x72, 0xe5, 0xf6, 0x4f, 0x07, 0x58, 0x2f, 0x90, 0x55, 0x48, 0x6a, 0x6c, + 0x33, 0x17, 0xd3, 0xba, 0xab, 0xf3, 0x70, 0xd5, 0xf0, 0xc8, 0xa2, 0x83, + 0x8d, 0x2c, 0x5b, 0xa6, 0x9f, 0x79, 0x62, 0xbe, 0xf0, 0x55, 0xa9, 0x4f, + 0xff, 0x61, 0xed, 0x88, 0x39, 0xdd, 0x81, 0x46, 0xd3, 0xf6, 0x40, 0x0f, + 0x7f, 0x29, 0x75, 0x17, 0x55, 0x2b, 0xcc, 0xf3, 0x9f, 0x1a, 0xf9, 0x4b, + 0xb5, 0xd5, 0x19, 0xf1, 0xdf, 0xbe, 0x28, 0xdf, 0xd6, 0x00, 0xe7, 0x71, + 0xdb, 0xaa, 0x0f, 0xb6, 0x75, 0xc1, 0xfa, 0x62, 0x17, 0x16, 0xbe, 0x59, + 0x93, 0x53, 0x28, 0x78, 0x90, 0x5c, 0x94, 0xad, 0x20, 0xe9, 0xad, 0x4d, + 0x91, 0xb9, 0x2f, 0x6b, 0xf7, 0xc8, 0xe7, 0xdb, 0x37, 0x71, 0x2c, 0xbc, + 0x11, 0xb7, 0x1b, 0x2f, 0xbd, 0xa7, 0xaa, 0xed, 0xea, 0xc7, 0x8a, 0x5b, + 0x45, 0xb1, 0x92, 0xe7, 0x0b, 0x51, 0x4b, 0xe0, 0x2d, 0xaa, 0xe8, 0x2f, + 0x7b, 0x2a, 0x5f, 0x01, 0x9e, 0x7a, 0x18, 0x51, 0x56, 0x9a, 0x55, 0x31, + 0x54, 0x2c, 0xdc, 0x95, 0x2d, 0x08, 0xd6, 0x2f, 0x53, 0x74, 0x37, 0x2c, + 0xbc, 0x2a, 0xa9, 0xb2, 0x72, 0xdb, 0x2c, 0x97, 0x1f, 0xc5, 0xf8, 0xfa, + 0x0c, 0xdd, 0x73, 0xbb, 0x53, 0x4d, 0xd4, 0x9a, 0xf4, 0x5b, 0x03, 0x22, + 0x1e, 0xce, 0xbe, 0x71, 0x9b, 0xc6, 0xdd, 0x0b, 0x88, 0x0d, 0xe4, 0xe5, + 0x15, 0x72, 0xda, 0x6d, 0xc1, 0x40, 0xb9, 0x72, 0x35, 0x42, 0x82, 0x6c, + 0x31, 0xfe, 0xba, 0x56, 0x8b, 0xf6, 0x41, 0xae, 0x27, 0x8a, 0x86, 0x54, + 0x5e, 0x60, 0xf3, 0x84, 0xb9, 0x57, 0x62, 0xf8, 0x32, 0x1f, 0xba, 0x28, + 0xb0, 0x15, 0xcc, 0xb1, 0xe4, 0xfe, 0xe1, 0x8a, 0xf8, 0x3f, 0x39, 0xcb, + 0x59, 0x7e, 0xca, 0x12, 0xda, 0xec, 0x2c, 0x54, 0x13, 0xa7, 0xf5, 0x47, + 0xe1, 0x94, 0xc5, 0xb3, 0xab, 0x7e, 0x5a, 0x69, 0x0f, 0xa7, 0x9e, 0x81, + 0x0f, 0x59, 0x6c, 0x15, 0xb0, 0xf7, 0xc4, 0x2e, 0x18, 0xd6, 0x10, 0xc7, + 0x1e, 0x53, 0xee, 0xb1, 0x7d, 0xad, 0xf4, 0xd0, 0x89, 0x4f, 0xa0, 0x1b, + 0x54, 0xd3, 0xdf, 0xd2, 0xc8, 0x8c, 0x7a, 0x2c, 0xee, 0x70, 0x44, 0x5a, + 0x46, 0x3a, 0x55, 0x89, 0x67, 0x86, 0x38, 0xd4, 0xfe, 0x49, 0xb8, 0xb1, + 0xe5, 0x39, 0x04, 0xeb, 0xb9, 0x1f, 0x6e, 0x93, 0xf9, 0x42, 0xcd, 0xa4, + 0x25, 0x5e, 0x2e, 0xd4, 0xd4, 0xd1, 0x2d, 0x3e, 0x42, 0xd2, 0x79, 0x43, + 0x89, 0x4d, 0x2b, 0x7a, 0xb8, 0xa5, 0xd4, 0x3e, 0xc3, 0xb6, 0xe6, 0x13, + 0xcc, 0x3c, 0x54, 0x20, 0xb4, 0x06, 0x45, 0x16, 0x79, 0xb5, 0x73, 0x3a, + 0xdf, 0xce, 0x16, 0x7a, 0xaa, 0x6e, 0xe9, 0x53, 0x0d, 0x82, 0x64, 0x9d, + 0x31, 0xa3, 0xc7, 0x63, 0xf6, 0x3f, 0xeb, 0x5e, 0x68, 0x7b, 0x38, 0x9d, + 0xdf, 0xa1, 0x4c, 0xbb, 0x5c, 0xd6, 0x89, 0xfe, 0xf6, 0x09, 0x05, 0x9d, + 0x40, 0xdf, 0x5c, 0x35, 0xd1, 0xe3, 0x68, 0xf6, 0xe5, 0x86, 0x43, 0xcd, + 0xfd, 0x15, 0x61, 0xfc, 0xda, 0xef, 0x6e, 0x29, 0xfc, 0xdb, 0xe8, 0x29, + 0xa4, 0xf1, 0x98, 0x5f, 0x34, 0x5b, 0x56, 0x80, 0xa3, 0xe4, 0x65, 0x40, + 0x3a, 0x17, 0x6c, 0x40, 0x78, 0x3f, 0xe6, 0xe2, 0x1e, 0x20, 0x31, 0x02, + 0xcf, 0x55, 0x70, 0x81, 0x21, 0xf3, 0x9f, 0x06, 0xa0, 0xfd, 0xe2, 0x89, + 0x18, 0xb5, 0x79, 0x2d, 0x2a, 0x79, 0xdc, 0xcc, 0x0d, 0xf4, 0x53, 0x75, + 0x4c, 0x6e, 0xd0, 0xdc, 0x89, 0x98, 0x3d, 0xa7, 0xe8, 0xa7, 0xc3, 0x54, + 0x64, 0x91, 0x7e, 0x2b, 0xfb, 0xd5, 0x41, 0xdb, 0x02, 0x8c, 0xa9, 0xd4, + 0x1b, 0xf3, 0x06, 0x85, 0xf0, 0xa7, 0x49, 0xb2, 0x3e, 0xf1, 0xd6, 0xb2, + 0x3f, 0x72, 0x6e, 0xe8, 0x65, 0xd6, 0xa1, 0x21, 0xc5, 0x41, 0xef, 0x7d, + 0xdf, 0x51, 0x04, 0x15, 0x68, 0x74, 0x44, 0x24, 0xbf, 0x6d, 0x01, 0x23, + 0x73, 0x0d, 0x64, 0x68, 0x26, 0xbd, 0x95, 0x31, 0x74, 0x30, 0x70, 0x3b, + 0x92, 0x0d, 0xaa, 0x24, 0xfc, 0xf5, 0xea, 0xe8, 0xe5, 0x17, 0x18, 0x4a, + 0xbb, 0x27, 0x66, 0x75, 0x3a, 0xf5, 0x32, 0x60, 0xa8, 0xea, 0xb2, 0x28, + 0xf8, 0xff, 0x21, 0x8a, 0x16, 0x3e, 0x9f, 0x79, 0x10, 0xee, 0x8b, 0xdf, + 0x23, 0x8a, 0x1e, 0xbb, 0x6a, 0x21, 0xe7, 0x83, 0x87, 0x8e, 0x06, 0x8f, + 0x0b, 0xc2, 0xd0, 0xf0, 0x8f, 0x90, 0x80, 0x27, 0x68, 0x48, 0x74, 0xb5, + 0xbc, 0xae, 0x1e, 0x97, 0x75, 0x54, 0x51, 0x1f, 0x57, 0x22, 0xfd, 0xba, + 0x11, 0x15, 0x8d, 0x32, 0xec, 0x18, 0x71, 0x8d, 0x2e, 0x01, 0xc5, 0xa0, + 0x3b, 0x55, 0x66, 0x11, 0xa8, 0x40, 0x26, 0x99, 0x7e, 0x51, 0xa3, 0x36, + 0x8c, 0xf8, 0xaa, 0x85, 0xbe, 0x8a, 0xa4, 0xf8, 0xb2, 0x54, 0x0b, 0x65, + 0x05, 0x38, 0x25, 0x40, 0x02, 0xe7, 0x84, 0x4e, 0x1b, 0x2c, 0x98, 0xe8, + 0x45, 0xf5, 0x98, 0xb9, 0x31, 0xa0, 0xf7, 0x3e, 0xf2, 0x26, 0x4f, 0x8f, + 0xd4, 0x27, 0xe4, 0x4b, 0x70, 0x42, 0x8f, 0xcd, 0x89, 0x6d, 0x24, 0x09, + 0xf8, 0x56, 0xa5, 0x7b, 0xce, 0xb0, 0x19, 0x20, 0x94, 0xa9, 0x60, 0xcf, + 0x8d, 0xcf, 0xf9, 0x31, 0xe0, 0x7c, 0x25, 0x85, 0xda, 0xf1, 0x0d, 0xb5, + 0x18, 0x1c, 0xd4, 0xd4, 0x14, 0x3a, 0xac, 0x30, 0x35, 0x81, 0x22, 0x69, + 0xa8, 0x98, 0xea, 0x99, 0xd7, 0xab, 0x5c, 0xe7, 0x91, 0x78, 0x38, 0x08, + 0x82, 0x91, 0x68, 0xa7, 0x53, 0x89, 0x7b, 0x32, 0xc3, 0x95, 0xd7, 0x05, + 0x46, 0x92, 0x3f, 0x4e, 0x5f, 0xde, 0xa9, 0x10, 0xf8, 0xc0, 0x42, 0x9f, + 0xeb, 0x4d, 0x5c, 0x52, 0xb8, 0xa1, 0x6a, 0x72, 0xc4, 0x44, 0x0b, 0x50, + 0xad, 0xf2, 0xa9, 0x3c, 0xd0, 0x35, 0x55, 0xa5, 0x59, 0x16, 0x70, 0x76, + 0xf0, 0x94, 0xcd, 0x40, 0x61, 0xdb, 0x48, 0xda, 0x8e, 0x21, 0xd2, 0xdf, + 0xb5, 0x45, 0x84, 0x12, 0x0a, 0x69, 0x1f, 0x39, 0x50, 0x79, 0x50, 0x7d, + 0x8a, 0xd2, 0x30, 0x80, 0x9d, 0x0c, 0xf2, 0xca, 0x35, 0x23, 0x66, 0x62, + 0xde, 0x17, 0xd0, 0xb9, 0x5d, 0x87, 0x65, 0xc7, 0x68, 0xe1, 0x65, 0x1d, + 0x8d, 0x94, 0xf5, 0x96, 0xe2, 0xf4, 0x43, 0x09, 0x2d, 0xf8, 0xea, 0x8a, + 0x1c, 0x36, 0x90, 0xc6, 0xcd, 0xfb, 0x94, 0xb8, 0x3d, 0x4c, 0x8b, 0x1f, + 0xd2, 0xcc, 0x4d, 0xde, 0x86, 0x5b, 0xca, 0xb7, 0x33, 0xc4, 0x8c, 0x67, + 0xcd, 0x55, 0xfd, 0x4a, 0x86, 0x8d, 0xea, 0xc5, 0xfa, 0x4d, 0xa9, 0x1d, + 0xb9, 0x7f, 0xdb, 0x8e, 0x54, 0xfa, 0xf6, 0x82, 0x70, 0xe4, 0x38, 0x11, + 0x9b, 0xd9, 0xd5, 0x2c, 0xe4, 0xc8, 0x1b, 0x31, 0x6d, 0xc2, 0x57, 0xbd, + 0xfa, 0x75, 0x21, 0xce, 0xe7, 0x01, 0x2b, 0x9d, 0xef, 0x64, 0x4c, 0x76, + 0x9f, 0xe9, 0xd6, 0xb2, 0x38, 0xe0, 0xbb, 0xb4, 0x10, 0xd9, 0xa6, 0x74, + 0x18, 0xd2, 0x28, 0x9a, 0x90, 0x0e, 0xa6, 0x08, 0x92, 0xf6, 0x93, 0x76, + 0xf0, 0xc8, 0x77, 0xfb, 0x22, 0x46, 0xbf, 0x7c, 0xe3, 0x99, 0x7b, 0x14, + 0x6a, 0x5c, 0x6b, 0x9b, 0xdd, 0xe0, 0x06, 0x91, 0x86, 0x1d, 0x01, 0xde, + 0x20, 0x0e, 0x33, 0x72, 0xcc, 0x2c, 0x4f, 0x90, 0x00, 0xdb, 0xeb, 0x15, + 0x73, 0x96, 0x5e, 0xc7, 0xab, 0xbd, 0x69, 0x61, 0xc1, 0x42, 0xa0, 0x65, + 0x4e, 0x56, 0xb1, 0x7a, 0xe3, 0x03, 0xf8, 0x62, 0x3b, 0xfc, 0x4d, 0x20, + 0x53, 0x1b, 0xb9, 0xe1, 0x8b, 0x41, 0xd9, 0x9c, 0x62, 0xb2, 0x2b, 0x7e, + 0xe7, 0xc0, 0xfa, 0x50, 0xa9, 0xf1, 0x14, 0x82, 0x82, 0x52, 0xc6, 0x92, + 0xc9, 0xef, 0x47, 0x95, 0x7d, 0xbd, 0x28, 0x6e, 0xd3, 0x42, 0xea, 0x30, + 0x55, 0x05, 0x07, 0x9c, 0x7e, 0xbe, 0xea, 0x9a, 0x4d, 0xdc, 0xc8, 0x96, + 0x31, 0x51, 0x6a, 0x18, 0x4e, 0x78, 0xb8, 0xc0, 0xdc, 0x4b, 0x12, 0x01, + 0xc6, 0x80, 0x72, 0x3a, 0x47, 0x36, 0xde, 0x7b, 0x1a, 0xd4, 0x3b, 0x0a, + 0xc9, 0x14, 0x13, 0x37, 0x6e, 0x89, 0xa1, 0xd5, 0x5f, 0x1f, 0xc4, 0x4f, + 0x4b, 0x17, 0xa9, 0xad, 0x78, 0xbb, 0x31, 0x09, 0x1c, 0x19, 0x9c, 0x84, + 0x7b, 0x33, 0x82, 0xcb, 0xef, 0xde, 0xd1, 0x27, 0xf9, 0x46, 0xd4, 0x0e, + 0x1c, 0x22, 0x0d, 0xe5, 0xc3, 0xb5, 0xb6, 0xeb, 0x66, 0xe8, 0x75, 0x48, + 0x93, 0x12, 0x41, 0x7c, 0x9f, 0x82, 0x91, 0xd6, 0xe9, 0xef, 0x92, 0xed, + 0x48, 0xdd, 0xd6, 0x5e, 0xea, 0x13, 0x6d, 0xa5, 0xb6, 0x24, 0xed, 0x08, + 0x70, 0x7f, 0xcc, 0x89, 0x80, 0x7f, 0xcf, 0x7d, 0xd3, 0x01, 0x5b, 0x75, + 0xa9, 0x90, 0x8e, 0x62, 0xe7, 0x5b, 0x92, 0xfe, 0x18, 0x29, 0xb4, 0xc3, + 0x30, 0x11, 0x73, 0xd0, 0x56, 0xba, 0x5d, 0xc6, 0xb9, 0x18, 0x0d, 0x23, + 0xed, 0xd9, 0x81, 0x30, 0x3d, 0xf4, 0x88, 0x36, 0xc7, 0x57, 0xe2, 0xe0, + 0x48, 0xad, 0x9e, 0xd8, 0x0d, 0xfb, 0x17, 0x5e, 0x30, 0xcd, 0xab, 0xd4, + 0xd5, 0x1e, 0x34, 0x65, 0xdb, 0x96, 0xf5, 0x6d, 0xf6, 0x8d, 0x66, 0x37, + 0xd4, 0xfe, 0x19, 0x5a, 0x66, 0x8b, 0x39, 0x98, 0x73, 0x2d, 0x3e, 0x4e, + 0x2c, 0x05, 0x24, 0x5a, 0x5b, 0x97, 0x30, 0xbe, 0x8c, 0xca, 0x1d, 0xf1, + 0xe1, 0x23, 0x36, 0xce, 0x66, 0x5b, 0x7f, 0xe4, 0xeb, 0x9c, 0xed, 0x27, + 0x5d, 0x72, 0x6f, 0xd5, 0x51, 0x77, 0x94, 0x0c, 0xbe, 0x81, 0x49, 0xe3, + 0x7b, 0x92, 0xc5, 0x6a, 0x25, 0xde, 0xd7, 0x89, 0x81, 0x86, 0x3d, 0x02, + 0x89, 0x12, 0x48, 0xaa, 0x13, 0x9a, 0x0f, 0xde, 0x39, 0x85, 0x1b, 0x97, + 0xcb, 0x06, 0xd3, 0xf2, 0xf6, 0x8f, 0xce, 0x8f, 0x64, 0x24, 0xf5, 0xc8, + 0xee, 0xaa, 0x1a, 0x3e, 0x18, 0x04, 0x30, 0xaf, 0xd7, 0x18, 0xe0, 0x44, + 0x4c, 0x86, 0x48, 0xff, 0x0f, 0xc9, 0xaf, 0x55, 0x7d, 0xd7, 0x46, 0x08, + 0x13, 0xcd, 0x90, 0x82, 0x3c, 0x16, 0x19, 0x35, 0xd9, 0x08, 0xe6, 0xbc, + 0xfa, 0x8f, 0x13, 0x7d, 0x7f, 0xb7, 0x7d, 0x38, 0x5b, 0x70, 0x14, 0xef, + 0xb1, 0xb7, 0x56, 0x58, 0x08, 0x12, 0xff, 0xe9, 0x29, 0x90, 0xe5, 0x38, + 0xc1, 0x92, 0xf3, 0x70, 0xf5, 0x42, 0x71, 0xda, 0xa0, 0x01, 0xa5, 0x4f, + 0xc3, 0x20, 0xbe, 0xca, 0x8e, 0xc2, 0xdb, 0x87, 0x57, 0x83, 0x2a, 0xe4, + 0x00, 0xfe, 0x2b, 0xfa, 0x69, 0xe3, 0xfe, 0xc1, 0xa8, 0xe7, 0xc3, 0x67, + 0xd1, 0xfb, 0x87, 0xbf, 0xab, 0xe4, 0xee, 0x74, 0x2e, 0x0b, 0x18, 0xcc, + 0x82, 0xf9, 0xc4, 0xb2, 0x40, 0xab, 0x06, 0xa4, 0xa1, 0x6e, 0xf7, 0x9c, + 0x0a, 0x05, 0xf9, 0x51, 0x9a, 0xf4, 0xb6, 0xe6, 0x9a, 0x6a, 0xf9, 0xc6, + 0x87, 0x17, 0xb8, 0x4e, 0xf9, 0x22, 0x0d, 0xe6, 0x89, 0xf8, 0x93, 0x45, + 0xf8, 0xe8, 0x4a, 0x67, 0x9b, 0x73, 0xf5, 0x7d, 0x34, 0x3e, 0xf6, 0x05, + 0x5a, 0x06, 0xbc, 0x9d, 0x8a, 0x0a, 0x2c, 0x2a, 0x7d, 0x11, 0xd9, 0x6b, + 0xee, 0x56, 0x1c, 0x09, 0xdf, 0x07, 0xa3, 0xce, 0x6d, 0xa7, 0x0d, 0x3e, + 0x8f, 0xc6, 0xb8, 0x7c, 0x99, 0xcc, 0xf3, 0xef, 0x67, 0x93, 0x61, 0x0d, + 0xd5, 0x65, 0x8e, 0x97, 0x02, 0x60, 0x1d, 0xc1, 0x14, 0x4d, 0x0f, 0x6e, + 0xbb, 0x04, 0x17, 0xee, 0x36, 0x32, 0xaa, 0x05, 0xda, 0x25, 0xb4, 0x55, + 0x1a, 0xe3, 0x98, 0x2c, 0xc3, 0xcf, 0xd8, 0x70, 0xe7, 0xae, 0xaa, 0x39, + 0xbb, 0x02, 0x14, 0xf0, 0xb1, 0xcc, 0xa4, 0x6f, 0x94, 0xee, 0xd3, 0x33, + 0xe1, 0x4f, 0x22, 0x6a, 0x6c, 0x32, 0xf9, 0xb7, 0x92, 0x69, 0xd0, 0x95, + 0x5b, 0x9f, 0x0f, 0x0b, 0x89, 0x0a, 0x6d, 0x23, 0x9e, 0xf8, 0x01, 0x51, + 0x25, 0x8d, 0x56, 0x49, 0x9b, 0xcb, 0xa8, 0x8b, 0x16, 0xff, 0x74, 0x9d, + 0xf4, 0x2d, 0xec, 0x59, 0x46, 0xa8, 0x9b, 0x16, 0x2f, 0xd2, 0x09, 0x67, + 0x52, 0x87, 0x00, 0xcc, 0xab, 0xce, 0xf6, 0xf0, 0x49, 0x39, 0x4d, 0x89, + 0x74, 0x8a, 0x93, 0xa9, 0x40, 0x52, 0xe0, 0xa4, 0xbb, 0x96, 0x58, 0xb4, + 0xcb, 0x02, 0x71, 0xfe, 0xd9, 0xcc, 0xf3, 0xfe, 0xe6, 0xc7, 0x52, 0xeb, + 0x9e, 0xa8, 0xac, 0x67, 0xd3, 0x48, 0xcb, 0x2d, 0x64, 0xc4, 0x46, 0x91, + 0xaa, 0x01, 0x2d, 0x5c, 0xe7, 0x2a, 0xf4, 0xa1, 0x6b, 0x5c, 0x3c, 0xd4, + 0xc0, 0xd1, 0x7f, 0xc4, 0x81, 0x80, 0xbf, 0x27, 0xb0, 0xe6, 0xfe, 0x39, + 0xb9, 0xd7, 0x26, 0x8f, 0x22, 0x00, 0xdf, 0x27, 0x3b, 0xd1, 0x82, 0x80, + 0xbe, 0x05, 0x20, 0xff, 0xcb, 0x5f, 0x97, 0x17, 0xb6, 0x40, 0xd7, 0xd8, + 0xdc, 0x04, 0x05, 0xe1, 0xbe, 0xda, 0x70, 0xa7, 0x61, 0x5e, 0x2f, 0x78, + 0xf1, 0xb1, 0xc5, 0x91, 0x87, 0xd6, 0xbe, 0x33, 0x33, 0xe3, 0x65, 0xa3, + 0xae, 0x1a, 0xe5, 0x69, 0xb9, 0xbf, 0x34, 0xf6, 0x52, 0xa0, 0xec, 0x5b, + 0x73, 0x10, 0x71, 0x1d, 0x77, 0x39, 0x0e, 0x92, 0x6f, 0xd4, 0xf9, 0x47, + 0xbf, 0x9c, 0x98, 0x63, 0x78, 0x8a, 0x4c, 0xdd, 0x27, 0x50, 0x24, 0xf8, + 0x24, 0xbf, 0xc2, 0xd7, 0x1a, 0xe4, 0x23, 0x62, 0xda, 0x3e, 0xdc, 0xb0, + 0x7f, 0x63, 0x93, 0xee, 0xd1, 0x17, 0x89, 0x9f, 0x00, 0x5a, 0x9d, 0x03, + 0x57, 0xc2, 0x26, 0x24, 0xcc, 0x4f, 0xae, 0xf4, 0xa6, 0xb5, 0x87, 0x12, + 0x25, 0x87, 0x48, 0x1d, 0x66, 0xff, 0xf1, 0x3a, 0x21, 0x57, 0x48, 0x35, + 0x6d, 0x45, 0xad, 0xff, 0x31, 0x42, 0x2e, 0x9e, 0x01, 0xb1, 0xc6, 0x55, + 0xf1, 0xa6, 0x6b, 0x64, 0x03, 0x68, 0x17, 0x0b, 0x6a, 0x23, 0xd9, 0x78, + 0x40, 0x55, 0x5a, 0x0c, 0x35, 0x05, 0x21, 0x44, 0xe3, 0x80, 0xee, 0xd8, + 0xb6, 0x49, 0xf0, 0x70, 0x4c, 0xeb, 0x89, 0x22, 0x09, 0x67, 0x91, 0xf5, + 0xac, 0xbd, 0x8f, 0xe4, 0xbf, 0x64, 0x4a, 0xf9, 0x28, 0xa8, 0x95, 0x14, + 0x7b, 0xa2, 0x57, 0x4f, 0xf9, 0x99, 0x52, 0x19, 0x08, 0x95, 0x61, 0xc5, + 0x58, 0xf7, 0x28, 0xbf, 0x3b, 0x3b, 0x9f, 0x1b, 0x97, 0xd0, 0x33, 0x79, + 0xf6, 0xff, 0x2f, 0x5a, 0xd8, 0x68, 0xd0, 0xf8, 0x79, 0x60, 0xa3, 0xa8, + 0xa8, 0x83, 0xf7, 0xb3, 0xc3, 0x74, 0xd7, 0x37, 0xb2, 0x18, 0x8f, 0x40, + 0xaf, 0x9f, 0x2c, 0xa4, 0x32, 0xef, 0x93, 0xf4, 0xb4, 0xe3, 0xbc, 0xd0, + 0x4d, 0xb0, 0xac, 0xee, 0x11, 0x26, 0xe2, 0xbd, 0x17, 0x55, 0x7d, 0xa2, + 0x5d, 0xb4, 0x51, 0xd1, 0xfb, 0x01, 0xc6, 0x92, 0xa0, 0x80, 0x13, 0xf2, + 0xa3, 0xe3, 0xbd, 0x92, 0x96, 0x4c, 0x47, 0x21, 0x2a, 0x0f, 0x31, 0xe2, + 0xd6, 0x83, 0xfb, 0xc6, 0x80, 0x0e, 0x62, 0x67, 0x68, 0x39, 0x50, 0x4e, + 0x35, 0x89, 0xf5, 0x32, 0xcd, 0x4f, 0xaa, 0x16, 0x77, 0x4b, 0x23, 0xc0, + 0xe7, 0x4f, 0x5b, 0x76, 0xa6, 0x21, 0x32, 0x4b, 0x3e, 0xcf, 0xac, 0xac, + 0x5b, 0xef, 0x6a, 0x44, 0x2a, 0x7d, 0xba, 0xbb, 0x95, 0x5e, 0xe4, 0x5b, + 0xf4, 0xcc, 0x38, 0x26, 0x87, 0xfb, 0x88, 0x83, 0x5c, 0xce, 0xe8, 0x9f, + 0xdc, 0x4a, 0x91, 0xde, 0x3b, 0x3b, 0x08, 0x85, 0xe6, 0xc4, 0x8f, 0xe2, + 0xea, 0xff, 0x30, 0x62, 0xfc, 0x02, 0x7a, 0xd5, 0x8b, 0xdf, 0x72, 0xb6, + 0xb3, 0x42, 0x35, 0xa6, 0x35, 0x4f, 0x86, 0xdb, 0xb1, 0x4b, 0xb3, 0x29, + 0xeb, 0xd2, 0xb0, 0x7d, 0x0a, 0x1f, 0xb8, 0x30, 0x5a, 0x01, 0x86, 0x49, + 0x5e, 0x23, 0xc6, 0x5d, 0x05, 0x2b, 0xfb, 0x7b, 0x49, 0x99, 0xed, 0x34, + 0x5d, 0xda, 0xf8, 0xa3, 0xc0, 0x47, 0x93, 0x24, 0x67, 0x0f, 0x0b, 0xd1, + 0x6e, 0x76, 0x1b, 0xcb, 0xe8, 0x72, 0x52, 0xb8, 0xf1, 0xb7, 0x75, 0xa6, + 0x08, 0x4e, 0xbb, 0x3a, 0x10, 0xc4, 0xdf, 0x09, 0xa1, 0x4f, 0xe5, 0x28, + 0x69, 0xd9, 0x46, 0x56, 0x53, 0x2f, 0x78, 0xb8, 0x63, 0xd4, 0xc5, 0xe4, + 0xa9, 0x4b, 0x35, 0x95, 0x59, 0xd6, 0xbb, 0xfa, 0x83, 0xdd, 0x75, 0x16, + 0xa5, 0xfc, 0xe0, 0x45, 0xb8, 0x0c, 0x14, 0xb3, 0x06, 0xd4, 0x1a, 0xcb, + 0x32, 0xd5, 0xbe, 0xab, 0x18, 0xe3, 0xa3, 0xfe, 0x5f, 0x99, 0x08, 0x63, + 0x91, 0x0a, 0xf4, 0xe2, 0xe1, 0x13, 0x92, 0x96, 0xbf, 0x95, 0xe9, 0x45, + 0xc2, 0xe5, 0x0c, 0xf7, 0x52, 0xa2, 0x6f, 0x4a, 0xfd, 0x60, 0x20, 0x3b, + 0x7a, 0xa7, 0x1e, 0x10, 0x01, 0xed, 0xcb, 0x98, 0x5c, 0x98, 0x47, 0xad, + 0x3e, 0xae, 0xa0, 0x62, 0x74, 0x95, 0x4b, 0x39, 0xc1, 0x66, 0x8d, 0x97, + 0x66, 0x85, 0xb7, 0x8d, 0x9e, 0xf0, 0x1a, 0x74, 0xe5, 0x2b, 0x6a, 0xc2, + 0x47, 0xe7, 0x2a, 0xd8, 0xec, 0x8a, 0xf5, 0x5a, 0xc3, 0x4c, 0xac, 0x5f, + 0xf6, 0x2b, 0x21, 0xba, 0xb9, 0xd0, 0x17, 0xab, 0xe5, 0x47, 0x8e, 0x2c, + 0xbe, 0xf3, 0xd7, 0x0f, 0x6a, 0x37, 0xfd, 0x14, 0x11, 0xde, 0x4c, 0x64, + 0xa8, 0xd8, 0x15, 0xee, 0x65, 0xb6, 0xce, 0x7b, 0x81, 0x3b, 0xe6, 0x71, + 0xbe, 0x8f, 0x05, 0xf7, 0x0e, 0xb7, 0x71, 0x9a, 0x47, 0x92, 0xcf, 0xff, + 0xf7, 0xa6, 0x2c, 0x95, 0x21, 0x8b, 0x54, 0xce, 0x54, 0xee, 0xcc, 0x42, + 0xc6, 0x1c, 0xdc, 0xf2, 0x09, 0xaa, 0x45, 0x3e, 0x73, 0x3e, 0xf6, 0xaa, + 0x56, 0xf2, 0xd7, 0xda, 0x6a, 0x21, 0x63, 0x07, 0xd3, 0x16, 0x94, 0xc1, + 0x2f, 0xe5, 0x0f, 0x91, 0x9d, 0x4d, 0x49, 0xa2, 0x4d, 0x96, 0xca, 0x3e, + 0xe2, 0x32, 0x45, 0xc2, 0x0d, 0x86, 0x98, 0x82, 0x56, 0x9c, 0xdb, 0xab, + 0x5f, 0x2c, 0xc0, 0x00, 0x72, 0x81, 0x30, 0x93, 0x4e, 0xa7, 0x91, 0xd4, + 0x6c, 0xe3, 0x0a, 0xe5, 0x94, 0x5f, 0x9b, 0xb9, 0x25, 0x43, 0xf6, 0x16, + 0x5b, 0x0f, 0x0b, 0x60, 0x26, 0x28, 0x02, 0x3a, 0xed, 0x65, 0x2e, 0x78, + 0xc9, 0x28, 0x93, 0x50, 0x3c, 0x46, 0xcb, 0x90, 0xb9, 0x82, 0xbe, 0xc1, + 0x35, 0x87, 0x1a, 0x0d, 0x84, 0x76, 0x1f, 0xd5, 0xe8, 0xb2, 0xb0, 0xd2, + 0x55, 0x06, 0x6c, 0xad, 0x28, 0x23, 0x0f, 0xf4, 0x3f, 0x11, 0x98, 0xaf, + 0xde, 0xdc, 0x49, 0x9e, 0x6f, 0xa0, 0x2c, 0x3d, 0xd0, 0x86, 0x0e, 0x6d, + 0x0f, 0x48, 0xf0, 0xa4, 0x5f, 0x92, 0x49, 0x5c, 0x3c, 0x1a, 0x56, 0x41, + 0x4d, 0x40, 0x8a, 0x26, 0x11, 0x85, 0x57, 0xe9, 0x3b, 0xab, 0x19, 0xfb, + 0x95, 0x73, 0x1c, 0x88, 0xe4, 0x6c, 0x21, 0xf2, 0xc4, 0xfd, 0x33, 0xe8, + 0x6b, 0x21, 0x43, 0xee, 0x12, 0x57, 0x6e, 0x77, 0xc1, 0x22, 0xe9, 0x66, + 0x8a, 0xee, 0x33, 0xad, 0x29, 0x81, 0xca, 0x50, 0x3c, 0x43, 0xf0, 0xda, + 0x17, 0x9a, 0xae, 0x9c, 0x29, 0x1a, 0xcd, 0x9e, 0xed, 0x7d, 0xd9, 0x75, + 0xa1, 0x2d, 0x8d, 0xf8, 0xdd, 0x1f, 0x9c, 0x4f, 0x09, 0x45, 0xdf, 0x3e, + 0x73, 0x98, 0xc4, 0x8a, 0xcd, 0xc9, 0x13, 0xb2, 0x4a, 0x98, 0x9b, 0xbd, + 0x7a, 0xf5, 0xe8, 0x41, 0xc9, 0x5c, 0x82, 0xa0, 0xce, 0xb8, 0x5c, 0xbe, + 0x07, 0x24, 0x8f, 0x56, 0x5c, 0x4b, 0xff, 0x0e, 0xbc, 0x51, 0x78, 0xd9, + 0x37, 0xd9, 0x8c, 0x43, 0x8f, 0xa4, 0x47, 0xc0, 0x02, 0x25, 0x73, 0x81, + 0xe3, 0x62, 0x5b, 0xb0, 0x1b, 0xe1, 0xe1, 0x34, 0xe9, 0x53, 0x29, 0xbf, + 0x81, 0xed, 0x7b, 0xe1, 0xe8, 0x2d, 0xbd, 0x2e, 0x91, 0x88, 0x2f, 0x2e, + 0x20, 0x70, 0x08, 0x35, 0x9b, 0xb0, 0xc0, 0xe1, 0xfe, 0xbb, 0xc1, 0x61, + 0x80, 0xa1, 0x52, 0x35, 0xfb, 0xb1, 0x47, 0x52, 0xbf, 0xf4, 0xbb, 0x0c, + 0xa0, 0x19, 0x7d, 0x8b, 0xae, 0xa0, 0x2d, 0x3e, 0xb7, 0xfc, 0x22, 0xac, + 0x7e, 0x28, 0x49, 0xba, 0xb6, 0xc7, 0xdb, 0x31, 0x53, 0x6b, 0x8d, 0x16, + 0xeb, 0x7b, 0xbb, 0x34, 0xc6, 0xb1, 0x66, 0x13, 0x74, 0x87, 0x0e, 0xe2, + 0x44, 0xf9, 0xe2, 0x20, 0x8a, 0xba, 0x14, 0x0a, 0x12, 0x00, 0xd7, 0x0d, + 0xdf, 0x38, 0x91, 0xb1, 0x94, 0x44, 0x5f, 0xa3, 0x09, 0x42, 0x53, 0x36, + 0xc4, 0xb8, 0xb1, 0x4b, 0x3c, 0x91, 0xac, 0xa7, 0xf4, 0xbe, 0xb0, 0x31, + 0x26, 0xa9, 0xb3, 0x42, 0xe4, 0x35, 0x56, 0x45, 0x6d, 0x55, 0x30, 0x52, + 0xbe, 0x17, 0xa8, 0x79, 0xba, 0xf2, 0xd8, 0xe2, 0x11, 0x8a, 0x1d, 0xda, + 0xff, 0x3b, 0xba, 0x47, 0x7a, 0x7d, 0xcd, 0x9e, 0x6f, 0xac, 0x30, 0x5c, + 0x85, 0x26, 0x33, 0x2e, 0x37, 0xdb, 0x74, 0xe0, 0x3e, 0x32, 0x27, 0x29, + 0xba, 0x3b, 0xa3, 0x75, 0x6a, 0x80, 0x85, 0x03, 0x54, 0xdd, 0xec, 0xbd, + 0xfc, 0x70, 0x77, 0x48, 0x73, 0x21, 0xbe, 0xdc, 0x67, 0x33, 0x5a, 0xb4, + 0x5b, 0x50, 0x0d, 0xf3, 0x63, 0x1d, 0x78, 0xe4, 0x29, 0x3c, 0x6f, 0x1a, + 0x37, 0x17, 0xee, 0xd9, 0x13, 0x7c, 0x14, 0x95, 0x09, 0x13, 0x42, 0xe4, + 0xfb, 0x6a, 0xff, 0x25, 0xa0, 0x04, 0xa5, 0xcf, 0xe2, 0xc6, 0x50, 0x51, + 0xf2, 0xd9, 0x2d, 0x9f, 0x63, 0xca, 0x1f, 0x8f, 0x47, 0x29, 0xa8, 0x26, + 0x1d, 0x97, 0x4a, 0xe7, 0x4a, 0xc2, 0xc6, 0x42, 0xbd, 0xa1, 0x8a, 0xf7, + 0xa5, 0x82, 0x8a, 0x73, 0x16, 0x06, 0x66, 0xb4, 0xc7, 0x42, 0x1e, 0x66, + 0x97, 0x05, 0x07, 0x97, 0xc0, 0x7f, 0x64, 0x7b, 0x97, 0x11, 0xa3, 0x53, + 0x94, 0x24, 0x84, 0xc5, 0x9e, 0x69, 0xb7, 0x4d, 0x63, 0x26, 0x63, 0x02, + 0x67, 0x02, 0x25, 0xe3, 0xfc, 0xea, 0x22, 0xcb, 0xbc, 0x8f, 0x2a, 0xc1, + 0x6e, 0x98, 0xc4, 0x4f, 0x70, 0x17, 0x5f, 0x1a, 0x4f, 0xcf, 0x89, 0xc2, + 0x92, 0x32, 0xa1, 0x24, 0xc3, 0xa3, 0x17, 0x7d, 0xee, 0x0f, 0xda, 0x02, + 0x80, 0xff, 0x10, 0xf2, 0xa7, 0xef, 0x11, 0x25, 0x8b, 0x41, 0x93, 0x93, + 0xb1, 0x62, 0x34, 0x7c, 0x44, 0xb8, 0xa3, 0xfd, 0x9a, 0x19, 0xe0, 0x4e, + 0xeb, 0xbb, 0xfd, 0x21, 0xce, 0x60, 0x61, 0x31, 0xc3, 0xa8, 0x7b, 0x8a, + 0xbf, 0x07, 0x8b, 0xd6, 0x88, 0x97, 0xcd, 0x65, 0xa8, 0xe6, 0x06, 0x82, + 0xf3, 0x4d, 0xca, 0x72, 0xd4, 0x7e, 0x0b, 0x32, 0x98, 0x24, 0x9f, 0x86, + 0x8b, 0x6a, 0x1d, 0x4c, 0xca, 0x51, 0x2b, 0x32, 0x4f, 0x40, 0xaf, 0x27, + 0x90, 0x73, 0x28, 0xbd, 0xef, 0xe6, 0x79, 0xd5, 0x96, 0x09, 0xbd, 0x45, + 0xb3, 0x32, 0x1c, 0xa0, 0x74, 0x3c, 0xb1, 0xdd, 0x97, 0xf4, 0xb2, 0xdd, + 0x6e, 0x9f, 0x27, 0xcf, 0xb3, 0x7c, 0xa5, 0xa1, 0x14, 0x08, 0x1a, 0x58, + 0xd8, 0xf3, 0xf4, 0x41, 0x2b, 0x8d, 0xe0, 0x0b, 0xe4, 0x08, 0x21, 0xbf, + 0x01, 0x5a, 0x9e, 0xb0, 0xa9, 0x43, 0xee, 0xba, 0x21, 0xbd, 0x0d, 0xd8, + 0x0d, 0x4f, 0x81, 0x5c, 0x07, 0xe3, 0x49, 0x4b, 0xd7, 0x28, 0x5f, 0xe0, + 0x34, 0x5b, 0xd3, 0x35, 0x80, 0xb4, 0xf9, 0x1e, 0xb7, 0xea, 0x95, 0x20, + 0x40, 0x40, 0x0a, 0xbd, 0xc7, 0x1d, 0xf8, 0xdd, 0xf3, 0xa9, 0x9e, 0x31, + 0x9b, 0xb4, 0x15, 0x0a, 0x94, 0xd8, 0xf2, 0xe8, 0x6b, 0x4a, 0xa5, 0xf5, + 0xbe, 0x6d, 0xb3, 0x61, 0x3a, 0xc9, 0x00, 0x00, 0x23, 0xc1, 0x1b, 0x6e, + 0x01, 0xe2, 0xbb, 0x52, 0x30, 0x6a, 0xdf, 0x32, 0xe5, 0x92, 0x8e, 0x55, + 0xbe, 0xb8, 0x6c, 0x04, 0x57, 0xbb, 0xc4, 0x1e, 0x5c, 0xf3, 0xa4, 0x26, + 0xe7, 0xef, 0xf5, 0xa8, 0x34, 0xff, 0xed, 0xcb, 0x9e, 0xa3, 0x0f, 0xc5, + 0xdf, 0x66, 0xb4, 0x99, 0xf6, 0x10, 0x56, 0xec, 0xb8, 0xcc, 0x2d, 0x6b, + 0xb5, 0x85, 0xe2, 0xfb, 0x47, 0xaf, 0x5f, 0x8a, 0x08, 0x69, 0xc9, 0xc2, + 0x06, 0x04, 0x53, 0x1c, 0xbd, 0xb2, 0xeb, 0x5c, 0x1c, 0x3d, 0x9b, 0x8a, + 0x01, 0x2b, 0x2b, 0xf6, 0xba, 0xc2, 0x2a, 0xf5, 0x50, 0xe3, 0x67, 0x25, + 0xa1, 0x66, 0x08, 0xf2, 0x4c, 0xef, 0xd3, 0xdd, 0xcb, 0x67, 0x87, 0x2b, + 0xa7, 0x01, 0x63, 0x37, 0x63, 0xbc, 0xd9, 0xe3, 0x95, 0x1d, 0x36, 0x0e, + 0x1f, 0xb9, 0xd6, 0x09, 0x86, 0x31, 0x21, 0xac, 0x09, 0x14, 0xa4, 0xbb, + 0x54, 0x81, 0xb8, 0x28, 0xe4, 0x5e, 0x96, 0x53, 0x37, 0x78, 0x6e, 0xfa, + 0xf3, 0xf9, 0xd5, 0x1f, 0xbd, 0xc8, 0xf5, 0x9e, 0x09, 0x43, 0x4c, 0x44, + 0x84, 0x51, 0xc1, 0x51, 0xe1, 0x66, 0x30, 0xd7, 0xfb, 0x41, 0x7b, 0x57, + 0xae, 0x80, 0x41, 0x04, 0xd6, 0xec, 0xc9, 0xb1, 0x1b, 0xdd, 0xb3, 0x6f, + 0x24, 0x38, 0x3f, 0x4f, 0xe5, 0x99, 0x2a, 0x31, 0x20, 0x04, 0x73, 0xa3, + 0xb4, 0x84, 0xc5, 0x21, 0x53, 0x20, 0xce, 0xd6, 0x87, 0x59, 0xfe, 0x5d, + 0x06, 0x26, 0x88, 0x96, 0x34, 0xb3, 0x45, 0x8d, 0xaa, 0x08, 0x87, 0xa0, + 0xc7, 0xa1, 0x5e, 0xde, 0xda, 0xe2, 0xb2, 0x50, 0x04, 0x05, 0x60, 0x50, + 0x7f, 0x85, 0xbc, 0x86, 0xf9, 0xa4, 0x9b, 0xff, 0x2f, 0xd4, 0xdd, 0xdf, + 0xd1, 0x5d, 0xd3, 0x1e, 0xd9, 0x2c, 0xf6, 0x9c, 0x57, 0x8b, 0xa2, 0xd6, + 0x76, 0xe6, 0x02, 0x19, 0x40, 0x9a, 0x8e, 0xf5, 0x6f, 0x7a, 0x26, 0x4a, + 0x01, 0xb1, 0xee, 0xfc, 0x38, 0x56, 0xc7, 0xdf, 0x93, 0x5e, 0x01, 0x07, + 0xd4, 0x4b, 0x89, 0xd5, 0xe4, 0xe3, 0xe6, 0x1b, 0x8c, 0x26, 0xf9, 0xa2, + 0x98, 0x96, 0xca, 0x4e, 0x52, 0x78, 0xa4, 0xb2, 0xa1, 0x21, 0x79, 0xf4, + 0x48, 0x47, 0x6a, 0x89, 0xa0, 0xfc, 0xc6, 0xcf, 0xcc, 0x4a, 0x9f, 0xfa, + 0x5b, 0xca, 0xb8, 0xe6, 0x34, 0xe8, 0x9f, 0x04, 0x3c, 0xf5, 0xc6, 0xd4, + 0xac, 0x94, 0x7f, 0x3c, 0x35, 0xa4, 0x88, 0xf8, 0x4c, 0xdf, 0x03, 0x7e, + 0xb5, 0xe2, 0xd5, 0xc6, 0x0c, 0xef, 0xfc, 0xeb, 0x42, 0xd4, 0x24, 0x34, + 0x01, 0xcd, 0x99, 0x27, 0xa3, 0x2c, 0x76, 0xff, 0x23, 0x96, 0x5e, 0xac, + 0xa5, 0x9d, 0xc4, 0xda, 0xee, 0xf5, 0x03, 0x72, 0xf9, 0x06, 0xdf, 0x2f, + 0x55, 0x0a, 0xca, 0x4d, 0x54, 0xc7, 0x36, 0x20, 0x4d, 0x49, 0x59, 0x75, + 0x4a, 0xcb, 0xfd, 0x55, 0xeb, 0x73, 0xda, 0xc8, 0x76, 0x0b, 0xd5, 0x60, + 0x28, 0x4e, 0xe1, 0xf1, 0xb1, 0xe3, 0xf0, 0x47, 0x6d, 0xf3, 0x8a, 0x88, + 0x63, 0xc3, 0xeb, 0x06, 0x4c, 0x5e, 0x76, 0x2b, 0xe6, 0x14, 0x1b, 0xe7, + 0x33, 0xc0, 0x26, 0x73, 0x71, 0xb8, 0xc0, 0x3a, 0x95, 0x2f, 0x99, 0xf5, + 0x2f, 0x76, 0x64, 0x64, 0xe0, 0x6b, 0xba, 0xde, 0x2e, 0x2a, 0x5a, 0x97, + 0xaf, 0x10, 0x7c, 0xff, 0x90, 0xab, 0xf5, 0x4f, 0x67, 0x36, 0x12, 0xee, + 0x02, 0x6f, 0xec, 0xed, 0x2f, 0x9a, 0x6e, 0x8f, 0x47, 0xb3, 0xef, 0xcd, + 0x72, 0x8f, 0x6f, 0xd7, 0xb7, 0x00, 0x32, 0xfb, 0x8d, 0xc9, 0xc3, 0x17, + 0x59, 0x24, 0x6b, 0x56, 0x7d, 0x9f, 0xa4, 0x66, 0xcb, 0xe2, 0x44, 0x81, + 0x22, 0xcd, 0x15, 0xd5, 0xdb, 0xcd, 0x18, 0x4c, 0xf3, 0x2c, 0x13, 0xf3, + 0xda, 0x19, 0xcb, 0x53, 0x6d, 0x18, 0xbb, 0x20, 0x87, 0xf7, 0x5c, 0x1d, + 0x1a, 0x5e, 0xb3, 0x89, 0x9f, 0x84, 0xa0, 0xad, 0x64, 0x6b, 0xce, 0xaf, + 0x9a, 0x28, 0xa3, 0x18, 0xf5, 0xf4, 0xae, 0xbc, 0xfd, 0x13, 0x27, 0xfc, + 0x93, 0x35, 0xef, 0x63, 0xfc, 0x63, 0x47, 0x1b, 0x1a, 0x27, 0xb9, 0x23, + 0xdf, 0xad, 0x35, 0x07, 0xe8, 0x9f, 0x1c, 0x61, 0xe9, 0xe0, 0xa8, 0x6b, + 0xce, 0xee, 0x49, 0xee, 0x93, 0x44, 0x4a, 0x24, 0x9f, 0xc3, 0xac, 0xe0, + 0x59, 0x9c, 0xc0, 0xa6, 0x16, 0x1d, 0xcd, 0xa2, 0x55, 0xe7, 0x60, 0xc9, + 0xc3, 0x90, 0xdb, 0x76, 0x6a, 0xe8, 0xbd, 0xa7, 0x6d, 0xce, 0xbc, 0xb8, + 0xcd, 0x7f, 0xa7, 0x81, 0xb9, 0x91, 0x50, 0x6b, 0xa5, 0xe1, 0xbd, 0xd9, + 0xe6, 0x2f, 0x86, 0xd9, 0x6a, 0xf3, 0xa6, 0x59, 0x78, 0xde, 0x49, 0x24, + 0xa7, 0xf4, 0x0f, 0x6b, 0x3e, 0x8f, 0x73, 0x7b, 0x9b, 0xa9, 0xba, 0xbf, + 0xaf, 0x97, 0xcd, 0x68, 0x18, 0x1f, 0xfc, 0x4d, 0xaf, 0x64, 0x14, 0x89, + 0x10, 0x93, 0x7c, 0x7c, 0x87, 0xf5, 0x7c, 0xfc, 0x27, 0xf3, 0xf1, 0x25, + 0x5d, 0xa2, 0x3e, 0xbc, 0x21, 0x18, 0x4a, 0x1e, 0x73, 0x42, 0x6c, 0xc8, + 0x97, 0x43, 0x66, 0x22, 0x16, 0x0a, 0x44, 0x2b, 0x64, 0xae, 0xe2, 0xf2, + 0xd5, 0xc2, 0x51, 0x42, 0xd8, 0xc8, 0xee, 0xe5, 0xbe, 0x8f, 0x02, 0x2f, + 0xd5, 0x10, 0xeb, 0xb8, 0x35, 0x42, 0x69, 0x03, 0xff, 0x6a, 0x7d, 0xaf, + 0x50, 0x6e, 0x9d, 0xa2, 0x65, 0x94, 0xb0, 0x3a, 0xe6, 0x0c, 0x0e, 0x87, + 0xaf, 0x9f, 0x11, 0x35, 0x1f, 0x97, 0x25, 0x9e, 0x20, 0x22, 0xe5, 0xfe, + 0x91, 0x9b, 0x71, 0xda, 0xd7, 0x9b, 0x05, 0x11, 0xbd, 0x2d, 0x9f, 0x6d, + 0x96, 0x54, 0xd2, 0x2b, 0xfe, 0x24, 0xa2, 0xa0, 0x25, 0x56, 0xec, 0x0a, + 0xcd, 0x19, 0x23, 0x31, 0x4b, 0x38, 0x69, 0xe0, 0x86, 0x67, 0xce, 0xe4, + 0xf5, 0xdc, 0x46, 0xf5, 0x72, 0x6e, 0xe1, 0x7f, 0x86, 0x01, 0x98, 0xd4, + 0x40, 0xd0, 0x86, 0xc5, 0x67, 0x5e, 0x1c, 0x31, 0xbe, 0xf0, 0x58, 0xe1, + 0x1f, 0x2c, 0xa8, 0xd4, 0x9f, 0x01, 0xf5, 0x7b, 0x14, 0x32, 0xb8, 0x7e, + 0xbf, 0xa7, 0x8e, 0xc6, 0x09, 0xd4, 0x19, 0x3f, 0x88, 0x3d, 0xf8, 0x1b, + 0xe7, 0x5b, 0xb5, 0x7a, 0xf5, 0x0f, 0x2f, 0x17, 0x51, 0xd7, 0x23, 0x02, + 0x87, 0x73, 0x68, 0x0b, 0xdf, 0x84, 0x63, 0xe6, 0xba, 0xb8, 0xc2, 0x88, + 0x9e, 0xcd, 0x5b, 0x25, 0x99, 0x6d, 0x99, 0x1a, 0xcb, 0x49, 0xae, 0x0d, + 0xb1, 0x59, 0x7d, 0x6f, 0x3a, 0x62, 0x0b, 0x20, 0x21, 0xed, 0x40, 0xf2, + 0x50, 0x83, 0xd6, 0x87, 0x00, 0x26, 0x1f, 0x1f, 0x84, 0xf6, 0x2c, 0xb8, + 0x64, 0x25, 0x59, 0x77, 0x7a, 0x41, 0x4d, 0xdc, 0xcd, 0x47, 0x4e, 0x24, + 0x98, 0x88, 0x45, 0x76, 0xee, 0xf7, 0x39, 0xc8, 0x2e, 0xd2, 0x30, 0x04, + 0x17, 0x23, 0x0d, 0xaa, 0xa8, 0x1e, 0x13, 0x77, 0x2e, 0x55, 0x95, 0x8d, + 0x3c, 0x8e, 0x06, 0xe0, 0x98, 0xb3, 0xe4, 0x97, 0xba, 0x9c, 0x28, 0x35, + 0x67, 0xab, 0x6b, 0xcb, 0x40, 0xcb, 0x7a, 0xfe, 0x5a, 0x89, 0xa9, 0x35, + 0x23, 0x2b, 0xad, 0x1a, 0x90, 0xd8, 0x61, 0xcc, 0x05, 0xbd, 0x37, 0x3b, + 0x52, 0x20, 0x28, 0xa0, 0xc6, 0x17, 0xc9, 0x3f, 0x83, 0x42, 0x37, 0x7d, + 0x7d, 0x60, 0x43, 0x1b, 0xbf, 0xba, 0xa3, 0xba, 0x3e, 0x5a, 0xdb, 0x33, + 0x5c, 0x33, 0x36, 0x3b, 0xb3, 0xc1, 0x98, 0x87, 0xe4, 0x0b, 0x94, 0xcc, + 0xc6, 0xcb, 0xd7, 0xe5, 0xd5, 0x31, 0x65, 0xc4, 0xdd, 0x09, 0x48, 0x05, + 0x60, 0x6a, 0xcf, 0x67, 0x37, 0x42, 0x78, 0x67, 0xbc, 0xa9, 0x7a, 0x1c, + 0x7d, 0x98, 0x27, 0x6c, 0x63, 0x31, 0xdc, 0x0f, 0x5d, 0xa8, 0x09, 0x7a, + 0xef, 0x78, 0x8c, 0x13, 0xd5, 0x1a, 0x03, 0xeb, 0x88, 0xcb, 0x01, 0x8f, + 0x73, 0x39, 0x5f, 0x88, 0x94, 0xdd, 0xd9, 0x73, 0xcc, 0x67, 0x1a, 0xa7, + 0x2e, 0x4a, 0xa1, 0x33, 0x7a, 0xf5, 0xcd, 0x8d, 0x36, 0x78, 0xb1, 0x8c, + 0x2d, 0x8e, 0xc7, 0x89, 0x17, 0xa5, 0x3c, 0xf8, 0x67, 0x4c, 0xe4, 0x31, + 0xdf, 0x77, 0xf9, 0x8b, 0xab, 0xb5, 0x80, 0x51, 0x69, 0xc9, 0x30, 0x5d, + 0x00, 0x06, 0x9f, 0xd4, 0xb5, 0x2c, 0x2a, 0xf5, 0x26, 0x6a, 0x5b, 0x92, + 0x3c, 0x6c, 0xae, 0x42, 0x49, 0x5c, 0xd9, 0x5e, 0xb0, 0x63, 0x78, 0x8d, + 0x1f, 0x68, 0x61, 0x05, 0xc8, 0xd0, 0x27, 0xa4, 0x18, 0xdb, 0x27, 0x33, + 0x07, 0xe0, 0x20, 0x4c, 0xf9, 0xc0, 0x91, 0xc7, 0x90, 0xb2, 0x7c, 0x9d, + 0xf6, 0xa0, 0x8d, 0x21, 0xd8, 0x08, 0x8c, 0x16, 0x78, 0x5c, 0xa9, 0x9b, + 0xf8, 0xfb, 0xcb, 0xa7, 0x49, 0x0e, 0xfd, 0xe7, 0x6b, 0x07, 0x6b, 0x08, + 0x7e, 0x88, 0xdc, 0x1d, 0xc5, 0x62, 0xd1, 0x3b, 0x51, 0xb1, 0x0e, 0x12, + 0xf1, 0xa3, 0xba, 0x96, 0xf3, 0x71, 0xe5, 0xe3, 0xf1, 0x07, 0x09, 0x18, + 0xee, 0x37, 0x93, 0x52, 0xf3, 0xf7, 0xc2, 0xa2, 0x36, 0x77, 0x4e, 0x89, + 0xf2, 0x76, 0xcb, 0x96, 0x74, 0x1d, 0x71, 0xe7, 0xb3, 0x51, 0xf2, 0x26, + 0x2f, 0x1e, 0x86, 0x32, 0xe9, 0xb8, 0x45, 0xb6, 0x84, 0xaa, 0x04, 0x23, + 0xd7, 0x92, 0x01, 0x3d, 0x64, 0x06, 0x35, 0xcd, 0x72, 0xc8, 0xbd, 0xb0, + 0x78, 0x3c, 0x2d, 0x83, 0x2b, 0x26, 0x0a, 0xc9, 0x89, 0x27, 0x76, 0x58, + 0xdb, 0xa4, 0x93, 0xea, 0xdb, 0x21, 0xa2, 0x70, 0xa6, 0xb5, 0x78, 0x40, + 0xe9, 0xa7, 0xee, 0xb9, 0x59, 0x19, 0x32, 0x2c, 0xfd, 0xea, 0x1b, 0xdc, + 0x68, 0x49, 0x81, 0x17, 0x9e, 0x94, 0x93, 0x4e, 0x21, 0xc7, 0xe8, 0x1a, + 0x59, 0x2f, 0xe4, 0x02, 0x8f, 0x05, 0xcf, 0x73, 0x0a, 0xd1, 0x0b, 0x2e, + 0x6e, 0xe1, 0x16, 0xa3, 0x2f, 0x1d, 0x6a, 0x64, 0x9d, 0x8a, 0xca, 0xf6, + 0x28, 0x9f, 0x3c, 0x0f, 0x26, 0x8a, 0x82, 0x9b, 0x75, 0xbf, 0x59, 0xd1, + 0xc1, 0xaa, 0x03, 0xaa, 0x5a, 0x37, 0x29, 0xc3, 0x09, 0xb0, 0x7f, 0xe2, + 0xd0, 0xc9, 0x29, 0xc9, 0x01, 0xb0, 0xaa, 0x52, 0xb4, 0x55, 0x42, 0xe9, + 0xe8, 0x65, 0xbc, 0xe0, 0x70, 0x1c, 0x77, 0xf5, 0xa8, 0x03, 0xcb, 0x0d, + 0xd9, 0x0d, 0x24, 0xaf, 0xf9, 0xd7, 0x7e, 0xef, 0x8c, 0xdb, 0x17, 0xef, + 0x5e, 0x1b, 0xb9, 0x2d, 0x8d, 0xc7, 0x37, 0x7b, 0x64, 0x8a, 0xf6, 0x8b, + 0xb4, 0x33, 0x62, 0xf7, 0x7d, 0xe9, 0x0c, 0x22, 0xe6, 0x87, 0xb7, 0xc9, + 0xd5, 0x01, 0x18, 0xb5, 0x78, 0x39, 0x0f, 0x43, 0xfe, 0x3e, 0x8c, 0x64, + 0x7a, 0xbd, 0x56, 0x34, 0x42, 0x6b, 0xf0, 0x63, 0x9f, 0x7e, 0x8b, 0x09, + 0x6a, 0xc6, 0x57, 0x49, 0xc1, 0x66, 0x26, 0x3d, 0xae, 0xba, 0xf7, 0xe5, + 0x68, 0x3f, 0xd3, 0x22, 0x6a, 0xc2, 0xa8, 0x8d, 0xce, 0xc0, 0x5d, 0x64, + 0xf3, 0x06, 0x0d, 0x8e, 0xbf, 0xeb, 0x7e, 0xd4, 0x7d, 0x6d, 0x81, 0xd5, + 0x55, 0xc1, 0x4b, 0x18, 0x11, 0x4e, 0xfd, 0x01, 0xbe, 0xaf, 0x72, 0x7f, + 0xf9, 0xa1, 0x66, 0x08, 0xea, 0x26, 0x17, 0x53, 0x02, 0x4c, 0x25, 0x06, + 0x1a, 0x9c, 0xf7, 0xcc, 0x73, 0x44, 0x73, 0x8a, 0xd1, 0xe6, 0x87, 0x44, + 0xb3, 0x45, 0x98, 0x8a, 0xa8, 0xb2, 0xfa, 0x35, 0x2a, 0x6c, 0x7f, 0x44, + 0x57, 0x2e, 0x0f, 0xef, 0xd3, 0x2d, 0xb0, 0xcc, 0x2d, 0xcc, 0xa0, 0x34, + 0xc5, 0x54, 0xf4, 0xed, 0x1f, 0x07, 0x38, 0x07, 0x8b, 0x39, 0xe4, 0x52, + 0x3d, 0x88, 0x04, 0x2a, 0x15, 0x63, 0xdd, 0xaf, 0x5f, 0x79, 0x01, 0xbd, + 0x9b, 0x86, 0xe8, 0x9b, 0x87, 0x7b, 0x9e, 0x24, 0xd4, 0xd0, 0x1b, 0x7c, + 0x57, 0x0f, 0x2c, 0x9b, 0x6e, 0x40, 0x2f, 0xf6, 0xa5, 0x43, 0x6a, 0x3e, + 0xab, 0x8b, 0x84, 0x28, 0x9a, 0x5d, 0x85, 0x17, 0x47, 0x36, 0x63, 0x1e, + 0xd0, 0x9f, 0x28, 0xf8, 0xfd, 0x8a, 0xc5, 0xf5, 0x0d, 0x76, 0x62, 0x77, + 0x95, 0xf7, 0xbb, 0x2a, 0x5f, 0x87, 0x70, 0x8d, 0xc9, 0xe1, 0xf9, 0x83, + 0x3e, 0xd0, 0xee, 0x97, 0x07, 0x59, 0xcb, 0x25, 0x37, 0x4f, 0x9c, 0x89, + 0xfc, 0x41, 0xea, 0x72, 0x24, 0x12, 0x30, 0xb5, 0x0a, 0xc4, 0x81, 0xd0, + 0x78, 0xd3, 0x0c, 0xc1, 0xd4, 0x0c, 0x57, 0x2b, 0x65, 0xb6, 0x3e, 0xa9, + 0xb7, 0x98, 0xa9, 0x30, 0xc3, 0xac, 0x7b, 0x3d, 0x71, 0x25, 0xc7, 0xe1, + 0x99, 0xc9, 0xff, 0x2d, 0xc5, 0x9f, 0x95, 0x54, 0x98, 0x0c, 0xba, 0x55, + 0xa2, 0xea, 0x87, 0xac, 0xd5, 0xba, 0x63, 0x34, 0x02, 0xbf, 0xc3, 0x96, + 0x8f, 0x84, 0xec, 0x5c, 0xea, 0x49, 0xbe, 0xf9, 0x1e, 0x15, 0x74, 0xc3, + 0x28, 0x22, 0xff, 0xeb, 0xf4, 0x30, 0x67, 0x4a, 0x40, 0x03, 0x30, 0x08, + 0x5b, 0xa4, 0xd2, 0x18, 0xd9, 0xec, 0xdd, 0x14, 0x4f, 0xe3, 0x6c, 0xda, + 0x66, 0x95, 0xd0, 0x32, 0x7b, 0x16, 0x83, 0x1c, 0x6c, 0x6c, 0x66, 0x31, + 0x40, 0x9b, 0xa4, 0x6e, 0x29, 0x39, 0xf2, 0x2d, 0x5c, 0xad, 0x76, 0x04, + 0xa1, 0x50, 0x03, 0x2d, 0x8e, 0xdb, 0xcb, 0x42, 0xb1, 0x9f, 0x98, 0xd9, + 0x5a, 0xf9, 0x14, 0xc4, 0xd1, 0x52, 0x48, 0x9c, 0x2b, 0x7e, 0x3c, 0x12, + 0x5f, 0xa8, 0x56, 0x76, 0x88, 0x2b, 0xb7, 0xfd, 0x16, 0x7a, 0x28, 0xb6, + 0xb3, 0xdf, 0xb0, 0xe8, 0x05, 0x3b, 0x79, 0xdd, 0x73, 0x7f, 0x8e, 0x33, + 0x61, 0x1b, 0xfa, 0x5c, 0x49, 0xc2, 0x21, 0xf6, 0x7c, 0x3b, 0x6a, 0x6f, + 0x08, 0x75, 0x91, 0x74, 0x75, 0xe8, 0x14, 0xea, 0xbe, 0xeb, 0x30, 0x68, + 0x92, 0x3a, 0xb7, 0x25, 0x53, 0x13, 0x0c, 0xc8, 0x17, 0x4d, 0x70, 0xeb, + 0xa8, 0x9d, 0xa1, 0x57, 0xbb, 0x20, 0x0a, 0x0b, 0x01, 0x06, 0x95, 0xd7, + 0x83, 0xe6, 0xe8, 0x58, 0x38, 0x67, 0x17, 0xf8, 0x9a, 0x3e, 0x30, 0xb3, + 0xfa, 0xea, 0xb0, 0x95, 0x12, 0x24, 0x9f, 0x35, 0x8a, 0x9c, 0xbf, 0xdf, + 0x2c, 0xff, 0xce, 0xb1, 0x52, 0x07, 0xc5, 0x3c, 0xaa, 0x6a, 0x2e, 0x9f, + 0x63, 0x7c, 0xbd, 0xd3, 0x0d, 0xe8, 0xf4, 0x9d, 0x0c, 0xce, 0x67, 0xdc, + 0x5e, 0x35, 0xfa, 0x51, 0xae, 0xfe, 0xca, 0x2a, 0x25, 0x83, 0x80, 0xd0, + 0x7e, 0xe0, 0x49, 0x4a, 0x4f, 0xde, 0x97, 0xc0, 0x28, 0x93, 0xd5, 0x03, + 0xf1, 0xcc, 0xc1, 0x25, 0x19, 0xc5, 0xc3, 0xdc, 0x8c, 0xe4, 0x45, 0x19, + 0x1b, 0x8a, 0x79, 0xc8, 0xb5, 0x14, 0x13, 0x8e, 0xe8, 0xef, 0xe0, 0xeb, + 0xff, 0x4c, 0x89, 0x4d, 0x8d, 0xf4, 0xd2, 0x55, 0xca, 0x3e, 0x5b, 0x8b, + 0xd2, 0x12, 0x36, 0x47, 0x88, 0x9b, 0x6d, 0x9d, 0xcd, 0x29, 0x3a, 0x30, + 0x80, 0x89, 0x9a, 0x7c, 0x9c, 0x84, 0xf9, 0x63, 0xae, 0x13, 0x6f, 0x5d, + 0x24, 0xb9, 0x58, 0xd3, 0x16, 0xb8, 0x46, 0xc9, 0xcc, 0x0f, 0xcf, 0xee, + 0x11, 0xfb, 0xfa, 0x8f, 0x4a, 0x3b, 0x4b, 0x29, 0x64, 0x13, 0x20, 0x8b, + 0xe4, 0x96, 0x8e, 0x9a, 0xa9, 0xe2, 0xc8, 0x74, 0x64, 0x21, 0x21, 0x6d, + 0x1d, 0x42, 0xcb, 0x3e, 0xce, 0x07, 0x11, 0x2a, 0xbc, 0x11, 0x4f, 0xfe, + 0xa8, 0x61, 0x80, 0x93, 0xcf, 0x19, 0xac, 0xdb, 0x96, 0xde, 0x59, 0xad, + 0x47, 0x4e, 0x46, 0x83, 0x5f, 0x56, 0x3c, 0x8a, 0xca, 0x01, 0x4d, 0x67, + 0xea, 0x27, 0xa2, 0x41, 0xc4, 0xda, 0x76, 0xf7, 0x00, 0x17, 0x6e, 0x44, + 0x36, 0x30, 0x86, 0x24, 0x4a, 0x5f, 0x03, 0xb5, 0x42, 0xa7, 0xc6, 0xd0, + 0xba, 0x49, 0x43, 0x39, 0x55, 0xc8, 0xa3, 0xf7, 0x85, 0x40, 0xce, 0x34, + 0x26, 0x4e, 0xb8, 0x2d, 0x4f, 0x38, 0xca, 0x7b, 0xe9, 0x30, 0x88, 0xc8, + 0xb5, 0xd0, 0x9a, 0x71, 0x4a, 0x6b, 0x30, 0xf0, 0x3a, 0xca, 0x6f, 0x26, + 0x3e, 0x21, 0x4c, 0x46, 0x91, 0x8f, 0x67, 0xab, 0xbe, 0xf7, 0x1f, 0xeb, + 0xc7, 0x8e, 0x00, 0x5e, 0x29, 0x22, 0x6d, 0x85, 0xe3, 0x08, 0x9f, 0x49, + 0xf8, 0x78, 0xa0, 0xc9, 0x2f, 0x63, 0xf9, 0x3e, 0xab, 0x89, 0x5a, 0x8d, + 0x72, 0xe5, 0x91, 0xd7, 0x6c, 0x39, 0xe6, 0x13, 0x0e, 0xd0, 0xd7, 0x5f, + 0x38, 0x67, 0x87, 0x28, 0x30, 0x2e, 0x5e, 0x7c, 0x69, 0x6b, 0x3b, 0x3d, + 0x3d, 0x49, 0x8e, 0xf6, 0x77, 0xe5, 0xfe, 0xe1, 0x74, 0xf9, 0x46, 0xda, + 0x96, 0x98, 0xac, 0x8a, 0x8f, 0xed, 0x07, 0xdd, 0xea, 0x09, 0xa4, 0x97, + 0x3f, 0x12, 0x40, 0x57, 0xc2, 0xfd, 0x76, 0xe6, 0xd8, 0x81, 0x5d, 0xc4, + 0x54, 0x2f, 0x52, 0x1d, 0x76, 0xeb, 0x83, 0x37, 0x48, 0xca, 0xc3, 0x8e, + 0x9a, 0x87, 0x8a, 0xe3, 0xbd, 0xfe, 0x90, 0x4f, 0x5a, 0xd1, 0xed, 0x44, + 0xe8, 0x1f, 0x2a, 0x1c, 0x5c, 0xd1, 0x37, 0x17, 0x97, 0x7a, 0x91, 0x76, + 0x42, 0x3b, 0xa2, 0x97, 0x8a, 0x45, 0xca, 0xdc, 0x47, 0x22, 0x43, 0xd7, + 0xf6, 0x89, 0x46, 0xfe, 0x9a, 0xbe, 0xe9, 0xbd, 0xb2, 0x91, 0x43, 0x40, + 0x39, 0x98, 0xe2, 0xf9, 0xa7, 0xac, 0x5f, 0x15, 0x80, 0x80, 0x5e, 0xa9, + 0xb1, 0xf6, 0x8d, 0x72, 0xed, 0xb4, 0x24, 0x5a, 0x0d, 0xfe, 0x79, 0x70, + 0x4f, 0xda, 0x98, 0xb1, 0xa8, 0x05, 0xf7, 0x03, 0x39, 0x24, 0x5a, 0x8e, + 0x6c, 0xe0, 0x1c, 0x53, 0xf3, 0xf6, 0x1a, 0x9c, 0x25, 0x53, 0xeb, 0xff, + 0xa9, 0x95, 0x15, 0x79, 0x1c, 0xc7, 0xbe, 0xb2, 0xb0, 0x94, 0xa4, 0x33, + 0xd8, 0x07, 0xfe, 0x0b, 0x3d, 0xfd, 0x91, 0xbe, 0x13, 0x00, 0xa5, 0x6f, + 0x82, 0xe8, 0x4e, 0xae, 0x99, 0xb7, 0xdf, 0x67, 0x58, 0x87, 0xdb, 0x10, + 0xc0, 0x48, 0xf3, 0x51, 0x70, 0x35, 0xc4, 0x0d, 0x81, 0x14, 0xbd, 0x2f, + 0x73, 0x60, 0x2c, 0x39, 0xcc, 0xac, 0xa2, 0x3b, 0x05, 0x5b, 0xde, 0x43, + 0x48, 0xc0, 0x82, 0x2c, 0xf8, 0xc8, 0x3f, 0x5b, 0xbf, 0x8e, 0x3e, 0x7f, + 0x50, 0x65, 0x61, 0x48, 0xb4, 0xc8, 0xa9, 0x4e, 0x4e, 0x42, 0xcc, 0x66, + 0x6a, 0x74, 0x3e, 0x0e, 0x3b, 0x71, 0xbc, 0xf0, 0x66, 0x11, 0x8e, 0xc9, + 0x67, 0x6c, 0xd2, 0x82, 0x36, 0x16, 0xaf, 0x52, 0x8d, 0xbf, 0xbe, 0xbe, + 0x27, 0x32, 0xd1, 0x86, 0xa8, 0x5c, 0xaa, 0xc5, 0xb7, 0x38, 0xb9, 0x8f, + 0x65, 0x88, 0x41, 0x1b, 0xbd, 0x6d, 0xcc, 0x87, 0xa5, 0x0d, 0x31, 0x9b, + 0x1e, 0xa5, 0x1e, 0x01, 0xfe, 0x66, 0x18, 0x4c, 0xcd, 0x35, 0x42, 0xd8, + 0x04, 0x52, 0x31, 0x82, 0x30, 0x7f, 0xb7, 0x18, 0xb0, 0x6b, 0x90, 0xa8, + 0x43, 0x31, 0xa8, 0xca, 0x12, 0xd1, 0xa8, 0x4d, 0x9b, 0x37, 0xf2, 0x31, + 0xb8, 0xf4, 0xfc, 0x9f, 0x47, 0x36, 0xcb, 0x15, 0xc8, 0xb8, 0x31, 0xa4, + 0xb4, 0x1b, 0xa3, 0x94, 0xcc, 0xce, 0x59, 0x6b, 0x6a, 0x7c, 0xb5, 0x3e, + 0x41, 0xfe, 0x93, 0x8c, 0x2c, 0xce, 0x3e, 0xeb, 0x67, 0x8b, 0x06, 0xe5, + 0xd5, 0x86, 0x17, 0x3a, 0x52, 0xd2, 0x7a, 0x70, 0x2f, 0x53, 0x91, 0x2f, + 0x8e, 0x7e, 0x34, 0x65, 0xc8, 0x2a, 0xb0, 0x13, 0xb1, 0x2f, 0x94, 0xab, + 0x1d, 0xfd, 0x47, 0xb1, 0xf9, 0xa2, 0xd1, 0xc4, 0xd5, 0x8a, 0x11, 0xdc, + 0xb9, 0xb8, 0x5c, 0x07, 0x45, 0x01, 0x69, 0x8a, 0x67, 0x7b, 0x1b, 0x74, + 0x5c, 0xb5, 0x61, 0x78, 0x99, 0x07, 0x6b, 0x91, 0x63, 0x29, 0x69, 0x77, + 0xde, 0x23, 0x54, 0x25, 0x95, 0x2f, 0x06, 0x1f, 0x76, 0x3e, 0x99, 0xfd, + 0x98, 0xe2, 0x77, 0x6e, 0x9f, 0x98, 0xdf, 0xc8, 0x8b, 0x70, 0x1f, 0xbe, + 0xdf, 0xea, 0xaa, 0x2b, 0x0e, 0x7a, 0x2c, 0x23, 0xbc, 0x8d, 0xbc, 0x9a, + 0xed, 0x87, 0x7a, 0x7f, 0x59, 0xe0, 0x5b, 0xf6, 0x47, 0x43, 0xec, 0x04, + 0x20, 0x9f, 0x5e, 0xbf, 0x34, 0xac, 0x84, 0x72, 0x98, 0x4f, 0x15, 0x62, + 0x89, 0x5e, 0x40, 0x2a, 0xb5, 0xf2, 0x8d, 0x61, 0xf2, 0x31, 0xfc, 0xea, + 0x25, 0x7d, 0x78, 0xc6, 0xeb, 0x65, 0x83, 0xf9, 0x9c, 0x69, 0x6a, 0x52, + 0xea, 0xb4, 0x27, 0xc8, 0x10, 0x51, 0x11, 0x70, 0xc7, 0x3b, 0xf2, 0xdf, + 0x30, 0x5b, 0x16, 0xee, 0x2c, 0x72, 0x28, 0xa1, 0x18, 0xcf, 0xa3, 0x0b, + 0xd2, 0x20, 0xee, 0xcd, 0x87, 0x5b, 0xd9, 0xe3, 0xe1, 0x13, 0x01, 0x44, + 0x14, 0x05, 0xa6, 0x0a, 0x74, 0x50, 0xc3, 0x8f, 0xc0, 0xcc, 0x98, 0xdc, + 0x78, 0x1c, 0x5b, 0x28, 0x82, 0x39, 0xab, 0x45, 0x45, 0x96, 0xd5, 0xc4, + 0x2e, 0xcc, 0x2c, 0x75, 0xc1, 0x0c, 0xc8, 0x06, 0x3c, 0x28, 0x8c, 0x52, + 0x4d, 0xf7, 0x72, 0x98, 0x88, 0xb4, 0x02, 0x00, 0xd3, 0xd6, 0x62, 0x42, + 0x23, 0x87, 0x55, 0xe9, 0x3c, 0x4b, 0x1f, 0x99, 0xba, 0x01, 0x2f, 0x20, + 0xe4, 0x5e, 0x86, 0x3e, 0xf9, 0x31, 0x74, 0x3a, 0x75, 0x84, 0xeb, 0xa1, + 0x63, 0xfb, 0x9f, 0x8d, 0x8c, 0x16, 0xd4, 0x73, 0x40, 0xdf, 0xc1, 0x46, + 0xb7, 0xbc, 0xef, 0x06, 0x36, 0x23, 0x79, 0x12, 0x84, 0x3e, 0xdb, 0xab, + 0x84, 0x05, 0xd7, 0xe1, 0x64, 0x1e, 0x61, 0x97, 0x77, 0x31, 0x2e, 0x45, + 0xb7, 0x19, 0x07, 0xee, 0x53, 0x4f, 0x44, 0x38, 0x24, 0x05, 0xb2, 0xe9, + 0xa7, 0x10, 0x40, 0x30, 0x85, 0xbc, 0xb8, 0xab, 0xaa, 0xa2, 0x21, 0x2d, + 0xb0, 0x63, 0x02, 0xfc, 0x16, 0x0b, 0x56, 0x2f, 0x86, 0x4d, 0x13, 0x8d, + 0xbd, 0xfe, 0x0f, 0x99, 0xf9, 0x76, 0xc6, 0xfd, 0x6e, 0x74, 0x01, 0x69, + 0x1f, 0x2d, 0xef, 0xb1, 0x76, 0x52, 0x2f, 0x4e, 0x7c, 0x5e, 0x61, 0x06, + 0x87, 0x34, 0x33, 0x8b, 0xd1, 0x40, 0x38, 0xfe, 0x69, 0x9c, 0x92, 0xc9, + 0x60, 0x35, 0xd8, 0xda, 0xee, 0x42, 0x0b, 0x6f, 0x54, 0x93, 0x02, 0xee, + 0x79, 0x5a, 0x2e, 0x56, 0x1c, 0x71, 0x51, 0x22, 0x4b, 0xfa, 0x12, 0x0e, + 0xc4, 0x52, 0x45, 0x17, 0x81, 0xd7, 0xa1, 0x84, 0x50, 0xa4, 0x75, 0xea, + 0xab, 0xbc, 0xc6, 0x8d, 0x6f, 0xfe, 0x1d, 0x24, 0xd4, 0xb8, 0x71, 0x74, + 0x5c, 0x3d, 0x2e, 0x56, 0x8f, 0x23, 0xc5, 0x5c, 0xa2, 0x63, 0x64, 0x6b, + 0x14, 0xf1, 0xff, 0x81, 0x3b, 0xfd, 0x9f, 0x4e, 0x5d, 0xb9, 0x95, 0x9d, + 0xd8, 0x40, 0x45, 0xde, 0xb4, 0x3f, 0xec, 0xdf, 0xe5, 0xf8, 0x5f, 0xd7, + 0x0a, 0x0c, 0x7b, 0x3d, 0xbc, 0xd1, 0x5d, 0xac, 0xc0, 0x30, 0x7e, 0x7c, + 0x1c, 0xd6, 0x8d, 0x87, 0x79, 0x75, 0x87, 0x58, 0xae, 0x4e, 0x1d, 0x7a, + 0x26, 0x7f, 0x8a, 0xe8, 0xbb, 0x37, 0xfe, 0x5c, 0x7c, 0x96, 0x2f, 0x9c, + 0x7e, 0xbc, 0xa6, 0xec, 0x3c, 0x3f, 0x1d, 0xdd, 0x99, 0x8d, 0x35, 0x2d, + 0x1d, 0xe0, 0x26, 0xb5, 0x27, 0x09, 0xf9, 0x45, 0x6e, 0xf4, 0xeb, 0x61, + 0xb5, 0x97, 0x75, 0x2c, 0x8a, 0x3c, 0x19, 0xd5, 0x62, 0xf2, 0xf5, 0x25, + 0xa4, 0x26, 0xc8, 0xdd, 0xee, 0x03, 0x25, 0xcf, 0x0e, 0x8a, 0xdb, 0x85, + 0xe3, 0x09, 0xb9, 0xc1, 0xf6, 0x0d, 0xc4, 0x94, 0x0e, 0x0f, 0x0a, 0xf6, + 0x25, 0x6f, 0x8e, 0xa2, 0x1f, 0x24, 0xfb, 0x93, 0xe3, 0xf0, 0x6c, 0xd9, + 0x1e, 0x4d, 0xee, 0x71, 0x4c, 0xb3, 0x96, 0x64, 0x2c, 0xe1, 0xd8, 0x47, + 0x74, 0x6f, 0x96, 0x63, 0x00, 0x55, 0xac, 0x67, 0x84, 0xca, 0xf7, 0x60, + 0x57, 0x24, 0x13, 0xdb, 0x10, 0x1b, 0x6f, 0xb9, 0x23, 0xff, 0x9a, 0xdd, + 0x15, 0x33, 0x0b, 0xc4, 0xb2, 0x06, 0xd9, 0x31, 0xd1, 0x43, 0x82, 0xf5, + 0x7c, 0x36, 0x9c, 0x22, 0x10, 0x9a, 0x6d, 0xa7, 0x7d, 0xf9, 0x92, 0x76, + 0x53, 0x4e, 0xc4, 0xfc, 0xe9, 0x2d, 0x15, 0x07, 0xdb, 0xcd, 0x0e, 0x63, + 0x04, 0xee, 0xae, 0xbc, 0x6a, 0x60, 0x3a, 0x59, 0x3a, 0x96, 0x0a, 0xcb, + 0x02, 0xd4, 0x5b, 0xc7, 0xd8, 0xf7, 0x0c, 0xcb, 0x70, 0x07, 0xab, 0x9e, + 0x0b, 0x4e, 0x10, 0x0e, 0xf7, 0x8f, 0xb9, 0xc2, 0xd4, 0xbb, 0x4d, 0xe4, + 0x5d, 0x2f, 0x25, 0x7a, 0xd0, 0x74, 0xa5, 0x0e, 0xad, 0x53, 0xcf, 0xa5, + 0x75, 0x7b, 0xd8, 0xd9, 0xaa, 0x79, 0xdd, 0x60, 0x30, 0x9e, 0xc6, 0x84, + 0x49, 0x7c, 0x91, 0x49, 0x48, 0xe1, 0xd9, 0x18, 0xd8, 0x0a, 0x20, 0x4b, + 0xc0, 0xb4, 0x55, 0x92, 0x96, 0x9c, 0x3b, 0x29, 0x6a, 0x91, 0x12, 0x6d, + 0xd3, 0x0f, 0x11, 0xf2, 0x4a, 0x84, 0xac, 0x50, 0x07, 0xac, 0xf4, 0x5b, + 0x48, 0x27, 0x87, 0x95, 0x27, 0x60, 0x71, 0x96, 0x06, 0x34, 0x4b, 0x86, + 0x94, 0x7d, 0xcc, 0x53, 0xf7, 0x9d, 0xa9, 0xd7, 0xd7, 0x97, 0xdf, 0x1a, + 0x58, 0xdd, 0x52, 0x25, 0x67, 0x9a, 0xb6, 0x7e, 0x24, 0x20, 0xfb, 0x3f, + 0x7e, 0x0e, 0xf3, 0xb5, 0x9d, 0xfb, 0x20, 0xaf, 0xf7, 0x74, 0x12, 0xcf, + 0x43, 0x85, 0x67, 0x05, 0xcf, 0x49, 0xf3, 0xf0, 0xd9, 0xd2, 0x9a, 0xcd, + 0xc6, 0x6b, 0x24, 0xcc, 0x2e, 0x72, 0xa4, 0xfd, 0xd3, 0xcf, 0xf6, 0x5b, + 0x16, 0xf2, 0x2e, 0xd1, 0x72, 0xce, 0x73, 0x4c, 0xba, 0xf9, 0xc1, 0xb7, + 0x75, 0xe8, 0x11, 0x3e, 0x26, 0x59, 0x8a, 0xc1, 0x85, 0x84, 0x2e, 0x07, + 0xef, 0xc7, 0xdc, 0x11, 0xb5, 0x94, 0xfe, 0x55, 0x09, 0x52, 0x15, 0x25, + 0x2c, 0x25, 0x02, 0x5e, 0x9e, 0xfc, 0xf1, 0x9e, 0x02, 0x89, 0x4e, 0x72, + 0xc6, 0x85, 0x5d, 0xc1, 0xde, 0x4e, 0x82, 0x6f, 0x65, 0x85, 0x2f, 0xd8, + 0xaa, 0x87, 0x5e, 0xe6, 0xac, 0x4e, 0x97, 0x1f, 0xba, 0x4c, 0x59, 0x70, + 0x0b, 0xd9, 0x9a, 0xac, 0x54, 0x2a, 0xca, 0x38, 0x98, 0xfe, 0x90, 0x5f, + 0x0d, 0x2d, 0xb6, 0x73, 0x08, 0x3c, 0x7a, 0x12, 0xc2, 0x51, 0xcf, 0xa8, + 0x7e, 0x2d, 0xf7, 0x18, 0xcf, 0x61, 0xce, 0x91, 0x07, 0x04, 0xad, 0xd6, + 0x59, 0xb8, 0xf5, 0xac, 0xc5, 0x4b, 0x2f, 0x5e, 0x14, 0xa5, 0xd5, 0x58, + 0x5c, 0x94, 0xc3, 0x7a, 0xfc, 0x17, 0x02, 0x79, 0x5a, 0x06, 0x7f, 0x4b, + 0xc5, 0xb7, 0xc5, 0x26, 0x63, 0x3a, 0x2b, 0x6a, 0xda, 0xa7, 0x67, 0xdc, + 0x1f, 0x34, 0x42, 0xe1, 0x0a, 0xb6, 0xae, 0x2f, 0xa1, 0xf7, 0x28, 0x20, + 0x36, 0xb6, 0xb9, 0xe0, 0xef, 0x74, 0x71, 0x52, 0x66, 0x3b, 0x39, 0xa5, + 0xde, 0x20, 0x37, 0x7e, 0xa1, 0x08, 0x53, 0x44, 0x8e, 0x97, 0x52, 0x22, + 0x9b, 0xa2, 0xba, 0xa1, 0xff, 0x33, 0x26, 0x58, 0xe1, 0x57, 0xf0, 0xbd, + 0xaf, 0x84, 0xf9, 0x9c, 0xb3, 0x5e, 0x7e, 0x17, 0x69, 0xd2, 0xf8, 0x14, + 0xfc, 0x74, 0x22, 0x0d, 0x00, 0xcc, 0x09, 0xba, 0x7f, 0xbd, 0x22, 0xf9, + 0x6b, 0x37, 0x63, 0xe7, 0x64, 0x92, 0xa0, 0xbf, 0x59, 0x5e, 0xd6, 0x41, + 0x53, 0xe7, 0x10, 0x2b, 0xff, 0x8e, 0x9c, 0x7a, 0xf1, 0xa7, 0xd6, 0x89, + 0x24, 0xc9, 0x6a, 0x48, 0xf4, 0x22, 0x4e, 0x7a, 0xcf, 0x04, 0x7e, 0xab, + 0x25, 0x7d, 0x11, 0x78, 0x40, 0x88, 0x05, 0x72, 0x5a, 0x08, 0xe6, 0x0d, + 0xde, 0xff, 0x9e, 0x0c, 0xd8, 0xad, 0x84, 0xa8, 0x8c, 0xd6, 0x8d, 0x13, + 0x8a, 0xe2, 0x01, 0x30, 0x99, 0xd5, 0xec, 0xec, 0xa7, 0x59, 0x2a, 0x02, + 0x06, 0x0e, 0x83, 0x8b, 0x3b, 0xa9, 0xc6, 0xfc, 0xf1, 0x9b, 0x3c, 0xd1, + 0xa1, 0x8a, 0xfe, 0x73, 0x71, 0xae, 0xe9, 0x11, 0x6d, 0x9f, 0x01, 0x41, + 0xf4, 0xb5, 0x16, 0x9b, 0xfc, 0x2b, 0x70, 0x27, 0x51, 0x8e, 0x73, 0x11, + 0xd2, 0xbf, 0x81, 0xac, 0xf6, 0xdf, 0xa7, 0x30, 0xc5, 0x4c, 0xc2, 0x45, + 0xb3, 0x22, 0xd8, 0xad, 0x7c, 0x28, 0x20, 0x1b, 0x86, 0x0d, 0xdd, 0x99, + 0x5b, 0x12, 0x4a, 0x51, 0xff, 0xd8, 0xaa, 0xfe, 0x65, 0x04, 0xe3, 0x65, + 0x96, 0x7c, 0xac, 0x89, 0xc7, 0xf1, 0x7b, 0x81, 0xf1, 0xb3, 0x45, 0x4b, + 0x58, 0x60, 0x50, 0xdf, 0xec, 0x4f, 0xba, 0x03, 0x1b, 0xdf, 0x67, 0xd4, + 0xe0, 0x51, 0xe2, 0xec, 0xde, 0x80, 0x9f, 0x07, 0xba, 0x90, 0xe4, 0x9e, + 0x3a, 0x47, 0x94, 0x6d, 0x5e, 0x00, 0xa9, 0x52, 0x86, 0xac, 0x7e, 0x89, + 0x1f, 0x1d, 0x54, 0xfc, 0x23, 0x55, 0x8d, 0x20, 0xe5, 0x28, 0xdb, 0xc3, + 0xbf, 0x53, 0xb0, 0x21, 0xa5, 0x9c, 0x5d, 0x9c, 0xb3, 0x02, 0x27, 0xc1, + 0xb3, 0xe6, 0x12, 0x3a, 0x80, 0x27, 0xab, 0xc8, 0x78, 0xff, 0x57, 0x46, + 0xfe, 0xf8, 0xfc, 0xdd, 0xdc, 0x13, 0xc9, 0x7a, 0xad, 0x34, 0xcc, 0x35, + 0xf9, 0xf1, 0x13, 0x4f, 0xeb, 0x1a, 0xa5, 0xe4, 0x4c, 0xc0, 0xb6, 0xc9, + 0x86, 0x2c, 0x40, 0xef, 0xcb, 0x6f, 0x38, 0x06, 0x7a, 0x55, 0x16, 0x4f, + 0x9f, 0x78, 0x5d, 0xcc, 0x2c, 0x8a, 0xfe, 0x48, 0x4d, 0xd6, 0x26, 0x9b, + 0x50, 0x98, 0x40, 0x26, 0x40, 0xbf, 0x9a, 0x58, 0x26, 0x57, 0x9d, 0x2e, + 0x8b, 0xdb, 0x8e, 0xe3, 0xdf, 0x90, 0xa2, 0xa0, 0xc8, 0xec, 0xef, 0xf9, + 0xbf, 0x7a, 0xf1, 0x8d, 0x9a, 0x76, 0x19, 0x0e, 0xb0, 0x57, 0xa7, 0xdb, + 0x98, 0x47, 0xc6, 0x6c, 0x81, 0x34, 0x29, 0x9d, 0xe1, 0x60, 0x60, 0xd0, + 0xe9, 0x0b, 0x61, 0x03, 0x19, 0x07, 0x98, 0x2d, 0xed, 0xf0, 0xfd, 0xe6, + 0x24, 0x89, 0x49, 0xe0, 0xb4, 0x63, 0x9d, 0x5a, 0xee, 0xb6, 0x10, 0x0f, + 0xfd, 0x2f, 0x96, 0xa4, 0xde, 0x92, 0xe4, 0x5e, 0x7c, 0x43, 0x69, 0x69, + 0xe2, 0xde, 0xd9, 0x58, 0x76, 0xa0, 0x99, 0xef, 0x86, 0x19, 0x55, 0x13, + 0xc6, 0x40, 0x31, 0xfc, 0x2a, 0x9e, 0x1f, 0x59, 0x3a, 0x77, 0x9e, 0x98, + 0xb1, 0xae, 0xc8, 0x3c, 0x36, 0x9c, 0x41, 0x9e, 0xce, 0x30, 0x06, 0xb6, + 0x0d, 0x0c, 0xe3, 0x3f, 0x54, 0x5b, 0xe0, 0xd6, 0x47, 0x20, 0xbb, 0x4d, + 0x14, 0x61, 0x78, 0x02, 0xa3, 0x38, 0xa5, 0xed, 0xfc, 0x64, 0x84, 0x45, + 0x61, 0xad, 0x02, 0x4f, 0xfb, 0x2e, 0xa5, 0x16, 0x12, 0xac, 0x89, 0xfa, + 0x0b, 0x03, 0x20, 0xc4, 0xf1, 0x74, 0x04, 0x2f, 0x97, 0x48, 0x6f, 0x66, + 0x10, 0xdb, 0xf4, 0x3f, 0x9d, 0x55, 0x98, 0xbc, 0x7b, 0x08, 0xef, 0xaa, + 0x68, 0x3a, 0x6b, 0x29, 0x19, 0x9d, 0xe1, 0x12, 0x06, 0xac, 0xae, 0xe6, + 0xdf, 0x57, 0xf5, 0x6c, 0x87, 0x80, 0x7e, 0x73, 0x16, 0xf1, 0x89, 0x91, + 0x72, 0xda, 0x25, 0xfb, 0xd1, 0x3b, 0xdd, 0x80, 0x14, 0xbe, 0xf6, 0x18, + 0x42, 0x7f, 0x13, 0xf0, 0xeb, 0xe5, 0x44, 0x3f, 0xa6, 0xdf, 0x33, 0xa3, + 0x77, 0xaa, 0x29, 0xff, 0x74, 0x54, 0xdd, 0x1b, 0xab, 0x7e, 0x14, 0x74, + 0x16, 0xe2, 0x68, 0xd3, 0x71, 0x78, 0x91, 0xe9, 0xad, 0x7c, 0x9a, 0x61, + 0xaf, 0x3a, 0xbc, 0xa6, 0xf8, 0x4c, 0x4a, 0xe2, 0x3c, 0x35, 0x40, 0x5e, + 0xbf, 0xcd, 0x6f, 0xaf, 0x13, 0xd2, 0xf1, 0x86, 0x8d, 0xd9, 0x61, 0x20, + 0x9e, 0xa0, 0x49, 0x93, 0x1f, 0x66, 0xd5, 0x10, 0x75, 0x87, 0x4c, 0x5d, + 0x7f, 0xbf, 0xf6, 0xe5, 0x68, 0x48, 0x38, 0x09, 0xde, 0x96, 0xb2, 0xc3, + 0x3e, 0x2f, 0x06, 0xc0, 0x64, 0x28, 0x69, 0x15, 0xc0, 0x10, 0x84, 0xae, + 0x56, 0x7d, 0x21, 0x48, 0xdb, 0xc4, 0x03, 0xb1, 0xd7, 0xd7, 0x39, 0x4d, + 0xad, 0x3d, 0xec, 0x0b, 0xd8, 0x82, 0x2e, 0x38, 0x10, 0x38, 0xa7, 0x74, + 0x19, 0xb2, 0xdb, 0xbe, 0x08, 0xa3, 0x07, 0x41, 0x82, 0x32, 0x15, 0x45, + 0xd1, 0x19, 0xed, 0x87, 0xc6, 0xe7, 0x14, 0x6d, 0x23, 0x92, 0x1b, 0x54, + 0x3a, 0xef, 0x5f, 0x50, 0x91, 0x58, 0x0e, 0x51, 0xa6, 0x61, 0x34, 0x63, + 0x59, 0x0d, 0x56, 0x0c, 0x45, 0x0e, 0xf7, 0xb8, 0xb0, 0x12, 0xc0, 0x8a, + 0xee, 0xea, 0x70, 0x87, 0xaf, 0x99, 0xc8, 0x46, 0x78, 0xd8, 0x7c, 0xc1, + 0xd9, 0x50, 0x97, 0x88, 0x1d, 0x32, 0xe1, 0x4b, 0x8c, 0x36, 0xd5, 0xe2, + 0x2e, 0xae, 0xee, 0xe5, 0x23, 0xbe, 0x5b, 0x38, 0x66, 0xae, 0x0a, 0x32, + 0x16, 0x1c, 0x8a, 0xb2, 0x0b, 0x43, 0x98, 0xcf, 0x69, 0x1b, 0x3b, 0x68, + 0x0e, 0x59, 0xc0, 0x7c, 0x51, 0x31, 0xb1, 0x08, 0x67, 0xb0, 0x2e, 0x3e, + 0xcb, 0x70, 0x00, 0x36, 0xad, 0xd1, 0xaa, 0x8a, 0xfd, 0x73, 0xc6, 0x23, + 0x18, 0xfa, 0x44, 0x2a, 0x76, 0x63, 0x16, 0xf3, 0x91, 0x8f, 0x68, 0xfb, + 0x15, 0x25, 0xa3, 0x3d, 0x28, 0x9e, 0x2c, 0xe6, 0x3c, 0x5e, 0xe2, 0x20, + 0xbc, 0x23, 0x51, 0x13, 0x76, 0x10, 0x5e, 0xae, 0x83, 0x3c, 0xfc, 0xb8, + 0xe9, 0x7a, 0xde, 0x52, 0x0a, 0xbe, 0xf1, 0xe3, 0x6c, 0x7a, 0x9a, 0x7e, + 0x0e, 0xd5, 0xf8, 0xf3, 0xe9, 0x09, 0x8a, 0x46, 0x66, 0x85, 0x4f, 0xa5, + 0xb9, 0xa5, 0xa3, 0x25, 0xb4, 0x57, 0xf1, 0x93, 0x2a, 0x63, 0xcd, 0xa9, + 0xd5, 0xf9, 0x28, 0x87, 0x19, 0x98, 0xda, 0x82, 0x5a, 0xbe, 0x96, 0x6e, + 0x5b, 0x29, 0x67, 0x6f, 0x86, 0x83, 0x60, 0x91, 0xfc, 0xe2, 0x05, 0x9c, + 0x65, 0x96, 0xbc, 0xce, 0xbf, 0x3b, 0x7f, 0xad, 0xe3, 0x84, 0x22, 0x4e, + 0xb0, 0xe9, 0x53, 0xbf, 0xd0, 0x77, 0x06, 0x2b, 0x74, 0x68, 0x8b, 0x61, + 0x59, 0xe2, 0x18, 0x05, 0x38, 0x9b, 0x28, 0xd6, 0x15, 0xea, 0x67, 0xd1, + 0x90, 0x27, 0x50, 0xf7, 0xbf, 0x7a, 0x2a, 0xf5, 0xa2, 0x94, 0x0f, 0x88, + 0x64, 0x1f, 0xfe, 0x39, 0x94, 0x7a, 0xc1, 0x78, 0x89, 0xfd, 0x5e, 0xb1, + 0x5b, 0x27, 0x71, 0x49, 0x0f, 0xb0, 0x7b, 0x56, 0x77, 0x05, 0x17, 0xd7, + 0xbe, 0x71, 0x6e, 0xde, 0xd0, 0x7d, 0x3a, 0x4c, 0xc7, 0x47, 0x57, 0x62, + 0xe6, 0x2a, 0x0f, 0x3d, 0xa8, 0x88, 0xbe, 0x82, 0xc3, 0xef, 0x26, 0x58, + 0x03, 0xff, 0x79, 0x58, 0x34, 0x1d, 0xc3, 0xa8, 0xde, 0x6a, 0xdd, 0xc9, + 0x5a, 0x0f, 0x2c, 0x50, 0x63, 0xbd, 0x3e, 0x5c, 0x9c, 0xb6, 0xb3, 0x9f, + 0x52, 0xa4, 0x88, 0x03, 0x18, 0xc7, 0x77, 0xcc, 0xb9, 0x8a, 0x4c, 0x20, + 0x1c, 0xab, 0xd2, 0x07, 0x76, 0x6f, 0x10, 0xc4, 0x80, 0x9b, 0xf2, 0x99, + 0xd0, 0x1a, 0x18, 0xfc, 0x0d, 0xf9, 0x4a, 0x2e, 0xa3, 0x39, 0x65, 0xef, + 0x6c, 0xe8, 0x74, 0x6f, 0x2e, 0xf8, 0x75, 0x81, 0x0e, 0x51, 0x32, 0xa9, + 0x59, 0x16, 0xc2, 0xb3, 0x75, 0x69, 0xdb, 0x09, 0xbf, 0x83, 0x69, 0x81, + 0x86, 0x8d, 0x35, 0xa2, 0x27, 0x3a, 0xa0, 0x4d, 0xf3, 0x6b, 0xce, 0x3a, + 0x88, 0x19, 0xd7, 0xa8, 0xe4, 0x7d, 0x32, 0xbd, 0xad, 0x8e, 0x95, 0xc1, + 0xc8, 0x12, 0xa6, 0x7c, 0xc7, 0xa6, 0x75, 0xeb, 0x3d, 0xc0, 0xa9, 0x95, + 0x26, 0xc9, 0xf5, 0x01, 0xb7, 0x31, 0xec, 0x9d, 0x3e, 0xdf, 0x4d, 0xde, + 0x1b, 0x8a, 0xf0, 0x5e, 0xd4, 0x80, 0xac, 0x69, 0x79, 0x3e, 0xc4, 0xaf, + 0x8a, 0xf7, 0x63, 0xe1, 0x60, 0xc3, 0x39, 0xb9, 0xd8, 0x3f, 0xec, 0xc2, + 0x38, 0x97, 0xaf, 0xb9, 0x65, 0xbb, 0xce, 0x5b, 0x2e, 0xb3, 0x58, 0xae, + 0x52, 0x34, 0x45, 0x04, 0x69, 0x15, 0xbb, 0x12, 0x5b, 0x10, 0x3a, 0x40, + 0xd7, 0x8b, 0xdb, 0x26, 0xc6, 0xe0, 0xdd, 0xd8, 0xad, 0x98, 0xd5, 0xc8, + 0x8e, 0x35, 0x19, 0xff, 0x4c, 0x55, 0x4b, 0xd3, 0x7d, 0x05, 0x0c, 0x4a, + 0xc7, 0xf5, 0x9a, 0x84, 0xc3, 0x8b, 0x24, 0xdb, 0x31, 0x26, 0x71, 0x8b, + 0x40, 0x7a, 0xe3, 0x31, 0x45, 0x81, 0x42, 0x1b, 0xe9, 0x62, 0x1a, 0xd2, + 0x9c, 0xc0, 0xcc, 0x5e, 0xbc, 0x8c, 0x60, 0x64, 0x02, 0x8a, 0xb8, 0xf7, + 0xb6, 0xf6, 0x36, 0xe1, 0x70, 0xe8, 0xd6, 0x0a, 0xbe, 0xe8, 0x35, 0x61, + 0xe8, 0xbf, 0xcd, 0xa2, 0x31, 0xa1, 0x31, 0x69, 0xfb, 0x3b, 0x9f, 0x0b, + 0x4e, 0x43, 0x37, 0x58, 0xfc, 0xf8, 0xa7, 0xb9, 0x39, 0x4a, 0xdd, 0x65, + 0x2a, 0xac, 0x49, 0x7e, 0xd0, 0x25, 0x6f, 0x21, 0x97, 0x92, 0x9c, 0x74, + 0x45, 0x0e, 0x18, 0xa2, 0x75, 0x42, 0x72, 0xf9, 0x66, 0x19, 0x66, 0xc1, + 0x26, 0x1d, 0x37, 0x2a, 0xcb, 0x31, 0x42, 0x93, 0x4c, 0x30, 0xe1, 0x86, + 0xf8, 0x0a, 0xfc, 0xe6, 0xa1, 0x37, 0x0c, 0xb9, 0xa5, 0xed, 0xc5, 0xda, + 0xae, 0xb5, 0x06, 0x65, 0xa2, 0x71, 0x04, 0x28, 0xbe, 0x36, 0x4c, 0xac, + 0x26, 0x3e, 0x5b, 0xd2, 0x85, 0x89, 0x0c, 0x06, 0xd7, 0xbf, 0x99, 0xa0, + 0xf8, 0xb5, 0x6e, 0x1a, 0x47, 0xf2, 0xf2, 0x54, 0x28, 0x89, 0x94, 0xf3, + 0xa4, 0x02, 0x02, 0xbc, 0x8a, 0xa9, 0x67, 0x5e, 0xbb, 0x0d, 0xd4, 0x34, + 0xce, 0x8f, 0xa2, 0x97, 0x70, 0x59, 0x9e, 0xa1, 0xd9, 0xed, 0x1e, 0x9e, + 0xd2, 0xa8, 0xcf, 0xd7, 0xf8, 0xe4, 0xeb, 0x4f, 0x42, 0xaa, 0x27, 0xd1, + 0xd4, 0xbe, 0xda, 0xdc, 0x23, 0x59, 0xdd, 0x35, 0xfc, 0x4b, 0x1b, 0x1c, + 0xea, 0xb5, 0x86, 0xcd, 0xc5, 0x7c, 0xd9, 0xc3, 0x42, 0xe8, 0xbb, 0x7b, + 0x31, 0x85, 0xec, 0x94, 0x50, 0x4d, 0xe9, 0xad, 0x53, 0x32, 0x59, 0xec, + 0x31, 0xee, 0x6e, 0x19, 0x38, 0x5e, 0xd7, 0x58, 0xee, 0xd6, 0x73, 0xf9, + 0x6f, 0x0b, 0x9a, 0xbb, 0x4a, 0xa1, 0xe4, 0x61, 0x8f, 0xd0, 0xb7, 0x71, + 0x1a, 0x10, 0x17, 0x57, 0x6d, 0xd4, 0xec, 0xb3, 0xb9, 0x44, 0xc7, 0xc9, + 0x15, 0x12, 0xf6, 0xcf, 0x34, 0xbd, 0x69, 0x0c, 0x89, 0x89, 0x59, 0x4d, + 0xc1, 0xe4, 0xad, 0xf4, 0x61, 0xb4, 0xe2, 0xbc, 0xe0, 0xb0, 0x16, 0x42, + 0xb9, 0xd8, 0x94, 0x3e, 0x6d, 0x25, 0xda, 0xb6, 0x06, 0x1c, 0x35, 0x3f, + 0x54, 0x5a, 0x90, 0x12, 0x44, 0x12, 0x71, 0x2f, 0xf9, 0xfa, 0x70, 0x62, + 0x8b, 0x5b, 0x5d, 0x4b, 0x0d, 0xf0, 0x5a, 0x4f, 0xb6, 0x6f, 0x91, 0x29, + 0x1b, 0x74, 0x48, 0xc3, 0x72, 0x78, 0x0e, 0x30, 0x50, 0x8a, 0xd8, 0xc7, + 0x58, 0x74, 0xa4, 0xe8, 0xa5, 0x14, 0x11, 0x7f, 0x76, 0x63, 0x51, 0xe1, + 0xe1, 0x60, 0xb6, 0x5d, 0x6d, 0xa8, 0x1d, 0x74, 0xad, 0x28, 0x6d, 0xb1, + 0x0d, 0xfc, 0x39, 0x5d, 0x08, 0x21, 0x8e, 0x53, 0x87, 0xc4, 0xb6, 0xb4, + 0xf7, 0xb1, 0x61, 0x71, 0x4a, 0x87, 0x10, 0xdc, 0x29, 0x43, 0x03, 0x87, + 0x6f, 0x81, 0x1d, 0x6f, 0xd0, 0xb2, 0x73, 0x7d, 0x61, 0x2f, 0x57, 0x5b, + 0xe8, 0xbd, 0xb3, 0xc8, 0xa6, 0xb8, 0x97, 0xbb, 0xeb, 0xf3, 0x85, 0x27, + 0xe5, 0x18, 0xae, 0xa3, 0xac, 0x58, 0x9a, 0x8a, 0x4b, 0xb3, 0x76, 0xa7, + 0x4a, 0x72, 0x5e, 0x9f, 0x40, 0x36, 0x23, 0xc8, 0x8f, 0x8f, 0xf2, 0x0c, + 0x8f, 0x1f, 0x17, 0x39, 0x3c, 0xe1, 0xbd, 0x6c, 0x26, 0xc0, 0x94, 0x9f, + 0x29, 0xed, 0xbc, 0xcc, 0x16, 0x39, 0xe0, 0xa6, 0x6f, 0x9c, 0x12, 0xa2, + 0x74, 0xab, 0x0f, 0x1d, 0x98, 0x35, 0x7a, 0xe2, 0x50, 0xed, 0x54, 0xeb, + 0x23, 0x5d, 0x10, 0xbd, 0xa0, 0x25, 0x13, 0x37, 0x77, 0x5b, 0x14, 0xf4, + 0x8c, 0x04, 0x50, 0xdf, 0xe3, 0x99, 0x61, 0x34, 0x58, 0xc6, 0x69, 0x54, + 0x08, 0xf1, 0xe0, 0xbb, 0xfb, 0xc4, 0x0a, 0x34, 0xba, 0x50, 0x42, 0xd5, + 0xca, 0xac, 0x1f, 0x40, 0xfe, 0xf3, 0x3e, 0x4f, 0x7b, 0x0c, 0x28, 0xec, + 0x6f, 0x17, 0x1c, 0xe7, 0x70, 0x31, 0x61, 0x7a, 0xd2, 0xee, 0xec, 0x40, + 0xda, 0xa0, 0xa6, 0x6f, 0xf5, 0xd3, 0xdc, 0x62, 0x07, 0xec, 0xd3, 0xb9, + 0x68, 0xb6, 0x71, 0xb2, 0x00, 0xe7, 0xfd, 0x3b, 0x2c, 0x57, 0x0d, 0x79, + 0xef, 0xac, 0x3a, 0x6a, 0xec, 0xc3, 0x1b, 0xa3, 0xfc, 0x63, 0x3c, 0x33, + 0x46, 0x5d, 0xde, 0x0a, 0x4e, 0xc5, 0x50, 0x89, 0xcc, 0xdd, 0x2b, 0xfe, + 0x53, 0xf3, 0x0a, 0x4a, 0x63, 0x43, 0x81, 0xf0, 0xb4, 0x72, 0x0c, 0x9e, + 0xf0, 0xfb, 0xe9, 0xde, 0xb6, 0xe5, 0xfd, 0x41, 0x1c, 0x6c, 0x16, 0xa5, + 0x37, 0xaa, 0x8a, 0xde, 0xa0, 0x26, 0xd8, 0x61, 0x83, 0x07, 0x66, 0x3f, + 0xaa, 0xa4, 0x5b, 0xc3, 0xcf, 0x01, 0xdc, 0xed, 0x9c, 0x98, 0x40, 0x9a, + 0xc0, 0x15, 0x6e, 0xb1, 0x71, 0xef, 0x5f, 0x7d, 0xde, 0xa1, 0xe4, 0x29, + 0xb1, 0xac, 0xa3, 0x79, 0x96, 0xd1, 0x1b, 0xce, 0x0e, 0x65, 0x01, 0x5d, + 0xe7, 0x82, 0xf4, 0xf8, 0x15, 0x51, 0x43, 0x55, 0x9f, 0xb4, 0x8d, 0x3a, + 0x49, 0x6c, 0x61, 0xc0, 0x00, 0x7c, 0x9a, 0xcd, 0x1d, 0x38, 0x42, 0x9c, + 0x1c, 0x51, 0xc0, 0x59, 0x02, 0x7c, 0x40, 0x6d, 0xa4, 0x5e, 0x4c, 0xc9, + 0xd3, 0xd9, 0x09, 0x63, 0x0c, 0xbe, 0x9e, 0xc0, 0xb3, 0x38, 0x5d, 0x20, + 0xec, 0x76, 0x30, 0x85, 0x0e, 0xc8, 0xc4, 0x17, 0x2b, 0xbb, 0x74, 0xa0, + 0x4e, 0x7c, 0x98, 0x86, 0xc6, 0x6c, 0xe4, 0x12, 0x1d, 0x0a, 0xf0, 0x95, + 0x1e, 0xb8, 0xd5, 0x19, 0xcd, 0x15, 0xd7, 0x0c, 0x30, 0xb7, 0x0e, 0xac, + 0x78, 0xd5, 0x6a, 0x83, 0x4a, 0x4e, 0xd5, 0x43, 0x6f, 0x9c, 0x99, 0x08, + 0x04, 0x0f, 0xce, 0x6e, 0x29, 0xf8, 0x0d, 0x09, 0xf6, 0x86, 0x55, 0xb8, + 0xda, 0x28, 0x9e, 0x1e, 0xb7, 0x2e, 0x69, 0x9e, 0x13, 0xcb, 0x3a, 0x1d, + 0x10, 0x89, 0x0c, 0xeb, 0x4c, 0x5f, 0x20, 0x46, 0x5a, 0x70, 0xbe, 0xe9, + 0xa4, 0x0c, 0x55, 0x96, 0x3d, 0x8b, 0x7c, 0xc2, 0xd5, 0x69, 0x5f, 0xd4, + 0xc0, 0x70, 0xc5, 0x27, 0x5e, 0xa6, 0xf3, 0xf3, 0xbe, 0x10, 0x02, 0xba, + 0x48, 0x07, 0x71, 0xe7, 0x7f, 0x11, 0xf0, 0xc9, 0xa5, 0xdf, 0x28, 0xbe, + 0x8c, 0xb1, 0x2b, 0x9a, 0xe1, 0x40, 0x8b, 0xee, 0xae, 0xc4, 0x41, 0x1d, + 0x7d, 0xa1, 0x63, 0xa9, 0x44, 0x1c, 0xa8, 0x38, 0x7d, 0xd5, 0xee, 0x90, + 0xa3, 0x26, 0x46, 0xe8, 0x1c, 0xb6, 0xcf, 0xff, 0x06, 0x6b, 0xc2, 0x91, + 0x2f, 0xe4, 0xc6, 0x8d, 0x7f, 0x82, 0xc6, 0xb1, 0x3f, 0x43, 0x8a, 0xd3, + 0xc0, 0x5a, 0xb5, 0xf9, 0x37, 0x4a, 0x62, 0x10, 0xdd, 0x76, 0x4d, 0x76, + 0x11, 0x9b, 0x59, 0x09, 0xfa, 0xd4, 0x15, 0xe1, 0x55, 0xb5, 0x63, 0x06, + 0xcc, 0xf0, 0xfa, 0x57, 0x6d, 0xbd, 0x49, 0x5c, 0x06, 0xb5, 0x93, 0x8f, + 0x24, 0xb3, 0x58, 0xae, 0x96, 0x35, 0x84, 0xfa, 0xb8, 0x46, 0x69, 0xf4, + 0x52, 0xb5, 0x0a, 0x8d, 0xdf, 0x38, 0xa6, 0xd0, 0x11, 0xd6, 0xc7, 0x00, + 0xa8, 0xe4, 0xe6, 0x79, 0x49, 0xdb, 0xbd, 0x58, 0x70, 0xa8, 0x9e, 0x92, + 0xf6, 0x86, 0xb8, 0x4c, 0x47, 0xb0, 0xc9, 0xaf, 0x27, 0xd5, 0xd1, 0x83, + 0xd4, 0x56, 0x89, 0x95, 0xe1, 0x96, 0x4b, 0x09, 0xd3, 0x8a, 0x45, 0x39, + 0xfb, 0xe6, 0x26, 0x51, 0x42, 0xb7, 0x23, 0xac, 0x2d, 0x07, 0x7d, 0x3f, + 0xe9, 0xa6, 0x08, 0x70, 0x9b, 0xf3, 0xd9, 0x25, 0x91, 0xf6, 0xd4, 0xa8, + 0x2a, 0xf0, 0x20, 0x23, 0x0f, 0xc2, 0xe5, 0x67, 0x1b, 0x04, 0x4c, 0x53, + 0x8c, 0xb3, 0xd2, 0x6b, 0x4c, 0x18, 0x2f, 0x9e, 0xad, 0x6b, 0x65, 0xb7, + 0x64, 0x5b, 0x19, 0x75, 0x39, 0xd3, 0x70, 0xe8, 0x3f, 0x3e, 0xb7, 0x55, + 0xf1, 0xa1, 0x6d, 0x41, 0x45, 0xd4, 0x38, 0xfb, 0x68, 0x27, 0xd0, 0xfc, + 0xf1, 0x98, 0xf8, 0xe6, 0x3d, 0x65, 0x63, 0x77, 0x4e, 0x83, 0x1f, 0xe3, + 0x35, 0xad, 0x5e, 0xe9, 0x31, 0x6a, 0xbe, 0x77, 0xc6, 0x33, 0xbc, 0x8e, + 0x46, 0xc2, 0x5c, 0xf0, 0x7b, 0xad, 0xe4, 0x6c, 0x2f, 0xb0, 0x49, 0xf5, + 0x5f, 0xe5, 0xec, 0x5f, 0x42, 0xec, 0xd7, 0x6e, 0x91, 0xf9, 0x7d, 0xe7, + 0xd2, 0x9b, 0xbd, 0x1c, 0x91, 0x44, 0xc0, 0x95, 0xf9, 0x96, 0xfe, 0x1a, + 0xf2, 0x97, 0x03, 0x85, 0x56, 0x7d, 0x03, 0xb1, 0xc2, 0x75, 0xb3, 0xc6, + 0x8d, 0x75, 0x71, 0x6e, 0x62, 0x38, 0xad, 0xc6, 0x3e, 0xdd, 0x9a, 0x3b, + 0xc2, 0x2f, 0xf7, 0x4d, 0xb3, 0x61, 0x9a, 0xca, 0xf9, 0xe7, 0x69, 0x10, + 0xd0, 0xaf, 0xa7, 0x77, 0x0a, 0x46, 0x1f, 0x88, 0x52, 0x34, 0xbb, 0x9e, + 0xd6, 0x71, 0x22, 0xc2, 0x70, 0xfe, 0x4d, 0x65, 0x13, 0x36, 0x5c, 0x5f, + 0x9d, 0xfe, 0xaa, 0xa6, 0xe7, 0x80, 0x8e, 0x0e, 0x74, 0xf6, 0x2b, 0x7e, + 0xca, 0xc5, 0x88, 0xc6, 0xb4, 0xc3, 0xa0, 0x99, 0xd8, 0x83, 0x8d, 0xca, + 0x35, 0x14, 0x31, 0x39, 0xbe, 0xbf, 0x6b, 0xbc, 0x9a, 0x5d, 0x39, 0x80, + 0xbd, 0x33, 0xe8, 0x19, 0xf5, 0x08, 0x76, 0x60, 0xec, 0xe8, 0x59, 0xcb, + 0xd8, 0x41, 0x74, 0x71, 0x26, 0xe6, 0x41, 0x6e, 0x66, 0x45, 0xda, 0xf2, + 0xc1, 0x47, 0xf1, 0xf5, 0xd7, 0xf0, 0x24, 0x07, 0x37, 0xc4, 0x5b, 0x90, + 0x53, 0x3a, 0x88, 0x35, 0xb0, 0x6b, 0xef, 0xe9, 0x43, 0x69, 0x1f, 0x23, + 0x9d, 0x8b, 0xdf, 0x79, 0x02, 0xc3, 0x83, 0x9c, 0xd7, 0x6a, 0x81, 0x6a, + 0x0b, 0xaa, 0x87, 0xe3, 0xba, 0x2c, 0x8b, 0x03, 0xe6, 0xbb, 0xfd, 0xa4, + 0x7b, 0x66, 0xaa, 0xac, 0x2d, 0x98, 0x6d, 0xa2, 0x93, 0xf6, 0x7a, 0x46, + 0x25, 0x98, 0x10, 0xac, 0xf9, 0x66, 0x75, 0x4d, 0x6d, 0x88, 0xb7, 0xcb, + 0xdd, 0x6a, 0xb8, 0x40, 0x8e, 0xa0, 0x3c, 0xe3, 0xe9, 0x7d, 0x86, 0xad, + 0x0c, 0x80, 0x31, 0x4d, 0x16, 0x69, 0xe9, 0x45, 0xfb, 0x13, 0x94, 0xd6, + 0xc9, 0x91, 0x3a, 0x0d, 0xa0, 0x60, 0xa5, 0xcd, 0x5e, 0xbe, 0xca, 0x93, + 0x7f, 0x47, 0x24, 0xd2, 0x3a, 0xe2, 0xe2, 0x1e, 0x0f, 0x72, 0x93, 0x39, + 0x7d, 0x55, 0xed, 0x51, 0x2c, 0xb6, 0x9b, 0x80, 0xaf, 0x0c, 0x39, 0x5e, + 0x5c, 0x7a, 0x5d, 0x9c, 0x38, 0x74, 0x8c, 0x13, 0xcb, 0xd3, 0x52, 0x5a, + 0x52, 0x22, 0xc5, 0x4f, 0xf0, 0xdb, 0x1d, 0xd2, 0x00, 0xd7, 0x50, 0x29, + 0x6c, 0x61, 0x9e, 0x5f, 0xb8, 0x5d, 0x29, 0x1c, 0xbc, 0x0e, 0x8d, 0x34, + 0x0c, 0x0f, 0x9c, 0xbe, 0x27, 0x1c, 0xe1, 0xf7, 0xb7, 0x48, 0x13, 0x61, + 0x02, 0xc6, 0xfd, 0xcb, 0xc7, 0x8f, 0x25, 0x70, 0xc1, 0x52, 0x10, 0x7b, + 0x02, 0x9b, 0xce, 0x69, 0x4b, 0x97, 0x37, 0x11, 0x1b, 0x53, 0x1f, 0xb4, + 0x45, 0x7c, 0x35, 0xc0, 0x95, 0x6e, 0x6e, 0x98, 0x77, 0x13, 0x2c, 0xb7, + 0xd1, 0x2e, 0x8c, 0xb7, 0xa5, 0xaa, 0x41, 0x2f, 0x54, 0xc4, 0xf2, 0x35, + 0xd3, 0x62, 0x57, 0x6b, 0x40, 0xa3, 0x63, 0xe8, 0x0a, 0x62, 0x18, 0xe6, + 0x8a, 0x80, 0xc1, 0x50, 0xc4, 0xfa, 0x92, 0xbc, 0xed, 0xb9, 0x06, 0xb1, + 0xf8, 0x85, 0xde, 0x9c, 0x1c, 0x22, 0x45, 0xe1, 0xca, 0x6f, 0xbe, 0x7a, + 0x53, 0xf3, 0x72, 0xe1, 0x76, 0x40, 0x91, 0x5e, 0xd1, 0x13, 0x99, 0xa0, + 0x9c, 0xe8, 0x8b, 0x0c, 0x96, 0x60, 0x05, 0xda, 0x51, 0x30, 0x12, 0x67, + 0xed, 0x3e, 0xd6, 0xa7, 0x79, 0x48, 0x51, 0xf7, 0x51, 0xa5, 0xa2, 0xa2, + 0x02, 0x4e, 0xcd, 0x2b, 0x9d, 0x72, 0xdf, 0xeb, 0x8c, 0x40, 0xb9, 0x9b, + 0xc7, 0x71, 0x4a, 0xc4, 0x25, 0x3a, 0x69, 0x34, 0x35, 0x3b, 0x1c, 0xc2, + 0x57, 0xed, 0x52, 0x65, 0x92, 0x14, 0x08, 0x57, 0xc1, 0x17, 0xd7, 0x30, + 0xfd, 0x62, 0xce, 0xf5, 0x94, 0x92, 0x82, 0x12, 0xe7, 0xde, 0x02, 0x9c, + 0xe6, 0xcf, 0xbb, 0x02, 0x5f, 0xfb, 0x23, 0xd5, 0xe4, 0xab, 0x5f, 0x92, + 0x2a, 0x04, 0x1e, 0x47, 0xf1, 0x9a, 0x40, 0x85, 0x71, 0xcf, 0x5b, 0x68, + 0xd6, 0x2a, 0xdb, 0x9a, 0xc8, 0x44, 0xc1, 0xcc, 0xcf, 0x89, 0xda, 0xf1, + 0xe0, 0xbb, 0x67, 0x39, 0xbd, 0xf1, 0xcb, 0x64, 0x93, 0x94, 0x60, 0x1d, + 0xb7, 0x90, 0xc2, 0xb1, 0x2a, 0x0f, 0xdd, 0xc5, 0xd7, 0x5f, 0xad, 0xe6, + 0xea, 0x4e, 0xea, 0xe0, 0xef, 0x78, 0xd8, 0x11, 0x6f, 0x51, 0x70, 0x98, + 0x1b, 0x00, 0xaf, 0x37, 0x01, 0xf2, 0x43, 0x3a, 0x21, 0x48, 0xc7, 0x7a, + 0x0f, 0x42, 0x8f, 0x9e, 0x31, 0x5b, 0x58, 0xc2, 0x81, 0xb2, 0x95, 0xc9, + 0x9e, 0x46, 0xf7, 0xff, 0x74, 0x6d, 0xf6, 0x85, 0xe4, 0xe4, 0x6e, 0x64, + 0xa5, 0x5b, 0xe3, 0x0d, 0x8d, 0xe9, 0x42, 0xe4, 0x05, 0x17, 0x0b, 0x7f, + 0x43, 0x4f, 0x9d, 0x7d, 0xb3, 0xab, 0xad, 0x60, 0x82, 0xa4, 0xd5, 0xb8, + 0xf0, 0x2c, 0xdd, 0x05, 0x67, 0x3b, 0x84, 0x5d, 0x52, 0x65, 0xd4, 0xde, + 0x4e, 0x64, 0x32, 0x2d, 0x37, 0xa7, 0xe3, 0x02, 0x83, 0xf3, 0x98, 0x46, + 0xf2, 0x9d, 0xc0, 0xd5, 0xe5, 0xc7, 0x46, 0xe5, 0xa3, 0xf9, 0xcc, 0xba, + 0xdd, 0xad, 0x6f, 0xea, 0x81, 0xe5, 0x23, 0x0c, 0x11, 0x01, 0x84, 0x47, + 0x7e, 0x22, 0x9e, 0xa1, 0x61, 0x29, 0x12, 0xfc, 0xd4, 0xbd, 0x09, 0xe0, + 0x90, 0x1a, 0x5b, 0x90, 0xb5, 0x33, 0xa1, 0x9b, 0xd6, 0x36, 0xca, 0x56, + 0x65, 0x1c, 0xba, 0xb7, 0x2b, 0x97, 0x82, 0x98, 0xca, 0xbb, 0x39, 0x47, + 0x48, 0x90, 0xef, 0x53, 0x97, 0x21, 0xd2, 0x66, 0x9b, 0x99, 0xc6, 0x5d, + 0xdf, 0x67, 0xb3, 0xa1, 0x92, 0x82, 0xd3, 0x5f, 0x85, 0xa5, 0xa0, 0xe9, + 0x16, 0x89, 0xdf, 0x08, 0x6c, 0x18, 0x03, 0x2c, 0xa8, 0x6c, 0xcf, 0x83, + 0x38, 0x12, 0x85, 0x93, 0xcd, 0xe6, 0x38, 0x93, 0x4b, 0xb4, 0xfa, 0x2b, + 0x2a, 0xa7, 0xae, 0x61, 0xef, 0xa3, 0xfd, 0x9e, 0xa2, 0xf1, 0xe9, 0x47, + 0x6a, 0xa5, 0x2e, 0xae, 0xa4, 0x80, 0xbb, 0x5f, 0x09, 0xe9, 0x84, 0x7a, + 0xf9, 0x60, 0x19, 0x9f, 0xc1, 0xe2, 0x42, 0xca, 0x0c, 0x8f, 0x78, 0xe4, + 0xeb, 0xd0, 0x37, 0x68, 0x8c, 0x23, 0x9d, 0x57, 0xf7, 0xd9, 0x41, 0x3d, + 0xbe, 0xbc, 0x2c, 0xca, 0xd6, 0x27, 0xa6, 0x0a, 0x22, 0x83, 0xcd, 0x10, + 0xa0, 0xfa, 0x80, 0xe7, 0x99, 0xf9, 0x5d, 0xaf, 0x95, 0x45, 0x7c, 0x67, + 0x87, 0x8a, 0x64, 0x6d, 0x53, 0xe5, 0x25, 0x50, 0x47, 0xa2, 0x25, 0xaf, + 0x4d, 0xbc, 0x73, 0x44, 0x6f, 0x8c, 0x06, 0xec, 0xe0, 0xdb, 0x72, 0x5f, + 0x01, 0xa2, 0x66, 0x53, 0x43, 0xcb, 0xd5, 0x42, 0xa4, 0xfc, 0x94, 0xe9, + 0xf1, 0x07, 0xce, 0xeb, 0x3d, 0x84, 0x0a, 0xdc, 0xe3, 0xd0, 0x73, 0x58, + 0xd3, 0x93, 0x67, 0xda, 0x98, 0x7c, 0x83, 0x2c, 0xd9, 0x23, 0x6b, 0x6c, + 0xe1, 0x82, 0x36, 0x1c, 0x43, 0x7c, 0xcf, 0xc5, 0x17, 0xf6, 0x9b, 0xc7, + 0xf3, 0x76, 0x85, 0xd0, 0xef, 0xd3, 0x56, 0x0d, 0xab, 0x47, 0xfb, 0xf7, + 0x62, 0x92, 0xa6, 0x7f, 0x27, 0xc3, 0x8c, 0x97, 0xa4, 0xd6, 0x1b, 0x8f, + 0x75, 0xa6, 0x63, 0x8c, 0xb4, 0x9d, 0xad, 0xa4, 0x39, 0xdd, 0x18, 0x23, + 0xe8, 0x86, 0xed, 0xd4, 0x1b, 0x14, 0x60, 0x71, 0x22, 0x99, 0x84, 0x3e, + 0x93, 0xec, 0x02, 0x47, 0x75, 0x6b, 0x68, 0x4f, 0xdd, 0x8f, 0x83, 0xf0, + 0x8c, 0xc3, 0x76, 0x1c, 0xeb, 0xed, 0x94, 0x3c, 0x3c, 0x1a, 0x39, 0xd1, + 0x6f, 0x02, 0x57, 0xad, 0x61, 0xb6, 0xa5, 0xaa, 0x0e, 0xc0, 0xa0, 0x38, + 0xee, 0x12, 0x82, 0x1f, 0x9e, 0xde, 0x38, 0x46, 0xaa, 0x03, 0x6b, 0x75, + 0x19, 0x4c, 0x2b, 0xfd, 0x3a, 0x0a, 0x3e, 0x6a, 0x00, 0xae, 0x63, 0xe9, + 0x5a, 0xea, 0x7d, 0x9c, 0x5e, 0x01, 0xd3, 0x9d, 0xb2, 0xf1, 0xef, 0xa8, + 0x5b, 0x4c, 0x2a, 0x44, 0x19, 0xce, 0x8b, 0x8c, 0x1f, 0xba, 0xd2, 0x41, + 0xa3, 0x20, 0x72, 0x0b, 0x30, 0x94, 0x58, 0xa9, 0x6d, 0xd2, 0xd5, 0xbe, + 0xb8, 0x20, 0x53, 0x28, 0xd7, 0xa2, 0x10, 0x23, 0x63, 0x32, 0xe7, 0xd5, + 0x3a, 0x00, 0xfe, 0x31, 0x7f, 0xd5, 0xac, 0x94, 0xd5, 0x67, 0x8e, 0x61, + 0x85, 0x42, 0x10, 0x16, 0xa6, 0xd4, 0x08, 0x4d, 0x65, 0x96, 0xf2, 0x85, + 0xfe, 0x52, 0x0b, 0x4d, 0x87, 0x66, 0x4c, 0x4d, 0xb1, 0x81, 0x5c, 0x6e, + 0xee, 0xaf, 0xd7, 0xc5, 0xf0, 0x33, 0xc6, 0x9d, 0x08, 0x3a, 0x2d, 0xbd, + 0x0e, 0x17, 0xc2, 0xa5, 0x31, 0x58, 0xae, 0x46, 0x0f, 0x1d, 0xb4, 0x15, + 0x52, 0xe7, 0xd9, 0x86, 0x88, 0x4e, 0x86, 0x32, 0xb9, 0x95, 0x5d, 0x4e, + 0xa4, 0x18, 0x95, 0x51, 0x6c, 0x58, 0xd4, 0xa7, 0xfe, 0x4d, 0x72, 0x8a, + 0xe7, 0xfd, 0x09, 0x85, 0x2b, 0xb1, 0x22, 0x3d, 0x12, 0xec, 0xa5, 0x5f, + 0xe7, 0x3c, 0xe5, 0x45, 0x7c, 0xd7, 0xa7, 0xd8, 0x9d, 0x78, 0x9f, 0xd5, + 0xb6, 0xf0, 0xc8, 0x0e, 0xd1, 0x7f, 0x8c, 0x12, 0x53, 0x23, 0x12, 0xd8, + 0x2a, 0x24, 0x12, 0xc9, 0x77, 0xa1, 0x07, 0x86, 0x9e, 0xea, 0x47, 0x39, + 0xbf, 0x5c, 0x48, 0xb3, 0xc9, 0x6b, 0xc9, 0x83, 0x8e, 0x3b, 0xbf, 0xa5, + 0x18, 0x8c, 0xc9, 0x78, 0xd6, 0x75, 0x72, 0x3a, 0x96, 0x31, 0xb0, 0x35, + 0x34, 0x31, 0xf4, 0xc1, 0x09, 0xb6, 0xba, 0x26, 0x6b, 0x21, 0xf0, 0xa1, + 0xf0, 0x9e, 0xc7, 0x5b, 0xa3, 0xbc, 0x7c, 0x18, 0x09, 0xf2, 0x26, 0xa5, + 0x81, 0x32, 0x97, 0x0c, 0x1c, 0x06, 0x91, 0x17, 0x7e, 0x13, 0xb9, 0xf4, + 0x65, 0x93, 0xdc, 0x86, 0x2b, 0x3d, 0x20, 0x63, 0xc6, 0x21, 0x93, 0xd5, + 0xbd, 0x50, 0x7e, 0x16, 0x94, 0x69, 0x38, 0x58, 0xcb, 0xe6, 0x86, 0x2c, + 0x0c, 0xd4, 0x78, 0x1f, 0x89, 0x67, 0x30, 0x0e, 0x03, 0x05, 0x31, 0x96, + 0x48, 0xbd, 0x60, 0x19, 0x60, 0xbf, 0x5a, 0xec, 0x31, 0xd7, 0x9b, 0x3e, + 0xc5, 0xb9, 0xc1, 0xf6, 0xbd, 0x2d, 0x9a, 0x7a, 0xdc, 0xd8, 0x43, 0x05, + 0x82, 0xe6, 0x03, 0xaa, 0x82, 0xb3, 0x57, 0x43, 0x14, 0xa2, 0x91, 0x00, + 0xbd, 0xaa, 0x4d, 0xa9, 0x59, 0x05, 0x01, 0x47, 0xd9, 0x04, 0xbe, 0xc0, + 0x1c, 0x6e, 0x18, 0xad, 0xfe, 0xf9, 0xc8, 0x88, 0x24, 0x47, 0x73, 0x9b, + 0xd0, 0xf6, 0xf6, 0xe9, 0xb4, 0x6e, 0xc6, 0x0d, 0x55, 0x71, 0xd2, 0x71, + 0x50, 0xfb, 0xf9, 0x29, 0x2e, 0x6b, 0x06, 0xe4, 0x70, 0xec, 0x36, 0x9c, + 0x62, 0x66, 0x00, 0xb9, 0x0e, 0x29, 0xc6, 0xe4, 0x38, 0xb2, 0xc7, 0x68, + 0x49, 0xd0, 0x88, 0xfc, 0x95, 0xd0, 0x0e, 0x09, 0xa1, 0xfd, 0xfe, 0x8e, + 0x08, 0xd9, 0xd8, 0x65, 0x42, 0xf6, 0x01, 0xc4, 0xc0, 0x8b, 0x72, 0x73, + 0xa5, 0x50, 0xad, 0xa3, 0xe4, 0x0d, 0xa0, 0x9b, 0xd5, 0xba, 0xdc, 0x77, + 0x01, 0x26, 0xac, 0x41, 0x31, 0x2a, 0x66, 0x10, 0x37, 0x94, 0x45, 0x83, + 0x6e, 0xfe, 0xac, 0x74, 0xd1, 0x59, 0x21, 0x58, 0xfd, 0x2c, 0xd6, 0xea, + 0x3e, 0xcc, 0xe3, 0x64, 0x8a, 0x2d, 0x38, 0x5e, 0xaf, 0xa4, 0x6f, 0xc5, + 0x71, 0x29, 0x08, 0x71, 0xb4, 0x39, 0xb4, 0xb4, 0x74, 0xa6, 0xc5, 0xda, + 0x69, 0xda, 0xba, 0x46, 0x08, 0xac, 0x7d, 0x5e, 0x1d, 0x41, 0xb6, 0x73, + 0x07, 0x69, 0x90, 0x93, 0xdb, 0x50, 0x20, 0xe9, 0xa5, 0x74, 0x3e, 0x46, + 0x22, 0x70, 0x3f, 0xad, 0x23, 0xde, 0xf3, 0xe2, 0x2d, 0x34, 0xce, 0xc6, + 0x7a, 0x42, 0x40, 0x03, 0x49, 0xe0, 0x3f, 0xcd, 0x64, 0x17, 0x70, 0xbf, + 0x7b, 0x56, 0xf1, 0xd8, 0x8a, 0x4b, 0xe1, 0x05, 0x5a, 0xd8, 0x23, 0xfc, + 0xd4, 0x1f, 0x4e, 0xff, 0x6b, 0x8a, 0x29, 0x79, 0xda, 0x68, 0x39, 0xd9, + 0xfa, 0x22, 0xf4, 0x10, 0x7c, 0x8f, 0xa6, 0x27, 0xbd, 0x3e, 0xb9, 0xa6, + 0xb2, 0x18, 0x87, 0x1f, 0x3d, 0xea, 0x00, 0xef, 0xba, 0x38, 0x3a, 0x54, + 0xe4, 0xd8, 0x8d, 0x2d, 0x00, 0xda, 0x08, 0xf5, 0x3b, 0x78, 0xea, 0x7e, + 0x45, 0x8f, 0x6a, 0x06, 0x25, 0x0c, 0x8f, 0x85, 0xf2, 0x24, 0xbe, 0xd7, + 0x3b, 0x0b, 0xd5, 0x0e, 0x90, 0xec, 0x68, 0x65, 0x9f, 0xdd, 0x73, 0x23, + 0xba, 0xa4, 0x77, 0x32, 0x69, 0xeb, 0xfa, 0x78, 0xf7, 0xaf, 0xc6, 0x75, + 0x5a, 0xce, 0x74, 0xe7, 0x58, 0xd6, 0xdf, 0xa4, 0xe1, 0x70, 0x15, 0xd1, + 0xca, 0xc8, 0x3f, 0x9e, 0x55, 0xb6, 0x2d, 0xf5, 0xf7, 0xdb, 0x97, 0x6e, + 0x5f, 0xbe, 0x31, 0xdb, 0x6f, 0x10, 0x37, 0xb1, 0x2c, 0x56, 0x43, 0x74, + 0xd4, 0xc8, 0xe7, 0x94, 0x0c, 0x32, 0x20, 0xf3, 0x0a, 0xa6, 0x0c, 0x4d, + 0xed, 0x43, 0x32, 0x62, 0x72, 0x67, 0x4f, 0xc5, 0x42, 0x74, 0x5e, 0xa9, + 0xf8, 0xc0, 0x94, 0x90, 0x2c, 0x03, 0x9f, 0x98, 0x5b, 0x67, 0x60, 0xc7, + 0x29, 0x15, 0x8a, 0x40, 0x03, 0xf1, 0x4d, 0x50, 0x54, 0x7f, 0x69, 0x04, + 0x40, 0xdc, 0x72, 0x9d, 0xe9, 0xef, 0xbf, 0xe3, 0x72, 0xe5, 0xf4, 0x88, + 0x9f, 0x48, 0x97, 0xf6, 0x71, 0x55, 0x2f, 0x17, 0x6a, 0x09, 0x36, 0x39, + 0x9f, 0xdb, 0xa8, 0x2f, 0x65, 0x68, 0x8e, 0x8c, 0x5e, 0xfa, 0x7f, 0x66, + 0xaa, 0xc4, 0xa5, 0xfb, 0xf2, 0x49, 0x90, 0x30, 0x98, 0xd1, 0xb4, 0x4a, + 0x0d, 0xf1, 0x0b, 0xc3, 0x88, 0xa2, 0x00, 0x39, 0x94, 0xfc, 0xdd, 0x14, + 0x71, 0x35, 0xbc, 0x99, 0x79, 0x6d, 0xfc, 0xeb, 0xa9, 0x66, 0x57, 0x25, + 0x1a, 0x4b, 0xdd, 0x8b, 0xb2, 0x0b, 0x8d, 0x7e, 0x1a, 0x11, 0x14, 0x4d, + 0xd6, 0x17, 0x04, 0xdf, 0x2c, 0xac, 0xae, 0xc6, 0x7f, 0xd2, 0x00, 0xf9, + 0x84, 0xb1, 0x99, 0x21, 0xca, 0xe8, 0x42, 0x7d, 0x78, 0x0a, 0xac, 0x12, + 0x99, 0x19, 0x6d, 0x96, 0xcb, 0x19, 0x9f, 0x7f, 0x77, 0xa9, 0x0d, 0xb4, + 0x14, 0x4d, 0xd4, 0x30, 0x57, 0x2f, 0x01, 0x01, 0xf0, 0xc6, 0xf1, 0xf9, + 0xda, 0x35, 0x59, 0x39, 0xa2, 0x56, 0x47, 0xfc, 0x34, 0x1b, 0xc9, 0x33, + 0xbb, 0x70, 0xda, 0x86, 0xac, 0xb9, 0xd6, 0xd3, 0x3a, 0xf1, 0x37, 0x39, + 0x4e, 0xe9, 0x09, 0x85, 0x52, 0x07, 0x7c, 0x2a, 0xf8, 0x17, 0x6c, 0x22, + 0x8c, 0x3a, 0xc7, 0x74, 0x6c, 0x8b, 0x18, 0x6f, 0x6e, 0xcb, 0xd2, 0x08, + 0x44, 0xfa, 0x11, 0xf1, 0xb6, 0xea, 0xdb, 0xe8, 0x5f, 0xc8, 0x89, 0x59, + 0xfb, 0x5e, 0x1d, 0xcd, 0x1c, 0x8e, 0x1b, 0xa9, 0x0e, 0x24, 0x39, 0xd0, + 0xb1, 0x87, 0x1c, 0xea, 0x52, 0xd3, 0xbd, 0x6a, 0x65, 0x2b, 0x7d, 0x01, + 0x8c, 0x4b, 0x91, 0x2f, 0x83, 0x38, 0x77, 0x27, 0x95, 0x2a, 0xec, 0xa9, + 0x38, 0x84, 0x28, 0x5a, 0x9b, 0xbc, 0xea, 0xd1, 0xb2, 0xd4, 0x90, 0xd7, + 0x78, 0xdc, 0x19, 0x5b, 0xbf, 0xa1, 0x5f, 0x35, 0xfa, 0x4c, 0xae, 0x61, + 0x37, 0x7b, 0x7e, 0x55, 0x03, 0x16, 0x74, 0x67, 0x69, 0x29, 0x30, 0x35, + 0x9d, 0xbc, 0xef, 0x77, 0x59, 0x71, 0x77, 0x25, 0x0d, 0x4a, 0x5c, 0x31, + 0x00, 0x24, 0xb5, 0x12, 0x2b, 0xa1, 0x00, 0x5f, 0x0b, 0xd3, 0x79, 0x2c, + 0xc6, 0x09, 0x1d, 0x7b, 0x3d, 0x29, 0xf4, 0x72, 0x56, 0x70, 0x5d, 0xcc, + 0xd2, 0xc4, 0x73, 0xfe, 0xc2, 0xb8, 0xa4, 0x35, 0x64, 0x21, 0x01, 0x9f, + 0xd4, 0x1b, 0x88, 0xb1, 0x7f, 0xd2, 0x3b, 0x36, 0x13, 0x40, 0x4d, 0xc5, + 0x16, 0x7e, 0x88, 0xa6, 0xe1, 0x0f, 0x43, 0x1c, 0x28, 0x60, 0x32, 0x40, + 0x83, 0xad, 0x58, 0x14, 0xb9, 0x2c, 0xf4, 0x7c, 0x50, 0x6f, 0x8b, 0x22, + 0xee, 0x10, 0xb2, 0x69, 0x04, 0x19, 0xfc, 0x50, 0xa4, 0xbe, 0x85, 0x4c, + 0x7f, 0xdd, 0x7b, 0x42, 0x89, 0xd6, 0x76, 0x3c, 0xd0, 0x6e, 0xb1, 0xb6, + 0xfc, 0x97, 0xe9, 0xb1, 0xc4, 0xfe, 0x7a, 0x83, 0x4e, 0x77, 0x82, 0x67, + 0x79, 0xd3, 0xb1, 0x71, 0xbc, 0x62, 0x84, 0xba, 0x18, 0x57, 0xb2, 0x70, + 0x81, 0x14, 0x3d, 0xfd, 0x38, 0x9a, 0x0a, 0x77, 0x0c, 0x0d, 0x1a, 0x69, + 0x02, 0x34, 0x90, 0xf0, 0x3c, 0xbd, 0xc7, 0x0c, 0x30, 0x2c, 0xa1, 0xe5, + 0x6f, 0xf0, 0x58, 0x7d, 0xac, 0x2b, 0xe7, 0xbd, 0x8d, 0xbb, 0x08, 0x8a, + 0x7a, 0x75, 0xe1, 0x2d, 0xa7, 0xf0, 0x9a, 0x7a, 0x1a, 0xbb, 0xae, 0x7a, + 0x20, 0x1e, 0x4d, 0x1a, 0x3d, 0x51, 0x74, 0xb7, 0x77, 0xa2, 0xdd, 0xa8, + 0x20, 0x25, 0xef, 0xd8, 0x4e, 0x4c, 0xe5, 0x69, 0x03, 0x97, 0x14, 0xf6, + 0x54, 0x23, 0xf3, 0x05, 0x4a, 0xad, 0xdc, 0xa0, 0x8f, 0x91, 0x15, 0x66, + 0xef, 0x88, 0x00, 0xac, 0x15, 0x28, 0x48, 0x98, 0x1d, 0x49, 0x90, 0x5b, + 0x2f, 0x53, 0x21, 0x5e, 0x21, 0xa4, 0xd3, 0x9c, 0xf4, 0x67, 0x53, 0x6e, + 0x4e, 0x54, 0xf9, 0x95, 0x4d, 0x3e, 0x77, 0xef, 0x36, 0x14, 0x54, 0x97, + 0x6c, 0x27, 0x53, 0x25, 0x3a, 0x76, 0x34, 0xc5, 0x20, 0x31, 0xd1, 0x5d, + 0x43, 0x65, 0x35, 0x33, 0x0e, 0x22, 0xaf, 0xb5, 0xb0, 0x76, 0xf0, 0x9d, + 0x4f, 0x8e, 0xeb, 0x21, 0xbc, 0xe4, 0xd9, 0x64, 0xf7, 0xb4, 0x5a, 0x0b, + 0xcc, 0xfb, 0x36, 0xb1, 0x71, 0x4e, 0x9c, 0x98, 0x2e, 0xef, 0x0d, 0x09, + 0x51, 0xfb, 0xea, 0x6f, 0x9b, 0xb8, 0x39, 0x0a, 0xbc, 0x89, 0x8d, 0x65, + 0x5a, 0xc3, 0x84, 0xff, 0x5d, 0x0e, 0xc3, 0xad, 0x87, 0x1e, 0xf1, 0x9a, + 0x1e, 0x7a, 0xb6, 0x28, 0x6b, 0xe5, 0x93, 0x0d, 0x54, 0xf5, 0xdc, 0xd3, + 0x6e, 0x78, 0xce, 0x7b, 0x63, 0x7a, 0x8b, 0xf0, 0x8b, 0xbe, 0x52, 0xd9, + 0x7f, 0xe2, 0x92, 0x7e, 0x4e, 0x41, 0xce, 0x16, 0x83, 0xa4, 0xb3, 0x1e, + 0x8d, 0x4e, 0xff, 0x22, 0x77, 0x12, 0x0f, 0x87, 0x80, 0x1e, 0x15, 0x65, + 0xe2, 0xbf, 0xb8, 0xfb, 0x9a, 0x71, 0x45, 0xa9, 0x32, 0x7c, 0x5f, 0x4c, + 0x90, 0x28, 0x76, 0x7a, 0x71, 0x37, 0x99, 0x68, 0x59, 0xc5, 0x1e, 0xcd, + 0x11, 0xc3, 0x50, 0x3f, 0x7a, 0x89, 0xf4, 0x03, 0xb0, 0xcc, 0x44, 0xf4, + 0x22, 0xdc, 0xad, 0x7c, 0xfe, 0x65, 0x08, 0x61, 0x9d, 0xbe, 0xb1, 0x93, + 0xe4, 0x5a, 0x67, 0xd1, 0xd4, 0x7c, 0xe1, 0xf6, 0x33, 0x50, 0xd5, 0x08, + 0x6f, 0xeb, 0xe7, 0x95, 0x75, 0xef, 0xd3, 0xc1, 0x85, 0xbc, 0x6f, 0x08, + 0x28, 0x24, 0xc0, 0x21, 0xa8, 0xb5, 0x7c, 0x3e, 0xdc, 0x59, 0x63, 0x74, + 0xf6, 0xcb, 0xd2, 0x5d, 0x5c, 0xb1, 0x36, 0x7e, 0xb4, 0x6f, 0xb3, 0xd4, + 0xa9, 0x16, 0x85, 0xbe, 0x00, 0xfd, 0xc9, 0xc7, 0x53, 0xfc, 0xdd, 0xc1, + 0xf5, 0x4b, 0x0f, 0x98, 0x2f, 0xb9, 0xa4, 0x97, 0x5d, 0xed, 0xc8, 0xd8, + 0x07, 0xa3, 0xe4, 0xdb, 0x99, 0x17, 0x45, 0xb5, 0x33, 0x3c, 0x50, 0x74, + 0xc0, 0x62, 0x9e, 0x61, 0xc8, 0x45, 0xfb, 0xea, 0x51, 0x2c, 0xa5, 0x41, + 0x56, 0xac, 0xd2, 0x78, 0x13, 0xb8, 0x12, 0x4f, 0x8a, 0xe7, 0xff, 0xb8, + 0x70, 0xaa, 0xe0, 0x56, 0x17, 0xfd, 0xe2, 0x5c, 0xc1, 0xa2, 0xd3, 0xf1, + 0x3c, 0xa9, 0x5a, 0xcc, 0x41, 0x58, 0x9f, 0x7d, 0x5c, 0xe6, 0xe6, 0xbd, + 0x6e, 0x8d, 0xcf, 0xd2, 0xf1, 0x8b, 0x7d, 0xd9, 0x5d, 0xea, 0x7c, 0x61, + 0x69, 0xa5, 0x03, 0xee, 0xf5, 0x87, 0x15, 0x3d, 0xe2, 0xf6, 0xc2, 0x52, + 0xb7, 0xd7, 0x79, 0xd0, 0x18, 0x46, 0x7d, 0xc7, 0xec, 0x11, 0xde, 0x88, + 0x49, 0x02, 0xcc, 0x5a, 0x78, 0xa3, 0x8e, 0x12, 0x0e, 0x64, 0x68, 0x58, + 0x92, 0xd5, 0x89, 0xb0, 0xf3, 0x00, 0x1f, 0xf5, 0xd3, 0x3d, 0x87, 0x50, + 0xd7, 0x52, 0xc4, 0x1c, 0x79, 0x28, 0x22, 0xd2, 0x16, 0xc9, 0xe0, 0x3e, + 0xa5, 0x4f, 0x5f, 0x90, 0x52, 0x40, 0x85, 0x0c, 0xec, 0xde, 0xf0, 0xc0, + 0x7a, 0xf3, 0xa4, 0xf8, 0xf8, 0x37, 0xd6, 0x8b, 0xfb, 0x02, 0x04, 0xc7, + 0xcd, 0xc8, 0xc6, 0x53, 0x92, 0xfd, 0x99, 0xdf, 0x66, 0xf8, 0x15, 0x4d, + 0xc0, 0x8e, 0x4e, 0xbb, 0x64, 0x04, 0xf8, 0xc9, 0x2d, 0xf4, 0x70, 0xfe, + 0xbe, 0x95, 0xcd, 0x8f, 0x25, 0x7b, 0x51, 0xdb, 0x18, 0x4d, 0x78, 0x04, + 0x9a, 0xe1, 0xdf, 0x8e, 0xba, 0x89, 0x42, 0x1a, 0xfc, 0x36, 0x69, 0x1f, + 0x0f, 0xe7, 0x06, 0xed, 0xf7, 0x9d, 0x51, 0x55, 0x62, 0xb7, 0xaa, 0x9c, + 0x34, 0x3c, 0x33, 0x34, 0xbe, 0x70, 0xfe, 0x6e, 0xf2, 0xfd, 0x30, 0xda, + 0xa0, 0xb6, 0x78, 0x44, 0x37, 0x24, 0xb0, 0x86, 0x29, 0xb2, 0xdd, 0x76, + 0xaf, 0x02, 0xcd, 0xec, 0xf5, 0x53, 0x84, 0xc3, 0x92, 0x69, 0xc5, 0xc6, + 0x6e, 0x93, 0xa0, 0xbd, 0x59, 0x9f, 0xe5, 0x23, 0x2c, 0x1a, 0x68, 0x77, + 0x68, 0xd8, 0xa6, 0x7e, 0x3e, 0xc8, 0x0d, 0x47, 0x88, 0xc1, 0x67, 0xfe, + 0x1b, 0xb1, 0x7e, 0xad, 0x5e, 0xb3, 0x28, 0xf1, 0xa6, 0xa0, 0xb6, 0xa0, + 0x84, 0xc9, 0xa5, 0x1f, 0x44, 0x30, 0xef, 0x2f, 0x33, 0xa1, 0xb5, 0xbd, + 0x65, 0x80, 0xb8, 0xf5, 0x7d, 0xe4, 0xd8, 0x27, 0x21, 0x8e, 0x9c, 0x66, + 0x86, 0x1f, 0x00, 0x88, 0xf4, 0xac, 0x15, 0xb7, 0x5e, 0xe8, 0xbc, 0x9f, + 0xf6, 0xa7, 0x6a, 0x22, 0xaa, 0x41, 0xc3, 0x9a, 0x7e, 0x0d, 0x8a, 0xe7, + 0xe6, 0x94, 0xbb, 0xf6, 0x06, 0x4f, 0xfb, 0x4a, 0xc4, 0x45, 0xad, 0xb4, + 0x1b, 0x3e, 0x22, 0x6d, 0xea, 0xa7, 0x4f, 0xa9, 0xa5, 0x3e, 0xda, 0x4f, + 0xf2, 0xff, 0x01, 0xc4, 0x6d, 0xbd, 0x76, 0xce, 0x15, 0xbe, 0x34, 0x33, + 0x9d, 0xb9, 0x10, 0x5e, 0xa8, 0x71, 0xda, 0xf8, 0xc7, 0xce, 0x0c, 0x0a, + 0xa0, 0x5e, 0xd2, 0x2a, 0x77, 0x53, 0xe1, 0x0a, 0x1f, 0x61, 0x31, 0x48, + 0x01, 0xe2, 0x37, 0xdd, 0xfa, 0x56, 0xac, 0xdf, 0x79, 0x88, 0xe9, 0xa0, + 0x31, 0xfd, 0x40, 0xcd, 0xfc, 0x00, 0xe4, 0xae, 0xd1, 0x2e, 0x3c, 0xae, + 0xfd, 0xa3, 0x78, 0x3e, 0x15, 0xcc, 0xa1, 0x34, 0xb2, 0x8b, 0xc3, 0x49, + 0x05, 0xa3, 0x43, 0x75, 0xdd, 0x21, 0x0a, 0xfc, 0xf7, 0xcb, 0xb8, 0x31, + 0x05, 0x44, 0xf6, 0xa0, 0xa7, 0x97, 0x0b, 0xf1, 0x6a, 0x47, 0xc7, 0x9c, + 0x6c, 0x92, 0xfe, 0xcb, 0xf3, 0xab, 0x5a, 0xb1, 0x11, 0xc4, 0x06, 0x58, + 0x79, 0x26, 0xe6, 0xca, 0x12, 0x86, 0xb5, 0xa1, 0x05, 0xfa, 0xeb, 0xa3, + 0xbe, 0x9c, 0xd2, 0x62, 0x26, 0x58, 0x8a, 0x00, 0xfd, 0xf5, 0xe6, 0xac, + 0x92, 0x0d, 0x5f, 0xb0, 0x09, 0xd1, 0xf4, 0xb2, 0x56, 0x61, 0x2f, 0x30, + 0x93, 0x86, 0x6b, 0x40, 0x31, 0x63, 0x7c, 0x10, 0xf4, 0x38, 0x27, 0xca, + 0x35, 0x47, 0x6d, 0x21, 0x30, 0x08, 0xa0, 0xf8, 0x8a, 0x6c, 0x30, 0xf0, + 0xdf, 0x20, 0x56, 0x3a, 0x01, 0xf4, 0x13, 0xb1, 0xe2, 0xf1, 0x5f, 0xf6, + 0x15, 0xf1, 0xe6, 0x3e, 0x4c, 0x00, 0xce, 0xe1, 0xb1, 0x7d, 0xac, 0x9a, + 0x5d, 0xa5, 0xa1, 0xfc, 0x7f, 0xcc, 0x42, 0xc1, 0x65, 0x1e, 0x12, 0xcf, + 0xdd, 0x21, 0x92, 0xb3, 0x33, 0xc5, 0x4c, 0xcb, 0xe2, 0xb5, 0xd1, 0xc6, + 0xa0, 0xaf, 0x60, 0x1c, 0x7d, 0xad, 0x9a, 0x07, 0x22, 0xb9, 0x94, 0x28, + 0x14, 0x73, 0x32, 0x16, 0xed, 0xab, 0x72, 0xf9, 0xf8, 0x63, 0x89, 0xfc, + 0xa7, 0xdc, 0x84, 0x1a, 0xfb, 0xd5, 0x66, 0xfa, 0xc4, 0xa9, 0xe3, 0x47, + 0x0f, 0x0e, 0xc4, 0xc4, 0xf6, 0x49, 0x43, 0x1b, 0x3a, 0xc2, 0xff, 0x5d, + 0x87, 0xf9, 0x4d, 0xc7, 0xdb, 0x8a, 0x0b, 0xe7, 0xc7, 0x66, 0x09, 0x5e, + 0x76, 0xbd, 0xd0, 0xb5, 0x30, 0xc6, 0x92, 0x9f, 0xd4, 0x06, 0x65, 0x1b, + 0x6a, 0x92, 0x1f, 0x2d, 0x7f, 0x6c, 0x54, 0x3f, 0x63, 0x12, 0x73, 0x00, + 0xfc, 0xa1, 0xbb, 0x13, 0xec, 0x7a, 0xb4, 0xc4, 0x94, 0x44, 0x79, 0xe5, + 0x49, 0x85, 0x32, 0x5b, 0x77, 0x11, 0x5e, 0x34, 0x2a, 0x90, 0xec, 0xb8, + 0x66, 0xdc, 0x6e, 0xb9, 0xcc, 0xc9, 0xc7, 0x5d, 0xd9, 0x31, 0xae, 0x41, + 0xcd, 0xb6, 0x65, 0x6e, 0x7a, 0x38, 0x0b, 0xa7, 0x56, 0x7c, 0xe8, 0xf0, + 0x16, 0xd8, 0x0e, 0x12, 0xe3, 0x5e, 0x9f, 0xbf, 0x6e, 0x8c, 0x24, 0x5e, + 0xc7, 0x6a, 0x2e, 0xe0, 0x17, 0xd0, 0xc3, 0x4c, 0x16, 0xd8, 0xb7, 0xb0, + 0x1f, 0x19, 0x0a, 0x11, 0x09, 0x9a, 0x15, 0xd3, 0x41, 0x20, 0x19, 0xac, + 0x74, 0xe4, 0xf5, 0x5d, 0xf9, 0x51, 0xb2, 0x4b, 0xde, 0xe6, 0xbc, 0x8e, + 0x92, 0x12, 0xad, 0x8e, 0x6b, 0x72, 0x51, 0x4d, 0xe0, 0xa4, 0x8d, 0x35, + 0x3f, 0x31, 0x97, 0xfd, 0x1c, 0x51, 0x55, 0x40, 0xd8, 0x3a, 0xa3, 0x1f, + 0x14, 0x0c, 0xd4, 0xe2, 0x4b, 0x14, 0x10, 0x18, 0xfc, 0x70, 0xd0, 0x02, + 0xe7, 0xad, 0xba, 0xa0, 0x84, 0x59, 0x35, 0x1b, 0x06, 0x58, 0x06, 0x10, + 0x72, 0x42, 0x72, 0x47, 0x68, 0xc8, 0x81, 0xad, 0x54, 0x44, 0x4d, 0x26, + 0x06, 0x38, 0x6c, 0x25, 0x37, 0x21, 0xf2, 0xa0, 0x12, 0xf8, 0x48, 0x4f, + 0xe7, 0xf6, 0xd8, 0x60, 0x85, 0xf0, 0x40, 0x6a, 0x11, 0x34, 0x98, 0x0a, + 0x45, 0xdf, 0xbd, 0xe3, 0x22, 0xee, 0xe6, 0x54, 0xb5, 0x64, 0x81, 0x77, + 0x52, 0x9d, 0x2b, 0x1c, 0xf7, 0xa1, 0x78, 0x39, 0xb9, 0x25, 0x74, 0x49, + 0x59, 0x97, 0x78, 0xf9, 0x7f, 0xa1, 0x8a, 0x47, 0x17, 0x1c, 0x03, 0xfb, + 0x9d, 0xe0, 0x7e, 0x61, 0x21, 0x0c, 0xff, 0x73, 0xda, 0x84, 0x54, 0xb5, + 0xa9, 0xda, 0x74, 0xb9, 0xef, 0xbb, 0x91, 0xb4, 0x66, 0xf7, 0x4a, 0xb0, + 0x7e, 0xd4, 0x1f, 0xaa, 0x63, 0xcd, 0xf9, 0xdb, 0x7c, 0xb8, 0xf6, 0x60, + 0x57, 0x58, 0x04, 0x8e, 0x28, 0x6f, 0x4b, 0xa8, 0xac, 0x76, 0x87, 0x22, + 0x10, 0x71, 0x98, 0x0b, 0xde, 0xc4, 0x98, 0x25, 0x58, 0x51, 0x18, 0x2a, + 0xed, 0x45, 0xdb, 0x7b, 0xb5, 0x1c, 0x79, 0xb5, 0x7e, 0xd5, 0x9d, 0x5d, + 0xe4, 0x3f, 0xa3, 0x72, 0x15, 0x7c, 0x73, 0xde, 0x52, 0x77, 0x41, 0x9d, + 0xa2, 0xa7, 0xa2, 0xbe, 0xf5, 0x09, 0x28, 0xc1, 0xfd, 0x01, 0x29, 0x88, + 0x9b, 0xde, 0xd5, 0x46, 0xec, 0xe3, 0xaa, 0xb5, 0xee, 0x8d, 0xf6, 0x7b, + 0xe1, 0x88, 0x0c, 0x18, 0x1b, 0xfa, 0x60, 0x86, 0x64, 0xe4, 0x5b, 0x3a, + 0xa3, 0x10, 0xb1, 0x61, 0x31, 0xdb, 0x20, 0x4c, 0x3d, 0x79, 0x5b, 0xa6, + 0x41, 0xba, 0x90, 0x53, 0x74, 0x35, 0x0c, 0x1e, 0xfd, 0xb3, 0xd2, 0xd9, + 0x20, 0x30, 0xce, 0xbc, 0xbc, 0x6d, 0xbd, 0x9b, 0xfd, 0xf7, 0x82, 0x38, + 0xa9, 0xa7, 0x53, 0x65, 0xa1, 0x71, 0xe2, 0x05, 0xac, 0x56, 0xf7, 0x49, + 0xe5, 0x11, 0x88, 0xac, 0x3b, 0xfd, 0x0a, 0x52, 0xc8, 0x83, 0xf4, 0xeb, + 0xa8, 0x1e, 0xd4, 0xbf, 0x65, 0x43, 0xc2, 0x4f, 0x95, 0xec, 0x7e, 0x93, + 0x54, 0x6f, 0x39, 0x8c, 0x75, 0x53, 0x90, 0x39, 0xb5, 0x8a, 0x7d, 0xb1, + 0xc1, 0x7c, 0x20, 0xdc, 0x02, 0x1c, 0x74, 0x2e, 0x8f, 0x66, 0xbe, 0x0b, + 0x7c, 0xd9, 0x54, 0xd9, 0xfb, 0x2a, 0x3f, 0x97, 0x29, 0x00, 0x2c, 0xfe, + 0x8b, 0xb3, 0x75, 0x69, 0x41, 0x06, 0x08, 0x0e, 0xce, 0xde, 0xa5, 0x97, + 0x7f, 0x3c, 0xdd, 0x82, 0x0e, 0xbf, 0x77, 0xc3, 0xaa, 0xc3, 0xdd, 0x46, + 0x90, 0xda, 0xec, 0xd9, 0x3a, 0x66, 0xc3, 0x01, 0x9f, 0xf3, 0x80, 0xba, + 0x64, 0x46, 0xf8, 0x76, 0x9c, 0x41, 0xee, 0xc6, 0xd3, 0x32, 0x04, 0xff, + 0x5a, 0xde, 0x5b, 0xc0, 0x31, 0x69, 0x74, 0x57, 0x48, 0x79, 0xa1, 0xed, + 0x8d, 0x0c, 0x16, 0xed, 0x0c, 0xc9, 0x31, 0xf4, 0x5c, 0x2c, 0x3a, 0x89, + 0x9a, 0x71, 0xdd, 0x43, 0x1c, 0xbf, 0x48, 0x6a, 0x3a, 0xd4, 0xe4, 0x33, + 0x0c, 0xcf, 0x98, 0xa2, 0x9b, 0x2d, 0x2a, 0x0f, 0xa2, 0xd9, 0xda, 0xdc, + 0x10, 0xa5, 0x25, 0x3f, 0xaa, 0xab, 0x5f, 0x1f, 0xda, 0xed, 0x89, 0x8d, + 0x03, 0x28, 0xe4, 0xd7, 0x36, 0x49, 0xca, 0x57, 0x23, 0x9b, 0x1f, 0x08, + 0xf5, 0x5d, 0xbf, 0x7b, 0x1f, 0x0a, 0x29, 0x7d, 0x0c, 0x97, 0x00, 0x79, + 0x9e, 0x29, 0x73, 0x89, 0x26, 0xe8, 0x76, 0xb7, 0xa5, 0x4e, 0x39, 0x13, + 0x97, 0xfd, 0x1c, 0x6f, 0xec, 0xf0, 0xa9, 0xcb, 0x85, 0x3e, 0x28, 0x37, + 0xae, 0xd4, 0x41, 0x0a, 0xe7, 0x11, 0xca, 0xb8, 0x91, 0x68, 0x65, 0xbe, + 0x64, 0x1c, 0xff, 0xfc, 0xf8, 0x98, 0xe2, 0x5a, 0x6d, 0x4b, 0x6d, 0xfe, + 0xbc, 0x16, 0x96, 0xff, 0xc2, 0x9e, 0xc5, 0x95, 0x85, 0x5e, 0x2d, 0xe2, + 0x91, 0x20, 0xca, 0x89, 0xbd, 0x10, 0x96, 0x90, 0x56, 0x3d, 0xe8, 0xc0, + 0x18, 0xf6, 0x1c, 0x42, 0xc5, 0xa0, 0xdf, 0x17, 0x12, 0x45, 0x53, 0x95, + 0x8a, 0x49, 0x1b, 0x59, 0x96, 0x5d, 0x11, 0xa4, 0x7e, 0x94, 0x4c, 0x30, + 0xec, 0x84, 0x41, 0x08, 0xb8, 0xaf, 0xc7, 0xe1, 0xce, 0x0a, 0xb2, 0x45, + 0x93, 0x66, 0x32, 0xfc, 0x54, 0xe6, 0x75, 0x2a, 0x33, 0x9e, 0x54, 0xb8, + 0x47, 0x70, 0x9a, 0x88, 0x54, 0x92, 0x4a, 0xdc, 0x1d, 0x17, 0xc4, 0x50, + 0x8b, 0xc9, 0x0c, 0x88, 0x12, 0xed, 0x3d, 0x00, 0x17, 0x24, 0x5f, 0x84, + 0xbb, 0xad, 0x2f, 0xa0, 0xb5, 0xea, 0xbb, 0x10, 0x76, 0x22, 0x15, 0x24, + 0x2a, 0xda, 0xa8, 0xdd, 0x24, 0x65, 0xf4, 0x35, 0xb2, 0xf4, 0x58, 0x2c, + 0x98, 0xfa, 0x11, 0x39, 0x37, 0xe5, 0xf8, 0xc1, 0x98, 0xd5, 0xa3, 0x29, + 0xd6, 0x41, 0xf0, 0x16, 0xb9, 0xf3, 0xad, 0xe9, 0x23, 0xba, 0xae, 0x64, + 0xc1, 0x78, 0x3d, 0xb4, 0x3f, 0x63, 0xf1, 0x99, 0x26, 0x30, 0x4d, 0x04, + 0xc8, 0x66, 0x47, 0x7f, 0x91, 0x82, 0x1e, 0x3f, 0xd4, 0xc1, 0x42, 0x98, + 0x41, 0xbe, 0x04, 0x53, 0x26, 0x33, 0xde, 0x92, 0xad, 0x09, 0x1f, 0xc6, + 0xa8, 0x38, 0x31, 0x12, 0x3a, 0xce, 0x60, 0xd6, 0x46, 0x34, 0xd3, 0x8c, + 0xc7, 0x74, 0xb9, 0x7d, 0xba, 0x02, 0xb6, 0xdf, 0x39, 0x6c, 0xfc, 0x88, + 0x97, 0x93, 0xa8, 0xef, 0x9a, 0x5f, 0x95, 0xf6, 0x83, 0xe9, 0xf6, 0x9f, + 0xc3, 0x0a, 0xaf, 0xca, 0xeb, 0x67, 0x6a, 0x26, 0x38, 0x5b, 0xcc, 0x86, + 0xbb, 0x53, 0xd8, 0x40, 0x20, 0x69, 0x4e, 0x02, 0xbc, 0x86, 0x28, 0xd8, + 0xf7, 0x5f, 0x5a, 0x17, 0x8c, 0x6e, 0x44, 0xa3, 0x39, 0xbc, 0x6b, 0x68, + 0x70, 0xcd, 0x28, 0x6c, 0xd4, 0xcb, 0xea, 0xd0, 0x95, 0x0f, 0xde, 0xcc, + 0x4f, 0x64, 0x88, 0x86, 0xcf, 0x17, 0xd3, 0xbb, 0x5b, 0x8e, 0xb9, 0x59, + 0x4a, 0xfa, 0x82, 0xe5, 0x71, 0xd9, 0x4c, 0x1c, 0xcd, 0xfb, 0x10, 0xbb, + 0x17, 0xd2, 0x05, 0x62, 0xc3, 0x58, 0xf7, 0x31, 0x48, 0xe1, 0x85, 0xdb, + 0xdc, 0xd9, 0x7b, 0x93, 0xb7, 0xcb, 0x28, 0x10, 0x0d, 0x84, 0xfa, 0x4b, + 0x86, 0x44, 0x19, 0xa5, 0x3b, 0x27, 0x5f, 0x87, 0x60, 0xce, 0xca, 0xda, + 0xba, 0x61, 0x56, 0x55, 0x1a, 0x61, 0xc8, 0x54, 0x47, 0x89, 0x96, 0xc2, + 0x2e, 0xa7, 0x52, 0x21, 0x46, 0x50, 0xcb, 0x0a, 0x62, 0xb7, 0x8a, 0xa5, + 0xea, 0x6d, 0xe0, 0xbb, 0xd2, 0x85, 0x33, 0xfc, 0xa3, 0xb6, 0xed, 0x63, + 0xd6, 0x31, 0x2a, 0xdc, 0x66, 0x6e, 0xde, 0x68, 0x45, 0x91, 0xef, 0xf0, + 0x96, 0x25, 0x64, 0xfb, 0xee, 0x10, 0x1a, 0xfb, 0xbe, 0x04, 0xc6, 0x20, + 0x37, 0x88, 0x25, 0x97, 0x87, 0x3d, 0xd7, 0x8f, 0x03, 0xd6, 0x58, 0x03, + 0x5f, 0x4c, 0x2c, 0x3c, 0xd8, 0x6d, 0x7a, 0x65, 0x45, 0x79, 0xbc, 0xba, + 0x6d, 0xfa, 0x83, 0x82, 0xc4, 0xca, 0x40, 0x15, 0xdd, 0x4d, 0x7e, 0xc3, + 0x00, 0xbd, 0x28, 0xb5, 0xa4, 0x0a, 0xa7, 0x45, 0xcc, 0xc7, 0xb9, 0x7b, + 0x72, 0xfe, 0x48, 0x9f, 0x50, 0x8c, 0x45, 0x5e, 0x9a, 0x58, 0x01, 0x04, + 0xe5, 0x3a, 0xac, 0x91, 0x2c, 0x05, 0x45, 0xa5, 0xff, 0x38, 0x59, 0xc4, + 0xed, 0x2b, 0x49, 0xa2, 0x3e, 0x63, 0x0e, 0xa4, 0x95, 0xa5, 0x34, 0x1d, + 0x30, 0xc6, 0xb7, 0x3b, 0x00, 0xec, 0x2b, 0xa7, 0x36, 0x36, 0x2c, 0x78, + 0x0e, 0x98, 0x01, 0xd4, 0xe8, 0x70, 0x73, 0xac, 0xb8, 0xd3, 0x9f, 0xe8, + 0xc1, 0x2d, 0xb0, 0x68, 0x1e, 0x2c, 0x22, 0x0d, 0x84, 0x61, 0xb2, 0xdd, + 0x44, 0x31, 0x99, 0x14, 0x21, 0x65, 0x97, 0x94, 0x52, 0x6d, 0xfd, 0x0c, + 0xde, 0x51, 0x07, 0x31, 0x7f, 0xd1, 0xe2, 0x4b, 0xb6, 0x99, 0xc4, 0x9a, + 0xca, 0x3b, 0x10, 0x2c, 0xda, 0xed, 0x51, 0x17, 0xd0, 0xe5, 0x1c, 0x9a, + 0x63, 0xaa, 0x4b, 0x8b, 0x29, 0xe6, 0xbe, 0x0c, 0x04, 0xdb, 0x3e, 0xd9, + 0x58, 0xde, 0x1f, 0x69, 0x91, 0xf4, 0xd9, 0xa3, 0xc8, 0x50, 0x92, 0x56, + 0x4e, 0x57, 0xf5, 0x0b, 0x15, 0x35, 0x5c, 0x82, 0x90, 0x37, 0x08, 0x29, + 0x1b, 0x08, 0x56, 0x37, 0xd0, 0x97, 0x97, 0x75, 0xad, 0x36, 0xf1, 0xd1, + 0x75, 0xfa, 0x4f, 0xa7, 0x77, 0xda, 0x04, 0x55, 0x71, 0x5d, 0xbe, 0xc9, + 0xae, 0xd3, 0x45, 0x70, 0x53, 0xfe, 0x74, 0x12, 0x50, 0x73, 0x50, 0x9a, + 0x16, 0xfc, 0x53, 0x79, 0xf6, 0xf2, 0xc4, 0xa6, 0xb3, 0x2e, 0x0f, 0x32, + 0x62, 0x85, 0xef, 0x05, 0x8a, 0x5c, 0xfc, 0xe0, 0x99, 0x9c, 0x11, 0xbd, + 0xf0, 0xa8, 0xfc, 0x84, 0x86, 0xf2, 0xee, 0x59, 0xfa, 0xa8, 0xae, 0xc8, + 0x54, 0x81, 0xe9, 0x96, 0xcf, 0x91, 0x7e, 0xb0, 0xfb, 0x21, 0xc1, 0x20, + 0x5d, 0x47, 0xad, 0x95, 0x10, 0xf1, 0xe4, 0x2f, 0x15, 0xd9, 0x5d, 0x0c, + 0x64, 0x7f, 0x1e, 0x8d, 0x70, 0xb0, 0x29, 0x21, 0x87, 0xfe, 0x74, 0xf3, + 0x1e, 0x0f, 0xe2, 0xfc, 0xfb, 0xb5, 0xef, 0xbb, 0x32, 0x20, 0xb4, 0xfa, + 0x1a, 0x79, 0xbc, 0x6c, 0xd6, 0x5d, 0xf2, 0xce, 0xf5, 0x0b, 0x02, 0xeb, + 0xf3, 0xb7, 0xbc, 0x77, 0x8e, 0x05, 0x8a, 0x44, 0xac, 0x0b, 0x84, 0x93, + 0x65, 0x2e, 0x7d, 0x3e, 0x39, 0x7b, 0x67, 0xa2, 0xc3, 0x58, 0x83, 0x15, + 0xb6, 0x38, 0xc6, 0x54, 0x34, 0xa0, 0x99, 0xf9, 0x08, 0x5e, 0x7c, 0xb7, + 0x26, 0x12, 0xc0, 0x56, 0x2c, 0x8d, 0x40, 0x72, 0x54, 0xa0, 0x18, 0x7a, + 0x68, 0x82, 0x6d, 0x70, 0xb1, 0xcd, 0x50, 0xb7, 0xd4, 0xcb, 0x88, 0x03, + 0x94, 0xab, 0xdd, 0x4e, 0xaa, 0xd1, 0x43, 0x1a, 0x68, 0x95, 0x41, 0xd8, + 0xc1, 0xb5, 0x36, 0x09, 0x9e, 0x7a, 0xd9, 0x24, 0x83, 0x8d, 0xc6, 0x82, + 0xa8, 0x95, 0x79, 0x27, 0x9d, 0x23, 0x80, 0x65, 0x20, 0x81, 0xb4, 0xfc, + 0x44, 0x05, 0x0c, 0x39, 0xaf, 0x7c, 0xe4, 0xf1, 0xce, 0xb5, 0xdc, 0x50, + 0x08, 0x15, 0x16, 0x34, 0xb2, 0xa7, 0x0c, 0x29, 0x62, 0xc7, 0x51, 0x3e, + 0x33, 0x34, 0x00, 0xfb, 0x3f, 0xf8, 0xe1, 0x28, 0x23, 0xf5, 0x55, 0xd7, + 0xd0, 0xb4, 0xfd, 0x62, 0xfa, 0x08, 0xb9, 0xf6, 0x17, 0x43, 0x60, 0x83, + 0x65, 0xb5, 0x99, 0xe0, 0xc0, 0x39, 0x9d, 0x66, 0x35, 0x2e, 0x2a, 0x59, + 0x7f, 0x9b, 0xce, 0x65, 0x67, 0xf8, 0x26, 0x0f, 0xc0, 0xf9, 0xd3, 0x61, + 0xca, 0x82, 0xc9, 0xf8, 0xd7, 0xa5, 0xb2, 0x3f, 0xf5, 0xe5, 0x10, 0x8e, + 0x9b, 0x23, 0x3c, 0x3d, 0x52, 0x4a, 0xb8, 0xf0, 0x4e, 0xb1, 0x19, 0x63, + 0xf1, 0xa6, 0x5b, 0xc8, 0x47, 0xeb, 0xf3, 0x7b, 0x47, 0xb2, 0x6e, 0xb4, + 0xc9, 0x72, 0xca, 0x9d, 0xd1, 0x3d, 0x27, 0x52, 0xf6, 0x5a, 0xa8, 0xc9, + 0x0e, 0xf0, 0x0e, 0x90, 0x69, 0x6e, 0x49, 0xfc, 0x6a, 0x5b, 0xdc, 0xeb, + 0x5a, 0x3b, 0xc7, 0x32, 0xe1, 0xc0, 0x1c, 0x50, 0xed, 0x13, 0x33, 0x24, + 0xee, 0x1c, 0x47, 0x04, 0x8a, 0x90, 0xe1, 0x49, 0x5e, 0x3a, 0xfc, 0xe6, + 0x24, 0xeb, 0x52, 0xaf, 0x64, 0x18, 0x9b, 0xeb, 0xd7, 0xa5, 0x08, 0xa0, + 0x03, 0x88, 0x76, 0x92, 0x41, 0x52, 0xa4, 0x63, 0xe5, 0x63, 0x49, 0x25, + 0xa3, 0x10, 0xbe, 0x18, 0x56, 0xff, 0x0d, 0xfd, 0x4c, 0xf2, 0xb4, 0x5b, + 0x2c, 0xc3, 0xa8, 0xf0, 0x1f, 0x4a, 0xd3, 0xee, 0xc5, 0x80, 0x1a, 0xc6, + 0xea, 0x73, 0xcc, 0x95, 0xef, 0xd7, 0xa8, 0xde, 0x10, 0xdf, 0xe7, 0xbc, + 0xbe, 0x6e, 0x39, 0x7a, 0xbd, 0x92, 0x01, 0xb6, 0x36, 0x18, 0xd9, 0xe1, + 0x5d, 0xb8, 0x5b, 0xd0, 0x17, 0xbc, 0xcb, 0x7c, 0xa4, 0x3d, 0x82, 0x55, + 0xd0, 0x95, 0x37, 0x1a, 0x4f, 0xe7, 0x47, 0x19, 0x5c, 0x0b, 0x4f, 0x14, + 0x70, 0x47, 0x1e, 0xe0, 0xb6, 0x77, 0x6e, 0x5f, 0xa3, 0x8e, 0x92, 0x32, + 0xb4, 0x1d, 0xbd, 0xc4, 0xca, 0x86, 0xca, 0xd7, 0x34, 0xcd, 0xef, 0x0c, + 0x10, 0xcb, 0x95, 0x58, 0xfc, 0x5e, 0x28, 0x8d, 0x15, 0x4c, 0xaf, 0x85, + 0x72, 0x85, 0x72, 0xc2, 0x86, 0x45, 0x0c, 0xfb, 0x71, 0x2e, 0x69, 0x1c, + 0x38, 0xc4, 0x11, 0xa7, 0x31, 0x7a, 0xe0, 0xab, 0x1f, 0xa9, 0x38, 0xbb, + 0x00, 0x75, 0x4c, 0xf7, 0x38, 0x31, 0xfc, 0x50, 0x76, 0xe3, 0xcb, 0x0a, + 0x68, 0xfb, 0x36, 0x86, 0x2a, 0x1a, 0xde, 0x49, 0xd5, 0x5c, 0xbf, 0xb7, + 0x69, 0x78, 0x29, 0x48, 0x75, 0x51, 0xb8, 0xb8, 0x7b, 0xe6, 0x35, 0xdb, + 0x40, 0xa8, 0x45, 0x26, 0x99, 0x45, 0x81, 0x38, 0xad, 0x64, 0xa0, 0x5c, + 0x37, 0x6e, 0x15, 0xd6, 0xdb, 0x42, 0x85, 0x23, 0x2c, 0x52, 0xc6, 0x2c, + 0x51, 0x30, 0x83, 0x6c, 0xd8, 0xdf, 0x02, 0x77, 0x30, 0xdf, 0xc2, 0xe2, + 0x1c, 0x8a, 0xb1, 0x29, 0xf6, 0x66, 0x48, 0x51, 0x5a, 0x9f, 0xad, 0x79, + 0x76, 0xf1, 0xb4, 0x22, 0xa6, 0xea, 0xfc, 0x8e, 0xcf, 0x0e, 0x72, 0xc3, + 0x40, 0x04, 0x0b, 0x58, 0xec, 0xa4, 0x75, 0x5d, 0x57, 0x57, 0x2e, 0x0a, + 0x5a, 0xdc, 0x63, 0x89, 0xc5, 0x90, 0x54, 0xd9, 0x9b, 0x06, 0x3b, 0x64, + 0x23, 0x18, 0x4e, 0x44, 0xa4, 0xd3, 0x6c, 0xa3, 0x4f, 0x3f, 0xef, 0xf4, + 0xf4, 0xe7, 0x06, 0xf4, 0x29, 0xd8, 0xd0, 0xaa, 0x76, 0x5a, 0x89, 0x3b, + 0xc3, 0x16, 0x47, 0x46, 0xdf, 0xcb, 0x7f, 0x95, 0x90, 0xc7, 0x39, 0x17, + 0xbc, 0x4d, 0x71, 0x64, 0xbd, 0x52, 0xa1, 0x30, 0x6b, 0x79, 0x6f, 0x04, + 0xd1, 0x69, 0xa4, 0xc5, 0xa7, 0x3d, 0x09, 0xa3, 0xc9, 0xca, 0x58, 0x0c, + 0xc2, 0xf4, 0xac, 0xfd, 0xc0, 0xdc, 0xf2, 0x79, 0xcf, 0xe0, 0xda, 0x4e, + 0xe2, 0x87, 0xe5, 0xbf, 0x40, 0x09, 0x6f, 0x1d, 0x65, 0x71, 0xcf, 0xe7, + 0x22, 0x1a, 0x9f, 0xf9, 0xbb, 0x95, 0xdb, 0x88, 0xc9, 0x3f, 0x06, 0x47, + 0x50, 0x4a, 0x1a, 0xd6, 0xa9, 0x3e, 0xa9, 0x7a, 0xdb, 0x5f, 0x98, 0xc6, + 0xee, 0x10, 0xae, 0x93, 0x37, 0x03, 0x94, 0xcc, 0xfd, 0xf8, 0xa9, 0x75, + 0x21, 0xf9, 0x27, 0x5e, 0xde, 0x0e, 0x23, 0x93, 0x48, 0x92, 0x08, 0xf2, + 0x07, 0xed, 0x7b, 0x79, 0x8f, 0x30, 0x14, 0x48, 0x72, 0x68, 0x1e, 0xb7, + 0x53, 0x22, 0xe3, 0x1b, 0xd0, 0x97, 0xf2, 0xd4, 0x37, 0xde, 0xa8, 0x1b, + 0x2f, 0x08, 0x58, 0xcf, 0x29, 0x29, 0x3c, 0x55, 0xf2, 0x46, 0x60, 0xc7, + 0xda, 0xa5, 0xde, 0xaf, 0xd9, 0x1d, 0xf6, 0xa0, 0xf2, 0x39, 0xa1, 0xb1, + 0x41, 0x7c, 0x8c, 0xa8, 0xe3, 0x8c, 0x5f, 0xe1, 0xb7, 0x26, 0x50, 0xae, + 0x7f, 0xcd, 0x7a, 0x00, 0x5f, 0x91, 0xff, 0xe6, 0xb0, 0xa2, 0xeb, 0x58, + 0x64, 0x73, 0x7b, 0xe2, 0xca, 0xcc, 0x4e, 0xfa, 0x4f, 0x7c, 0x33, 0x42, + 0x8e, 0x54, 0x4e, 0x7b, 0xf1, 0xc0, 0x91, 0x90, 0x51, 0x42, 0x17, 0x24, + 0x7b, 0xb4, 0x37, 0x46, 0xaa, 0x10, 0x39, 0x58, 0x51, 0xa5, 0x8f, 0xad, + 0x0c, 0xb6, 0x8d, 0x08, 0xeb, 0xcc, 0x7d, 0xe5, 0x87, 0x20, 0x7f, 0x4d, + 0x68, 0xe0, 0x50, 0x14, 0x4d, 0xbf, 0x79, 0x4b, 0x52, 0x5a, 0x88, 0xae, + 0x3f, 0xb9, 0xec, 0x0b, 0x14, 0x81, 0xc2, 0x8a, 0x06, 0xad, 0xfd, 0x31, + 0x16, 0x60, 0xe7, 0xd1, 0x60, 0x6c, 0x62, 0xcb, 0x17, 0xfd, 0x05, 0x48, + 0xf6, 0x11, 0xf8, 0xaf, 0xd4, 0x6b, 0x1d, 0xab, 0x4a, 0xc5, 0xa2, 0xbd, + 0x63, 0xf3, 0xc0, 0xdd, 0xd6, 0xdd, 0x75, 0x8b, 0x04, 0x10, 0xab, 0x78, + 0x82, 0x31, 0xb8, 0xb9, 0x35, 0x93, 0x8d, 0x31, 0x41, 0x61, 0xc3, 0xb9, + 0x67, 0xc4, 0xb1, 0x34, 0x6a, 0x8f, 0x98, 0x51, 0xb0, 0x77, 0x90, 0x97, + 0x2e, 0xe6, 0xe0, 0xb4, 0xfc, 0xb9, 0xf3, 0x28, 0x85, 0x88, 0xdf, 0x19, + 0x87, 0x95, 0x25, 0x14, 0x4c, 0x91, 0x32, 0x2e, 0x98, 0x1b, 0x92, 0xc9, + 0xb3, 0xb8, 0x3b, 0xb8, 0xef, 0x72, 0x54, 0xa0, 0xd1, 0x25, 0x3d, 0x02, + 0x2e, 0x8f, 0x7c, 0x33, 0x4f, 0x48, 0xfc, 0xbc, 0xac, 0x81, 0xad, 0xa1, + 0x39, 0xc0, 0xbe, 0x10, 0xfd, 0x42, 0x9d, 0x7f, 0x67, 0x53, 0xff, 0x15, + 0xd1, 0xef, 0x26, 0x0e, 0x38, 0x37, 0xbf, 0xe7, 0xb5, 0xc1, 0x0f, 0xc5, + 0x5f, 0x48, 0xbd, 0x07, 0x80, 0xa7, 0x99, 0xba, 0xbf, 0x23, 0xcb, 0xb4, + 0x64, 0x29, 0xf9, 0xa4, 0xf2, 0x58, 0x8f, 0xfc, 0x64, 0x1c, 0xfe, 0x2f, + 0xb0, 0x49, 0x60, 0xac, 0xa7, 0x66, 0xe3, 0x8c, 0x33, 0x12, 0x82, 0x6a, + 0xb4, 0x0d, 0x1c, 0xb2, 0x19, 0x5d, 0x95, 0x10, 0xd3, 0x81, 0x0d, 0x3f, + 0x0a, 0x8e, 0xe4, 0x27, 0x8f, 0xc0, 0x12, 0x08, 0xfd, 0x4a, 0x01, 0xcd, + 0xa9, 0xe0, 0x2f, 0xf1, 0x6c, 0xbb, 0xd4, 0x28, 0x47, 0x10, 0x6f, 0x23, + 0x63, 0x20, 0x27, 0xe0, 0xe2, 0xe8, 0xb7, 0xba, 0x19, 0x07, 0xaa, 0x28, + 0x20, 0x12, 0x29, 0x33, 0x7e, 0x54, 0x6a, 0x76, 0x71, 0xde, 0xc2, 0xdf, + 0xbf, 0x1c, 0xb0, 0x73, 0x01, 0xf5, 0xf2, 0x60, 0x19, 0x7d, 0xf2, 0xd2, + 0xf5, 0x2e, 0x35, 0xa4, 0xb9, 0xff, 0xc6, 0x4c, 0x87, 0xf0, 0x4b, 0xa2, + 0x52, 0x8e, 0xdf, 0x80, 0xf6, 0x82, 0xc3, 0x49, 0x6d, 0x3d, 0xba, 0xf0, + 0x09, 0x75, 0x59, 0x12, 0x69, 0xe8, 0x2d, 0x6c, 0x1c, 0xc7, 0x81, 0x32, + 0xb6, 0xd7, 0x3d, 0x4a, 0x8d, 0x1a, 0xbd, 0x67, 0x29, 0xc3, 0x55, 0x0e, + 0xfc, 0x0a, 0xf0, 0x29, 0xc2, 0x59, 0x30, 0x72, 0x28, 0x01, 0x43, 0xa8, + 0x10, 0x2f, 0x26, 0xbb, 0xf3, 0x7c, 0xf8, 0x78, 0x7b, 0xc0, 0x53, 0x48, + 0xc3, 0x62, 0xa1, 0x27, 0xb7, 0x2a, 0x59, 0xcb, 0xb4, 0x64, 0xcb, 0x57, + 0x3d, 0x74, 0x95, 0xfe, 0x9b, 0xda, 0x29, 0xc7, 0x2a, 0x5b, 0xb1, 0xb0, + 0x9e, 0xbe, 0x41, 0x4e, 0x07, 0x29, 0x02, 0x11, 0xa0, 0xef, 0x06, 0x89, + 0x43, 0x21, 0x77, 0x2d, 0x53, 0xf0, 0xd8, 0xc2, 0xd0, 0x6b, 0x00, 0xbc, + 0x21, 0x98, 0x55, 0x46, 0x70, 0x90, 0x2a, 0xcf, 0xd1, 0x3b, 0x9c, 0xea, + 0x26, 0x49, 0x27, 0x2f, 0x87, 0x80, 0x6c, 0x8f, 0xa2, 0x52, 0xd5, 0xc2, + 0x68, 0x25, 0xe3, 0x2a, 0x27, 0x87, 0x6c, 0x28, 0x75, 0x1a, 0x44, 0x7d, + 0x41, 0x4c, 0x0e, 0x83, 0x01, 0x4c, 0xd2, 0xc0, 0x89, 0x32, 0x61, 0x00, + 0x91, 0x21, 0x1b, 0x06, 0x03, 0x9e, 0xf2, 0xb3, 0x0e, 0x63, 0x1e, 0xe2, + 0x56, 0xf3, 0x6f, 0x38, 0xd0, 0xe3, 0xa0, 0x0d, 0xf2, 0x5a, 0x02, 0x23, + 0x99, 0x9b, 0x61, 0x5e, 0x8e, 0xb4, 0x7e, 0x18, 0xce, 0x7a, 0x6d, 0xcc, + 0x9f, 0x51, 0x38, 0x79, 0xdb, 0xe8, 0x19, 0xd2, 0x23, 0x28, 0xcd, 0x45, + 0x27, 0xcf, 0xdc, 0xc5, 0x67, 0x9f, 0xab, 0x5a, 0xc3, 0x40, 0x85, 0x61, + 0xf3, 0x84, 0x36, 0x2d, 0x25, 0xb3, 0x5b, 0x07, 0x86, 0xae, 0xf9, 0x84, + 0x57, 0xe1, 0x86, 0x69, 0xea, 0x24, 0x44, 0xc9, 0x60, 0x0b, 0xef, 0x6b, + 0x36, 0xb7, 0xd7, 0x7e, 0x15, 0x01, 0x4b, 0x4d, 0x7c, 0xf3, 0x8a, 0x5c, + 0xf9, 0xff, 0x7b, 0x93, 0xa7, 0xf5, 0x72, 0xa6, 0x37, 0x22, 0x67, 0xc8, + 0xf8, 0xbd, 0x9b, 0x7d, 0x05, 0x16, 0xe4, 0x9c, 0xb6, 0xac, 0x7f, 0x5d, + 0xe7, 0x4c, 0x63, 0x6c, 0xe8, 0x9e, 0xac, 0x30, 0x64, 0x52, 0x74, 0x7c, + 0xbb, 0xce, 0x07, 0xc0, 0xde, 0xac, 0x2e, 0x1e, 0x16, 0x3f, 0xea, 0x5b, + 0x96, 0xcc, 0x8f, 0x61, 0xbc, 0x04, 0xb0, 0x21, 0xa3, 0xa2, 0x8e, 0x79, + 0x7f, 0xdc, 0x2f, 0xc2, 0xa3, 0xdf, 0x99, 0x86, 0x61, 0x67, 0xf6, 0x38, + 0x31, 0xe7, 0xbf, 0x3a, 0x22, 0xdb, 0x27, 0xfc, 0x4b, 0x1d, 0xd1, 0xda, + 0x68, 0x15, 0x45, 0x44, 0xfd, 0x61, 0x58, 0x64, 0x98, 0x7d, 0x29, 0x0b, + 0x67, 0x52, 0xae, 0x7a, 0x01, 0xb5, 0x13, 0x89, 0xea, 0xd4, 0xd9, 0xa3, + 0xd9, 0xbb, 0xf2, 0x62, 0x19, 0x2f, 0x39, 0xe5, 0x98, 0xd7, 0x16, 0x5b, + 0x4e, 0x42, 0xa9, 0x81, 0x91, 0x54, 0xe9, 0x80, 0xb9, 0xd9, 0xf6, 0xe6, + 0xe1, 0x83, 0x91, 0x57, 0xd4, 0xe7, 0xe4, 0x69, 0x21, 0xd8, 0xc1, 0x7b, + 0xa3, 0x17, 0x3a, 0x01, 0xf1, 0xab, 0x66, 0xf5, 0xb6, 0xbd, 0x11, 0x09, + 0xbf, 0x75, 0xad, 0xdf, 0x6f, 0x65, 0x82, 0x66, 0x30, 0xfb, 0x5d, 0x25, + 0x01, 0x94, 0x74, 0xab, 0x47, 0xc7, 0x0c, 0x54, 0x2b, 0xdb, 0x7d, 0x41, + 0xd2, 0x20, 0x59, 0xa5, 0xdf, 0xd9, 0xb1, 0x45, 0x09, 0x19, 0x3e, 0xd2, + 0x23, 0xc4, 0x3a, 0xdc, 0xd5, 0xff, 0x6f, 0x27, 0x90, 0x8f, 0x47, 0x76, + 0x37, 0xdd, 0xc6, 0xf0, 0xfd, 0x8d, 0x61, 0x32, 0xf5, 0xfd, 0xf8, 0x28, + 0x03, 0x82, 0xaf, 0x22, 0xbd, 0xe4, 0x2b, 0xa4, 0xde, 0xd4, 0x52, 0x94, + 0x99, 0x9f, 0xda, 0xf4, 0xbe, 0x0e, 0xfb, 0x6b, 0x0b, 0x49, 0x8b, 0x77, + 0xb9, 0x77, 0xee, 0xc3, 0x0d, 0x7e, 0x7e, 0x84, 0xd7, 0xbb, 0x18, 0x1d, + 0x81, 0xf0, 0xf0, 0xbf, 0x9a, 0x9a, 0x41, 0xd2, 0x2b, 0x04, 0x50, 0x27, + 0xb3, 0xf7, 0x9d, 0xd7, 0xb4, 0x99, 0x5c, 0xd9, 0x52, 0xbd, 0xb6, 0x03, + 0x5a, 0xb8, 0x2f, 0x11, 0xc2, 0x18, 0x62, 0x3e, 0xb1, 0x67, 0x9c, 0x50, + 0x30, 0x57, 0xf8, 0x96, 0x57, 0x4b, 0x95, 0x39, 0x37, 0x92, 0xa4, 0xe1, + 0x04, 0x7b, 0x19, 0xcc, 0x47, 0xb6, 0xb5, 0xa7, 0xad, 0x22, 0x46, 0xf8, + 0x32, 0x3d, 0xee, 0xee, 0x82, 0x6e, 0x79, 0xa1, 0x37, 0xba, 0x10, 0x2a, + 0x4b, 0xdd, 0x91, 0xe8, 0xa9, 0x23, 0xaa, 0x40, 0x26, 0x28, 0x6c, 0x3a, + 0x3d, 0x77, 0x1e, 0xc0, 0xdd, 0xe7, 0x40, 0x0d, 0xbd, 0x59, 0x72, 0x32, + 0xd6, 0x9f, 0x8a, 0x19, 0xc7, 0xf5, 0xd4, 0x71, 0x9d, 0xef, 0x44, 0x17, + 0xb4, 0xb7, 0x35, 0x10, 0x11, 0x0b, 0x1a, 0xe2, 0x0f, 0x1f, 0x47, 0xbf, + 0xd0, 0x68, 0x6d, 0x59, 0x27, 0xc9, 0xa6, 0xaf, 0x54, 0x9e, 0x2a, 0x43, + 0x47, 0x60, 0x02, 0x81, 0xda, 0x5f, 0x3a, 0xbc, 0x46, 0x9a, 0x1c, 0x92, + 0x2e, 0xf4, 0x15, 0x06, 0xec, 0xe2, 0x9e, 0x10, 0x03, 0xd1, 0x78, 0x9f, + 0xe0, 0x49, 0xe3, 0xc4, 0x7f, 0x83, 0x12, 0xc6, 0x15, 0xd2, 0xe8, 0x1b, + 0x40, 0x7b, 0x86, 0xaf, 0x86, 0xb3, 0x02, 0xe1, 0x5a, 0x71, 0x57, 0x80, + 0x1d, 0x31, 0x1d, 0xb6, 0x61, 0x7d, 0xc6, 0xde, 0xf8, 0x5c, 0x9f, 0xa5, + 0x3c, 0x30, 0xa0, 0x3c, 0xdd, 0x1f, 0xeb, 0xe6, 0x08, 0x6b, 0x3c, 0xc0, + 0x0e, 0xd1, 0x23, 0x95, 0x45, 0x30, 0xdc, 0x7f, 0x49, 0xfb, 0x61, 0xf4, + 0x5d, 0xe8, 0xdf, 0x35, 0x61, 0x1d, 0xcb, 0x3c, 0x98, 0x75, 0x07, 0x41, + 0xd4, 0xf0, 0xe7, 0x73, 0xaf, 0x0a, 0x89, 0x20, 0xda, 0x54, 0x9e, 0x01, + 0x38, 0xe4, 0x66, 0x3c, 0x2d, 0x5f, 0x43, 0x53, 0x8a, 0xa8, 0x79, 0x41, + 0x93, 0x19, 0x5c, 0x60, 0x3d, 0x65, 0xb3, 0x71, 0x80, 0x42, 0x93, 0x9a, + 0xa4, 0x0c, 0x76, 0xb2, 0xc6, 0x92, 0x10, 0xe8, 0x88, 0xb3, 0x21, 0x8c, + 0x7e, 0xa2, 0xad, 0xdf, 0x41, 0x34, 0xc3, 0x96, 0xe9, 0x94, 0xc9, 0x3a, + 0x50, 0x6e, 0xe5, 0x67, 0xd9, 0x09, 0x5b, 0x11, 0xd9, 0xbf, 0x45, 0x99, + 0xa8, 0x00, 0xfe, 0x02, 0x82, 0x62, 0xfc, 0xea, 0x55, 0x9d, 0x3f, 0x8c, + 0x7e, 0x06, 0xd7, 0x90, 0xd1, 0xc0, 0xcc, 0x4a, 0x98, 0xa2, 0x37, 0x3a, + 0x72, 0xd7, 0xc9, 0xd0, 0xb9, 0x15, 0x55, 0x29, 0xc1, 0xc5, 0x7b, 0x64, + 0xa8, 0x82, 0xac, 0x73, 0x8f, 0x0e, 0xba, 0x75, 0xc1, 0x77, 0x18, 0x27, + 0x03, 0x8e, 0xe0, 0x87, 0xb5, 0x0e, 0x2c, 0xa7, 0x67, 0x83, 0x1f, 0x1e, + 0xb2, 0x6e, 0xd9, 0x61, 0x08, 0x97, 0xf4, 0x94, 0x8c, 0x5d, 0x42, 0x14, + 0x63, 0xd5, 0x04, 0x68, 0x15, 0xc2, 0xdc, 0x80, 0xd9, 0x4e, 0x63, 0xf0, + 0x8f, 0x47, 0xd9, 0xa3, 0xc0, 0xe2, 0x2e, 0x10, 0x81, 0xf8, 0x4b, 0xe5, + 0x61, 0xf3, 0xc5, 0x4f, 0x84, 0x58, 0x98, 0xd2, 0x69, 0xfe, 0x08, 0x79, + 0x85, 0x4f, 0xc8, 0x0e, 0xdd, 0x02, 0xd3, 0xfd, 0x94, 0xa3, 0xfd, 0xb3, + 0x58, 0x0d, 0xa7, 0x72, 0x26, 0x0a, 0xbf, 0x66, 0xcc, 0x71, 0x4f, 0xeb, + 0x26, 0x39, 0x17, 0x52, 0x08, 0x50, 0x64, 0x66, 0x83, 0x7f, 0xd0, 0xc7, + 0x7f, 0xba, 0xe0, 0x18, 0x90, 0x48, 0x19, 0x1e, 0x5f, 0x84, 0xe9, 0x64, + 0x64, 0x12, 0xc2, 0xcf, 0x53, 0x30, 0x44, 0xc0, 0xc8, 0x6c, 0x42, 0xea, + 0xfb, 0x6d, 0x61, 0xda, 0x1f, 0x4a, 0xd1, 0xe1, 0xc6, 0xd4, 0x09, 0x62, + 0xf6, 0xf1, 0xec, 0x64, 0x8a, 0xb7, 0x8a, 0x68, 0x4f, 0xe8, 0x01, 0x4d, + 0x20, 0x29, 0x67, 0x45, 0xe5, 0x4e, 0x6e, 0x49, 0xe0, 0x15, 0x42, 0xbf, + 0xf5, 0x08, 0x3d, 0x79, 0x7b, 0xd6, 0xbe, 0xda, 0x9a, 0x66, 0xa4, 0x12, + 0xb8, 0x32, 0xbf, 0xd0, 0x70, 0xc2, 0xa0, 0xf0, 0xc3, 0xf9, 0x9b, 0xff, + 0x65, 0x32, 0xa7, 0x08, 0xf5, 0x79, 0x1e, 0x15, 0xef, 0x6f, 0x32, 0xe0, + 0xa1, 0xdb, 0x68, 0xdd, 0xa1, 0xd5, 0x14, 0xf6, 0xb0, 0x75, 0x0d, 0x43, + 0xbb, 0x91, 0xd7, 0xf0, 0xb0, 0x8b, 0x17, 0x0d, 0xc7, 0x33, 0x48, 0xeb, + 0x78, 0xac, 0x97, 0xe6, 0x61, 0x55, 0x28, 0xae, 0x5a, 0xa1, 0x0a, 0xb6, + 0x64, 0x8a, 0xf8, 0x40, 0xda, 0xe5, 0x60, 0xf6, 0x45, 0xd8, 0xf0, 0xc8, + 0x7a, 0x9f, 0x77, 0x6a, 0x09, 0x88, 0xd3, 0x1d, 0x40, 0x63, 0xc5, 0x38, + 0x89, 0x6c, 0x0f, 0xe4, 0xc0, 0x1b, 0x2a, 0x56, 0xea, 0xa3, 0xcc, 0xde, + 0x9d, 0x06, 0x29, 0x2a, 0x7e, 0xed, 0x07, 0x9a, 0x43, 0x80, 0xa6, 0xa4, + 0x25, 0x5f, 0x24, 0x91, 0x84, 0xf3, 0xc0, 0x23, 0x06, 0x65, 0xee, 0x6e, + 0xba, 0xa8, 0x80, 0x46, 0xf3, 0x8b, 0xf8, 0xde, 0x3f, 0x47, 0x96, 0x85, + 0x2b, 0x51, 0x03, 0x02, 0xc9, 0x58, 0x6a, 0x33, 0x5c, 0x9a, 0xe2, 0xf9, + 0x6b, 0x1e, 0xad, 0x2f, 0x82, 0x9c, 0xf5, 0x3d, 0xf8, 0x14, 0xbf, 0x96, + 0x80, 0xe0, 0xd0, 0xa5, 0x7a, 0x2e, 0x1e, 0x5d, 0xc3, 0x4f, 0x99, 0xdd, + 0x91, 0x02, 0xad, 0x9f, 0x32, 0x1b, 0xd7, 0xa9, 0x75, 0xdc, 0x3d, 0x11, + 0x6d, 0x18, 0xff, 0xe9, 0x47, 0xe6, 0xc4, 0x63, 0x14, 0xb8, 0xe2, 0x57, + 0x60, 0xbd, 0xb7, 0x2f, 0xb4, 0x57, 0x39, 0xb9, 0x33, 0xa6, 0x4f, 0x16, + 0x14, 0xf2, 0x7e, 0x2c, 0xa0, 0x76, 0xa5, 0xfe, 0x04, 0x2c, 0x53, 0x96, + 0xaf, 0x80, 0xfd, 0xe5, 0xc2, 0x5d, 0x36, 0xcd, 0xec, 0xbb, 0xc0, 0xd1, + 0xeb, 0x10, 0x03, 0xcc, 0xf9, 0x9c, 0xa7, 0xc9, 0xc1, 0x72, 0xb8, 0x72, + 0x3c, 0x58, 0x21, 0x02, 0x7a, 0x25, 0xd8, 0x7b, 0x99, 0x49, 0x8a, 0x10, + 0x9c, 0x1f, 0x9a, 0x89, 0xc4, 0xd0, 0x3f, 0x2a, 0xa9, 0x36, 0x6d, 0x48, + 0x83, 0x2c, 0xbd, 0x62, 0xec, 0x51, 0xfe, 0x53, 0x32, 0x03, 0x61, 0x0c, + 0x85, 0x5b, 0xb4, 0x06, 0x29, 0x27, 0x3d, 0x9c, 0xf2, 0x56, 0x14, 0xc4, + 0xbf, 0x20, 0x5b, 0x56, 0xb2, 0xd9, 0x63, 0xcd, 0x36, 0xd6, 0x17, 0x76, + 0x25, 0x0c, 0xbb, 0x12, 0xa9, 0xe2, 0xda, 0x42, 0x82, 0xef, 0xcd, 0xbf, + 0x02, 0x09, 0x74, 0xaa, 0x2a, 0x89, 0xc6, 0xbc, 0xc1, 0x57, 0x75, 0x17, + 0xbc, 0x93, 0x47, 0x73, 0x2a, 0x99, 0xec, 0xc2, 0x8a, 0xe8, 0xf9, 0x8a, + 0xd9, 0x8b, 0x78, 0x67, 0xf2, 0x6f, 0x83, 0x3d, 0x30, 0xa8, 0xe4, 0xa9, + 0x8a, 0xff, 0x54, 0xf6, 0xa1, 0x9d, 0xe2, 0xbb, 0xcd, 0x03, 0x3a, 0x16, + 0xc3, 0xc3, 0x5e, 0xff, 0x38, 0xb8, 0x67, 0x15, 0x0b, 0x2c, 0xc3, 0xbc, + 0x86, 0xe5, 0x61, 0x8a, 0x0c, 0xd9, 0x23, 0xe1, 0x88, 0xc6, 0x10, 0x89, + 0xca, 0x32, 0x77, 0x41, 0x7d, 0xd9, 0x7a, 0x31, 0x79, 0x42, 0x52, 0x0b, + 0xca, 0x07, 0x9d, 0x74, 0xfd, 0x57, 0xe0, 0xcc, 0xe0, 0x74, 0xc2, 0xde, + 0x57, 0x55, 0x15, 0xa2, 0x28, 0x68, 0x74, 0x8c, 0x05, 0xfa, 0x27, 0x0d, + 0x23, 0x79, 0x7b, 0x59, 0x2d, 0x16, 0xdf, 0x0b, 0xb6, 0xe1, 0x51, 0x48, + 0x84, 0xdf, 0xb2, 0x42, 0x51, 0x56, 0x46, 0xbd, 0x3c, 0x3e, 0xa6, 0x59, + 0xea, 0x27, 0x60, 0xad, 0xb9, 0xd5, 0x8d, 0x90, 0x01, 0x43, 0x57, 0x05, + 0x75, 0xc0, 0xc4, 0xa5, 0x00, 0x6d, 0x56, 0x2e, 0xe6, 0x03, 0x17, 0xbd, + 0x48, 0x78, 0x95, 0x65, 0x96, 0x4a, 0x55, 0x8c, 0xa3, 0x60, 0xea, 0xfd, + 0xd4, 0x3f, 0x07, 0x15, 0x0b, 0xec, 0x37, 0x7c, 0xf5, 0x64, 0x11, 0x94, + 0x2e, 0xdc, 0x17, 0x6b, 0x4a, 0xd8, 0x83, 0xb5, 0xf2, 0xf2, 0x7f, 0x87, + 0x6d, 0x2c, 0xdd, 0x6d, 0x55, 0x6c, 0x95, 0x16, 0x18, 0xe5, 0xe9, 0x7b, + 0x90, 0xa0, 0xd6, 0x1b, 0xce, 0x06, 0xc1, 0x99, 0x84, 0x0f, 0xd6, 0xff, + 0x67, 0x08, 0xe9, 0xc2, 0xd5, 0x7f, 0x0f, 0x1e, 0x75, 0x4a, 0x11, 0x06, + 0xec, 0xc9, 0xe6, 0x4b, 0x2e, 0x87, 0xad, 0x40, 0x80, 0xb6, 0x2f, 0x9c, + 0xd1, 0xed, 0xaf, 0x07, 0x17, 0x6e, 0xe8, 0x3a, 0x47, 0xb0, 0x73, 0x01, + 0x56, 0x9f, 0x70, 0xf5, 0xe3, 0x4d, 0x8a, 0x1b, 0x21, 0x71, 0x3b, 0x09, + 0xf0, 0x34, 0x25, 0xe5, 0x3c, 0x23, 0x3d, 0x67, 0x5d, 0xa7, 0xb1, 0x2d, + 0x3c, 0x15, 0x17, 0x96, 0x25, 0xe0, 0xc8, 0x35, 0xbe, 0x58, 0xe1, 0x0e, + 0xda, 0x73, 0x19, 0x3d, 0x06, 0x0c, 0xf4, 0x26, 0xd0, 0xfb, 0xaa, 0x92, + 0x06, 0xa0, 0x26, 0x94, 0xad, 0x92, 0xe8, 0x80, 0xcd, 0xc0, 0x1a, 0x4b, + 0x70, 0xe8, 0x0d, 0x12, 0xc7, 0xef, 0xc2, 0xd1, 0xc5, 0x9b, 0x77, 0xda, + 0x0f, 0x43, 0xcb, 0x58, 0x09, 0x73, 0xd1, 0x6b, 0x78, 0xbe, 0x76, 0xea, + 0xe6, 0x9f, 0x4b, 0x6d, 0x6d, 0x14, 0x1a, 0x4e, 0x99, 0xb0, 0x13, 0x1b, + 0x18, 0xd4, 0xd7, 0x13, 0x6c, 0x52, 0xcb, 0xb1, 0x66, 0xfc, 0x78, 0x8c, + 0xbe, 0x1c, 0x67, 0xcf, 0x30, 0xed, 0x2f, 0x91, 0x09, 0xf0, 0x89, 0xe6, + 0xe1, 0xff, 0x32, 0x66, 0x20, 0x6f, 0x96, 0x16, 0x57, 0x3b, 0x06, 0x49, + 0xaa, 0xd9, 0x2c, 0xe7, 0xed, 0xdc, 0x9c, 0x27, 0x9a, 0xef, 0x5c, 0xaa, + 0x17, 0xda, 0x31, 0x4c, 0xf9, 0x05, 0x77, 0x72, 0x9a, 0x89, 0xb9, 0xf2, + 0x63, 0x0b, 0x36, 0x48, 0xbb, 0xd1, 0x07, 0x1f, 0x48, 0x7d, 0x99, 0xcc, + 0x7e, 0xbc, 0x2f, 0x20, 0xbf, 0x70, 0xa2, 0xf3, 0xaf, 0xee, 0x56, 0xb8, + 0x24, 0x5f, 0x3a, 0x49, 0x70, 0xfb, 0xd6, 0xc6, 0xf5, 0x47, 0x7d, 0x00, + 0x6f, 0xd6, 0xd0, 0xbb, 0xc3, 0xa0, 0xae, 0x6e, 0x7b, 0x04, 0x19, 0x6f, + 0x27, 0xb3, 0x62, 0x16, 0x64, 0x20, 0xdb, 0x84, 0x1f, 0xd4, 0x0e, 0xb4, + 0xe6, 0x79, 0x8d, 0x7f, 0x09, 0x43, 0xbd, 0xa0, 0x9e, 0xb5, 0xc1, 0xf4, + 0x14, 0xf4, 0x20, 0xae, 0x52, 0x75, 0xf4, 0x7b, 0xde, 0x0e, 0x46, 0x3e, + 0x52, 0xcf, 0x82, 0xc1, 0x6c, 0x38, 0xf1, 0xf8, 0x1d, 0x97, 0xcb, 0x3f, + 0x34, 0x59, 0x6a, 0xfe, 0x9e, 0xe6, 0x36, 0xc7, 0x3e, 0xaa, 0x15, 0xe3, + 0xb4, 0xd5, 0x34, 0x66, 0xfb, 0x97, 0x45, 0x58, 0xa5, 0x4c, 0x47, 0xe5, + 0x21, 0x04, 0x10, 0xc6, 0xea, 0x47, 0x37, 0xa3, 0x85, 0x73, 0xdd, 0x7c, + 0x2f, 0x12, 0xc6, 0xf5, 0x6f, 0x17, 0xd4, 0x44, 0x1f, 0x80, 0x4f, 0xad, + 0x18, 0xf9, 0x83, 0x4c, 0x8c, 0x9d, 0xe3, 0x4c, 0x73, 0x18, 0xe7, 0x81, + 0x08, 0xb7, 0xeb, 0x2e, 0x72, 0xa5, 0xf3, 0x42, 0x51, 0xc4, 0x12, 0xe8, + 0x62, 0xae, 0xb1, 0x1f, 0xe1, 0x78, 0x5b, 0x97, 0x86, 0x3b, 0x6d, 0xf9, + 0x10, 0x78, 0x79, 0xbd, 0xd3, 0x3b, 0xa4, 0xa1, 0x0e, 0xc3, 0x6e, 0xc6, + 0x68, 0x72, 0x53, 0x08, 0xb5, 0xc4, 0xa3, 0x79, 0xe7, 0xf0, 0xfe, 0x14, + 0xc3, 0xf5, 0xfa, 0x0b, 0xc3, 0x88, 0xd2, 0xf6, 0xdf, 0x91, 0x63, 0x4f, + 0x8e, 0xa6, 0xcd, 0x2a, 0xcf, 0xe2, 0x8c, 0x17, 0xa8, 0xe8, 0xb3, 0x20, + 0x84, 0xf6, 0xe0, 0x45, 0x48, 0xbe, 0xa2, 0xa7, 0xbe, 0x82, 0x84, 0x9e, + 0x86, 0x1d, 0x63, 0xd4, 0xc0, 0xb0, 0xf3, 0xb8, 0x65, 0xc3, 0x27, 0xa2, + 0xa4, 0x69, 0xc7, 0x56, 0xe0, 0x95, 0x97, 0xa2, 0xa0, 0xe6, 0xd9, 0x73, + 0x3d, 0x8b, 0xde, 0x38, 0xf5, 0xfd, 0x69, 0xa8, 0x14, 0x9a, 0x71, 0x07, + 0xa7, 0xb6, 0xc4, 0x40, 0xa0, 0x22, 0x61, 0x91, 0x22, 0x62, 0x56, 0xd9, + 0x81, 0x7e, 0x75, 0x76, 0xa1, 0xf6, 0x88, 0x12, 0xf5, 0x5b, 0xbf, 0x88, + 0x1d, 0x1e, 0xc2, 0x3a, 0xd8, 0x76, 0x8d, 0xfb, 0xf1, 0xce, 0xc3, 0x2f, + 0xb2, 0xfb, 0x63, 0xef, 0xf5, 0xc2, 0x8c, 0x0b, 0x85, 0xf9, 0xad, 0x60, + 0x25, 0xf1, 0x71, 0x74, 0x11, 0x9a, 0x3d, 0xff, 0xea, 0xa7, 0x0c, 0x6a, + 0x1b, 0x20, 0x71, 0xfd, 0x89, 0xdc, 0xb4, 0xf0, 0x38, 0x55, 0x79, 0xd3, + 0x66, 0xdd, 0xb4, 0x31, 0xa9, 0x2a, 0xe0, 0x47, 0x7d, 0x8b, 0xa9, 0x9c, + 0x34, 0x4d, 0x93, 0xfe, 0x0a, 0x5e, 0x3b, 0xc7, 0x1d, 0xb5, 0xc0, 0x59, + 0x9e, 0x1b, 0x94, 0x88, 0xdf, 0xe6, 0xef, 0x21, 0x53, 0xfb, 0xb8, 0xa7, + 0x12, 0x18, 0xd9, 0xd2, 0x2f, 0x2c, 0x37, 0xb7, 0x6b, 0x4d, 0x4f, 0x8b, + 0x3f, 0x80, 0xdc, 0xd3, 0x89, 0x2e, 0x2c, 0xda, 0x7e, 0x33, 0x95, 0x00, + 0x1d, 0xa3, 0x1f, 0x73, 0x86, 0x02, 0x8c, 0x7c, 0x1e, 0x09, 0xd1, 0x3c, + 0xef, 0x49, 0xd6, 0xbc, 0x9f, 0x6c, 0x59, 0x0b, 0x53, 0xb5, 0xb1, 0x54, + 0xeb, 0xa5, 0x26, 0xfa, 0x75, 0x77, 0x63, 0x67, 0x97, 0xa8, 0x2a, 0xaa, + 0x05, 0x72, 0x92, 0x04, 0x65, 0x02, 0xd6, 0x08, 0x80, 0xf1, 0x7a, 0xf4, + 0x48, 0x5d, 0x14, 0x55, 0x57, 0xef, 0xdb, 0x90, 0x43, 0x9d, 0xa6, 0x6c, + 0x22, 0xd0, 0x18, 0x68, 0x01, 0x5c, 0x79, 0x1d, 0xda, 0xcd, 0x87, 0x74, + 0xdf, 0x2c, 0xe6, 0x5b, 0x51, 0xf3, 0x8b, 0x8c, 0xf1, 0xe4, 0x2c, 0x83, + 0x6e, 0x30, 0x73, 0xb1, 0x97, 0x0c, 0x56, 0x82, 0xde, 0x6c, 0x44, 0x04, + 0x75, 0x1c, 0xe3, 0x34, 0xaf, 0x04, 0xbb, 0xfd, 0x15, 0xf4, 0xd2, 0x53, + 0x7f, 0x7f, 0xe7, 0xa6, 0x5d, 0x85, 0x18, 0x90, 0x12, 0xb2, 0x6d, 0xcc, + 0xe6, 0x7a, 0x4a, 0xa5, 0xa8, 0x55, 0xc8, 0x49, 0x9a, 0x47, 0x6a, 0xa8, + 0xd6, 0xc2, 0xcf, 0x2a, 0xfc, 0xa0, 0x71, 0x14, 0x9d, 0x96, 0x9e, 0xff, + 0xfc, 0x15, 0x05, 0x8c, 0xdc, 0x50, 0x2e, 0xf2, 0xc0, 0x6c, 0x20, 0x66, + 0x8a, 0xbc, 0x3f, 0x09, 0x10, 0x48, 0x75, 0x15, 0xbb, 0x7c, 0x7b, 0x0c, + 0xc9, 0x0d, 0xb1, 0x45, 0x62, 0x73, 0x2e, 0x09, 0x45, 0x75, 0xc6, 0xf1, + 0x11, 0xf8, 0xeb, 0x84, 0x42, 0x3f, 0xd7, 0x7c, 0x52, 0x08, 0x74, 0x84, + 0x65, 0x82, 0x3a, 0x2d, 0x12, 0x12, 0x57, 0x9c, 0x4c, 0xa8, 0x5c, 0x0d, + 0x90, 0x49, 0x25, 0x65, 0xff, 0x35, 0x47, 0x0e, 0xaf, 0x8c, 0x80, 0xf4, + 0x2d, 0x42, 0xb1, 0x53, 0x4b, 0x5e, 0x90, 0xfb, 0x05, 0x36, 0x2a, 0x26, + 0x8b, 0xec, 0xac, 0x42, 0xa7, 0x44, 0x00, 0xff, 0x47, 0xc8, 0x5a, 0x03, + 0x41, 0xed, 0x6a, 0x4f, 0x6c, 0x68, 0x31, 0x9f, 0x2b, 0x55, 0x98, 0x12, + 0x4a, 0x02, 0x88, 0xe0, 0xe8, 0x94, 0x1d, 0x8b, 0x9e, 0xed, 0x6f, 0xf8, + 0x34, 0xb7, 0x77, 0x26, 0x6e, 0xcf, 0x68, 0x88, 0x72, 0x1e, 0x94, 0x66, + 0x5b, 0x11, 0x52, 0x30, 0x89, 0x5e, 0x38, 0xdf, 0x55, 0x03, 0x68, 0x3f, + 0x5a, 0xae, 0x08, 0x83, 0xa7, 0xa8, 0xbd, 0xd0, 0x62, 0x66, 0x24, 0x02, + 0x9e, 0x6d, 0xcb, 0xab, 0x16, 0xd4, 0x43, 0x3b, 0xdb, 0xd0, 0x83, 0xdc, + 0xb9, 0xa9, 0xce, 0x68, 0x34, 0x07, 0xab, 0x86, 0x09, 0xcd, 0xac, 0xb8, + 0x17, 0x4e, 0xd2, 0x54, 0x71, 0x7d, 0xee, 0x9a, 0x6d, 0xe3, 0x37, 0xb1, + 0x7a, 0xd8, 0x3d, 0xc8, 0x13, 0x08, 0x87, 0x18, 0x3b, 0xb6, 0x13, 0x85, + 0x96, 0x2f, 0x0e, 0xfc, 0xf2, 0xf5, 0xf3, 0xf0, 0xf3, 0xaf, 0x65, 0xa5, + 0xcb, 0x56, 0x69, 0xa3, 0x10, 0x37, 0x39, 0x10, 0x12, 0xa9, 0xe5, 0x5a, + 0xf8, 0xc1, 0x46, 0x2b, 0x04, 0x37, 0xb7, 0x8d, 0xd9, 0xcb, 0x72, 0x8e, + 0xd7, 0x41, 0xcb, 0xf0, 0x0f, 0xa6, 0x1f, 0x4d, 0xaf, 0xe8, 0x58, 0x2f, + 0xe4, 0xea, 0x89, 0x04, 0x50, 0x3c, 0xcc, 0xd8, 0x41, 0x55, 0x48, 0xd0, + 0x57, 0x5e, 0x93, 0xa1, 0x29, 0x79, 0xce, 0xfd, 0x0d, 0xbc, 0xfb, 0xe8, + 0x18, 0x79, 0x31, 0x97, 0xef, 0xc4, 0xf4, 0x46, 0x83, 0x5e, 0xd2, 0x1f, + 0xee, 0xab, 0x3e, 0x6c, 0xa4, 0xb0, 0x79, 0x42, 0xb7, 0xbf, 0x1b, 0xc9, + 0x7e, 0xfc, 0xe2, 0xba, 0xa1, 0x56, 0x0a, 0xc2, 0x60, 0x46, 0x5e, 0xee, + 0x62, 0x5e, 0xd2, 0x17, 0xf0, 0x9c, 0xa9, 0x79, 0xdd, 0xea, 0xea, 0x02, + 0xdd, 0x23, 0xe7, 0x2c, 0x63, 0xfa, 0x38, 0xb5, 0xf4, 0x32, 0x4c, 0x19, + 0x6a, 0x2d, 0x64, 0xcd, 0xc6, 0x37, 0xd0, 0x14, 0xf0, 0xbd, 0xef, 0xb3, + 0xae, 0x8a, 0x98, 0xe7, 0x4c, 0xa3, 0x0b, 0x45, 0x76, 0x25, 0x00, 0xd9, + 0xe6, 0x02, 0xc0, 0x5c, 0x14, 0xf4, 0x5d, 0x70, 0x9c, 0x32, 0x2b, 0xe3, + 0x21, 0xa3, 0x8a, 0x08, 0x50, 0x4e, 0xd5, 0x8a, 0x44, 0x79, 0xe6, 0x57, + 0x89, 0x92, 0x0d, 0x33, 0xe0, 0x10, 0x39, 0x27, 0x62, 0xa9, 0x93, 0x9a, + 0x22, 0x34, 0x03, 0xa8, 0x4b, 0xc1, 0x49, 0xdc, 0x80, 0xa6, 0x92, 0xca, + 0xdb, 0xc2, 0xc6, 0x6d, 0xa4, 0xbd, 0x04, 0x19, 0xae, 0x0c, 0x61, 0x0b, + 0x9b, 0x12, 0xf4, 0x0a, 0x11, 0x89, 0xc6, 0x5f, 0x6d, 0xf8, 0x35, 0x03, + 0xdf, 0x8d, 0xa2, 0x4d, 0xe0, 0x53, 0xda, 0x53, 0x73, 0xa3, 0x6f, 0x89, + 0xc8, 0xf4, 0xfd, 0xbb, 0x1c, 0x0c, 0xc1, 0xef, 0x47, 0xe0, 0x34, 0x15, + 0x0a, 0x47, 0x38, 0xe9, 0x04, 0xe4, 0x6c, 0x8f, 0x4a, 0xb3, 0x67, 0x04, + 0x9a, 0xc3, 0x4b, 0x73, 0x03, 0xac, 0xac, 0xb2, 0x3c, 0xc5, 0x78, 0x27, + 0x05, 0xdc, 0x1d, 0xe5, 0x70, 0x05, 0xbc, 0x9c, 0x43, 0xab, 0xf9, 0x11, + 0x8a, 0xb5, 0x00, 0xc0, 0x7a, 0x3f, 0xa7, 0x39, 0x75, 0xc3, 0x99, 0x05, + 0x69, 0x2a, 0x60, 0xc0, 0x9d, 0x8c, 0x08, 0xd8, 0x60, 0x7b, 0x78, 0x49, + 0x7a, 0xb9, 0x3f, 0xfd, 0x90, 0x92, 0x94, 0xff, 0x73, 0x41, 0x3b, 0xe6, + 0x6c, 0xd2, 0x50, 0xa8, 0x15, 0xab, 0xc7, 0xc8, 0xff, 0xac, 0xf5, 0x11, + 0x66, 0x26, 0xa2, 0xc1, 0x35, 0x11, 0x46, 0x09, 0x62, 0x3d, 0xd5, 0x93, + 0x04, 0x87, 0x99, 0x53, 0x75, 0xe3, 0x61, 0x94, 0x3a, 0xcc, 0xe9, 0xfd, + 0xba, 0xa6, 0xa1, 0x8c, 0x22, 0xf4, 0x1e, 0xcb, 0x3b, 0x76, 0x52, 0x1a, + 0x3b, 0x95, 0x23, 0xd6, 0xf1, 0xbc, 0x52, 0xa9, 0x44, 0x52, 0x15, 0x95, + 0xab, 0xe7, 0x46, 0x25, 0xb5, 0x04, 0x2e, 0xa7, 0xb4, 0x7a, 0xf5, 0x39, + 0x80, 0xd3, 0x77, 0xaa, 0x49, 0x78, 0xf1, 0x5a, 0x4d, 0x3d, 0x50, 0xb9, + 0x67, 0xd5, 0x71, 0x2c, 0x50, 0x0c, 0x45, 0x07, 0x80, 0x3c, 0xbf, 0xa2, + 0xe6, 0x08, 0x32, 0x9f, 0x46, 0x7b, 0x6b, 0xb8, 0x80, 0x1b, 0x10, 0x2f, + 0x96, 0x6d, 0x9a, 0x89, 0x21, 0xad, 0x86, 0xe6, 0x58, 0x76, 0x4b, 0x30, + 0x9f, 0x68, 0xed, 0xee, 0x6a, 0xca, 0x83, 0x00, 0x93, 0x97, 0x9a, 0x62, + 0x46, 0x79, 0x38, 0xa2, 0x8b, 0xed, 0xa1, 0x5e, 0x2f, 0xfd, 0x49, 0x5b, + 0x9a, 0xd7, 0xa9, 0x98, 0xaf, 0x0a, 0x4e, 0x64, 0xfe, 0xfd, 0x33, 0x29, + 0xe0, 0x8e, 0xc1, 0xf2, 0x63, 0x03, 0x12, 0x9f, 0x85, 0x09, 0xd9, 0x36, + 0xac, 0xd8, 0x13, 0xd6, 0xd1, 0x99, 0x0c, 0x61, 0x9c, 0xa0, 0x7b, 0x26, + 0xa0, 0x8d, 0xe3, 0x5a, 0x92, 0x45, 0xf8, 0x94, 0x4a, 0xc3, 0xf1, 0xdf, + 0xf6, 0x40, 0xb1, 0x8b, 0x1a, 0xd1, 0x96, 0x89, 0x8f, 0x80, 0x44, 0xcd, + 0x69, 0x29, 0x0f, 0x21, 0xda, 0x8e, 0xc7, 0x22, 0x10, 0x99, 0x0e, 0xf8, + 0x2f, 0xdd, 0x64, 0x8f, 0xab, 0x81, 0xec, 0x97, 0x8c, 0x53, 0xe2, 0x5f, + 0xd4, 0x37, 0x74, 0xea, 0xd0, 0x78, 0x19, 0xa1, 0x43, 0x48, 0x13, 0x0b, + 0xdd, 0xb9, 0x19, 0xca, 0x37, 0x03, 0x09, 0x03, 0x52, 0xc3, 0xeb, 0x83, + 0x55, 0x00, 0xcc, 0xdd, 0x04, 0x12, 0xae, 0x7f, 0x9c, 0xdf, 0x9b, 0xc5, + 0xc4, 0x5b, 0xcb, 0x9f, 0x6f, 0xcc, 0xf5, 0x27, 0xbb, 0x79, 0x85, 0x10, + 0xa0, 0x15, 0x13, 0x31, 0x85, 0x90, 0x23, 0x81, 0x12, 0x1e, 0xd0, 0xce, + 0xaa, 0x55, 0x3b, 0xe6, 0xd7, 0xe3, 0x86, 0xfd, 0x4f, 0x39, 0x01, 0x00, + 0x4f, 0xa1, 0xd2, 0x2c, 0x61, 0x5c, 0xa4, 0x47, 0x69, 0x8b, 0xfd, 0x18, + 0x6f, 0x48, 0x2e, 0x86, 0x2e, 0x6c, 0xfe, 0xcc, 0x73, 0xe7, 0x41, 0x0c, + 0xd8, 0x64, 0x2b, 0x5e, 0x92, 0x89, 0x8f, 0x30, 0x2e, 0xc6, 0x68, 0xfc, + 0xa5, 0x85, 0xd9, 0x15, 0xa3, 0xc0, 0xce, 0xb1, 0xfb, 0xa0, 0xe7, 0x25, + 0x31, 0x40, 0x47, 0x6d, 0xc8, 0x52, 0x9b, 0x4a, 0xaf, 0x36, 0x33, 0xee, + 0xfc, 0x7f, 0xf1, 0x54, 0x6a, 0x2f, 0xcd, 0xa3, 0x9a, 0x14, 0x47, 0xa4, + 0x47, 0xa2, 0x6a, 0xd2, 0x0d, 0x18, 0x64, 0x1a, 0x21, 0x20, 0x24, 0xfc, + 0xd5, 0xed, 0xf5, 0x55, 0xa2, 0x9a, 0xff, 0xc3, 0x0f, 0x6a, 0x36, 0xe7, + 0xa1, 0x3f, 0xfe, 0x04, 0xf1, 0x00, 0xa2, 0xcc, 0xb6, 0xf0, 0x93, 0x76, + 0xf9, 0x42, 0xba, 0x53, 0x52, 0x54, 0x81, 0x10, 0x7d, 0xa5, 0xec, 0x7f, + 0x63, 0x62, 0x4f, 0xf2, 0xbe, 0xfa, 0x27, 0x6f, 0xbe, 0xee, 0xe6, 0xe7, + 0x39, 0x81, 0x9f, 0x92, 0x60, 0x2f, 0x66, 0x3d, 0x5b, 0xf0, 0x99, 0xc6, + 0x99, 0xb9, 0x17, 0x2e, 0x04, 0xa1, 0x64, 0x71, 0xa8, 0x72, 0x9f, 0x6a, + 0xae, 0x10, 0x58, 0x4d, 0xb4, 0x82, 0x58, 0x02, 0x11, 0x48, 0x34, 0xd5, + 0xb4, 0xdb, 0x98, 0x30, 0xd5, 0x6a, 0x19, 0x23, 0x5a, 0x3d, 0x0b, 0xd0, + 0x92, 0xb4, 0x21, 0xce, 0xe5, 0x53, 0x26, 0xea, 0x56, 0xfc, 0xc7, 0x48, + 0xaa, 0xa1, 0x3b, 0xf7, 0x4e, 0x0c, 0x48, 0x92, 0x33, 0xf7, 0x65, 0x4d, + 0x84, 0xe1, 0xe8, 0x7c, 0x60, 0x7a, 0xab, 0x4b, 0x2f, 0xc9, 0x07, 0xfa, + 0xc2, 0xb0, 0xb0, 0x8c, 0xcb, 0xf5, 0x74, 0x23, 0x01, 0xe4, 0x5b, 0x8b, + 0x32, 0x8d, 0x24, 0x11, 0x09, 0x1f, 0xd3, 0x8e, 0x60, 0x5e, 0xa4, 0xa3, + 0x97, 0xc5, 0x2f, 0xe9, 0xc1, 0x92, 0xab, 0xad, 0xe0, 0x4f, 0x67, 0x34, + 0x8b, 0x3f, 0x9f, 0x8d, 0x49, 0x7b, 0x11, 0x8d, 0x13, 0x41, 0xbf, 0xf5, + 0x89, 0xbf, 0xe6, 0x1d, 0x13, 0xf4, 0xfa, 0xa1, 0x18, 0xfa, 0x49, 0x54, + 0x1e, 0xd4, 0xf9, 0x67, 0x34, 0xec, 0x96, 0x3b, 0xda, 0xb5, 0x97, 0x03, + 0xf7, 0x52, 0x9b, 0x49, 0xf7, 0x37, 0x3e, 0x4c, 0x80, 0x53, 0x40, 0x8a, + 0x1a, 0xf8, 0xb1, 0x81, 0xe2, 0x5a, 0x68, 0x86, 0x24, 0xda, 0xf5, 0x95, + 0x90, 0xee, 0x66, 0x06, 0xf9, 0xaa, 0x58, 0x70, 0xb1, 0xe4, 0x54, 0xe4, + 0xfa, 0x60, 0x95, 0x29, 0x5a, 0xab, 0xb7, 0xd3, 0x11, 0x97, 0xb5, 0xb4, + 0x84, 0x8b, 0xba, 0x75, 0x66, 0xb9, 0x76, 0x12, 0xa5, 0xa2, 0x1d, 0x59, + 0x07, 0x31, 0x83, 0xf2, 0xab, 0x2e, 0x85, 0x30, 0x0b, 0xb4, 0x5f, 0x56, + 0x14, 0x04, 0x2f, 0x55, 0x32, 0x00, 0xc5, 0x0b, 0x13, 0x22, 0x2d, 0xc5, + 0x57, 0x71, 0x29, 0x56, 0xe3, 0xf5, 0xc4, 0xa2, 0x19, 0x4c, 0x2c, 0x0f, + 0x71, 0x55, 0x3a, 0x41, 0x37, 0x5b, 0x18, 0x91, 0x05, 0xb4, 0xbb, 0x3e, + 0xc0, 0x50, 0xb4, 0xec, 0x0b, 0xf6, 0xfd, 0x26, 0x8e, 0xd9, 0x15, 0x76, + 0x27, 0x83, 0x74, 0x10, 0x4b, 0x36, 0xb6, 0xf2, 0x7f, 0xc9, 0xa7, 0x89, + 0x05, 0xe7, 0x68, 0x2a, 0x0c, 0x36, 0x03, 0xd5, 0x07, 0x92, 0x0a, 0x67, + 0xa5, 0xff, 0x4e, 0x0c, 0x08, 0x38, 0x57, 0x28, 0x53, 0x27, 0x4d, 0x27, + 0x1b, 0x18, 0x70, 0x8e, 0x89, 0x46, 0xdf, 0x04, 0x4a, 0xd6, 0x2d, 0x05, + 0x1d, 0x0c, 0xfb, 0xfa, 0x71, 0xd7, 0xba, 0x9a, 0xa5, 0x7a, 0xe6, 0xd8, + 0x4f, 0x34, 0x1f, 0x82, 0x6f, 0x29, 0x40, 0x4a, 0xe3, 0xdf, 0x38, 0x3a, + 0x0f, 0x30, 0xe8, 0xd4, 0xa8, 0xc0, 0x1a, 0x92, 0x27, 0xdd, 0x49, 0x91, + 0x6d, 0x25, 0x56, 0x34, 0xc9, 0x5b, 0x17, 0xcd, 0x57, 0x65, 0x2c, 0x65, + 0xde, 0x2b, 0x68, 0x8d, 0xd5, 0xd7, 0xa8, 0x49, 0x4e, 0x46, 0xd4, 0xa4, + 0x5a, 0x9c, 0xa1, 0x17, 0x25, 0x0a, 0x46, 0xa9, 0x13, 0xa4, 0xa5, 0x02, + 0x1f, 0xd4, 0x36, 0x4e, 0x97, 0xfa, 0x71, 0x9a, 0x52, 0x86, 0xda, 0xff, + 0x07, 0xa4, 0xe3, 0x2b, 0xb8, 0x3f, 0x34, 0xd7, 0x73, 0x75, 0xd1, 0x0b, + 0xf3, 0x77, 0x16, 0x32, 0x18, 0xe4, 0x56, 0xf1, 0xac, 0x84, 0xb1, 0x1a, + 0x9f, 0xbf, 0x7b, 0x34, 0x5b, 0xac, 0x00, 0x05, 0x71, 0x16, 0xa3, 0xe1, + 0xab, 0x66, 0xac, 0x4f, 0xde, 0x76, 0x19, 0x1e, 0x31, 0x31, 0x33, 0x8f, + 0x40, 0xfd, 0x2b, 0xe1, 0x95, 0xde, 0xf6, 0xd3, 0x08, 0x0c, 0x5a, 0x6c, + 0x4c, 0x42, 0x5c, 0x4b, 0x46, 0x79, 0x2a, 0x98, 0xbb, 0x8b, 0x07, 0x43, + 0x4e, 0x8d, 0x6f, 0x21, 0x75, 0x72, 0xde, 0x72, 0x18, 0xbe, 0xb7, 0x34, + 0x21, 0x1b, 0xc0, 0x91, 0xdd, 0x69, 0x42, 0x60, 0xc6, 0x09, 0x5b, 0xe0, + 0x56, 0xf7, 0xd8, 0x39, 0xb9, 0xd1, 0x07, 0x30, 0xf1, 0xc6, 0xc6, 0x50, + 0x7d, 0x27, 0xbf, 0xf6, 0x96, 0x00, 0x29, 0x6d, 0xbc, 0x2f, 0x74, 0xeb, + 0x98, 0x60, 0x32, 0x73, 0xb7, 0x80, 0xf8, 0x3b, 0x8e, 0x30, 0xf1, 0x9d, + 0xa8, 0xa1, 0xb6, 0x3f, 0x7f, 0x09, 0x1b, 0xc3, 0x93, 0xf6, 0xa4, 0x0f, + 0x1f, 0x63, 0x0d, 0xdc, 0x72, 0x3e, 0x82, 0x47, 0x9a, 0x4b, 0x9b, 0x1d, + 0x82, 0x46, 0x99, 0x66, 0x85, 0x3e, 0xd3, 0x84, 0x05, 0x8d, 0xaa, 0x38, + 0x6d, 0x01, 0x6a, 0xa6, 0x8c, 0xf1, 0xaa, 0x8d, 0x0a, 0x98, 0xb9, 0xf0, + 0xd5, 0x7f, 0xe3, 0xd3, 0xa9, 0x94, 0x5d, 0xbc, 0x5b, 0x13, 0x29, 0x15, + 0x89, 0xe9, 0x79, 0x98, 0x3b, 0x74, 0x11, 0x9c, 0x60, 0x19, 0x5b, 0xce, + 0x84, 0x45, 0xe7, 0xdc, 0xd3, 0xd5, 0xd4, 0x3c, 0x5b, 0x54, 0x67, 0xa6, + 0x34, 0x4f, 0xe2, 0x3b, 0xf5, 0xb9, 0x51, 0xf9, 0xfb, 0xe4, 0xe8, 0xb5, + 0x88, 0xe3, 0x36, 0x60, 0xf4, 0x01, 0xac, 0x0c, 0xdd, 0x04, 0x3a, 0x3c, + 0xfc, 0x59, 0xbb, 0xa0, 0xa1, 0xaa, 0x9f, 0x7d, 0xca, 0x1e, 0xf1, 0x2f, + 0x56, 0xa1, 0x37, 0x60, 0x07, 0xf8, 0x20, 0xd3, 0x97, 0x7d, 0x30, 0xdb, + 0x6f, 0x94, 0x3f, 0xf6, 0x4b, 0xe3, 0xd4, 0xb8, 0x7f, 0x89, 0x69, 0xe4, + 0xe6, 0xd1, 0x69, 0xf8, 0xc2, 0xd3, 0xac, 0xc6, 0xae, 0x35, 0x5b, 0x65, + 0x8b, 0xbe, 0x86, 0x20, 0x2f, 0x19, 0xe0, 0xaf, 0xcd, 0x2b, 0x30, 0xe3, + 0xc5, 0x0f, 0x01, 0x18, 0xd5, 0xfa, 0x00, 0xb2, 0xf5, 0xbe, 0xb4, 0x8c, + 0xd3, 0x31, 0x45, 0xbf, 0x0d, 0xa9, 0x87, 0x15, 0x72, 0x1a, 0x26, 0x7b, + 0x2d, 0xbf, 0xee, 0x8e, 0x10, 0xdb, 0x1b, 0xc8, 0xeb, 0x47, 0x07, 0x0f, + 0xba, 0x4a, 0x46, 0xbc, 0xa9, 0x7e, 0xa9, 0x20, 0x11, 0x04, 0xe4, 0x1a, + 0x24, 0x3a, 0xc6, 0x8f, 0x00, 0x87, 0x12, 0xd8, 0x2f, 0xab, 0x2c, 0xea, + 0x83, 0x09, 0x8d, 0x50, 0x54, 0x24, 0x7d, 0x56, 0x37, 0xbb, 0x99, 0xbd, + 0xf6, 0xc8, 0x99, 0x02, 0x0d, 0x8a, 0x06, 0xba, 0xe8, 0x32, 0xf4, 0xd4, + 0xf4, 0x26, 0xf5, 0xcc, 0xc1, 0x8f, 0x8f, 0x48, 0xd7, 0xe3, 0x0f, 0x41, + 0xcb, 0xe0, 0x61, 0x0b, 0x03, 0x0f, 0x7f, 0x31, 0x8a, 0xc9, 0xba, 0x2e, + 0xce, 0x73, 0x1b, 0x7c, 0xbc, 0x19, 0x50, 0x9b, 0xab, 0x0e, 0x42, 0x20, + 0x56, 0x56, 0x99, 0x40, 0xa2, 0xea, 0x45, 0x26, 0x45, 0xf7, 0x15, 0x44, + 0x65, 0xcd, 0x47, 0xbd, 0x82, 0xd3, 0x8a, 0x82, 0x0f, 0x96, 0x82, 0x5f, + 0xeb, 0x7c, 0x1d, 0x43, 0x5b, 0xd0, 0x5b, 0x36, 0xeb, 0xe6, 0x05, 0x7f, + 0x51, 0x6f, 0x16, 0x7f, 0xc4, 0x86, 0xcd, 0x15, 0xa7, 0x44, 0x12, 0x9a, + 0xd6, 0xf3, 0xd2, 0xdf, 0x4d, 0x7a, 0x48, 0x6e, 0xe5, 0x8a, 0x41, 0x8c, + 0xf0, 0x17, 0x38, 0x7d, 0x50, 0x76, 0x2f, 0x42, 0x31, 0x5c, 0xe6, 0x94, + 0x38, 0x40, 0xf8, 0x97, 0x74, 0x88, 0x31, 0xfb, 0x3b, 0xcc, 0xa0, 0x61, + 0x6d, 0xaf, 0x04, 0x51, 0xa9, 0x8a, 0x17, 0x1a, 0x5b, 0x58, 0x16, 0x98, + 0x75, 0xb9, 0xe2, 0xb3, 0x04, 0xd4, 0x03, 0x10, 0x3f, 0x2e, 0x7c, 0x0e, + 0xbb, 0x4f, 0xae, 0xa9, 0xc8, 0xe3, 0xe1, 0x8b, 0x73, 0x54, 0xef, 0xa9, + 0x27, 0xf4, 0x1d, 0xe7, 0xa3, 0x95, 0x9f, 0x52, 0xfc, 0x40, 0xd2, 0x9e, + 0x00, 0x7b, 0xc0, 0x00, 0xbb, 0x5d, 0xcb, 0xac, 0x65, 0x51, 0xc4, 0x1d, + 0x08, 0xed, 0x28, 0xab, 0xe8, 0x59, 0xa8, 0x11, 0x34, 0x51, 0xb7, 0x91, + 0x75, 0x2e, 0x12, 0x94, 0x3c, 0xcc, 0x1b, 0xb7, 0x61, 0x3e, 0x19, 0x2d, + 0x2a, 0x85, 0xc6, 0x4d, 0x81, 0x5b, 0x59, 0xdf, 0x11, 0x61, 0x66, 0x33, + 0xec, 0x8c, 0x4d, 0x14, 0x43, 0x0b, 0x04, 0xf2, 0x44, 0xe7, 0x62, 0xf3, + 0x69, 0xb4, 0x8f, 0x63, 0x9f, 0xd5, 0x2b, 0xd2, 0x53, 0xbd, 0xeb, 0xc2, + 0x1d, 0x59, 0x2f, 0xb2, 0x7c, 0x20, 0x4c, 0x4e, 0x1d, 0x30, 0x6e, 0x67, + 0x3b, 0x5f, 0x85, 0xfa, 0xf8, 0x53, 0xef, 0xbd, 0xc5, 0x66, 0x16, 0x96, + 0x42, 0xec, 0x38, 0xc7, 0xdd, 0x15, 0xcf, 0x87, 0xd7, 0x6a, 0x06, 0x5e, + 0xb3, 0x90, 0x1c, 0x3b, 0x25, 0xc9, 0xb9, 0x10, 0xb6, 0xeb, 0x9a, 0x94, + 0xb2, 0x6d, 0x40, 0xe6, 0x8b, 0x21, 0xf6, 0xb2, 0xbc, 0xc6, 0xef, 0x54, + 0x47, 0x53, 0x7d, 0x36, 0xd1, 0x2d, 0xdb, 0xd1, 0xf4, 0x9a, 0xee, 0xfa, + 0xe9, 0x9c, 0xbb, 0x3c, 0xf4, 0xc1, 0xab, 0x25, 0x4d, 0xab, 0x3c, 0xea, + 0xec, 0x94, 0x2f, 0xff, 0xd3, 0x0b, 0x8b, 0x98, 0x34, 0x7a, 0x4c, 0xbb, + 0x9c, 0x4e, 0x96, 0x75, 0x41, 0xaf, 0xe2, 0xc1, 0x61, 0x05, 0xea, 0xd3, + 0x4b, 0x7d, 0xf0, 0x21, 0xd4, 0x7b, 0xcb, 0x73, 0x84, 0x55, 0x2f, 0x03, + 0xd6, 0x86, 0x99, 0xd2, 0xbc, 0x52, 0xaa, 0x84, 0x20, 0x4c, 0xca, 0x01, + 0xec, 0x3a, 0x44, 0x4a, 0xfb, 0xd9, 0xfc, 0xf7, 0x2f, 0xc6, 0x6a, 0x67, + 0xc3, 0xe5, 0x55, 0x22, 0xae, 0x74, 0xa7, 0x0e, 0x53, 0x7d, 0xb9, 0xbd, + 0x1a, 0x33, 0xf9, 0xcf, 0x07, 0x35, 0xd2, 0xd7, 0xf4, 0x7c, 0xa4, 0x57, + 0x56, 0x11, 0xf7, 0x06, 0x5e, 0xb4, 0xa3, 0xd3, 0x51, 0xc3, 0xff, 0xdb, + 0x20, 0x2e, 0x11, 0xbb, 0x9b, 0x48, 0x2b, 0x63, 0x2b, 0x29, 0xd1, 0xf8, + 0x56, 0xd8, 0xf0, 0x7d, 0x73, 0x6f, 0x47, 0x8d, 0xbe, 0x04, 0xe8, 0xb4, + 0x1b, 0x6b, 0xc7, 0x29, 0x96, 0xc4, 0x9a, 0x51, 0x47, 0x75, 0x95, 0x96, + 0x08, 0x36, 0x76, 0x5c, 0x58, 0x2e, 0xe5, 0x58, 0x7a, 0x1f, 0x7a, 0x57, + 0x43, 0xf2, 0x51, 0x81, 0x29, 0x84, 0x96, 0xcd, 0xbc, 0x58, 0x13, 0x3c, + 0x4d, 0x00, 0x68, 0x7c, 0x44, 0xbf, 0x0d, 0xf2, 0x65, 0x8e, 0x14, 0xad, + 0xf1, 0xed, 0x9e, 0x7d, 0xa3, 0x8d, 0xb6, 0x76, 0x23, 0x0c, 0x0a, 0xc5, + 0x46, 0xd8, 0x9f, 0x88, 0x43, 0x10, 0x4f, 0x74, 0x37, 0xa0, 0xd0, 0x5b, + 0xc9, 0x06, 0x9f, 0xe9, 0x2b, 0x00, 0x98, 0x46, 0xa4, 0x67, 0xc3, 0x9f, + 0xd1, 0x8c, 0x3d, 0x40, 0x0a, 0x37, 0x10, 0xfe, 0xd4, 0xc5, 0xed, 0x8d, + 0x97, 0x78, 0x72, 0x95, 0x55, 0x58, 0x4a, 0x08, 0xa4, 0x5e, 0xbb, 0xee, + 0xe3, 0x2b, 0x49, 0xea, 0x7a, 0x15, 0x1f, 0x90, 0x0f, 0x10, 0x0a, 0xb2, + 0x27, 0x21, 0x31, 0xef, 0x1b, 0xb0, 0xdb, 0xdf, 0x58, 0xe1, 0xf6, 0x40, + 0x52, 0xd6, 0x1b, 0x2c, 0xb1, 0x7a, 0xf1, 0x78, 0x55, 0xb2, 0x75, 0xb3, + 0x64, 0x02, 0xef, 0x3e, 0xc7, 0xeb, 0xa8, 0x9f, 0x96, 0xe1, 0xb3, 0x31, + 0x08, 0x88, 0x12, 0x33, 0x3a, 0xc7, 0x10, 0x2c, 0x3e, 0x9b, 0xa7, 0xfc, + 0xf7, 0x6b, 0xa7, 0xec, 0x67, 0xde, 0x08, 0xe4, 0x56, 0x44, 0x81, 0xd7, + 0x55, 0xda, 0xe8, 0x6e, 0xe1, 0x04, 0x0b, 0x88, 0x9a, 0x97, 0x1b, 0x57, + 0xc6, 0x19, 0x85, 0xb8, 0x05, 0x6e, 0xf9, 0x55, 0x8f, 0x9a, 0x2d, 0x91, + 0x44, 0x1d, 0xca, 0x84, 0x55, 0x2b, 0xa1, 0xbb, 0x1e, 0xf0, 0x11, 0x11, + 0xb3, 0x58, 0xe5, 0x0f, 0x31, 0x39, 0xa8, 0x44, 0xa1, 0xf1, 0x55, 0xb8, + 0x22, 0x0f, 0x60, 0x61, 0x1f, 0xf1, 0xdb, 0xdb, 0x21, 0xa8, 0xcb, 0x90, + 0x36, 0xd8, 0xc4, 0x94, 0xc0, 0xcd, 0xe0, 0xc8, 0xe7, 0x40, 0xa4, 0xa2, + 0x2b, 0xd8, 0x06, 0x62, 0x55, 0x03, 0x54, 0x88, 0x98, 0x52, 0x5e, 0x9c, + 0x51, 0x59, 0x5b, 0x29, 0x4f, 0x84, 0xd9, 0x91, 0x16, 0xc1, 0x02, 0xc7, + 0x17, 0x95, 0x08, 0xe0, 0xe3, 0x6e, 0xd6, 0xf0, 0x73, 0x05, 0x43, 0x15, + 0x8b, 0x33, 0x10, 0x6b, 0xa1, 0x52, 0xa6, 0xfc, 0x78, 0x29, 0x44, 0xbe, + 0x1c, 0xcb, 0x82, 0x91, 0x83, 0x6b, 0xff, 0x6d, 0x2a, 0x9a, 0xdb, 0xf8, + 0x94, 0x45, 0x4b, 0x31, 0xe8, 0x3f, 0x0d, 0xda, 0xcf, 0x5e, 0xf3, 0x6a, + 0x29, 0x8a, 0xdc, 0xed, 0x5b, 0x38, 0xac, 0x69, 0x8a, 0x75, 0x7c, 0xa0, + 0x07, 0x61, 0xdf, 0xb5, 0x58, 0x94, 0x23, 0x82, 0x79, 0x00, 0xbd, 0x5b, + 0x8b, 0x03, 0xf9, 0xb1, 0xac, 0x3a, 0xf3, 0x43, 0x61, 0xef, 0xc0, 0x77, + 0xa7, 0x86, 0x98, 0xf8, 0x86, 0x8b, 0x43, 0x9a, 0xba, 0x11, 0x0c, 0xc8, + 0x33, 0x95, 0x27, 0xe8, 0x87, 0x50, 0x3d, 0x7d, 0xa6, 0x0b, 0xc1, 0xc0, + 0x60, 0x81, 0x4d, 0x27, 0x73, 0xad, 0xdd, 0xb4, 0xa6, 0xc4, 0x70, 0x3d, + 0x3e, 0x88, 0x40, 0x91, 0x8d, 0x80, 0x9f, 0x55, 0x1a, 0x04, 0xeb, 0xb3, + 0xc1, 0x9a, 0x51, 0xae, 0x69, 0x91, 0xab, 0xaf, 0xe0, 0x68, 0xb3, 0x6e, + 0x10, 0xfa, 0xda, 0x3d, 0x1d, 0xe6, 0x24, 0x87, 0x52, 0x44, 0xc9, 0x8c, + 0x36, 0x13, 0xbe, 0x92, 0x45, 0xe4, 0xc2, 0x55, 0x86, 0x6e, 0x3c, 0xd5, + 0x53, 0x3b, 0xe9, 0xe2, 0x65, 0x0e, 0xc1, 0xa6, 0x61, 0xbd, 0x2a, 0x1c, + 0x27, 0x5d, 0x8f, 0xd7, 0xfa, 0x51, 0xec, 0x4e, 0xe3, 0xd4, 0x98, 0x18, + 0xda, 0x0c, 0x36, 0xd5, 0xa4, 0xa3, 0xc8, 0xce, 0xed, 0x16, 0x8d, 0xc6, + 0x7d, 0x88, 0x5c, 0x82, 0x23, 0x1b, 0x50, 0x60, 0xc2, 0xaa, 0xe8, 0x5a, + 0x51, 0x9a, 0x21, 0xfb, 0xd4, 0x83, 0xa2, 0xed, 0x67, 0xe4, 0x8d, 0x7d, + 0x32, 0xdf, 0xff, 0x09, 0x45, 0x35, 0x5c, 0x76, 0x4b, 0x40, 0x8c, 0x69, + 0x83, 0x18, 0x1b, 0x14, 0x82, 0x78, 0xd0, 0xa9, 0x7f, 0x38, 0x16, 0x89, + 0x6e, 0x2d, 0xd7, 0xd4, 0xbe, 0x1d, 0xa3, 0x42, 0x19, 0x7f, 0x27, 0x0a, + 0x14, 0x15, 0x4c, 0xbf, 0x45, 0xd7, 0x05, 0xca, 0x3a, 0x93, 0xba, 0xb4, + 0x80, 0x69, 0xe0, 0x5b, 0xcd, 0x5f, 0xc4, 0x58, 0x6b, 0x53, 0x39, 0x7e, + 0xae, 0x59, 0xd5, 0x35, 0xc4, 0x31, 0xf8, 0x55, 0xe7, 0x55, 0xe0, 0x5c, + 0x3e, 0xc4, 0xfc, 0x07, 0xa1, 0x10, 0xa7, 0x05, 0xa4, 0xbe, 0x37, 0x80, + 0xd6, 0x7d, 0x97, 0x70, 0xc9, 0xae, 0x06, 0x7e, 0x41, 0x92, 0x03, 0xcf, + 0xbd, 0xa2, 0x2d, 0x53, 0x95, 0x26, 0xb9, 0x6e, 0x7e, 0xd6, 0xcf, 0x7e, + 0x57, 0x50, 0x7a, 0x12, 0x37, 0x47, 0xbd, 0x6e, 0xc9, 0x3c, 0x42, 0xb9, + 0x3a, 0xe6, 0x5b, 0x6e, 0x79, 0x8b, 0xb2, 0xad, 0x65, 0x94, 0x0b, 0x48, + 0x46, 0xeb, 0x5e, 0x10, 0xc5, 0x27, 0xd2, 0xc1, 0xda, 0xc4, 0x68, 0xd5, + 0x5a, 0xf2, 0x76, 0xd8, 0xcb, 0x73, 0x42, 0xe4, 0x61, 0xa3, 0x37, 0xbd, + 0xa7, 0x79, 0xec, 0x98, 0x82, 0x69, 0x87, 0x68, 0xd9, 0xa6, 0xb3, 0xa8, + 0xc4, 0xb3, 0x6b, 0x60, 0x4c, 0x1b, 0x6a, 0x66, 0x36, 0x74, 0xc6, 0x5a, + 0x60, 0x38, 0xa2, 0xa9, 0x63, 0x22, 0x6d, 0x37, 0x04, 0xc9, 0x1a, 0x44, + 0x42, 0x4d, 0xaf, 0xdd, 0xf5, 0xd6, 0x4e, 0xe7, 0x11, 0xeb, 0x16, 0x5c, + 0x16, 0x1c, 0xb4, 0x0a, 0xeb, 0xea, 0x2f, 0xd3, 0xa3, 0x1e, 0x18, 0x08, + 0x3b, 0x34, 0x09, 0xe0, 0xab, 0xf0, 0x28, 0xf6, 0x6d, 0x76, 0x8b, 0x26, + 0x9f, 0xda, 0x55, 0xc6, 0x1f, 0x1d, 0x0b, 0xbd, 0xe9, 0x73, 0x77, 0x5e, + 0x4a, 0x43, 0x29, 0x84, 0x25, 0x4c, 0xee, 0x6a, 0x93, 0x7e, 0x30, 0x40, + 0x79, 0x95, 0xa8, 0x2b, 0x9e, 0xe2, 0xf1, 0xf0, 0x22, 0xe0, 0x09, 0xc6, + 0x9c, 0xf2, 0x45, 0xba, 0x04, 0x7e, 0x73, 0x72, 0xfc, 0x90, 0x81, 0x5a, + 0x3e, 0x6e, 0x6c, 0xed, 0x80, 0x33, 0x57, 0x9a, 0xab, 0x7b, 0x90, 0x4c, + 0x5e, 0x83, 0x03, 0x18, 0xf6, 0xfe, 0xe4, 0x85, 0x02, 0x90, 0x64, 0x62, + 0xb2, 0x02, 0xe9, 0xb4, 0x87, 0xbc, 0x2c, 0xda, 0xef, 0x52, 0x47, 0x5b, + 0x83, 0x37, 0x70, 0xac, 0x61, 0xf8, 0x82, 0x92, 0x21, 0xf9, 0x2b, 0x9b, + 0xe8, 0xf8, 0xd6, 0x77, 0x82, 0x26, 0x14, 0x6d, 0x7e, 0xc6, 0x15, 0x66, + 0xe7, 0x30, 0x96, 0x8f, 0xbd, 0xb7, 0xc8, 0x71, 0x5e, 0x84, 0x07, 0x58, + 0xae, 0xcd, 0xf5, 0xed, 0xd4, 0xec, 0x60, 0x87, 0xba, 0x18, 0x9d, 0x6e, + 0x59, 0x93, 0x48, 0x19, 0x03, 0x7a, 0xe9, 0xb4, 0x2d, 0xd6, 0x0f, 0xd4, + 0xa3, 0x38, 0x3b, 0xf2, 0x78, 0xcc, 0x86, 0x68, 0xb2, 0x3f, 0xb9, 0x64, + 0x6c, 0x38, 0xda, 0x08, 0xd8, 0x60, 0x3e, 0xc4, 0x16, 0x1a, 0x1e, 0x59, + 0x73, 0x4f, 0x6a, 0x76, 0x60, 0xbb, 0x35, 0x85, 0x22, 0x71, 0xd5, 0x23, + 0x54, 0x4f, 0xba, 0x6e, 0xbf, 0xab, 0xf0, 0x15, 0xb7, 0x79, 0x24, 0x50, + 0x4d, 0x7e, 0xe2, 0x84, 0x56, 0x58, 0xda, 0x68, 0x44, 0xec, 0x21, 0xe3, + 0x6f, 0xcb, 0x11, 0xc4, 0xd8, 0xeb, 0x0a, 0x0e, 0x31, 0x79, 0x32, 0x39, + 0xc3, 0xad, 0x33, 0xc7, 0x96, 0xaf, 0xb0, 0x12, 0x12, 0x30, 0xe3, 0xdd, + 0x23, 0x08, 0x23, 0x98, 0x31, 0xcd, 0x90, 0x7a, 0xfc, 0xc7, 0xe2, 0x8a, + 0xb4, 0x40, 0x26, 0x89, 0x92, 0xfa, 0x5e, 0xde, 0x7f, 0x2a, 0x67, 0xed, + 0xd4, 0x96, 0xc6, 0xed, 0x6d, 0xc3, 0x06, 0xed, 0xdb, 0xb3, 0x45, 0x85, + 0x85, 0xa9, 0x8a, 0x9a, 0xb1, 0xf5, 0xfa, 0x5d, 0xd6, 0xf4, 0x20, 0xee, + 0xb0, 0x26, 0x8c, 0xee, 0xb0, 0xee, 0x66, 0x82, 0xbd, 0x6f, 0xac, 0xe1, + 0x9d, 0xfa, 0x29, 0xe2, 0x2c, 0x2f, 0x42, 0x97, 0x9f, 0xa0, 0x0e, 0x36, + 0x0a, 0x3e, 0x06, 0x58, 0x74, 0x15, 0x86, 0x25, 0x35, 0x2f, 0x41, 0x1a, + 0x11, 0x2a, 0xdb, 0x1b, 0x02, 0x1c, 0xb0, 0xcb, 0x0b, 0x63, 0xf0, 0x95, + 0x8e, 0xd6, 0xe3, 0x35, 0x8b, 0xe3, 0xdb, 0xdf, 0xb0, 0xab, 0x14, 0xb1, + 0x33, 0xb4, 0x8e, 0xa0, 0xba, 0xc5, 0x71, 0x22, 0xc2, 0xa3, 0x4c, 0x4f, + 0x59, 0x9a, 0xfa, 0xe0, 0x53, 0x54, 0xb0, 0x5f, 0x05, 0xf6, 0x68, 0x3b, + 0x19, 0x56, 0xb5, 0x99, 0xa1, 0x01, 0x46, 0x3d, 0x14, 0x1b, 0x05, 0xa7, + 0xf6, 0xf1, 0xf8, 0xfe, 0x3b, 0x6e, 0x66, 0x78, 0xef, 0x52, 0xd9, 0x4e, + 0xc0, 0x3c, 0x90, 0x68, 0xe6, 0xdd, 0x21, 0x66, 0x3f, 0xb8, 0x29, 0x41, + 0xac, 0x08, 0x5e, 0xad, 0x8a, 0xf5, 0x6d, 0x55, 0x65, 0xa6, 0x8d, 0x6d, + 0x48, 0xa8, 0x25, 0x45, 0x7b, 0x45, 0xab, 0x9a, 0x8d, 0xd8, 0x41, 0x41, + 0x59, 0x37, 0xe7, 0x6b, 0x6a, 0xa4, 0xe5, 0xaa, 0x51, 0x69, 0x14, 0x5a, + 0x4e, 0x20, 0xb4, 0x65, 0x4d, 0xce, 0xa7, 0xa0, 0xae, 0x59, 0xfe, 0x69, + 0xe6, 0x13, 0x85, 0x3c, 0x34, 0x06, 0x5b, 0x72, 0xd9, 0xbf, 0x69, 0x12, + 0x90, 0x51, 0xa4, 0x54, 0x4c, 0x3d, 0xb9, 0xd6, 0x42, 0xb6, 0x6a, 0x15, + 0x27, 0x71, 0x21, 0x1a, 0xe9, 0xc4, 0xda, 0xef, 0xa7, 0x97, 0xa6, 0x1d, + 0x2e, 0x6d, 0xdf, 0xb1, 0x32, 0x24, 0xfa, 0xc6, 0xe9, 0x4b, 0x2f, 0xe0, + 0x1a, 0x93, 0x1b, 0x1e, 0xd6, 0x7e, 0x45, 0xf0, 0x4f, 0xb3, 0x56, 0xa3, + 0x6f, 0x56, 0x71, 0x40, 0x85, 0x77, 0xef, 0x15, 0xab, 0xb7, 0x10, 0x91, + 0x70, 0xc6, 0xe0, 0xc8, 0xf2, 0x5c, 0x0a, 0xbb, 0x33, 0x58, 0x0c, 0x91, + 0x0f, 0x64, 0x45, 0x2a, 0x72, 0x9a, 0xe1, 0xec, 0x07, 0xea, 0xf5, 0xf4, + 0xa8, 0xf5, 0x31, 0x8b, 0xc7, 0xb2, 0x1c, 0x2d, 0xd9, 0x6b, 0x5e, 0x75, + 0xb9, 0x9a, 0xeb, 0x6e, 0x0f, 0x53, 0x28, 0x3c, 0x71, 0xec, 0x04, 0x67, + 0xb2, 0x30, 0x67, 0x1a, 0x4c, 0xe0, 0xcf, 0x46, 0xc8, 0x9b, 0xb9, 0xdb, + 0xad, 0x3f, 0xaf, 0x68, 0x5e, 0xc1, 0x89, 0x1b, 0x7b, 0xb8, 0xc0, 0x04, + 0x6b, 0xf9, 0xc3, 0x58, 0x94, 0xb6, 0xd5, 0x5e, 0x4c, 0xd5, 0xe8, 0x8d, + 0xfe, 0x06, 0xfb, 0x38, 0x3c, 0xb8, 0x2d, 0xf0, 0x09, 0x23, 0x29, 0x4e, + 0x2b, 0x25, 0x0e, 0x74, 0xe8, 0xf3, 0x54, 0xb3, 0xfc, 0xf0, 0xe0, 0x89, + 0x3c, 0x6c, 0x9e, 0x9a, 0xd5, 0x44, 0xe7, 0xf0, 0x3a, 0x70, 0x84, 0x4f, + 0xcd, 0x2d, 0xa2, 0xfb, 0xc7, 0xb2, 0x38, 0x67, 0xb6, 0xc7, 0x55, 0x70, + 0x4c, 0x62, 0xf7, 0x39, 0x59, 0x18, 0x3d, 0x64, 0x44, 0x07, 0xb5, 0x34, + 0x24, 0x7f, 0x9d, 0x90, 0xed, 0xe0, 0x35, 0xb8, 0x76, 0xb0, 0xcc, 0xe6, + 0x2a, 0x50, 0x54, 0x91, 0xaa, 0x02, 0x2c, 0xf6, 0x3f, 0x81, 0x69, 0x41, + 0xd1, 0x5e, 0xb2, 0x94, 0x03, 0x37, 0x07, 0xde, 0x0c, 0x41, 0xf9, 0xbc, + 0x88, 0xcd, 0x0b, 0x1a, 0x11, 0x2c, 0x1d, 0x7c, 0x26, 0xce, 0xc0, 0x99, + 0xd1, 0xd4, 0xca, 0x0a, 0xe8, 0xbf, 0xb9, 0xcb, 0x27, 0x43, 0x40, 0xf7, + 0x34, 0xe6, 0x3c, 0x1b, 0x80, 0xe9, 0x67, 0x77, 0x7e, 0x1d, 0x67, 0xcf, + 0x3f, 0xaf, 0xea, 0x49, 0xf6, 0x8d, 0x33, 0x4f, 0x89, 0xb1, 0x30, 0x6c, + 0x17, 0x0d, 0xa2, 0x8a, 0x16, 0x7e, 0x16, 0x54, 0xcb, 0xef, 0x6c, 0x89, + 0x6b, 0xef, 0xaa, 0x3e, 0x10, 0x46, 0xbc, 0xd5, 0xca, 0xbe, 0xd7, 0x9f, + 0x9a, 0x61, 0xc7, 0x07, 0xfb, 0x2f, 0x3e, 0x94, 0x88, 0x20, 0x97, 0x44, + 0x1b, 0xc3, 0xb4, 0x7a, 0x2b, 0x09, 0xa4, 0x65, 0xb6, 0xe6, 0x0f, 0x7e, + 0x1f, 0x08, 0x85, 0x29, 0x48, 0xb3, 0x7c, 0xfb, 0xd9, 0x8c, 0x13, 0x7e, + 0x8e, 0x51, 0x35, 0xfa, 0x41, 0x8e, 0x4b, 0xab, 0xfe, 0xd8, 0x0c, 0x1c, + 0x2f, 0x38, 0xa4, 0x2e, 0x80, 0x75, 0x85, 0x71, 0xf4, 0x7b, 0xea, 0x12, + 0xe1, 0x7d, 0xce, 0xe9, 0xe4, 0x74, 0xf3, 0x0b, 0xea, 0x77, 0xb8, 0xdf, + 0xf5, 0xaa, 0x90, 0xa1, 0x51, 0xf3, 0x02, 0x8c, 0xb8, 0x40, 0xd4, 0xdd, + 0x87, 0xc3, 0xbd, 0x37, 0x95, 0x2d, 0xd0, 0xa2, 0x5b, 0x00, 0x14, 0xe3, + 0x9b, 0xea, 0x8e, 0xa3, 0xf2, 0xc2, 0x80, 0x0f, 0x8d, 0x1e, 0xc8, 0x4a, + 0x6b, 0x92, 0x21, 0x71, 0x35, 0x84, 0x84, 0x8b, 0x6d, 0x1a, 0x0e, 0xbb, + 0xf9, 0x0f, 0xa2, 0xcf, 0xcf, 0x16, 0x29, 0x38, 0x81, 0xd8, 0x55, 0x10, + 0x2f, 0x29, 0x4d, 0x4a, 0x31, 0x09, 0x60, 0xf9, 0xcd, 0x47, 0xaa, 0x38, + 0x2b, 0x99, 0xf1, 0x3a, 0x34, 0xc4, 0xaf, 0xa9, 0x9f, 0x02, 0xa3, 0xe1, + 0xb8, 0x79, 0x51, 0x49, 0x10, 0xab, 0x8b, 0x1b, 0x2d, 0x7a, 0xa5, 0xd9, + 0x27, 0x8b, 0x26, 0xee, 0x4e, 0xa4, 0xdd, 0x55, 0xe8, 0x62, 0x26, 0x1a, + 0xfc, 0x3e, 0xf0, 0xcf, 0x04, 0x19, 0xdb, 0xfb, 0xc6, 0xc3, 0x62, 0xbc, + 0x24, 0x75, 0xde, 0xc1, 0xd3, 0xc1, 0xe5, 0xd0, 0x23, 0x37, 0xa1, 0xad, + 0x85, 0x41, 0xf3, 0x30, 0x4f, 0xc7, 0x7e, 0xc3, 0x2b, 0xa5, 0xd6, 0x3d, + 0xc4, 0x33, 0xf9, 0x8e, 0xa7, 0xbb, 0x73, 0xaa, 0x2f, 0x0c, 0xa5, 0x74, + 0x3d, 0xe7, 0x88, 0x04, 0x3b, 0x8a, 0xc0, 0x3a, 0xd4, 0x2a, 0x56, 0xd5, + 0x43, 0x3d, 0x4f, 0x7a, 0x0d, 0xb7, 0x44, 0xc9, 0x48, 0xf0, 0x65, 0x21, + 0x94, 0xc8, 0xc8, 0x38, 0x05, 0x7d, 0x55, 0x8e, 0x5b, 0x6f, 0xe8, 0x7f, + 0x1a, 0xf1, 0xc6, 0xd9, 0x3c, 0x63, 0xfd, 0x29, 0xfe, 0x9d, 0xa2, 0xe6, + 0x33, 0x6e, 0xf7, 0x10, 0x14, 0xbc, 0x26, 0x0c, 0xac, 0x22, 0x6f, 0x50, + 0x21, 0x0e, 0x98, 0x43, 0xf7, 0x8a, 0x55, 0xf0, 0x9f, 0xc0, 0xcc, 0xaf, + 0xc5, 0x44, 0xb5, 0xe1, 0x86, 0x8a, 0x62, 0x5e, 0x13, 0x02, 0x1e, 0xd9, + 0xa6, 0xe4, 0x91, 0xea, 0x66, 0xd3, 0x27, 0x46, 0x26, 0x30, 0x12, 0x63, + 0x27, 0x61, 0xd0, 0xab, 0x49, 0x31, 0x92, 0xc8, 0x32, 0xd8, 0x8f, 0x30, + 0x29, 0x33, 0xbb, 0xe2, 0x33, 0xc5, 0x48, 0xc4, 0x3d, 0x4e, 0x44, 0x06, + 0xc7, 0xb8, 0x15, 0x24, 0x12, 0x90, 0x75, 0x63, 0x50, 0xc0, 0x06, 0x7c, + 0x32, 0x14, 0xd6, 0x95, 0xf6, 0x8d, 0x62, 0x2c, 0x54, 0xdb, 0x5d, 0x8c, + 0x36, 0x5e, 0x21, 0x9a, 0xa5, 0xee, 0xc9, 0x2e, 0x75, 0xe9, 0xd1, 0x8b, + 0xc7, 0xb4, 0x52, 0xa0, 0xd1, 0x16, 0xfb, 0x02, 0xd3, 0x35, 0xc7, 0x04, + 0xd5, 0xe3, 0x87, 0x10, 0xd1, 0x0a, 0x38, 0xd5, 0x3d, 0xa1, 0x0c, 0x6d, + 0x56, 0x95, 0xb7, 0xa2, 0x7a, 0x4b, 0x8e, 0x0a, 0x27, 0x9a, 0x0d, 0x01, + 0x72, 0xf7, 0xc2, 0xab, 0x3b, 0x4b, 0xd1, 0x30, 0x71, 0xb3, 0x5f, 0xaa, + 0xf0, 0x87, 0x45, 0x8d, 0xc2, 0xa9, 0x97, 0x03, 0x47, 0x0e, 0xbe, 0xcb, + 0x15, 0x1a, 0x00, 0xa9, 0x1b, 0xf2, 0x16, 0x27, 0x4a, 0x61, 0x87, 0xde, + 0xc4, 0xe6, 0x49, 0x39, 0xec, 0xc7, 0xcb, 0x5a, 0x65, 0x0c, 0x3c, 0xd4, + 0x3f, 0xca, 0x8d, 0xe7, 0x66, 0xe3, 0x93, 0xff, 0xf1, 0xb7, 0x32, 0x54, + 0x1b, 0xc4, 0xc0, 0xcc, 0xc4, 0xca, 0x15, 0x04, 0x2e, 0xf9, 0x12, 0x70, + 0x75, 0x4b, 0xad, 0x22, 0x3c, 0xaf, 0x65, 0x8d, 0xfe, 0x0a, 0x5b, 0x00, + 0x01, 0x07, 0x32, 0xf1, 0x0c, 0xb2, 0x60, 0x24, 0x68, 0xa8, 0x4e, 0xb6, + 0xd0, 0xf5, 0x36, 0x4d, 0xb1, 0xea, 0xdf, 0x8d, 0x0a, 0x71, 0x61, 0x65, + 0x40, 0x25, 0x37, 0x7e, 0x4d, 0x2e, 0x6e, 0x56, 0x71, 0x1c, 0x07, 0xb7, + 0x6f, 0x02, 0x98, 0x33, 0x50, 0x96, 0xba, 0xf4, 0xb9, 0xc0, 0xa3, 0xeb, + 0xbf, 0x71, 0x1e, 0x81, 0x8d, 0x94, 0x58, 0x31, 0xb8, 0x81, 0x26, 0xc7, + 0x59, 0x43, 0xf6, 0x08, 0xb0, 0xbd, 0xae, 0xc4, 0x44, 0xaf, 0x35, 0x76, + 0xf7, 0xfb, 0x1f, 0x29, 0xe6, 0x64, 0xe6, 0x95, 0x21, 0x53, 0xf2, 0x68, + 0x21, 0x41, 0x51, 0x1f, 0x16, 0xfc, 0xeb, 0x7d, 0x29, 0xef, 0xd1, 0xc5, + 0x2c, 0xd6, 0x79, 0xb8, 0xbf, 0x7e, 0xbc, 0x5c, 0x81, 0x97, 0x3b, 0x9e, + 0xb3, 0x9e, 0x9a, 0xf1, 0x93, 0x4f, 0x07, 0x73, 0x64, 0x63, 0x8b, 0x72, + 0x3a, 0xcc, 0x71, 0x2c, 0xfa, 0x09, 0x74, 0x5a, 0x66, 0xaa, 0xb4, 0x3e, + 0x4d, 0xf2, 0x3c, 0x8a, 0x0d, 0xfe, 0x40, 0x72, 0x23, 0x09, 0x7f, 0x40, + 0xd3, 0xc3, 0x1a, 0x14, 0x52, 0x44, 0x3a, 0x48, 0x55, 0x14, 0xef, 0x67, + 0xdd, 0x17, 0x58, 0x50, 0x20, 0x2e, 0x1f, 0x79, 0x83, 0x19, 0x87, 0x5c, + 0x19, 0xc6, 0x3f, 0x12, 0x96, 0xaa, 0x92, 0x19, 0xf3, 0x98, 0xfb, 0x1e, + 0x0d, 0x09, 0xad, 0x89, 0x11, 0x43, 0xda, 0x0e, 0xd7, 0xc4, 0x7e, 0xdc, + 0xf3, 0x22, 0xaf, 0x29, 0xba, 0x08, 0x10, 0xb3, 0xdc, 0x21, 0x7d, 0x7b, + 0x9d, 0xce, 0xeb, 0xc7, 0xbb, 0x66, 0xcb, 0xf0, 0xf0, 0x22, 0x9b, 0x71, + 0x76, 0xbe, 0x6c, 0x42, 0xfa, 0x6f, 0xcc, 0xd3, 0xc6, 0xdb, 0xd3, 0xc0, + 0xac, 0x88, 0xa9, 0xf3, 0x9f, 0x2c, 0x8e, 0xf0, 0xb2, 0x64, 0xe7, 0x0c, + 0x62, 0x5b, 0x57, 0x0a, 0x67, 0x39, 0x3a, 0x8e, 0x14, 0xbd, 0x06, 0x32, + 0x04, 0xcd, 0xb5, 0x46, 0x66, 0x8e, 0xd3, 0x32, 0x44, 0x83, 0x0f, 0xf4, + 0x3c, 0x34, 0x68, 0x14, 0x4e, 0x14, 0x98, 0x72, 0x11, 0xc6, 0xbf, 0x1b, + 0xdf, 0x7e, 0x86, 0x75, 0x8c, 0x34, 0xa6, 0x87, 0x2a, 0x9b, 0x9e, 0xc5, + 0x95, 0x5f, 0x2e, 0x1a, 0x66, 0x25, 0xa4, 0x1e, 0x19, 0x64, 0x63, 0xdb, + 0x4a, 0x5d, 0x98, 0x07, 0x40, 0x43, 0x86, 0x7d, 0x80, 0x44, 0x5d, 0x31, + 0x55, 0xb1, 0xa6, 0x0e, 0x4f, 0xe3, 0xa6, 0x69, 0xa8, 0xfc, 0x64, 0x4f, + 0xd2, 0x04, 0xae, 0x59, 0x51, 0x46, 0xe4, 0xb9, 0x36, 0xd0, 0x5d, 0xa4, + 0x22, 0xcf, 0x63, 0x33, 0x76, 0xd1, 0x0f, 0xff, 0x20, 0x62, 0x8d, 0x7b, + 0xac, 0xe7, 0x3d, 0x19, 0xae, 0x23, 0x28, 0xdb, 0x3c, 0xc6, 0x08, 0xf4, + 0xdd, 0x20, 0x94, 0x53, 0x29, 0x56, 0x0b, 0x83, 0x8c, 0x93, 0xcb, 0xb2, + 0x42, 0xa1, 0xd6, 0x1d, 0x4e, 0xdb, 0xbe, 0x6c, 0x73, 0xc3, 0x45, 0x76, + 0x98, 0x75, 0x8a, 0xc8, 0xd4, 0xc4, 0xd1, 0x5f, 0x63, 0x5f, 0xd9, 0xd7, + 0x74, 0x90, 0x6c, 0xf0, 0xc7, 0x54, 0xfd, 0xa4, 0x70, 0x6d, 0x25, 0xa4, + 0xec, 0x6f, 0xc6, 0xba, 0x38, 0xd3, 0x7e, 0x71, 0x87, 0xc1, 0x0b, 0x79, + 0xa2, 0x54, 0x95, 0x24, 0x27, 0x5f, 0x90, 0x23, 0x42, 0xac, 0x2a, 0x8b, + 0xa6, 0x43, 0xcf, 0x9d, 0xb9, 0x17, 0xe6, 0x4b, 0x44, 0x56, 0x33, 0x11, + 0x6e, 0xf1, 0x69, 0x67, 0xc8, 0xbd, 0x38, 0x33, 0x99, 0xca, 0xdd, 0x6b, + 0x13, 0xf8, 0x33, 0xdd, 0xd6, 0xb0, 0xfc, 0x85, 0x3c, 0x65, 0xcf, 0x42, + 0x64, 0xc9, 0x72, 0x75, 0x45, 0x0e, 0x23, 0xaa, 0xfd, 0xcc, 0x89, 0x1f, + 0x43, 0x74, 0x87, 0x74, 0xb3, 0x0b, 0xc8, 0x69, 0xb3, 0xb9, 0xdd, 0x8c, + 0x0b, 0x00, 0x42, 0xed, 0xd7, 0xcf, 0x7e, 0x70, 0x50, 0xaf, 0xea, 0x21, + 0x6d, 0xca, 0x9e, 0xb7, 0xce, 0x66, 0x6b, 0x1c, 0xbe, 0xb4, 0xbd, 0xdd, + 0x6b, 0x61, 0x71, 0xf5, 0xa2, 0x77, 0x00, 0xc0, 0xb6, 0x5c, 0x7c, 0xe4, + 0x5f, 0x68, 0x6a, 0x46, 0xa2, 0xb7, 0x24, 0x40, 0x3d, 0xd9, 0x50, 0xf6, + 0x8d, 0x5a, 0x2a, 0x39, 0xe4, 0xa2, 0xdb, 0x90, 0xb9, 0xd0, 0x81, 0x52, + 0x70, 0xfd, 0x78, 0xb4, 0x27, 0x08, 0x98, 0x2e, 0x84, 0x81, 0xdb, 0xfb, + 0xa9, 0x64, 0x1d, 0xbc, 0xf4, 0x9e, 0xfd, 0x1d, 0xd0, 0x50, 0x1d, 0x46, + 0x4a, 0x1d, 0x45, 0x22, 0xc8, 0x79, 0x41, 0x34, 0xcb, 0xfe, 0x1c, 0xc8, + 0xa6, 0x99, 0xa1, 0x04, 0x2a, 0x31, 0x68, 0xaf, 0xa4, 0x74, 0x43, 0xb1, + 0x84, 0xb0, 0x82, 0xf6, 0x08, 0x2c, 0x1b, 0x55, 0x09, 0x93, 0x3b, 0xaa, + 0xf8, 0x9d, 0x57, 0x0b, 0x4d, 0xa3, 0x93, 0x78, 0xf6, 0x90, 0x69, 0x7a, + 0x1e, 0x9c, 0x3d, 0x25, 0xcc, 0x90, 0x68, 0xe6, 0x4a, 0xbf, 0xfa, 0xa3, + 0x83, 0x3b, 0x30, 0x56, 0x3c, 0xb6, 0x88, 0x78, 0x98, 0x6c, 0x92, 0xde, + 0x37, 0x26, 0x4f, 0x83, 0x5c, 0xff, 0xdb, 0x69, 0x62, 0x68, 0x4f, 0x1e, + 0x35, 0x8d, 0x12, 0xcf, 0x3e, 0xf7, 0x15, 0x49, 0xc9, 0x88, 0xf2, 0x21, + 0xe1, 0xb7, 0x07, 0xde, 0xb2, 0x5e, 0xa3, 0xa8, 0x93, 0x42, 0x54, 0x7f, + 0x85, 0x07, 0xf4, 0xd3, 0xf7, 0x29, 0x6c, 0x98, 0x45, 0x9c, 0x8f, 0x24, + 0xb9, 0xf8, 0x4a, 0x73, 0x28, 0x3b, 0x8d, 0x92, 0xa3, 0xe2, 0xb0, 0xf9, + 0x65, 0xf6, 0x1f, 0x47, 0xfc, 0x38, 0xfc, 0x7c, 0xea, 0x90, 0xb7, 0xdb, + 0x3b, 0xc7, 0xdd, 0xf7, 0xd0, 0xec, 0xf0, 0xaa, 0x46, 0xf4, 0x83, 0x9c, + 0x54, 0x4b, 0x03, 0xb0, 0x46, 0xd4, 0x71, 0x1e, 0x93, 0x83, 0x41, 0x95, + 0x56, 0x2f, 0x12, 0xde, 0xc3, 0x2e, 0x53, 0xd6, 0x3f, 0x3c, 0xd0, 0x5f, + 0x4f, 0x27, 0x64, 0xdc, 0x6b, 0xd8, 0x8d, 0xb2, 0x2d, 0x32, 0x55, 0xa5, + 0x20, 0x7a, 0x80, 0xc8, 0xca, 0xbe, 0xc1, 0x8c, 0xb3, 0x6d, 0x2a, 0x4e, + 0x94, 0x61, 0x70, 0xcd, 0x3c, 0x4a, 0xbb, 0x6b, 0x5c, 0x00, 0x91, 0xac, + 0xc1, 0x06, 0x0d, 0x87, 0x33, 0x14, 0xe9, 0xd2, 0x91, 0x5b, 0xfa, 0x7e, + 0x3b, 0xc3, 0x44, 0x66, 0x1c, 0xe9, 0x68, 0x5a, 0x43, 0x3a, 0x27, 0xa6, + 0x47, 0x45, 0x0b, 0x59, 0x97, 0xeb, 0xf9, 0x80, 0x0e, 0x93, 0xd0, 0x92, + 0x0a, 0xfc, 0x87, 0x25, 0x91, 0x5d, 0x2a, 0x28, 0x0c, 0x39, 0x19, 0x60, + 0x14, 0x30, 0x6b, 0x01, 0xe6, 0x5b, 0xe1, 0x03, 0xae, 0xe3, 0xbc, 0x6c, + 0xe5, 0x56, 0x1a, 0x0b, 0x50, 0x25, 0x62, 0x0d, 0x8e, 0x2a, 0x00, 0xc8, + 0x57, 0x82, 0x4c, 0xbd, 0x9e, 0x76, 0xef, 0x97, 0xcd, 0xec, 0xe6, 0xa5, + 0x66, 0x5c, 0xd8, 0xb8, 0xc4, 0xad, 0x78, 0xf6, 0x49, 0x16, 0x88, 0x20, + 0x9e, 0xec, 0x2b, 0x2d, 0x3b, 0x2f, 0xd4, 0x32, 0x21, 0xba, 0x19, 0x37, + 0xfe, 0x20, 0x52, 0x09, 0xec, 0x7d, 0x1d, 0x57, 0x19, 0x53, 0xa7, 0x60, + 0x24, 0x07, 0x1a, 0x14, 0xff, 0xed, 0x25, 0xe4, 0x3d, 0x45, 0x53, 0x3c, + 0xe7, 0x16, 0x4f, 0xd7, 0x6a, 0xcd, 0x14, 0x7a, 0xe0, 0xda, 0xbf, 0x66, + 0x06, 0xae, 0x29, 0x29, 0x08, 0x40, 0x8f, 0xf0, 0x38, 0x05, 0xf3, 0xba, + 0x90, 0xe9, 0x0f, 0x38, 0x6f, 0xe3, 0x08, 0xc2, 0x37, 0x3f, 0x8c, 0x7a, + 0x15, 0x8c, 0xb4, 0x5f, 0xdf, 0xb3, 0x0f, 0x14, 0xa3, 0x28, 0x12, 0xac, + 0xe0, 0x9c, 0x81, 0xf1, 0x0d, 0xba, 0x5e, 0xe3, 0x45, 0x9c, 0xdd, 0xc0, + 0x87, 0x7b, 0x4e, 0xd4, 0x67, 0x78, 0xc7, 0x9e, 0x3a, 0x6c, 0x3c, 0x7e, + 0xac, 0x5f, 0xbd, 0x4a, 0x4f, 0xb9, 0xf7, 0xeb, 0xa6, 0x38, 0x1f, 0x76, + 0x25, 0x82, 0x97, 0x41, 0x23, 0xd8, 0x58, 0x52, 0xe2, 0xba, 0x06, 0x70, + 0xf5, 0xe9, 0x7e, 0x7c, 0xf7, 0xf0, 0xb4, 0x7e, 0x81, 0xec, 0xe4, 0xbc, + 0xc7, 0x55, 0x57, 0x63, 0x0d, 0x22, 0x43, 0x82, 0x9b, 0x67, 0x76, 0x77, + 0xb6, 0xca, 0xd8, 0xce, 0x07, 0x33, 0xb9, 0x32, 0x59, 0x61, 0x52, 0x8e, + 0x25, 0x42, 0xbe, 0x91, 0x65, 0x50, 0x93, 0x39, 0xca, 0x94, 0x6a, 0x76, + 0x09, 0x54, 0xfc, 0x46, 0x47, 0x1d, 0x9c, 0xe3, 0x62, 0xfc, 0x5e, 0x65, + 0x5c, 0x89, 0xbd, 0x4d, 0x23, 0xd5, 0x5d, 0x04, 0x29, 0xdd, 0x27, 0xf6, + 0xd8, 0x38, 0x9e, 0xbc, 0xc3, 0xc4, 0xdf, 0xa9, 0xa1, 0xc0, 0x96, 0x1f, + 0x8e, 0xa7, 0xdf, 0xe9, 0x97, 0x2f, 0xe8, 0xca, 0xaa, 0x5e, 0x99, 0x45, + 0x4a, 0xed, 0x09, 0xde, 0x47, 0xfc, 0xbe, 0x4d, 0xda, 0xd8, 0xe3, 0x08, + 0xd8, 0x87, 0xbd, 0x66, 0xc1, 0xa7, 0x1c, 0xf9, 0x2f, 0x60, 0x21, 0x53, + 0xbb, 0xed, 0x59, 0xc2, 0x0c, 0x4d, 0x6e, 0xe8, 0xa4, 0x0a, 0x78, 0x1b, + 0xc3, 0x79, 0x9c, 0xd0, 0xc4, 0xe5, 0x02, 0x5e, 0x7d, 0xad, 0x10, 0x5d, + 0x92, 0x7c, 0x5c, 0xac, 0x3a, 0x7e, 0x89, 0xb4, 0xf3, 0xc2, 0xe6, 0x4f, + 0x10, 0x25, 0x27, 0xa1, 0xb0, 0xd8, 0x72, 0xb8, 0x05, 0x52, 0x47, 0x8c, + 0xa6, 0x16, 0xfe, 0x33, 0x4c, 0x68, 0x73, 0xc0, 0xee, 0xdc, 0x65, 0xd9, + 0xd9, 0xc1, 0x28, 0x9b, 0x77, 0xa1, 0x95, 0x3b, 0xe5, 0x59, 0xa8, 0xbf, + 0x32, 0xce, 0xbb, 0x24, 0x57, 0xb8, 0x2e, 0x3b, 0x59, 0x96, 0x4e, 0xdd, + 0x3a, 0xa8, 0x5a, 0xd3, 0xbf, 0xa1, 0x5a, 0x30, 0x81, 0x91, 0xac, 0xe5, + 0x7b, 0x0d, 0xd7, 0xb7, 0x61, 0x5b, 0xf5, 0x8c, 0xbf, 0x64, 0xb5, 0x5f, + 0x72, 0x4c, 0xfe, 0x56, 0x17, 0xd0, 0x3a, 0xdc, 0x5d, 0x3d, 0x92, 0x4f, + 0xa7, 0x27, 0xe2, 0x68, 0x50, 0x40, 0x19, 0x78, 0x0e, 0x71, 0x55, 0x11, + 0xc6, 0xab, 0x56, 0x8f, 0x04, 0x25, 0x6b, 0x81, 0xb7, 0xf9, 0x6f, 0x4e, + 0x36, 0x74, 0xb1, 0xc8, 0xff, 0xdc, 0x0b, 0x42, 0x34, 0x55, 0x68, 0x46, + 0xe0, 0xf0, 0xf8, 0x08, 0x4a, 0x9a, 0x7b, 0xc4, 0xff, 0x70, 0xf5, 0xc3, + 0x9e, 0xa7, 0x69, 0x51, 0x62, 0x83, 0x0f, 0xbe, 0x35, 0x4a, 0x7d, 0x8c, + 0x8f, 0xeb, 0xf8, 0xe5, 0x30, 0x35, 0x68, 0xb8, 0x5f, 0xa8, 0xbd, 0xa4, + 0x40, 0xd3, 0x2e, 0x83, 0xe2, 0x43, 0xdd, 0xed, 0xf2, 0x79, 0x96, 0x34, + 0x0b, 0xfb, 0x30, 0x9e, 0xe8, 0xe4, 0x06, 0x5b, 0x36, 0xc3, 0x5d, 0x21, + 0xd3, 0xec, 0xe4, 0xf5, 0xab, 0x32, 0xa5, 0x6a, 0x38, 0xc0, 0x22, 0x46, + 0x99, 0xd8, 0xd8, 0x5a, 0x01, 0x37, 0x82, 0x2d, 0x9f, 0x12, 0xee, 0x44, + 0xa9, 0xaf, 0x37, 0x84, 0x4d, 0x63, 0x5d, 0xbc, 0x31, 0x0b, 0x12, 0xee, + 0xfd, 0x6e, 0x41, 0x19, 0x91, 0x01, 0xee, 0x30, 0x27, 0xbe, 0x92, 0xae, + 0x57, 0xff, 0x3d, 0xf6, 0xf7, 0x50, 0x24, 0xed, 0x8a, 0xf2, 0x4c, 0xa3, + 0x4c, 0x12, 0xfe, 0x81, 0x8a, 0xd6, 0x6a, 0x3a, 0x57, 0x71, 0x21, 0x01, + 0xcb, 0xf5, 0xf3, 0x3a, 0x15, 0x58, 0xfb, 0x57, 0x31, 0x1d, 0xed, 0x36, + 0xf4, 0x1f, 0x49, 0xb0, 0x45, 0xd5, 0xd0, 0xc5, 0x79, 0x6c, 0xad, 0x2f, + 0x7c, 0x05, 0x32, 0x27, 0xe6, 0xe6, 0x5d, 0x9c, 0xcd, 0x7e, 0x8a, 0x4e, + 0xc4, 0xe7, 0xfe, 0x5d, 0x70, 0x9e, 0x10, 0x20, 0xda, 0x5f, 0x37, 0x20, + 0xab, 0x5a, 0x61, 0xb5, 0xbe, 0x29, 0xd1, 0xc5, 0x85, 0xff, 0x0b, 0x6a, + 0x16, 0x02, 0xf6, 0x14, 0xbc, 0xe2, 0xa8, 0x32, 0xe6, 0xc2, 0xd3, 0x5b, + 0x1c, 0x33, 0x5c, 0x5a, 0x51, 0x89, 0x17, 0x35, 0x21, 0x1b, 0x20, 0xd8, + 0x0d, 0xb7, 0x4c, 0xdd, 0xaa, 0x17, 0xec, 0xb0, 0xad, 0x87, 0x5e, 0x06, + 0x8a, 0x8c, 0xa8, 0x50, 0x73, 0x65, 0x18, 0xfc, 0xfe, 0x77, 0xa6, 0xcc, + 0x15, 0xbd, 0xfb, 0x76, 0x14, 0x82, 0xb9, 0xab, 0xc1, 0x04, 0xd5, 0x56, + 0x0e, 0xf0, 0xb7, 0xb0, 0x45, 0xa8, 0x0b, 0xa1, 0xcb, 0x12, 0xb9, 0xe8, + 0x35, 0xf9, 0x17, 0xd9, 0x70, 0xc3, 0x9c, 0x50, 0x8e, 0x90, 0x97, 0xcf, + 0xda, 0x50, 0xfd, 0x8e, 0x72, 0xc2, 0x3f, 0x93, 0xfc, 0xe7, 0xdd, 0x79, + 0x6e, 0x23, 0x6e, 0x54, 0xca, 0x8b, 0xc1, 0xb8, 0x05, 0xe8, 0x4c, 0x6c, + 0xe2, 0xbe, 0xf1, 0xa5, 0xb5, 0x25, 0x8f, 0x82, 0xe9, 0xca, 0x0b, 0xc8, + 0x4f, 0xc2, 0xe5, 0xd2, 0x5c, 0xa3, 0xba, 0xbe, 0x08, 0x23, 0xe6, 0x25, + 0x2f, 0x55, 0x29, 0x9c, 0x68, 0x06, 0x93, 0x94, 0xfb, 0xf8, 0xce, 0xce, + 0x72, 0xb9, 0x6c, 0xe2, 0x4a, 0x97, 0xcf, 0xbb, 0xc9, 0xa8, 0x30, 0x0b, + 0x07, 0x98, 0x79, 0xed, 0x27, 0x48, 0x19, 0x19, 0xe3, 0x22, 0xc9, 0x61, + 0x12, 0x2d, 0xbf, 0x89, 0xe5, 0xc1, 0x09, 0xd7, 0xd6, 0x16, 0x38, 0xa1, + 0x7b, 0x23, 0xa4, 0xc7, 0x44, 0x29, 0x90, 0xdf, 0xd9, 0xbb, 0x1e, 0x82, + 0xb7, 0x99, 0x13, 0x39, 0x10, 0x2b, 0x21, 0xd5, 0xd1, 0xd0, 0x89, 0x5d, + 0xac, 0x3e, 0x24, 0xa0, 0xe8, 0xfc, 0xbe, 0xf2, 0x4b, 0x6e, 0x1a, 0x81, + 0x16, 0x3e, 0xc9, 0xf4, 0xdb, 0xa3, 0xe7, 0x71, 0x9e, 0x22, 0xec, 0xa7, + 0x9c, 0x25, 0x07, 0x9e, 0xfa, 0x4a, 0x4b, 0xf1, 0xcb, 0x49, 0x11, 0xb2, + 0xd2, 0x30, 0x94, 0x78, 0x0f, 0x3c, 0x02, 0x8f, 0x26, 0x77, 0xd4, 0x38, + 0x4b, 0x69, 0x9f, 0xc8, 0xb3, 0xf3, 0xd5, 0x62, 0x43, 0xde, 0xdf, 0x07, + 0x5d, 0x9f, 0x7a, 0x19, 0xab, 0x1f, 0xe9, 0xe8, 0x27, 0x6d, 0xad, 0x9f, + 0x06, 0x89, 0x68, 0x47, 0x59, 0xfd, 0xa1, 0xdb, 0x82, 0x7c, 0xad, 0x63, + 0x0d, 0x18, 0x71, 0x72, 0x31, 0x54, 0xf1, 0x5b, 0x16, 0xcd, 0xb0, 0xc9, + 0xde, 0x63, 0xa3, 0xdc, 0x96, 0xfe, 0x66, 0x9a, 0x98, 0xc0, 0xe1, 0x00, + 0x3f, 0x59, 0x4c, 0xdb, 0xbc, 0x55, 0xf8, 0x25, 0x3d, 0x63, 0xcf, 0x99, + 0x9b, 0x1f, 0xce, 0xba, 0x5c, 0xaa, 0xc3, 0x11, 0x0d, 0xdf, 0x1c, 0xfe, + 0x3e, 0x57, 0x08, 0x95, 0xba, 0x02, 0xdf, 0x76, 0x67, 0xe7, 0xe9, 0xeb, + 0xaf, 0x97, 0xb7, 0x22, 0xd2, 0x86, 0x02, 0x3a, 0x74, 0x35, 0x3b, 0xb8, + 0x62, 0xb4, 0xad, 0xd7, 0xf7, 0xcb, 0xca, 0x98, 0xb5, 0xc8, 0x49, 0xfe, + 0x6e, 0xb4, 0x94, 0x34, 0x3e, 0xd7, 0xbc, 0xfe, 0x73, 0x8a, 0xf3, 0x92, + 0x6d, 0x58, 0x43, 0x8e, 0xbd, 0x88, 0xfc, 0xab, 0x65, 0x9f, 0x42, 0x6c, + 0x3f, 0xda, 0x0d, 0x9d, 0x0c, 0x3a, 0x9a, 0xb5, 0xc4, 0x45, 0x6f, 0x8e, + 0x89, 0xff, 0x55, 0xe7, 0xe6, 0x03, 0x48, 0x94, 0x79, 0xf3, 0xd4, 0x24, + 0x0b, 0x71, 0x87, 0x21, 0x08, 0x73, 0x8a, 0xac, 0xe2, 0x9b, 0x3f, 0xc8, + 0xd0, 0x1d, 0xef, 0xdc, 0x05, 0x9a, 0xe1, 0x24, 0x70, 0x35, 0x54, 0x1d, + 0x8d, 0x67, 0x5c, 0x9f, 0x43, 0x29, 0x0d, 0xd8, 0x6a, 0x6d, 0xb4, 0xce, + 0x8e, 0xe5, 0x12, 0x4b, 0x10, 0x98, 0x15, 0xdb, 0x3f, 0x16, 0x86, 0xfb, + 0x83, 0x10, 0x78, 0x3e, 0x76, 0xc7, 0x24, 0x6f, 0xca, 0x7c, 0xf4, 0x0d, + 0xf4, 0xb2, 0xab, 0x64, 0x76, 0xbf, 0x53, 0xed, 0x1a, 0x43, 0xbb, 0x01, + 0x23, 0xef, 0x97, 0xea, 0xa9, 0xb9, 0xda, 0xa7, 0x43, 0x71, 0x86, 0x0c, + 0xe4, 0x30, 0xc8, 0x41, 0x6b, 0xf1, 0x3a, 0xba, 0x98, 0x90, 0x95, 0xf7, + 0x38, 0xe3, 0xe3, 0x81, 0x22, 0x3d, 0x1a, 0x1a, 0x23, 0xb1, 0x8d, 0xc6, + 0xa5, 0xaf, 0x4e, 0x9f, 0xf1, 0x41, 0x5f, 0x8e, 0xbf, 0xe6, 0xa9, 0xb9, + 0x19, 0xba, 0x95, 0x0d, 0xe2, 0xef, 0xf3, 0x85, 0x10, 0xa2, 0x7d, 0x39, + 0xd9, 0x97, 0x72, 0xd1, 0x3d, 0x2a, 0x41, 0xd3, 0xd4, 0x97, 0xfe, 0x79, + 0x76, 0x60, 0xb7, 0xa1, 0xcc, 0x1c, 0x50, 0xa7, 0xcb, 0x17, 0xe0, 0x89, + 0x5e, 0xf1, 0xb3, 0x7c, 0x09, 0x43, 0xd2, 0xe0, 0x9f, 0xaf, 0x15, 0xad, + 0x28, 0x6b, 0x81, 0x06, 0x65, 0x21, 0x26, 0x1a, 0xac, 0xbe, 0x00, 0x17, + 0x4a, 0x06, 0x89, 0x0e, 0x67, 0x6a, 0x53, 0x13, 0x13, 0xc6, 0x66, 0xe6, + 0x3e, 0xe4, 0x90, 0xef, 0xf2, 0xdf, 0xcc, 0xf9, 0x6e, 0x2c, 0xf9, 0x80, + 0xc5, 0x84, 0xd0, 0x29, 0xd1, 0x0b, 0xbd, 0x0a, 0x89, 0x6f, 0xc9, 0x33, + 0xa8, 0x48, 0x2a, 0x27, 0x66, 0x6a, 0xf3, 0x91, 0xe3, 0x66, 0x0d, 0x9a, + 0x20, 0x94, 0x9d, 0x42, 0x15, 0xc4, 0x52, 0x5b, 0x67, 0x0c, 0x18, 0x1a, + 0x6b, 0x14, 0xb6, 0x23, 0xa5, 0x4e, 0x8d, 0xfb, 0x28, 0xf4, 0x9a, 0xf8, + 0x60, 0xa0, 0xbe, 0x09, 0x34, 0x5c, 0xcd, 0xbd, 0x24, 0x66, 0x97, 0xad, + 0x73, 0xdb, 0xad, 0x4b, 0x03, 0x20, 0x85, 0x8f, 0xdb, 0x45, 0xb2, 0xaf, + 0x0d, 0x8e, 0xa9, 0x85, 0xfb, 0xb9, 0xd2, 0x33, 0x18, 0x77, 0x8b, 0x8c, + 0x05, 0x0e, 0x8e, 0x66, 0xb3, 0xa4, 0x76, 0xe3, 0x9b, 0x05, 0x44, 0x17, + 0x74, 0xdc, 0xdd, 0x66, 0xcd, 0x25, 0x9b, 0xe2, 0xef, 0x76, 0x73, 0x28, + 0x8b, 0xfb, 0x04, 0xec, 0x45, 0x6b, 0x8e, 0x9f, 0x27, 0xec, 0x1d, 0x5a, + 0xc2, 0x3c, 0x95, 0x9c, 0xd9, 0x94, 0x3e, 0x05, 0x61, 0xb2, 0x69, 0xcd, + 0xe8, 0xb3, 0x76, 0x26, 0x5c, 0x03, 0x9a, 0x98, 0x67, 0x59, 0xf8, 0x33, + 0x35, 0xe2, 0x44, 0xf4, 0x3d, 0x0d, 0xea, 0x23, 0x7c, 0x55, 0x47, 0x79, + 0x61, 0x5f, 0x08, 0x9c, 0xa8, 0xe3, 0x80, 0x9f, 0xa3, 0xde, 0x35, 0xdb, + 0x2c, 0x49, 0x78, 0xc4, 0xba, 0x6c, 0x62, 0xb7, 0x22, 0x4d, 0x9c, 0x9b, + 0x9e, 0xc1, 0x6e, 0xbf, 0x78, 0xa2, 0x21, 0x94, 0x68, 0x26, 0xb2, 0xf4, + 0x2b, 0x3a, 0x8d, 0xa3, 0xd0, 0x74, 0x52, 0x85, 0xe9, 0xed, 0xd7, 0x4d, + 0x37, 0x0c, 0xd2, 0x21, 0xb6, 0x51, 0x22, 0x71, 0xd2, 0x78, 0xfa, 0x15, + 0x40, 0x9c, 0x0b, 0x0f, 0xa4, 0x04, 0x42, 0x98, 0x4a, 0x86, 0x2d, 0x80, + 0x21, 0xe4, 0x69, 0x02, 0x7a, 0x9c, 0xba, 0x87, 0x1f, 0x94, 0x7e, 0x11, + 0x80, 0x2c, 0x36, 0xfd, 0xc9, 0x34, 0x6f, 0x04, 0x90, 0xe0, 0xbc, 0x45, + 0xe2, 0xde, 0x05, 0x27, 0xcb, 0x05, 0xb4, 0x96, 0x88, 0x69, 0xae, 0x81, + 0x69, 0xaf, 0x54, 0x41, 0x84, 0x73, 0x33, 0x40, 0xf8, 0xf2, 0xf2, 0xe2, + 0xcd, 0xba, 0xe5, 0x5a, 0x30, 0xc6, 0x0a, 0xbe, 0x48, 0x36, 0xc1, 0x47, + 0xa5, 0x66, 0x0c, 0xe9, 0x44, 0xfb, 0xd2, 0x13, 0xed, 0x33, 0x3c, 0x3e, + 0x1c, 0x41, 0x43, 0xa8, 0xb1, 0x68, 0xdf, 0xf8, 0x60, 0x67, 0x4c, 0x38, + 0x11, 0x1f, 0xf6, 0xa5, 0x6f, 0x3e, 0xf0, 0x6c, 0x6e, 0x16, 0x51, 0xcc, + 0x95, 0x2c, 0x9e, 0x17, 0xa1, 0x43, 0x4c, 0xf1, 0xa1, 0x82, 0x24, 0x49, + 0xe0, 0xc3, 0xd3, 0x22, 0x2b, 0x96, 0x0f, 0x0e, 0x33, 0xfc, 0x7b, 0x1b, + 0xf2, 0x8a, 0x23, 0x19, 0x00, 0x66, 0x74, 0x34, 0xc8, 0x50, 0x42, 0x29, + 0xaa, 0x32, 0x50, 0x67, 0xfa, 0x3f, 0x88, 0xcb, 0xe0, 0x32, 0x20, 0x97, + 0x54, 0xd0, 0x7b, 0x38, 0xb5, 0x64, 0xc3, 0x56, 0x5c, 0xcd, 0xcc, 0xcb, + 0x5a, 0xa6, 0x80, 0xf1, 0xc0, 0xe5, 0x48, 0x61, 0xa4, 0xb1, 0x24, 0xc6, + 0x52, 0xc5, 0x59, 0xff, 0xab, 0x64, 0xf5, 0x64, 0xfb, 0x5e, 0xb7, 0xef, + 0x44, 0x75, 0xf1, 0xdf, 0x27, 0x7d, 0x1c, 0x4a, 0x1f, 0x7d, 0xa4, 0x5a, + 0x3c, 0x76, 0x1d, 0xe9, 0x55, 0xbc, 0x16, 0x46, 0x3a, 0x80, 0x69, 0x35, + 0xc0, 0xff, 0x38, 0xba, 0xa9, 0x83, 0x4b, 0x6f, 0xae, 0x23, 0xce, 0x00, + 0x11, 0x48, 0x5b, 0x3d, 0x16, 0x96, 0x9f, 0xec, 0x70, 0x0c, 0x1c, 0x06, + 0xa4, 0xd8, 0xfd, 0x1a, 0xed, 0x66, 0xd2, 0xd8, 0xa6, 0xee, 0x23, 0x25, + 0x4f, 0xb3, 0x60, 0x86, 0x9c, 0xad, 0xe6, 0xc7, 0xe6, 0x44, 0xf2, 0xd8, + 0xd0, 0x10, 0x90, 0xd2, 0xbc, 0xa0, 0xfe, 0xef, 0xe7, 0x74, 0x74, 0x72, + 0x8f, 0x26, 0x44, 0x64, 0x34, 0xb7, 0x01, 0x40, 0xdf, 0xf9, 0xa5, 0x06, + 0xb8, 0x72, 0xb7, 0x89, 0x44, 0xc7, 0xae, 0x54, 0x10, 0xa5, 0x6a, 0x74, + 0x0a, 0x64, 0xd4, 0x19, 0xb2, 0x5c, 0x43, 0xbb, 0x64, 0xc0, 0xc4, 0xd5, + 0x59, 0x3c, 0xf6, 0xf5, 0x7f, 0xce, 0x50, 0xf4, 0x1f, 0x2a, 0x6d, 0x22, + 0xed, 0xe4, 0xd8, 0x90, 0x52, 0xde, 0xfd, 0xc2, 0x95, 0xfb, 0x0c, 0x9b, + 0x8c, 0x9c, 0xa8, 0x25, 0xe2, 0x22, 0xb3, 0xc5, 0xd3, 0x7f, 0x46, 0xb5, + 0xae, 0xe6, 0xa0, 0xb4, 0xd6, 0x68, 0x4e, 0x49, 0xae, 0x95, 0x9a, 0xb7, + 0xd2, 0xc3, 0x17, 0xe8, 0xad, 0x2d, 0xe5, 0xa4, 0xe0, 0x47, 0xcd, 0xed, + 0x6b, 0xba, 0xe8, 0x25, 0x81, 0xf3, 0x8d, 0xde, 0xc5, 0x82, 0x3a, 0xe5, + 0x13, 0x51, 0xdb, 0x9b, 0x80, 0xdd, 0x0b, 0x0d, 0xa0, 0x1d, 0xfc, 0x2a, + 0x03, 0x70, 0x4a, 0xff, 0x99, 0x41, 0xe2, 0xe2, 0xb3, 0xee, 0x11, 0x47, + 0x12, 0xfd, 0xa0, 0xc0, 0xb5, 0xae, 0x08, 0xd6, 0x73, 0xdb, 0x05, 0x0b, + 0xf9, 0x04, 0xe4, 0x4a, 0x75, 0x66, 0x20, 0x8e, 0x0c, 0x5c, 0xe0, 0xa0, + 0x72, 0x36, 0x4a, 0xd2, 0x7a, 0xaf, 0x1f, 0xf4, 0xc5, 0xa9, 0x10, 0x31, + 0x3c, 0x55, 0x15, 0x91, 0x21, 0xa3, 0x27, 0x32, 0x0a, 0x59, 0x00, 0x67, + 0x1d, 0x50, 0x14, 0x7e, 0xa6, 0xed, 0xee, 0xb2, 0x7a, 0xdd, 0x7f, 0x32, + 0x0a, 0x33, 0x3e, 0x4c, 0x3a, 0x90, 0xc8, 0xde, 0xd9, 0x24, 0x44, 0x57, + 0xdd, 0x6f, 0x44, 0x62, 0x85, 0xda, 0xd3, 0x4d, 0x95, 0x7a, 0xaa, 0x66, + 0xe6, 0x07, 0x1d, 0xfd, 0x4b, 0xb3, 0x16, 0x56, 0x75, 0x57, 0x21, 0x12, + 0x04, 0xa4, 0xce, 0x26, 0x9c, 0xc5, 0x5e, 0x4d, 0x86, 0x99, 0xfe, 0xd8, + 0xec, 0xb0, 0xe4, 0xcf, 0x19, 0xb0, 0x8c, 0x3c, 0x52, 0xc6, 0xbd, 0xdc, + 0x93, 0x9a, 0x74, 0x95, 0x2c, 0xa1, 0x83, 0x9b, 0xd0, 0x16, 0xcb, 0x3b, + 0xf1, 0xb2, 0x52, 0xcd, 0xe5, 0x4e, 0xe3, 0xcf, 0xec, 0x48, 0xb0, 0xee, + 0x59, 0xeb, 0x22, 0x02, 0x69, 0xf4, 0x41, 0xbd, 0x00, 0x01, 0xb9, 0x8e, + 0xaa, 0x81, 0x48, 0xe2, 0xcc, 0x16, 0xc1, 0x37, 0x87, 0xdd, 0x3f, 0xf0, + 0x29, 0x81, 0x1e, 0x1e, 0xe8, 0x6c, 0x07, 0x70, 0xc4, 0x2b, 0xc0, 0xd8, + 0xf6, 0x53, 0x2b, 0xe2, 0x14, 0xe5, 0x86, 0xbf, 0xc1, 0x16, 0x2e, 0x0e, + 0x3e, 0x44, 0xf1, 0x59, 0x2b, 0xc3, 0xfe, 0x57, 0xbd, 0xc1, 0x3d, 0x4f, + 0x70, 0xe1, 0xc2, 0xb9, 0xd1, 0x1d, 0xdd, 0x2a, 0x67, 0xe3, 0x7d, 0x89, + 0x1d, 0x4b, 0x54, 0x6d, 0x60, 0xf7, 0x09, 0xb4, 0x6b, 0xd5, 0xab, 0x40, + 0x4e, 0xe9, 0xa4, 0xb2, 0x06, 0x54, 0xcc, 0xd8, 0xb1, 0x00, 0xf9, 0x63, + 0x0c, 0x1f, 0x59, 0xed, 0x25, 0x3e, 0x2b, 0x1c, 0xc1, 0x7a, 0xdd, 0xc9, + 0x8c, 0x64, 0xd5, 0xaa, 0x7c, 0xc2, 0xb7, 0x36, 0xa5, 0xf8, 0x0d, 0x70, + 0x65, 0x74, 0xbb, 0xfa, 0xa0, 0x77, 0x1c, 0x83, 0x0f, 0x11, 0xab, 0x43, + 0xdf, 0xba, 0xdb, 0x14, 0x44, 0x17, 0xb5, 0x25, 0x55, 0xe4, 0xa6, 0x40, + 0x40, 0xac, 0x0e, 0xa0, 0x6d, 0xee, 0x73, 0x76, 0xed, 0x39, 0x98, 0x15, + 0xb2, 0x53, 0x2d, 0x90, 0xaf, 0x9c, 0x87, 0xe3, 0x34, 0xf8, 0x50, 0xda, + 0xd0, 0x17, 0x6f, 0x19, 0x2f, 0x32, 0x4c, 0x5a, 0xe7, 0x7e, 0x9a, 0x8d, + 0x09, 0x92, 0xa8, 0x16, 0xe2, 0xa4, 0x44, 0xf4, 0x8b, 0x1a, 0x19, 0x86, + 0xb2, 0x0c, 0x7a, 0xc4, 0x2b, 0xa4, 0x3a, 0xdf, 0x6f, 0x4c, 0xd1, 0x8a, + 0xb3, 0x9e, 0x32, 0xd5, 0x19, 0x66, 0x9c, 0x3e, 0x41, 0x72, 0xe9, 0x7f, + 0x9a, 0x75, 0xd1, 0xb0, 0x1a, 0x32, 0x8b, 0xab, 0x7b, 0xc6, 0x61, 0x7b, + 0x5f, 0xac, 0x1d, 0x84, 0x5c, 0x12, 0xc7, 0xb4, 0x4c, 0xa0, 0x46, 0x38, + 0x3a, 0x6b, 0xee, 0x37, 0xbc, 0xba, 0x2f, 0xb4, 0x5c, 0x47, 0xbc, 0x6a, + 0x1b, 0x59, 0x65, 0xf0, 0xbe, 0xeb, 0x83, 0xb9, 0x65, 0xb3, 0x23, 0xb8, + 0xc9, 0x9e, 0x38, 0x5b, 0x63, 0xe4, 0x49, 0xef, 0x98, 0x9d, 0xfc, 0xde, + 0x53, 0x1c, 0xee, 0x61, 0xe2, 0x52, 0x66, 0x50, 0xac, 0x5d, 0x26, 0x5a, + 0xa8, 0x89, 0x92, 0x84, 0xde, 0xdf, 0x8e, 0x0c, 0x4a, 0x4c, 0xf6, 0xe3, + 0x76, 0x41, 0x89, 0xfc, 0x6b, 0x05, 0xed, 0x7c, 0xc3, 0xb5, 0x66, 0xa6, + 0xef, 0x8f, 0x10, 0x08, 0x5f, 0xd7, 0xd7, 0x26, 0x8f, 0x97, 0x18, 0xf7, + 0x84, 0x32, 0x6b, 0x1e, 0x37, 0x4d, 0x9f, 0x12, 0x30, 0xcd, 0xd1, 0xa2, + 0xa3, 0x03, 0x6a, 0x65, 0x0b, 0x19, 0xaf, 0x06, 0xd5, 0x2c, 0x0d, 0x5c, + 0x4a, 0x40, 0xe0, 0xb4, 0x00, 0x3a, 0x83, 0xb9, 0x72, 0xd3, 0x93, 0xe7, + 0x12, 0x28, 0x55, 0x63, 0x27, 0x01, 0xda, 0x70, 0xbd, 0x5e, 0xc0, 0xf6, + 0xe5, 0x9e, 0x1b, 0x74, 0x97, 0x52, 0xef, 0xf6, 0x03, 0xd1, 0xe7, 0x3f, + 0xab, 0x55, 0xbb, 0xa3, 0x0e, 0x47, 0x41, 0x8a, 0x4e, 0x1a, 0xd8, 0x40, + 0xdd, 0x0f, 0xe4, 0x05, 0x2e, 0x39, 0x93, 0x2b, 0x4e, 0xd6, 0xa4, 0x57, + 0x61, 0xaf, 0x93, 0x13, 0xb4, 0x3c, 0xad, 0x5b, 0xf2, 0xe8, 0x39, 0x7a, + 0x7c, 0xe1, 0x9b, 0x1c, 0xb6, 0xa8, 0x21, 0x8c, 0x66, 0x8b, 0x11, 0xfe, + 0xc4, 0x89, 0x86, 0xe1, 0xde, 0xaf, 0xee, 0x6b, 0xd2, 0x69, 0xf4, 0xac, + 0x84, 0x06, 0x51, 0x1d, 0xad, 0x4e, 0x89, 0x51, 0x1e, 0xbe, 0x36, 0x42, + 0xac, 0xd9, 0x6f, 0xe8, 0x75, 0x82, 0xf4, 0xab, 0xb4, 0xcc, 0x7c, 0xe9, + 0x66, 0x35, 0x44, 0x10, 0xc1, 0x29, 0xa6, 0xee, 0xb9, 0xd8, 0xc4, 0x04, + 0x5d, 0x38, 0x9a, 0xc3, 0x6a, 0x01, 0x21, 0x64, 0x04, 0x09, 0x6c, 0x8d, + 0xaa, 0x01, 0x71, 0xb8, 0x6c, 0x51, 0xb7, 0xc6, 0x25, 0x52, 0x5b, 0x7a, + 0x80, 0xd5, 0x22, 0x60, 0x13, 0xfe, 0xe7, 0x9c, 0x14, 0xb9, 0x6f, 0xd9, + 0x0b, 0x88, 0x9d, 0x96, 0xaf, 0xc5, 0xc5, 0x41, 0xd6, 0xa5, 0xa0, 0x2d, + 0xd2, 0x75, 0x9d, 0xcb, 0xcd, 0xa9, 0x90, 0x67, 0xad, 0x93, 0x4d, 0x17, + 0x30, 0xc4, 0x8e, 0x58, 0x03, 0xc4, 0xc9, 0x47, 0x99, 0xca, 0x13, 0x36, + 0x09, 0x9e, 0xb1, 0x3f, 0x62, 0x88, 0xa4, 0x1e, 0xec, 0x1e, 0xcd, 0x9b, + 0xdf, 0x24, 0x13, 0x0e, 0x10, 0x6e, 0x82, 0xee, 0x82, 0x8b, 0xe9, 0x55, + 0x56, 0x30, 0x8d, 0x6d, 0x54, 0xd1, 0xd6, 0xce, 0x53, 0xaf, 0xa2, 0xbf, + 0xde, 0x53, 0x7b, 0x45, 0x21, 0x4e, 0x42, 0xc0, 0x0a, 0xc8, 0xea, 0xf0, + 0xbb, 0x37, 0x63, 0x8c, 0xb2, 0xb2, 0xde, 0x57, 0xf9, 0x1a, 0xc5, 0xb8, + 0xcf, 0x3b, 0xf4, 0xb4, 0xd5, 0x7c, 0x77, 0x46, 0x30, 0x6e, 0xed, 0xe1, + 0xab, 0x33, 0x95, 0xca, 0xb8, 0x1b, 0x3a, 0x72, 0x25, 0x56, 0x02, 0x2d, + 0x58, 0x2b, 0xdb, 0x2e, 0x93, 0xae, 0xdf, 0x99, 0xcb, 0x72, 0x01, 0xfb, + 0xdb, 0xd9, 0x35, 0xb3, 0x33, 0xaf, 0x7d, 0x90, 0xce, 0xa2, 0xf4, 0x92, + 0xd0, 0x28, 0xd6, 0x08, 0x98, 0xb9, 0x91, 0xc1, 0x53, 0x1b, 0xcd, 0x55, + 0x8c, 0x4b, 0x71, 0xd0, 0x78, 0xfe, 0x00, 0xa1, 0x22, 0xd4, 0xe2, 0x81, + 0xb3, 0x6b, 0x2a, 0x19, 0xd9, 0x57, 0x1f, 0xa6, 0xbc, 0x55, 0x00, 0x62, + 0x31, 0x67, 0xb3, 0x82, 0x0c, 0xea, 0xc4, 0xbf, 0x5d, 0x7c, 0x42, 0x6a, + 0xe3, 0x46, 0x93, 0x20, 0x34, 0xc8, 0xb0, 0x3c, 0xff, 0x27, 0xdf, 0x51, + 0x29, 0x8a, 0xc4, 0x6c, 0xd6, 0x11, 0x83, 0xa8, 0xe5, 0x6e, 0x96, 0xca, + 0x7a, 0xbc, 0x14, 0xe5, 0x3a, 0x7f, 0x02, 0x25, 0x6e, 0xcf, 0x1d, 0x93, + 0xfc, 0x9b, 0xf5, 0x3a, 0x18, 0xee, 0x97, 0xfb, 0x5a, 0x6e, 0x87, 0xf6, + 0x40, 0x28, 0x9c, 0x87, 0x6c, 0x00, 0xdc, 0x0c, 0x65, 0x9b, 0xbd, 0xad, + 0x44, 0x38, 0x00, 0x8c, 0x6a, 0x42, 0x26, 0xbe, 0x08, 0x96, 0xc8, 0x27, + 0x83, 0xdf, 0xda, 0x6b, 0xc4, 0x16, 0xa4, 0x50, 0x6d, 0x62, 0x9e, 0x16, + 0x1c, 0xb1, 0xeb, 0x61, 0x30, 0xa8, 0x98, 0x89, 0x47, 0xf7, 0xa2, 0x2a, + 0xbe, 0xa6, 0xee, 0x6b, 0x36, 0x77, 0xf1, 0xe9, 0x61, 0x93, 0xfb, 0xfc, + 0x58, 0xe1, 0xe3, 0xc4, 0x37, 0x52, 0x37, 0x27, 0x16, 0x76, 0x20, 0xb3, + 0x92, 0x5b, 0x21, 0xf6, 0xdb, 0x76, 0x32, 0x78, 0x7e, 0xcd, 0xe2, 0x8a, + 0x50, 0x58, 0x2c, 0x29, 0x4b, 0xde, 0xda, 0x07, 0x38, 0x19, 0xd7, 0x69, + 0x4c, 0x00, 0x54, 0x67, 0xf9, 0x69, 0xea, 0x8e, 0xa3, 0xe5, 0x26, 0x8a, + 0x11, 0x2e, 0x86, 0x66, 0x78, 0x0d, 0x8e, 0x7a, 0x75, 0x63, 0x83, 0x32, + 0x1d, 0x61, 0x09, 0x85, 0x18, 0x58, 0xce, 0xfa, 0x8b, 0x7f, 0x23, 0x63, + 0x8e, 0xd2, 0xa2, 0x1e, 0xe7, 0x16, 0xfb, 0x8f, 0x1f, 0x81, 0x29, 0x27, + 0xb7, 0xb6, 0xeb, 0x7c, 0x19, 0xc1, 0x83, 0x8b, 0xa7, 0x6f, 0x9e, 0x8e, + 0x3c, 0x61, 0x4e, 0x9a, 0xc9, 0x3a, 0xcc, 0xd4, 0x86, 0x61, 0xb9, 0xed, + 0xc0, 0xc1, 0x34, 0x08, 0x4a, 0x8f, 0x92, 0x42, 0xba, 0x20, 0x4b, 0x4b, + 0x28, 0x25, 0xd3, 0x07, 0x51, 0x16, 0xdc, 0x03, 0x1a, 0x5a, 0x26, 0xaf, + 0xa4, 0xe9, 0x1e, 0x45, 0xd7, 0x6e, 0x3c, 0x5c, 0x50, 0xdb, 0x65, 0xbc, + 0xb1, 0x74, 0xd2, 0x7f, 0x92, 0x20, 0x98, 0x36, 0xb8, 0x7d, 0xb8, 0x8d, + 0x87, 0x90, 0x84, 0x21, 0x1c, 0x3f, 0x76, 0xb1, 0x1a, 0xa5, 0x6b, 0xba, + 0x42, 0xea, 0xca, 0xd7, 0xf6, 0x9c, 0x63, 0xb6, 0x59, 0xf9, 0x21, 0xf5, + 0x3f, 0x07, 0x03, 0x86, 0xdc, 0xc2, 0x1b, 0xbf, 0xf7, 0x44, 0xc9, 0x04, + 0x4d, 0x94, 0x8e, 0xec, 0xac, 0x23, 0x3c, 0xd1, 0xb6, 0x14, 0xc2, 0x5f, + 0x8b, 0x35, 0x9b, 0x3e, 0xdc, 0x60, 0x11, 0x2e, 0x27, 0x63, 0x2f, 0xb3, + 0xd7, 0x01, 0x6c, 0xa6, 0x09, 0x84, 0xb4, 0x39, 0x48, 0x3a, 0x62, 0x9d, + 0xa8, 0xd8, 0x73, 0xa6, 0xbf, 0x0a, 0x2f, 0x30, 0x8f, 0x9d, 0x08, 0x28, + 0x57, 0x74, 0x72, 0x89, 0xa9, 0x8f, 0x0e, 0xb2, 0x4f, 0xcb, 0x22, 0x29, + 0xe5, 0xd3, 0x2d, 0x93, 0x13, 0xe4, 0xac, 0x07, 0xb3, 0x3d, 0xd7, 0x85, + 0x5e, 0xa3, 0x64, 0x06, 0x19, 0x3b, 0x4f, 0x96, 0xde, 0x7e, 0x35, 0xa7, + 0xe6, 0x54, 0x4b, 0x5d, 0x6e, 0x8c, 0xed, 0x3a, 0x3e, 0x73, 0xb1, 0x48, + 0xab, 0x59, 0x55, 0x66, 0x87, 0xcb, 0x08, 0x45, 0xce, 0x7e, 0x2c, 0x70, + 0x72, 0x03, 0x0c, 0xae, 0x84, 0x66, 0x6f, 0xaa, 0x35, 0xc1, 0xda, 0xad, + 0xfb, 0x46, 0x93, 0xdc, 0x4c, 0x61, 0x90, 0x88, 0x42, 0xac, 0x29, 0x47, + 0x8a, 0xb7, 0x50, 0xb6, 0x32, 0xad, 0x54, 0x69, 0xca, 0x70, 0xad, 0xb9, + 0xfa, 0x04, 0x9a, 0x43, 0x63, 0x0d, 0xab, 0x28, 0x90, 0xc5, 0xd5, 0x88, + 0xaa, 0x3c, 0x9f, 0xc9, 0x9a, 0xa2, 0x54, 0x0d, 0x9d, 0xd4, 0x06, 0xf9, + 0x49, 0xf8, 0x2b, 0x35, 0xac, 0xa9, 0x62, 0xfd, 0xc4, 0x0a, 0x93, 0xcb, + 0x1e, 0xeb, 0xe9, 0xbc, 0xb9, 0xc7, 0x9c, 0xab, 0x06, 0xe9, 0xf2, 0x51, + 0x7d, 0xeb, 0xef, 0x33, 0x5e, 0x74, 0xce, 0x4f, 0x60, 0xad, 0x34, 0x8c, + 0xdc, 0xf2, 0x9d, 0x3a, 0xe4, 0x99, 0xa8, 0xc5, 0x10, 0x19, 0xaa, 0xae, + 0xea, 0x5d, 0x2a, 0xf9, 0x56, 0x67, 0x0e, 0x17, 0x55, 0x6e, 0xa0, 0x9f, + 0x0f, 0xd2, 0x16, 0xbe, 0xa5, 0x1e, 0x02, 0x28, 0x16, 0xa4, 0x1b, 0x0c, + 0xaf, 0x88, 0xc7, 0x8d, 0xb5, 0xc4, 0xf8, 0x2c, 0x0a, 0xee, 0x93, 0xd9, + 0xea, 0x11, 0x50, 0xaa, 0x25, 0x64, 0xf1, 0xaa, 0x5b, 0xcc, 0x4c, 0xf9, + 0x22, 0x11, 0x3d, 0xd7, 0x54, 0x0b, 0x9c, 0x10, 0x40, 0x77, 0x5d, 0x66, + 0x1c, 0x6c, 0xc2, 0xe2, 0x01, 0xef, 0x0c, 0xbd, 0x42, 0x43, 0xf3, 0x24, + 0x8d, 0x8f, 0xdf, 0xa1, 0x1d, 0x04, 0x2f, 0xdc, 0x3e, 0xd8, 0x3c, 0x04, + 0x5f, 0x17, 0x83, 0xa7, 0x18, 0xfc, 0xf6, 0x5f, 0x97, 0x32, 0x5e, 0x80, + 0x05, 0x6d, 0x10, 0x42, 0x35, 0x0e, 0x57, 0xe0, 0x6e, 0x7b, 0x9c, 0xc0, + 0x5b, 0xde, 0x02, 0xc7, 0xc4, 0x9d, 0xba, 0x32, 0x89, 0xc1, 0x7d, 0xdc, + 0x2a, 0xa9, 0x3d, 0xb6, 0xb0, 0x50, 0xd3, 0x99, 0xa0, 0xf5, 0x45, 0x77, + 0x22, 0x08, 0x6a, 0x8e, 0xd3, 0xc8, 0x6e, 0x87, 0xf3, 0xe9, 0xe2, 0x3c, + 0x10, 0x62, 0xfa, 0x25, 0x69, 0xc2, 0x46, 0x0d, 0x6e, 0xf2, 0xaf, 0x7e, + 0x4d, 0x4f, 0x29, 0x4d, 0x13, 0x72, 0x1e, 0x01, 0xe1, 0x10, 0x81, 0x3b, + 0x9d, 0x8a, 0xa8, 0xc6, 0x1a, 0x3b, 0xa3, 0xdb, 0xc7, 0xb0, 0x8d, 0xc7, + 0x2e, 0x1c, 0xc5, 0x95, 0x62, 0xeb, 0x32, 0x81, 0xd1, 0x30, 0x8c, 0x4d, + 0x43, 0x53, 0xaa, 0x32, 0x45, 0xd8, 0xfb, 0x2e, 0x86, 0xe8, 0xa9, 0x55, + 0x08, 0x6e, 0x23, 0x1e, 0x1c, 0x4d, 0xb1, 0x38, 0xa4, 0xce, 0x63, 0xa8, + 0xd1, 0x87, 0x2f, 0xd9, 0xa9, 0x27, 0x36, 0xb7, 0xbf, 0xab, 0xc4, 0x75, + 0xe5, 0xc6, 0x99, 0x6f, 0x1e, 0x1a, 0x7d, 0xd4, 0x86, 0xd7, 0xda, 0x3a, + 0x9f, 0x6e, 0xad, 0xa2, 0xa7, 0x66, 0xdc, 0xc5, 0xdf, 0xc2, 0xe9, 0x29, + 0x8d, 0x48, 0x0b, 0x6d, 0xd3, 0x8c, 0xef, 0x91, 0x19, 0x6d, 0x0e, 0x8b, + 0xdd, 0xe6, 0x51, 0xf9, 0x6c, 0xac, 0x92, 0xf4, 0x41, 0x60, 0x7f, 0x53, + 0x94, 0xe9, 0xbd, 0xbc, 0xc2, 0xfb, 0x3d, 0x20, 0xc6, 0x3e, 0xe3, 0x95, + 0xf1, 0xd9, 0x57, 0x7a, 0x61, 0xc1, 0x50, 0x83, 0xca, 0x82, 0x12, 0x80, + 0xc3, 0xc7, 0xa2, 0xfc, 0x44, 0x9a, 0xe1, 0x13, 0x90, 0x94, 0x5c, 0x94, + 0x8c, 0xfe, 0xcc, 0x29, 0xa4, 0x17, 0x03, 0xdd, 0x5a, 0x7c, 0x56, 0x63, + 0x01, 0x8a, 0x73, 0x26, 0x8d, 0xe1, 0x68, 0x16, 0xc3, 0x2b, 0x35, 0xf2, + 0xad, 0x5a, 0x0c, 0x13, 0x1a, 0x1a, 0x16, 0x31, 0x8f, 0x8c, 0x1d, 0x0c, + 0xb2, 0x87, 0x83, 0x1f, 0xeb, 0xd0, 0xa4, 0xf9, 0xc1, 0x2b, 0x25, 0x64, + 0x32, 0x40, 0xe0, 0x42, 0x4a, 0xfa, 0x4f, 0x70, 0x19, 0x2f, 0x67, 0x62, + 0xc5, 0x87, 0x05, 0xe7, 0xc9, 0x7c, 0xca, 0x57, 0x04, 0xdf, 0xb5, 0xbd, + 0x02, 0xe5, 0x0e, 0x29, 0x9f, 0xf9, 0xdc, 0xa5, 0xf9, 0x1e, 0x26, 0x60, + 0xb4, 0xc1, 0x36, 0xe7, 0xaa, 0x6d, 0xf8, 0xd6, 0x53, 0x96, 0x3f, 0x8f, + 0x54, 0x96, 0xe1, 0x2f, 0xd3, 0xa0, 0x7a, 0xea, 0x67, 0x52, 0x57, 0x6d, + 0x2f, 0x3d, 0xd3, 0x80, 0xfa, 0xd9, 0x00, 0x07, 0x95, 0x17, 0x52, 0xa5, + 0x15, 0x4d, 0x91, 0xec, 0xdc, 0x09, 0x13, 0xad, 0xd5, 0xcc, 0x09, 0xe9, + 0xbd, 0xe1, 0x5f, 0x35, 0x52, 0x48, 0x19, 0xdf, 0x36, 0x40, 0xdc, 0xd9, + 0x03, 0x70, 0x7c, 0xb3, 0xa1, 0x9e, 0x8f, 0x0d, 0x06, 0xb9, 0xbd, 0xb9, + 0x34, 0x72, 0xf6, 0xe2, 0x77, 0xd1, 0x13, 0x4e, 0xe8, 0x61, 0x15, 0x07, + 0x3c, 0x32, 0x17, 0x81, 0x59, 0xa0, 0xa3, 0x21, 0x7f, 0x14, 0x67, 0x2c, + 0xe5, 0x22, 0xae, 0x2e, 0x09, 0xc9, 0x1b, 0xe3, 0x2f, 0xa7, 0x1b, 0x6c, + 0x85, 0x64, 0x3b, 0x27, 0x3a, 0xdb, 0xbe, 0xce, 0x9e, 0xd5, 0xda, 0x9a, + 0xac, 0x07, 0xea, 0xf7, 0x0f, 0xaa, 0x9e, 0xa2, 0xde, 0xf4, 0x71, 0x66, + 0x19, 0xff, 0x8b, 0x9c, 0x95, 0xf1, 0x49, 0x94, 0x14, 0xc8, 0x07, 0x03, + 0x73, 0x38, 0x32, 0xf7, 0xe9, 0x2b, 0x8b, 0x61, 0x06, 0x6d, 0xf2, 0xa0, + 0x26, 0xc5, 0x64, 0xcc, 0x27, 0xa0, 0xa8, 0x11, 0xee, 0x08, 0x2f, 0x6e, + 0xb1, 0x65, 0xbd, 0xdc, 0x97, 0x9e, 0x9c, 0x45, 0x33, 0xeb, 0x39, 0xca, + 0x70, 0x8d, 0x92, 0xc3, 0x0c, 0xd6, 0x84, 0x61, 0x7f, 0xb8, 0xde, 0xe5, + 0x27, 0xa3, 0xab, 0xa9, 0x0b, 0xa5, 0xad, 0x55, 0xc9, 0xcd, 0x46, 0x35, + 0x84, 0xf4, 0x6e, 0x20, 0x2c, 0xa4, 0xa1, 0x1d, 0xd3, 0xf0, 0xc3, 0x36, + 0xcd, 0xa5, 0xe2, 0x3f, 0x44, 0x31, 0x71, 0x2b, 0x96, 0x01, 0x2e, 0x99, + 0x6e, 0x2e, 0x1b, 0xe1, 0x3b, 0xd0, 0xff, 0x9d, 0xf9, 0xdb, 0xb5, 0xb9, + 0x9a, 0x73, 0x8a, 0x20, 0x2e, 0x10, 0xee, 0x5a, 0x15, 0xc3, 0x4b, 0x0b, + 0x09, 0x70, 0xad, 0xb5, 0x00, 0xec, 0xb2, 0xb3, 0x38, 0xbd, 0x77, 0x8b, + 0x10, 0xe5, 0xe2, 0x45, 0x51, 0x50, 0x33, 0x85, 0xa0, 0x5a, 0x0c, 0x1f, + 0x21, 0xa5, 0x07, 0xe9, 0xa3, 0x03, 0xd9, 0x19, 0x85, 0x7b, 0x48, 0xa5, + 0x55, 0x35, 0x9d, 0x02, 0xe7, 0xac, 0xa8, 0x21, 0x0a, 0x3b, 0x48, 0xb3, + 0x33, 0xbb, 0x56, 0xcb, 0xf7, 0xfc, 0xf7, 0x75, 0xde, 0xe5, 0x8a, 0xb6, + 0x8d, 0x55, 0xb6, 0xbf, 0x2d, 0xc1, 0xe1, 0x9b, 0x0e, 0x32, 0xdc, 0x16, + 0x52, 0xf9, 0x09, 0x5e, 0xdf, 0xec, 0x6b, 0x93, 0xe0, 0x57, 0x52, 0x5b, + 0x08, 0xd1, 0xeb, 0x97, 0x78, 0x68, 0xbb, 0x10, 0x39, 0x7d, 0x68, 0x9a, + 0x3b, 0x85, 0xa6, 0x88, 0x41, 0xd8, 0x32, 0x40, 0x3c, 0x29, 0x2d, 0x63, + 0x5c, 0x5d, 0xf4, 0xea, 0x2b, 0xbc, 0x94, 0x37, 0xae, 0x95, 0xed, 0x7e, + 0x4a, 0x39, 0x78, 0x39, 0x6e, 0x2b, 0x8c, 0x91, 0xaf, 0xb7, 0x42, 0xf1, + 0x08, 0xb7, 0x09, 0x9b, 0xa6, 0x4e, 0x67, 0x35, 0x7a, 0x52, 0xaa, 0x83, + 0x2a, 0x6a, 0x7f, 0x34, 0xfb, 0x6f, 0xa7, 0x09, 0x40, 0xd1, 0xa5, 0x48, + 0x66, 0xe7, 0x34, 0xe4, 0xab, 0xa5, 0xf2, 0x72, 0x32, 0xfb, 0xa4, 0x2d, + 0xcf, 0xd7, 0xe4, 0xe2, 0x76, 0x04, 0x58, 0x67, 0x0f, 0x8c, 0x87, 0x29, + 0x6b, 0x7e, 0x27, 0x57, 0x29, 0xaa, 0x94, 0xae, 0x11, 0xe4, 0x89, 0x6e, + 0x23, 0xf7, 0x8e, 0xf5, 0x4c, 0x7d, 0x59, 0x17, 0x43, 0xca, 0xda, 0x66, + 0xc6, 0x39, 0x6f, 0xcf, 0xf0, 0x29, 0xae, 0x1b, 0x69, 0x65, 0x7f, 0x80, + 0x69, 0xaa, 0x4d, 0xda, 0xf5, 0x66, 0x61, 0xa6, 0xef, 0x3b, 0x82, 0x71, + 0xb1, 0xab, 0xca, 0xc9, 0xa6, 0x62, 0x9e, 0xae, 0xf4, 0xdf, 0x2e, 0x27, + 0xcf, 0x88, 0xc2, 0x79, 0xdf, 0x8c, 0x4d, 0xfa, 0x0b, 0xf3, 0xd0, 0xa7, + 0x7a, 0x68, 0xe1, 0xfa, 0x2f, 0xd4, 0xe7, 0x51, 0x2e, 0xb2, 0xe4, 0x1d, + 0xab, 0xf8, 0x70, 0x22, 0xf1, 0xc5, 0x7f, 0xeb, 0x06, 0x44, 0x08, 0x92, + 0x8f, 0x95, 0xe1, 0xc3, 0xdb, 0xf8, 0x78, 0xfa, 0x71, 0x01, 0xb9, 0x47, + 0xfb, 0xb1, 0xe4, 0x81, 0x16, 0x2b, 0x06, 0xbd, 0x7c, 0xce, 0x7a, 0x34, + 0x76, 0x40, 0x86, 0x59, 0xb7, 0x1f, 0xfc, 0x05, 0x93, 0x8d, 0x85, 0x12, + 0x5a, 0x52, 0xf2, 0xb5, 0xe8, 0xa7, 0x9e, 0x08, 0x80, 0xb5, 0xdd, 0x6f, + 0xf4, 0xbd, 0xbc, 0x11, 0x54, 0xb0, 0x11, 0x5a, 0x4a, 0xf8, 0x59, 0x2f, + 0xa1, 0x02, 0x43, 0x65, 0xbf, 0xe7, 0xb2, 0x73, 0x8d, 0xe3, 0xd1, 0xa3, + 0xcd, 0xa4, 0xd9, 0xd2, 0xba, 0xb8, 0x94, 0x2c, 0x6e, 0xa2, 0x0b, 0x41, + 0x3c, 0xab, 0xca, 0xa6, 0x7d, 0x91, 0x8c, 0x8d, 0xdb, 0x3b, 0xe8, 0xb7, + 0xbe, 0x14, 0xf1, 0xdc, 0x31, 0x40, 0xec, 0xbd, 0x4e, 0x6c, 0x94, 0x20, + 0x73, 0x03, 0x1a, 0x1c, 0xad, 0x8c, 0x76, 0x5f, 0x5d, 0x22, 0xc0, 0x17, + 0xd5, 0xea, 0xd8, 0x0c, 0xee, 0xcc, 0x14, 0x20, 0xe0, 0x8e, 0xf7, 0x9b, + 0xcf, 0x88, 0xc7, 0xc0, 0x0d, 0xfc, 0x81, 0xac, 0x58, 0x7a, 0xbe, 0xd3, + 0x8c, 0xe1, 0xaa, 0xab, 0xc3, 0x0f, 0xfd, 0x9c, 0xee, 0x84, 0xc3, 0xd9, + 0x06, 0x84, 0xaa, 0x08, 0x23, 0xd2, 0x6f, 0x4f, 0xfb, 0xd7, 0xf1, 0xa3, + 0xac, 0x74, 0xc3, 0x5d, 0xcd, 0x3a, 0x87, 0xa7, 0x62, 0x16, 0x81, 0xb8, + 0x3b, 0x22, 0xa0, 0xd8, 0x64, 0x1e, 0x92, 0xa3, 0x23, 0xce, 0x3f, 0x50, + 0xc7, 0x0a, 0x22, 0x1f, 0x2e, 0xe3, 0x85, 0xca, 0xb8, 0x2e, 0x04, 0xb8, + 0x8b, 0xbd, 0xe9, 0xc0, 0xc3, 0xd9, 0x8b, 0xe2, 0x69, 0x67, 0x6d, 0x3b, + 0x5b, 0x99, 0xe1, 0xab, 0x2d, 0x59, 0xca, 0xcb, 0x49, 0x3f, 0xcb, 0x59, + 0xa6, 0x33, 0x54, 0x28, 0x4c, 0x6b, 0xad, 0x45, 0xf4, 0x9f, 0x42, 0xe6, + 0x8d, 0xb0, 0x4c, 0x7b, 0x46, 0x69, 0xbc, 0x63, 0xd3, 0x05, 0x2c, 0x62, + 0xe1, 0xb4, 0x42, 0xeb, 0x69, 0xee, 0x92, 0xd9, 0x38, 0xcb, 0xaf, 0x29, + 0x29, 0x5c, 0x40, 0x6b, 0xfd, 0x39, 0x73, 0x59, 0x8d, 0x7a, 0xad, 0xf2, + 0x71, 0xfa, 0x3e, 0xfa, 0x95, 0x34, 0xb5, 0x78, 0xfb, 0x45, 0x59, 0x8e, + 0xd8, 0x9e, 0x94, 0x42, 0x7f, 0xad, 0xa3, 0x80, 0x10, 0xe8, 0xf7, 0x4f, + 0xcb, 0x77, 0x2d, 0xcd, 0xd4, 0x22, 0x63, 0xc8, 0x11, 0xec, 0xea, 0x62, + 0x6f, 0x13, 0x07, 0x1e, 0x40, 0x77, 0xfe, 0xcc, 0x41, 0xb6, 0xc9, 0x59, + 0x5e, 0x26, 0xec, 0x50, 0x06, 0x62, 0x49, 0x9f, 0xc9, 0xfd, 0xb5, 0x7e, + 0xa8, 0x46, 0xb1, 0xcb, 0x23, 0x43, 0xcc, 0x79, 0x49, 0x52, 0x70, 0x32, + 0x87, 0x6a, 0x0b, 0x85, 0x00, 0x0d, 0xbc, 0x7e, 0x65, 0xb6, 0x8f, 0xee, + 0xe5, 0xb4, 0xa0, 0x75, 0xc3, 0x30, 0x21, 0x76, 0x12, 0x53, 0x6f, 0xb6, + 0xf5, 0xf2, 0x82, 0x68, 0xb0, 0x3a, 0xe0, 0xb1, 0x86, 0x00, 0x85, 0x6d, + 0x47, 0x23, 0xd9, 0x43, 0xa8, 0x6c, 0x96, 0xb4, 0xf3, 0xea, 0xdd, 0x1d, + 0x0a, 0x44, 0x0d, 0x5f, 0xde, 0x7c, 0xe7, 0x84, 0x0a, 0xab, 0xa3, 0xd3, + 0xd9, 0x23, 0x69, 0xf8, 0xb0, 0xf9, 0x53, 0x2b, 0x28, 0x11, 0xa2, 0x79, + 0xb9, 0x50, 0x86, 0x59, 0x05, 0x62, 0x23, 0xb3, 0x32, 0x01, 0x6f, 0xc0, + 0xf2, 0xf6, 0x39, 0xca, 0x2e, 0xdc, 0x2f, 0xcc, 0x46, 0xc9, 0x69, 0x32, + 0xc0, 0x13, 0x35, 0xf0, 0xf5, 0xd9, 0xae, 0xfe, 0x70, 0xb5, 0xf1, 0xd9, + 0x82, 0x57, 0x6f, 0x75, 0xfa, 0x52, 0xe9, 0x9e, 0xe6, 0x31, 0xfe, 0xc4, + 0x88, 0x50, 0x27, 0x38, 0xe8, 0x8c, 0x04, 0x3e, 0xe3, 0x35, 0x9f, 0xfd, + 0xff, 0x14, 0x28, 0x0d, 0x73, 0x64, 0xc8, 0x11, 0xb1, 0xa6, 0xcd, 0x8a, + 0x08, 0x60, 0x25, 0xd2, 0x2a, 0xce, 0x1c, 0xe4, 0x96, 0x76, 0xd3, 0x5c, + 0xb7, 0xbf, 0xfe, 0x14, 0xa7, 0xb5, 0xd1, 0x7f, 0x74, 0x21, 0x4e, 0x6b, + 0x4d, 0xf5, 0xd4, 0xdb, 0x75, 0x73, 0xae, 0x24, 0x57, 0x4f, 0x7c, 0xf8, + 0x61, 0x76, 0xc6, 0x6d, 0xa6, 0x17, 0x64, 0xb0, 0x58, 0x57, 0xd5, 0x91, + 0x72, 0x50, 0x78, 0xec, 0x85, 0x1e, 0x8a, 0x73, 0x6c, 0xa1, 0x18, 0x66, + 0x0e, 0x88, 0x9e, 0x81, 0x46, 0x16, 0x0c, 0xd4, 0x28, 0x0c, 0xca, 0x26, + 0x6f, 0x76, 0xfb, 0xd3, 0x48, 0x88, 0x49, 0x5f, 0xae, 0xab, 0xf8, 0x23, + 0x3e, 0x05, 0xbe, 0xa5, 0x33, 0x22, 0x02, 0xc4, 0x42, 0xab, 0x12, 0xff, + 0xba, 0x38, 0x66, 0x08, 0x53, 0xa1, 0x93, 0x29, 0x44, 0xed, 0x2f, 0x11, + 0x38, 0x0f, 0x1f, 0x40, 0x88, 0x86, 0xac, 0x2e, 0x40, 0x3f, 0x6e, 0x11, + 0xbf, 0x06, 0x6a, 0x10, 0x92, 0x8a, 0x9a, 0xfa, 0xfd, 0xa7, 0xef, 0x1c, + 0x07, 0x86, 0x70, 0xbf, 0xd0, 0x24, 0xe5, 0x4e, 0x50, 0x4e, 0xb4, 0xe7, + 0x18, 0x17, 0x26, 0x55, 0x81, 0x9a, 0xb7, 0x0c, 0x02, 0xa5, 0x95, 0x5f, + 0xfa, 0x22, 0x50, 0x5b, 0xe0, 0x20, 0x2b, 0xe8, 0x7a, 0xcb, 0x98, 0xc3, + 0x55, 0xcb, 0x0a, 0xd6, 0x68, 0x93, 0xe2, 0xb7, 0xa7, 0xbc, 0x3a, 0xee, + 0x86, 0x7c, 0x36, 0x88, 0x19, 0x57, 0xb6, 0x89, 0x01, 0x91, 0x3d, 0xf3, + 0xa1, 0xda, 0x31, 0x2e, 0x62, 0x34, 0xba, 0xdf, 0xfd, 0x80, 0x34, 0x75, + 0x35, 0x1c, 0x9d, 0x57, 0x28, 0x42, 0x01, 0xd0, 0x35, 0x0b, 0x41, 0x9e, + 0xce, 0x97, 0xa8, 0x3c, 0xb5, 0x8d, 0x16, 0xe8, 0x91, 0x92, 0xca, 0xd0, + 0x12, 0xde, 0xf2, 0x37, 0x87, 0xde, 0x6b, 0xba, 0xc7, 0x35, 0xce, 0xa4, + 0xa0, 0xbe, 0x75, 0xfe, 0xf0, 0x0a, 0xb4, 0xc2, 0x9b, 0x1e, 0x24, 0x78, + 0xb3, 0xdc, 0xfb, 0x3f, 0x3e, 0xac, 0x7a, 0x09, 0xee, 0x2d, 0x79, 0xc6, + 0x64, 0xb9, 0xb7, 0xb2, 0xb9, 0xfb, 0xa0, 0xff, 0xff, 0x3f, 0x79, 0xa5, + 0xd5, 0x93, 0x26, 0xfa, 0x6b, 0xfe, 0x83, 0x29, 0x6e, 0x8d, 0x9d, 0xe9, + 0x79, 0x30, 0xa5, 0xd7, 0x30, 0x72, 0xc5, 0xa1, 0xb8, 0xf0, 0x52, 0x4d, + 0xca, 0x39, 0x78, 0x24, 0x3c, 0xcb, 0x7c, 0x15, 0xb7, 0x51, 0x39, 0x50, + 0x50, 0x4d, 0x60, 0x91, 0xd9, 0x43, 0xa1, 0x4e, 0xbe, 0x0c, 0xac, 0x86, + 0xf7, 0xb8, 0x6e, 0xbe, 0xe5, 0x79, 0x3b, 0x0c, 0xc2, 0xff, 0xb6, 0xc7, + 0x8e, 0x67, 0xba, 0x44, 0x39, 0xd3, 0xbb, 0x51, 0x0f, 0xc4, 0x3d, 0xe9, + 0x2f, 0xa8, 0xa1, 0x08, 0x90, 0x90, 0x15, 0x28, 0x9a, 0xab, 0xbf, 0x47, + 0x40, 0x3e, 0xe2, 0xd7, 0x9b, 0x59, 0x38, 0xe4, 0x27, 0x52, 0x4a, 0xc7, + 0x78, 0x51, 0xe2, 0xe4, 0xc6, 0x04, 0xb4, 0x5e, 0x0e, 0x6c, 0x4e, 0x31, + 0x30, 0x4e, 0x73, 0x0b, 0xae, 0xe4, 0x68, 0xc0, 0xdd, 0xd1, 0x9f, 0xc1, + 0x67, 0x3d, 0xb5, 0x5a, 0x99, 0x2b, 0x2e, 0xd6, 0x89, 0x01, 0x55, 0x16, + 0x0a, 0x03, 0x79, 0xaf, 0x9f, 0x8f, 0x7a, 0x09, 0xda, 0x20, 0x82, 0xc1, + 0x35, 0xb4, 0x8d, 0x47, 0xb9, 0x69, 0x3c, 0x88, 0x2c, 0xe6, 0x03, 0x98, + 0x23, 0x55, 0x18, 0xe3, 0x9f, 0x55, 0x82, 0xfa, 0x3c, 0xd1, 0xef, 0xa6, + 0x6c, 0xdb, 0xf4, 0x5d, 0xf7, 0x04, 0xf6, 0xb5, 0xa6, 0x80, 0xbb, 0xee, + 0x5b, 0x2c, 0x41, 0x35, 0x36, 0xba, 0xae, 0xb4, 0x2b, 0x25, 0x4d, 0x7b, + 0xd8, 0x68, 0xc3, 0x47, 0x0c, 0xb3, 0x24, 0x04, 0xbf, 0x27, 0x08, 0xe5, + 0x80, 0x86, 0xbf, 0xf2, 0x41, 0x6b, 0xb9, 0x5c, 0xfb, 0xca, 0x29, 0x8f, + 0xf4, 0x14, 0x65, 0xab, 0x27, 0x0d, 0x4c, 0xaf, 0xc5, 0xe4, 0x25, 0x29, + 0x31, 0x42, 0x65, 0x08, 0x34, 0x51, 0xab, 0x44, 0x26, 0x94, 0x0a, 0xfe, + 0x45, 0xf6, 0x9b, 0xc7, 0x9b, 0x9e, 0x21, 0x06, 0x9d, 0x4d, 0xd9, 0x09, + 0x7b, 0xc3, 0x4c, 0xff, 0x62, 0xfe, 0x59, 0x23, 0x51, 0xbc, 0x58, 0x16, + 0x10, 0xea, 0xd6, 0xfd, 0xbd, 0x01, 0x31, 0x0e, 0x9b, 0x1d, 0x4c, 0xb5, + 0x7f, 0x39, 0x28, 0x1d, 0xa7, 0x5d, 0x3f, 0x80, 0x99, 0x0d, 0x7e, 0xd2, + 0xf4, 0x43, 0xf2, 0x91, 0xb9, 0x75, 0x4f, 0x41, 0x80, 0xae, 0x32, 0x9a, + 0x66, 0x2b, 0x4d, 0xa2, 0x84, 0x6a, 0x20, 0x7b, 0xd7, 0xca, 0xc7, 0x22, + 0x46, 0x49, 0xd3, 0x37, 0x69, 0x38, 0xb6, 0xce, 0x33, 0xa1, 0x8f, 0x59, + 0x14, 0x05, 0x00, 0xb9, 0xf1, 0x2f, 0x94, 0x85, 0x52, 0x0c, 0xfd, 0xf0, + 0x43, 0xea, 0x97, 0xc8, 0x88, 0xb0, 0xbc, 0xa5, 0xc3, 0x43, 0x73, 0x30, + 0x34, 0x4e, 0x59, 0x09, 0x6f, 0xf4, 0x14, 0x50, 0x99, 0x9e, 0x29, 0xcb, + 0x10, 0xc6, 0xb1, 0x84, 0x2b, 0x87, 0xb5, 0x96, 0x3a, 0xa8, 0x76, 0xa6, + 0x8e, 0xc8, 0xc9, 0xef, 0xd4, 0xe5, 0x22, 0x0a, 0x17, 0x4c, 0x6c, 0x6b, + 0xa5, 0x18, 0x0f, 0xd2, 0xf0, 0x5c, 0xd0, 0x34, 0x1c, 0xa5, 0xdc, 0xd4, + 0xcb, 0x1e, 0x44, 0xf4, 0x8f, 0xe2, 0x19, 0x06, 0x18, 0xf4, 0xc7, 0x81, + 0x5c, 0x30, 0xcf, 0x4b, 0xed, 0x91, 0x12, 0x91, 0x2e, 0x42, 0x43, 0x47, + 0xe7, 0x0e, 0x33, 0x87, 0x22, 0x5a, 0xf0, 0xeb, 0xc0, 0x55, 0xd5, 0x6a, + 0xd8, 0x61, 0xca, 0x3d, 0x22, 0x6c, 0x0d, 0xaf, 0x11, 0xf3, 0xec, 0x8f, + 0x15, 0x47, 0x27, 0x71, 0x8a, 0x0c, 0x3a, 0x63, 0xa4, 0x53, 0xd9, 0xe0, + 0xcf, 0x99, 0x3d, 0xb4, 0x5e, 0xf3, 0xbf, 0x20, 0x17, 0x52, 0xbd, 0x6c, + 0x26, 0xb9, 0xdb, 0x26, 0x64, 0xc0, 0x69, 0x6b, 0xdb, 0xca, 0xf0, 0xe3, + 0xb5, 0x4a, 0xf9, 0xd5, 0xaf, 0x01, 0xe5, 0x96, 0x52, 0x6f, 0x75, 0xb7, + 0x33, 0x7e, 0xa6, 0x55, 0x68, 0xb6, 0x14, 0x21, 0xcd, 0x65, 0xdb, 0xc9, + 0xbd, 0x56, 0x74, 0x20, 0x88, 0xfb, 0xa3, 0xad, 0xc9, 0x7d, 0xc3, 0xdb, + 0xb6, 0x37, 0xec, 0xc8, 0x0b, 0x3b, 0x46, 0xe3, 0x19, 0xbb, 0x98, 0xdb, + 0x18, 0xfa, 0xf3, 0x05, 0xf3, 0x3c, 0x7d, 0x5e, 0x5c, 0xa1, 0x3a, 0x35, + 0xde, 0x62, 0xcb, 0x13, 0xbd, 0xe6, 0x10, 0xbf, 0x34, 0xd6, 0x8d, 0xa4, + 0x9d, 0xa9, 0x7c, 0xf4, 0x68, 0xda, 0x12, 0xfb, 0xac, 0x5b, 0xf8, 0xdd, + 0x1e, 0xff, 0xdc, 0x7f, 0x0d, 0x39, 0x75, 0x27, 0x18, 0x27, 0x81, 0x83, + 0x66, 0x66, 0x44, 0xf0, 0x5b, 0xb4, 0xff, 0xe4, 0x54, 0x0f, 0x0b, 0xdf, + 0xb4, 0x68, 0xcb, 0x26, 0xca, 0xae, 0xdc, 0x5a, 0xad, 0x0c, 0xe3, 0x2f, + 0x9a, 0xfa, 0xbc, 0x4b, 0x1c, 0x46, 0x08, 0xb8, 0x09, 0x35, 0xdf, 0xde, + 0xcb, 0xf0, 0x78, 0x3d, 0x5f, 0x4d, 0xc1, 0xbf, 0xfe, 0x60, 0xcd, 0xc8, + 0xdf, 0xda, 0x5b, 0x73, 0x65, 0x62, 0x37, 0x0a, 0xe2, 0x42, 0x17, 0xaa, + 0xa7, 0x69, 0x3b, 0x4d, 0xd7, 0x65, 0x79, 0x98, 0xae, 0x7f, 0x75, 0x8f, + 0x62, 0xb4, 0xbc, 0xf2, 0x69, 0xe2, 0x14, 0xd5, 0x6a, 0x68, 0x78, 0xaa, + 0x2c, 0xef, 0x68, 0xbf, 0xc5, 0x13, 0x09, 0xae, 0x79, 0x10, 0x63, 0x4b, + 0x44, 0x4c, 0x9c, 0x4a, 0x88, 0x5f, 0x89, 0x42, 0x40, 0xcb, 0xc8, 0xd0, + 0x9e, 0x54, 0x74, 0x6f, 0xea, 0x69, 0x2f, 0x52, 0x56, 0x13, 0x02, 0x90, + 0xcd, 0xfb, 0x71, 0x90, 0xe6, 0x26, 0x0d, 0x62, 0xa3, 0x26, 0xc3, 0x6d, + 0xbe, 0x3b, 0xe7, 0xac, 0x33, 0xc8, 0x4b, 0xd3, 0x0f, 0xae, 0x4f, 0xf2, + 0x05, 0x02, 0xa0, 0x2a, 0x65, 0xd4, 0x0f, 0xf0, 0xab, 0xb7, 0xfd, 0xc3, + 0xa4, 0xf7, 0xf2, 0x23, 0x59, 0x64, 0xae, 0x6d, 0x6e, 0x36, 0x15, 0xe9, + 0x1c, 0xa9, 0x96, 0x64, 0xc5, 0xcb, 0xa7, 0xb8, 0x26, 0x53, 0x97, 0xbb, + 0x9a, 0x22, 0x8a, 0x6e, 0x1f, 0x68, 0xbc, 0x7a, 0x30, 0x70, 0xea, 0x73, + 0xc7, 0x38, 0x70, 0xf5, 0x21, 0x36, 0x15, 0x63, 0x7a, 0x24, 0x2e, 0x2f, + 0x1d, 0x5d, 0xcb, 0x56, 0x44, 0x90, 0xa3, 0x78, 0x7c, 0x58, 0xc2, 0xf5, + 0x79, 0x3d, 0x99, 0x23, 0xc5, 0x2e, 0x0f, 0x90, 0xd5, 0x61, 0x76, 0x76, + 0x24, 0x2e, 0xbc, 0xfa, 0x9c, 0x7a, 0xad, 0xda, 0x0c, 0x4c, 0x21, 0x16, + 0x84, 0xfb, 0x64, 0xee, 0x18, 0x2e, 0x05, 0xe8, 0xc1, 0x88, 0x0f, 0xac, + 0x97, 0x40, 0x84, 0x94, 0x82, 0xf1, 0xf7, 0x6d, 0x1c, 0x1e, 0x24, 0x07, + 0x7f, 0x5d, 0x26, 0xea, 0xae, 0xbc, 0xc6, 0x3e, 0xe8, 0xdc, 0x4f, 0xf9, + 0x62, 0x96, 0xda, 0xc3, 0x78, 0x72, 0x3f, 0xa7, 0xe5, 0x2f, 0xb4, 0x25, + 0x00, 0x31, 0xd8, 0xa2, 0x65, 0x11, 0x1b, 0xe5, 0x2f, 0x37, 0xb1, 0x36, + 0x0c, 0x13, 0xa0, 0x42, 0x6a, 0xe5, 0x19, 0x55, 0x44, 0xd7, 0xb5, 0x1a, + 0xdd, 0x76, 0x1b, 0x2e, 0xe9, 0x10, 0x7e, 0xfc, 0xe3, 0xa4, 0x8d, 0x4a, + 0x8e, 0x02, 0x0d, 0x6e, 0xe4, 0x44, 0x6c, 0x51, 0x26, 0x7a, 0x30, 0x03, + 0x15, 0xce, 0xc7, 0x24, 0x02, 0xce, 0x7f, 0x9e, 0xed, 0x4b, 0x2a, 0xaf, + 0xab, 0x06, 0x3a, 0x7e, 0x7e, 0xed, 0x10, 0x76, 0xa6, 0x3d, 0xee, 0x11, + 0x8f, 0x7c, 0x15, 0xaa, 0x0a, 0x87, 0x8a, 0x20, 0x26, 0x21, 0xb5, 0x37, + 0xc2, 0x9d, 0xda, 0x61, 0x32, 0xf9, 0x94, 0xc0, 0xfa, 0xca, 0x43, 0x1c, + 0xb3, 0xd6, 0x97, 0x1f, 0xb6, 0xdc, 0xe4, 0xb6, 0x44, 0x00, 0x82, 0xc9, + 0x26, 0xc1, 0xb3, 0xbf, 0x91, 0x45, 0xc9, 0x4c, 0x78, 0x33, 0x9f, 0xe3, + 0xec, 0x5e, 0xe0, 0x2c, 0x0c, 0x4e, 0x88, 0x0a, 0x20, 0x43, 0x10, 0xf0, + 0x1e, 0xeb, 0xea, 0x59, 0x55, 0xa2, 0xc5, 0x7d, 0xbd, 0xcb, 0x12, 0x50, + 0x26, 0xee, 0xda, 0xf9, 0xc4, 0x33, 0xc7, 0x59, 0xbe, 0xb6, 0x7e, 0x7e, + 0x0a, 0x08, 0xd7, 0x6c, 0xab, 0x20, 0x53, 0x68, 0x67, 0x98, 0x88, 0xfe, + 0x0c, 0x10, 0x60, 0x4a, 0x36, 0xf6, 0xef, 0x94, 0xf0, 0x95, 0x53, 0x21, + 0x8d, 0x77, 0x64, 0x54, 0x55, 0x04, 0x72, 0x17, 0xc8, 0x88, 0x4e, 0x04, + 0x34, 0x0a, 0x0d, 0x6b, 0x6c, 0xc3, 0x17, 0x96, 0xb0, 0xc7, 0x3f, 0x93, + 0x1b, 0xa0, 0x92, 0xdf, 0xeb, 0x81, 0xd9, 0xd0, 0x9c, 0x73, 0xa0, 0xde, + 0x3b, 0xa9, 0x49, 0xe7, 0x92, 0x09, 0x49, 0xef, 0x84, 0xc5, 0x7f, 0x61, + 0xcd, 0x21, 0x54, 0x1b, 0xc1, 0x2a, 0xae, 0xc6, 0xf9, 0x43, 0x18, 0xb1, + 0xfc, 0x74, 0xef, 0x22, 0x63, 0x8a, 0x04, 0xbd, 0x4a, 0x2a, 0x32, 0xfc, + 0x97, 0x23, 0x2a, 0xc8, 0x0f, 0x9c, 0x0d, 0xc3, 0x50, 0xbc, 0xdd, 0xa4, + 0x30, 0x62, 0x1e, 0x45, 0x82, 0xe2, 0xd0, 0x98, 0x00, 0x06, 0x9b, 0x5b, + 0x23, 0xf5, 0x1e, 0x48, 0x16, 0x23, 0x6f, 0x3f, 0x77, 0xde, 0x34, 0x18, + 0xd4, 0xed, 0x34, 0x74, 0xac, 0xaa, 0x0f, 0x3f, 0xf1, 0x84, 0x86, 0x30, + 0xc0, 0xee, 0xf5, 0x19, 0x40, 0x36, 0xd7, 0x4c, 0x8f, 0x12, 0x48, 0x28, + 0x05, 0xfb, 0x6e, 0x8d, 0xca, 0x2f, 0xa3, 0x75, 0x39, 0x11, 0x9e, 0xda, + 0x37, 0x6f, 0x7f, 0x70, 0xff, 0x69, 0xc3, 0x6f, 0x1c, 0xe0, 0x32, 0xc0, + 0x8a, 0x9b, 0x5b, 0xac, 0xd5, 0x45, 0x39, 0xca, 0x8b, 0x8e, 0xa4, 0x66, + 0x8f, 0x75, 0x16, 0x30, 0x19, 0x06, 0x15, 0x98, 0xdc, 0xbe, 0xe2, 0x33, + 0x80, 0x66, 0x9b, 0x91, 0xdb, 0x5e, 0xf3, 0xaf, 0xdd, 0xe7, 0x5e, 0x76, + 0x4b, 0xa0, 0x39, 0x1d, 0x03, 0xf2, 0xdc, 0xe6, 0xe3, 0x1a, 0xd9, 0x91, + 0xf2, 0xa4, 0xc4, 0x29, 0xce, 0x15, 0xad, 0xaa, 0x1a, 0x8c, 0x43, 0x39, + 0x84, 0xf7, 0x41, 0xe7, 0xbf, 0x93, 0x39, 0x8e, 0xc7, 0xc1, 0xb2, 0x51, + 0xfe, 0x01, 0x15, 0x3a, 0x47, 0x0d, 0x0b, 0xde, 0xf3, 0x62, 0x85, 0xfc, + 0x20, 0x6c, 0x31, 0x02, 0x30, 0x8f, 0xf7, 0xd6, 0x71, 0x48, 0xb9, 0xe8, + 0x71, 0x7b, 0x98, 0xff, 0x62, 0x11, 0x0d, 0xbc, 0x32, 0x1b, 0x55, 0xe0, + 0x88, 0x0e, 0xe3, 0x90, 0x52, 0x6b, 0x99, 0x35, 0x59, 0x7e, 0x86, 0x62, + 0x44, 0x4a, 0xa9, 0x3b, 0xcf, 0x12, 0x8d, 0x67, 0xde, 0x42, 0xfd, 0xe5, + 0x1a, 0xc3, 0x4c, 0xea, 0x20, 0x92, 0x0f, 0xca, 0xda, 0xc7, 0x15, 0xbb, + 0x8d, 0x05, 0x58, 0xe3, 0x8c, 0x44, 0x44, 0xb9, 0x66, 0x6d, 0x78, 0x43, + 0x9e, 0xc5, 0x27, 0x7f, 0x03, 0x7f, 0x10, 0xd8, 0x8e, 0x4f, 0xc5, 0x45, + 0xc5, 0xb1, 0xf3, 0x11, 0xb4, 0xef, 0x20, 0xcb, 0xd8, 0xbb, 0x5b, 0xd6, + 0x10, 0x8d, 0x55, 0xc7, 0x3b, 0x98, 0xb5, 0x02, 0x60, 0xa4, 0x5d, 0xe7, + 0x93, 0x9c, 0x16, 0x33, 0x06, 0xc5, 0x13, 0x77, 0x9e, 0x50, 0x4a, 0x36, + 0xf1, 0x31, 0x18, 0x94, 0x54, 0x9a, 0xa9, 0x19, 0xed, 0xb9, 0xbb, 0x4b, + 0xd8, 0xb8, 0xd8, 0xc2, 0xee, 0x33, 0x1a, 0x04, 0xc5, 0x73, 0x72, 0xcc, + 0xd0, 0x6e, 0x3d, 0x4d, 0xc9, 0xba, 0x1a, 0xe6, 0x87, 0x4e, 0xb0, 0x57, + 0x98, 0xd9, 0x81, 0xb1, 0x3b, 0xd0, 0x61, 0x4a, 0xbe, 0xc6, 0xb0, 0x90, + 0xeb, 0x52, 0x62, 0x0e, 0x6e, 0xae, 0x1e, 0x95, 0xf1, 0x3f, 0x66, 0xa1, + 0x53, 0x2b, 0x1f, 0x2c, 0x9c, 0x75, 0x98, 0xc5, 0xb2, 0xce, 0xf7, 0x62, + 0xfa, 0x8d, 0x30, 0x79, 0x81, 0x30, 0x4d, 0x16, 0x92, 0x00, 0xff, 0x61, + 0xd1, 0xc5, 0x90, 0xe7, 0xd5, 0x1f, 0x99, 0x60, 0xde, 0x57, 0x83, 0x0e, + 0x37, 0xa9, 0x5b, 0x28, 0x16, 0xaf, 0x8c, 0x3a, 0xe5, 0xaa, 0x20, 0x20, + 0xfb, 0xf7, 0x65, 0x41, 0x4c, 0xae, 0x9d, 0x31, 0x9f, 0x47, 0xe6, 0x76, + 0x49, 0xf3, 0x57, 0x12, 0x2b, 0x25, 0x81, 0x56, 0xca, 0x52, 0xf7, 0x2c, + 0xd5, 0xc0, 0x49, 0x23, 0xaf, 0xd4, 0x43, 0xeb, 0x05, 0xe3, 0xa9, 0x15, + 0xf6, 0xb8, 0x06, 0xc5, 0xfc, 0xd1, 0x32, 0x46, 0x67, 0xf3, 0x08, 0x71, + 0x30, 0xc2, 0x98, 0x7a, 0x51, 0x43, 0x65, 0x6a, 0xf5, 0xe7, 0x88, 0xe0, + 0xb8, 0xf7, 0x2f, 0x43, 0x76, 0xfc, 0x58, 0x38, 0x16, 0xd1, 0x4a, 0x1a, + 0x9d, 0xea, 0x13, 0xb8, 0x11, 0x5b, 0x8d, 0x4d, 0x45, 0x56, 0xca, 0xf8, + 0xec, 0x87, 0xbd, 0x7d, 0x1f, 0x69, 0xbe, 0x5c, 0xc2, 0x27, 0x8d, 0x14, + 0xe9, 0xb3, 0xac, 0x43, 0xfc, 0xf4, 0x41, 0x0f, 0x8b, 0x83, 0x2c, 0xf9, + 0xc9, 0xfc, 0xa9, 0xc9, 0x19, 0xc1, 0xa7, 0x69, 0x25, 0x55, 0xa9, 0x92, + 0x0b, 0x4b, 0x60, 0xbb, 0x59, 0xe6, 0xd8, 0xa4, 0x4c, 0x9f, 0x19, 0x50, + 0xff, 0x49, 0xb6, 0xfa, 0x43, 0xb1, 0xde, 0x3e, 0xc2, 0x4b, 0xf9, 0x1f, + 0xe9, 0xed, 0xd6, 0x32, 0xc1, 0x14, 0xf9, 0x49, 0xb2, 0x47, 0x20, 0x94, + 0xfd, 0xe7, 0x21, 0x9f, 0x26, 0x75, 0xcb, 0xf3, 0x1b, 0x56, 0x83, 0x0e, + 0x51, 0x44, 0x6a, 0xc0, 0x82, 0x67, 0x59, 0x22, 0x0e, 0xa1, 0x37, 0x82, + 0x46, 0x27, 0xcb, 0xcd, 0xd5, 0xc6, 0x70, 0x46, 0x88, 0xf5, 0x03, 0x5f, + 0x1d, 0xfe, 0xeb, 0xa8, 0xe0, 0xd5, 0x17, 0x84, 0x2a, 0xef, 0x10, 0x2e, + 0x0a, 0x5b, 0x98, 0x83, 0xc0, 0x3b, 0x71, 0x7c, 0xfe, 0xeb, 0xc2, 0x42, + 0x9b, 0x13, 0xce, 0xbd, 0x27, 0xdd, 0x23, 0x00, 0x1c, 0x6c, 0x07, 0xbd, + 0xf0, 0x7b, 0xf1, 0x17, 0x11, 0xa7, 0x7b, 0xce, 0xbd, 0x4f, 0x00, 0x4f, + 0xa0, 0x1b, 0xa9, 0xd3, 0xc8, 0x22, 0x6a, 0xe7, 0x1b, 0x4b, 0x88, 0x73, + 0x94, 0x58, 0xe1, 0x01, 0xba, 0x24, 0x53, 0x68, 0x97, 0xbe, 0x91, 0xd3, + 0x22, 0xec, 0x5a, 0xb4, 0x12, 0x1b, 0x2e, 0x33, 0xeb, 0xf5, 0x31, 0xe9, + 0x90, 0xb4, 0xec, 0x7b, 0x83, 0xe5, 0x57, 0x93, 0x15, 0x02, 0xfa, 0xb2, + 0x8d, 0xc8, 0xab, 0xa8, 0xae, 0x20, 0xd2, 0xf1, 0x7e, 0xf8, 0xcc, 0x46, + 0x24, 0x19, 0xf9, 0x17, 0xd7, 0x6b, 0xe0, 0x2e, 0x22, 0xe0, 0x13, 0x35, + 0xf9, 0xfa, 0x21, 0x86, 0x97, 0x6f, 0x69, 0xf1, 0xbc, 0xff, 0x5f, 0x75, + 0xab, 0x50, 0x26, 0xa2, 0x53, 0x62, 0x0c, 0x4d, 0x4b, 0xa6, 0x48, 0xf1, + 0x2b, 0xfa, 0xff, 0x68, 0x85, 0x12, 0xa5, 0x6b, 0xf6, 0xee, 0xd3, 0xec, + 0x37, 0xac, 0xe2, 0xb3, 0x1f, 0xf7, 0xd9, 0xd2, 0x03, 0x1b, 0xf1, 0x9c, + 0x51, 0x03, 0xa3, 0xa0, 0xbb, 0x0e, 0xf4, 0x2a, 0x8c, 0xea, 0xc3, 0x99, + 0x6a, 0x5c, 0x85, 0x9f, 0xb9, 0xfa, 0x7e, 0xc0, 0xed, 0xb8, 0xdb, 0x2a, + 0x72, 0xb4, 0x98, 0xad, 0x6e, 0xbe, 0xe3, 0x61, 0x01, 0xd7, 0xa7, 0xfb, + 0x54, 0xd4, 0x97, 0xa2, 0xd7, 0x2f, 0x66, 0xdd, 0xa9, 0x1d, 0x7c, 0x95, + 0x49, 0x73, 0x50, 0x12, 0x71, 0xf2, 0x3d, 0x1e, 0xad, 0x12, 0xdb, 0xa0, + 0x47, 0x37, 0xf3, 0x72, 0x2c, 0x32, 0x58, 0xe2, 0x38, 0x6d, 0x34, 0x9d, + 0x88, 0x5f, 0xb9, 0xf1, 0x8a, 0x96, 0xe8, 0x1f, 0x85, 0x7c, 0xfa, 0x71, + 0xb8, 0x0a, 0x24, 0xa9, 0xc3, 0xf8, 0xfa, 0x65, 0x87, 0x1b, 0x4b, 0xb9, + 0xf2, 0xf7, 0x2e, 0xb1, 0x5e, 0x30, 0xc2, 0x6b, 0x6c, 0x0a, 0x17, 0x3f, + 0x77, 0x0c, 0xcd, 0x30, 0x73, 0x40, 0xa1, 0xdc, 0x18, 0xd3, 0xd5, 0x56, + 0x07, 0x8e, 0x72, 0xee, 0xea, 0x85, 0x39, 0x0d, 0xa0, 0x23, 0x58, 0x22, + 0x6b, 0x2a, 0x0b, 0x3d, 0xf1, 0xed, 0x5e, 0x96, 0x69, 0x84, 0x37, 0xd9, + 0x99, 0x80, 0x89, 0xcb, 0x59, 0x68, 0xd4, 0x2e, 0x23, 0xdb, 0xfd, 0x0d, + 0xe7, 0xab, 0xc5, 0x98, 0x05, 0x9d, 0xee, 0x8e, 0x5d, 0xfa, 0x2d, 0x0a, + 0xad, 0x6f, 0x80, 0x52, 0xf0, 0x69, 0x91, 0xfa, 0xc3, 0x90, 0x89, 0x11, + 0xd1, 0xe4, 0xa2, 0x1e, 0x75, 0xb8, 0x6e, 0xc4, 0x1a, 0x4d, 0x5f, 0xb6, + 0x39, 0x21, 0xcb, 0xeb, 0xdb, 0x3d, 0x4f, 0x9a, 0x52, 0x4c, 0x9c, 0x51, + 0x52, 0x28, 0xdc, 0x2a, 0xa1, 0xdc, 0xdc, 0x06, 0xae, 0xe6, 0x6f, 0x91, + 0xb8, 0x5b, 0xb2, 0xad, 0x7d, 0xce, 0xec, 0xba, 0xdd, 0xf3, 0x0e, 0x94, + 0xe9, 0x78, 0x72, 0x25, 0x15, 0x21, 0x2c, 0x6b, 0xca, 0xee, 0xb8, 0xfc, + 0x10, 0x73, 0x67, 0x73, 0xbd, 0x4f, 0x6c, 0x06, 0x90, 0xd7, 0xbc, 0xd7, + 0x7b, 0xf8, 0x25, 0xa4, 0x68, 0x94, 0xd4, 0xe5, 0x56, 0xf0, 0xa1, 0xb5, + 0x9f, 0xf7, 0xa5, 0x3a, 0xd8, 0x30, 0xf1, 0xf9, 0x4a, 0x62, 0x14, 0x62, + 0x80, 0x34, 0xcc, 0xbf, 0x8a, 0xbc, 0x86, 0xa7, 0x28, 0x83, 0x21, 0x98, + 0xb1, 0xc9, 0xdb, 0x1a, 0x2d, 0xd0, 0xe9, 0x9e, 0x3c, 0x21, 0xee, 0x6e, + 0x1a, 0x54, 0x25, 0x11, 0x26, 0x9a, 0xb7, 0x6b, 0x44, 0xb8, 0x46, 0xbd, + 0xf0, 0x25, 0x08, 0xbc, 0x75, 0x19, 0xb3, 0x1a, 0xd3, 0x7f, 0x79, 0x4c, + 0x81, 0x23, 0x42, 0x66, 0xb8, 0x64, 0x13, 0xdf, 0x8e, 0xdc, 0xd6, 0x8f, + 0x07, 0xa1, 0xe1, 0x93, 0x87, 0x9f, 0x8f, 0x37, 0x10, 0xba, 0x28, 0x1e, + 0xde, 0xa7, 0xf3, 0x87, 0x89, 0xcd, 0x89, 0xa2, 0x47, 0x92, 0x18, 0x87, + 0x29, 0x69, 0xc1, 0x88, 0x32, 0xdd, 0x42, 0xae, 0x18, 0xb2, 0x37, 0x5a, + 0xf0, 0x69, 0x9d, 0xce, 0x08, 0xfe, 0x19, 0xcb, 0x90, 0x3f, 0x4f, 0x43, + 0x76, 0x57, 0xb6, 0x25, 0xf4, 0x85, 0x18, 0xa0, 0xe9, 0x79, 0x1a, 0xea, + 0xcb, 0x66, 0x0a, 0x92, 0x1d, 0x82, 0xca, 0x50, 0x63, 0xb7, 0x14, 0x45, + 0xfe, 0xf8, 0x21, 0xa3, 0x3e, 0xe1, 0x5d, 0xd8, 0x6d, 0xdc, 0x7f, 0xb8, + 0x77, 0x08, 0x8b, 0x4b, 0x54, 0x2a, 0xa8, 0xd5, 0xa4, 0xc3, 0x13, 0x80, + 0x1e, 0x06, 0xc3, 0xc4, 0x5e, 0x61, 0xe1, 0x9f, 0x5f, 0x70, 0x9e, 0x77, + 0x30, 0x9a, 0x35, 0x36, 0x32, 0x28, 0x8f, 0xa8, 0x8c, 0x30, 0xbe, 0x60, + 0xdc, 0x75, 0x98, 0x54, 0x94, 0x2c, 0xb8, 0x1b, 0xd4, 0xa6, 0x6a, 0x73, + 0xab, 0x7a, 0x27, 0x7d, 0xef, 0x98, 0x9d, 0xe5, 0xd0, 0x8e, 0x2a, 0x08, + 0x28, 0xef, 0x1b, 0x21, 0xe2, 0xb2, 0xca, 0x10, 0x5a, 0x48, 0x21, 0xee, + 0x47, 0x9f, 0x05, 0xf7, 0x7e, 0x03, 0xf2, 0x39, 0x67, 0xb2, 0x3f, 0xec, + 0xcc, 0x85, 0x1e, 0xfa, 0x51, 0x54, 0xbb, 0x4a, 0x21, 0xda, 0x47, 0x0b, + 0x01, 0x59, 0x27, 0xb7, 0xf8, 0x49, 0x89, 0x59, 0xa6, 0xae, 0xc0, 0x96, + 0x51, 0xa9, 0xbb, 0xe2, 0xe1, 0xf0, 0x28, 0x50, 0x9a, 0x50, 0x7d, 0x3b, + 0x08, 0x77, 0xd3, 0xd4, 0xa4, 0x5a, 0x75, 0x89, 0xd0, 0x27, 0x4e, 0xba, + 0x4e, 0x62, 0x6f, 0x74, 0x36, 0x51, 0x26, 0x6b, 0xd1, 0xfa, 0x35, 0x41, + 0x29, 0x59, 0x91, 0xe1, 0xcd, 0x00, 0xc9, 0xfb, 0x85, 0xfc, 0xd0, 0xd6, + 0xc7, 0xc5, 0x5c, 0x5c, 0x53, 0x3e, 0xb2, 0x1e, 0x9a, 0x0f, 0xe5, 0xb7, + 0xdb, 0x26, 0xa3, 0x02, 0x6c, 0xfa, 0xe0, 0x2d, 0xf8, 0x70, 0x37, 0x1a, + 0xdd, 0x11, 0x74, 0xda, 0x16, 0x38, 0x8a, 0xab, 0x2f, 0x4e, 0x7f, 0x8b, + 0xd6, 0x53, 0x13, 0x7e, 0x4b, 0x65, 0x08, 0x11, 0xf8, 0x14, 0x39, 0x94, + 0x45, 0xeb, 0x60, 0xd8, 0x63, 0xb4, 0x55, 0x06, 0x7a, 0xb8, 0xa1, 0x02, + 0x88, 0xe5, 0x82, 0x09, 0x07, 0xa3, 0x62, 0x33, 0x65, 0x25, 0x1d, 0x85, + 0x6c, 0xc8, 0x77, 0xbe, 0xfd, 0xb7, 0xdc, 0x93, 0x3d, 0xc0, 0x49, 0x9c, + 0x8a, 0x3d, 0x47, 0xd8, 0x06, 0xb1, 0x97, 0x03, 0xa1, 0x87, 0xbc, 0x89, + 0x34, 0x64, 0xda, 0xe1, 0x6f, 0xa1, 0x27, 0xf2, 0xa2, 0x14, 0xed, 0x22, + 0xd8, 0x16, 0x79, 0xef, 0x9c, 0xfe, 0x4d, 0xcc, 0x12, 0x80, 0x91, 0x85, + 0xa4, 0x01, 0xf8, 0x29, 0xc1, 0x1f, 0x55, 0x51, 0x1d, 0x45, 0xe6, 0x05, + 0x0e, 0xd2, 0xcc, 0x7f, 0x4d, 0x54, 0xb3, 0x1c, 0x0d, 0xb6, 0x7b, 0x05, + 0xca, 0x19, 0x76, 0x8c, 0xf0, 0xce, 0x6b, 0x7f, 0xe2, 0xc4, 0xb8, 0xcf, + 0x72, 0xda, 0xcf, 0xe0, 0x35, 0x0c, 0xb2, 0xa7, 0x0b, 0xa1, 0xcf, 0x45, + 0x00, 0xc2, 0xff, 0x61, 0x40, 0x65, 0x4d, 0x8b, 0x36, 0xfb, 0x33, 0xae, + 0xef, 0x6e, 0xaf, 0x14, 0xd1, 0x69, 0x2f, 0xda, 0x4d, 0x9c, 0x7b, 0x61, + 0x33, 0xe5, 0x1b, 0x02, 0xff, 0xa3, 0xa1, 0x17, 0xa7, 0xe8, 0xdb, 0x53, + 0xf5, 0x61, 0x1c, 0x07, 0x4b, 0x74, 0x25, 0xbd, 0x79, 0xcf, 0x8d, 0x1d, + 0x06, 0x5c, 0xea, 0xe9, 0x7e, 0xff, 0x00, 0xba, 0xb1, 0x37, 0x86, 0x0b, + 0xbd, 0x04, 0x7e, 0xdb, 0x10, 0xa3, 0x65, 0x27, 0x27, 0x17, 0x9a, 0x95, + 0xef, 0x10, 0x34, 0xfc, 0xf3, 0xb2, 0x70, 0xb8, 0x40, 0x83, 0x0d, 0x9f, + 0x89, 0x97, 0x93, 0x01, 0x2c, 0xaf, 0xea, 0xcb, 0x47, 0xaf, 0x42, 0x6d, + 0x77, 0xa4, 0x33, 0x4b, 0x60, 0x07, 0xaa, 0xd6, 0x2c, 0x1f, 0x23, 0x64, + 0x8f, 0xcc, 0x3e, 0xff, 0xb0, 0x66, 0x2f, 0x3d, 0xf9, 0xff, 0x7d, 0x30, + 0x47, 0x6b, 0xd5, 0x97, 0x37, 0x42, 0xbb, 0xdf, 0xbe, 0x3a, 0x9f, 0x8a, + 0xb8, 0xbc, 0x1b, 0x9b, 0x9a, 0x7c, 0x35, 0xa8, 0x83, 0xad, 0x20, 0xe3, + 0x0d, 0xd5, 0x52, 0x91, 0x6d, 0x2d, 0x9b, 0x9e, 0xa0, 0xc1, 0xa9, 0x36, + 0xb2, 0x23, 0xdc, 0x28, 0x85, 0x0d, 0x43, 0xc5, 0x6e, 0x92, 0xfd, 0x23, + 0x6e, 0x62, 0x59, 0x8a, 0x22, 0x38, 0x55, 0x50, 0xf4, 0xb5, 0x8e, 0x73, + 0x85, 0xe6, 0xca, 0x9a, 0x47, 0xa3, 0x1b, 0x8c, 0x9b, 0x6e, 0x03, 0xd7, + 0xf4, 0x1e, 0x0a, 0xd4, 0x0b, 0x2f, 0xef, 0xc9, 0x3c, 0x73, 0x52, 0x5a, + 0x90, 0x00, 0xa5, 0x69, 0x3f, 0x32, 0x5f, 0x72, 0x32, 0x22, 0xfd, 0xa9, + 0x4a, 0x1f, 0xcb, 0xa1, 0x67, 0x85, 0xbd, 0x0a, 0xa9, 0xf9, 0x40, 0xcb, + 0x57, 0x0c, 0xfc, 0x39, 0x98, 0xd1, 0x26, 0x44, 0xd4, 0xa6, 0x73, 0x6e, + 0x64, 0xc0, 0x0f, 0xd3, 0xc0, 0x35, 0xd4, 0x82, 0xb7, 0xed, 0x30, 0x76, + 0x08, 0xa9, 0x17, 0x3e, 0x52, 0xec, 0xb8, 0x6f, 0x00, 0x10, 0x08, 0x23, + 0xe4, 0xcd, 0x5d, 0xc5, 0x04, 0xe6, 0xdd, 0xa4, 0x6a, 0xf4, 0x26, 0x13, + 0xf6, 0x41, 0x04, 0x3c, 0xfc, 0xd7, 0x13, 0xa5, 0x75, 0x24, 0x94, 0x52, + 0x2d, 0xd8, 0x41, 0xf2, 0x1a, 0x74, 0x6d, 0x2d, 0xf1, 0xd5, 0x8f, 0x5d, + 0x96, 0x11, 0xab, 0x85, 0xce, 0xc7, 0x29, 0x7f, 0x28, 0xf7, 0x08, 0x3a, + 0xdd, 0x22, 0x4c, 0x95, 0xf4, 0xf7, 0xd9, 0x79, 0x1a, 0xea, 0xb4, 0x36, + 0xcd, 0x50, 0x9f, 0xf6, 0x21, 0xe0, 0x8d, 0x57, 0x04, 0x77, 0x78, 0xde, + 0xdf, 0x05, 0x52, 0x67, 0x32, 0x5a, 0xdc, 0xa2, 0xa8, 0xc5, 0xe3, 0xcd, + 0xe8, 0x30, 0x26, 0x5d, 0x65, 0x18, 0xe1, 0x95, 0x0e, 0x3a, 0xaa, 0xb6, + 0xc0, 0x59, 0x7d, 0xed, 0xcf, 0x7f, 0xc4, 0x6d, 0x9f, 0xce, 0x92, 0x22, + 0x23, 0x80, 0xcc, 0x96, 0xd1, 0x70, 0xd6, 0x0a, 0x49, 0x55, 0x02, 0x5e, + 0xcd, 0xc9, 0x43, 0xa5, 0xd2, 0xe4, 0x2f, 0x1f, 0xd0, 0x7d, 0x99, 0x53, + 0xbe, 0xf7, 0xc9, 0x81, 0xc2, 0x95, 0x1b, 0x37, 0x8c, 0x33, 0xe4, 0x65, + 0x0c, 0x9a, 0x83, 0xc6, 0x83, 0xf6, 0xcb, 0x7f, 0x18, 0x38, 0x30, 0x68, + 0xcd, 0x19, 0x60, 0xed, 0x54, 0x0d, 0x23, 0xab, 0x94, 0x4c, 0x95, 0xb0, + 0x7a, 0xb2, 0x08, 0x85, 0xf9, 0x82, 0x8c, 0xf1, 0xa9, 0x4c, 0xb7, 0x8e, + 0x76, 0x0f, 0xab, 0x3a, 0x94, 0x5c, 0xb2, 0x90, 0xdf, 0x54, 0x3a, 0xe0, + 0xe7, 0x8e, 0x65, 0x31, 0x09, 0x2d, 0xee, 0x30, 0x18, 0xf3, 0x50, 0x1b, + 0x7f, 0x4d, 0x9e, 0x1b, 0xed, 0xac, 0x0a, 0xc9, 0x83, 0xe9, 0x17, 0x54, + 0x70, 0x99, 0xcd, 0xde, 0x44, 0xd3, 0xff, 0x8e, 0xdf, 0xf5, 0x7d, 0x65, + 0x7b, 0x58, 0x99, 0x42, 0xb4, 0x8a, 0x94, 0x26, 0x1a, 0x4f, 0x8d, 0x92, + 0xc1, 0x91, 0xb5, 0x95, 0x88, 0x99, 0x58, 0x53, 0x29, 0x73, 0x41, 0x97, + 0x19, 0x36, 0x28, 0xf5, 0x3b, 0x46, 0x83, 0xfa, 0x3f, 0x01, 0x28, 0x8f, + 0x4b, 0xe6, 0x3e, 0x61, 0xec, 0x20, 0xb3, 0xc6, 0xf3, 0x80, 0x03, 0x75, + 0xf5, 0x64, 0x77, 0x05, 0x99, 0x6a, 0x8f, 0xff, 0xeb, 0x2e, 0x7e, 0x5a, + 0x8d, 0xab, 0xbf, 0x6e, 0x3d, 0x55, 0x55, 0x1b, 0xd9, 0x3d, 0xd2, 0x9e, + 0x3e, 0xbb, 0x24, 0x80, 0x71, 0x75, 0xf8, 0xdb, 0xa7, 0xb9, 0x11, 0x06, + 0x92, 0x19, 0x27, 0xe3, 0x0c, 0x00, 0xe5, 0x7b, 0x82, 0x63, 0x20, 0x87, + 0x8a, 0xc6, 0xf3, 0x2b, 0x03, 0xb5, 0x3e, 0x28, 0x67, 0xac, 0x4e, 0x85, + 0xbe, 0xe2, 0x99, 0xe6, 0x01, 0x0b, 0x4c, 0xf2, 0xc8, 0xc9, 0x64, 0x55, + 0xef, 0xd3, 0x53, 0x74, 0x4a, 0x0c, 0xb9, 0xb1, 0x6c, 0xfc, 0xc5, 0x47, + 0x6f, 0x6e, 0x9c, 0xc4, 0x9a, 0x96, 0x80, 0x41, 0x9e, 0x44, 0xa6, 0x30, + 0x2e, 0x49, 0x6c, 0xd4, 0x8e, 0x10, 0x2d, 0x37, 0x1e, 0x8e, 0x83, 0xd6, + 0x12, 0x2b, 0x8d, 0xf9, 0x18, 0xb3, 0xa9, 0xcf, 0xe1, 0x81, 0x54, 0x18, + 0xb1, 0xbd, 0xaf, 0x95, 0xe5, 0x2f, 0x5a, 0xb3, 0x8b, 0xc4, 0xeb, 0xce, + 0x9d, 0x92, 0x6e, 0xd1, 0xdd, 0x4e, 0xbe, 0x58, 0x46, 0x09, 0x23, 0xee, + 0xfa, 0x77, 0x8f, 0x44, 0xc2, 0x4e, 0xb9, 0x41, 0x9d, 0xf7, 0x08, 0x46, + 0xcf, 0xd1, 0xc0, 0xe2, 0x01, 0x96, 0x72, 0x2f, 0x25, 0xd2, 0x6c, 0xda, + 0x51, 0x95, 0x76, 0xab, 0x74, 0x15, 0xb1, 0x5d, 0xe5, 0x5b, 0x19, 0x46, + 0x8c, 0x4b, 0xb0, 0xe4, 0x60, 0xc9, 0x20, 0x42, 0xd3, 0x47, 0x65, 0xfd, + 0xe6, 0xaa, 0x89, 0xe9, 0x62, 0x14, 0xd6, 0xad, 0x67, 0xf5, 0xa0, 0xfb, + 0x96, 0x64, 0x84, 0x07, 0x15, 0x88, 0xe8, 0xde, 0x11, 0x5a, 0x15, 0x01, + 0x34, 0x7f, 0x41, 0x03, 0xbe, 0xf9, 0x3f, 0xf8, 0x7a, 0x89, 0xb0, 0xa7, + 0xcb, 0x81, 0xc8, 0x1e, 0x39, 0xdb, 0xb3, 0x24, 0x85, 0x29, 0xeb, 0x86, + 0xf5, 0xf7, 0xf5, 0xce, 0xf4, 0x66, 0x9a, 0x44, 0x15, 0x41, 0xa5, 0xe7, + 0x71, 0x1f, 0x1d, 0xc9, 0x68, 0xee, 0xdb, 0x01, 0x24, 0x85, 0x73, 0xc0, + 0x2a, 0xd2, 0x5c, 0x8a, 0xed, 0xee, 0xa3, 0x4a, 0xe8, 0x10, 0xe6, 0xf1, + 0x7a, 0x33, 0xa3, 0x80, 0x29, 0x69, 0x77, 0x6a, 0xb3, 0xc3, 0x19, 0x49, + 0xc5, 0xc1, 0xf7, 0xbd, 0xc4, 0x50, 0x69, 0x4c, 0xbf, 0x1a, 0xdb, 0x37, + 0x04, 0xca, 0x70, 0x02, 0xa0, 0x43, 0xb2, 0x1b, 0x66, 0x99, 0x63, 0x84, + 0xe6, 0x91, 0x61, 0xff, 0x2b, 0x00, 0xa4, 0x57, 0xc3, 0xc7, 0xfe, 0xb9, + 0x82, 0xd6, 0x7b, 0xc3, 0xe9, 0xe3, 0x48, 0x5b, 0x27, 0xa6, 0xd3, 0x67, + 0x2b, 0xf7, 0x78, 0x80, 0x04, 0xfe, 0xd1, 0xb2, 0xe7, 0xce, 0x5b, 0xec, + 0x8b, 0xaf, 0x4e, 0x2f, 0x6a, 0xc2, 0x25, 0x60, 0x79, 0x31, 0x04, 0x74, + 0x38, 0x0b, 0x0b, 0xda, 0xbf, 0x63, 0x6f, 0x8b, 0x90, 0x45, 0xeb, 0xf7, + 0x3f, 0x7a, 0xdd, 0x0e, 0x29, 0x82, 0x23, 0xb5, 0x86, 0x24, 0xf1, 0x38, + 0xb0, 0x89, 0x16, 0x74, 0x16, 0xfb, 0x46, 0xbb, 0x90, 0xa3, 0xb7, 0x23, + 0xd3, 0x8c, 0x5c, 0xdb, 0xa6, 0x43, 0x83, 0xdd, 0xeb, 0x69, 0x19, 0xce, + 0x9e, 0x9e, 0x3e, 0x0a, 0x42, 0x7e, 0xfc, 0x1c, 0x92, 0x45, 0xf5, 0x10, + 0x0b, 0xa9, 0x58, 0x05, 0xce, 0xdc, 0x2a, 0x41, 0x45, 0x3f, 0x27, 0x1a, + 0xc8, 0x61, 0x49, 0x53, 0x8e, 0x58, 0x88, 0x8a, 0x45, 0xe7, 0x20, 0xbc, + 0x33, 0xf5, 0x8e, 0x7e, 0x32, 0x8e, 0xa2, 0x15, 0xb4, 0xf4, 0x0e, 0x3f, + 0x98, 0x9b, 0x31, 0x13, 0x54, 0xd0, 0x55, 0xb2, 0x79, 0x8d, 0x47, 0x72, + 0xbf, 0x69, 0x83, 0x24, 0x7f, 0x68, 0x10, 0x39, 0x3e, 0x78, 0x11, 0xee, + 0x8a, 0xfe, 0x1b, 0x47, 0x9f, 0xf8, 0xa4, 0xf6, 0xa1, 0x0e, 0xb8, 0xbd, + 0x15, 0xc8, 0xd7, 0x43, 0x42, 0xc8, 0xca, 0xbc, 0xa4, 0x3c, 0xb3, 0x71, + 0x1d, 0x1a, 0xc6, 0x55, 0xdc, 0x09, 0xa3, 0x22, 0x62, 0x94, 0x0c, 0x35, + 0xc5, 0xab, 0x07, 0x69, 0xb2, 0x18, 0x6b, 0x70, 0xbf, 0xbc, 0x52, 0xaf, + 0xce, 0x47, 0xd9, 0xd2, 0x61, 0x2d, 0x10, 0xc9, 0x84, 0x23, 0x43, 0xc6, + 0xb4, 0x9d, 0x40, 0xc9, 0x8a, 0xc6, 0x07, 0x60, 0x9a, 0x1d, 0x8c, 0xcc, + 0x5b, 0x7f, 0x85, 0x7a, 0x91, 0x5b, 0x6c, 0x0a, 0x8c, 0xfe, 0x96, 0x13, + 0x0e, 0x54, 0xce, 0x17, 0x05, 0x23, 0xec, 0xd9, 0xf8, 0xf0, 0xca, 0x26, + 0xae, 0xe6, 0xc9, 0x73, 0x8d, 0x9c, 0x03, 0xea, 0x70, 0x75, 0x7a, 0xf7, + 0xf1, 0xd4, 0x5f, 0x82, 0x5a, 0xc1, 0x29, 0xbe, 0x74, 0xba, 0xcb, 0x78, + 0x75, 0xc9, 0x2f, 0xf2, 0x6c, 0x57, 0x61, 0xd1, 0x93, 0x89, 0x13, 0xcf, + 0x00, 0x1f, 0xdb, 0x3b, 0xa2, 0xed, 0xf0, 0x60, 0xaf, 0xa8, 0x7d, 0xcc, + 0x24, 0x92, 0xf0, 0x26, 0x4d, 0x7e, 0x7b, 0x7b, 0x21, 0x89, 0x91, 0xf2, + 0x1b, 0xcc, 0x80, 0xf9, 0x54, 0xbe, 0xf6, 0x09, 0x04, 0xb8, 0x10, 0x54, + 0x34, 0xf3, 0xd1, 0x53, 0xd7, 0x4b, 0x9b, 0x58, 0x67, 0x69, 0xc2, 0x72, + 0xdc, 0xd4, 0x3b, 0x11, 0x3c, 0xd0, 0xf4, 0x7e, 0xa5, 0x05, 0x85, 0xb7, + 0x1b, 0x70, 0x83, 0xc5, 0xae, 0xc4, 0x72, 0x20, 0x4b, 0x20, 0x63, 0xd1, + 0x4b, 0xd7, 0x60, 0x77, 0x4a, 0x2c, 0xbb, 0x47, 0x7c, 0xf0, 0x5b, 0xf6, + 0x75, 0x32, 0xdd, 0xcc, 0x64, 0xf7, 0x8a, 0xf5, 0xa2, 0x90, 0xed, 0x4b, + 0x07, 0x80, 0xf5, 0x74, 0x14, 0x44, 0x11, 0x32, 0xa8, 0x68, 0x9c, 0x9d, + 0xc8, 0x7d, 0x83, 0xfb, 0x62, 0x5d, 0x61, 0xda, 0x9e, 0x37, 0x81, 0x18, + 0xf5, 0x14, 0x8c, 0xc0, 0x62, 0x5e, 0xd4, 0x4c, 0xe1, 0x2e, 0x0c, 0x0b, + 0xfc, 0x6f, 0x93, 0x84, 0xf9, 0x6d, 0x08, 0x9f, 0xc6, 0x98, 0x75, 0x10, + 0xee, 0x37, 0x70, 0xc5, 0x95, 0x04, 0xc2, 0x15, 0x6d, 0xd3, 0x69, 0x69, + 0x78, 0x7e, 0xb5, 0x55, 0xa3, 0x87, 0x3c, 0xbb, 0x48, 0xd1, 0x57, 0x12, + 0xb3, 0xd0, 0xd1, 0x33, 0x47, 0x9b, 0x89, 0x2b, 0x3e, 0xd9, 0x31, 0x51, + 0xf1, 0x4f, 0x73, 0xd7, 0x31, 0xf4, 0xb9, 0x15, 0x89, 0xc6, 0xfd, 0xe4, + 0x25, 0x15, 0xc3, 0x28, 0x84, 0x5b, 0x32, 0x03, 0x77, 0xee, 0x01, 0x9e, + 0xbf, 0xf8, 0x7d, 0xa4, 0x4a, 0x6c, 0x39, 0x8d, 0x18, 0xee, 0x61, 0xfa, + 0xfe, 0xa5, 0x5f, 0xbd, 0xde, 0xb1, 0x3d, 0x5e, 0x1f, 0xd3, 0x49, 0x72, + 0x18, 0xb4, 0xb7, 0x87, 0x9b, 0x77, 0x7d, 0x94, 0xa2, 0xe0, 0x72, 0x4f, + 0x69, 0xa1, 0x27, 0x15, 0x0f, 0x91, 0x6a, 0xf6, 0x19, 0x7f, 0x9d, 0xf9, + 0x67, 0x7d, 0x75, 0x15, 0xac, 0x03, 0xba, 0x03, 0xb5, 0x72, 0xa9, 0xcd, + 0x5a, 0x8a, 0x99, 0xe3, 0x4a, 0x47, 0x48, 0x7c, 0x69, 0xb9, 0x79, 0x26, + 0x69, 0xa3, 0x8a, 0x19, 0x88, 0xfa, 0xba, 0x5b, 0x01, 0x08, 0x15, 0x81, + 0xf4, 0x73, 0xd5, 0x44, 0x95, 0x44, 0xfe, 0x5b, 0x01, 0x59, 0x25, 0xf2, + 0x8b, 0x3e, 0x96, 0x77, 0xe9, 0x6e, 0xc0, 0x04, 0xda, 0x2f, 0x28, 0xe5, + 0x94, 0xea, 0x2a, 0xcb, 0x45, 0x74, 0xeb, 0xea, 0x69, 0x9b, 0x76, 0xe1, + 0xed, 0x75, 0xe2, 0xfa, 0x51, 0x41, 0x57, 0xfe, 0x02, 0xaa, 0x9f, 0xda, + 0x41, 0xfa, 0xdd, 0x99, 0x79, 0xfc, 0x23, 0x7f, 0x8e, 0x2c, 0xee, 0x43, + 0x16, 0xa2, 0x9f, 0x7a, 0x6a, 0x68, 0xd4, 0x51, 0xe4, 0x84, 0x52, 0x50, + 0xf6, 0x33, 0x79, 0x30, 0x7e, 0xcd, 0xf0, 0xdc, 0x8a, 0xa6, 0x56, 0x12, + 0x7d, 0x35, 0xcb, 0x94, 0x1e, 0x5f, 0x88, 0x23, 0xe8, 0x92, 0x47, 0x5f, + 0xbd, 0xe1, 0xf4, 0xf8, 0x94, 0x88, 0xf7, 0xf3, 0x56, 0x2d, 0xb9, 0x9d, + 0xc2, 0xaf, 0x01, 0x29, 0xd5, 0xc7, 0x24, 0x2b, 0x65, 0xcc, 0xe6, 0x52, + 0x52, 0xca, 0x92, 0x6b, 0x9e, 0x74, 0x81, 0x1b, 0xb6, 0x63, 0xb6, 0xb0, + 0x3c, 0x11, 0xf5, 0xb0, 0xad, 0x5f, 0x1d, 0x7e, 0xda, 0xba, 0x29, 0x17, + 0xc2, 0x3c, 0xfd, 0x2d, 0x55, 0x43, 0xd9, 0xc1, 0x68, 0xc3, 0xb4, 0x60, + 0x87, 0x11, 0x24, 0x1a, 0x81, 0x59, 0xcd, 0x62, 0xc0, 0x37, 0x5e, 0x44, + 0xbe, 0xd0, 0x08, 0xb0, 0xe3, 0x90, 0x94, 0xd4, 0x35, 0xaa, 0x8d, 0x6d, + 0xaa, 0x08, 0xdd, 0x31, 0x95, 0x61, 0x7e, 0x7a, 0x13, 0x03, 0x23, 0x97, + 0xde, 0xa0, 0x33, 0x1d, 0x17, 0x17, 0x58, 0xba, 0xf3, 0x1a, 0x6c, 0xa7, + 0x7b, 0xff, 0x9a, 0x2a, 0x70, 0x3d, 0x6e, 0xf9, 0x5d, 0x30, 0x54, 0xe8, + 0x58, 0xb2, 0xe5, 0x84, 0x05, 0x9c, 0x43, 0xca, 0x6e, 0x56, 0x57, 0x8d, + 0x2f, 0x6a, 0xaf, 0xa5, 0x3b, 0xba, 0x19, 0x0a, 0x25, 0x92, 0x4c, 0xcd, + 0x20, 0x0a, 0x1c, 0xe2, 0x3d, 0x33, 0x95, 0xe4, 0x43, 0x92, 0xfa, 0x47, + 0xb9, 0x4e, 0x3c, 0x52, 0x47, 0xf7, 0x5f, 0xf8, 0xc5, 0x83, 0xd5, 0xc8, + 0x41, 0xcd, 0xd0, 0x76, 0x0f, 0x4f, 0x63, 0x2e, 0x90, 0xe5, 0xfd, 0xc2, + 0x34, 0x82, 0x7c, 0x90, 0x3e, 0x98, 0x6d, 0x18, 0xc9, 0x6d, 0xb4, 0xcd, + 0x38, 0x81, 0x89, 0xb7, 0xd1, 0x82, 0x9c, 0xb7, 0xb1, 0x92, 0x1e, 0x45, + 0x71, 0x25, 0xd3, 0x99, 0xcf, 0x35, 0xa3, 0x8e, 0xf1, 0x2f, 0x11, 0x90, + 0x3f, 0x23, 0x6c, 0xdc, 0x55, 0x7c, 0x18, 0x01, 0xaf, 0x40, 0xf8, 0x42, + 0xb2, 0x3b, 0x67, 0xe9, 0xac, 0xbc, 0x76, 0x9a, 0xf4, 0x11, 0x35, 0x71, + 0x7d, 0x7f, 0xdf, 0xa0, 0xf2, 0xd5, 0xa7, 0x81, 0x48, 0xa6, 0xf7, 0xf8, + 0xa1, 0xbc, 0x95, 0x78, 0x69, 0x25, 0xe7, 0x9a, 0xb6, 0x4f, 0xfb, 0x64, + 0x86, 0xb3, 0x67, 0x2c, 0x78, 0x75, 0x0a, 0x3b, 0x5a, 0xc8, 0xa2, 0x25, + 0x7f, 0xce, 0xbd, 0xcc, 0x54, 0x23, 0xe7, 0x58, 0x73, 0xee, 0x8e, 0x30, + 0x2c, 0x27, 0x01, 0xa3, 0x3a, 0x8b, 0x64, 0xa8, 0x3c, 0x56, 0x01, 0x63, + 0x91, 0xb4, 0xef, 0xb7, 0x07, 0x2e, 0xc5, 0xcd, 0xfd, 0xe5, 0x62, 0x42, + 0x2b, 0xb3, 0x9e, 0x38, 0x88, 0xd4, 0x74, 0x4e, 0x96, 0xeb, 0x74, 0xc1, + 0x48, 0x9b, 0xe3, 0xbd, 0xcf, 0x5d, 0x27, 0x2c, 0xbe, 0xf1, 0x43, 0xfe, + 0x7c, 0x96, 0x81, 0xf8, 0xfe, 0x8a, 0xbe, 0x00, 0xd8, 0x5f, 0xa4, 0x3a, + 0x61, 0x03, 0x75, 0x91, 0xba, 0x6e, 0x57, 0xfd, 0x4c, 0xa5, 0xe2, 0xc5, + 0x10, 0x3a, 0x64, 0xf9, 0x19, 0x43, 0x18, 0x1b, 0xba, 0xbf, 0x92, 0x3b, + 0x77, 0x04, 0x1d, 0x6c, 0x85, 0xdd, 0xf9, 0xfb, 0x4a, 0xde, 0x04, 0x7f, + 0xdd, 0x4d, 0x14, 0xed, 0x9b, 0x28, 0x3c, 0xb0, 0xc6, 0xca, 0xeb, 0xc1, + 0xbc, 0xf3, 0xd4, 0x65, 0xb3, 0xf1, 0x01, 0x74, 0xaf, 0xec, 0x70, 0x0a, + 0xa5, 0xb8, 0x24, 0x95, 0xba, 0x4b, 0x97, 0xb3, 0x39, 0x25, 0x72, 0xc0, + 0xb9, 0xc0, 0xaa, 0x31, 0xe4, 0x6a, 0xe3, 0x67, 0xe7, 0x55, 0x61, 0x9d, + 0xcf, 0xa7, 0x6a, 0xe8, 0x44, 0xe0, 0x4d, 0x3a, 0x12, 0x22, 0x70, 0xe6, + 0xe8, 0x8f, 0xc2, 0x99, 0xe8, 0x8c, 0xfd, 0x1e, 0xaa, 0x1b, 0x4a, 0x50, + 0x3a, 0x23, 0xfd, 0x84, 0xe3, 0x89, 0x7a, 0x9c, 0x80, 0xe9, 0x18, 0xdc, + 0x90, 0x9d, 0xe6, 0xb9, 0x60, 0x85, 0xe2, 0x9c, 0x2b, 0x31, 0x0b, 0x31, + 0x20, 0x98, 0x13, 0xb9, 0x9a, 0xf9, 0x43, 0x3d, 0x5c, 0xb3, 0xe4, 0xa5, + 0x8c, 0xb5, 0x57, 0x91, 0xa5, 0x72, 0xfd, 0x3b, 0x92, 0x77, 0x4f, 0xde, + 0xba, 0x4e, 0xcd, 0x45, 0x39, 0xf4, 0x87, 0xef, 0x93, 0x3c, 0x57, 0x4b, + 0xbb, 0x46, 0x77, 0x6f, 0x6e, 0xbe, 0x68, 0x1d, 0xa5, 0xe9, 0xcb, 0x3b, + 0x28, 0x3e, 0x93, 0x75, 0x06, 0xa8, 0x26, 0x14, 0x86, 0xf1, 0x7b, 0xdd, + 0xac, 0x0d, 0x99, 0xec, 0x1e, 0x1a, 0xbd, 0xd6, 0xf4, 0x02, 0x51, 0xe1, + 0xb3, 0x53, 0x5e, 0x47, 0xa0, 0x72, 0xf6, 0x46, 0x8a, 0xfc, 0x5e, 0x53, + 0xde, 0xc1, 0x5b, 0x27, 0x0c, 0x10, 0x5e, 0x6c, 0x83, 0xfa, 0x43, 0x8a, + 0xa6, 0xf3, 0xa1, 0xc6, 0xd3, 0xaf, 0x45, 0xee, 0xec, 0x0f, 0xb2, 0xb6, + 0x9f, 0xc5, 0x2f, 0x91, 0xe5, 0xd9, 0x43, 0x50, 0x05, 0x3e, 0xdf, 0x18, + 0xc1, 0x2d, 0xb1, 0xa8, 0x3f, 0x07, 0xa6, 0x07, 0xbf, 0x1a, 0xc1, 0xe9, + 0x49, 0x33, 0x1d, 0x50, 0x08, 0xf5, 0xfe, 0x00, 0xbe, 0x3c, 0xd7, 0x4f, + 0xf0, 0x77, 0x57, 0xb5, 0x05, 0xad, 0xf1, 0xa1, 0xf0, 0xe6, 0xb4, 0xf8, + 0x92, 0xa2, 0xf5, 0xd6, 0x47, 0x5c, 0x92, 0x92, 0x0a, 0x1e, 0xd5, 0x69, + 0xb6, 0xd0, 0x5a, 0x45, 0x0b, 0x3f, 0x91, 0x2e, 0xdf, 0x11, 0xa6, 0x51, + 0xd1, 0x17, 0x44, 0x61, 0x37, 0x60, 0xe5, 0x8d, 0x9c, 0x2a, 0x8b, 0xbb, + 0x86, 0xcd, 0xff, 0x94, 0x61, 0xb5, 0x31, 0xef, 0x52, 0x48, 0x5c, 0xe9, + 0x36, 0x3c, 0x6a, 0x6a, 0xa2, 0xe1, 0xfd, 0x90, 0x37, 0x6e, 0x42, 0xd4, + 0xb7, 0xd9, 0xe9, 0x96, 0xde, 0xb7, 0xa8, 0x47, 0xe3, 0x23, 0x1a, 0x98, + 0x14, 0xd3, 0xc6, 0x1c, 0xe3, 0x5b, 0xae, 0x55, 0x93, 0x7d, 0x35, 0x44, + 0x15, 0xda, 0x0f, 0xb5, 0xca, 0xbc, 0xa2, 0xc6, 0xa4, 0x60, 0xe0, 0xef, + 0x86, 0xd3, 0x71, 0xef, 0x9f, 0xdf, 0xce, 0xa0, 0xac, 0x07, 0xe7, 0x83, + 0x9f, 0x3b, 0xc0, 0x29, 0x66, 0x8b, 0x5a, 0x65, 0x17, 0xb5, 0x86, 0x2e, + 0xfb, 0xb2, 0x49, 0xf3, 0x1e, 0xcc, 0x0b, 0x86, 0xd0, 0x76, 0x78, 0xd9, + 0xf4, 0xe7, 0xbd, 0x85, 0xe4, 0xfc, 0xdf, 0xc5, 0x07, 0x33, 0x58, 0x81, + 0x3d, 0x83, 0x67, 0xfd, 0x34, 0x43, 0x4a, 0x30, 0xac, 0x9d, 0x17, 0xe8, + 0xf1, 0x15, 0xc4, 0xcc, 0xd4, 0x5c, 0x16, 0x79, 0xc6, 0xd2, 0x30, 0xff, + 0x17, 0x13, 0x69, 0xd7, 0x45, 0x63, 0xb1, 0x72, 0x5a, 0x71, 0x02, 0x17, + 0x21, 0x67, 0x1a, 0x69, 0x96, 0x66, 0xb9, 0x02, 0x1a, 0x19, 0x03, 0x1f, + 0xad, 0x9d, 0xa4, 0xa7, 0xf1, 0xcf, 0xd1, 0xf3, 0x50, 0x29, 0xcb, 0x06, + 0xf8, 0xf5, 0x5f, 0x67, 0x6f, 0xd2, 0x2e, 0x3b, 0x07, 0x5f, 0x0e, 0x3b, + 0xb8, 0x3c, 0x70, 0xe0, 0x45, 0x5d, 0xa6, 0x56, 0x4e, 0xfb, 0x1d, 0xbf, + 0x23, 0x8b, 0x08, 0x97, 0xd0, 0x61, 0x83, 0xd0, 0x0a, 0x14, 0xbf, 0xe4, + 0x51, 0x47, 0x93, 0x87, 0x4e, 0x1d, 0x14, 0x80, 0x37, 0xf5, 0x94, 0x85, + 0x8d, 0x26, 0x2f, 0x4f, 0x9a, 0xe8, 0x13, 0xbe, 0xd2, 0x2f, 0x96, 0x78, + 0x00, 0x73, 0xa8, 0x57, 0x26, 0x9f, 0xef, 0xe3, 0x12, 0x0b, 0x39, 0xf5, + 0xa5, 0x2c, 0xec, 0x56, 0xc7, 0xb0, 0x92, 0x1b, 0xfe, 0x07, 0x96, 0xc0, + 0x65, 0x86, 0xf9, 0xae, 0x03, 0x74, 0x97, 0xa0, 0xf5, 0x34, 0x2f, 0x65, + 0x57, 0xa3, 0xa6, 0x76, 0xe5, 0x1d, 0xd3, 0xbd, 0xdd, 0x1b, 0xb4, 0x2c, + 0xeb, 0x7b, 0x6b, 0xfd, 0x85, 0xd6, 0x6a, 0xec, 0x93, 0xb8, 0x9e, 0x81, + 0x21, 0x0b, 0xeb, 0xb5, 0x60, 0x9a, 0x89, 0xb2, 0xcc, 0xc0, 0xd2, 0xd9, + 0x92, 0x47, 0xe5, 0x82, 0xc2, 0xeb, 0x8d, 0xba, 0xee, 0x82, 0xb4, 0xd0, + 0x4c, 0x69, 0x6e, 0x87, 0x53, 0x1e, 0x43, 0x01, 0x55, 0x8a, 0x99, 0x4c, + 0x12, 0xa8, 0x6d, 0x65, 0x95, 0x83, 0xf6, 0xe1, 0xdc, 0xcd, 0x82, 0x73, + 0xed, 0xf4, 0x59, 0x62, 0x35, 0xa7, 0x89, 0x11, 0x0d, 0x35, 0x50, 0x2c, + 0xab, 0xcf, 0x74, 0x6b, 0x75, 0x55, 0x69, 0x75, 0x1a, 0xf1, 0x69, 0x6f, + 0x35, 0xdd, 0x7f, 0x90, 0x9d, 0x49, 0x45, 0xde, 0x74, 0x7b, 0x5c, 0xc5, + 0x8b, 0x74, 0x84, 0xc9, 0x82, 0xad, 0x7e, 0x17, 0x15, 0x98, 0xfc, 0xe9, + 0x47, 0xdb, 0xb3, 0x2c, 0x65, 0x38, 0x49, 0x73, 0xa6, 0xa0, 0x76, 0x53, + 0x51, 0x25, 0x28, 0x14, 0x09, 0xa3, 0x5f, 0x28, 0x0f, 0x37, 0x5a, 0x7e, + 0xe0, 0xa0, 0x4c, 0x7c, 0xb5, 0x93, 0x6a, 0xfd, 0xf4, 0x7d, 0x5a, 0xd2, + 0x5f, 0x7f, 0x9c, 0x55, 0x71, 0x2a, 0x07, 0x66, 0x2a, 0x17, 0x70, 0x46, + 0x7b, 0xcb, 0x28, 0xb9, 0xa1, 0x82, 0x69, 0xec, 0x80, 0xf1, 0x61, 0xa8, + 0xd0, 0xf9, 0x99, 0xd2, 0xf7, 0x15, 0x4d, 0x5d, 0x51, 0x0e, 0xb3, 0xba, + 0x4e, 0x1f, 0xd1, 0x1f, 0xb9, 0xeb, 0x67, 0x97, 0x94, 0xd1, 0xfc, 0x12, + 0x1d, 0xd7, 0x61, 0x63, 0xa5, 0x6b, 0x42, 0xde, 0x5f, 0x92, 0x28, 0x71, + 0x76, 0xff, 0xeb, 0x7b, 0x02, 0x5f, 0xf7, 0x45, 0x04, 0x19, 0x7c, 0xa1, + 0x16, 0x17, 0xa0, 0x79, 0x6f, 0x52, 0xda, 0x23, 0xcf, 0xf8, 0x26, 0xb9, + 0x3c, 0xa4, 0xd8, 0x29, 0x2d, 0xea, 0xee, 0x04, 0xcf, 0xce, 0x7f, 0x4d, + 0xec, 0xb3, 0x64, 0xcb, 0xfb, 0x92, 0x30, 0xf4, 0x64, 0xca, 0x84, 0x60, + 0xfc, 0x82, 0xf4, 0xe9, 0xd8, 0xfc, 0xd2, 0x4b, 0x4d, 0x98, 0x7a, 0x64, + 0x4c, 0xa8, 0x33, 0xd9, 0xc8, 0xbb, 0xb2, 0xa1, 0x35, 0x13, 0xc2, 0x18, + 0x24, 0x91, 0xe7, 0xd1, 0xab, 0x68, 0x34, 0x1c, 0x89, 0xcf, 0x60, 0xba, + 0xcb, 0x74, 0xd5, 0xb8, 0x1e, 0x19, 0xc3, 0x0a, 0x63, 0x66, 0x29, 0x25, + 0x0d, 0x26, 0x93, 0xc0, 0xdd, 0x9f, 0x9e, 0xf5, 0xe0, 0x7d, 0x8c, 0x14, + 0xe8, 0x01, 0xe5, 0xd5, 0x65, 0x1a, 0x47, 0xbe, 0xbb, 0x40, 0x42, 0x6b, + 0xc9, 0x26, 0xc4, 0xab, 0x4a, 0xe6, 0x13, 0xfe, 0x7e, 0x7a, 0xde, 0x98, + 0xbb, 0xd1, 0xe2, 0xdc, 0x28, 0x92, 0x1d, 0x3f, 0x32, 0x0b, 0x19, 0x96, + 0x25, 0x57, 0xe3, 0x80, 0xde, 0xc6, 0xa8, 0x9b, 0x56, 0x48, 0xdf, 0x0c, + 0x4c, 0x21, 0xc7, 0x3b, 0x82, 0x47, 0xd2, 0x79, 0xa8, 0x68, 0x2b, 0xb4, + 0x9d, 0xec, 0x4f, 0x2c, 0xdd, 0xd0, 0x71, 0xca, 0xc3, 0x48, 0x21, 0x7e, + 0x51, 0x2e, 0xfc, 0xca, 0xb1, 0x75, 0x07, 0x14, 0xf5, 0x0b, 0xd8, 0x25, + 0xa9, 0x78, 0x7b, 0x84, 0xf1, 0xe0, 0x61, 0x79, 0x67, 0x44, 0x57, 0x74, + 0x80, 0xfa, 0x03, 0xf1, 0x45, 0xd1, 0xab, 0x4d, 0xc3, 0x59, 0xee, 0x6a, + 0xfe, 0x13, 0xaf, 0x62, 0xbc, 0x23, 0x89, 0xe0, 0x35, 0x8e, 0xa4, 0xc4, + 0x80, 0x4e, 0xa5, 0x68, 0xa6, 0xd5, 0x9a, 0xf2, 0xaf, 0x69, 0x34, 0x5d, + 0x57, 0x5b, 0x4a, 0xcd, 0x59, 0x33, 0x31, 0xb6, 0x27, 0xeb, 0xb6, 0xf5, + 0xed, 0x83, 0x73, 0x3a, 0xe2, 0xd8, 0xc2, 0x7b, 0x29, 0x8a, 0xa2, 0xcf, + 0x82, 0x70, 0xd6, 0x65, 0x7d, 0xfb, 0xb1, 0x55, 0xd2, 0xc2, 0xd1, 0x72, + 0x4c, 0x01, 0x5d, 0x69, 0x22, 0x18, 0x5e, 0xd4, 0x4e, 0xd3, 0x19, 0x30, + 0x91, 0x66, 0x16, 0x4f, 0xe3, 0xbc, 0xb0, 0xf6, 0x7b, 0x31, 0x3c, 0x6d, + 0x0a, 0x52, 0xaf, 0xa7, 0xc3, 0x11, 0xf8, 0x4e, 0xc8, 0xab, 0x82, 0x5c, + 0x4e, 0x16, 0x14, 0x90, 0xb6, 0xd6, 0x82, 0xf0, 0x30, 0x85, 0x0c, 0x73, + 0xf3, 0x32, 0x76, 0x71, 0xdf, 0xa8, 0xb9, 0x1b, 0x3c, 0x71, 0x1d, 0x4e, + 0x2c, 0x2a, 0x09, 0xb8, 0xb1, 0xfe, 0x25, 0x5b, 0x5f, 0x8e, 0x2a, 0x86, + 0xd4, 0xeb, 0x47, 0x57, 0x37, 0x21, 0xcc, 0xfc, 0x0a, 0x1d, 0xae, 0x71, + 0xa3, 0xeb, 0xe1, 0xf8, 0xcd, 0xa7, 0x05, 0x37, 0x6d, 0xcb, 0x81, 0xdd, + 0x7d, 0x10, 0x9c, 0x71, 0x8c, 0xf9, 0xa2, 0xf1, 0xda, 0x09, 0xfe, 0x65, + 0x73, 0x5a, 0x9d, 0xb9, 0xe3, 0x3e, 0x94, 0x29, 0x96, 0xc5, 0xc6, 0x32, + 0x7e, 0x47, 0xc6, 0x35, 0x80, 0x0e, 0xf0, 0x21, 0x6f, 0x3b, 0x3f, 0x80, + 0xb7, 0x66, 0xb3, 0x2e, 0xf4, 0x29, 0x8a, 0xa4, 0xcf, 0x38, 0x8c, 0x0b, + 0xf5, 0x1e, 0x65, 0x4f, 0x6d, 0x3f, 0xd6, 0x9f, 0xa1, 0x79, 0x36, 0x77, + 0xf1, 0x73, 0x18, 0xac, 0x05, 0x71, 0xa6, 0x0e, 0x01, 0xb4, 0xef, 0x3a, + 0x56, 0x14, 0x3e, 0x0a, 0x26, 0x57, 0x9b, 0x56, 0x17, 0x7f, 0xff, 0x17, + 0x68, 0x53, 0xe0, 0x03, 0x07, 0x8e, 0x82, 0xf3, 0x14, 0xcb, 0xe1, 0xe3, + 0xe9, 0xc3, 0x0c, 0xf8, 0xea, 0xd3, 0x9d, 0x84, 0x6d, 0x5c, 0x36, 0xd3, + 0xec, 0x1b, 0xe3, 0xd7, 0xe3, 0x87, 0xcf, 0x10, 0xbf, 0x12, 0xdb, 0x44, + 0x78, 0xf1, 0xc7, 0xb8, 0x31, 0xd4, 0x36, 0x5a, 0x5c, 0xda, 0xeb, 0xf0, + 0xd0, 0xde, 0xd9, 0x51, 0x55, 0x21, 0x2d, 0xd8, 0x4c, 0x1b, 0x7a, 0x83, + 0x7c, 0x6a, 0xb9, 0x88, 0x0e, 0x03, 0x9c, 0xa1, 0x96, 0x90, 0x54, 0x75, + 0x86, 0xf0, 0x9a, 0xad, 0x92, 0x4c, 0xd1, 0x66, 0x4c, 0xcc, 0x3b, 0xf1, + 0x05, 0x91, 0x50, 0x73, 0x99, 0x08, 0x04, 0x8c, 0x1e, 0x3d, 0x54, 0x2d, + 0x8e, 0xf5, 0x52, 0xe8, 0x7b, 0x8f, 0x2a, 0x34, 0xf5, 0xa5, 0x06, 0x53, + 0xa1, 0xd8, 0x35, 0x95, 0xa1, 0xc7, 0xed, 0x82, 0x55, 0x31, 0x0a, 0x8e, + 0x1b, 0x85, 0x6f, 0xc7, 0x75, 0x78, 0xae, 0x3e, 0x7f, 0xe6, 0x12, 0xf5, + 0xf6, 0xfa, 0x32, 0xc3, 0x2b, 0xe2, 0xcc, 0xb1, 0xbe, 0x30, 0xcb, 0xc6, + 0x7a, 0x88, 0x34, 0x03, 0x64, 0xb7, 0xa3, 0x94, 0x4d, 0x67, 0xac, 0x06, + 0xb2, 0xf3, 0x62, 0x00, 0xf2, 0xda, 0xce, 0x4c, 0x70, 0xe3, 0x15, 0x5a, + 0xd0, 0x8a, 0x6b, 0xfe, 0x0f, 0xf7, 0x2d, 0x48, 0x01, 0x8b, 0x3a, 0x63, + 0x2e, 0x8b, 0x79, 0x49, 0xcd, 0x3f, 0xbc, 0x18, 0x52, 0x3f, 0x18, 0xeb, + 0x54, 0x42, 0x8a, 0x2d, 0x43, 0xef, 0xa0, 0x0f, 0x20, 0x2d, 0x7b, 0xa5, + 0xe6, 0x69, 0xb5, 0x16, 0x52, 0xa7, 0xa0, 0xd4, 0xff, 0xf3, 0xe4, 0xc2, + 0x7e, 0xa0, 0x83, 0xee, 0xca, 0x18, 0x29, 0x7e, 0x6b, 0x1f, 0x69, 0xde, + 0x11, 0x46, 0x0f, 0x59, 0x28, 0xcd, 0x4d, 0x88, 0xf7, 0xae, 0xe0, 0x04, + 0xf8, 0x6c, 0xb4, 0x77, 0x02, 0xaa, 0x45, 0xba, 0xfd, 0x2e, 0x70, 0x43, + 0xf9, 0x71, 0x58, 0x3b, 0xb9, 0x18, 0x7d, 0x88, 0xe3, 0x5c, 0x17, 0x37, + 0x3f, 0x9d, 0x0c, 0xfc, 0xb3, 0x4b, 0x40, 0xd3, 0x71, 0xb6, 0x85, 0x2d, + 0x3a, 0x3b, 0xff, 0xfa, 0xe8, 0xe6, 0x49, 0xbb, 0x99, 0x43, 0x2c, 0x7f, + 0xac, 0xaa, 0xc4, 0x06, 0x74, 0x37, 0x78, 0x49, 0x1d, 0x3e, 0x79, 0x91, + 0xd8, 0x7c, 0x80, 0x5a, 0xfd, 0x21, 0xa5, 0xdb, 0x70, 0xe4, 0xfb, 0x76, + 0xbb, 0xaa, 0x4e, 0xa7, 0x3d, 0xb9, 0xab, 0xd7, 0x4d, 0xe9, 0x4f, 0xa2, + 0x51, 0x8b, 0x60, 0x68, 0x0b, 0x50, 0xe3, 0xd6, 0xbc, 0xc7, 0xab, 0x39, + 0x00, 0x2d, 0x54, 0x27, 0x43, 0x42, 0xa7, 0x5b, 0x8a, 0x8c, 0x62, 0xb4, + 0xa3, 0x47, 0x19, 0xd6, 0xf9, 0x6d, 0x4c, 0xae, 0x98, 0xa8, 0x35, 0x9c, + 0x1b, 0x44, 0x84, 0xb3, 0x02, 0x00, 0x97, 0x8c, 0xdc, 0x94, 0xc3, 0x93, + 0xd8, 0x7e, 0x43, 0x27, 0xf0, 0x7d, 0x7c, 0x62, 0x6f, 0x32, 0x60, 0x56, + 0x0d, 0xdb, 0x23, 0x14, 0xed, 0x7e, 0xc1, 0x0d, 0xef, 0x99, 0x45, 0x5a, + 0xb0, 0x30, 0x8d, 0x7d, 0x80, 0x7a, 0x0e, 0x8a, 0x55, 0xbb, 0xbb, 0x64, + 0x89, 0x71, 0xc7, 0x6c, 0xd2, 0x1e, 0x49, 0x08, 0xf8, 0x00, 0xf8, 0x6a, + 0x41, 0x7d, 0x04, 0x40, 0x2e, 0xf0, 0x76, 0xe8, 0xc8, 0x1b, 0x75, 0x1e, + 0xcd, 0x06, 0x4b, 0x18, 0x2f, 0xc2, 0x3f, 0x14, 0x30, 0x8d, 0xb0, 0xd2, + 0xd6, 0xf4, 0x64, 0x50, 0x59, 0x4f, 0x14, 0xd7, 0x59, 0xc3, 0x30, 0xa6, + 0xb1, 0x4f, 0xa8, 0xcf, 0x8d, 0xa0, 0x0d, 0x7f, 0x3c, 0xd0, 0x87, 0x88, + 0x99, 0x07, 0x89, 0xd4, 0xe6, 0xba, 0xe1, 0x4c, 0x5e, 0x1a, 0x11, 0xe6, + 0x8e, 0x77, 0x65, 0xc9, 0x7f, 0x72, 0xff, 0x43, 0xe8, 0xa8, 0x42, 0xc0, + 0xdb, 0x4f, 0xf6, 0xbd, 0x43, 0xa8, 0xa8, 0xd7, 0xbb, 0xfb, 0x51, 0x8c, + 0xd2, 0xc6, 0x1e, 0x82, 0x48, 0x3f, 0x9d, 0xe4, 0x34, 0xc0, 0xdb, 0x71, + 0x4e, 0x49, 0xb5, 0xe0, 0x77, 0x37, 0x79, 0x08, 0xde, 0xad, 0x61, 0x96, + 0x0b, 0x1e, 0x1a, 0x36, 0xb1, 0xe3, 0xa2, 0x29, 0xc9, 0xfe, 0xf1, 0x07, + 0x32, 0x14, 0x20, 0x62, 0xaf, 0xaf, 0xa4, 0x60, 0xeb, 0xaf, 0xc8, 0x9a, + 0x04, 0xf2, 0xfc, 0x4a, 0x05, 0x77, 0xf2, 0x32, 0x89, 0x1c, 0x9d, 0xeb, + 0xed, 0xa6, 0x33, 0xe8, 0xb4, 0x7e, 0x4f, 0xc5, 0x9e, 0xbd, 0x89, 0x7f, + 0xff, 0x75, 0x02, 0x4a, 0x8a, 0xc5, 0x00, 0xe9, 0x30, 0xe6, 0x7d, 0x01, + 0xfa, 0xcb, 0x2f, 0xd5, 0xf1, 0x36, 0xd3, 0x93, 0xf2, 0x35, 0xc9, 0x6a, + 0x60, 0x94, 0x9f, 0x7d, 0x1d, 0x30, 0x3e, 0x2a, 0xd0, 0xe5, 0xef, 0xd7, + 0xa5, 0xe9, 0xcd, 0x95, 0x91, 0xd0, 0xba, 0x10, 0x25, 0x49, 0xe1, 0xba, + 0x01, 0x7c, 0x9a, 0xff, 0x34, 0x03, 0x2c, 0xda, 0xe2, 0x41, 0x45, 0xb1, + 0xcf, 0x87, 0x30, 0xed, 0xd2, 0xae, 0x08, 0xa9, 0x54, 0x69, 0x0b, 0xcc, + 0x91, 0xb8, 0xc4, 0x2e, 0x9d, 0x18, 0x17, 0x83, 0xdf, 0x1f, 0x69, 0x11, + 0x54, 0x23, 0xd4, 0x9c, 0xd5, 0x5b, 0xd5, 0x5d, 0xf2, 0xe8, 0xa1, 0x12, + 0x19, 0x04, 0xe9, 0x43, 0x10, 0x49, 0xce, 0x9b, 0xfa, 0xac, 0x18, 0xeb, + 0x1b, 0x7f, 0xc0, 0x90, 0x5d, 0x88, 0x47, 0xe7, 0x16, 0x0f, 0x46, 0xd4, + 0x14, 0x2d, 0x1d, 0x42, 0xc1, 0x3e, 0x06, 0x81, 0xfc, 0x25, 0xb9, 0xde, + 0x98, 0x5f, 0x2f, 0xcf, 0x9b, 0x43, 0xe8, 0xc4, 0x1e, 0x6d, 0x0a, 0xce, + 0x29, 0x2f, 0x17, 0x17, 0xfd, 0xbc, 0x57, 0x5c, 0x9e, 0xe5, 0x20, 0x8c, + 0x9f, 0xb3, 0xac, 0xd2, 0xb9, 0x24, 0xba, 0x52, 0xe8, 0xef, 0xcc, 0x3c, + 0x6f, 0xa4, 0x5c, 0x32, 0x2f, 0x94, 0x91, 0xe0, 0x4e, 0xd8, 0x02, 0xce, + 0xbd, 0x28, 0xbf, 0xde, 0x9c, 0xe7, 0x2b, 0xf4, 0x11, 0x67, 0x2a, 0xc9, + 0x06, 0x21, 0x85, 0xae, 0xad, 0xf2, 0x76, 0xee, 0xb1, 0x8e, 0xbe, 0xbb, + 0xff, 0x72, 0x64, 0xd2, 0xe2, 0xa3, 0xa3, 0x81, 0xa5, 0xc8, 0x8c, 0xaf, + 0x4e, 0x7a, 0x5b, 0xcf, 0xbb, 0xc8, 0x52, 0x49, 0xea, 0x08, 0xea, 0xe9, + 0x07, 0xab, 0xb6, 0x97, 0xc4, 0x6d, 0x14, 0xc7, 0xd3, 0x76, 0xda, 0xf8, + 0x0c, 0x24, 0x32, 0x70, 0xcd, 0xf9, 0x9b, 0xc2, 0xa9, 0x76, 0x59, 0x34, + 0x49, 0x34, 0x45, 0x03, 0x1a, 0x7b, 0x80, 0x99, 0xfd, 0xfe, 0x87, 0xff, + 0xe0, 0x44, 0xca, 0xbc, 0x7e, 0xc9, 0x9d, 0x91, 0x1d, 0xb7, 0x4c, 0x23, + 0x6c, 0xa9, 0x84, 0x1a, 0xae, 0xba, 0x09, 0x4c, 0x8a, 0x35, 0x79, 0xc5, + 0x33, 0x13, 0x31, 0xa4, 0x3d, 0x8f, 0x9e, 0x90, 0xba, 0x25, 0x29, 0x90, + 0x49, 0x81, 0xed, 0x3e, 0x57, 0x6d, 0x5c, 0x4b, 0xca, 0x44, 0x3d, 0x79, + 0xa7, 0xf1, 0xa5, 0x41, 0xbc, 0xd4, 0xa0, 0x75, 0x0c, 0x8e, 0x52, 0xeb, + 0x1b, 0x1a, 0xa5, 0xf4, 0xea, 0x02, 0xd5, 0x43, 0x19, 0x0c, 0x05, 0x9d, + 0x4d, 0xb3, 0x15, 0x44, 0x4e, 0x39, 0xbb, 0xb9, 0xf4, 0xee, 0x94, 0x7c, + 0x9e, 0x4e, 0x7b, 0x12, 0x75, 0xf8, 0xd4, 0x88, 0x6a, 0xcd, 0xea, 0x91, + 0x7f, 0x11, 0x82, 0x30, 0x37, 0xb2, 0xc5, 0x7f, 0x4e, 0xac, 0xfe, 0x63, + 0x68, 0x81, 0xdf, 0x61, 0xcc, 0xd2, 0xf9, 0x77, 0xfe, 0xb9, 0x2a, 0x54, + 0x66, 0x0e, 0x57, 0x8f, 0xab, 0xb8, 0xc7, 0xd3, 0x65, 0x9f, 0x00, 0xa2, + 0x0c, 0xfa, 0xb5, 0x24, 0x58, 0x2c, 0x98, 0xb2, 0x2b, 0xa6, 0x99, 0x99, + 0x7c, 0xfd, 0x3b, 0x73, 0x4c, 0x22, 0x15, 0x5a, 0x88, 0x92, 0x8d, 0x49, + 0xa2, 0x67, 0x23, 0x66, 0xf6, 0x7e, 0x73, 0x70, 0xec, 0x5b, 0xd8, 0x21, + 0x7a, 0x41, 0x25, 0xaf, 0xda, 0xb2, 0xa4, 0xf9, 0x6a, 0xd7, 0x2c, 0x8d, + 0xaa, 0x48, 0x3a, 0xc0, 0xa3, 0x2c, 0x02, 0xc1, 0xc0, 0x79, 0x71, 0xeb, + 0xc3, 0x5b, 0x50, 0x4c, 0xc2, 0x84, 0x75, 0x4a, 0xce, 0x0a, 0xbf, 0x50, + 0x75, 0x42, 0xfb, 0xa8, 0x2c, 0x81, 0x68, 0x35, 0x65, 0xf5, 0x02, 0x18, + 0xf7, 0x48, 0xce, 0xa6, 0xe8, 0xef, 0xd2, 0x54, 0x2e, 0x98, 0x08, 0xc3, + 0x94, 0x78, 0xd0, 0x44, 0x3a, 0x63, 0x0a, 0x47, 0xb5, 0xfa, 0xe6, 0x2d, + 0xf1, 0xda, 0xa9, 0x85, 0x85, 0xf1, 0x40, 0x3b, 0xca, 0x92, 0x5d, 0x2e, + 0x02, 0x81, 0x8a, 0x11, 0xa9, 0x5b, 0x01, 0xc2, 0x00, 0x98, 0xdf, 0x43, + 0xa3, 0x9e, 0x23, 0x40, 0x26, 0xc4, 0xaa, 0x67, 0x65, 0x9f, 0xb6, 0x62, + 0x91, 0x55, 0x80, 0xf3, 0x1e, 0x60, 0x9b, 0x2e, 0x3a, 0x23, 0x72, 0x00, + 0xca, 0x66, 0xd1, 0x46, 0xbd, 0x4e, 0x6c, 0xb6, 0x2c, 0xac, 0x97, 0x9a, + 0x47, 0xa0, 0x28, 0xc7, 0x67, 0xc5, 0x9f, 0x21, 0x5f, 0x06, 0x9e, 0x18, + 0x6e, 0x42, 0xdf, 0xb7, 0xa1, 0xff, 0x44, 0xa1, 0xb4, 0x0a, 0x55, 0xa5, + 0xa3, 0x08, 0xc1, 0x3c, 0xea, 0x36, 0xfe, 0x43, 0xc8, 0x0d, 0x0a, 0x7d, + 0x62, 0xfc, 0xda, 0xed, 0x3a, 0x90, 0x63, 0x62, 0x4b, 0xcf, 0x7b, 0x76, + 0x48, 0x09, 0x7e, 0x4f, 0x1a, 0x96, 0xef, 0x3f, 0xb6, 0x00, 0x26, 0xcc, + 0x79, 0x8f, 0xda, 0x65, 0x5e, 0x2a, 0x04, 0x0d, 0xb7, 0x97, 0x1a, 0x4f, + 0x2e, 0x49, 0x27, 0x9a, 0xa9, 0x97, 0x14, 0xdd, 0xf6, 0xf2, 0x74, 0x3f, + 0xc7, 0x1c, 0xe1, 0x55, 0x44, 0xdf, 0x1b, 0x79, 0x90, 0x0f, 0x5b, 0x45, + 0x4c, 0x62, 0xcf, 0x5a, 0x0a, 0xe9, 0x4e, 0xd9, 0x40, 0xee, 0x5d, 0x90, + 0xa5, 0x3b, 0x9c, 0x0b, 0x0c, 0x09, 0xd1, 0xb8, 0x77, 0xc8, 0xd7, 0xf4, + 0x97, 0x46, 0x2d, 0x6e, 0x33, 0xa7, 0xd9, 0xba, 0x13, 0x71, 0x3a, 0x0f, + 0x8c, 0xed, 0xa9, 0xce, 0x2c, 0x22, 0xc8, 0xb6, 0xfa, 0x2f, 0xdc, 0x2c, + 0x0c, 0xe4, 0xa6, 0x55, 0x47, 0x6a, 0xca, 0xc9, 0x48, 0x7f, 0x4b, 0x76, + 0x5b, 0xe9, 0x8f, 0xd1, 0x03, 0x67, 0x2d, 0xae, 0x93, 0x34, 0xe5, 0x22, + 0xb5, 0x43, 0xe1, 0xe4, 0x2b, 0xb0, 0xee, 0x60, 0xa9, 0x56, 0xe7, 0x3e, + 0x91, 0xf7, 0x42, 0x3e, 0x7d, 0xd3, 0x0e, 0xa2, 0x4f, 0xe3, 0xd9, 0x0f, + 0xad, 0x1c, 0xa3, 0x08, 0x66, 0x50, 0x10, 0x09, 0x91, 0x68, 0x1d, 0x4d, + 0xfb, 0x90, 0x56, 0x6f, 0xc5, 0xdb, 0x10, 0x10, 0xd4, 0x13, 0x97, 0x6a, + 0x87, 0xa6, 0x12, 0x35, 0x08, 0xa3, 0x53, 0x2c, 0x09, 0xa6, 0x23, 0x49, + 0xc7, 0xcf, 0x1c, 0xeb, 0x2a, 0x15, 0x8d, 0x7e, 0x48, 0xf4, 0xcb, 0x19, + 0xce, 0x13, 0xec, 0xd5, 0xde, 0x34, 0x1d, 0xc0, 0xc0, 0x21, 0x34, 0x3d, + 0x5e, 0x5b, 0x79, 0x0c, 0x37, 0x08, 0xdd, 0xa7, 0x07, 0x54, 0xb4, 0x48, + 0xa7, 0xe4, 0x5c, 0xb8, 0x13, 0xbd, 0x64, 0x54, 0x57, 0x40, 0x27, 0xcb, + 0x19, 0xc8, 0x92, 0x41, 0x9b, 0x43, 0x4b, 0x0c, 0xb8, 0x2e, 0x06, 0x07, + 0xcb, 0x8e, 0xf0, 0x73, 0x08, 0x30, 0x3b, 0xa9, 0xfe, 0xf7, 0xbf, 0x9b, + 0x90, 0x4f, 0xc1, 0xf0, 0x77, 0x21, 0x8b, 0xfb, 0x90, 0x12, 0x0f, 0x14, + 0x8c, 0x5a, 0x2f, 0xd4, 0x8b, 0x00, 0x4b, 0x62, 0x87, 0x4c, 0xbc, 0x01, + 0x6a, 0x79, 0x33, 0xba, 0x3b, 0xea, 0xd6, 0xc8, 0x9d, 0x51, 0x7c, 0x9b, + 0x9f, 0xc3, 0x11, 0x30, 0x9f, 0x0b, 0x7f, 0x66, 0xfc, 0x9d, 0x1c, 0xd3, + 0x1e, 0x3c, 0x68, 0x0c, 0xff, 0x1f, 0xa6, 0xaa, 0xca, 0x0e, 0xb1, 0xe0, + 0x41, 0xd2, 0x1c, 0xf7, 0xfd, 0xb8, 0x71, 0xa0, 0xf9, 0x7f, 0x52, 0x2d, + 0xb4, 0xeb, 0x00, 0xbc, 0xc4, 0x91, 0xc8, 0xb3, 0x88, 0x6a, 0x10, 0xac, + 0xb3, 0x44, 0x14, 0xf4, 0x66, 0x56, 0x09, 0xf0, 0x3d, 0xf7, 0x23, 0x1b, + 0xca, 0xae, 0xd2, 0xde, 0x20, 0xea, 0x2d, 0x14, 0x57, 0xbb, 0x62, 0xaa, + 0x00, 0x5d, 0xa0, 0x8a, 0xf0, 0x9e, 0xc2, 0x0a, 0x57, 0x5d, 0x67, 0x1c, + 0x4a, 0x7e, 0x4e, 0x5c, 0xbc, 0xf3, 0x89, 0x8f, 0x5e, 0xe8, 0x02, 0xac, + 0xe6, 0x48, 0x93, 0xce, 0xbd, 0x3a, 0xe3, 0x69, 0x91, 0x9e, 0xa1, 0xcc, + 0x19, 0x88, 0xb2, 0xa7, 0x38, 0x66, 0xc7, 0x05, 0xa1, 0x78, 0xe2, 0x12, + 0x26, 0x7c, 0x8f, 0x8f, 0xd8, 0x25, 0x4e, 0x4d, 0xc3, 0x6a, 0x0c, 0xa3, + 0xc8, 0x2e, 0xcf, 0xb5, 0xad, 0xfd, 0x6c, 0xb3, 0xca, 0xb6, 0x26, 0x02, + 0xda, 0x6b, 0x09, 0x5b, 0x62, 0xb2, 0x95, 0x92, 0x2f, 0x07, 0xd2, 0x52, + 0x83, 0x1f, 0x88, 0x44, 0x0d, 0x4c, 0xb0, 0x08, 0xf1, 0xd2, 0x40, 0x61, + 0x6c, 0xc2, 0xab, 0x8c, 0x71, 0xb2, 0x39, 0x34, 0x72, 0xad, 0x5c, 0x38, + 0xf9, 0x2e, 0x5d, 0xda, 0x10, 0xef, 0x7b, 0x55, 0x04, 0xce, 0x28, 0x1c, + 0xab, 0xcd, 0x81, 0xa2, 0xbc, 0x59, 0xf9, 0xeb, 0xc5, 0x3b, 0x03, 0xc8, + 0x8c, 0x05, 0x7d, 0x08, 0x49, 0x2a, 0x51, 0x6f, 0x56, 0x3d, 0x30, 0x02, + 0xf3, 0x8d, 0x55, 0x89, 0x8d, 0x6e, 0x56, 0x94, 0xcc, 0xc8, 0x5a, 0x8c, + 0xe8, 0x26, 0x06, 0x2b, 0xa3, 0x3d, 0x59, 0x3c, 0xa2, 0x75, 0x8c, 0xb6, + 0x73, 0xca, 0x0a, 0xb3, 0x67, 0x93, 0xe3, 0xf4, 0x8f, 0xde, 0x38, 0xe2, + 0x73, 0xe2, 0x7f, 0xd2, 0x8b, 0x78, 0x8e, 0x0c, 0x76, 0xc2, 0x0c, 0x85, + 0xa2, 0x28, 0xc0, 0x90, 0xeb, 0xdc, 0x4b, 0x88, 0x40, 0xcc, 0x05, 0x86, + 0xcc, 0x78, 0x5d, 0xd3, 0x78, 0x7a, 0x8a, 0x96, 0x5e, 0x40, 0x4a, 0x75, + 0xe0, 0x82, 0x63, 0x7c, 0x77, 0x1e, 0xc2, 0x9a, 0xef, 0xe9, 0xbb, 0x3b, + 0x0b, 0x8e, 0x5e, 0x3d, 0xe4, 0xc1, 0x23, 0xf5, 0x2d, 0xf7, 0x32, 0xa1, + 0xca, 0x5d, 0xf6, 0x9b, 0x5b, 0x23, 0xe1, 0x2d, 0xf0, 0x30, 0xd0, 0x82, + 0xa3, 0xac, 0x59, 0xf5, 0x7d, 0x75, 0x0f, 0xd4, 0xe2, 0xad, 0x27, 0x6e, + 0xb9, 0x1b, 0x06, 0xe1, 0xb9, 0x85, 0xe4, 0xdb, 0x02, 0x3b, 0xe1, 0x22, + 0x85, 0x58, 0xa4, 0x6f, 0xde, 0x6b, 0x0c, 0x23, 0xc3, 0x55, 0x63, 0x05, + 0x18, 0xf7, 0xc3, 0xe3, 0x4f, 0xbb, 0x1e, 0x2c, 0x7f, 0x6c, 0xc1, 0x9b, + 0x1f, 0x42, 0x18, 0xa6, 0x63, 0x78, 0xa5, 0x79, 0x35, 0x69, 0x92, 0x72, + 0x76, 0xcf, 0x4f, 0xed, 0xc7, 0x56, 0x51, 0x23, 0x06, 0xf9, 0xfe, 0xe2, + 0x65, 0xf2, 0x54, 0x1a, 0x8f, 0x5b, 0x15, 0x9f, 0xa9, 0xe0, 0x5d, 0xbf, + 0x83, 0x78, 0xc9, 0xf7, 0xdc, 0xe9, 0x47, 0x51, 0xe1, 0x38, 0x3b, 0xa6, + 0xbf, 0x6c, 0x3c, 0xd8, 0xfa, 0xf3, 0xaa, 0x36, 0xbb, 0xa3, 0xa9, 0x5b, + 0xd6, 0x17, 0xfe, 0xfa, 0x13, 0x2e, 0x9f, 0x13, 0x0e, 0x18, 0xd5, 0x8a, + 0x34, 0x92, 0x5f, 0x3f, 0x82, 0x94, 0x4a, 0x44, 0xae, 0x1a, 0xac, 0xf2, + 0x56, 0x55, 0xa5, 0x53, 0xd6, 0x6e, 0x67, 0x8e, 0xd1, 0xaf, 0x37, 0x18, + 0x9d, 0xa2, 0x8e, 0x12, 0x7d, 0x2a, 0x05, 0x98, 0xd7, 0x04, 0xc3, 0xb9, + 0xc5, 0xd9, 0x78, 0xbb, 0x37, 0xbf, 0x65, 0x6e, 0xb9, 0xae, 0xd9, 0xd8, + 0x9a, 0x58, 0x78, 0x57, 0xac, 0xc4, 0xb8, 0x73, 0x42, 0x39, 0x3a, 0xb9, + 0x6f, 0x3a, 0xa0, 0x9b, 0xbb, 0xaa, 0x5b, 0x18, 0x0f, 0x3f, 0xe8, 0xdb, + 0x00, 0x27, 0x8d, 0x50, 0x72, 0x3b, 0x9a, 0x0d, 0x92, 0xbb, 0x42, 0x7b, + 0x2b, 0x9c, 0x55, 0x8c, 0x57, 0x32, 0x41, 0x10, 0x6a, 0x53, 0x31, 0xfe, + 0x4e, 0x87, 0xfb, 0xc9, 0x77, 0x3b, 0x48, 0x71, 0x30, 0x42, 0xf2, 0xaa, + 0x08, 0xed, 0x3f, 0xec, 0x64, 0xe1, 0x52, 0x0f, 0xba, 0xce, 0xb3, 0xe6, + 0x64, 0x82, 0x9c, 0x12, 0x70, 0x98, 0xe6, 0xbb, 0xc4, 0x3a, 0xe1, 0xab, + 0x29, 0x98, 0x75, 0xbc, 0x2b, 0xc5, 0xa8, 0x14, 0x69, 0x1a, 0x7b, 0xc4, + 0x97, 0x22, 0xb5, 0xee, 0x64, 0x8e, 0x04, 0x4c, 0x4b, 0x8e, 0x21, 0x8d, + 0x8a, 0xf2, 0xe4, 0xba, 0x76, 0xb6, 0xb7, 0x8d, 0x36, 0x45, 0x47, 0x7f, + 0x58, 0xf6, 0x72, 0x90, 0xe2, 0xa8, 0x32, 0x15, 0x46, 0x52, 0x3d, 0x55, + 0xbd, 0x30, 0xf5, 0x75, 0x90, 0x37, 0xf2, 0x2f, 0x77, 0x0d, 0xac, 0x28, + 0x99, 0xd3, 0xe6, 0x7f, 0x7f, 0x2d, 0xa3, 0x42, 0x86, 0x50, 0x87, 0x73, + 0xc0, 0x59, 0xc4, 0x57, 0x50, 0x0f, 0x70, 0x26, 0x56, 0x24, 0xbc, 0x6d, + 0xdc, 0x57, 0x4c, 0x75, 0xf7, 0xa1, 0x7b, 0x14, 0xad, 0x12, 0xef, 0xfe, + 0x7e, 0x12, 0x7e, 0xe7, 0x56, 0x8e, 0x73, 0x6f, 0x8a, 0xce, 0x63, 0x9b, + 0x19, 0x70, 0xd3, 0x1f, 0xa0, 0x79, 0x8d, 0xc5, 0xc7, 0x61, 0x82, 0xae, + 0x18, 0x16, 0xa1, 0x27, 0xe3, 0xdd, 0x65, 0xa6, 0x7a, 0x0a, 0xed, 0x50, + 0x16, 0x47, 0x9e, 0x35, 0xea, 0x0e, 0xfb, 0x76, 0x1b, 0xfa, 0xfd, 0x96, + 0x92, 0xf5, 0x94, 0x2e, 0x22, 0xe3, 0x43, 0x65, 0x69, 0x0c, 0x15, 0x0d, + 0x19, 0xee, 0xf1, 0x78, 0xca, 0x11, 0xcf, 0x5f, 0x23, 0xc0, 0xc9, 0x0e, + 0xac, 0xe7, 0xe9, 0xfc, 0x6a, 0xb4, 0x29, 0x66, 0x9b, 0xe7, 0xe3, 0x5e, + 0x60, 0x5a, 0x79, 0x85, 0x53, 0x80, 0x78, 0xd8, 0x08, 0x5d, 0x8c, 0x88, + 0x72, 0x1e, 0x92, 0x1f, 0xa8, 0xb2, 0x85, 0xc5, 0x17, 0xb5, 0x9f, 0x65, + 0xea, 0x33, 0xe0, 0xa3, 0x6c, 0x1c, 0xaf, 0xbd, 0x0e, 0x03, 0xcb, 0x75, + 0x46, 0xc6, 0x21, 0x3b, 0x4b, 0xf6, 0x3b, 0xa9, 0x5c, 0x2b, 0xb8, 0x74, + 0x04, 0x0a, 0x66, 0x08, 0xa6, 0x0b, 0x26, 0x5a, 0xdc, 0xd4, 0x93, 0x02, + 0x83, 0x66, 0x78, 0x7e, 0x1e, 0x87, 0x5c, 0xa0, 0x0a, 0xc3, 0x50, 0x90, + 0xce, 0x62, 0x12, 0x08, 0x2b, 0x7c, 0xb7, 0xdd, 0x88, 0xcb, 0xdc, 0xed, + 0x3e, 0x85, 0x25, 0xf8, 0xf3, 0xea, 0x2d, 0x01, 0xf0, 0xfc, 0x9c, 0x19, + 0x08, 0x41, 0x8b, 0x22, 0x94, 0xbe, 0xa8, 0x56, 0x5f, 0xce, 0x1f, 0xce, + 0xe7, 0x74, 0x8c, 0xea, 0x7b, 0x97, 0x49, 0x2b, 0x9d, 0x7a, 0x26, 0xd3, + 0x97, 0x79, 0x81, 0x4d, 0x83, 0x4c, 0x83, 0x33, 0xd5, 0xaa, 0xe2, 0x59, + 0xe9, 0xcc, 0x9f, 0xf9, 0x02, 0xee, 0xab, 0x4e, 0x17, 0x7e, 0xdd, 0x00, + 0xb6, 0x61, 0x2f, 0xc4, 0x27, 0x96, 0x7c, 0xbb, 0x77, 0xcf, 0x68, 0x8d, + 0xac, 0x08, 0xea, 0x38, 0xc8, 0x2e, 0x77, 0x7e, 0xd7, 0x42, 0xb3, 0x56, + 0xf1, 0xba, 0xdc, 0xcd, 0x8d, 0xa4, 0xdd, 0xa0, 0x35, 0x17, 0x84, 0x86, + 0x34, 0xb5, 0xa3, 0xf8, 0xca, 0x8a, 0x0f, 0xc3, 0x91, 0xa1, 0xa7, 0x8a, + 0x52, 0x29, 0x53, 0xe1, 0x4f, 0xd4, 0x9e, 0xd3, 0xee, 0x6d, 0xdc, 0x09, + 0x69, 0xef, 0x07, 0x04, 0x6c, 0x44, 0xe0, 0x67, 0xb5, 0x41, 0x2b, 0x8e, + 0x8a, 0x9b, 0x52, 0xe9, 0x24, 0x44, 0x79, 0x3a, 0xa3, 0x91, 0x18, 0x22, + 0x77, 0x45, 0xb7, 0xe1, 0xc9, 0xdb, 0xd9, 0x2b, 0x1c, 0x21, 0xf4, 0x42, + 0x36, 0x20, 0x4b, 0x97, 0xe7, 0x0c, 0xcd, 0x19, 0xfb, 0x74, 0x68, 0xc9, + 0xd2, 0x95, 0x26, 0x52, 0x2c, 0x29, 0x97, 0x77, 0x78, 0xd2, 0x74, 0xea, + 0x80, 0x17, 0xc2, 0x36, 0x66, 0xa9, 0x3e, 0x93, 0x66, 0x44, 0x08, 0xa9, + 0x1d, 0xa9, 0xd6, 0xf6, 0x53, 0xa0, 0x77, 0xaf, 0x1a, 0x0c, 0x33, 0xb0, + 0xe2, 0x9f, 0x43, 0xcf, 0x6d, 0x66, 0x52, 0xab, 0xf4, 0x86, 0xf3, 0x8c, + 0x88, 0x56, 0xa0, 0xfb, 0xb3, 0xa9, 0x05, 0xbe, 0x98, 0x11, 0xed, 0xea, + 0x8f, 0x49, 0xf3, 0x44, 0x12, 0x6b, 0x4c, 0x8d, 0x36, 0xbe, 0xb5, 0xa7, + 0x38, 0xc8, 0xdc, 0x43, 0x52, 0x99, 0xf5, 0x04, 0x6c, 0x10, 0x3b, 0x7a, + 0xa9, 0xab, 0x75, 0xce, 0x6f, 0x7a, 0x35, 0xcf, 0xc2, 0x7e, 0x81, 0xc6, + 0xe5, 0x93, 0x96, 0x31, 0x4f, 0xa9, 0xbf, 0x5b, 0x67, 0x2a, 0x0e, 0x29, + 0xc7, 0xa1, 0x35, 0x27, 0xc5, 0xb4, 0xc8, 0x18, 0xfa, 0xc4, 0x27, 0x98, + 0x3a, 0xa0, 0x47, 0x49, 0xc2, 0x8b, 0x3d, 0x42, 0xbf, 0x98, 0xa4, 0x35, + 0x63, 0x3f, 0x1d, 0xa3, 0xe5, 0x62, 0xb3, 0x1b, 0x70, 0x61, 0xb7, 0x87, + 0x79, 0x13, 0x87, 0xaf, 0x4e, 0xc5, 0xa2, 0xf4, 0x8e, 0x98, 0x8a, 0xc0, + 0x42, 0x87, 0x1a, 0x55, 0x13, 0x72, 0x89, 0x44, 0xb1, 0x84, 0x76, 0x22, + 0x35, 0x9f, 0xf6, 0x6f, 0x57, 0x2d, 0x4e, 0x79, 0x7a, 0xa4, 0xf9, 0x9d, + 0xf0, 0xf9, 0x81, 0x9e, 0x9f, 0xd4, 0x4b, 0xf0, 0xa0, 0xbb, 0xd3, 0x78, + 0x36, 0x26, 0xda, 0xa7, 0xcc, 0x27, 0xa7, 0x67, 0x15, 0xe4, 0x17, 0x07, + 0x87, 0x88, 0xf5, 0xab, 0x86, 0xb4, 0xae, 0x70, 0x5a, 0x07, 0x47, 0x5f, + 0xf7, 0x50, 0x15, 0xad, 0x79, 0xcc, 0x6f, 0xdd, 0x04, 0x72, 0xad, 0x3b, + 0xc4, 0x48, 0xad, 0x93, 0x4b, 0x5f, 0xbb, 0xb0, 0x65, 0x0c, 0x65, 0x5d, + 0xe4, 0x5a, 0xbf, 0xe1, 0xec, 0x1c, 0x39, 0xfb, 0xda, 0xf3, 0x4b, 0xb4, + 0x84, 0x02, 0x91, 0x12, 0x5b, 0xa4, 0xd5, 0x91, 0x17, 0x86, 0x39, 0xa5, + 0x64, 0xef, 0x81, 0x99, 0x27, 0x02, 0x91, 0x0a, 0xbd, 0xf3, 0xa5, 0x30, + 0xcf, 0x3e, 0xc7, 0x5b, 0xc0, 0x24, 0xa6, 0x54, 0xe7, 0x2e, 0xaf, 0x3c, + 0xcf, 0xfe, 0x95, 0x9f, 0xda, 0xf3, 0x57, 0x85, 0xa1, 0x11, 0x79, 0x78, + 0xc0, 0xd6, 0x11, 0xd9, 0x27, 0x17, 0x27, 0x8b, 0xf6, 0x03, 0x73, 0x0c, + 0x2b, 0x8a, 0x02, 0xeb, 0x9e, 0xc4, 0x17, 0x0e, 0x9c, 0xf8, 0x5d, 0x45, + 0xbf, 0xb2, 0x6f, 0x9f, 0x67, 0x95, 0xad, 0x9c, 0xf8, 0x75, 0xf3, 0x9a, + 0x2c, 0x76, 0x0e, 0xcb, 0x76, 0x00, 0x6b, 0x43, 0xeb, 0x14, 0x42, 0xee, + 0xad, 0x96, 0xf1, 0x9e, 0x6a, 0x77, 0x4c, 0x90, 0x2b, 0x17, 0xa4, 0xdb, + 0xdc, 0x8a, 0x13, 0x4f, 0xe5, 0x54, 0xf0, 0x00, 0xe2, 0x3a, 0x23, 0x93, + 0xbd, 0x51, 0x72, 0xaf, 0x24, 0x75, 0x1a, 0x22, 0x5f, 0x9b, 0xc1, 0x91, + 0x57, 0x86, 0x6c, 0x4b, 0xd7, 0x36, 0x58, 0xc1, 0x52, 0x14, 0x04, 0x85, + 0xd5, 0x40, 0x1f, 0x6a, 0xf9, 0x5b, 0xd3, 0xa9, 0x82, 0x19, 0xfe, 0xa5, + 0x3f, 0xef, 0x37, 0x13, 0xc6, 0xcc, 0x54, 0xb5, 0xac, 0xc1, 0x87, 0xbc, + 0x13, 0xee, 0x17, 0x86, 0x00, 0x1e, 0x37, 0x9c, 0xe1, 0x3e, 0x90, 0x02, + 0x92, 0x12, 0x33, 0x5f, 0x8f, 0x80, 0xb2, 0x06, 0x4e, 0x97, 0x3e, 0x0a, + 0x2c, 0x2f, 0x1b, 0x1b, 0x53, 0xde, 0x64, 0x28, 0x73, 0x0d, 0x87, 0x0f, + 0xa1, 0x8a, 0x4d, 0xb1, 0x1d, 0x5c, 0xb1, 0x58, 0x58, 0x95, 0x3d, 0x30, + 0xf7, 0x34, 0xcc, 0x88, 0x75, 0xb4, 0x7a, 0x6e, 0x51, 0x37, 0x4f, 0xc1, + 0xae, 0xe6, 0xa9, 0x92, 0x7e, 0xc5, 0x1b, 0x57, 0xce, 0xfb, 0xcb, 0x83, + 0x22, 0x75, 0x82, 0x5e, 0x02, 0xae, 0x99, 0x1c, 0xdc, 0x48, 0xcd, 0x4c, + 0xfe, 0x04, 0xf6, 0x73, 0x84, 0xfd, 0x72, 0xff, 0x47, 0x4e, 0xb7, 0x6a, + 0xc2, 0xcf, 0x4c, 0x26, 0x2d, 0x20, 0x63, 0x10, 0x1f, 0xfd, 0x62, 0x86, + 0x3e, 0x22, 0x74, 0xf1, 0xa7, 0x12, 0x2c, 0x6f, 0x4b, 0x51, 0x04, 0xcb, + 0x99, 0x98, 0x66, 0x90, 0xab, 0x7e, 0xd6, 0x99, 0xbc, 0xb0, 0x94, 0x82, + 0x55, 0xa3, 0x9b, 0xf4, 0x78, 0x49, 0xd4, 0x89, 0xc6, 0xd5, 0x57, 0x46, + 0x3f, 0x42, 0xbc, 0x33, 0xd2, 0x31, 0x6f, 0xc9, 0xe3, 0x8c, 0xc9, 0x07, + 0xd0, 0xb6, 0xdd, 0x0b, 0x12, 0xf6, 0xe3, 0x96, 0xf3, 0xcc, 0x67, 0x4a, + 0xa9, 0x5b, 0xcf, 0x92, 0x33, 0x41, 0xd7, 0x21, 0xa1, 0x2c, 0x5a, 0xde, + 0x28, 0x6c, 0x2b, 0x15, 0xb6, 0xeb, 0xc9, 0x10, 0x47, 0x35, 0x6c, 0x1d, + 0xcf, 0x8c, 0x10, 0x6a, 0xc9, 0xb7, 0x40, 0xfb, 0x49, 0xbe, 0xab, 0x92, + 0x60, 0xff, 0xc1, 0x2a, 0x9c, 0xa5, 0x20, 0xd3, 0x15, 0x37, 0xe2, 0xbd, + 0x18, 0xc7, 0x80, 0xac, 0xcf, 0x0b, 0x59, 0x6f, 0x38, 0x52, 0x86, 0x73, + 0x1d, 0xf3, 0x4d, 0x70, 0x57, 0x87, 0xe3, 0xe4, 0x8a, 0xdd, 0xc5, 0x11, + 0x8b, 0xdd, 0x8f, 0xc9, 0x86, 0x4b, 0x4d, 0x20, 0xe2, 0x42, 0x4d, 0x21, + 0x5b, 0x2a, 0xf8, 0x87, 0xcb, 0x43, 0xb1, 0xc4, 0xdb, 0xb3, 0xc6, 0xf9, + 0xca, 0x15, 0x62, 0x83, 0xcf, 0x32, 0x52, 0x9d, 0xc0, 0xc2, 0x0e, 0xcf, + 0xc0, 0x93, 0xc6, 0x51, 0xdd, 0xb8, 0x53, 0xc9, 0x73, 0x2b, 0x54, 0x42, + 0xb8, 0x44, 0x6e, 0x8b, 0x3c, 0xc4, 0x29, 0xb7, 0xba, 0xa4, 0x30, 0x53, + 0x08, 0xd6, 0x66, 0xbd, 0x07, 0xff, 0xec, 0x69, 0x61, 0x38, 0x41, 0xf6, + 0xaa, 0x23, 0xac, 0xbf, 0xd5, 0x5c, 0x37, 0x2e, 0x9b, 0xf9, 0x10, 0xa6, + 0x7e, 0x74, 0x3c, 0x91, 0x75, 0xac, 0x85, 0xa8, 0xb1, 0xbb, 0x43, 0x81, + 0x24, 0xb4, 0x96, 0x31, 0x3a, 0x91, 0x64, 0x74, 0x1a, 0xb4, 0x9c, 0x84, + 0x90, 0xc4, 0x1f, 0x06, 0x8e, 0x70, 0x00, 0x36, 0x01, 0xe6, 0x3f, 0x4b, + 0xc9, 0xf4, 0x02, 0xf7, 0x28, 0xd9, 0x0c, 0xc5, 0x7b, 0x8f, 0x8a, 0x53, + 0x8f, 0x2a, 0xc7, 0x98, 0xb1, 0xd8, 0x3f, 0x85, 0x3f, 0xcc, 0xcd, 0xf1, + 0x71, 0xc5, 0x86, 0x17, 0x48, 0xbf, 0xd5, 0x37, 0x71, 0x55, 0x27, 0xe0, + 0x63, 0x48, 0x7c, 0x5f, 0x7b, 0xb4, 0x90, 0x62, 0xbd, 0x33, 0xd4, 0xa7, + 0xfc, 0x5f, 0x35, 0x36, 0xa0, 0x39, 0x5c, 0x06, 0x59, 0xfd, 0x16, 0x3b, + 0x98, 0xb0, 0xfe, 0x16, 0xc0, 0xb3, 0x3c, 0xaf, 0x90, 0xbe, 0x2c, 0x64, + 0xff, 0xe1, 0xbc, 0xfc, 0x74, 0x48, 0x95, 0xa0, 0xd3, 0x13, 0x10, 0x76, + 0xc9, 0xbe, 0x47, 0x9a, 0xf3, 0xe4, 0xd9, 0x31, 0x88, 0xfa, 0xbd, 0x13, + 0x1e, 0x37, 0x57, 0x1a, 0x06, 0x41, 0x0c, 0xd9, 0x0e, 0x48, 0x4b, 0xed, + 0x81, 0xaf, 0xe9, 0x72, 0x7b, 0x73, 0x44, 0x57, 0x28, 0xaa, 0xe7, 0x57, + 0xd2, 0xad, 0x03, 0xa3, 0xc6, 0xdc, 0xaa, 0x96, 0x4e, 0xe0, 0x55, 0x4d, + 0x0d, 0x21, 0x23, 0x81, 0x46, 0x2d, 0x37, 0x29, 0x7e, 0x31, 0xa8, 0x86, + 0x97, 0x0b, 0x6f, 0xb1, 0x09, 0x5a, 0x84, 0x26, 0x90, 0xa3, 0x19, 0xaf, + 0x9b, 0xb8, 0x3f, 0xf0, 0x2b, 0x14, 0x21, 0x1e, 0xab, 0x8e, 0x58, 0x83, + 0x52, 0x59, 0x5e, 0xd0, 0x2e, 0x27, 0x30, 0x2e, 0xf7, 0xc0, 0x84, 0xa1, + 0x28, 0x62, 0xdb, 0xe2, 0xce, 0xee, 0x05, 0xc9, 0x55, 0x0f, 0x44, 0x3c, + 0xc2, 0x08, 0x06, 0x54, 0x56, 0x5f, 0x1e, 0x8f, 0xc5, 0x52, 0xc1, 0x2b, + 0x65, 0xb4, 0xa0, 0xb4, 0x10, 0xce, 0xe0, 0xdc, 0xf9, 0xc9, 0xfe, 0x3c, + 0x99, 0x3d, 0xe9, 0xf7, 0xc0, 0xbb, 0x16, 0x1d, 0xe2, 0x01, 0xb7, 0x15, + 0x62, 0x44, 0x65, 0x5c, 0x8f, 0x0f, 0xd1, 0x3f, 0xae, 0xc6, 0x7b, 0x2f, + 0xc4, 0x75, 0xc1, 0x44, 0x69, 0x50, 0x11, 0x70, 0x6b, 0xb2, 0x30, 0x59, + 0xa4, 0xc9, 0xb5, 0xe1, 0x44, 0x63, 0xf3, 0x9b, 0x4f, 0xfe, 0xc3, 0xa3, + 0x46, 0xb7, 0x7e, 0x67, 0xac, 0xd3, 0x48, 0x2e, 0x53, 0xd9, 0x37, 0xb3, + 0x6b, 0x1e, 0xd1, 0x74, 0x02, 0xf9, 0xf4, 0xba, 0xd5, 0x46, 0x2e, 0xfd, + 0x7f, 0x4b, 0xbb, 0xb6, 0xc6, 0x16, 0xf0, 0xd5, 0x0b, 0x3b, 0x89, 0xe4, + 0x1b, 0x4b, 0x27, 0x1b, 0xb6, 0x40, 0x0f, 0xe4, 0xa9, 0xe2, 0x4d, 0xcb, + 0x53, 0xe0, 0xd2, 0xf8, 0x6f, 0x5a, 0x32, 0x9d, 0x63, 0x86, 0x96, 0xae, + 0x56, 0x1e, 0xae, 0xf9, 0x97, 0x3d, 0xfb, 0xe2, 0x3d, 0x1b, 0x55, 0xde, + 0xc0, 0x5c, 0xd6, 0x0e, 0x75, 0x1d, 0x7e, 0x1d, 0xd9, 0x9a, 0xb9, 0x37, + 0xeb, 0xe4, 0x96, 0x21, 0xf2, 0xab, 0x62, 0xe1, 0x81, 0x6b, 0x82, 0x04, + 0x90, 0x7b, 0x61, 0x6c, 0xe5, 0x44, 0xce, 0x4e, 0x4b, 0xa5, 0xce, 0xde, + 0x97, 0x41, 0x9c, 0x4d, 0x6b, 0xd1, 0x1a, 0x4f, 0xf7, 0xc5, 0xc8, 0x34, + 0x0a, 0x5f, 0x58, 0xac, 0xc3, 0x85, 0xd5, 0xa1, 0x83, 0xba, 0x7d, 0x4b, + 0xe7, 0x40, 0x27, 0x21, 0x13, 0x59, 0x75, 0x6e, 0x7b, 0x77, 0x36, 0xa4, + 0x7b, 0xfb, 0x27, 0x42, 0xc3, 0xdb, 0xeb, 0x31, 0xfb, 0xb9, 0x78, 0x7a, + 0x94, 0x62, 0x74, 0x20, 0xc3, 0x46, 0x77, 0x3d, 0x5c, 0xa2, 0xd4, 0x90, + 0x24, 0x4d, 0xb0, 0xd2, 0xb6, 0xce, 0xcb, 0x3d, 0x77, 0xdc, 0x35, 0xb9, + 0x53, 0x25, 0xea, 0x40, 0x1f, 0xe4, 0x6a, 0x49, 0x95, 0xb8, 0xc6, 0xaf, + 0x4c, 0xb4, 0xc2, 0x3f, 0x1f, 0x14, 0xc6, 0x94, 0xab, 0xe7, 0xee, 0x6f, + 0x83, 0xe6, 0x2a, 0x41, 0x29, 0x8c, 0x0f, 0xb1, 0x5d, 0xe3, 0xa3, 0x8f, + 0x8b, 0x72, 0xd5, 0x32, 0xca, 0x15, 0xe9, 0x46, 0x32, 0xec, 0xc2, 0x80, + 0x35, 0xaa, 0xa7, 0x86, 0x69, 0x19, 0xdb, 0x12, 0xe2, 0x3f, 0x47, 0xae, + 0x5a, 0xeb, 0xe1, 0xda, 0xce, 0x3b, 0x4c, 0xe5, 0x24, 0xdf, 0xaf, 0x14, + 0x65, 0x31, 0xe3, 0x84, 0x3f, 0xba, 0x5b, 0x8f, 0x5b, 0x44, 0x57, 0x6f, + 0x2f, 0xf4, 0x3a, 0xd9, 0x70, 0x18, 0xb9, 0xcb, 0x34, 0x4a, 0xf9, 0xb0, + 0x3b, 0xf7, 0x8c, 0xf0, 0xa0, 0x67, 0x0a, 0xf4, 0xc0, 0xfa, 0x4c, 0x55, + 0xeb, 0xe5, 0xdb, 0xee, 0x19, 0x51, 0xa4, 0xca, 0x8d, 0xe1, 0x9f, 0x1f, + 0x1d, 0xa8, 0x4c, 0xfe, 0xf5, 0x23, 0xe2, 0x2d, 0x74, 0x6b, 0x73, 0x7f, + 0xea, 0x84, 0x03, 0x55, 0xb6, 0x57, 0xc8, 0x56, 0x17, 0x15, 0x38, 0xd2, + 0xbb, 0xde, 0xe5, 0x95, 0x06, 0xb9, 0xad, 0x22, 0xfb, 0xdc, 0x21, 0x07, + 0x3f, 0x43, 0x38, 0xaf, 0xdf, 0xdf, 0x5b, 0x35, 0xec, 0x43, 0xaf, 0x37, + 0xff, 0x3b, 0x8a, 0x5e, 0x64, 0x93, 0xb4, 0x5e, 0x49, 0x2d, 0x5b, 0x5a, + 0xa0, 0x21, 0xb0, 0x17, 0x00, 0x0b, 0x58, 0x6e, 0xde, 0x4b, 0x42, 0x1c, + 0x3f, 0xc3, 0x53, 0xc1, 0xa7, 0x65, 0xff, 0x9a, 0x28, 0xce, 0xcd, 0xeb, + 0xcc, 0xaf, 0xa1, 0xb0, 0xe6, 0xea, 0x28, 0xdf, 0x93, 0x6d, 0xee, 0xba, + 0x35, 0x96, 0x7d, 0x8b, 0x09, 0x1a, 0xa8, 0x4a, 0x64, 0xaa, 0x39, 0xfb, + 0xcf, 0xcb, 0x80, 0x6d, 0x79, 0xe5, 0x88, 0x9b, 0x62, 0x78, 0x1b, 0x1d, + 0x37, 0xc9, 0x3d, 0x2d, 0xba, 0xe2, 0x8e, 0x9a, 0xd0, 0xba, 0xb2, 0xb5, + 0x5f, 0x58, 0x5d, 0x29, 0x7c, 0xc4, 0xa8, 0x3d, 0xd1, 0x83, 0x33, 0xea, + 0xf4, 0x03, 0x78, 0xac, 0x53, 0x5e, 0x6e, 0x6a, 0x3c, 0x81, 0x6b, 0x83, + 0x22, 0x77, 0xe7, 0x35, 0xe0, 0x16, 0xd5, 0x66, 0xb5, 0x09, 0x40, 0x42, + 0xf6, 0x8c, 0x52, 0x11, 0x37, 0x49, 0x86, 0xf1, 0xf8, 0x9b, 0x3c, 0x18, + 0x07, 0xb4, 0x91, 0x43, 0x74, 0x0c, 0x46, 0xff, 0xda, 0x8f, 0x03, 0xd9, + 0x5b, 0xab, 0x35, 0x78, 0x04, 0xc3, 0xdd, 0xa3, 0xcb, 0xab, 0x33, 0x58, + 0x76, 0x20, 0xc2, 0xb2, 0x37, 0xdc, 0x71, 0x48, 0xc5, 0xc0, 0x13, 0x6a, + 0x8b, 0xaf, 0xaf, 0xaa, 0xb6, 0xb8, 0xa0, 0x44, 0xd9, 0x8b, 0x68, 0x33, + 0xd5, 0xd0, 0xe0, 0x1b, 0x3e, 0x36, 0x47, 0x8d, 0x42, 0xcd, 0x42, 0x8c, + 0x1f, 0x8e, 0x93, 0xae, 0xf7, 0xba, 0x6b, 0xd0, 0x73, 0xc3, 0xa5, 0x83, + 0xb3, 0xfd, 0x88, 0x55, 0x88, 0x43, 0x12, 0x72, 0x42, 0x76, 0xb3, 0xf4, + 0x8a, 0x6e, 0x17, 0x64, 0x5f, 0x08, 0x76, 0xb1, 0xfe, 0x82, 0xa6, 0xff, + 0x38, 0x59, 0x25, 0xa5, 0x30, 0x88, 0x34, 0xe0, 0x4b, 0x32, 0x90, 0x6b, + 0x18, 0x5a, 0x29, 0xd8, 0xc5, 0x17, 0x30, 0xa7, 0xe8, 0x02, 0x56, 0xe4, + 0x07, 0x36, 0xf6, 0x4d, 0x1a, 0x97, 0xd5, 0x66, 0xc4, 0x2c, 0xbd, 0xe5, + 0xf9, 0x09, 0xc5, 0xb8, 0x5c, 0x13, 0x81, 0x99, 0xab, 0x51, 0x0b, 0x36, + 0xe7, 0xf0, 0x37, 0x0f, 0xb1, 0xb4, 0xcb, 0x66, 0xa7, 0x09, 0x8e, 0xbf, + 0xee, 0xdb, 0x6d, 0x53, 0xc0, 0x00, 0x66, 0x8f, 0x67, 0x7d, 0xdf, 0x7b, + 0x5c, 0xf2, 0x38, 0xac, 0x08, 0x59, 0x72, 0x24, 0x7b, 0xd8, 0x2b, 0xa9, + 0x37, 0x1e, 0x10, 0x5a, 0xeb, 0x8d, 0x08, 0x8d, 0xf0, 0x88, 0xac, 0x28, + 0x7c, 0xf6, 0xb2, 0x6a, 0xfd, 0xc3, 0x56, 0xdd, 0xd3, 0xe4, 0xcb, 0xb4, + 0x42, 0x0a, 0x66, 0xc9, 0x4e, 0x97, 0xa8, 0x67, 0xbf, 0xe5, 0x22, 0xb3, + 0x6b, 0x42, 0x1e, 0x46, 0xe4, 0x68, 0x8f, 0xc7, 0xd0, 0xbb, 0x08, 0x0b, + 0x3d, 0xb3, 0xfe, 0x03, 0xe2, 0x79, 0x94, 0x69, 0x45, 0x75, 0xd1, 0x05, + 0x7d, 0xa2, 0x3a, 0x4b, 0xe1, 0x75, 0x37, 0xe1, 0x58, 0xd8, 0x0e, 0xc8, + 0xc7, 0x32, 0xde, 0x9c, 0x0f, 0xb2, 0x15, 0x25, 0x27, 0xa9, 0xf2, 0xd9, + 0xda, 0xe9, 0xe5, 0x00, 0x50, 0xe8, 0x7f, 0x78, 0x8b, 0x49, 0x19, 0xbb, + 0xa5, 0xcb, 0xb8, 0x88, 0x45, 0xcc, 0x4e, 0x79, 0xe2, 0x6b, 0x6a, 0x32, + 0x72, 0x5c, 0xa8, 0xf5, 0x17, 0x1a, 0x19, 0xe8, 0xba, 0xc8, 0x76, 0x8c, + 0xfb, 0x04, 0x00, 0x99, 0x51, 0x53, 0xb2, 0xbb, 0x25, 0x25, 0x39, 0x44, + 0x8a, 0xa6, 0x45, 0x45, 0xb7, 0x1e, 0x60, 0x77, 0x1d, 0x97, 0x78, 0x22, + 0x9d, 0x48, 0x74, 0x60, 0x1c, 0x41, 0xc0, 0x38, 0x42, 0x9b, 0xb4, 0xf9, + 0x4b, 0x5b, 0x39, 0xfa, 0xb5, 0xae, 0xd5, 0x83, 0x87, 0xa6, 0xa6, 0xad, + 0x7a, 0xf1, 0x9a, 0x20, 0xf1, 0x40, 0x24, 0xe9, 0x9f, 0xeb, 0x98, 0x91, + 0x23, 0xe8, 0x3e, 0x6c, 0x54, 0x4c, 0x5b, 0x99, 0xa6, 0x04, 0xb9, 0x07, + 0x0d, 0x9e, 0xdd, 0x24, 0x4f, 0x3a, 0x75, 0x6c, 0xa1, 0x66, 0xe9, 0x99, + 0xea, 0x14, 0x48, 0x78, 0x8b, 0x31, 0x82, 0x02, 0x14, 0x5c, 0xc4, 0x57, + 0xf5, 0x14, 0x37, 0xdd, 0xb1, 0x22, 0x6c, 0xbd, 0xe2, 0x88, 0xe3, 0x3a, + 0x51, 0xde, 0xc5, 0x9a, 0xa8, 0x76, 0x8d, 0xa5, 0x6a, 0x24, 0x9f, 0xaa, + 0x5f, 0xc9, 0x16, 0x63, 0x6d, 0x68, 0x01, 0x7d, 0xee, 0x36, 0xa9, 0xdc, + 0x61, 0x2f, 0x43, 0x86, 0xfe, 0x26, 0xeb, 0xfb, 0xba, 0x6b, 0x8c, 0x3b, + 0x94, 0xec, 0x7f, 0x99, 0x3c, 0x95, 0x4a, 0x69, 0x52, 0x96, 0xcf, 0x81, + 0x49, 0x83, 0x7f, 0xfe, 0xe2, 0xe8, 0xce, 0x0a, 0xdf, 0xd9, 0x6b, 0xcf, + 0x05, 0xbe, 0xfa, 0x98, 0x7f, 0xbb, 0xee, 0xa1, 0x9f, 0x49, 0xf4, 0x64, + 0x2d, 0xc0, 0x4f, 0x3c, 0x8a, 0x98, 0xf3, 0x57, 0x2c, 0x9c, 0x6c, 0x06, + 0x98, 0x44, 0x17, 0xf9, 0x5d, 0xcf, 0x7e, 0xa2, 0xd4, 0xfc, 0xcb, 0x10, + 0x93, 0xe5, 0x6d, 0x89, 0x7f, 0x76, 0x44, 0x19, 0x85, 0x89, 0x89, 0x9c, + 0x77, 0xca, 0x73, 0xea, 0xb5, 0xa8, 0x37, 0x2c, 0xdb, 0x68, 0x6f, 0xb8, + 0xb3, 0x23, 0x42, 0x99, 0x03, 0xb1, 0x6f, 0x09, 0xa0, 0x92, 0x45, 0xff, + 0xee, 0xbb, 0xef, 0x31, 0xfa, 0x0a, 0x13, 0xe6, 0x08, 0x72, 0x11, 0x57, + 0x3a, 0xe7, 0xee, 0x17, 0x06, 0x90, 0xac, 0x26, 0x88, 0x83, 0x43, 0x58, + 0x6e, 0x87, 0x80, 0xd3, 0x5f, 0x0c, 0x12, 0x2b, 0x73, 0x1b, 0x3c, 0xcb, + 0xd2, 0x66, 0x72, 0xf7, 0x05, 0x05, 0x89, 0x2a, 0x5b, 0x60, 0xda, 0x8d, + 0x6c, 0xd4, 0xe2, 0x06, 0xed, 0xe7, 0x8e, 0xb5, 0x31, 0x26, 0x89, 0x03, + 0x53, 0xc9, 0x96, 0xad, 0x88, 0xd9, 0x2e, 0xce, 0x02, 0x19, 0xe6, 0x71, + 0x2f, 0x85, 0xa9, 0x91, 0xc9, 0xaf, 0x9d, 0x3b, 0xc0, 0x51, 0xf2, 0x91, + 0xd6, 0xb4, 0xc4, 0xbd, 0xee, 0x5f, 0x3f, 0xcb, 0x65, 0x0e, 0xd5, 0x23, + 0x35, 0xbc, 0x98, 0x78, 0x34, 0x00, 0x17, 0xf7, 0x43, 0x32, 0x51, 0xdd, + 0x0a, 0x49, 0x2c, 0x80, 0x21, 0xa4, 0x47, 0x26, 0x61, 0x06, 0x75, 0xe4, + 0x53, 0xfd, 0xd6, 0x29, 0x8e, 0xd7, 0x6a, 0x21, 0x5a, 0x63, 0xea, 0x0e, + 0x04, 0xce, 0xc7, 0xb0, 0x8c, 0xe8, 0xbc, 0xbf, 0x1e, 0x1f, 0xc3, 0xca, + 0x1d, 0x21, 0x54, 0x2c, 0xba, 0xeb, 0xa3, 0x5b, 0xa7, 0x93, 0xfc, 0xfb, + 0x6a, 0xe9, 0x36, 0x62, 0xf9, 0xf3, 0x20, 0xe4, 0x85, 0xe7, 0x6b, 0x03, + 0xbc, 0x0b, 0xc7, 0x59, 0x25, 0x19, 0x93, 0xc5, 0x9f, 0xfd, 0x79, 0xa7, + 0xd0, 0xf3, 0xa6, 0x21, 0xac, 0x47, 0xc0, 0x78, 0xb8, 0xa6, 0xee, 0x27, + 0x40, 0xe4, 0x26, 0xe0, 0x05, 0xc2, 0x58, 0x3a, 0xe4, 0x69, 0xdb, 0x97, + 0x7b, 0xa7, 0x38, 0xb7, 0xea, 0xc3, 0xa9, 0x82, 0xc8, 0x26, 0x08, 0x1d, + 0x2b, 0x91, 0x3d, 0x89, 0xdc, 0xe1, 0x7c, 0x4b, 0xb5, 0x26, 0x5a, 0xd5, + 0x29, 0x1c, 0x73, 0xf8, 0x3e, 0x1d, 0xc8, 0xbe, 0xa2, 0x44, 0x06, 0xed, + 0xea, 0x9e, 0x8a, 0xe9, 0x73, 0x21, 0xed, 0x7a, 0x63, 0x81, 0x87, 0x34, + 0x37, 0x65, 0x01, 0xc1, 0x1c, 0x60, 0x03, 0x09, 0x13, 0x91, 0x38, 0xed, + 0x61, 0x54, 0x30, 0xa8, 0x6a, 0xc8, 0x07, 0x23, 0xa4, 0x31, 0x1d, 0xd1, + 0xdc, 0x66, 0x06, 0x1d, 0x82, 0xeb, 0x79, 0xd8, 0x53, 0x9a, 0xfc, 0x54, + 0xa5, 0x3f, 0x77, 0x9d, 0xd7, 0x1c, 0xe6, 0x48, 0xf1, 0xaf, 0x9e, 0x2d, + 0x57, 0xcb, 0xd2, 0x7a, 0xa7, 0x04, 0xc8, 0x00, 0xfe, 0xee, 0x98, 0x5b, + 0x2a, 0x89, 0xd9, 0x3e, 0x51, 0x68, 0x4f, 0xe8, 0xa8, 0x37, 0x69, 0x47, + 0x32, 0xb4, 0x21, 0xc7, 0x66, 0xdb, 0x37, 0xb3, 0x00, 0xd6, 0xdd, 0xd1, + 0x6b, 0x74, 0x3d, 0x9a, 0x39, 0x49, 0xd6, 0x33, 0x4e, 0xca, 0xd5, 0xb4, + 0xdd, 0xd6, 0x25, 0x03, 0xa2, 0xdb, 0x29, 0x30, 0xe5, 0xcc, 0x94, 0xff, + 0x88, 0x8c, 0xb9, 0x3a, 0x77, 0xe2, 0x7f, 0x76, 0x7a, 0x16, 0x63, 0x85, + 0x90, 0x9f, 0xc3, 0xbc, 0x30, 0xee, 0xeb, 0x2e, 0x10, 0x94, 0x88, 0xb9, + 0x97, 0x0b, 0x3d, 0xf5, 0xba, 0xee, 0xde, 0x55, 0x53, 0x05, 0xc5, 0x3a, + 0xed, 0xc8, 0xd1, 0x6b, 0xcd, 0xa9, 0xc3, 0x0a, 0x11, 0xfc, 0xa8, 0x6a, + 0x8e, 0xb1, 0xc4, 0x18, 0x4a, 0x57, 0x80, 0xb6, 0x8a, 0x46, 0x92, 0xa5, + 0x61, 0x66, 0x12, 0x19, 0xd7, 0xbb, 0x75, 0x1c, 0x1d, 0xed, 0xc0, 0x29, + 0x1d, 0xe0, 0x76, 0x61, 0x88, 0x68, 0x03, 0xde, 0x7f, 0xa6, 0x43, 0xb2, + 0xca, 0x0c, 0xb2, 0x7b, 0x13, 0x78, 0xd0, 0xf4, 0x1d, 0x2c, 0xdf, 0x14, + 0xe8, 0xc2, 0x83, 0x8d, 0x89, 0x72, 0x4d, 0x68, 0x71, 0xbf, 0x6d, 0xfa, + 0x7d, 0xdc, 0x1b, 0x58, 0xd3, 0x95, 0xf6, 0xb8, 0x42, 0xa7, 0xdb, 0xa2, + 0x22, 0x02, 0x27, 0xba, 0x39, 0x89, 0x70, 0x7c, 0xd5, 0x9f, 0x4d, 0x9f, + 0x56, 0x94, 0x35, 0x65, 0xc0, 0x13, 0x6e, 0xb9, 0xc8, 0x55, 0xb9, 0x0f, + 0x81, 0xf3, 0x59, 0x03, 0x2c, 0xb7, 0x04, 0x74, 0x8f, 0xb1, 0xa2, 0x98, + 0x68, 0x00, 0xdc, 0x95, 0xa6, 0xa0, 0x2a, 0xe6, 0x2e, 0x04, 0x1a, 0xe8, + 0x4a, 0x7e, 0x08, 0xba, 0xc7, 0x68, 0x0f, 0xb8, 0xe9, 0xba, 0xd3, 0x65, + 0x08, 0x9e, 0x3e, 0x63, 0xee, 0x84, 0x16, 0x4e, 0x21, 0xca, 0xda, 0x8d, + 0xf2, 0x06, 0x34, 0x5d, 0x95, 0x79, 0xad, 0x35, 0x73, 0x28, 0x89, 0x83, + 0xd0, 0x23, 0x12, 0x97, 0xc0, 0x05, 0x69, 0xad, 0x31, 0xdc, 0xe4, 0x04, + 0xc9, 0xda, 0x39, 0x5a, 0x3c, 0xbe, 0xd9, 0xea, 0x8d, 0x4e, 0x2c, 0xa3, + 0xe4, 0x82, 0x98, 0x17, 0x74, 0x6a, 0xf1, 0xca, 0xeb, 0xf6, 0xf3, 0x69, + 0x48, 0xee, 0xe9, 0xca, 0x98, 0xf1, 0xc3, 0x8b, 0x57, 0xf4, 0xdd, 0x3f, + 0x14, 0xed, 0x95, 0xf1, 0xa6, 0x50, 0x1a, 0xe4, 0xce, 0xab, 0x86, 0x65, + 0x27, 0xb0, 0x84, 0xd4, 0x76, 0xf6, 0x03, 0x8d, 0x46, 0xf7, 0x4a, 0xef, + 0xcc, 0x84, 0x4a, 0x2b, 0x85, 0x11, 0x22, 0xd1, 0x35, 0x88, 0xce, 0x6b, + 0x8e, 0xc4, 0xe3, 0x6c, 0x67, 0xae, 0xf1, 0x58, 0xe5, 0x51, 0x1a, 0xdf, + 0x12, 0xd5, 0x44, 0x03, 0x69, 0x77, 0x12, 0x46, 0xbc, 0xfc, 0xda, 0x3f, + 0xc9, 0x88, 0x0f, 0xba, 0x46, 0xf0, 0xbe, 0x27, 0x4e, 0x93, 0xe3, 0xdc, + 0xa5, 0x31, 0xcb, 0xee, 0x8c, 0x7b, 0x41, 0x00, 0xc9, 0x52, 0x94, 0x4a, + 0x5a, 0xac, 0x31, 0xbd, 0xe9, 0xab, 0xb4, 0xd0, 0x17, 0x3e, 0xd3, 0x7a, + 0x45, 0x21, 0x02, 0xfa, 0x17, 0x92, 0x85, 0x6a, 0x9d, 0x5e, 0xa6, 0xc0, + 0xe7, 0x5d, 0x1c, 0x5d, 0xc6, 0x13, 0x92, 0x48, 0x4d, 0x43, 0xe0, 0xbb, + 0x61, 0x7d, 0x02, 0x3b, 0x81, 0x13, 0x88, 0x71, 0x76, 0x84, 0x37, 0x39, + 0x3a, 0x96, 0x2f, 0x37, 0xb2, 0x40, 0x57, 0x1a, 0x95, 0x57, 0xf7, 0x3f, + 0xb6, 0x7f, 0xed, 0x1a, 0xef, 0xf2, 0x9b, 0x0f, 0xe7, 0x02, 0x5a, 0xf2, + 0xad, 0x9d, 0xd3, 0x38, 0x2b, 0x43, 0x43, 0x97, 0x0e, 0x69, 0xbc, 0xaa, + 0x51, 0x23, 0xff, 0x87, 0x7f, 0x59, 0xb4, 0xa6, 0x64, 0xd8, 0xe9, 0xc3, + 0xf5, 0x73, 0x7b, 0x3a, 0xa4, 0x11, 0xb6, 0xa2, 0xf3, 0xa6, 0xf4, 0xe7, + 0xcb, 0x40, 0x3c, 0x2b, 0xa8, 0xa3, 0x68, 0x30, 0x38, 0xe1, 0xc4, 0x94, + 0xdb, 0x25, 0x64, 0xbd, 0xdb, 0x0d, 0x6c, 0xe7, 0x5f, 0xc6, 0x3d, 0xf4, + 0xd3, 0x8d, 0x80, 0x1f, 0x4b, 0x4c, 0xc0, 0xa8, 0x39, 0x1e, 0x58, 0x2e, + 0x56, 0x1f, 0xa9, 0x4e, 0x5d, 0xd9, 0xde, 0x11, 0x9e, 0xe8, 0x08, 0x1a, + 0xca, 0x6f, 0xdd, 0x1c, 0x7a, 0x13, 0x94, 0xe7, 0x5a, 0xf1, 0x91, 0x7b, + 0x7a, 0x07, 0xd7, 0x66, 0x7d, 0x25, 0xc0, 0x57, 0xe7, 0x42, 0x4b, 0x9c, + 0x4e, 0x9f, 0xb4, 0x79, 0xe5, 0xae, 0x18, 0x9d, 0x82, 0x28, 0x84, 0x12, + 0xad, 0x9f, 0x5b, 0xd6, 0xd1, 0xe8, 0xe2, 0x90, 0x1b, 0x40, 0xd4, 0x3d, + 0x54, 0xde, 0x02, 0xce, 0xc9, 0x5d, 0xcf, 0x50, 0x33, 0xa6, 0x84, 0x4d, + 0xb0, 0x3f, 0xbf, 0x77, 0xf6, 0xbe, 0x6a, 0xd5, 0x39, 0xf8, 0xb1, 0xc1, + 0xaa, 0xbc, 0x71, 0x8e, 0x15, 0x78, 0x98, 0x01, 0x85, 0x7c, 0xd6, 0x0c, + 0xe7, 0x63, 0x8b, 0x9e, 0xc0, 0x9e, 0x00, 0xd2, 0xbc, 0xfe, 0x96, 0x41, + 0x56, 0xdd, 0x36, 0x31, 0x2d, 0xb8, 0x04, 0x14, 0x7a, 0x3b, 0x33, 0xf6, + 0x09, 0xd6, 0x11, 0x26, 0x96, 0x0d, 0x75, 0xa5, 0x66, 0xea, 0xed, 0x6e, + 0xe4, 0x3d, 0xca, 0xb4, 0xd0, 0x90, 0x9d, 0xf3, 0x97, 0xad, 0xc0, 0x13, + 0x28, 0xc2, 0xe6, 0x38, 0x1f, 0x17, 0xc4, 0x39, 0x0e, 0x9b, 0xcf, 0x63, + 0x17, 0xb7, 0x8e, 0x62, 0xb5, 0xfb, 0xe4, 0x54, 0xce, 0x18, 0x4f, 0x60, + 0xe4, 0x9b, 0xe3, 0xc6, 0xeb, 0xc3, 0x55, 0x38, 0xcf, 0x14, 0x2d, 0x97, + 0x0b, 0x2f, 0x8a, 0xd6, 0x7b, 0xef, 0x93, 0x7a, 0x3b, 0x85, 0xf1, 0x6f, + 0xcd, 0x2a, 0x72, 0xa3, 0x34, 0xbc, 0x5d, 0x57, 0x6e, 0xff, 0x03, 0x62, + 0x6d, 0xed, 0x79, 0xe3, 0xd7, 0x6d, 0x11, 0xb2, 0x2d, 0x9f, 0xb8, 0x3e, + 0x0c, 0xe7, 0xab, 0xee, 0x5f, 0x04, 0x66, 0x93, 0x09, 0x25, 0xed, 0xdc, + 0x05, 0xa3, 0x98, 0xdc, 0x72, 0x48, 0x0f, 0x21, 0xf3, 0x03, 0x63, 0x86, + 0x39, 0x80, 0x29, 0xd0, 0x1a, 0x05, 0x78, 0x81, 0x1c, 0x10, 0xde, 0x8c, + 0x7e, 0xb0, 0xd6, 0x3a, 0xa5, 0xc5, 0x4e, 0xc9, 0x77, 0x50, 0xed, 0x43, + 0x54, 0x23, 0x5b, 0xe9, 0x62, 0x6b, 0x37, 0x30, 0xff, 0x4c, 0x34, 0x74, + 0x90, 0xb4, 0x01, 0xfc, 0x6e, 0x48, 0x99, 0xdb, 0xc6, 0x66, 0x8f, 0xc0, + 0xd4, 0x25, 0x43, 0xe2, 0x7a, 0x27, 0xce, 0xb8, 0xc6, 0x1f, 0x7d, 0xf9, + 0x31, 0x2c, 0xb9, 0x77, 0x68, 0x4a, 0xc6, 0x17, 0x7e, 0xb4, 0x47, 0x7c, + 0xdb, 0x5d, 0xed, 0x67, 0xdf, 0xf1, 0x00, 0x73, 0x9f, 0xc5, 0x80, 0xd9, + 0x50, 0xb5, 0x04, 0x61, 0x11, 0xe6, 0xe4, 0x4c, 0x3b, 0x7c, 0xf1, 0x03, + 0x76, 0x42, 0x65, 0x5e, 0x89, 0x67, 0x04, 0x67, 0x37, 0x2e, 0x64, 0xde, + 0xbc, 0x38, 0xe2, 0xe4, 0x69, 0x56, 0xcf, 0x22, 0x5c, 0x19, 0xe4, 0x3d, + 0x36, 0x97, 0x6a, 0x43, 0x2b, 0x30, 0x25, 0x39, 0xfb, 0x9f, 0x4e, 0x7d, + 0xb6, 0xbd, 0xf8, 0x00, 0x70, 0xa9, 0x22, 0x5c, 0x0d, 0x3a, 0x71, 0xce, + 0x35, 0x4a, 0xcc, 0xb1, 0xd2, 0xea, 0x3b, 0xd6, 0xb3, 0xce, 0x8e, 0x71, + 0x29, 0x7b, 0xaf, 0x38, 0x1f, 0x71, 0x49, 0x30, 0x50, 0x1b, 0xff, 0xad, + 0x67, 0x32, 0x7a, 0xde, 0xd0, 0x2e, 0x37, 0xef, 0xaf, 0xbe, 0xff, 0x85, + 0x2c, 0x1f, 0x5e, 0xb8, 0xef, 0x32, 0x82, 0x91, 0x3e, 0x91, 0x78, 0x6c, + 0xdb, 0x92, 0xd9, 0xc3, 0x80, 0x37, 0x73, 0x04, 0xc4, 0xcf, 0x82, 0xdb, + 0xff, 0x8f, 0x51, 0x53, 0xda, 0x64, 0x90, 0xd1, 0xca, 0x35, 0x6c, 0x44, + 0x83, 0x3b, 0x68, 0xca, 0xb3, 0x64, 0xae, 0xea, 0x8f, 0x7b, 0xc6, 0xcc, + 0x54, 0x69, 0xc1, 0xf8, 0x69, 0xcf, 0xc3, 0xdc, 0xfb, 0x7d, 0x6b, 0xa8, + 0xb5, 0x76, 0x4c, 0x71, 0x43, 0xcc, 0x6b, 0x1b, 0xfe, 0x96, 0xb2, 0x30, + 0x01, 0xb9, 0x62, 0xab, 0x3c, 0x64, 0x3f, 0x37, 0x05, 0xbd, 0xca, 0x77, + 0x7a, 0x5b, 0xcc, 0xd2, 0xf6, 0xc9, 0x66, 0xbe, 0x89, 0x80, 0xb1, 0x36, + 0xa2, 0xa6, 0xe3, 0x1d, 0xdb, 0x43, 0x40, 0xc7, 0x6f, 0xdf, 0x75, 0x7e, + 0x1f, 0x16, 0xf8, 0x2f, 0xf7, 0xd6, 0x3a, 0x1c, 0x22, 0xd1, 0x3b, 0xc7, + 0xe9, 0x7d, 0x13, 0x57, 0x0c, 0x88, 0x01, 0x70, 0xea, 0x42, 0x3c, 0xd2, + 0x61, 0x29, 0x06, 0xc9, 0x8b, 0xf0, 0x1d, 0x08, 0x4e, 0x7f, 0x59, 0x5e, + 0xa1, 0xf0, 0x1b, 0xb1, 0x38, 0x7b, 0xbd, 0x84, 0x16, 0xad, 0x5d, 0x84, + 0xe6, 0x81, 0x23, 0x24, 0x29, 0x25, 0xfd, 0xb1, 0x57, 0x7f, 0x0f, 0x14, + 0x08, 0x90, 0x31, 0x07, 0x8b, 0xb1, 0x5e, 0x78, 0x0b, 0x88, 0xe0, 0xbe, + 0xd9, 0x75, 0xc9, 0x1d, 0x12, 0x87, 0xbd, 0xee, 0x8d, 0xbd, 0x3a, 0x9c, + 0xeb, 0xf9, 0xe1, 0xe9, 0x6d, 0x4e, 0x9d, 0x34, 0x41, 0xe2, 0xba, 0x67, + 0x3c, 0x35, 0x36, 0x87, 0xa4, 0x63, 0x0f, 0x89, 0xf2, 0x35, 0x26, 0xe5, + 0x7f, 0xb1, 0x1e, 0x61, 0xcf, 0x36, 0x72, 0x1b, 0x8c, 0xd7, 0x4a, 0x40, + 0x70, 0x2c, 0xa0, 0xb7, 0xec, 0xb9, 0x7d, 0x9e, 0x2e, 0x32, 0x20, 0xba, + 0xe8, 0x44, 0x52, 0xd7, 0xf9, 0x79, 0xc9, 0xab, 0x75, 0xbc, 0x9a, 0xaf, + 0x84, 0x96, 0x37, 0x88, 0xe1, 0x1b, 0xf2, 0xc9, 0x70, 0xae, 0x1b, 0xca, + 0x25, 0xfb, 0x6d, 0x85, 0xad, 0xed, 0x49, 0xde, 0x76, 0x29, 0xd3, 0xa6, + 0x3d, 0xdf, 0x26, 0x17, 0xb0, 0x0d, 0x4d, 0xcb, 0x00, 0xc6, 0x71, 0xdf, + 0x10, 0x18, 0x11, 0x7f, 0x49, 0x89, 0x20, 0x88, 0x0c, 0xae, 0xb9, 0xe5, + 0x1a, 0x41, 0x77, 0xec, 0x06, 0x6a, 0x7f, 0x4a, 0xa7, 0x8f, 0x4b, 0x2d, + 0x8e, 0x91, 0x77, 0xb3, 0x87, 0xc3, 0xfd, 0xbf, 0xc4, 0xc4, 0x23, 0x75, + 0x07, 0x4f, 0x83, 0xd0, 0x5e, 0x68, 0x32, 0x7b, 0x7e, 0x70, 0xe8, 0x02, + 0x37, 0xe5, 0x52, 0x9a, 0x66, 0x74, 0xcd, 0xbd, 0xc0, 0xa5, 0xaf, 0x70, + 0x46, 0xae, 0xd0, 0x4f, 0x66, 0xbb, 0x0f, 0xf6, 0x14, 0xd6, 0xa4, 0x17, + 0x84, 0xa1, 0xd1, 0xb8, 0x2e, 0xb4, 0xf4, 0x33, 0x7f, 0xfa, 0xa1, 0x4e, + 0xe2, 0xaa, 0x5b, 0x1a, 0xea, 0x64, 0x41, 0x99, 0x61, 0xc1, 0xe3, 0x6d, + 0xe5, 0xfa, 0x47, 0x4b, 0xeb, 0x6a, 0xeb, 0xad, 0x14, 0xa1, 0xbf, 0xd4, + 0x34, 0xbc, 0x01, 0x98, 0x50, 0xc3, 0x7b, 0xbf, 0x1f, 0x60, 0xff, 0x10, + 0x92, 0x89, 0x68, 0x30, 0x16, 0x18, 0xa6, 0x47, 0xbe, 0xd1, 0x95, 0x6b, + 0xc1, 0x5b, 0xb8, 0x55, 0x78, 0xc9, 0x00, 0xbf, 0x54, 0x55, 0xdc, 0xc6, + 0xd2, 0x4b, 0xf9, 0xff, 0xe4, 0x5b, 0xc4, 0xee, 0xa5, 0x01, 0xd4, 0x60, + 0xcd, 0xb6, 0x3b, 0x44, 0x90, 0x7a, 0x7d, 0xbd, 0x77, 0xe6, 0xe1, 0x07, + 0xef, 0x13, 0x6e, 0xd1, 0xf6, 0x4d, 0xd3, 0xbf, 0xa2, 0x8f, 0x83, 0xf0, + 0xc4, 0x58, 0xf7, 0x13, 0x8d, 0xd4, 0xd8, 0x08, 0xa0, 0x8e, 0x76, 0x2b, + 0x23, 0x73, 0x97, 0xab, 0xe6, 0x1a, 0xda, 0x93, 0xaf, 0x39, 0xde, 0xfb, + 0x10, 0x0a, 0x9b, 0xed, 0x32, 0x62, 0xc5, 0x11, 0xbc, 0x5b, 0x95, 0x89, + 0xc4, 0x10, 0x88, 0x75, 0x5d, 0x23, 0xc3, 0x67, 0xc9, 0x6f, 0x14, 0x20, + 0x83, 0xf0, 0xa3, 0xbc, 0xf6, 0x34, 0x6e, 0xc4, 0x31, 0xad, 0x46, 0xd3, + 0x07, 0x54, 0xef, 0x52, 0x86, 0xe6, 0xea, 0x5b, 0x01, 0xc1, 0x56, 0xa3, + 0x37, 0x2f, 0xb7, 0x4a, 0x21, 0xab, 0x0a, 0x90, 0x32, 0x1a, 0xab, 0xbe, + 0xf8, 0x64, 0x6a, 0xf2, 0xa1, 0x58, 0xbc, 0x59, 0xa9, 0xdb, 0x9d, 0x82, + 0xf0, 0xcb, 0xa3, 0x18, 0xb8, 0x56, 0xa8, 0xbe, 0x25, 0xd0, 0x1b, 0xf2, + 0x28, 0xb5, 0x1a, 0x3a, 0x1b, 0x29, 0x69, 0xe1, 0x9d, 0x36, 0x80, 0x67, + 0x9f, 0xc1, 0x05, 0xdc, 0x49, 0x60, 0xde, 0xed, 0xf2, 0xc6, 0x77, 0xf0, + 0x06, 0x03, 0xb4, 0x2f, 0xc0, 0x3d, 0x54, 0x49, 0xb5, 0xa8, 0x63, 0x2c, + 0x2b, 0x29, 0x2f, 0x12, 0xe4, 0xdb, 0xd6, 0x5e, 0x3c, 0x48, 0x20, 0x1d, + 0xf0, 0x72, 0x47, 0xc3, 0x6b, 0x8e, 0xf5, 0x0c, 0xb2, 0xb0, 0x80, 0x9c, + 0x59, 0x73, 0x75, 0x4a, 0x10, 0xc0, 0x33, 0x68, 0x1e, 0x85, 0x28, 0x98, + 0x96, 0x51, 0xf1, 0x0f, 0xeb, 0x6a, 0x38, 0x75, 0xb7, 0x01, 0x88, 0xa2, + 0xb4, 0x39, 0xeb, 0x00, 0xd5, 0x7d, 0x72, 0x3e, 0xe7, 0x39, 0xb1, 0xcc, + 0x3e, 0x30, 0xdd, 0x15, 0x0b, 0x49, 0xd7, 0x4c, 0x25, 0x97, 0x64, 0x3b, + 0x05, 0x41, 0x57, 0x73, 0x23, 0xe9, 0x95, 0xf7, 0x81, 0x9b, 0xbe, 0x9a, + 0x77, 0x92, 0x58, 0x7e, 0xa4, 0x5d, 0x4a, 0x29, 0x04, 0xf7, 0x2d, 0x10, + 0x11, 0x95, 0x42, 0x40, 0x14, 0x7d, 0x4b, 0x3d, 0xbc, 0xc1, 0x1e, 0xac, + 0x99, 0xa2, 0x76, 0x63, 0x3f, 0xc8, 0xe8, 0xf2, 0x7d, 0xdd, 0x2d, 0xe0, + 0x97, 0xe9, 0x6e, 0x92, 0x32, 0x57, 0xb2, 0x23, 0x5b, 0x22, 0xee, 0x98, + 0xaf, 0x40, 0x88, 0xcf, 0x55, 0xfd, 0x92, 0xfa, 0x53, 0x6c, 0x5d, 0xcb, + 0xf0, 0x0a, 0xfe, 0xe6, 0x39, 0xb5, 0xca, 0xa6, 0x7b, 0x9f, 0xed, 0xea, + 0x20, 0x1a, 0x4a, 0xd9, 0x85, 0x3f, 0xb8, 0x77, 0x65, 0xe5, 0xfb, 0x15, + 0x99, 0xfd, 0x54, 0x62, 0xe4, 0x13, 0x7c, 0xd6, 0x32, 0x93, 0x85, 0x1e, + 0x30, 0xb4, 0xec, 0xaa, 0xa1, 0x6a, 0x4d, 0xc0, 0x02, 0x09, 0x1f, 0x29, + 0x81, 0x00, 0x5a, 0xa7, 0xe5, 0xb2, 0x05, 0xfa, 0x88, 0x98, 0xca, 0xef, + 0x7d, 0x94, 0xc0, 0x34, 0x5d, 0x57, 0xcb, 0xc2, 0x2b, 0x31, 0x21, 0x92, + 0xa9, 0xdf, 0x2c, 0x39, 0x8a, 0xc9, 0x42, 0x72, 0x08, 0x01, 0xfd, 0x46, + 0x3a, 0xad, 0x9e, 0x92, 0x67, 0x20, 0x86, 0xf2, 0x6f, 0x24, 0xd5, 0x4e, + 0x6c, 0x2e, 0x3f, 0xd4, 0xc0, 0x90, 0x08, 0x99, 0x47, 0x52, 0xfe, 0xa7, + 0xbe, 0x01, 0x9e, 0xd8, 0x84, 0x43, 0x97, 0x87, 0xa2, 0xcc, 0xb0, 0x66, + 0x7d, 0xf2, 0xe1, 0x56, 0xc6, 0x57, 0x49, 0xc8, 0x28, 0x94, 0x0b, 0x47, + 0xf6, 0x8f, 0xe1, 0x76, 0x4b, 0xbd, 0xc2, 0x9a, 0x8c, 0x44, 0xb2, 0xb2, + 0x33, 0xbc, 0x19, 0x57, 0x33, 0xd8, 0x64, 0xee, 0x22, 0xf6, 0x41, 0x60, + 0x2c, 0x45, 0x7d, 0xa0, 0x22, 0xee, 0xc3, 0xcb, 0x99, 0x13, 0x10, 0x1d, + 0x63, 0x74, 0xb4, 0x97, 0x70, 0x2d, 0x22, 0x12, 0xbf, 0x60, 0x26, 0x42, + 0xc6, 0x6d, 0xe0, 0xfb, 0x4d, 0x31, 0x10, 0x12, 0x8c, 0x2e, 0x67, 0x57, + 0xdb, 0x99, 0x10, 0x64, 0xe7, 0x6c, 0xaf, 0xa4, 0xa4, 0x65, 0x20, 0xc6, + 0x0c, 0xe8, 0x0b, 0xeb, 0xf6, 0x64, 0x68, 0x1c, 0x65, 0xe4, 0x03, 0x98, + 0x2e, 0x50, 0x0b, 0x44, 0x19, 0xd1, 0xd3, 0x21, 0xbf, 0xb8, 0x6c, 0x2c, + 0x0e, 0x69, 0xb6, 0x81, 0x95, 0x44, 0x5f, 0x1a, 0x12, 0xfc, 0xd4, 0x58, + 0x0c, 0x91, 0x5c, 0xd2, 0x3c, 0x4a, 0x80, 0xd5, 0x1d, 0x8d, 0x9c, 0xc9, + 0x02, 0x0e, 0x72, 0x5d, 0x1a, 0x2f, 0xb1, 0xd0, 0xe5, 0x8a, 0x24, 0x66, + 0xd0, 0xbb, 0x67, 0x85, 0x9e, 0xa3, 0x02, 0xa9, 0x7f, 0x49, 0xb7, 0x07, + 0x40, 0x20, 0xea, 0x3b, 0xea, 0x72, 0x27, 0xab, 0x43, 0x23, 0x54, 0x5d, + 0xa7, 0xac, 0x5f, 0x61, 0x32, 0x6a, 0xf2, 0x56, 0x32, 0xd2, 0xc3, 0x2e, + 0x4e, 0x6c, 0xc5, 0xfe, 0x10, 0x92, 0x27, 0x9c, 0xc2, 0x22, 0x85, 0x46, + 0xfa, 0x15, 0x77, 0x09, 0xeb, 0x13, 0xf4, 0x7a, 0xc1, 0x1d, 0x3c, 0x15, + 0xa8, 0x8f, 0x59, 0x58, 0xd3, 0xd8, 0xbb, 0x7f, 0xca, 0x03, 0xb7, 0x69, + 0xd8, 0x29, 0x09, 0x93, 0x69, 0x4c, 0x4b, 0x08, 0xd3, 0xe5, 0x52, 0x87, + 0xcd, 0x87, 0x8a, 0x55, 0xdb, 0x70, 0x17, 0x97, 0x2c, 0x1b, 0x93, 0x2c, + 0x42, 0xf9, 0x34, 0x7c, 0x26, 0xab, 0x21, 0xac, 0x4b, 0x45, 0xb5, 0xc8, + 0xf5, 0xa2, 0x63, 0x9a, 0x4b, 0xe0, 0x91, 0xb7, 0x51, 0x6c, 0xb5, 0x93, + 0x89, 0x71, 0xc0, 0x2c, 0x99, 0x75, 0xda, 0x8f, 0x0f, 0xa2, 0xdc, 0xd3, + 0x39, 0x32, 0x87, 0x4f, 0x05, 0x16, 0xac, 0xeb, 0x40, 0x44, 0xfd, 0xcd, + 0x54, 0xb1, 0x8b, 0x31, 0x05, 0x96, 0x63, 0x05, 0xd5, 0x49, 0x0e, 0x2a, + 0x88, 0xc7, 0xe8, 0x52, 0xc8, 0x72, 0xcc, 0xc9, 0xc5, 0xc5, 0x69, 0x31, + 0xf3, 0xb5, 0x24, 0x9b, 0xe5, 0x6d, 0x28, 0x1e, 0x9b, 0xfc, 0x10, 0xb3, + 0xa4, 0x71, 0xd7, 0xde, 0xf6, 0xfa, 0xef, 0x2a, 0x3d, 0x7d, 0xf1, 0x76, + 0xb5, 0x55, 0xdd, 0xe8, 0xd0, 0xaa, 0x54, 0x2b, 0x18, 0x68, 0x5a, 0x14, + 0x82, 0x97, 0xaf, 0xb1, 0xbe, 0x0e, 0x45, 0x85, 0x32, 0xc6, 0xfa, 0xa2, + 0x7f, 0xff, 0x2e, 0x6b, 0xc5, 0x69, 0x11, 0x32, 0xc4, 0xf2, 0x07, 0xd9, + 0x73, 0x6c, 0x15, 0x09, 0x71, 0x50, 0xa6, 0xfb, 0x7a, 0x89, 0x6f, 0xf1, + 0x68, 0xa9, 0x81, 0x6b, 0xb3, 0x87, 0xaa, 0xaf, 0x19, 0x1c, 0xd1, 0xe5, + 0x9d, 0x0c, 0xa2, 0xcf, 0xc6, 0xea, 0xc3, 0xff, 0x04, 0xfd, 0xe0, 0x42, + 0x0b, 0xc6, 0xfa, 0x16, 0x61, 0x52, 0xad, 0x80, 0xb6, 0xe1, 0x16, 0x95, + 0x41, 0x5e, 0x10, 0x97, 0xa7, 0xdb, 0x2c, 0xbc, 0x70, 0x07, 0x89, 0x4f, + 0x78, 0x5f, 0x17, 0x49, 0xe3, 0x79, 0xdc, 0x22, 0xc4, 0x0e, 0x42, 0x6a, + 0x59, 0x48, 0x8e, 0x3f, 0x2b, 0x31, 0xab, 0xe4, 0x3d, 0x3d, 0x45, 0xf6, + 0x6e, 0x0f, 0x9d, 0x79, 0xc6, 0x5c, 0x85, 0x70, 0xf6, 0xff, 0xd7, 0xc1, + 0x21, 0xa5, 0xdd, 0x25, 0x7b, 0x72, 0x43, 0xf3, 0x66, 0x39, 0xcd, 0xce, + 0x9e, 0x01, 0xa2, 0xaf, 0xc7, 0xd0, 0x3b, 0x1e, 0x22, 0x01, 0xc2, 0xb6, + 0x76, 0xe3, 0x32, 0x61, 0x8e, 0xea, 0x5a, 0xf4, 0xe7, 0x38, 0x98, 0xcb, + 0x4e, 0x49, 0x41, 0x5e, 0x6d, 0x09, 0xbe, 0x3a, 0x09, 0xfb, 0x97, 0x9f, + 0x30, 0x57, 0x8e, 0x26, 0x9b, 0x85, 0xad, 0x6d, 0xe5, 0xad, 0xe5, 0xc6, + 0xc6, 0xe8, 0x23, 0xe2, 0x26, 0x71, 0x83, 0x4e, 0xeb, 0x08, 0xc9, 0xe4, + 0x6b, 0xb2, 0x51, 0xd7, 0xf9, 0x48, 0x50, 0xc4, 0x41, 0x92, 0xae, 0xa6, + 0x3d, 0xc7, 0xac, 0x35, 0x9e, 0xab, 0x02, 0xe3, 0x35, 0x79, 0x54, 0x22, + 0x45, 0x09, 0xcc, 0x59, 0xb6, 0x5b, 0x07, 0x5e, 0x52, 0xc4, 0xc6, 0x34, + 0xce, 0xa5, 0x5b, 0xb5, 0xdc, 0xd7, 0x89, 0xb0, 0x97, 0x2e, 0x02, 0xaf, + 0x37, 0xb5, 0x1b, 0x0a, 0x87, 0x14, 0xe5, 0xca, 0x98, 0x04, 0xea, 0xad, + 0xec, 0x15, 0x79, 0x26, 0xde, 0xdf, 0x0b, 0x4f, 0xf2, 0x55, 0x2b, 0xc6, + 0xd9, 0x43, 0x4a, 0x11, 0x7c, 0x65, 0x37, 0xab, 0x33, 0x8b, 0x49, 0x0b, + 0xe4, 0xec, 0xae, 0x28, 0x6c, 0x70, 0xa7, 0x5f, 0xbe, 0x04, 0x50, 0x57, + 0xa5, 0xfb, 0x53, 0x93, 0x64, 0xfd, 0x52, 0xb9, 0xa7, 0x9f, 0x66, 0x03, + 0x7d, 0xdf, 0x8c, 0x3f, 0xdc, 0x1f, 0x9d, 0xe1, 0x8c, 0x42, 0xe6, 0x7f, + 0xd5, 0xf9, 0x9a, 0x57, 0x08, 0xb7, 0x98, 0xac, 0x69, 0xad, 0x31, 0xd1, + 0x8a, 0x70, 0x9e, 0x69, 0x26, 0x48, 0xb9, 0x6e, 0xfc, 0x28, 0x35, 0x45, + 0xb3, 0xd4, 0xc3, 0xa3, 0xcc, 0xf0, 0x38, 0x04, 0x55, 0x09, 0xc3, 0x99, + 0xf1, 0xee, 0xd9, 0x0c, 0x90, 0xbc, 0x17, 0x5c, 0xd4, 0x43, 0x8a, 0xdb, + 0xe8, 0x77, 0x6a, 0xba, 0x94, 0x67, 0xb2, 0xd8, 0xb5, 0xf7, 0x11, 0x92, + 0x2a, 0xf3, 0xcc, 0x87, 0xd4, 0x39, 0x38, 0xa2, 0x28, 0x32, 0xbf, 0xa3, + 0xa7, 0xe1, 0xb5, 0xe7, 0x53, 0x51, 0xd2, 0x95, 0x50, 0xe8, 0x99, 0x9d, + 0xf3, 0x76, 0x30, 0x35, 0x62, 0xaf, 0x79, 0x61, 0x7d, 0xee, 0xf7, 0x10, + 0xf5, 0x87, 0x97, 0xa2, 0x8d, 0x1c, 0xb7, 0xb6, 0x54, 0x3f, 0x40, 0xf8, + 0x7d, 0xb9, 0xeb, 0x53, 0x37, 0xd9, 0x60, 0x5d, 0x3e, 0xa9, 0xd3, 0x85, + 0x35, 0x1c, 0x51, 0x25, 0x77, 0x13, 0x0f, 0x24, 0xc4, 0xa3, 0x6a, 0x44, + 0x52, 0xca, 0xb4, 0xc1, 0x00, 0x22, 0x28, 0x68, 0xde, 0xfc, 0xff, 0x62, + 0x37, 0xcd, 0x62, 0x7d, 0x02, 0xe9, 0xc2, 0xa2, 0x6e, 0xdb, 0x04, 0xce, + 0x33, 0xce, 0xc5, 0x1b, 0x05, 0x83, 0x04, 0xd3, 0xdd, 0x15, 0xcb, 0x28, + 0x39, 0x9f, 0xd7, 0xdf, 0x78, 0xbb, 0xc0, 0x84, 0x7d, 0x00, 0xea, 0xb9, + 0xa0, 0xf8, 0x0a, 0x3a, 0x36, 0x2e, 0xbc, 0x6c, 0x1d, 0x61, 0x9a, 0x4a, + 0xbd, 0x4f, 0x52, 0x1b, 0x8e, 0x19, 0x3a, 0xba, 0xf4, 0xc9, 0x01, 0xa3, + 0x09, 0xdd, 0xb9, 0x6f, 0x22, 0x9c, 0x5c, 0x92, 0xc3, 0xe2, 0x28, 0xc2, + 0x18, 0x76, 0xd2, 0x55, 0xae, 0x48, 0xc2, 0xc8, 0xe1, 0x59, 0xf3, 0x45, + 0x3e, 0xb7, 0x55, 0x37, 0xaf, 0xba, 0x58, 0x07, 0xc4, 0x87, 0x7a, 0x78, + 0xa0, 0x2a, 0x7d, 0xf0, 0x0a, 0x3b, 0xd9, 0x57, 0x66, 0x8a, 0x4a, 0x66, + 0x3a, 0xf1, 0xb1, 0x56, 0x9e, 0x2f, 0xe7, 0x73, 0x84, 0xe6, 0xa0, 0x70, + 0xa8, 0x13, 0xf5, 0x51, 0xfc, 0x68, 0xeb, 0xfc, 0xb5, 0x9c, 0x3e, 0x72, + 0x07, 0xd3, 0x20, 0x8f, 0xb3, 0x4c, 0x38, 0x83, 0x16, 0x13, 0x29, 0xa7, + 0x15, 0x6b, 0x1d, 0x0e, 0x53, 0xf5, 0x3e, 0x4c, 0x5b, 0xdd, 0x36, 0xcd, + 0xb9, 0xc8, 0x57, 0x58, 0x1e, 0xbe, 0x49, 0x2a, 0x7c, 0x3b, 0x2e, 0x98, + 0xf3, 0x58, 0xa6, 0xda, 0x58, 0xfd, 0x7b, 0x01, 0x00, 0xb1, 0xd1, 0x0d, + 0x93, 0x1f, 0xa1, 0x72, 0x30, 0x3e, 0x51, 0x2b, 0xa8, 0x5e, 0x67, 0x2c, + 0x08, 0x63, 0x79, 0x95, 0xbc, 0x4c, 0x57, 0x7c, 0x10, 0x47, 0xc9, 0x8f, + 0xfc, 0xba, 0xe0, 0x79, 0x78, 0xe7, 0x55, 0x16, 0xd2, 0xb6, 0x6b, 0xc6, + 0x60, 0xeb, 0xa0, 0x4b, 0x9e, 0x18, 0x70, 0x6a, 0x70, 0x99, 0xbb, 0x24, + 0x14, 0xa3, 0x0e, 0x04, 0xc1, 0xa8, 0x39, 0x5c, 0x1d, 0x2d, 0x09, 0xe8, + 0xf8, 0xfc, 0x41, 0x31, 0x39, 0x54, 0xea, 0x93, 0x31, 0x18, 0x6a, 0xc6, + 0xcc, 0xa0, 0x4c, 0xff, 0xf3, 0xd3, 0x62, 0x51, 0x43, 0xe6, 0x06, 0x54, + 0x37, 0x17, 0x5d, 0x3a, 0x92, 0x1f, 0x48, 0x34, 0x28, 0xf9, 0xdd, 0x51, + 0x75, 0xd7, 0x65, 0x2a, 0xac, 0x88, 0xaf, 0x49, 0x76, 0x39, 0xd0, 0xc5, + 0x16, 0x74, 0xb3, 0xd9, 0x0c, 0x33, 0x11, 0x62, 0x68, 0xa8, 0x43, 0x75, + 0x52, 0x0b, 0xbc, 0x3a, 0xb7, 0xc8, 0xef, 0x19, 0xd3, 0x52, 0xc5, 0x17, + 0xc6, 0x15, 0x97, 0x9f, 0xc6, 0xb2, 0xc5, 0x3f, 0x01, 0xba, 0xe2, 0x32, + 0x21, 0x8a, 0xd8, 0x20, 0x33, 0x6e, 0xdc, 0xf6, 0x60, 0x89, 0x67, 0x11, + 0x59, 0x18, 0xb3, 0x12, 0x01, 0xf4, 0xb3, 0x55, 0xe8, 0x02, 0x4e, 0x2d, + 0x54, 0x97, 0xc3, 0x2e, 0x1f, 0xc1, 0xf1, 0xb8, 0xb0, 0xd9, 0x69, 0x68, + 0xe4, 0x0d, 0x2c, 0xca, 0xe3, 0x70, 0x2c, 0xff, 0x1f, 0x8e, 0x27, 0x3f, + 0x53, 0x1d, 0xe0, 0x9b, 0xb4, 0xd1, 0xcd, 0xb4, 0x3f, 0x01, 0x11, 0x7b, + 0x69, 0xf3, 0x0f, 0x22, 0xde, 0x74, 0x47, 0x45, 0x64, 0xf0, 0xf0, 0xa7, + 0x8b, 0xba, 0xc0, 0xa4, 0xcf, 0xeb, 0xf9, 0x1d, 0x12, 0xcd, 0xdb, 0x2c, + 0x63, 0xc1, 0x86, 0xc0, 0xdf, 0xd2, 0xe2, 0x39, 0x66, 0xe3, 0x31, 0x19, + 0x2f, 0x14, 0x28, 0xf2, 0xb2, 0xb3, 0x6b, 0x5f, 0x31, 0xfb, 0x8a, 0x43, + 0xf4, 0x96, 0x10, 0x2b, 0xaa, 0x24, 0x39, 0x2a, 0x33, 0xa6, 0x5b, 0xae, + 0x30, 0xf9, 0xeb, 0xee, 0xfc, 0x5a, 0x84, 0x98, 0x93, 0x57, 0x9c, 0x08, + 0x97, 0xcd, 0xc6, 0x03, 0x62, 0xdd, 0x6f, 0xb8, 0x14, 0x99, 0x99, 0xa8, + 0x24, 0xaa, 0xc3, 0xee, 0x64, 0xdb, 0x6a, 0xad, 0x09, 0x1e, 0x15, 0xa8, + 0x19, 0x29, 0x68, 0x58, 0x31, 0xa3, 0x0d, 0xa1, 0x35, 0xf5, 0xef, 0x33, + 0xb6, 0x7f, 0xe7, 0x99, 0xc0, 0x96, 0xfb, 0x2c, 0x30, 0x8f, 0x68, 0x71, + 0xae, 0x9d, 0x25, 0x2f, 0xca, 0xee, 0x33, 0x98, 0xbc, 0x01, 0x16, 0x8e, + 0x04, 0x04, 0x80, 0x4e, 0x4f, 0x2b, 0x5b, 0x8b, 0xa1, 0x28, 0x30, 0x7c, + 0xa7, 0x34, 0x39, 0x51, 0x51, 0x98, 0x53, 0xe8, 0x2f, 0x3c, 0x93, 0x76, + 0xe9, 0x28, 0x18, 0x86, 0x2c, 0xd2, 0x2a, 0xff, 0x62, 0x54, 0x13, 0xe6, + 0x02, 0x01, 0xfa, 0x4b, 0x0c, 0x85, 0x43, 0x5f, 0x6f, 0x1e, 0x7a, 0xb2, + 0xed, 0xf2, 0x3d, 0xcd, 0x0e, 0x91, 0x36, 0xcf, 0x88, 0x79, 0xef, 0x1c, + 0x4a, 0xb0, 0x8b, 0x78, 0x0a, 0xa4, 0xcc, 0x83, 0x83, 0x22, 0xef, 0x3f, + 0xa7, 0x0d, 0x7f, 0xfa, 0xbd, 0x6b, 0xd9, 0x3a, 0x58, 0xa9, 0xcf, 0x1e, + 0x69, 0x73, 0xc6, 0x55, 0x24, 0x22, 0x05, 0x69, 0x87, 0xde, 0x55, 0x5f, + 0xd8, 0x6d, 0xef, 0xc8, 0x62, 0xe8, 0xdc, 0x17, 0x94, 0x48, 0xc4, 0x31, + 0x62, 0xd3, 0x63, 0x4b, 0xd7, 0x64, 0x9c, 0xc2, 0x88, 0xd1, 0xce, 0x28, + 0x8c, 0xb4, 0x12, 0xa0, 0xdf, 0xce, 0x1a, 0x0f, 0xc3, 0x8e, 0xf5, 0x35, + 0xa4, 0x64, 0xcf, 0xb2, 0x44, 0xfe, 0x1e, 0x21, 0x71, 0x6d, 0x68, 0x78, + 0x0f, 0x68, 0xe1, 0x62, 0xb4, 0xf3, 0x51, 0x89, 0xd9, 0x2c, 0x7d, 0x3e, + 0xc4, 0xb5, 0xfd, 0x20, 0x98, 0xc9, 0x9e, 0xbd, 0xdd, 0xcf, 0xc6, 0x9e, + 0x5f, 0xbe, 0xbd, 0xd9, 0x87, 0xea, 0xd8, 0x21, 0x1c, 0xe1, 0x7c, 0x40, + 0x29, 0x39, 0x16, 0x56, 0x16, 0xfc, 0x99, 0x03, 0x09, 0xd8, 0x99, 0x4c, + 0x69, 0x4b, 0x0b, 0x22, 0xfe, 0xa1, 0x09, 0x88, 0x64, 0xa2, 0x40, 0x91, + 0xe8, 0xe0, 0x63, 0xea, 0x81, 0x44, 0x80, 0xb3, 0x78, 0x4f, 0xb2, 0x06, + 0x08, 0x19, 0x9c, 0xdd, 0x39, 0x0d, 0xb5, 0xb6, 0xd2, 0xca, 0x55, 0x50, + 0xc5, 0x9f, 0x75, 0x54, 0x53, 0x06, 0x65, 0x78, 0x38, 0xcc, 0xb1, 0xd0, + 0xbd, 0x2c, 0xe9, 0x26, 0xd2, 0x51, 0x05, 0xdc, 0x7b, 0x60, 0x80, 0x7e, + 0x20, 0x02, 0x3d, 0xe8, 0x4a, 0x8d, 0xc9, 0xbc, 0x9c, 0x7a, 0xaa, 0x03, + 0xa3, 0xc4, 0x56, 0x72, 0xb6, 0x77, 0x96, 0x9d, 0x77, 0xe6, 0x68, 0xcb, + 0x79, 0x55, 0x65, 0x0b, 0x03, 0x4a, 0x65, 0x53, 0xe1, 0xaf, 0x6d, 0xc9, + 0x87, 0x84, 0xe6, 0x02, 0x05, 0x2e, 0xcb, 0x02, 0x70, 0x9a, 0xe3, 0xf3, + 0x16, 0x23, 0xad, 0x20, 0x95, 0x0d, 0x8b, 0x74, 0x9a, 0xdc, 0x8e, 0x9d, + 0x00, 0x9c, 0x88, 0x15, 0x3e, 0x8b, 0x6d, 0xb8, 0xce, 0x62, 0xba, 0x49, + 0xe8, 0x53, 0x7a, 0x77, 0x97, 0xc0, 0x08, 0x8f, 0xb0, 0x6c, 0x22, 0x8b, + 0x6e, 0x37, 0xee, 0x87, 0x3f, 0x49, 0xfa, 0xdd, 0x6e, 0x8e, 0xb4, 0x6b, + 0x99, 0x69, 0xf7, 0xff, 0x2e, 0xf6, 0xe1, 0x94, 0x36, 0x95, 0x51, 0x09, + 0xc7, 0x4c, 0xa7, 0xe2, 0x40, 0xe5, 0xd6, 0xe0, 0x67, 0xd6, 0x56, 0x57, + 0xf2, 0xb9, 0x77, 0x2e, 0x22, 0xbf, 0xd2, 0x63, 0xc7, 0xe7, 0x76, 0x88, + 0x68, 0x34, 0x1f, 0xa0, 0xdd, 0x4e, 0x84, 0xe1, 0x1a, 0xca, 0x96, 0x79, + 0xfb, 0x46, 0x05, 0x7b, 0x83, 0xbe, 0x6f, 0x11, 0xea, 0x54, 0x1a, 0x02, + 0x21, 0x1b, 0x85, 0xd7, 0x68, 0xaf, 0x20, 0x01, 0x54, 0x0f, 0xa0, 0x50, + 0x55, 0xfe, 0xea, 0x24, 0xc4, 0x3e, 0xdb, 0xd4, 0x8a, 0x79, 0x14, 0xed, + 0xb0, 0xf5, 0xd5, 0xbd, 0x00, 0x14, 0x8e, 0x57, 0xb8, 0x35, 0xbc, 0x7c, + 0xf1, 0x65, 0xc5, 0xb5, 0x8a, 0x96, 0x0c, 0x08, 0xf8, 0x1d, 0x58, 0xc0, + 0x5b, 0xc2, 0x7d, 0xac, 0xc0, 0xd9, 0xf6, 0x44, 0x34, 0x08, 0x2c, 0x35, + 0x97, 0x6f, 0xde, 0x08, 0xf3, 0xd5, 0xdd, 0x52, 0xf8, 0x72, 0xe1, 0xd8, + 0xef, 0xa1, 0xe2, 0x5f, 0x20, 0x58, 0x5c, 0xc4, 0xb7, 0x17, 0x44, 0xdb, + 0x7b, 0xca, 0x7f, 0x5c, 0xa6, 0x15, 0xf5, 0x2a, 0xb1, 0x41, 0xe1, 0x45, + 0x53, 0x94, 0xaa, 0x35, 0x8e, 0x2e, 0x3b, 0x55, 0xd6, 0x1d, 0x9d, 0xe9, + 0x6a, 0x19, 0xcf, 0xff, 0xa0, 0x34, 0x30, 0x32, 0x6d, 0x81, 0x39, 0xf0, + 0xf5, 0x83, 0xfa, 0xa3, 0x24, 0x43, 0x2b, 0x9b, 0x07, 0x35, 0xff, 0x16, + 0x00, 0xcf, 0xcd, 0x7d, 0x3e, 0xb0, 0xa7, 0xeb, 0xe5, 0x50, 0x97, 0xa9, + 0xae, 0x58, 0x8f, 0x37, 0xc5, 0xc9, 0x27, 0x7b, 0x6f, 0x9b, 0x62, 0xe5, + 0xfd, 0xd8, 0x2a, 0x66, 0x13, 0x69, 0x75, 0x84, 0x7a, 0x7d, 0x8d, 0x07, + 0xba, 0x12, 0x40, 0xa3, 0x09, 0xcb, 0xf1, 0x6a, 0xb8, 0xe2, 0x9b, 0x85, + 0xc4, 0xe6, 0x82, 0x86, 0x67, 0xf1, 0xb2, 0x14, 0x11, 0x9f, 0x49, 0x39, + 0xe9, 0xcb, 0xc5, 0x9a, 0x2b, 0x77, 0x2e, 0x06, 0xf1, 0x75, 0x33, 0x1e, + 0x6f, 0xa9, 0xd7, 0x56, 0xae, 0xd4, 0xc4, 0x1f, 0x5c, 0x93, 0xcd, 0xed, + 0x37, 0xa8, 0xc3, 0x4f, 0x58, 0x1d, 0x81, 0x2a, 0x68, 0x19, 0x28, 0x57, + 0xbf, 0x66, 0xb0, 0x5b, 0x67, 0xca, 0x15, 0xb1, 0x1d, 0x06, 0x54, 0x33, + 0xa5, 0x06, 0x9b, 0xea, 0x42, 0xea, 0x0c, 0xda, 0x9b, 0x0e, 0x4f, 0xff, + 0xb6, 0xc8, 0x9e, 0x38, 0x06, 0xfa, 0xbb, 0x90, 0x41, 0xab, 0x9a, 0xea, + 0xa4, 0x59, 0xfb, 0x91, 0x83, 0x4a, 0x32, 0xff, 0x2e, 0x49, 0x3e, 0x2a, + 0x12, 0xa0, 0x34, 0x65, 0xff, 0xd9, 0x7b, 0x2c, 0x28, 0x77, 0xeb, 0x70, + 0x7a, 0x27, 0x29, 0x60, 0x6c, 0x80, 0xbc, 0x00, 0x7a, 0x15, 0xba, 0xaf, + 0xe8, 0x59, 0xf3, 0x0b, 0x92, 0x67, 0x0c, 0x24, 0xcf, 0xc0, 0x61, 0x38, + 0xaa, 0x00, 0x3c, 0x8d, 0x53, 0xb7, 0x93, 0xe0, 0x25, 0x37, 0x29, 0x91, + 0x5f, 0x79, 0xda, 0xa8, 0x9d, 0x07, 0x1c, 0x1a, 0x6c, 0xb7, 0xdf, 0x2c, + 0xab, 0x09, 0x55, 0x0d, 0x2b, 0xc4, 0x31, 0x27, 0xa2, 0x42, 0xdf, 0x17, + 0x5f, 0xf8, 0x08, 0x65, 0x71, 0xc9, 0x34, 0x8f, 0xf8, 0xa1, 0xd9, 0x23, + 0x93, 0x3f, 0xcc, 0x79, 0x60, 0xb7, 0x66, 0xf3, 0x20, 0x18, 0x72, 0x74, + 0x3f, 0x70, 0xce, 0x12, 0x80, 0x9f, 0xfb, 0xd6, 0xb2, 0x3b, 0x0b, 0xc3, + 0xa5, 0xc4, 0x81, 0xb9, 0xe0, 0x62, 0x1c, 0x5f, 0x68, 0x24, 0x72, 0xf0, + 0x14, 0xda, 0x0f, 0xc9, 0x75, 0xb2, 0x3d, 0xa9, 0x98, 0x90, 0xa4, 0x21, + 0xe1, 0x1f, 0xf4, 0x7a, 0x7e, 0x35, 0xe5, 0x54, 0xa9, 0x8a, 0xa3, 0x72, + 0x41, 0x9f, 0xbd, 0x2b, 0x31, 0x74, 0xd4, 0xa3, 0xb9, 0x40, 0xa2, 0x71, + 0x0a, 0x55, 0x39, 0xf0, 0x90, 0x97, 0x03, 0xb0, 0x6e, 0x93, 0xad, 0x43, + 0xc6, 0x86, 0x5c, 0x1d, 0x4a, 0x21, 0xe0, 0xa9, 0x68, 0x7d, 0x12, 0xee, + 0x7d, 0x33, 0xcc, 0x64, 0x4e, 0x4a, 0xe4, 0x86, 0x12, 0xe8, 0x4f, 0xbf, + 0x18, 0x2d, 0x16, 0xe0, 0xf2, 0xca, 0x22, 0x88, 0x35, 0xcd, 0xf4, 0xab, + 0x67, 0xe8, 0x60, 0x6c, 0xa9, 0x84, 0xa4, 0x51, 0xeb, 0x7f, 0x4f, 0xb8, + 0x70, 0x1b, 0x2f, 0xbf, 0x5b, 0x85, 0xcd, 0x7d, 0xd5, 0xde, 0xe6, 0x64, + 0x6f, 0xd5, 0x7a, 0x6d, 0xec, 0x53, 0x56, 0x61, 0xae, 0xc0, 0x52, 0xe5, + 0x2d, 0xc1, 0xf1, 0xdf, 0x1f, 0x7a, 0x7c, 0x4a, 0x8a, 0x4d, 0x7d, 0xcc, + 0x5b, 0xcd, 0x88, 0xd6, 0x56, 0x25, 0xb1, 0x5e, 0xf5, 0xf8, 0x34, 0x8c, + 0xad, 0x6e, 0x85, 0x6b, 0xd1, 0x86, 0x32, 0xf8, 0x1b, 0xc5, 0x10, 0xd1, + 0xce, 0xdf, 0xd0, 0x74, 0x99, 0x5b, 0x56, 0xab, 0xfb, 0x6e, 0x25, 0x8d, + 0x9a, 0x87, 0xc7, 0x4d, 0x14, 0xc4, 0x58, 0x11, 0x08, 0x71, 0x7f, 0x26, + 0x8c, 0xee, 0x76, 0x16, 0x73, 0x98, 0x8a, 0xf3, 0x58, 0xb0, 0xa8, 0x18, + 0xd5, 0xc3, 0x2a, 0x90, 0xe2, 0x05, 0xb6, 0xe6, 0xa3, 0x34, 0xda, 0x7e, + 0xa0, 0xa1, 0x61, 0x6b, 0xfe, 0x20, 0x49, 0x1f, 0x8e, 0x1d, 0x70, 0x6d, + 0x13, 0x59, 0x78, 0x99, 0xb0, 0x91, 0xaa, 0xc3, 0x53, 0xe0, 0xeb, 0x4a, + 0x0d, 0xeb, 0x08, 0xa2, 0xb1, 0xe4, 0x8f, 0x8b, 0x69, 0x40, 0x21, 0x82, + 0x1e, 0x15, 0x23, 0xb4, 0xce, 0xc4, 0xd2, 0xfe, 0xb0, 0xe6, 0x77, 0xba, + 0xe1, 0x7f, 0x11, 0xb9, 0x4d, 0xfb, 0x0b, 0x86, 0xd5, 0xb1, 0x65, 0x9f, + 0x7f, 0x48, 0xe1, 0x6e, 0x73, 0xa5, 0x00, 0x36, 0xf6, 0x80, 0xfe, 0x98, + 0x0f, 0x62, 0x71, 0xa5, 0x9a, 0x5b, 0x3c, 0x5c, 0xb0, 0x93, 0x9b, 0x46, + 0x9c, 0x0d, 0x3b, 0x4f, 0x64, 0x59, 0x7f, 0x27, 0x76, 0xc5, 0x50, 0x89, + 0x04, 0x64, 0x80, 0xf4, 0xdd, 0x52, 0x40, 0x40, 0x47, 0xc4, 0x43, 0xe3, + 0xbc, 0xd3, 0x2e, 0xab, 0x6f, 0xee, 0xf1, 0xbe, 0x16, 0x73, 0x2f, 0x26, + 0xdb, 0x23, 0x55, 0x9f, 0xbb, 0x0d, 0xb9, 0xae, 0x5c, 0xfb, 0x37, 0x74, + 0xb1, 0x3f, 0x60, 0x39, 0x42, 0xbf, 0x1f, 0xd5, 0x51, 0x9f, 0x3f, 0x59, + 0xf4, 0x10, 0x6c, 0x59, 0x36, 0x68, 0x9e, 0x54, 0xca, 0xc8, 0x7d, 0x6c, + 0x24, 0xc2, 0x78, 0x79, 0x5e, 0x69, 0x22, 0x90, 0x52, 0xa1, 0x42, 0x34, + 0xc0, 0x9c, 0x84, 0x09, 0x26, 0xe1, 0x10, 0x3e, 0x49, 0xcb, 0xea, 0xc6, + 0x5e, 0x1d, 0x47, 0xa5, 0x57, 0xd4, 0x4b, 0x2c, 0xb8, 0x4c, 0xc0, 0xba, + 0x65, 0xe9, 0xc9, 0x55, 0xef, 0x50, 0x65, 0x6f, 0x0f, 0xc6, 0xcf, 0x13, + 0x11, 0x87, 0x6c, 0x68, 0x3f, 0xf9, 0x18, 0x5e, 0x4f, 0xa2, 0x89, 0x84, + 0xae, 0xbd, 0x1b, 0xa5, 0x93, 0xac, 0xab, 0xd7, 0x98, 0xf1, 0x57, 0xc7, + 0xf1, 0xb5, 0xbc, 0x39, 0xff, 0xbd, 0x38, 0xb0, 0x02, 0x44, 0x9f, 0x50, + 0x2d, 0x80, 0xaf, 0xd9, 0xc2, 0xf1, 0x99, 0x7e, 0x0d, 0xea, 0xb0, 0xbd, + 0x49, 0xc5, 0xc5, 0x82, 0xb2, 0x49, 0x24, 0xbe, 0x0e, 0xaf, 0x7e, 0x53, + 0xf5, 0x9b, 0xe4, 0xab, 0xc9, 0x4d, 0xf2, 0x3f, 0x60, 0x5f, 0xec, 0xed, + 0x88, 0xa0, 0x00, 0x43, 0xf5, 0xb5, 0x06, 0xbb, 0x8a, 0xc4, 0x57, 0x6a, + 0xd2, 0xe8, 0xf7, 0xe0, 0x83, 0xb6, 0x7b, 0x1d, 0xf8, 0xd4, 0x85, 0xc0, + 0x2e, 0xac, 0x96, 0x95, 0xcb, 0xd7, 0xf8, 0x9c, 0x54, 0x1a, 0xd3, 0xe6, + 0x02, 0xee, 0x7f, 0x4f, 0x36, 0x6b, 0x82, 0xde, 0x66, 0x03, 0x58, 0xfc, + 0x11, 0x1f, 0x5e, 0x77, 0x02, 0x89, 0x9b, 0x69, 0x6a, 0xb2, 0x97, 0xb1, + 0x14, 0x69, 0xf2, 0x5a, 0xc2, 0x41, 0x5f, 0x16, 0xd7, 0x1b, 0x23, 0xca, + 0xb3, 0x58, 0x2d, 0xe7, 0xad, 0x25, 0x69, 0xc5, 0xdd, 0x70, 0x7b, 0x31, + 0x41, 0xed, 0x27, 0xa3, 0x06, 0xe0, 0xde, 0xb5, 0xc3, 0xe6, 0x90, 0x06, + 0x7a, 0x8c, 0x8a, 0xc4, 0x97, 0x92, 0x46, 0x52, 0x6c, 0xd2, 0x22, 0x68, + 0xf0, 0x64, 0xdd, 0xd0, 0x05, 0x96, 0xcd, 0x94, 0x1c, 0x1c, 0xc3, 0x7b, + 0xfc, 0x5b, 0x50, 0x6a, 0x75, 0x1c, 0x4b, 0x1c, 0x14, 0x02, 0xdf, 0x97, + 0xb0, 0x36, 0x92, 0x3c, 0xa2, 0xfa, 0xe4, 0x8f, 0xe3, 0xc3, 0x48, 0x4d, + 0x68, 0x48, 0x52, 0xd2, 0x64, 0xf0, 0x52, 0x5b, 0xe5, 0x05, 0x53, 0x2b, + 0xb7, 0x30, 0x32, 0xdc, 0x12, 0xb2, 0x16, 0x0e, 0x2f, 0x17, 0xfa, 0x0e, + 0xb3, 0x33, 0xa6, 0xfc, 0xf8, 0xff, 0x03, 0x87, 0x4d, 0xa0, 0x5b, 0x56, + 0x48, 0x34, 0x3b, 0x22, 0x5e, 0x2e, 0xef, 0xbf, 0xdd, 0x23, 0x7a, 0x04, + 0x0c, 0x29, 0x0e, 0x3a, 0xa4, 0x66, 0x16, 0x8a, 0xae, 0x7e, 0x9d, 0x32, + 0xd6, 0x32, 0x5c, 0x75, 0x2c, 0xc0, 0xee, 0xbf, 0xa9, 0xea, 0x73, 0xd4, + 0x74, 0x40, 0xdb, 0x41, 0x3e, 0xdc, 0x87, 0x5e, 0xab, 0xc5, 0xa4, 0xfd, + 0xa8, 0xa6, 0x8b, 0x05, 0x2d, 0x2d, 0x1b, 0x52, 0x27, 0xeb, 0x47, 0x2f, + 0x97, 0xdb, 0x89, 0x6b, 0x65, 0x4d, 0x22, 0x0c, 0x7a, 0x0e, 0x41, 0x8e, + 0xa9, 0x15, 0x14, 0x30, 0x05, 0x5a, 0xac, 0xd5, 0x36, 0x2f, 0x96, 0x9f, + 0xf3, 0xa6, 0x59, 0xa3, 0xbf, 0xcc, 0x3c, 0xe2, 0x9b, 0xd6, 0x3d, 0xfd, + 0xfc, 0x54, 0xb3, 0x17, 0xe1, 0x4f, 0x22, 0x60, 0x79, 0x80, 0xaf, 0xbb, + 0x1a, 0x42, 0x03, 0x74, 0x85, 0x87, 0xb9, 0x9a, 0x10, 0x68, 0x77, 0xe8, + 0x94, 0x67, 0xb7, 0x5e, 0x3f, 0x32, 0x50, 0xf0, 0x7b, 0x41, 0x67, 0x0c, + 0x42, 0x6a, 0xdd, 0xcf, 0x2b, 0x03, 0xb4, 0xa4, 0x60, 0x39, 0x5c, 0x4a, + 0xec, 0xdb, 0xf3, 0x12, 0x12, 0x80, 0x87, 0xdb, 0x23, 0x46, 0xee, 0x64, + 0x1f, 0xc5, 0xfb, 0xf9, 0x49, 0xfa, 0xee, 0x1d, 0x57, 0x79, 0x5f, 0x3f, + 0xd7, 0xb2, 0xa4, 0x42, 0xee, 0xc6, 0x97, 0x87, 0x93, 0x88, 0x66, 0xac, + 0xd7, 0x91, 0xf1, 0xc1, 0xe1, 0xfa, 0x3d, 0x2a, 0xf2, 0x07, 0xc3, 0xc7, + 0xe0, 0x0d, 0xaa, 0x02, 0xd4, 0x7b, 0x78, 0xd2, 0x7f, 0x2f, 0xd9, 0x81, + 0x3f, 0x84, 0x6a, 0xbb, 0xc2, 0x24, 0x62, 0x6e, 0xac, 0x4a, 0x3a, 0x0e, + 0x93, 0x0b, 0x45, 0xb8, 0x75, 0x76, 0xcb, 0xae, 0x55, 0x4d, 0x10, 0x6e, + 0xfa, 0x2f, 0x19, 0xb0, 0x74, 0x04, 0xd0, 0xd7, 0xdb, 0x3d, 0xc2, 0x7e, + 0x73, 0xa4, 0x90, 0x6f, 0x30, 0xab, 0x09, 0x28, 0x28, 0x46, 0x97, 0x46, + 0x04, 0xa1, 0x59, 0x85, 0xc4, 0x1a, 0xbb, 0xdb, 0x8b, 0xbe, 0x22, 0x05, + 0x2e, 0x9f, 0x4f, 0xe7, 0xd9, 0x60, 0x4b, 0xb5, 0x4f, 0xbe, 0xe3, 0x2e, + 0x81, 0xdd, 0x78, 0x96, 0x62, 0xe7, 0xda, 0xce, 0x83, 0x8b, 0x59, 0xdc, + 0x5f, 0x23, 0x2f, 0x79, 0x6c, 0x91, 0x99, 0xc0, 0xc9, 0x6f, 0x34, 0x6c, + 0x83, 0xed, 0x01, 0x9d, 0xc4, 0x5f, 0x08, 0x54, 0xaf, 0xc0, 0x17, 0x93, + 0xb7, 0xa8, 0x66, 0x37, 0xb2, 0xc9, 0xa6, 0x3b, 0x3f, 0xbb, 0x33, 0xe4, + 0xa0, 0x90, 0x26, 0x1c, 0x02, 0x5a, 0x4e, 0xe9, 0x6d, 0x1a, 0x29, 0x34, + 0x7a, 0x81, 0x38, 0x71, 0xb2, 0x69, 0x71, 0xe1, 0xcc, 0xb5, 0x36, 0xaf, + 0x3e, 0xd7, 0x2f, 0x2a, 0x31, 0xf3, 0xa9, 0x5e, 0xf7, 0x14, 0x65, 0xee, + 0x3d, 0xe5, 0x34, 0x8a, 0x28, 0xe5, 0xb3, 0x81, 0x9b, 0xda, 0x9b, 0xa9, + 0xc1, 0xa9, 0xc4, 0x30, 0x0c, 0xc7, 0x1f, 0x3d, 0xf9, 0x62, 0x3f, 0x2b, + 0x86, 0x0c, 0x3e, 0xe9, 0x45, 0xee, 0x85, 0x03, 0xb8, 0x38, 0x8d, 0x6b, + 0x89, 0x45, 0x5d, 0x23, 0x3f, 0x13, 0x33, 0x3a, 0x36, 0x59, 0xe4, 0x85, + 0x4f, 0x10, 0x00, 0xbc, 0x99, 0xeb, 0xf1, 0x60, 0x00, 0xeb, 0x22, 0xd9, + 0x6d, 0xbd, 0xbb, 0xd9, 0x32, 0x59, 0x8a, 0xc9, 0x0e, 0x12, 0x3f, 0x1d, + 0xf0, 0x44, 0xd4, 0xd4, 0x14, 0x07, 0x97, 0x8f, 0xf5, 0xaf, 0xfe, 0xf3, + 0xf4, 0x99, 0x36, 0x0d, 0xb5, 0x7b, 0x95, 0xb2, 0x9e, 0xc9, 0xf0, 0x48, + 0x6c, 0x5f, 0x61, 0x18, 0x44, 0x37, 0x37, 0x96, 0xbd, 0x5b, 0x92, 0xf9, + 0xb3, 0x9d, 0xf6, 0xca, 0x39, 0xe7, 0xcb, 0x2a, 0x9a, 0xe2, 0x87, 0x17, + 0x43, 0xa9, 0xda, 0x67, 0x70, 0xbe, 0xae, 0x00, 0xe2, 0x8f, 0x80, 0xe7, + 0x9a, 0x2e, 0xdc, 0x1c, 0x4a, 0xb5, 0x6c, 0x2a, 0x24, 0x03, 0xd7, 0x7b, + 0xdb, 0x7f, 0x39, 0xd0, 0xdf, 0x86, 0x99, 0xd4, 0x30, 0x88, 0xee, 0xaf, + 0x22, 0x78, 0x8f, 0xd4, 0xf3, 0x20, 0x57, 0x18, 0xfb, 0x62, 0x79, 0x1a, + 0xbc, 0xb2, 0xc7, 0xc1, 0x99, 0x8a, 0xf9, 0x5c, 0x87, 0xfd, 0x2b, 0x0b, + 0x74, 0x72, 0x73, 0x7a, 0x06, 0x02, 0x39, 0x92, 0x64, 0x4a, 0x4d, 0x01, + 0x9d, 0x18, 0x62, 0xfb, 0x29, 0x7f, 0x10, 0x96, 0x2b, 0xf3, 0x3f, 0x1f, + 0xc1, 0xf9, 0x39, 0x76, 0xff, 0x64, 0xba, 0x58, 0x7c, 0x5f, 0x65, 0x44, + 0x6a, 0xb2, 0xe1, 0xaf, 0xb0, 0x4c, 0xe4, 0xe8, 0xe6, 0x12, 0xc7, 0x7c, + 0xd3, 0x6e, 0x74, 0x3a, 0x13, 0x70, 0xac, 0xfb, 0x05, 0xdb, 0x71, 0x46, + 0xd9, 0x47, 0x8a, 0xa7, 0xbd, 0x1d, 0xd4, 0x5d, 0x81, 0x7d, 0x32, 0x51, + 0x88, 0x4c, 0xdd, 0x09, 0x85, 0x6d, 0x28, 0x2c, 0xab, 0x0d, 0x15, 0xe6, + 0x76, 0xdc, 0x91, 0x25, 0xd0, 0xcc, 0x7a, 0xb0, 0x61, 0x23, 0xb4, 0x46, + 0xbc, 0xeb, 0x65, 0xec, 0xe1, 0x60, 0xed, 0x3b, 0x9a, 0xf9, 0xf0, 0x2a, + 0x0e, 0xdf, 0x61, 0x92, 0x91, 0x86, 0xe1, 0x4a, 0x3b, 0x5c, 0x8e, 0x0e, + 0x76, 0x76, 0x3a, 0x62, 0x25, 0x51, 0x71, 0x1a, 0xba, 0xef, 0x7f, 0x80, + 0x28, 0xc1, 0x15, 0x12, 0x9f, 0x5f, 0x0e, 0xbd, 0x82, 0xa2, 0x2c, 0xe3, + 0x9f, 0xb9, 0x72, 0x09, 0x1e, 0x7d, 0x83, 0x88, 0xd3, 0x86, 0x6b, 0x21, + 0x3e, 0x57, 0x46, 0x88, 0xfa, 0x54, 0x08, 0x1f, 0x05, 0x03, 0xa0, 0xbb, + 0x16, 0x3e, 0xe8, 0x92, 0xe4, 0x3d, 0xde, 0x66, 0xcc, 0x6c, 0x11, 0x71, + 0x88, 0x11, 0x02, 0x3e, 0xbd, 0xfd, 0x5b, 0xf2, 0xdd, 0x05, 0x99, 0x31, + 0xc1, 0x82, 0xf6, 0x0b, 0xea, 0x5f, 0x80, 0x84, 0x20, 0xc3, 0x80, 0xa1, + 0x65, 0xb9, 0x8b, 0x18, 0xbe, 0xe3, 0x25, 0x24, 0xc7, 0x28, 0xe2, 0x2f, + 0xe6, 0x72, 0x15, 0x6b, 0x2a, 0x5f, 0x57, 0x5b, 0xed, 0xc4, 0x8e, 0x5a, + 0x29, 0x6d, 0xa5, 0x2a, 0x6c, 0xd8, 0x40, 0x3f, 0x29, 0x87, 0xeb, 0x2e, + 0x52, 0x39, 0x0d, 0x06, 0x30, 0x22, 0xf5, 0x91, 0xfe, 0x80, 0xeb, 0x8b, + 0xfb, 0x4a, 0x0b, 0xdd, 0xbf, 0x88, 0x04, 0x0d, 0xe6, 0x9d, 0x36, 0xc1, + 0xf8, 0xd5, 0x8d, 0x22, 0x45, 0x4b, 0x01, 0xb8, 0xc2, 0xbb, 0x83, 0x29, + 0xc4, 0x53, 0x44, 0x68, 0x1d, 0x5b, 0xaa, 0x28, 0x38, 0xaa, 0x8d, 0x41, + 0xba, 0x0e, 0x43, 0xba, 0x66, 0xc2, 0xc6, 0xd7, 0x77, 0x49, 0x0c, 0x22, + 0x3f, 0xe8, 0x72, 0xad, 0xbd, 0x6d, 0xb5, 0xc7, 0xe9, 0x87, 0x52, 0xfe, + 0x35, 0x11, 0x77, 0x38, 0x15, 0xc5, 0x45, 0xcd, 0x9b, 0x78, 0x57, 0x7d, + 0xb0, 0x3f, 0x43, 0xc1, 0x83, 0x33, 0x01, 0x9d, 0x86, 0x4d, 0xf8, 0xc6, + 0x5e, 0x46, 0x57, 0xb6, 0xbe, 0x86, 0x8f, 0x5a, 0x31, 0xbd, 0x33, 0xa6, + 0x39, 0x01, 0x06, 0xe3, 0x9e, 0x2c, 0x68, 0x25, 0x4c, 0x6b, 0x9e, 0x03, + 0xcf, 0x6a, 0xa6, 0xe0, 0x25, 0xb9, 0x76, 0x79, 0x57, 0x2f, 0xed, 0x25, + 0x74, 0x90, 0xcb, 0x70, 0x22, 0x8f, 0x12, 0xaa, 0x06, 0x33, 0x8d, 0x2e, + 0x8c, 0xe1, 0x4c, 0x98, 0x60, 0x32, 0xe4, 0x84, 0x19, 0xd2, 0x7f, 0x4d, + 0x1c, 0x4c, 0x39, 0xfd, 0x19, 0xe2, 0x00, 0xc8, 0xd1, 0xba, 0x37, 0x04, + 0xa6, 0x44, 0x56, 0xd8, 0x51, 0x27, 0xfe, 0xd9, 0xe2, 0x92, 0xb7, 0xf4, + 0x2d, 0x28, 0x8e, 0xb4, 0x4c, 0x48, 0xb8, 0xf5, 0x2e, 0x4f, 0xd4, 0xc1, + 0x43, 0xac, 0x2e, 0x4a, 0x90, 0x06, 0xae, 0xd9, 0xac, 0x5d, 0xde, 0x80, + 0xef, 0x03, 0xc2, 0x18, 0x35, 0x18, 0x99, 0x86, 0x93, 0x38, 0xdd, 0x7c, + 0xde, 0x48, 0xcd, 0x50, 0xc4, 0xd4, 0xe6, 0xd0, 0xf0, 0xb3, 0x6a, 0xf5, + 0x87, 0x4d, 0xe0, 0x5b, 0x69, 0xac, 0x62, 0x11, 0xac, 0x69, 0x11, 0x1a, + 0xbc, 0xea, 0x26, 0x9f, 0xc5, 0x2d, 0x2d, 0xb1, 0x8a, 0xf3, 0x55, 0xd5, + 0x66, 0x36, 0x21, 0xb7, 0xb9, 0x0b, 0x23, 0x45, 0x86, 0xab, 0x49, 0xee, + 0x26, 0xaa, 0x19, 0x4d, 0x0e, 0xc7, 0x05, 0x13, 0x7a, 0xd0, 0xa5, 0x8c, + 0x24, 0x43, 0x60, 0xfd, 0xaa, 0xcf, 0x01, 0x8e, 0xc5, 0xd4, 0x83, 0x91, + 0x8d, 0x3d, 0xd1, 0xea, 0xba, 0x3d, 0x7a, 0x54, 0x26, 0x01, 0x7d, 0x6b, + 0x99, 0xf0, 0x36, 0x24, 0x11, 0xd9, 0xcb, 0xae, 0x86, 0x48, 0xb6, 0x41, + 0xae, 0x2e, 0x4a, 0x8f, 0xf8, 0x67, 0x8b, 0x56, 0x5a, 0x27, 0xa6, 0xaa, + 0xb9, 0x04, 0xbf, 0x12, 0x11, 0xde, 0x9f, 0x05, 0x90, 0x14, 0x77, 0xcc, + 0x1d, 0x59, 0x93, 0x61, 0xa3, 0x80, 0x93, 0xa1, 0x08, 0x29, 0x47, 0x45, + 0xf5, 0x2a, 0xf7, 0x01, 0xb4, 0x96, 0xe3, 0x36, 0x89, 0x7c, 0xa7, 0xf2, + 0xb3, 0xbd, 0x24, 0x7b, 0x31, 0xe8, 0x25, 0xc2, 0x05, 0x18, 0x9d, 0x39, + 0xa6, 0xb4, 0x7b, 0x4a, 0x75, 0xad, 0x47, 0x19, 0x7f, 0x0f, 0x61, 0xab, + 0x6e, 0x22, 0x93, 0xfe, 0xa2, 0x11, 0x09, 0x12, 0xb3, 0x4b, 0xe2, 0x60, + 0x3b, 0x77, 0x7e, 0x5c, 0xcc, 0x86, 0x2a, 0xe0, 0x77, 0xb0, 0x94, 0xbf, + 0xa5, 0x9f, 0x79, 0x90, 0x7d, 0xed, 0xad, 0x29, 0xf3, 0xfc, 0x50, 0x0b, + 0xda, 0xbb, 0x35, 0xe4, 0x0c, 0x19, 0x28, 0xd7, 0x23, 0x65, 0x29, 0x2d, + 0x5b, 0xbd, 0x9d, 0x8c, 0xe0, 0x17, 0xb3, 0x3a, 0xa7, 0x08, 0xab, 0x90, + 0x27, 0x86, 0x8a, 0x2e, 0x4c, 0xe0, 0x77, 0x18, 0xe3, 0xa3, 0x5a, 0x33, + 0x14, 0x72, 0x10, 0xda, 0x09, 0xe7, 0xae, 0xd1, 0x2c, 0x3c, 0x82, 0x6b, + 0x5e, 0xf5, 0x7e, 0x8d, 0x60, 0xca, 0x75, 0xa1, 0x4a, 0x19, 0x24, 0x8e, + 0x12, 0x23, 0x2f, 0x8c, 0x68, 0x2d, 0xfd, 0xdd, 0xa7, 0xef, 0xaf, 0x9a, + 0x53, 0x9a, 0x0d, 0x5e, 0x84, 0x8e, 0x58, 0xc9, 0x88, 0x83, 0xc2, 0x70, + 0xdc, 0xb1, 0x7c, 0x70, 0xbe, 0x41, 0x3e, 0xb4, 0xea, 0x28, 0x0c, 0xd4, + 0x77, 0x00, 0x52, 0xed, 0xed, 0x07, 0x26, 0x63, 0x1e, 0xf8, 0x7d, 0x6e, + 0x6e, 0x2f, 0x70, 0x96, 0x3f, 0x8d, 0x68, 0x4f, 0x9f, 0x26, 0xa0, 0x15, + 0x1a, 0x1d, 0x51, 0x50, 0x27, 0x40, 0x97, 0xf5, 0xf0, 0x2c, 0x3e, 0x05, + 0xb2, 0x52, 0x52, 0x91, 0x37, 0xe1, 0x19, 0x7f, 0xa2, 0xc7, 0x4e, 0xea, + 0x8b, 0xa5, 0x0e, 0x57, 0x55, 0x17, 0xdc, 0x5e, 0x83, 0x03, 0x09, 0x9f, + 0x93, 0xdc, 0x65, 0x68, 0x1f, 0x19, 0x4b, 0xdf, 0xac, 0x38, 0x4e, 0x40, + 0x5f, 0x85, 0x44, 0x27, 0x97, 0x53, 0x27, 0x18, 0xe4, 0x62, 0x02, 0x34, + 0xbe, 0xa3, 0x3d, 0xfd, 0x2a, 0x1a, 0x82, 0xd9, 0x4d, 0xc2, 0xba, 0x15, + 0x27, 0xb4, 0xb5, 0x93, 0xc7, 0xdf, 0x1a, 0x3e, 0x59, 0x71, 0xb5, 0xbd, + 0x5b, 0x07, 0xff, 0xad, 0x99, 0xd2, 0x72, 0xa0, 0x7b, 0x0c, 0xf4, 0x84, + 0x29, 0x73, 0xdc, 0x7d, 0xaf, 0xa6, 0xb5, 0x62, 0xfd, 0x6d, 0x07, 0xe3, + 0xee, 0x73, 0x88, 0x9d, 0x86, 0xca, 0x9e, 0x9f, 0xb3, 0x97, 0x6e, 0x29, + 0x8a, 0xba, 0xf7, 0x04, 0xe5, 0xff, 0x73, 0x5e, 0x50, 0x62, 0x66, 0xbb, + 0x60, 0x72, 0x11, 0x85, 0x68, 0x4d, 0xc6, 0xaa, 0xd9, 0x09, 0x05, 0x23, + 0x19, 0xf5, 0x21, 0x56, 0xb8, 0xb9, 0x9a, 0xb6, 0xb8, 0xfa, 0x5a, 0xa4, + 0x46, 0xf9, 0x21, 0x60, 0x18, 0x5f, 0x1b, 0xb6, 0xac, 0x86, 0xb0, 0x01, + 0xb7, 0xe2, 0xe2, 0xfb, 0x48, 0xb1, 0x7c, 0x77, 0x43, 0xaa, 0x65, 0xa2, + 0x7d, 0xb5, 0xf1, 0x63, 0x9f, 0x4a, 0xdc, 0xfe, 0x96, 0x70, 0x1d, 0xfd, + 0xf2, 0x88, 0xca, 0x33, 0x73, 0x44, 0x0f, 0x72, 0x38, 0x94, 0x8f, 0xd7, + 0xe4, 0x1f, 0xb5, 0x25, 0x37, 0x7a, 0x8a, 0x47, 0x38, 0xf6, 0x80, 0x6d, + 0xcb, 0xc6, 0x22, 0xcf, 0x63, 0x77, 0x6c, 0xe0, 0x54, 0x34, 0x9c, 0xfe, + 0x39, 0x5d, 0xc1, 0x10, 0xf2, 0x5a, 0x6f, 0xb2, 0xa5, 0x01, 0xf5, 0x66, + 0x43, 0x73, 0x35, 0x1d, 0xa3, 0x70, 0x5a, 0x1f, 0x87, 0x49, 0xe4, 0x98, + 0xd9, 0x1b, 0xd7, 0xe0, 0xf5, 0x22, 0xf2, 0xab, 0xfe, 0x6d, 0xf7, 0xf6, + 0x6f, 0x60, 0x02, 0x32, 0xf9, 0xa7, 0x12, 0xbe, 0xed, 0xc3, 0x43, 0x3c, + 0x48, 0x40, 0x32, 0x60, 0x26, 0x2f, 0x5f, 0x69, 0x66, 0x06, 0x0a, 0xd2, + 0xa8, 0x50, 0x3c, 0xbf, 0x06, 0xae, 0xb0, 0xdb, 0xae, 0x9d, 0x72, 0x95, + 0x9c, 0xad, 0xf1, 0x8b, 0x31, 0x7e, 0xbe, 0x55, 0x7d, 0x01, 0xf6, 0xdf, + 0xf8, 0xc7, 0x9c, 0x03, 0x03, 0x31, 0x1d, 0xa8, 0xd6, 0x64, 0xe3, 0x5e, + 0x6d, 0x92, 0xfb, 0x97, 0x8a, 0xc1, 0x34, 0x8c, 0x45, 0xc5, 0x23, 0x85, + 0x27, 0x19, 0x60, 0xf4, 0xd7, 0x8a, 0x50, 0x58, 0xb3, 0xa9, 0x13, 0x9f, + 0xef, 0xc8, 0x0c, 0xc1, 0x81, 0x4c, 0xf2, 0x42, 0x99, 0x64, 0x2d, 0xb8, + 0xd6, 0xad, 0xd1, 0x59, 0xcb, 0xfa, 0xf1, 0x23, 0x8d, 0x30, 0x7d, 0x86, + 0x73, 0x5c, 0x53, 0x7f, 0xe4, 0x4d, 0x83, 0x10, 0xbb, 0xf2, 0xcf, 0x93, + 0x4e, 0x58, 0x60, 0x85, 0xc4, 0x12, 0x87, 0x7d, 0x4c, 0x4f, 0x6d, 0xa6, + 0x7a, 0x96, 0x02, 0x19, 0xf3, 0xcd, 0x1b, 0x88, 0x66, 0xb1, 0x26, 0x5b, + 0x35, 0x08, 0xc5, 0xdd, 0xf3, 0x16, 0xbd, 0x2b, 0x24, 0x43, 0x8d, 0xd0, + 0x16, 0xb5, 0x02, 0x27, 0xd3, 0xa4, 0xb0, 0x06, 0x34, 0xa0, 0xe8, 0x37, + 0x53, 0xbc, 0xd6, 0x1c, 0x34, 0x4d, 0x06, 0x52, 0x76, 0xb1, 0x27, 0xde, + 0x12, 0x4f, 0x8d, 0xc1, 0xf6, 0x15, 0x3c, 0x8c, 0x0f, 0xab, 0xf9, 0xf8, + 0xab, 0xa1, 0xcc, 0x7a, 0xb6, 0x43, 0x59, 0x0a, 0x22, 0xe5, 0x1b, 0xd8, + 0xd9, 0x3e, 0xd7, 0x8f, 0x38, 0x16, 0x1b, 0x44, 0x91, 0xc0, 0xbc, 0x76, + 0xfc, 0x44, 0x2a, 0x3c, 0xcd, 0x83, 0x6f, 0xba, 0x4a, 0xb1, 0x36, 0x11, + 0xbb, 0xca, 0x4d, 0xc1, 0x1a, 0x65, 0xb4, 0x5a, 0x99, 0xaf, 0x30, 0x54, + 0x52, 0x1f, 0x95, 0xad, 0x73, 0x5e, 0x92, 0x7f, 0x4d, 0x4e, 0xe8, 0x10, + 0x1b, 0xf6, 0x7d, 0x44, 0xef, 0xd1, 0xa1, 0x7c, 0x51, 0x33, 0xfb, 0x18, + 0x90, 0xa2, 0xe2, 0x8e, 0x85, 0xd4, 0xf5, 0x29, 0xc1, 0x2b, 0x81, 0x58, + 0x46, 0x3b, 0xa3, 0x0c, 0xc8, 0x91, 0x0a, 0x35, 0x6e, 0x1c, 0xf7, 0xf3, + 0x2f, 0x30, 0x6b, 0x75, 0xfa, 0xc3, 0x86, 0xb5, 0x88, 0xfc, 0x9d, 0x82, + 0xf3, 0x73, 0xd9, 0xb1, 0xb2, 0x26, 0x45, 0x61, 0xc3, 0x2f, 0xb1, 0x29, + 0xe8, 0xf5, 0x70, 0xa3, 0xda, 0x84, 0xf0, 0xe9, 0xe3, 0xde, 0x09, 0x81, + 0xae, 0x0c, 0xc5, 0xa7, 0x23, 0x58, 0x2e, 0x99, 0xda, 0x3e, 0xbe, 0x62, + 0x09, 0xd0, 0x57, 0xec, 0x37, 0x2c, 0x3f, 0x46, 0x9e, 0xba, 0x1c, 0x52, + 0x93, 0x2c, 0x66, 0xcf, 0x9b, 0xd1, 0x67, 0xac, 0xb2, 0xfd, 0xc8, 0xcd, + 0x47, 0x7e, 0x1f, 0x1e, 0x7e, 0x02, 0x87, 0xcb, 0xf2, 0x93, 0xd7, 0x4e, + 0x5a, 0xf1, 0x15, 0xf4, 0x8d, 0xaa, 0x95, 0x7a, 0xe0, 0x33, 0x70, 0x68, + 0xc7, 0x3f, 0x73, 0xfa, 0xed, 0xc1, 0xd2, 0x24, 0xc3, 0x03, 0x24, 0xc4, + 0xe9, 0x77, 0x6b, 0xc5, 0x2b, 0x8e, 0xfb, 0x8b, 0xae, 0x67, 0x20, 0x57, + 0xa9, 0xf8, 0x61, 0xf1, 0xf4, 0xa2, 0x59, 0xe4, 0xe2, 0xf6, 0xb7, 0x0f, + 0x76, 0x69, 0x33, 0xb8, 0x00, 0x80, 0x4e, 0xbd, 0xb2, 0x20, 0xb4, 0xb4, + 0x80, 0x4a, 0xb9, 0x1c, 0x8f, 0x2f, 0xd4, 0x5d, 0xdf, 0xc1, 0xd9, 0x54, + 0x02, 0xba, 0x4d, 0x24, 0x0d, 0xd5, 0x25, 0x7d, 0x06, 0x50, 0x3e, 0x69, + 0x01, 0x2c, 0xf5, 0xbe, 0x4f, 0x8e, 0x42, 0xfa, 0x5d, 0x10, 0xe3, 0xc1, + 0xaa, 0x45, 0x5c, 0x50, 0xa4, 0xdd, 0x80, 0x3a, 0xc1, 0xab, 0x46, 0x1a, + 0x32, 0x00, 0x2b, 0xf6, 0xdf, 0xb1, 0x3c, 0x6a, 0x77, 0xbf, 0x88, 0xc8, + 0x5e, 0x74, 0x34, 0x0c, 0xfd, 0x21, 0xdd, 0x82, 0x42, 0x34, 0xe4, 0x4f, + 0x68, 0x26, 0xff, 0x52, 0x25, 0x95, 0xfe, 0x23, 0x6e, 0x75, 0xd1, 0x19, + 0xb1, 0x0e, 0xb4, 0xb2, 0x33, 0x96, 0x12, 0x52, 0x7c, 0xc5, 0x11, 0x01, + 0x5e, 0x10, 0x72, 0xfa, 0x96, 0x83, 0x3b, 0x24, 0x7e, 0x91, 0x31, 0x97, + 0xbd, 0x0c, 0x4c, 0x14, 0xf2, 0xad, 0x96, 0x59, 0x6c, 0x90, 0x98, 0x87, + 0x72, 0x38, 0xda, 0xf1, 0xea, 0x9d, 0xc6, 0xd7, 0x30, 0xb7, 0x25, 0x9c, + 0x6d, 0xac, 0x6a, 0xfd, 0xe6, 0xc7, 0x6a, 0xf8, 0xac, 0xc2, 0xa3, 0x91, + 0x37, 0xd4, 0xf7, 0xc3, 0x99, 0xa7, 0x0b, 0xaf, 0xb4, 0x2e, 0x53, 0xd6, + 0x58, 0x52, 0x83, 0xf3, 0xe9, 0x4d, 0xab, 0xef, 0x0a, 0xb7, 0xaa, 0x6c, + 0x26, 0x6c, 0x5f, 0x8c, 0x62, 0x90, 0xd0, 0xc2, 0xad, 0x95, 0x80, 0x10, + 0x0e, 0x71, 0xe3, 0x8a, 0xa1, 0x39, 0x7c, 0xe9, 0x2e, 0xb6, 0x24, 0xd2, + 0xdb, 0xf3, 0x70, 0xd8, 0x5c, 0x24, 0xff, 0x7e, 0x6c, 0xd6, 0x15, 0xfb, + 0x9f, 0xd8, 0x50, 0xc4, 0x97, 0x39, 0x00, 0x8f, 0xea, 0xb5, 0xe6, 0xd3, + 0x41, 0x9e, 0x12, 0x71, 0xa2, 0x92, 0x9b, 0x73, 0x01, 0x73, 0x89, 0x90, + 0xb7, 0x08, 0x1c, 0x96, 0x6d, 0x1b, 0x36, 0xc2, 0xf5, 0xbd, 0xfa, 0x34, + 0x66, 0x52, 0x81, 0xc1, 0x9d, 0x8f, 0x0a, 0x4f, 0x11, 0xde, 0xf7, 0x1f, + 0xac, 0xf7, 0xbb, 0x3e, 0xd1, 0x4f, 0x7c, 0x16, 0x91, 0xf9, 0xc4, 0x1c, + 0xed, 0x2b, 0x8e, 0x84, 0x93, 0xde, 0x23, 0xf5, 0xb7, 0xbf, 0x90, 0xeb, + 0xc3, 0x90, 0x62, 0x38, 0xd6, 0x24, 0x54, 0x19, 0xbb, 0xf4, 0x33, 0xbf, + 0x30, 0x43, 0x3b, 0x0b, 0x38, 0xda, 0x48, 0x12, 0xb7, 0x3a, 0xd5, 0x14, + 0xed, 0x11, 0x23, 0x7b, 0x4e, 0x04, 0xea, 0x7d, 0x63, 0x42, 0xb5, 0x9f, + 0xc8, 0x0c, 0x44, 0x5b, 0x09, 0xdf, 0x42, 0x95, 0x8f, 0x6f, 0x04, 0xb8, + 0x11, 0x8a, 0xc2, 0x2c, 0xd9, 0xf5, 0x69, 0x19, 0xeb, 0x81, 0xd0, 0x86, + 0x87, 0x80, 0xdf, 0x75, 0xfd, 0xd9, 0x6a, 0x6a, 0x6b, 0x1f, 0xcb, 0x45, + 0xf8, 0xe7, 0x4c, 0xcd, 0x8b, 0x54, 0x35, 0x91, 0x9e, 0x27, 0x12, 0x5e, + 0x65, 0x0a, 0xf4, 0xd6, 0xf8, 0xb4, 0x4c, 0xfe, 0xe0, 0x7e, 0x7b, 0xa1, + 0xa1, 0x58, 0xb9, 0x9e, 0x4a, 0xa4, 0xda, 0x18, 0x94, 0xd5, 0x4e, 0x0b, + 0x6d, 0x90, 0x76, 0x6b, 0xb8, 0xab, 0x6d, 0x24, 0x02, 0xc6, 0x3e, 0xd6, + 0xdc, 0x96, 0xab, 0x51, 0x95, 0x98, 0xde, 0x78, 0xd9, 0xda, 0xfe, 0x6f, + 0x69, 0x74, 0xa2, 0x9f, 0x84, 0x6b, 0x8b, 0x8d, 0xa7, 0x76, 0xac, 0xf4, + 0x0d, 0xa3, 0x20, 0x6e, 0x7d, 0xa1, 0x18, 0x48, 0xd4, 0x0c, 0xd1, 0x38, + 0x84, 0x38, 0x14, 0xc9, 0x7c, 0xc5, 0x79, 0x32, 0xaa, 0xcc, 0x4b, 0xee, + 0x5c, 0xab, 0xfb, 0x39, 0x97, 0x8c, 0x15, 0x1b, 0xdc, 0x34, 0x09, 0x61, + 0x71, 0x01, 0x87, 0xe4, 0xb4, 0x63, 0x52, 0x2e, 0x6b, 0xfe, 0xc2, 0x36, + 0x4c, 0x2d, 0xab, 0x42, 0xec, 0xa3, 0xd8, 0x93, 0x47, 0xba, 0x23, 0xd0, + 0x4f, 0xcc, 0x90, 0x03, 0x53, 0xbb, 0x4d, 0x2f, 0x2e, 0x78, 0x9f, 0xcb, + 0x3b, 0x52, 0xd6, 0x2d, 0xc4, 0x11, 0xfd, 0x66, 0xdb, 0xdb, 0x61, 0xc2, + 0x31, 0xbc, 0x42, 0x9c, 0x4c, 0x02, 0xd3, 0x5d, 0x30, 0x3c, 0xf9, 0x93, + 0xa9, 0x42, 0x71, 0x91, 0xbb, 0x90, 0x04, 0x67, 0xc6, 0xe4, 0x2b, 0x76, + 0x86, 0x34, 0xcc, 0xcb, 0x9a, 0xf7, 0xdd, 0x4b, 0x57, 0xac, 0x76, 0x9a, + 0xa9, 0xef, 0x7a, 0x15, 0x85, 0x58, 0xcc, 0xb2, 0x7a, 0x83, 0x7c, 0xd3, + 0x41, 0x0d, 0x46, 0x54, 0x30, 0x1c, 0x68, 0x18, 0x4a, 0x41, 0x77, 0x3d, + 0x50, 0xa8, 0xfa, 0x71, 0x67, 0xcd, 0xfb, 0xf0, 0x1a, 0x7b, 0xee, 0xcf, + 0xea, 0x83, 0x5f, 0x25, 0x0b, 0x91, 0xba, 0x17, 0xd9, 0x47, 0xf2, 0x27, + 0x04, 0xbd, 0x82, 0x3c, 0x27, 0xfd, 0x3a, 0x4c, 0xbd, 0x78, 0x48, 0xe7, + 0x4c, 0x66, 0xdb, 0x9f, 0x8a, 0x81, 0xde, 0x24, 0x44, 0x0a, 0xce, 0xf8, + 0x53, 0x77, 0xf9, 0x11, 0xfc, 0x02, 0xab, 0xbe, 0x82, 0xf5, 0x51, 0x20, + 0x27, 0xe1, 0xae, 0xdd, 0x78, 0x70, 0x33, 0x28, 0xd0, 0x34, 0x80, 0xca, + 0x3a, 0xeb, 0x5a, 0xad, 0x38, 0x3c, 0xa3, 0xce, 0x53, 0xbd, 0x1b, 0x4a, + 0x9d, 0x85, 0x9d, 0xde, 0x66, 0xdb, 0x4c, 0xd3, 0x4a, 0x55, 0xbd, 0x8f, + 0x55, 0xe6, 0x43, 0x48, 0x57, 0xe4, 0xa7, 0xc2, 0x25, 0x0a, 0x31, 0xe0, + 0x0b, 0x7c, 0xf2, 0x24, 0xc6, 0x84, 0xbf, 0x5b, 0x02, 0x84, 0x4c, 0x75, + 0x14, 0x5c, 0x64, 0x5d, 0x1c, 0xe8, 0xc3, 0x3c, 0x6a, 0x8e, 0xf4, 0xee, + 0x45, 0x70, 0x6a, 0x7d, 0x0b, 0x92, 0xab, 0x08, 0xd5, 0x06, 0x77, 0x68, + 0x8e, 0xa4, 0x3e, 0x56, 0xf6, 0xb2, 0xaa, 0xf9, 0x42, 0xf4, 0x99, 0x30, + 0x6b, 0xc1, 0x68, 0x83, 0xd4, 0x01, 0x34, 0xb0, 0xb1, 0x90, 0xe4, 0x67, + 0x22, 0xa4, 0x99, 0xc4, 0x4c, 0xe9, 0x17, 0x1b, 0xf8, 0xb0, 0x8a, 0x02, + 0xe3, 0xf9, 0xc1, 0x16, 0x43, 0xca, 0x79, 0xf3, 0x87, 0xdf, 0x4f, 0x70, + 0x52, 0x87, 0x36, 0xf0, 0xbb, 0xc8, 0x51, 0x1f, 0x24, 0xf2, 0x3d, 0xe5, + 0xd6, 0xb8, 0x3b, 0x51, 0xf6, 0xc1, 0xc9, 0x9a, 0x98, 0x20, 0x4e, 0x19, + 0xb2, 0xb7, 0xdf, 0x4f, 0xf5, 0x8b, 0x38, 0xa2, 0x83, 0xc1, 0x17, 0x2c, + 0xbb, 0x7d, 0x7b, 0x47, 0x9e, 0xee, 0x92, 0x12, 0x2b, 0xa0, 0x99, 0x6f, + 0x69, 0x34, 0x99, 0x6f, 0xc9, 0x7a, 0xa4, 0xbf, 0xa3, 0x53, 0xe3, 0x12, + 0x81, 0xec, 0xac, 0xaa, 0x20, 0x48, 0x32, 0x35, 0xc2, 0x88, 0xda, 0xa7, + 0xad, 0x13, 0xbf, 0x62, 0x7b, 0xf0, 0x2c, 0x08, 0x5f, 0xce, 0xfe, 0xb4, + 0xdb, 0x70, 0xe5, 0x19, 0xd4, 0xf2, 0xf3, 0xeb, 0xfb, 0x2d, 0xbf, 0x26, + 0x75, 0x3b, 0xd5, 0x7a, 0xe6, 0xf5, 0x83, 0xaf, 0xa9, 0xc2, 0x9d, 0x49, + 0xfb, 0x6d, 0xf3, 0x72, 0x7e, 0x8c, 0x0d, 0xb6, 0xf4, 0x4b, 0x96, 0x95, + 0xb2, 0x2e, 0xfd, 0x96, 0x2f, 0xbd, 0xa2, 0x28, 0x21, 0x52, 0x8b, 0xce, + 0x30, 0xf4, 0x17, 0x97, 0xca, 0x2f, 0xa0, 0xd5, 0xb3, 0xdc, 0xbc, 0xaa, + 0x54, 0x46, 0x9c, 0x13, 0xf1, 0x6d, 0xa6, 0xb6, 0x68, 0x69, 0x8b, 0xae, + 0x2b, 0xb6, 0xdf, 0x90, 0x85, 0x4f, 0x2d, 0x8c, 0x6f, 0x52, 0xcb, 0xad, + 0x1a, 0x33, 0x61, 0xbf, 0xd8, 0xce, 0xd9, 0xd2, 0x2e, 0xff, 0x64, 0x08, + 0x93, 0x11, 0x01, 0x87, 0x8f, 0xd2, 0x17, 0x75, 0x8a, 0x99, 0xe8, 0x7f, + 0x4f, 0x1b, 0xf2, 0xb8, 0x96, 0xff, 0x04, 0x8a, 0x64, 0x41, 0x3e, 0xf9, + 0xa4, 0x67, 0xe1, 0x60, 0xea, 0xe0, 0x5e, 0x74, 0xa6, 0x0d, 0xf1, 0x6a, + 0xc3, 0x34, 0x57, 0xd8, 0x29, 0xf3, 0x01, 0x29, 0x86, 0xcd, 0xd9, 0x57, + 0x52, 0xf2, 0xd7, 0x3f, 0x0a, 0x6a, 0xc8, 0x05, 0x6a, 0x01, 0xf9, 0x8f, + 0xd1, 0xdb, 0x22, 0xda, 0xe0, 0x96, 0xdc, 0x62, 0xb3, 0xed, 0x73, 0x42, + 0x6a, 0x7f, 0x7d, 0x5b, 0x30, 0x20, 0xc6, 0xd7, 0xde, 0x59, 0xd0, 0x95, + 0x19, 0xe6, 0x8b, 0xe5, 0x99, 0xdf, 0x55, 0x68, 0xc1, 0x26, 0x58, 0x01, + 0x70, 0x2b, 0x9e, 0x56, 0x1e, 0xbc, 0x4f, 0x1e, 0xa4, 0x83, 0x51, 0x98, + 0xbe, 0x50, 0x9a, 0xeb, 0x1a, 0x52, 0x0a, 0x71, 0xaa, 0xf9, 0xc4, 0x77, + 0xc6, 0x51, 0x01, 0x9a, 0xa6, 0x3c, 0x98, 0xeb, 0x52, 0x4d, 0xc3, 0x10, + 0x5d, 0x4b, 0xad, 0xe4, 0x91, 0xff, 0x7f, 0xde, 0xb2, 0x62, 0xd5, 0xbd, + 0x87, 0xca, 0x91, 0x59, 0xdc, 0x27, 0xd2, 0x8b, 0xad, 0x70, 0xe7, 0x83, + 0xb2, 0x9b, 0xaf, 0x39, 0xa8, 0xf2, 0x0c, 0xd7, 0x48, 0x99, 0x7a, 0x06, + 0x47, 0x39, 0x7a, 0xb4, 0xf4, 0x4b, 0xcc, 0xa7, 0x09, 0xd0, 0xbd, 0x72, + 0x48, 0xcf, 0xd8, 0x58, 0x95, 0x53, 0xe9, 0x9e, 0x7d, 0xc0, 0xc0, 0xfe, + 0x7f, 0x96, 0xf0, 0x52, 0xcf, 0x33, 0xac, 0xe0, 0xe2, 0xde, 0xd8, 0xf6, + 0xfa, 0xfd, 0xa6, 0x92, 0x34, 0x63, 0x6e, 0xec, 0xa4, 0x89, 0xe0, 0x42, + 0x13, 0x43, 0xe6, 0xeb, 0x7d, 0xe4, 0xa0, 0xca, 0xc7, 0x74, 0xea, 0x58, + 0x36, 0x1b, 0x4f, 0x1a, 0xa7, 0x70, 0x53, 0x5c, 0xde, 0xa5, 0xc7, 0x5f, + 0x57, 0xb8, 0x07, 0x52, 0x7e, 0x81, 0xab, 0x4b, 0x9e, 0xff, 0x72, 0x67, + 0xf2, 0xbc, 0xb8, 0x1a, 0xac, 0x23, 0xea, 0xd7, 0x26, 0x46, 0x19, 0xe0, + 0x19, 0xa2, 0xe2, 0x9d, 0x7b, 0x2c, 0x68, 0xe9, 0x88, 0xe9, 0xf2, 0xc5, + 0x78, 0x74, 0x25, 0xae, 0x4f, 0xdd, 0xa7, 0x1d, 0xc4, 0xcd, 0xe3, 0x85, + 0x3a, 0xde, 0x6e, 0x4f, 0x3f, 0x0f, 0xc5, 0xe4, 0x65, 0x35, 0x6f, 0x0b, + 0xa6, 0x64, 0x12, 0x02, 0x08, 0xad, 0x3b, 0x68, 0xe8, 0xa7, 0xe5, 0xc4, + 0x3c, 0x72, 0x0b, 0x6d, 0xd3, 0x95, 0xb3, 0x4e, 0x82, 0xa6, 0xa9, 0x83, + 0xcb, 0xb7, 0x22, 0xeb, 0xe7, 0x29, 0xad, 0xc6, 0xa4, 0x1c, 0x98, 0x0b, + 0x68, 0x2a, 0x04, 0x45, 0xe4, 0xdd, 0xf3, 0x3c, 0x45, 0x04, 0xdf, 0x36, + 0xa4, 0xde, 0x09, 0x17, 0x4a, 0x00, 0xc2, 0x58, 0x7e, 0xce, 0x80, 0x52, + 0xf5, 0xb7, 0xa1, 0x35, 0x56, 0x0d, 0x9a, 0x79, 0x78, 0x9a, 0x2b, 0xbb, + 0x45, 0x0b, 0x6c, 0x38, 0x0b, 0x81, 0x38, 0x37, 0x00, 0xe0, 0x7a, 0x17, + 0x96, 0xe2, 0xcf, 0xb5, 0xc3, 0x27, 0x5a, 0x10, 0x2c, 0xfb, 0x51, 0xc1, + 0x2a, 0x70, 0x86, 0x58, 0x08, 0xca, 0x47, 0xe1, 0xe4, 0x12, 0x23, 0xe4, + 0xcd, 0x08, 0xa0, 0x9d, 0x59, 0xf7, 0xb1, 0x95, 0xe6, 0x60, 0xdf, 0xbe, + 0x1c, 0x8d, 0xa7, 0x9b, 0x32, 0x00, 0xcd, 0x1a, 0xe4, 0x42, 0xba, 0x22, + 0x08, 0x7f, 0x82, 0xff, 0x47, 0x62, 0x44, 0x42, 0x6b, 0x8e, 0x8f, 0x76, + 0x39, 0xfb, 0x50, 0x4d, 0x1c, 0x44, 0x40, 0xc9, 0x22, 0x21, 0xe8, 0xa4, + 0x44, 0x4c, 0x5a, 0x6c, 0x5b, 0x10, 0x9c, 0x34, 0xb1, 0xe1, 0x49, 0x22, + 0xa3, 0x6e, 0xe2, 0xeb, 0x0a, 0x45, 0xd7, 0x62, 0x81, 0x91, 0x4d, 0x28, + 0x45, 0xe6, 0x0c, 0x0c, 0x20, 0xbc, 0x8e, 0x28, 0xa8, 0xa9, 0x07, 0xc2, + 0xaf, 0xcd, 0xde, 0xd8, 0xcf, 0x49, 0x4e, 0x52, 0x8a, 0x23, 0xbf, 0x1d, + 0xaf, 0x5b, 0xfd, 0x08, 0xca, 0x30, 0x4a, 0xca, 0x2f, 0xc7, 0x39, 0x98, + 0x1b, 0xfb, 0x74, 0x6b, 0xc4, 0xba, 0x0b, 0xdb, 0xef, 0x73, 0xb7, 0xad, + 0x5f, 0xe8, 0x47, 0x6e, 0x0d, 0x8e, 0xf7, 0x87, 0x11, 0x4d, 0x3b, 0x1e, + 0x3b, 0xc1, 0xaa, 0x8c, 0x33, 0x73, 0x1e, 0x16, 0x07, 0x1f, 0xd7, 0x53, + 0x14, 0xf9, 0x4c, 0x01, 0x59, 0x66, 0x51, 0x08, 0x22, 0x72, 0x57, 0x87, + 0x70, 0x53, 0x7f, 0x67, 0x80, 0x90, 0x9d, 0x66, 0x42, 0xe8, 0x50, 0xac, + 0x1e, 0x1a, 0x46, 0x00, 0x61, 0x9d, 0x42, 0xd4, 0x0c, 0x93, 0x55, 0xe5, + 0x25, 0x95, 0x3a, 0xb3, 0x87, 0x2c, 0xea, 0x81, 0x64, 0x15, 0x8b, 0x4e, + 0x49, 0x21, 0x12, 0x00, 0x52, 0x3c, 0x9e, 0x6d, 0x0d, 0x1c, 0xe7, 0xcf, + 0x7c, 0x15, 0x72, 0x8a, 0x1f, 0x1d, 0x4d, 0x7c, 0x3b, 0x6d, 0xcc, 0xf3, + 0x64, 0x14, 0x98, 0xfb, 0x15, 0x19, 0x0e, 0xf7, 0x9e, 0xd8, 0x9a, 0x63, + 0xb2, 0x5c, 0xdf, 0x7b, 0x74, 0xd9, 0xfc, 0x05, 0xe0, 0xd1, 0x53, 0xe1, + 0x20, 0xd7, 0xa5, 0x81, 0xe5, 0x4f, 0xfb, 0x08, 0x15, 0x0e, 0xa8, 0xbc, + 0x76, 0x40, 0xf5, 0x7c, 0x39, 0x0f, 0xfa, 0x68, 0xd9, 0x0b, 0x85, 0xb7, + 0x9a, 0x4c, 0x41, 0x06, 0x7d, 0x4c, 0x25, 0x3a, 0xec, 0xba, 0x4a, 0x41, + 0x53, 0x1b, 0xc4, 0xfa, 0xeb, 0x6f, 0x4d, 0x84, 0xf4, 0x2a, 0xd7, 0xbe, + 0x30, 0x21, 0x6c, 0x43, 0x71, 0xef, 0x83, 0x33, 0xca, 0xfa, 0x15, 0x83, + 0x0c, 0xe5, 0x9f, 0x60, 0x34, 0xe9, 0x55, 0x5d, 0x3b, 0xf8, 0x0d, 0x3b, + 0x13, 0xe8, 0x82, 0x16, 0xd7, 0xf1, 0x10, 0x48, 0x77, 0xcb, 0xb1, 0x06, + 0x5a, 0xe4, 0x9d, 0xce, 0x9a, 0x1f, 0x54, 0x87, 0x41, 0xfd, 0xb6, 0x75, + 0xb6, 0x63, 0xba, 0x13, 0x90, 0x4a, 0xed, 0xf0, 0xfc, 0x6c, 0x73, 0x70, + 0x0c, 0xc8, 0x93, 0xc5, 0x4d, 0x48, 0xc0, 0x52, 0xb0, 0x68, 0x32, 0x07, + 0x0d, 0x09, 0x2e, 0x94, 0xe8, 0x48, 0x54, 0x89, 0xcf, 0x61, 0x76, 0x23, + 0x23, 0x35, 0x6e, 0xd4, 0xd9, 0xad, 0x68, 0x1f, 0x4c, 0x89, 0x36, 0xe3, + 0x55, 0xd8, 0xb1, 0xfa, 0xb2, 0x11, 0x31, 0xbd, 0x14, 0x50, 0x3e, 0xb9, + 0xfc, 0xfa, 0x6a, 0x05, 0xa7, 0x80, 0x17, 0x39, 0xfb, 0xeb, 0x71, 0xc7, + 0x85, 0xec, 0x5b, 0x0f, 0xa4, 0xef, 0xf1, 0x46, 0xc9, 0x9e, 0xd2, 0xad, + 0x3a, 0x0c, 0x73, 0xa4, 0x79, 0xd5, 0x1a, 0x8c, 0x92, 0x78, 0x8b, 0xa6, + 0xf0, 0x1a, 0x49, 0xc0, 0xdc, 0x18, 0x91, 0x0d, 0x1e, 0x80, 0xb1, 0x41, + 0x54, 0xef, 0x45, 0xf3, 0x12, 0x65, 0xae, 0x50, 0x89, 0x9e, 0x15, 0x3a, + 0x2d, 0xdf, 0xf6, 0xdb, 0xc2, 0x4d, 0x16, 0xf9, 0x1a, 0x05, 0xe9, 0x1b, + 0xb8, 0x48, 0x45, 0xb7, 0x1d, 0xd7, 0x73, 0x07, 0x76, 0x72, 0x36, 0xeb, + 0x94, 0x3c, 0xac, 0x5e, 0x35, 0x59, 0x5d, 0x09, 0xfa, 0x99, 0xa7, 0xa2, + 0x3e, 0xca, 0xc2, 0xfe, 0xc5, 0x1a, 0x1f, 0xb8, 0xe3, 0x65, 0xf3, 0xb2, + 0x24, 0x8a, 0x4e, 0x8e, 0x19, 0x91, 0x43, 0x8e, 0x1b, 0x05, 0x42, 0x24, + 0x53, 0xc2, 0x65, 0x39, 0x14, 0x16, 0x04, 0xf7, 0xaa, 0xbe, 0x33, 0x31, + 0xfe, 0xcf, 0xe4, 0x77, 0x5e, 0x33, 0x32, 0x52, 0xb7, 0x19, 0x93, 0xed, + 0x82, 0x0a, 0xfb, 0xde, 0xe1, 0xed, 0x32, 0x71, 0x9a, 0x8b, 0x76, 0x29, + 0x53, 0x7a, 0xb6, 0x03, 0x91, 0xd0, 0x75, 0xbd, 0x30, 0x50, 0x27, 0x1b, + 0x49, 0xa0, 0x6c, 0x54, 0x12, 0x36, 0x0c, 0x43, 0x1a, 0xe4, 0x1e, 0x38, + 0x6a, 0xe6, 0x66, 0x68, 0x1d, 0xeb, 0xfc, 0x47, 0x55, 0x25, 0x4b, 0x4a, + 0x23, 0xcc, 0x89, 0x75, 0xa5, 0xa0, 0x74, 0x50, 0xec, 0xfb, 0x01, 0x4f, + 0x23, 0xac, 0x05, 0x5b, 0x89, 0x64, 0x0c, 0x46, 0xce, 0xc5, 0xdf, 0x7f, + 0x2b, 0xd3, 0x3c, 0x7e, 0xcc, 0x71, 0xd0, 0x20, 0x1c, 0x2e, 0xe2, 0x17, + 0xf4, 0xd2, 0xaa, 0xd8, 0x4b, 0x6d, 0x1f, 0x53, 0xa1, 0x7d, 0x49, 0x90, + 0x10, 0xf6, 0x2a, 0x0f, 0xef, 0x36, 0xac, 0xd1, 0xcf, 0x04, 0x8f, 0xc6, + 0x02, 0x7d, 0x8d, 0x8f, 0x4a, 0x9b, 0x72, 0xdc, 0x30, 0x38, 0xeb, 0x66, + 0x8d, 0x8e, 0xc8, 0xc1, 0xb7, 0x21, 0x60, 0x9c, 0xbd, 0xae, 0x93, 0xcd, + 0x63, 0xbc, 0x0d, 0xf9, 0xc6, 0x67, 0x2d, 0x6c, 0x7a, 0x24, 0xbd, 0x31, + 0x69, 0xcb, 0x0e, 0xbd, 0x48, 0x1c, 0x3f, 0x8d, 0xd5, 0x4e, 0xab, 0x10, + 0xc3, 0x47, 0x50, 0x95, 0xf2, 0xa4, 0x13, 0x9d, 0xfd, 0x87, 0x6f, 0x45, + 0x50, 0xf4, 0xbc, 0x5b, 0x50, 0x9b, 0x03, 0xf0, 0x1d, 0x40, 0x64, 0xa8, + 0xe2, 0xcc, 0x95, 0xf8, 0x94, 0x97, 0x5e, 0xf1, 0x6f, 0x9b, 0x82, 0x4d, + 0xe3, 0x68, 0x0c, 0xbb, 0xd9, 0xe3, 0x06, 0xf2, 0x33, 0x19, 0xec, 0x99, + 0x65, 0x3a, 0x7b, 0x44, 0x89, 0xb0, 0x24, 0x73, 0x94, 0xe8, 0x19, 0x67, + 0x05, 0xce, 0x8c, 0x78, 0x44, 0xec, 0x7b, 0x9e, 0x04, 0x18, 0xd9, 0x7e, + 0xfd, 0x51, 0xf6, 0xf0, 0x75, 0xc5, 0x3b, 0xb2, 0x85, 0xa9, 0xea, 0x61, + 0x08, 0xbc, 0xf5, 0x1f, 0xb7, 0xca, 0xb2, 0xf6, 0x9c, 0x14, 0x71, 0xf4, + 0x2c, 0x66, 0xd8, 0x0d, 0x7b, 0xb9, 0x55, 0xb2, 0xd3, 0xb7, 0x08, 0xac, + 0x3b, 0x36, 0x91, 0xe9, 0xf7, 0x30, 0x21, 0x9c, 0xa9, 0xd1, 0xcc, 0x05, + 0x24, 0x61, 0x22, 0x91, 0xc9, 0xce, 0x68, 0x2f, 0xe8, 0x26, 0x26, 0x16, + 0x0b, 0xe2, 0x83, 0xa2, 0x33, 0xd5, 0xfe, 0x0a, 0x06, 0x15, 0xfc, 0x37, + 0xda, 0x9d, 0xc0, 0x5e, 0xad, 0xad, 0xd5, 0xef, 0xd2, 0xa8, 0x11, 0x5a, + 0xf5, 0x22, 0x0c, 0x2c, 0x88, 0xce, 0xaa, 0xa0, 0x73, 0xe9, 0x19, 0xa7, + 0x3e, 0x6c, 0x08, 0x3c, 0x62, 0xe4, 0xb6, 0x9b, 0xc1, 0x2c, 0xc9, 0x97, + 0x06, 0xb5, 0xc9, 0x3c, 0xe8, 0x1e, 0x82, 0x9d, 0x6f, 0x4d, 0xa0, 0xb5, + 0xe8, 0xc2, 0xe9, 0x1a, 0xea, 0x2f, 0x7a, 0xe2, 0x14, 0x29, 0x1f, 0x6b, + 0xe5, 0xcc, 0x31, 0x88, 0x2c, 0x5d, 0xed, 0xc0, 0x21, 0xee, 0xae, 0x16, + 0x90, 0x4c, 0x96, 0x7b, 0x8d, 0x87, 0xe9, 0x39, 0x25, 0x0c, 0xbe, 0x16, + 0xa5, 0x27, 0x03, 0x5c, 0xbc, 0x29, 0xc6, 0xfd, 0x13, 0xe4, 0x65, 0xb0, + 0x97, 0x69, 0x5a, 0x2c, 0x5d, 0x34, 0xb0, 0xfd, 0xd6, 0x7c, 0xac, 0x48, + 0xd5, 0x56, 0x75, 0xb3, 0xf3, 0xe9, 0x55, 0x40, 0xb1, 0x26, 0x74, 0xee, + 0xff, 0xdf, 0xb8, 0x4e, 0x1c, 0x80, 0x14, 0xe3, 0xba, 0x4a, 0x7b, 0xd7, + 0x4f, 0x76, 0x3e, 0x74, 0xa7, 0x41, 0xbd, 0xc8, 0xed, 0xed, 0x99, 0x6e, + 0x38, 0x4e, 0xaa, 0xe0, 0xdd, 0x70, 0x9c, 0xe7, 0xd8, 0x65, 0x43, 0xbd, + 0xfa, 0xa4, 0xa6, 0xd0, 0x8c, 0xcd, 0xba, 0x45, 0xb0, 0x23, 0xf8, 0x79, + 0x0a, 0xfe, 0x46, 0x21, 0x37, 0x48, 0x34, 0x41, 0x5b, 0xd3, 0x2c, 0xdb, + 0x08, 0xf1, 0x5d, 0x52, 0x14, 0x15, 0xff, 0x05, 0xcd, 0xf1, 0x95, 0x50, + 0x3d, 0x16, 0xfe, 0x71, 0x38, 0xe7, 0x28, 0xd2, 0xb8, 0xfd, 0xb7, 0xb7, + 0x2f, 0x27, 0xf9, 0xe7, 0x19, 0xfe, 0x73, 0x4d, 0xa6, 0xc9, 0xf7, 0x39, + 0xdb, 0xf0, 0x8b, 0x28, 0xd1, 0xe4, 0x61, 0x25, 0xe2, 0xb8, 0x33, 0xa6, + 0x9d, 0xd7, 0x2d, 0x3a, 0x5c, 0x82, 0x06, 0xe9, 0x8b, 0x59, 0xcc, 0x80, + 0xf0, 0x1b, 0xec, 0x69, 0xef, 0x2b, 0xc4, 0xa9, 0x01, 0x55, 0x36, 0xd6, + 0x31, 0x97, 0x31, 0x7e, 0x9c, 0x32, 0xb5, 0xa7, 0x4c, 0xc0, 0x69, 0xc0, + 0x2f, 0x27, 0x45, 0x80, 0xca, 0xfc, 0x4a, 0x10, 0x77, 0xce, 0x62, 0xdb, + 0x68, 0x5c, 0xf1, 0x66, 0x37, 0x2f, 0xbe, 0x76, 0x95, 0x0c, 0xdb, 0x87, + 0x81, 0x95, 0x2f, 0x8d, 0x2b, 0x5a, 0xbf, 0xcc, 0x78, 0xb2, 0xee, 0x31, + 0x26, 0x73, 0x07, 0xe6, 0x5b, 0x39, 0x8d, 0x2f, 0x2f, 0x0c, 0x12, 0x47, + 0x95, 0x7f, 0x8b, 0x58, 0x9e, 0xe0, 0x36, 0xb8, 0x6c, 0xe3, 0x52, 0x8c, + 0x04, 0x4f, 0x01, 0x24, 0x41, 0x4c, 0xb1, 0xe6, 0x53, 0xac, 0x82, 0x41, + 0xd5, 0xfa, 0x54, 0x4b, 0xe1, 0x51, 0x8a, 0x74, 0xfd, 0x21, 0xd3, 0xaa, + 0x9e, 0xc0, 0x14, 0x31, 0xa6, 0xa8, 0xfb, 0x10, 0x37, 0x3d, 0x1f, 0x6c, + 0xb9, 0x60, 0x47, 0x88, 0xa5, 0x19, 0x66, 0x0a, 0x96, 0x18, 0x2d, 0x8b, + 0x38, 0xe9, 0x31, 0xf5, 0xfe, 0x1d, 0x5e, 0xde, 0x9e, 0x92, 0x3a, 0x81, + 0xf5, 0x84, 0x27, 0x7f, 0x69, 0x68, 0x98, 0x5b, 0x70, 0x82, 0xa3, 0x3b, + 0x17, 0xd5, 0x02, 0xfe, 0xba, 0xcf, 0xd7, 0x26, 0xf7, 0xc7, 0xc8, 0x48, + 0xb0, 0x0c, 0xe7, 0xb7, 0x5a, 0xad, 0x76, 0x7a, 0xc3, 0x76, 0x42, 0x4c, + 0x7f, 0x90, 0xc4, 0x00, 0x4f, 0x32, 0x2a, 0x64, 0x8e, 0xbb, 0x3a, 0x4c, + 0xe6, 0xaf, 0x6d, 0xcb, 0x6c, 0xec, 0xf0, 0x8a, 0x4d, 0x27, 0x44, 0xc8, + 0x06, 0xfa, 0x8a, 0xfc, 0x0c, 0x47, 0xcb, 0x2c, 0x57, 0x71, 0x96, 0xa3, + 0x74, 0x09, 0x36, 0x39, 0x89, 0x58, 0x08, 0xa3, 0xbe, 0xdf, 0x1e, 0x8b, + 0x6f, 0xca, 0xa4, 0x3c, 0x57, 0x56, 0x0e, 0x47, 0x46, 0x8c, 0x58, 0x1b, + 0xac, 0x6b, 0xd2, 0xe6, 0x27, 0x76, 0xb5, 0xcd, 0xe1, 0xcb, 0x18, 0xdf, + 0x27, 0x85, 0xcc, 0x15, 0xd2, 0xac, 0xe9, 0xfa, 0x5e, 0xde, 0x4d, 0x42, + 0x9c, 0x52, 0xe1, 0x80, 0x02, 0xa4, 0x02, 0xe2, 0xb3, 0x71, 0xe7, 0x8e, + 0xdc, 0x4c, 0x01, 0x13, 0x05, 0x3b, 0x5c, 0x27, 0x3c, 0x19, 0xd4, 0xee, + 0xd0, 0x11, 0xb3, 0xc2, 0x8d, 0xee, 0x33, 0x73, 0x9b, 0xee, 0x44, 0xbe, + 0x3e, 0xf0, 0x4f, 0x55, 0x83, 0x7e, 0x47, 0x73, 0x20, 0xd0, 0xcf, 0x6e, + 0xa8, 0xfd, 0x1e, 0x4f, 0x67, 0x52, 0xeb, 0x2d, 0xa9, 0x80, 0x2b, 0x8a, + 0x27, 0x7f, 0x8b, 0x00, 0xff, 0x28, 0x09, 0xb3, 0x9a, 0xe5, 0x54, 0xec, + 0x4d, 0x00, 0x74, 0xcf, 0x3f, 0x6d, 0x5f, 0xf5, 0xc9, 0x91, 0x81, 0x24, + 0x61, 0xa9, 0x3d, 0x06, 0xf2, 0x40, 0x83, 0x07, 0xce, 0x66, 0xee, 0xa7, + 0xf0, 0xc8, 0xc8, 0x79, 0xab, 0x07, 0x0b, 0x73, 0xe6, 0xe9, 0xef, 0xa1, + 0xdf, 0x94, 0x8a, 0x92, 0xb4, 0x5a, 0x35, 0x95, 0x28, 0x8b, 0x35, 0x43, + 0x12, 0x0a, 0x59, 0x94, 0x22, 0x3a, 0x29, 0xb1, 0xd1, 0x75, 0x19, 0x9a, + 0x78, 0x27, 0xd0, 0x38, 0xe7, 0x7f, 0x90, 0x18, 0xdc, 0xef, 0xff, 0xfa, + 0x0d, 0xe4, 0xf7, 0xb3, 0x7a, 0x5f, 0x15, 0x0a, 0x83, 0x02, 0x62, 0x19, + 0x07, 0xa7, 0x00, 0xcc, 0xc6, 0x53, 0x63, 0x3c, 0x57, 0x01, 0x51, 0x16, + 0x9f, 0x71, 0x5b, 0xc1, 0x5a, 0x4c, 0xd3, 0xd4, 0x74, 0x6c, 0x41, 0x7e, + 0xed, 0x61, 0x09, 0x39, 0x50, 0x10, 0x55, 0xe9, 0x9d, 0x18, 0x1d, 0xc8, + 0x37, 0x27, 0xd9, 0x62, 0x00, 0x7d, 0x20, 0x51, 0x58, 0x6f, 0x9e, 0x52, + 0x5f, 0x9b, 0xd1, 0xec, 0x5c, 0x62, 0x31, 0x8f, 0xc3, 0x83, 0xf7, 0x11, + 0xb2, 0xfa, 0xad, 0x25, 0xae, 0xb0, 0x8b, 0x97, 0x9c, 0xbe, 0x43, 0x53, + 0x71, 0xbb, 0x7f, 0x12, 0x01, 0x99, 0xbd, 0xc9, 0xc0, 0x83, 0x51, 0xf6, + 0xbb, 0x4b, 0x2d, 0xd0, 0xf6, 0x64, 0x7d, 0xa9, 0x0a, 0x9a, 0xc8, 0x67, + 0xf4, 0x89, 0x28, 0x2f, 0x92, 0x50, 0x7f, 0xb0, 0x01, 0x5b, 0x2f, 0xc2, + 0xde, 0x8a, 0xab, 0x78, 0xf5, 0xd4, 0xa1, 0x28, 0x31, 0xb6, 0x55, 0x4f, + 0xed, 0xe2, 0xd4, 0xeb, 0x24, 0xf6, 0xf8, 0x22, 0xa0, 0x3d, 0x78, 0x80, + 0xc1, 0x05, 0xdf, 0x14, 0x7d, 0x29, 0xa0, 0x3b, 0x06, 0x42, 0xd5, 0xd3, + 0x59, 0xf4, 0xe2, 0x68, 0x41, 0xb9, 0x19, 0x6e, 0x3d, 0xc4, 0xd4, 0xe1, + 0xfa, 0x2f, 0x86, 0x41, 0xfb, 0x2b, 0x9d, 0x67, 0x5d, 0xb8, 0x85, 0xbf, + 0x50, 0xc9, 0xf6, 0x5e, 0x7e, 0xf9, 0xbe, 0x49, 0xb8, 0x45, 0x1f, 0x6f, + 0xb9, 0xdb, 0x3d, 0xe1, 0xce, 0x42, 0x16, 0x91, 0x2f, 0x09, 0x98, 0x63, + 0x03, 0xa9, 0x87, 0x4e, 0xf7, 0x1e, 0x26, 0x9c, 0xd4, 0x76, 0x42, 0xae, + 0x56, 0x2f, 0x66, 0x24, 0x14, 0x37, 0xac, 0xa7, 0x72, 0xdc, 0xf2, 0x6e, + 0x29, 0xd7, 0x52, 0xa5, 0x06, 0x5e, 0x6a, 0x10, 0x6e, 0x96, 0xd8, 0x44, + 0x90, 0x5b, 0x44, 0xfb, 0xea, 0x75, 0x15, 0x54, 0x73, 0xc1, 0x81, 0x35, + 0x40, 0x19, 0x44, 0xa7, 0xf1, 0xca, 0x4d, 0xd7, 0x54, 0xb7, 0x34, 0xd7, + 0xd4, 0x2a, 0xcd, 0x60, 0x28, 0xb9, 0x27, 0xea, 0x0d, 0x2c, 0x52, 0xd4, + 0x56, 0x24, 0x81, 0x86, 0x12, 0x5c, 0x81, 0x0e, 0x7e, 0xfd, 0x9e, 0x01, + 0x64, 0x7f, 0x00, 0xf5, 0x23, 0xf9, 0x1f, 0x3e, 0xd5, 0x67, 0xfa, 0x6c, + 0x4e, 0xb3, 0x6e, 0x4e, 0xcc, 0x38, 0xc1, 0xc8, 0x48, 0x5f, 0xe4, 0xbd, + 0xea, 0x46, 0xcf, 0x80, 0x98, 0x60, 0xc3, 0x1e, 0x8c, 0x5e, 0x36, 0x1c, + 0x4d, 0x2c, 0x72, 0x57, 0xad, 0x25, 0x87, 0x2a, 0x63, 0x76, 0xe8, 0x30, + 0x16, 0xff, 0xbb, 0xbd, 0x6e, 0x88, 0xd9, 0xc2, 0x83, 0xda, 0x8a, 0x8e, + 0x2a, 0x42, 0x15, 0x7f, 0x64, 0x7d, 0xb6, 0xfb, 0x82, 0x55, 0xb0, 0x16, + 0x93, 0x60, 0x1c, 0x06, 0xe0, 0xdd, 0x8d, 0xe9, 0x4b, 0x1f, 0x74, 0x98, + 0x1e, 0xfe, 0x1b, 0x8b, 0xbb, 0x36, 0x76, 0x87, 0xda, 0xf4, 0xd2, 0x60, + 0x9b, 0x49, 0x63, 0x3b, 0xc4, 0x88, 0xfc, 0x7b, 0x12, 0xfe, 0xcc, 0xf8, + 0x97, 0x77, 0x80, 0xdd, 0x9d, 0x34, 0x6e, 0xec, 0x18, 0x2b, 0x18, 0xb1, + 0x3b, 0x77, 0xa4, 0x72, 0x20, 0x25, 0x62, 0xbc, 0x4d, 0x53, 0x8e, 0x40, + 0x41, 0xd5, 0x3a, 0x43, 0x52, 0xf7, 0xe2, 0xd0, 0xbf, 0x3f, 0x8f, 0x19, + 0xf6, 0x36, 0x9b, 0x12, 0xc6, 0xe9, 0xc9, 0x78, 0x82, 0x89, 0x6a, 0x64, + 0x7b, 0xda, 0x8e, 0x3e, 0xc6, 0x9a, 0xe9, 0x59, 0x32, 0x31, 0x6b, 0x1d, + 0x7b, 0xd8, 0x13, 0xb6, 0xec, 0x2d, 0x0f, 0xf1, 0x7f, 0x48, 0xf0, 0xfd, + 0x58, 0x06, 0x20, 0xa3, 0x2e, 0x5f, 0x28, 0x0a, 0xe7, 0x52, 0x15, 0x0d, + 0x65, 0x46, 0xd8, 0x84, 0x0e, 0x80, 0x7d, 0x84, 0xb4, 0xd7, 0x5c, 0x3d, + 0xa4, 0x81, 0x5a, 0xcb, 0x11, 0x0e, 0xe5, 0x76, 0x53, 0xc8, 0x35, 0xf5, + 0x44, 0x08, 0xbc, 0x2c, 0x80, 0x99, 0xeb, 0x8b, 0x68, 0xae, 0x78, 0xee, + 0xef, 0xb6, 0x66, 0xe9, 0x31, 0x3d, 0xe8, 0x70, 0xe1, 0x98, 0x03, 0x38, + 0xb5, 0x0b, 0xaf, 0x23, 0xd4, 0x45, 0x14, 0xdb, 0xc3, 0xfe, 0x26, 0xee, + 0x17, 0x14, 0x4f, 0x44, 0x93, 0xa1, 0xe9, 0xad, 0xd6, 0x01, 0x98, 0x76, + 0x6c, 0x86, 0x14, 0xe4, 0x2c, 0x52, 0x74, 0x09, 0x7b, 0x0a, 0x10, 0x90, + 0x79, 0x1c, 0xae, 0xd7, 0x37, 0x9f, 0x3a, 0x89, 0xe9, 0x69, 0x89, 0xda, + 0x5f, 0xf7, 0x93, 0x1f, 0x41, 0x4c, 0x0c, 0xc1, 0x54, 0x53, 0xb1, 0x8a, + 0x89, 0x75, 0x01, 0x83, 0x14, 0x6f, 0x7e, 0xab, 0x6d, 0xad, 0xfd, 0x59, + 0xe5, 0x6c, 0xd8, 0x13, 0x7a, 0xd2, 0x3e, 0xc4, 0x40, 0xca, 0x00, 0x42, + 0x3e, 0x26, 0x4c, 0xb5, 0xe1, 0x2c, 0xa2, 0x1b, 0x80, 0xd3, 0x67, 0x06, + 0x3c, 0xc9, 0xf7, 0x85, 0x9b, 0x2c, 0xd4, 0x01, 0x2d, 0xb1, 0xd1, 0x02, + 0xd9, 0x3e, 0x8b, 0x96, 0x93, 0x25, 0x95, 0x8c, 0x85, 0xc7, 0x92, 0xff, + 0xdc, 0x17, 0xa4, 0xf3, 0x0e, 0xb7, 0x71, 0xce, 0x36, 0x35, 0x56, 0x88, + 0xce, 0x9d, 0x1e, 0xdf, 0x4e, 0xc9, 0x57, 0x3a, 0x11, 0x82, 0xd5, 0x72, + 0x21, 0x41, 0x9b, 0xe5, 0x9b, 0xe7, 0x91, 0x0c, 0x96, 0xf8, 0x67, 0xc4, + 0x1a, 0x4e, 0xf9, 0x7c, 0xdc, 0x4b, 0x9f, 0x1a, 0xe3, 0x18, 0x70, 0xd3, + 0xf4, 0xce, 0x43, 0x3c, 0x28, 0x23, 0xad, 0x10, 0xfe, 0x2c, 0x34, 0x80, + 0xb3, 0x68, 0xf3, 0x3b, 0x58, 0xd0, 0xa8, 0x70, 0xa3, 0x8a, 0x22, 0x47, + 0x40, 0x75, 0x17, 0x42, 0x30, 0x67, 0x17, 0xd5, 0xf5, 0x51, 0xe1, 0x47, + 0xa2, 0xab, 0xad, 0x81, 0x07, 0xea, 0xa5, 0x34, 0x39, 0x83, 0xae, 0x06, + 0x1d, 0x87, 0x03, 0x80, 0xef, 0xd6, 0x28, 0x00, 0x81, 0xa0, 0x67, 0x50, + 0xcd, 0x2f, 0xd1, 0x16, 0x7c, 0x8d, 0x60, 0x20, 0xeb, 0x9d, 0x19, 0x32, + 0x3e, 0x91, 0xbe, 0xf1, 0x92, 0xff, 0x67, 0x87, 0x43, 0x8f, 0x65, 0x15, + 0x5e, 0xb7, 0xc6, 0xea, 0x86, 0x91, 0x9e, 0x51, 0x9c, 0x91, 0xe6, 0xf1, + 0x58, 0xbf, 0x6d, 0xf9, 0x56, 0x6a, 0x17, 0x16, 0xc6, 0xf2, 0xcd, 0x63, + 0xb8, 0xee, 0xee, 0x90, 0xaf, 0x24, 0x41, 0xfe, 0x75, 0xdc, 0x14, 0x3b, + 0xa9, 0xa1, 0x4a, 0x86, 0xbc, 0xb5, 0x30, 0xed, 0x8c, 0xbc, 0xc6, 0x8b, + 0xbc, 0x6e, 0x46, 0xb5, 0x53, 0x3a, 0x00, 0x3c, 0x19, 0x50, 0xf3, 0xcb, + 0x39, 0x39, 0x01, 0x2e, 0x74, 0xfc, 0x6a, 0x68, 0xc3, 0x3a, 0x22, 0x5a, + 0x9b, 0x0a, 0xb0, 0x27, 0x1c, 0x8a, 0x29, 0xda, 0xbb, 0xac, 0xf8, 0x9f, + 0xde, 0x99, 0x38, 0x63, 0x91, 0x0b, 0xe8, 0x49, 0x7e, 0x86, 0x54, 0x42, + 0x33, 0x3b, 0x66, 0xbf, 0x1d, 0x63, 0xcf, 0x32, 0x2d, 0x45, 0x69, 0xa5, + 0x34, 0xa7, 0xcd, 0xd6, 0x9a, 0x42, 0x51, 0x72, 0x8d, 0x24, 0x82, 0xe9, + 0x36, 0xf4, 0x84, 0x23, 0x77, 0x0f, 0x9e, 0x0b, 0xc2, 0xaf, 0x57, 0x28, + 0xe3, 0x18, 0x82, 0x20, 0xe3, 0xf5, 0xb1, 0x5e, 0x7a, 0xb0, 0x1e, 0xe6, + 0x17, 0x1a, 0x9a, 0x25, 0xa8, 0xe2, 0x66, 0xd3, 0xcc, 0x91, 0x8d, 0xf7, + 0xc3, 0xf1, 0x4e, 0xde, 0xc2, 0x8b, 0x11, 0x09, 0xeb, 0x64, 0x0a, 0x08, + 0x2a, 0x4d, 0x35, 0x93, 0x56, 0x43, 0xd0, 0xa5, 0x67, 0x96, 0xa0, 0x36, + 0xdb, 0x6b, 0x4a, 0xcc, 0x2b, 0xd1, 0xc7, 0xa9, 0xaa, 0x33, 0x0b, 0xdd, + 0x39, 0x15, 0x47, 0x10, 0xa6, 0x8b, 0x5d, 0x20, 0x98, 0x4d, 0x19, 0x08, + 0xa6, 0xf8, 0x63, 0xa0, 0xcc, 0xe8, 0x89, 0x2d, 0xf5, 0xfd, 0xb5, 0x74, + 0xf1, 0x70, 0xee, 0x3d, 0x7f, 0x41, 0x6f, 0xa1, 0x32, 0xe2, 0x5a, 0xdb, + 0x36, 0xbf, 0x05, 0x2b, 0xc1, 0xa5, 0x3e, 0x19, 0xc1, 0xf8, 0x13, 0x2c, + 0xaf, 0xca, 0xe8, 0xa1, 0x19, 0x25, 0xb9, 0x93, 0x67, 0x29, 0x74, 0x1b, + 0x82, 0x62, 0x2a, 0xf6, 0xc5, 0x13, 0xdc, 0x6e, 0x4c, 0x4b, 0x24, 0x9a, + 0x8c, 0x7c, 0x2c, 0x0f, 0x84, 0xf8, 0x29, 0x3d, 0xad, 0xe4, 0x71, 0x69, + 0xe2, 0xcb, 0x96, 0x6d, 0x7e, 0x00, 0x4c, 0x3a, 0x4a, 0x04, 0xed, 0xff, + 0xa1, 0x98, 0x1d, 0xd3, 0xff, 0xd6, 0xb0, 0xb2, 0x98, 0x1b, 0xbd, 0xcb, + 0x7f, 0xbf, 0xd2, 0xe8, 0x77, 0x62, 0x24, 0x45, 0x07, 0x40, 0x9c, 0x48, + 0x33, 0x34, 0x05, 0xd5, 0x95, 0xa3, 0xff, 0xbe, 0xaa, 0x3a, 0x3b, 0x3f, + 0x79, 0x4d, 0x13, 0xe9, 0xc1, 0xcb, 0xdb, 0xca, 0x69, 0x05, 0x84, 0x85, + 0xff, 0xf0, 0xfd, 0x81, 0x1e, 0xa0, 0x70, 0x25, 0x36, 0xe2, 0x74, 0x1a, + 0x3c, 0x42, 0x12, 0xa5, 0x84, 0x4a, 0xca, 0x64, 0x0a, 0xa2, 0x02, 0xf1, + 0x30, 0x32, 0x7a, 0x25, 0x46, 0xb7, 0x9d, 0x14, 0xb4, 0xa0, 0x74, 0x00, + 0x37, 0x08, 0x85, 0x2e, 0xd8, 0x40, 0xe7, 0xf4, 0x08, 0x5b, 0xeb, 0x65, + 0xc0, 0x09, 0x66, 0xe8, 0xf1, 0xe8, 0x7e, 0xcd, 0x57, 0xba, 0x5a, 0x53, + 0x4d, 0xa3, 0x73, 0xf7, 0x50, 0x09, 0x31, 0xff, 0xf9, 0x32, 0xf5, 0xf3, + 0x07, 0x24, 0xd8, 0xbd, 0xb0, 0x01, 0xcd, 0x8f, 0xe0, 0x73, 0xd0, 0xa6, + 0x55, 0xc1, 0xbe, 0x5b, 0xaa, 0x64, 0xb8, 0x19, 0xf6, 0xec, 0x38, 0x47, + 0xc0, 0x36, 0x42, 0xac, 0xce, 0x26, 0xad, 0x32, 0xcf, 0x4c, 0xdd, 0xfe, + 0xfd, 0x76, 0xbb, 0x61, 0x3a, 0x4d, 0x2c, 0x8d, 0x8c, 0xf3, 0x2e, 0x19, + 0x13, 0xd1, 0xeb, 0x49, 0x7d, 0xf5, 0x35, 0xcc, 0x2f, 0x00, 0x1b, 0xbc, + 0x67, 0xf8, 0x8e, 0x26, 0x3b, 0xe4, 0x63, 0x61, 0x3b, 0x9d, 0x7a, 0x88, + 0x74, 0x84, 0x4e, 0x4f, 0xd2, 0x68, 0x33, 0x37, 0xe8, 0xd7, 0xcd, 0xb9, + 0xd1, 0x7d, 0x67, 0x56, 0x94, 0x19, 0x0e, 0x68, 0xd9, 0xf2, 0xff, 0x8e, + 0x3d, 0x51, 0xd9, 0x5f, 0x51, 0x21, 0x44, 0xc3, 0x59, 0xce, 0xa8, 0x33, + 0x4d, 0x11, 0x66, 0x51, 0x01, 0x62, 0xe1, 0x0c, 0x7c, 0x9b, 0x52, 0xe2, + 0xdc, 0xef, 0x95, 0xdb, 0x41, 0x49, 0xe9, 0x40, 0x50, 0x8b, 0xc7, 0xc2, + 0x75, 0x17, 0xed, 0xb0, 0xd2, 0xed, 0x96, 0xe1, 0x46, 0x83, 0x94, 0x6b, + 0xd9, 0xb6, 0x6b, 0xa9, 0x36, 0xb1, 0x51, 0x29, 0x3c, 0xed, 0x05, 0xbf, + 0x97, 0x1d, 0x48, 0xb8, 0xaf, 0x77, 0x53, 0xb5, 0xf7, 0x7f, 0x8e, 0xd3, + 0x87, 0xb3, 0xa9, 0xcc, 0xbd, 0x50, 0xe2, 0xa7, 0x77, 0xe6, 0x2c, 0xb3, + 0xaa, 0x2f, 0xf3, 0x69, 0x85, 0x67, 0xcb, 0xe9, 0x12, 0x93, 0x64, 0x41, + 0x05, 0x20, 0xbc, 0x0a, 0x19, 0xec, 0x14, 0xb2, 0xab, 0xbe, 0xb0, 0xad, + 0x6f, 0x55, 0x7d, 0xcb, 0x7b, 0xd1, 0xca, 0xa4, 0x0b, 0xcf, 0xe5, 0x27, + 0x72, 0x96, 0x01, 0xac, 0xc4, 0x85, 0x08, 0x1d, 0x0f, 0x50, 0xe1, 0xe3, + 0x3c, 0xaa, 0x54, 0x4a, 0x1a, 0xc9, 0xb8, 0x5e, 0x0d, 0x89, 0x1f, 0x29, + 0x46, 0x3a, 0x37, 0xcd, 0xf2, 0x72, 0xa0, 0x05, 0x25, 0x2e, 0x53, 0x35, + 0x81, 0x18, 0xa4, 0x4a, 0x33, 0x48, 0xf6, 0x4d, 0xb6, 0x79, 0xd2, 0x68, + 0x7b, 0xd5, 0x26, 0x9b, 0xd9, 0xc1, 0xa8, 0x96, 0x20, 0xa9, 0x79, 0x30, + 0x34, 0xe0, 0x36, 0x85, 0x8c, 0x6e, 0x25, 0x0b, 0x96, 0x14, 0x7e, 0x64, + 0x05, 0x48, 0xcb, 0x8b, 0x7b, 0xbc, 0xf4, 0x5d, 0x52, 0x03, 0x63, 0xb0, + 0x70, 0xde, 0x0b, 0xac, 0x09, 0xe1, 0x0c, 0x53, 0xdb, 0x96, 0x48, 0x78, + 0x3a, 0xcc, 0x10, 0xe0, 0x3c, 0x74, 0x2f, 0x42, 0xa2, 0xc9, 0xec, 0x92, + 0x6e, 0xb9, 0x0e, 0x78, 0x05, 0xb4, 0xc4, 0xcc, 0xa3, 0x13, 0xf3, 0x98, + 0x81, 0x60, 0x95, 0x2a, 0x04, 0x64, 0xb4, 0x88, 0x30, 0x79, 0xb0, 0x11, + 0xb1, 0xad, 0xf4, 0x20, 0x20, 0xf7, 0x71, 0x34, 0x16, 0x36, 0xd8, 0x4b, + 0x4a, 0x3b, 0x7a, 0x75, 0x85, 0xb1, 0x00, 0xbe, 0x2c, 0x5e, 0x20, 0x73, + 0xa9, 0x9e, 0x7d, 0xe5, 0x80, 0x88, 0x74, 0xfd, 0xb7, 0x55, 0x43, 0x18, + 0x99, 0xe2, 0xc7, 0x0d, 0xb1, 0x1e, 0xad, 0x8d, 0x65, 0x26, 0x7a, 0x47, + 0x64, 0x4e, 0x66, 0x26, 0x86, 0x48, 0xb0, 0xb1, 0x09, 0xa2, 0x1a, 0x2b, + 0xb5, 0x21, 0x18, 0xb1, 0x00, 0xfd, 0x07, 0x9d, 0x8d, 0x98, 0x05, 0x1d, + 0x48, 0x44, 0x37, 0xc4, 0x9a, 0xe1, 0x31, 0x9f, 0x05, 0x8d, 0x2e, 0x35, + 0x74, 0x3e, 0xd4, 0xa9, 0xae, 0xf6, 0xf8, 0x3b, 0xc6, 0xa1, 0x30, 0xa9, + 0xde, 0xf4, 0x4e, 0x78, 0xe8, 0x53, 0xf9, 0x53, 0xd3, 0x05, 0x62, 0x1c, + 0x1c, 0xd0, 0xc6, 0x82, 0x0b, 0xb9, 0x46, 0x67, 0x6b, 0xed, 0xaf, 0x5b, + 0xd5, 0x99, 0x65, 0x5b, 0x3b, 0xf1, 0x54, 0x7f, 0xa8, 0x9f, 0xc5, 0x17, + 0x06, 0x5b, 0xef, 0xf0, 0x68, 0xec, 0x6c, 0x2d, 0x93, 0xd8, 0xe6, 0xd2, + 0xd8, 0x2c, 0x50, 0x4e, 0x98, 0xa7, 0x4d, 0x07, 0x3e, 0x2b, 0x67, 0x41, + 0x01, 0x4a, 0x63, 0x59, 0xe3, 0xc2, 0x19, 0x83, 0xb0, 0xc1, 0x1a, 0x79, + 0xec, 0xbd, 0x48, 0x29, 0x87, 0xdd, 0xf4, 0xa8, 0x08, 0x49, 0xcb, 0xd4, + 0x08, 0xbd, 0xd6, 0xd8, 0xfb, 0xb2, 0x92, 0x70, 0xb4, 0x42, 0x23, 0x51, + 0x7c, 0xb3, 0x13, 0xf0, 0x29, 0xe7, 0x68, 0x3a, 0x7e, 0xb5, 0x9a, 0xbf, + 0xa6, 0x7c, 0x0f, 0x81, 0x8e, 0x5c, 0xd8, 0x8c, 0xbe, 0x82, 0xbd, 0x11, + 0x25, 0x55, 0xfb, 0x34, 0xab, 0x93, 0x69, 0xd1, 0x8d, 0xf6, 0x8c, 0x63, + 0x00, 0xbe, 0x42, 0x2e, 0xd7, 0xa3, 0x77, 0x8a, 0x51, 0x7b, 0xfa, 0x31, + 0x6f, 0xbf, 0x25, 0xac, 0xdd, 0x46, 0xce, 0xba, 0x87, 0xe2, 0x00, 0x08, + 0xa7, 0xfe, 0x43, 0x17, 0xa7, 0x13, 0xee, 0x2e, 0x8e, 0x38, 0x17, 0xc2, + 0xe6, 0xc9, 0x7e, 0x71, 0xcc, 0x73, 0x49, 0xc2, 0xb4, 0xe9, 0x08, 0x4d, + 0xe0, 0xf8, 0x49, 0x58, 0x03, 0x58, 0x37, 0x07, 0x46, 0xd2, 0x81, 0xc5, + 0x2f, 0x8c, 0x88, 0x17, 0xb9, 0x78, 0x39, 0x2c, 0x8c, 0x35, 0x4a, 0x43, + 0x79, 0x00, 0x0e, 0x8b, 0xd6, 0x2f, 0x99, 0xb3, 0x5b, 0xec, 0x87, 0x57, + 0x91, 0x50, 0xe2, 0xe0, 0xc4, 0xb3, 0x1e, 0x69, 0x67, 0x36, 0x4a, 0x0c, + 0x22, 0xfc, 0x34, 0x10, 0xa6, 0x62, 0xee, 0x20, 0x5d, 0xa6, 0x8c, 0x65, + 0x82, 0x25, 0x45, 0x84, 0xbd, 0x7d, 0x30, 0x31, 0xe3, 0xb5, 0x34, 0x30, + 0xb5, 0x05, 0xaf, 0x07, 0x56, 0x76, 0xc3, 0x12, 0x1a, 0x36, 0xb2, 0xa2, + 0xa7, 0x34, 0xb0, 0xee, 0xd0, 0x6c, 0x46, 0xa1, 0x70, 0x08, 0x1c, 0x5a, + 0x46, 0x3a, 0x0a, 0xef, 0xa4, 0x5d, 0x9b, 0x70, 0x75, 0x55, 0x47, 0xb5, + 0x82, 0x4e, 0x4c, 0x7a, 0xdd, 0x07, 0x23, 0xb2, 0x04, 0xaa, 0xc3, 0xd0, + 0x18, 0x07, 0xe9, 0xe1, 0xdd, 0x2e, 0xa7, 0xfb, 0xc1, 0x10, 0xd1, 0x88, + 0x30, 0xf9, 0x26, 0x84, 0x68, 0xb4, 0x66, 0x5c, 0x44, 0x0f, 0x5d, 0x12, + 0x09, 0xe1, 0x8c, 0xed, 0xae, 0x09, 0xe5, 0xa8, 0x89, 0x7a, 0x10, 0x50, + 0x7e, 0x22, 0x07, 0x79, 0x58, 0xf0, 0x6e, 0xb6, 0x37, 0xb1, 0xd0, 0xbd, + 0x97, 0x35, 0x93, 0xe6, 0x4f, 0x2f, 0xe7, 0x36, 0x8d, 0x9e, 0x1c, 0x23, + 0xa1, 0xfe, 0xff, 0x7f, 0x3e, 0xd7, 0x54, 0xfb, 0x9c, 0x5f, 0x9c, 0x43, + 0xfc, 0xb5, 0x76, 0x18, 0xd1, 0xc4, 0x4e, 0xdc, 0xb6, 0xc8, 0xc4, 0x44, + 0x2b, 0x74, 0xcf, 0xad, 0xbe, 0x1f, 0xd3, 0x04, 0x0c, 0x97, 0x94, 0x98, + 0x8c, 0x4e, 0x32, 0x51, 0x72, 0xd7, 0xf8, 0x0b, 0x36, 0x4c, 0x7d, 0x05, + 0x8b, 0x71, 0xf7, 0x31, 0xac, 0x3c, 0x17, 0x6b, 0x54, 0x53, 0xfc, 0xf7, + 0xd7, 0x8d, 0x6c, 0x1b, 0xf0, 0x80, 0xb3, 0xa0, 0x1d, 0x9d, 0x9a, 0xbd, + 0x07, 0xa3, 0x13, 0x84, 0xd0, 0x30, 0xd1, 0xbf, 0x19, 0xa6, 0x15, 0xb2, + 0x29, 0x4b, 0x67, 0x69, 0x18, 0x4b, 0xb8, 0x1b, 0xae, 0x1f, 0x2f, 0x6f, + 0x35, 0xd1, 0xaf, 0x76, 0xbd, 0x55, 0x73, 0x04, 0x73, 0x67, 0x7e, 0x58, + 0x75, 0xbc, 0x13, 0x94, 0xe9, 0x6e, 0x1b, 0x81, 0x72, 0x42, 0x33, 0xeb, + 0x9d, 0x3b, 0xae, 0x54, 0xe5, 0xff, 0xe2, 0x85, 0xa9, 0x85, 0x0f, 0x54, + 0xee, 0xff, 0x24, 0xda, 0xcd, 0xba, 0x84, 0xac, 0x40, 0x44, 0x67, 0x55, + 0xfa, 0xa4, 0x77, 0xdb, 0x42, 0xe8, 0x20, 0x52, 0xa6, 0x04, 0xaf, 0xb6, + 0xd0, 0x8d, 0x9c, 0x25, 0x13, 0x1c, 0x15, 0xb1, 0x56, 0xba, 0x36, 0xf4, + 0x39, 0xba, 0x90, 0x5b, 0x8b, 0x40, 0xf7, 0x7b, 0x2e, 0xbc, 0x9b, 0x77, + 0xcd, 0x19, 0x06, 0x7b, 0xae, 0x13, 0xcc, 0x35, 0xda, 0x72, 0x56, 0x11, + 0x3c, 0x1b, 0x48, 0x56, 0xf2, 0x6d, 0x2e, 0x48, 0xb0, 0x50, 0x75, 0xe8, + 0x0f, 0xc0, 0x7c, 0x5c, 0x4e, 0xb2, 0xe8, 0xe1, 0xda, 0x87, 0xa3, 0xb8, + 0xfe, 0x7a, 0x97, 0xc2, 0x32, 0x3a, 0x69, 0x0c, 0xfc, 0x1f, 0x0a, 0x52, + 0xfd, 0x94, 0xc3, 0x04, 0x50, 0x49, 0x69, 0xc3, 0x9a, 0x56, 0xf0, 0x3e, + 0x65, 0xa6, 0xb9, 0x22, 0xd7, 0xbc, 0xfd, 0x7f, 0x40, 0xca, 0xbf, 0xec, + 0x28, 0xf5, 0xc7, 0xb6, 0x4b, 0x6b, 0xe9, 0x96, 0x68, 0x66, 0x8d, 0xfd, + 0x4b, 0xfd, 0x89, 0x32, 0x6f, 0x51, 0x2a, 0x39, 0xbc, 0x85, 0xf0, 0xea, + 0x7b, 0x13, 0xec, 0xab, 0x72, 0x47, 0x16, 0x74, 0xbd, 0x7e, 0x48, 0x53, + 0x57, 0x9a, 0x09, 0x31, 0xf2, 0xc2, 0x4c, 0xb9, 0x38, 0x2d, 0xf9, 0xf3, + 0x34, 0xf7, 0xc5, 0xb6, 0x48, 0x9f, 0x5e, 0xaf, 0x17, 0xe0, 0x4e, 0xfb, + 0xfa, 0xe0, 0x59, 0xd8, 0x61, 0x97, 0x0c, 0x59, 0x23, 0xbf, 0x51, 0x51, + 0xcf, 0x0c, 0x82, 0x39, 0xd1, 0xa5, 0x07, 0x01, 0x40, 0x35, 0x1d, 0x40, + 0xbe, 0x1a, 0xe0, 0x7a, 0xf9, 0xe0, 0x49, 0xa5, 0x4b, 0xf1, 0xb3, 0x5b, + 0x89, 0x4a, 0x56, 0x1b, 0x8d, 0xcd, 0xa5, 0x97, 0xc6, 0x0c, 0xa9, 0xca, + 0x1f, 0x73, 0x9f, 0xdb, 0x34, 0xb7, 0x8a, 0xd3, 0x23, 0x93, 0x50, 0x82, + 0xad, 0x9f, 0xb7, 0xdf, 0x67, 0x3c, 0xa2, 0x45, 0xd8, 0x3f, 0xc5, 0x32, + 0xad, 0x9c, 0x32, 0x72, 0xab, 0x5e, 0x7c, 0x80, 0x1a, 0x6f, 0x95, 0xc0, + 0xce, 0xa7, 0x54, 0x17, 0x72, 0x2c, 0xa8, 0xb6, 0x5f, 0x1a, 0xf6, 0x10, + 0x7e, 0xd7, 0x87, 0x61, 0x8f, 0x67, 0x46, 0xd2, 0x18, 0x9f, 0xb7, 0xee, + 0xbd, 0xef, 0x01, 0x86, 0x14, 0xd7, 0x2d, 0xde, 0x59, 0x4d, 0x83, 0xf3, + 0x13, 0x4f, 0xaf, 0xbe, 0x52, 0x2d, 0x57, 0x61, 0x53, 0x2a, 0x54, 0xeb, + 0x66, 0x83, 0xf6, 0xc7, 0x18, 0x9d, 0x12, 0x69, 0xd8, 0x18, 0xff, 0x89, + 0x62, 0xf6, 0xcb, 0xa7, 0xa3, 0xeb, 0xe9, 0xc3, 0xc6, 0x0c, 0xb9, 0x0d, + 0x93, 0x41, 0xc0, 0x23, 0x8a, 0x28, 0xe4, 0xf5, 0x91, 0x06, 0x40, 0xd8, + 0xc7, 0xf2, 0x8a, 0x1e, 0x2d, 0x37, 0x8b, 0x0d, 0x67, 0x7c, 0x72, 0x82, + 0x15, 0x16, 0x37, 0x45, 0x95, 0x9b, 0xd0, 0xc0, 0x3e, 0x0c, 0xdb, 0xc2, + 0xa9, 0xad, 0x9d, 0x46, 0xe6, 0xa5, 0x8b, 0x56, 0x48, 0x5d, 0x8d, 0x10, + 0xe5, 0x47, 0x10, 0x8d, 0xd5, 0x82, 0x5e, 0x30, 0xb9, 0x7f, 0x5f, 0xbe, + 0x57, 0x00, 0xfc, 0x37, 0xbd, 0x59, 0x04, 0xa4, 0xbb, 0x43, 0x11, 0x4e, + 0x25, 0xa7, 0xf2, 0x79, 0x50, 0xbc, 0x07, 0x0a, 0x3d, 0xe4, 0x1b, 0xc5, + 0xd5, 0x10, 0x95, 0x88, 0x01, 0x78, 0x50, 0x1a, 0xa1, 0x56, 0x97, 0x11, + 0x3d, 0x7c, 0xe4, 0xc4, 0x73, 0x4d, 0x61, 0xcf, 0x01, 0x26, 0x00, 0x15, + 0x61, 0xbf, 0xd5, 0xac, 0xa8, 0x61, 0x77, 0x55, 0xbd, 0xc6, 0xd0, 0xd8, + 0xc0, 0x82, 0xa8, 0x67, 0x78, 0xc8, 0xea, 0x1c, 0x84, 0xf6, 0x82, 0x43, + 0x5f, 0x1c, 0xb9, 0xbc, 0x1c, 0xa0, 0x46, 0x6b, 0xbe, 0x9f, 0xdf, 0x98, + 0x71, 0x00, 0x50, 0xbd, 0x99, 0x57, 0x55, 0xbd, 0x93, 0x61, 0xfd, 0xa0, + 0x12, 0xae, 0x6f, 0x18, 0xdc, 0x59, 0x70, 0xf0, 0x5a, 0x66, 0x99, 0xc0, + 0xbf, 0x59, 0xac, 0x73, 0x52, 0x23, 0x9f, 0x74, 0x2e, 0xfd, 0xf3, 0x90, + 0xe4, 0x85, 0x11, 0x18, 0x3d, 0x64, 0xf7, 0xd5, 0x08, 0xc4, 0x6a, 0xa7, + 0x36, 0x0a, 0xf3, 0xbe, 0x6c, 0xf7, 0x80, 0x4c, 0x82, 0x07, 0xfe, 0xe1, + 0xa9, 0xd6, 0x60, 0x2d, 0xa6, 0xca, 0xa4, 0x18, 0x8b, 0xe7, 0x05, 0xad, + 0xa7, 0x5c, 0xe4, 0xf5, 0x3b, 0x28, 0x0d, 0x62, 0x6a, 0xa5, 0xe4, 0x4c, + 0x97, 0x6a, 0x0e, 0xd7, 0x2c, 0xc6, 0xec, 0x34, 0x79, 0x9e, 0xe7, 0xdd, + 0x5d, 0xb8, 0xfe, 0x47, 0x17, 0x7c, 0x09, 0x79, 0xa5, 0x17, 0xc4, 0xd1, + 0xa6, 0xf2, 0xc3, 0x40, 0x3d, 0xe7, 0x30, 0xb1, 0x33, 0x53, 0xbf, 0xbe, + 0x5d, 0x68, 0xbc, 0xc4, 0x12, 0x82, 0x78, 0x69, 0x1a, 0x4d, 0x27, 0x5d, + 0x79, 0x06, 0x42, 0xff, 0xe1, 0x97, 0x1a, 0x9c, 0xe1, 0x30, 0xa0, 0xe3, + 0x2f, 0x0e, 0xdb, 0x6d, 0xcd, 0x7b, 0x9d, 0x9a, 0xf9, 0x79, 0x6e, 0xa5, + 0x30, 0x11, 0x58, 0x8a, 0x03, 0x28, 0xbe, 0x56, 0xa9, 0x86, 0xca, 0xa8, + 0x59, 0xb3, 0x08, 0x5b, 0x0c, 0x26, 0x8d, 0x08, 0xd3, 0xac, 0xcb, 0x13, + 0x6b, 0x9b, 0xe5, 0x60, 0x51, 0xd9, 0xa6, 0xa0, 0x7e, 0x44, 0x8c, 0x7b, + 0xf6, 0x1d, 0x96, 0xf0, 0x5e, 0x91, 0x16, 0x19, 0x96, 0xdc, 0xd9, 0x33, + 0x16, 0x6a, 0xa4, 0xd9, 0xb5, 0x77, 0xd9, 0xc3, 0x34, 0x21, 0xea, 0xac, + 0xaf, 0x4e, 0x2c, 0xc1, 0x22, 0x3f, 0xfa, 0xd9, 0xc6, 0x31, 0xbb, 0x4b, + 0xb5, 0x60, 0x30, 0xc5, 0xa3, 0xe3, 0xab, 0xb9, 0x48, 0xa9, 0x61, 0x0d, + 0x5d, 0xc0, 0x0e, 0xbc, 0xb5, 0xd5, 0xb9, 0xa1, 0xd8, 0xcb, 0x13, 0x7f, + 0x57, 0xd1, 0x67, 0x29, 0x58, 0x13, 0x27, 0xbc, 0x56, 0x76, 0x5c, 0xbb, + 0x7a, 0x93, 0xbe, 0xb4, 0x31, 0xaf, 0x79, 0xaf, 0xde, 0x41, 0xce, 0x5a, + 0x0f, 0xed, 0x71, 0xc0, 0x3f, 0xb2, 0xa2, 0x1f, 0x62, 0x31, 0x5f, 0x93, + 0xaa, 0x92, 0x0d, 0xc6, 0x26, 0x35, 0x30, 0x73, 0xf3, 0x7f, 0xc8, 0x95, + 0xe2, 0x0e, 0x02, 0xe6, 0xd5, 0xbf, 0xa9, 0x04, 0x65, 0x7f, 0xf1, 0x43, + 0xf9, 0x02, 0x5f, 0xe4, 0xde, 0xe6, 0x8d, 0xee, 0x5c, 0xd8, 0x33, 0x0d, + 0xa8, 0x45, 0xf7, 0x79, 0xe1, 0x96, 0x56, 0xb2, 0xea, 0xd7, 0x6c, 0xaf, + 0x75, 0x46, 0x58, 0x3e, 0xca, 0xb0, 0xbd, 0xad, 0x52, 0xcb, 0xfb, 0x52, + 0x52, 0x22, 0x6f, 0x71, 0x5f, 0x81, 0xeb, 0xe7, 0x75, 0x52, 0x60, 0x97, + 0xb6, 0x6e, 0xa0, 0xac, 0x15, 0xe0, 0xee, 0x06, 0xfd, 0xdd, 0xa1, 0x94, + 0x6e, 0x07, 0x26, 0xcf, 0x70, 0xf2, 0xc5, 0x2d, 0xae, 0x85, 0x57, 0xc4, + 0x3c, 0x26, 0xfd, 0xb7, 0x5f, 0xe9, 0xd2, 0x0f, 0x16, 0xaf, 0x1f, 0xdd, + 0x22, 0x7e, 0xdf, 0xc7, 0x1e, 0x29, 0x97, 0x07, 0x23, 0x0e, 0xed, 0x1b, + 0x66, 0x9f, 0xb2, 0x8d, 0x94, 0x1c, 0x55, 0xeb, 0xa5, 0xb9, 0xa6, 0x0d, + 0x2e, 0xf1, 0xd5, 0x31, 0xbe, 0xeb, 0x9c, 0x4b, 0x07, 0xd4, 0x88, 0x5a, + 0x63, 0xd5, 0x0d, 0x0a, 0x8a, 0x5c, 0x14, 0xf6, 0x40, 0xd3, 0x44, 0xa4, + 0x04, 0xd6, 0x28, 0x02, 0xba, 0x3c, 0x49, 0xac, 0x3f, 0x22, 0x3d, 0x98, + 0xef, 0x23, 0x2a, 0x56, 0x4e, 0xdb, 0xcd, 0xd8, 0x20, 0x00, 0x5a, 0xdd, + 0xe2, 0xcb, 0x7d, 0xd7, 0x56, 0x6a, 0xfc, 0xa1, 0x37, 0xc3, 0x4a, 0x9b, + 0xdc, 0x39, 0x10, 0x2e, 0xb3, 0x9d, 0x0b, 0x38, 0xa2, 0x0a, 0x9c, 0x79, + 0xdc, 0xb7, 0xb5, 0x1c, 0xe6, 0x32, 0x5f, 0x62, 0xbd, 0x3f, 0xa4, 0xd8, + 0xf4, 0x4d, 0x7e, 0xa5, 0x57, 0x4e, 0x29, 0xd0, 0x8f, 0x0c, 0x37, 0xc6, + 0x3b, 0x43, 0x9b, 0xc9, 0x7c, 0xd0, 0xe2, 0x9f, 0x9d, 0x39, 0x4b, 0xd3, + 0x10, 0xaa, 0x8e, 0x50, 0x8b, 0x79, 0xcb, 0x8e, 0xc4, 0x5c, 0xe0, 0x98, + 0x81, 0xf0, 0x77, 0xed, 0x17, 0xca, 0x2f, 0x1f, 0xa4, 0xf7, 0x17, 0xc7, + 0x37, 0x08, 0x55, 0xb8, 0x8c, 0x58, 0xa6, 0x2d, 0x66, 0xf7, 0x12, 0xca, + 0xd2, 0x81, 0xe8, 0xcd, 0x61, 0x6d, 0x5a, 0x8a, 0x87, 0xcb, 0x98, 0x0e, + 0x4c, 0x1c, 0x40, 0x34, 0x2a, 0x9e, 0x3b, 0xe4, 0x0e, 0x3c, 0x2f, 0x18, + 0xd9, 0x91, 0xf6, 0x53, 0xba, 0x2c, 0x8c, 0x39, 0x24, 0x8b, 0xc1, 0xeb, + 0xb6, 0xa3, 0xe0, 0x7d, 0x85, 0xfe, 0x71, 0x47, 0xf8, 0xe8, 0x44, 0x2f, + 0xc0, 0x50, 0xe9, 0x51, 0xc5, 0x70, 0x27, 0xdf, 0xc9, 0x27, 0xf4, 0x6d, + 0x9f, 0x8b, 0x67, 0xf4, 0x35, 0x04, 0x2e, 0xee, 0x7b, 0xf1, 0x26, 0x94, + 0x2c, 0xff, 0xd0, 0x8c, 0x20, 0x6f, 0x33, 0x14, 0x4e, 0xd5, 0x92, 0xc9, + 0x6d, 0x18, 0x6e, 0xc7, 0x7f, 0xd2, 0xf4, 0x5e, 0xf7, 0x82, 0xb4, 0x79, + 0x84, 0x94, 0x4f, 0xaa, 0x58, 0x44, 0xb1, 0xc8, 0x11, 0xcd, 0xe2, 0x17, + 0x37, 0x7c, 0x37, 0xa3, 0x00, 0xb9, 0x7b, 0xf0, 0x39, 0x33, 0xea, 0x89, + 0xda, 0x64, 0xbe, 0xe5, 0x98, 0x6e, 0x4e, 0xef, 0xb9, 0x7d, 0xee, 0x3b, + 0xec, 0x39, 0x34, 0x68, 0x10, 0x93, 0x2b, 0x53, 0xa5, 0xf6, 0x78, 0x50, + 0x19, 0x3c, 0xd0, 0x69, 0xf9, 0x90, 0x2e, 0x53, 0x24, 0x55, 0xbb, 0xf4, + 0x9b, 0x34, 0x94, 0xed, 0xa7, 0x2a, 0xaa, 0x40, 0xd4, 0x41, 0xe5, 0xc7, + 0xa4, 0x12, 0x41, 0x6b, 0xb2, 0x9a, 0x40, 0x51, 0x7f, 0xf2, 0x5f, 0x3c, + 0xb6, 0x51, 0x9a, 0xd3, 0xf6, 0x13, 0xe4, 0xfe, 0x51, 0xc8, 0xe7, 0xb7, + 0xe6, 0x56, 0x6a, 0x1f, 0x4e, 0xbd, 0x5d, 0x89, 0xc7, 0x79, 0x3f, 0x77, + 0xf4, 0xfc, 0x13, 0xff, 0x17, 0x05, 0x18, 0x4c, 0xe6, 0xd8, 0x1e, 0xeb, + 0x0a, 0x4e, 0x78, 0xa2, 0xfd, 0xbf, 0x6d, 0xf4, 0xec, 0xae, 0x88, 0xf0, + 0x8a, 0xbf, 0xad, 0x2d, 0x2e, 0xb1, 0x30, 0xa3, 0xd6, 0x8d, 0x54, 0x45, + 0xbc, 0x13, 0xff, 0x06, 0x64, 0xb4, 0x0f, 0x73, 0x08, 0x2c, 0x78, 0x1e, + 0x3f, 0x2b, 0x2a, 0x65, 0x93, 0x8f, 0xe6, 0x75, 0xf7, 0x7e, 0x26, 0x54, + 0x81, 0x23, 0x2d, 0x4f, 0xc8, 0xe5, 0x8d, 0x7c, 0x88, 0x4a, 0xf0, 0x1d, + 0x30, 0xe2, 0x89, 0x70, 0x1c, 0xb1, 0xc6, 0xe7, 0x19, 0xc3, 0x69, 0x7b, + 0x25, 0x50, 0x16, 0x50, 0x51, 0x48, 0xfe, 0xe4, 0xdf, 0x07, 0xa7, 0x5b, + 0xc2, 0x8e, 0xd1, 0xc9, 0x6d, 0x06, 0x78, 0x46, 0x61, 0x2a, 0x52, 0xda, + 0xf5, 0xe9, 0x08, 0x63, 0x55, 0x66, 0xea, 0xb7, 0x49, 0x9d, 0x3c, 0xc9, + 0x0f, 0xfd, 0xd2, 0xf7, 0x74, 0xf2, 0x88, 0xe9, 0xe9, 0x71, 0x96, 0x70, + 0x89, 0x07, 0x1c, 0xf2, 0x3f, 0xb4, 0x9d, 0x4e, 0x14, 0x50, 0x28, 0xd2, + 0x5f, 0xae, 0x46, 0x2a, 0xfb, 0x8a, 0x8c, 0x27, 0x39, 0xb1, 0x76, 0x79, + 0x3b, 0x95, 0xe9, 0x51, 0x9b, 0x81, 0x3c, 0x02, 0x48, 0xd4, 0x95, 0x13, + 0x5c, 0x61, 0xc5, 0x5d, 0x8f, 0x73, 0xb5, 0xc1, 0xa6, 0xa2, 0x3e, 0x69, + 0x5c, 0xdf, 0xe9, 0x1b, 0x97, 0x57, 0xf1, 0xa9, 0x3c, 0xe5, 0xa4, 0x7f, + 0xaf, 0xf7, 0xfa, 0x51, 0x97, 0x13, 0x65, 0xaf, 0xd1, 0xab, 0xc2, 0x98, + 0x2b, 0xe9, 0xc0, 0x85, 0xc0, 0x46, 0x56, 0x8f, 0xb6, 0xed, 0xdf, 0x2f, + 0xf8, 0x58, 0xc4, 0xad, 0x64, 0x5b, 0x37, 0x23, 0xd1, 0x19, 0x83, 0x2c, + 0x4e, 0x15, 0x0e, 0x1d, 0x15, 0x28, 0xa6, 0x63, 0xbe, 0xd5, 0xb7, 0x88, + 0x0f, 0xcb, 0xbd, 0x31, 0xae, 0x0c, 0x14, 0x8c, 0xc9, 0x23, 0x6c, 0x9e, + 0x07, 0xe9, 0xaf, 0xfc, 0x47, 0x8a, 0xff, 0x3d, 0xcc, 0x9d, 0xe2, 0xb5, + 0x6e, 0x95, 0x3b, 0x0d, 0xd4, 0xf0, 0xc8, 0xa5, 0xfa, 0x49, 0x78, 0x80, + 0x8a, 0x68, 0x1d, 0x73, 0x9a, 0xfe, 0x29, 0x7a, 0x9c, 0x9c, 0xcd, 0xf5, + 0xe1, 0xeb, 0x28, 0xb7, 0xfb, 0x8c, 0xb0, 0x1a, 0xeb, 0x0e, 0xa6, 0x22, + 0x3b, 0xf0, 0xca, 0xfa, 0x84, 0x11, 0xaf, 0x2d, 0x93, 0xd3, 0xb1, 0x1a, + 0x15, 0x97, 0xc6, 0x36, 0x81, 0x03, 0xe7, 0xfa, 0x95, 0x61, 0x0a, 0x21, + 0x79, 0x18, 0x6d, 0x0d, 0x12, 0x30, 0x46, 0x0c, 0x31, 0xee, 0xa5, 0x54, + 0x76, 0x56, 0x8d, 0xaa, 0x35, 0x4f, 0x36, 0xc1, 0x8d, 0xdf, 0xd0, 0xca, + 0x78, 0x49, 0xfd, 0x47, 0x69, 0x4c, 0xd2, 0xab, 0x0d, 0xde, 0xbf, 0x6f, + 0x1e, 0xf5, 0xd3, 0x69, 0x89, 0xa1, 0x87, 0x5b, 0x37, 0xd4, 0xf3, 0xa8, + 0x7d, 0xdb, 0xbc, 0xf0, 0xc2, 0x71, 0xb6, 0x11, 0xb4, 0xdb, 0x8d, 0xae, + 0x71, 0x40, 0x6b, 0x9d, 0x05, 0x9a, 0x5f, 0x60, 0xdf, 0x1a, 0x27, 0xb5, + 0x63, 0xbf, 0xfb, 0xb1, 0xa0, 0x44, 0x70, 0xfa, 0xec, 0xe3, 0xe9, 0x2d, + 0xfb, 0x01, 0x5c, 0xfd, 0xce, 0x01, 0xfb, 0x8e, 0x1b, 0x2e, 0x0b, 0x14, + 0xf1, 0xcc, 0x44, 0xb4, 0x07, 0x7d, 0xda, 0xf1, 0xd5, 0xda, 0x4e, 0x62, + 0x6f, 0xed, 0x16, 0xe8, 0xd1, 0xaf, 0x20, 0xb5, 0x10, 0x1b, 0x3b, 0x9e, + 0x44, 0xf4, 0x0a, 0x8a, 0x3d, 0x73, 0x90, 0xfe, 0x90, 0x16, 0x0d, 0x82, + 0x24, 0x1f, 0xda, 0x73, 0xa6, 0x71, 0xd4, 0x1d, 0x5b, 0xee, 0x70, 0xfe, + 0x23, 0x42, 0x24, 0x70, 0x9d, 0xcf, 0x87, 0x45, 0x2f, 0xe8, 0x1b, 0xd5, + 0xc8, 0xb4, 0xce, 0x1d, 0xb3, 0x3a, 0x55, 0x20, 0x9a, 0x37, 0xcd, 0xe0, + 0x41, 0x1e, 0x9a, 0x77, 0x97, 0x4a, 0x7c, 0x07, 0x73, 0x77, 0x3e, 0xc1, + 0xfe, 0x07, 0x9b, 0x02, 0x99, 0x46, 0x8a, 0x58, 0xb7, 0x0e, 0x2a, 0x6a, + 0xff, 0x5b, 0x7b, 0xf5, 0x8d, 0xb7, 0x1f, 0x02, 0x7f, 0x4e, 0x2a, 0xe4, + 0x85, 0x1e, 0x06, 0xe3, 0x9a, 0xa1, 0x4d, 0x6f, 0x3c, 0xc6, 0xf9, 0x10, + 0x25, 0xfb, 0x7f, 0x2e, 0x04, 0x0f, 0x69, 0x0f, 0xa6, 0x5c, 0x7d, 0xa3, + 0xd2, 0xd5, 0x9a, 0xb5, 0x0c, 0x4a, 0x44, 0xab, 0xe2, 0xfb, 0x58, 0xbe, + 0x9a, 0x45, 0x98, 0x26, 0x93, 0xb8, 0xd8, 0x8c, 0x8e, 0x31, 0x74, 0x33, + 0x9e, 0x7d, 0x10, 0xc5, 0xf2, 0x43, 0x26, 0x9e, 0x82, 0xbe, 0x7a, 0x1f, + 0xbe, 0x00, 0xa2, 0xb6, 0xc4, 0x40, 0xa4, 0xdf, 0x6d, 0x27, 0xab, 0xf3, + 0x71, 0x8b, 0x45, 0xc1, 0xb3, 0x2e, 0xf6, 0x4d, 0x9f, 0xaf, 0xa5, 0x94, + 0x72, 0x25, 0x93, 0xf5, 0xde, 0x09, 0x89, 0x0c, 0x94, 0x9b, 0x9c, 0x0b, + 0x14, 0xb4, 0x17, 0x1e, 0xe8, 0x08, 0x52, 0xfc, 0xf1, 0x0d, 0x79, 0xd5, + 0xf0, 0x18, 0x17, 0xea, 0x6c, 0xde, 0x71, 0x0e, 0xde, 0xf7, 0x80, 0x35, + 0x27, 0xbe, 0x67, 0xa9, 0x45, 0x7d, 0x96, 0x2a, 0x22, 0x6b, 0xc8, 0x06, + 0x6e, 0x90, 0x74, 0xab, 0x1d, 0x6b, 0x65, 0xbb, 0xb1, 0xae, 0x13, 0x6c, + 0x0c, 0xd0, 0xe6, 0xca, 0xbc, 0xf4, 0xf6, 0x8a, 0x61, 0x33, 0x7e, 0x46, + 0x27, 0x27, 0xcd, 0x79, 0x57, 0xb1, 0x58, 0x4a, 0xdc, 0xb9, 0x5e, 0xa0, + 0x43, 0x84, 0x58, 0x89, 0x46, 0x0c, 0x6e, 0x60, 0x43, 0x72, 0x4a, 0x74, + 0xf8, 0x07, 0xaa, 0xed, 0x03, 0x02, 0x86, 0x0b, 0x48, 0x2a, 0x09, 0x6b, + 0x97, 0xa9, 0x4d, 0xe9, 0x53, 0x3f, 0xd2, 0xfb, 0xd6, 0x7f, 0x98, 0x3c, + 0x74, 0x2b, 0x75, 0x01, 0x14, 0xd7, 0x05, 0xd4, 0x42, 0xa3, 0x87, 0xef, + 0x9e, 0x60, 0x46, 0x8c, 0xf9, 0x73, 0x1d, 0x97, 0xb0, 0x31, 0x84, 0xa5, + 0x6f, 0x6d, 0x91, 0x12, 0xaa, 0xd4, 0xfc, 0xf8, 0x84, 0x65, 0x99, 0x3c, + 0x36, 0x09, 0xa6, 0x70, 0x1c, 0x31, 0xa1, 0xa3, 0xc6, 0x58, 0x3f, 0x27, + 0xc4, 0x80, 0x12, 0xbc, 0xd8, 0x61, 0xa3, 0x2c, 0x0d, 0x0f, 0x22, 0xd2, + 0xe6, 0xcc, 0xe5, 0xe4, 0x40, 0x92, 0xeb, 0xe4, 0xab, 0xed, 0xdf, 0x93, + 0x40, 0x41, 0x57, 0x44, 0xe1, 0x3a, 0x02, 0x14, 0x22, 0x30, 0xfd, 0x50, + 0x57, 0xc7, 0xe2, 0x00, 0xd2, 0xe5, 0xb0, 0x2c, 0x1f, 0x3e, 0x9f, 0xde, + 0x77, 0xc3, 0xb0, 0x3d, 0xef, 0x00, 0x57, 0x32, 0xe9, 0x34, 0x51, 0x22, + 0x38, 0xb6, 0xbe, 0x6a, 0x2e, 0xcd, 0xea, 0xbe, 0x5e, 0xc7, 0x45, 0x97, + 0x59, 0x5a, 0x3d, 0xa6, 0xca, 0x9f, 0x07, 0x30, 0x70, 0x76, 0x2c, 0xa4, + 0xbb, 0xf3, 0xe4, 0x45, 0xce, 0x57, 0x74, 0x04, 0xc7, 0x3c, 0x19, 0xa5, + 0x43, 0x9d, 0x0b, 0xce, 0xdb, 0xb5, 0xff, 0xc7, 0x7a, 0xe6, 0x6b, 0xe6, + 0xfa, 0x8b, 0xaa, 0xd7, 0x73, 0x3f, 0x7a, 0x13, 0xbf, 0xc7, 0x2e, 0x11, + 0xea, 0xae, 0x9a, 0xab, 0x44, 0x38, 0xb8, 0x15, 0x06, 0xa2, 0x38, 0xda, + 0x8e, 0x76, 0xf1, 0x7f, 0x0d, 0xce, 0x46, 0x5b, 0xa6, 0xb6, 0x8a, 0x25, + 0xeb, 0x6c, 0xb5, 0x18, 0xb5, 0x08, 0x99, 0x01, 0xc7, 0x61, 0xa3, 0xb4, + 0x2f, 0x06, 0x93, 0x95, 0x2e, 0xc6, 0x65, 0x91, 0x40, 0xa4, 0x7e, 0x4e, + 0x46, 0xe9, 0xb4, 0x6d, 0x50, 0x6f, 0xf2, 0x2e, 0x48, 0x6e, 0x83, 0x23, + 0x44, 0xb4, 0xf8, 0x2d, 0x0f, 0xeb, 0x46, 0x9b, 0x96, 0x61, 0x2d, 0x5a, + 0xbf, 0x01, 0xfd, 0xe4, 0x5a, 0x15, 0x2d, 0x76, 0x1e, 0x97, 0xb3, 0x1f, + 0x0e, 0x8c, 0x83, 0xf2, 0x98, 0x0c, 0x82, 0x56, 0x27, 0x39, 0xaf, 0x77, + 0x3a, 0xdf, 0x4d, 0x43, 0x41, 0xd7, 0x38, 0xc5, 0x87, 0xcd, 0xd4, 0x10, + 0x01, 0x3d, 0x8e, 0xb4, 0x7c, 0x47, 0xe3, 0xa2, 0xb8, 0x41, 0x81, 0x73, + 0x10, 0xd9, 0xfd, 0xe2, 0xe3, 0x94, 0xd0, 0x27, 0xfd, 0x9d, 0xd4, 0x44, + 0x2a, 0x31, 0x3e, 0x01, 0x90, 0x6b, 0xc5, 0x49, 0x85, 0x0e, 0x1a, 0x7f, + 0x5a, 0x89, 0xa7, 0x3f, 0x36, 0xde, 0xe9, 0x06, 0x7f, 0x1a, 0x34, 0x9c, + 0x7b, 0x5d, 0x37, 0x66, 0xc6, 0x5d, 0xa9, 0xac, 0xac, 0x85, 0xdb, 0x21, + 0x1e, 0x57, 0xcb, 0xd4, 0x64, 0xd1, 0xf1, 0x67, 0x4f, 0x38, 0xf5, 0x06, + 0xc9, 0xa0, 0x9c, 0x62, 0x2c, 0x55, 0xa7, 0xf2, 0xf4, 0xdf, 0x56, 0xa3, + 0xd0, 0x96, 0x8b, 0xe8, 0x0b, 0x88, 0xa8, 0x5e, 0x3d, 0xc1, 0xc6, 0x3b, + 0x17, 0x57, 0xe8, 0x46, 0x29, 0xb0, 0xa2, 0x3d, 0xe8, 0x91, 0x02, 0x8c, + 0xcc, 0x70, 0xa6, 0x07, 0xb2, 0xf3, 0xd5, 0xbc, 0x66, 0xb0, 0xe5, 0x10, + 0xd5, 0x8f, 0xd9, 0x9f, 0xec, 0xe5, 0xcc, 0xe9, 0x20, 0xcc, 0x60, 0x3f, + 0xef, 0x4f, 0x0e, 0x9b, 0x66, 0xb8, 0x35, 0x96, 0x9a, 0x27, 0xfb, 0xfb, + 0xe2, 0x61, 0x39, 0x3a, 0x73, 0x12, 0xbc, 0xe4, 0x2b, 0x61, 0x19, 0x2c, + 0x89, 0xb5, 0x72, 0x9d, 0xbd, 0xba, 0x40, 0x89, 0xd3, 0x8b, 0x24, 0x0b, + 0x6d, 0x67, 0x75, 0xf7, 0xd7, 0x9f, 0x7d, 0x99, 0x07, 0xfd, 0xd8, 0x25, + 0x0f, 0x03, 0x6e, 0xc2, 0x66, 0xfc, 0x7b, 0x6a, 0x89, 0x9f, 0x0f, 0xb9, + 0x4e, 0xd0, 0xb7, 0xf1, 0x7a, 0x7c, 0x50, 0x65, 0xcb, 0x80, 0x7f, 0xac, + 0x4d, 0xae, 0xe4, 0x04, 0x5f, 0x9c, 0x50, 0xdb, 0xa2, 0xeb, 0x99, 0x1c, + 0x1d, 0x0e, 0x99, 0x74, 0x8d, 0xd0, 0xc9, 0x8c, 0x9a, 0x20, 0x3f, 0xf4, + 0xb7, 0x63, 0xcb, 0xe3, 0x36, 0x81, 0x3b, 0x64, 0x39, 0xd5, 0x69, 0x0c, + 0xa0, 0x8b, 0xd0, 0x5f, 0xb7, 0xb5, 0xba, 0x37, 0x73, 0xf9, 0x4f, 0x91, + 0xa5, 0x61, 0xff, 0x8a, 0x8d, 0x39, 0x56, 0xdd, 0xd9, 0x33, 0x56, 0xba, + 0x49, 0xe2, 0x22, 0x36, 0xc6, 0xaa, 0xe9, 0x6a, 0xed, 0x2c, 0x5b, 0x6c, + 0xbd, 0x2b, 0xca, 0xae, 0x45, 0x40, 0xb1, 0x04, 0x0d, 0xf9, 0xa0, 0x53, + 0xcd, 0xec, 0x04, 0x3d, 0x1f, 0xb5, 0xab, 0xe8, 0xc8, 0xb8, 0xeb, 0x8f, + 0xa8, 0xa3, 0x47, 0x77, 0x7b, 0xde, 0xd1, 0x4a, 0x00, 0x63, 0x46, 0x92, + 0xad, 0xd7, 0x6d, 0x9d, 0x1a, 0x50, 0x0b, 0x8f, 0x08, 0x24, 0xb0, 0x32, + 0x07, 0xa7, 0x9a, 0xc4, 0x27, 0x77, 0x89, 0xe0, 0xad, 0x91, 0x55, 0x65, + 0x90, 0x80, 0x39, 0x6f, 0xfc, 0x54, 0x00, 0xae, 0xd6, 0x81, 0xe0, 0x45, + 0xa8, 0x02, 0xb8, 0x95, 0xca, 0xe7, 0x74, 0x44, 0x85, 0x8b, 0x6f, 0x95, + 0x44, 0xf5, 0x2c, 0xa7, 0x2f, 0x48, 0x99, 0x32, 0x94, 0xc6, 0xb5, 0x7f, + 0xcd, 0x3c, 0x87, 0x3f, 0x4b, 0xad, 0xc4, 0x66, 0xc9, 0x86, 0xe5, 0x0f, + 0xaa, 0x9c, 0x5f, 0x88, 0x08, 0x56, 0x9a, 0x54, 0xe9, 0xe5, 0x40, 0x46, + 0x12, 0x9b, 0x87, 0xa4, 0x39, 0xca, 0x45, 0x61, 0x7d, 0x4f, 0x03, 0x88, + 0x71, 0xb7, 0x1c, 0x43, 0x66, 0xf7, 0x5b, 0x59, 0xa6, 0xa6, 0x7d, 0x83, + 0x28, 0xb5, 0xf9, 0xd3, 0xec, 0xc8, 0xb5, 0x04, 0x93, 0x9a, 0x27, 0x43, + 0x5f, 0xa2, 0xc8, 0x5b, 0xe1, 0xcf, 0x4b, 0xf5, 0x84, 0x4d, 0x87, 0x3c, + 0x6e, 0x40, 0xa1, 0xa0, 0x27, 0xa7, 0x83, 0xbb, 0x98, 0x0d, 0xf8, 0x26, + 0x02, 0x6b, 0xf3, 0x21, 0x8f, 0xf6, 0x21, 0x6a, 0x68, 0xb2, 0x98, 0xa4, + 0x5d, 0x66, 0x01, 0xe9, 0x28, 0x86, 0x77, 0x2d, 0x5c, 0x3a, 0x6a, 0x52, + 0x46, 0x3e, 0xe9, 0x73, 0x9d, 0x7f, 0x67, 0xd8, 0x2e, 0x30, 0x3c, 0x7e, + 0xc9, 0xeb, 0xdf, 0x99, 0x00, 0xfd, 0xdf, 0x6f, 0x4d, 0x33, 0x40, 0x17, + 0xf5, 0xfe, 0xc2, 0x60, 0xf8, 0x40, 0xd1, 0x49, 0xd7, 0x01, 0xdc, 0x43, + 0xf0, 0x23, 0x40, 0x29, 0xb1, 0x78, 0xe6, 0xfc, 0x6a, 0xe8, 0xda, 0xc0, + 0x3d, 0x8c, 0x7e, 0xe5, 0x54, 0xa3, 0x57, 0xda, 0x05, 0x72, 0xf6, 0xde, + 0xd5, 0x1b, 0xe2, 0xc0, 0x50, 0x67, 0xe1, 0x3b, 0xbd, 0x88, 0x1a, 0x4e, + 0x41, 0xf1, 0x0c, 0xef, 0x9c, 0xed, 0x2b, 0xa8, 0xc7, 0x97, 0xc1, 0x6c, + 0x0c, 0x52, 0x42, 0xc2, 0xb7, 0xcf, 0xe2, 0x99, 0x71, 0x48, 0xa7, 0x92, + 0x4d, 0xf0, 0xf3, 0x33, 0xf7, 0xdb, 0x46, 0xf2, 0x3c, 0x57, 0x6e, 0xf3, + 0x17, 0xb8, 0x68, 0xa4, 0xa6, 0x90, 0x2a, 0xe5, 0x15, 0x7d, 0x0f, 0x87, + 0x0a, 0x33, 0xb7, 0xeb, 0x96, 0x8d, 0xe5, 0xc9, 0xd8, 0x8b, 0x3f, 0x85, + 0x4a, 0xe2, 0x77, 0xa9, 0x2a, 0x24, 0xe0, 0x23, 0x8e, 0x6a, 0xde, 0xf4, + 0xac, 0x6c, 0xff, 0xae, 0x78, 0xb9, 0x21, 0x73, 0x09, 0x13, 0x6f, 0x41, + 0x0a, 0x29, 0x68, 0x74, 0x60, 0x67, 0x5e, 0x51, 0x6f, 0xf8, 0x09, 0x28, + 0xf3, 0x84, 0x51, 0x21, 0x31, 0x2e, 0xfa, 0x3e, 0x5c, 0x29, 0x3d, 0xd9, + 0xb8, 0xc9, 0x72, 0x83, 0x17, 0xb4, 0x5d, 0x60, 0xbe, 0xb2, 0x2a, 0x1b, + 0xda, 0x72, 0xad, 0x93, 0x1e, 0xa8, 0x46, 0x29, 0xe1, 0xde, 0xc3, 0x90, + 0x50, 0x0e, 0xa0, 0xf2, 0xdc, 0xbf, 0x2e, 0x22, 0xbc, 0xc9, 0x00, 0xbd, + 0x1c, 0x6a, 0x34, 0x9f, 0xac, 0x81, 0x2e, 0x3e, 0x4d, 0x86, 0x46, 0xff, + 0x05, 0x8e, 0x50, 0x01, 0x20, 0x2d, 0x81, 0x03, 0xf2, 0x73, 0xb3, 0x46, + 0xb7, 0x79, 0xda, 0x4f, 0x40, 0x6e, 0xbd, 0xdc, 0x43, 0x9b, 0x28, 0x25, + 0x6a, 0x16, 0x37, 0x04, 0x09, 0xf0, 0x40, 0x2b, 0x79, 0x53, 0xd2, 0x2d, + 0xb4, 0x68, 0x19, 0x6e, 0x23, 0x3c, 0xdd, 0x8e, 0x85, 0x31, 0xb2, 0x59, + 0x93, 0xf8, 0xad, 0x79, 0x37, 0xd2, 0xd7, 0x72, 0xcf, 0x4d, 0xb5, 0xec, + 0x3e, 0x71, 0x1c, 0x3f, 0x97, 0x0f, 0xbb, 0x5f, 0x1d, 0xfd, 0xf1, 0x9b, + 0x52, 0x62, 0x8b, 0x1d, 0x72, 0xb2, 0x18, 0xd0, 0x0e, 0x5a, 0xac, 0xa4, + 0xf9, 0xd6, 0xd7, 0xa9, 0x13, 0xd2, 0xb6, 0x6b, 0x20, 0xf8, 0x9f, 0xde, + 0x43, 0x35, 0x31, 0x84, 0xbe, 0xc2, 0xe4, 0x89, 0xfb, 0x2c, 0xbd, 0x15, + 0xd0, 0x79, 0x6b, 0x27, 0x7c, 0x79, 0x10, 0xcb, 0xcf, 0xad, 0x24, 0x1c, + 0xb6, 0x17, 0x5b, 0x76, 0x75, 0x7d, 0xa8, 0xa2, 0x3f, 0xf6, 0x6c, 0xf6, + 0xb5, 0x07, 0xc6, 0xce, 0xfe, 0xdd, 0xb7, 0x65, 0x85, 0x9c, 0xb2, 0x99, + 0xab, 0x96, 0x7e, 0x73, 0x03, 0xbf, 0x01, 0x59, 0xd9, 0x96, 0x0d, 0x08, + 0x25, 0xbd, 0xae, 0x06, 0xe9, 0xd3, 0x82, 0xeb, 0xde, 0x00, 0x2e, 0xc2, + 0xf6, 0x74, 0x2b, 0x70, 0x31, 0xd8, 0x2d, 0x47, 0x7f, 0x09, 0x6f, 0xc4, + 0x91, 0xd6, 0x40, 0x0f, 0x34, 0xcf, 0xc6, 0xf0, 0x54, 0xa0, 0x66, 0x5f, + 0xf0, 0x89, 0x9b, 0x6e, 0x4e, 0xfb, 0x39, 0x93, 0x7e, 0x88, 0xb5, 0xd7, + 0xca, 0xc2, 0x08, 0x5c, 0x4d, 0x83, 0xe9, 0x4c, 0xb7, 0x46, 0xf5, 0x1b, + 0x8c, 0xe0, 0x81, 0x7b, 0x95, 0x06, 0xa0, 0x9f, 0xb7, 0x3c, 0x74, 0x96, + 0x48, 0x30, 0x69, 0x6c, 0x83, 0x0b, 0x1e, 0x9a, 0x40, 0x3d, 0xb3, 0xd4, + 0x85, 0xef, 0xfb, 0x77, 0x37, 0xb0, 0xd8, 0x43, 0xaf, 0x19, 0x65, 0x15, + 0xdf, 0x9a, 0x99, 0xf4, 0x8b, 0x72, 0x15, 0x74, 0xa1, 0xc5, 0xe5, 0x04, + 0x2b, 0x13, 0x91, 0xc0, 0x79, 0xa7, 0xf2, 0x7a, 0xf9, 0x91, 0x07, 0x55, + 0xab, 0xa7, 0xf0, 0x4a, 0x66, 0xfb, 0x25, 0x80, 0x46, 0x3f, 0xc1, 0xee, + 0xaf, 0xef, 0xb2, 0xb1, 0x07, 0x7c, 0x74, 0xd8, 0x94, 0x84, 0x97, 0xc7, + 0x4e, 0x65, 0xdf, 0x2b, 0x07, 0x2a, 0x53, 0x78, 0x30, 0x7d, 0xcf, 0x46, + 0x45, 0x10, 0x34, 0x00, 0xc7, 0xf6, 0xf2, 0x81, 0xd1, 0x61, 0xde, 0x63, + 0x8f, 0x66, 0xb2, 0xff, 0xd8, 0x2e, 0x67, 0x68, 0xe8, 0x1e, 0xe3, 0xbb, + 0x7c, 0x3c, 0xec, 0xb3, 0x07, 0x84, 0x35, 0x31, 0x8a, 0x38, 0x7e, 0xfb, + 0xe7, 0xc1, 0x40, 0x4c, 0xb6, 0xd8, 0x9a, 0xf6, 0x98, 0x01, 0xaa, 0xc4, + 0xf1, 0x5d, 0x34, 0x6f, 0xb8, 0xbc, 0xc1, 0x52, 0xde, 0xa2, 0x0b, 0x5d, + 0x8e, 0x7e, 0xea, 0x67, 0xf8, 0xff, 0xf3, 0x77, 0x81, 0xe0, 0xf2, 0x2f, + 0x84, 0xa0, 0xc4, 0xe8, 0x50, 0x16, 0x3d, 0xab, 0x9d, 0xa1, 0x12, 0x79, + 0x1d, 0xee, 0x09, 0xa2, 0xe2, 0x7c, 0xdb, 0xb2, 0x4d, 0xf4, 0xa3, 0x16, + 0xe3, 0x7a, 0x05, 0x5d, 0xd4, 0x0d, 0xb3, 0x67, 0x23, 0x6a, 0xbd, 0x73, + 0xa6, 0x5f, 0xb8, 0xfd, 0x41, 0x3b, 0x7a, 0x08, 0x77, 0x04, 0xd5, 0x7e, + 0x27, 0x5f, 0x60, 0xb4, 0x12, 0xbb, 0xbe, 0x07, 0xbb, 0x68, 0x43, 0xf4, + 0x7d, 0x50, 0xff, 0x6e, 0x61, 0x4a, 0xd3, 0x4e, 0x41, 0xbb, 0xb9, 0xbb, + 0xa7, 0xfb, 0x39, 0xc5, 0xa1, 0xf3, 0x69, 0xdf, 0x41, 0x2a, 0x16, 0x55, + 0x44, 0x06, 0xe5, 0xa3, 0x6c, 0xaa, 0x20, 0x23, 0x4a, 0xfb, 0x29, 0x39, + 0xe0, 0x47, 0xec, 0x88, 0x6d, 0x1b, 0x6c, 0xfe, 0xcf, 0x17, 0x9e, 0x84, + 0x12, 0xc7, 0xf9, 0x5e, 0x27, 0x35, 0x9e, 0xcf, 0xb4, 0x7a, 0x74, 0x92, + 0x06, 0xa0, 0x95, 0x94, 0x73, 0x4d, 0x0f, 0x80, 0xc6, 0x43, 0x69, 0xd8, + 0xda, 0xa1, 0xc2, 0x9f, 0x27, 0x7e, 0x4d, 0x86, 0x93, 0x0a, 0x75, 0x0e, + 0xf2, 0xa6, 0xaf, 0x16, 0x52, 0xec, 0x16, 0xa2, 0x37, 0x82, 0xfc, 0xed, + 0xb5, 0xd0, 0xab, 0x26, 0xfd, 0xcb, 0x2f, 0x0f, 0x24, 0x13, 0xc7, 0x67, + 0x97, 0x32, 0xc2, 0x10, 0xb2, 0x10, 0xb5, 0x51, 0xe0, 0xb5, 0x6b, 0x28, + 0x6e, 0xb4, 0x3d, 0x97, 0x2d, 0x09, 0xef, 0xf0, 0x36, 0xbb, 0x34, 0x40, + 0x8e, 0x7e, 0x5a, 0xbc, 0x3a, 0xab, 0xd9, 0x78, 0x12, 0xe7, 0xd2, 0x0e, + 0x3f, 0x58, 0x00, 0xa5, 0x68, 0xeb, 0xb8, 0x05, 0xd0, 0x00, 0xc0, 0x5c, + 0xb4, 0x19, 0x79, 0x21, 0xf0, 0x6c, 0x5a, 0xc3, 0xf0, 0xa4, 0x65, 0x08, + 0xf7, 0x27, 0x2d, 0xb6, 0x3d, 0xbc, 0xef, 0x86, 0x53, 0x49, 0x93, 0x3f, + 0x26, 0xb4, 0x49, 0xa0, 0x3c, 0x00, 0xb8, 0xc7, 0x38, 0x13, 0xd9, 0x47, + 0x69, 0x58, 0x9c, 0x45, 0x00, 0xdd, 0x76, 0x64, 0xc5, 0x19, 0xc2, 0x98, + 0x45, 0x6d, 0x2f, 0xf5, 0x18, 0xb3, 0xbd, 0x62, 0x39, 0xb2, 0xbe, 0xd2, + 0x07, 0xd6, 0x89, 0x27, 0x0b, 0xa8, 0xc6, 0xc1, 0xba, 0x41, 0x84, 0x6c, + 0xad, 0x27, 0xb0, 0x9a, 0xc6, 0x33, 0x4c, 0x28, 0x93, 0x25, 0x05, 0x0d, + 0xb4, 0x64, 0xbb, 0xa7, 0x96, 0xbf, 0x8d, 0x2e, 0xe6, 0x01, 0x1b, 0xd7, + 0xff, 0xc8, 0x3b, 0x88, 0xab, 0x89, 0x3a, 0xde, 0xd1, 0x5c, 0xf8, 0x94, + 0xeb, 0x5f, 0x7f, 0xc5, 0x83, 0x96, 0x1b, 0x62, 0x9f, 0x8a, 0xf1, 0x1d, + 0x82, 0x6d, 0x2e, 0xba, 0x09, 0xaa, 0xd7, 0x8b, 0x92, 0x36, 0x55, 0x64, + 0xe1, 0x63, 0xfb, 0x2f, 0x22, 0x78, 0x11, 0x6f, 0xff, 0xaf, 0xa8, 0xbf, + 0x30, 0x99, 0xc1, 0xd4, 0x82, 0x8a, 0xc4, 0x60, 0xa4, 0x34, 0x7d, 0xc3, + 0x6f, 0x12, 0xcb, 0xf6, 0x9e, 0x1f, 0xb9, 0x85, 0xac, 0x33, 0xea, 0x46, + 0x63, 0x30, 0x69, 0xc0, 0x38, 0xfd, 0x13, 0x71, 0xf4, 0x95, 0x15, 0xff, + 0xfb, 0x5b, 0x11, 0xdf, 0xac, 0xd3, 0xa6, 0xa5, 0xda, 0xe5, 0x55, 0x57, + 0x35, 0x25, 0xc6, 0x8c, 0x79, 0xf6, 0x08, 0x74, 0x0c, 0xf8, 0xe9, 0x3b, + 0x51, 0x2e, 0x31, 0x17, 0xed, 0x55, 0xf2, 0x27, 0xf8, 0x5f, 0x53, 0x43, + 0x4e, 0xe3, 0xad, 0x34, 0xe2, 0xc2, 0x04, 0x77, 0x23, 0xff, 0x73, 0xcb, + 0x98, 0x83, 0x07, 0x14, 0x0f, 0x5a, 0xa4, 0xf7, 0x87, 0x5e, 0x2b, 0x44, + 0x5a, 0x22, 0x15, 0xd9, 0x81, 0x7c, 0xa7, 0x26, 0xad, 0xe5, 0x47, 0xd2, + 0xe3, 0x3f, 0x73, 0x18, 0xcd, 0x34, 0x05, 0x29, 0xf2, 0xa3, 0x1e, 0x5d, + 0x64, 0x4c, 0x43, 0x95, 0xc4, 0x7b, 0xa8, 0xc5, 0x9d, 0x08, 0x25, 0xf1, + 0x5c, 0x46, 0xfd, 0x7e, 0xf2, 0xbe, 0xd0, 0xa6, 0x3a, 0xaa, 0x5d, 0x73, + 0x6e, 0x9f, 0xc3, 0xa0, 0xd8, 0x65, 0x64, 0xee, 0x8d, 0xef, 0xca, 0x21, + 0x54, 0xed, 0xd6, 0xd8, 0x50, 0x9c, 0xdd, 0xae, 0x5b, 0x87, 0x0c, 0x1e, + 0xb8, 0x9f, 0xf0, 0x33, 0x38, 0x3e, 0xf6, 0x5e, 0x0d, 0x2c, 0x3c, 0x11, + 0x3d, 0xdf, 0x22, 0x27, 0x37, 0x1c, 0x75, 0x6c, 0x4e, 0x17, 0xff, 0xf9, + 0x71, 0xf5, 0x58, 0x42, 0xa4, 0xf0, 0x62, 0x0b, 0x35, 0xa5, 0xd9, 0x9b, + 0x92, 0xd5, 0x8a, 0x7c, 0xe8, 0x57, 0x5d, 0x0d, 0x66, 0xa6, 0x8a, 0x71, + 0xc3, 0x4b, 0x3e, 0xd6, 0xaf, 0x9c, 0x78, 0xcb, 0xc8, 0x33, 0x06, 0xa3, + 0x72, 0x30, 0x85, 0x2a, 0x28, 0x05, 0x61, 0x4e, 0x35, 0xad, 0xaa, 0xa7, + 0xa6, 0x0f, 0x9a, 0x74, 0x84, 0x80, 0x3d, 0xa7, 0xf2, 0x7e, 0x65, 0x0f, + 0x4b, 0x7a, 0xcd, 0x87, 0x1f, 0x88, 0x04, 0x70, 0x62, 0xcb, 0xe8, 0x58, + 0x2b, 0x63, 0xa1, 0x67, 0x94, 0x89, 0xf6, 0x8f, 0xac, 0xae, 0xb7, 0x00, + 0x4c, 0x7f, 0x42, 0x50, 0x9c, 0x7b, 0x87, 0x07, 0x9e, 0x47, 0x46, 0xf6, + 0x97, 0xea, 0xd9, 0x6b, 0x79, 0x69, 0xb0, 0x5e, 0x65, 0xd5, 0x85, 0x60, + 0x41, 0x27, 0xe7, 0x7a, 0xda, 0x11, 0x46, 0xf9, 0xb4, 0x8f, 0x02, 0x5a, + 0xae, 0x6d, 0x90, 0xeb, 0x7b, 0x42, 0x47, 0x18, 0x5c, 0xe3, 0x21, 0x70, + 0x44, 0x1f, 0xe0, 0xeb, 0x2d, 0xb9, 0xd9, 0x20, 0xbd, 0x56, 0x6c, 0xce, + 0xe0, 0x4f, 0x19, 0xc8, 0x6d, 0xf8, 0xe8, 0x1c, 0x91, 0x6e, 0xf6, 0x70, + 0x22, 0x58, 0xea, 0x09, 0x65, 0x88, 0xe8, 0xe8, 0x21, 0xbe, 0xc8, 0xb4, + 0x2c, 0x2d, 0xed, 0x21, 0x66, 0xee, 0x94, 0xac, 0x43, 0xe6, 0x14, 0x3a, + 0x4a, 0x52, 0xc4, 0xf7, 0x7b, 0x63, 0x4a, 0x59, 0x5b, 0x1a, 0x80, 0xaa, + 0xba, 0x87, 0x0f, 0x20, 0x33, 0x7f, 0xec, 0x7c, 0x31, 0xb0, 0x90, 0x7b, + 0x1d, 0x7c, 0x45, 0x06, 0xd7, 0x7d, 0xed, 0xa7, 0x86, 0x04, 0x39, 0x0d, + 0xd6, 0x12, 0x42, 0xa7, 0x73, 0x3a, 0x19, 0xd3, 0x08, 0xfc, 0xe2, 0x67, + 0xaf, 0xcd, 0xcd, 0x5e, 0x57, 0xe2, 0x84, 0xc3, 0x63, 0x6e, 0xed, 0x06, + 0xe0, 0x21, 0x16, 0xf8, 0x6a, 0x94, 0xc3, 0xb4, 0x2a, 0xac, 0x0e, 0x1a, + 0x12, 0x70, 0x4b, 0x72, 0x01, 0x62, 0x0f, 0x3b, 0x61, 0xcd, 0x5d, 0x10, + 0x66, 0x4c, 0xdd, 0x84, 0xd6, 0x04, 0x5a, 0x02, 0xdc, 0xe6, 0xae, 0x8f, + 0xc7, 0x07, 0xe1, 0xd8, 0x34, 0xd6, 0xf7, 0xce, 0xc7, 0x70, 0xc8, 0x2c, + 0x4e, 0xea, 0x37, 0x9f, 0xab, 0x66, 0xe1, 0x05, 0x25, 0x58, 0xd5, 0xbb, + 0xd3, 0x89, 0xfd, 0x15, 0x20, 0x6b, 0xca, 0xfc, 0x04, 0xb4, 0xe3, 0x44, + 0x3f, 0x67, 0x7c, 0xfc, 0x4d, 0x13, 0xa2, 0x3e, 0x88, 0xb1, 0x9e, 0xca, + 0xaa, 0x5f, 0x87, 0x1f, 0x3f, 0x22, 0xed, 0xa2, 0x6e, 0xb3, 0x59, 0x1c, + 0x8f, 0x28, 0x11, 0xc7, 0x8c, 0xea, 0x84, 0x30, 0x54, 0x41, 0x43, 0xaf, + 0x58, 0x87, 0x95, 0xfc, 0x42, 0x60, 0x6e, 0x08, 0xf5, 0xb0, 0x22, 0xa5, + 0x97, 0x3b, 0x56, 0xfe, 0xe2, 0x97, 0xf2, 0x63, 0xf2, 0x83, 0x26, 0x9e, + 0x53, 0xf8, 0x6a, 0xa8, 0xac, 0xea, 0xb8, 0xc6, 0x0e, 0x00, 0x56, 0xc7, + 0x4e, 0x2b, 0x57, 0x57, 0xc4, 0xaa, 0x2e, 0x7e, 0x54, 0xf5, 0x22, 0x8a, + 0x6e, 0xac, 0xfc, 0x70, 0x39, 0x89, 0x60, 0x15, 0x6a, 0x7e, 0xec, 0x1f, + 0x2a, 0x81, 0x87, 0x9a, 0x31, 0x30, 0x0b, 0x45, 0xd8, 0xf7, 0x28, 0xb0, + 0x7a, 0x58, 0x44, 0x10, 0x0a, 0xd7, 0xf2, 0xc6, 0x99, 0x56, 0x59, 0xb5, + 0x12, 0x5a, 0x94, 0x4c, 0x71, 0x5a, 0xcc, 0xf9, 0xcd, 0xac, 0xd8, 0xe7, + 0x34, 0xf0, 0x68, 0x82, 0x94, 0xd9, 0x3b, 0x78, 0x30, 0x37, 0x9b, 0xd3, + 0xf0, 0x1b, 0xce, 0xf4, 0x9f, 0x1d, 0xce, 0x18, 0x72, 0x7a, 0xc0, 0xe1, + 0x53, 0x66, 0x1c, 0x9a, 0xd3, 0x02, 0xae, 0x2b, 0xed, 0x9a, 0xb7, 0xf5, + 0xc7, 0x6e, 0xba, 0xef, 0x33, 0x1c, 0xaf, 0x6f, 0xf4, 0x18, 0x20, 0x82, + 0x28, 0xf5, 0xde, 0x0d, 0xff, 0x97, 0xa7, 0x83, 0xe0, 0x4b, 0x20, 0x67, + 0x2a, 0xea, 0xc3, 0x12, 0x99, 0x7b, 0x8f, 0xdc, 0x0d, 0x33, 0x74, 0x10, + 0xf9, 0x65, 0x7a, 0x4c, 0x1b, 0x30, 0xad, 0x6c, 0x90, 0xdb, 0x8e, 0xc1, + 0x16, 0xbb, 0x71, 0xaf, 0xb1, 0x15, 0xeb, 0xc9, 0x26, 0x57, 0xf3, 0xd6, + 0x1c, 0x21, 0xdf, 0x58, 0x94, 0xeb, 0x9f, 0x29, 0x9a, 0x98, 0x2d, 0x10, + 0xe4, 0xa2, 0x0b, 0x7f, 0x3c, 0x87, 0x88, 0x6f, 0x10, 0xc5, 0x38, 0x2a, + 0xa9, 0xf3, 0x4f, 0xc9, 0xbd, 0x75, 0xb5, 0x6e, 0x5a, 0xa4, 0x2a, 0xdb, + 0xb9, 0x58, 0x6a, 0xfe, 0x3c, 0x27, 0x30, 0xa3, 0xf1, 0x44, 0x5f, 0xbf, + 0xb3, 0x65, 0x12, 0x71, 0x27, 0x2c, 0x62, 0xe1, 0x22, 0xf2, 0xa1, 0x96, + 0x93, 0xf6, 0x3f, 0x1d, 0x90, 0x65, 0xae, 0x27, 0x1b, 0xdd, 0x50, 0x3b, + 0x4d, 0x87, 0x9a, 0xf3, 0x1a, 0x1a, 0x4a, 0xc4, 0x84, 0xb7, 0xf7, 0xeb, + 0xf6, 0xce, 0x13, 0xb2, 0x19, 0x9a, 0x00, 0xcf, 0x39, 0x14, 0x92, 0xaa, + 0x5f, 0x4a, 0x10, 0x46, 0xd1, 0x02, 0x96, 0x72, 0xed, 0x54, 0x19, 0x40, + 0x0f, 0x46, 0x02, 0x35, 0x12, 0xb2, 0x95, 0xb4, 0xb8, 0x92, 0x4d, 0x8c, + 0xb1, 0x38, 0x1d, 0xb0, 0xe4, 0x33, 0xc9, 0x3f, 0xa6, 0x22, 0xfd, 0x2d, + 0x71, 0x98, 0xf7, 0xd4, 0x60, 0x60, 0x0d, 0xd6, 0x62, 0xc9, 0x76, 0xe1, + 0x58, 0x89, 0x95, 0x42, 0x0d, 0x49, 0x7f, 0x35, 0x83, 0x48, 0x20, 0x9d, + 0xc0, 0x9f, 0xf5, 0xe0, 0x51, 0xbc, 0x01, 0xbd, 0xb1, 0xe5, 0x96, 0xde, + 0xf6, 0x20, 0x0c, 0xf3, 0xa0, 0x16, 0xf4, 0x95, 0xf4, 0x85, 0x92, 0x3e, + 0xcf, 0x3e, 0x2a, 0xc6, 0xed, 0x39, 0x51, 0xb3, 0x14, 0x31, 0x3a, 0xf8, + 0x58, 0x59, 0xc8, 0xf5, 0x72, 0x1f, 0x8c, 0xcb, 0x8d, 0x93, 0x1e, 0xb4, + 0x61, 0x50, 0x58, 0xa4, 0xc0, 0xf3, 0xf8, 0xb2, 0x39, 0x2c, 0x56, 0x39, + 0xb6, 0x87, 0x1a, 0x26, 0x7f, 0x06, 0x2d, 0xdd, 0x70, 0x22, 0xeb, 0x33, + 0x8b, 0x3c, 0xba, 0x21, 0x29, 0x7b, 0x8d, 0xde, 0xf6, 0x8f, 0x69, 0x6c, + 0x98, 0x26, 0xd0, 0xa3, 0xd8, 0xfb, 0xef, 0xab, 0x22, 0x7c, 0x39, 0x85, + 0x82, 0xda, 0xc3, 0xb7, 0xec, 0xe8, 0x75, 0x62, 0xc5, 0x85, 0x31, 0xf6, + 0xe3, 0x8f, 0x98, 0x44, 0x37, 0xd6, 0x29, 0xa0, 0xa9, 0x3c, 0x1a, 0xf0, + 0xb6, 0x53, 0xcf, 0x77, 0x08, 0x62, 0xc2, 0x8a, 0xdc, 0x15, 0x77, 0x18, + 0xe8, 0x29, 0x63, 0xf3, 0xea, 0x48, 0x15, 0x16, 0x34, 0x8c, 0x78, 0xbd, + 0x16, 0xad, 0x9c, 0xf3, 0x38, 0xf3, 0xf7, 0x2b, 0x17, 0x95, 0xc2, 0x2c, + 0xfe, 0x97, 0xd8, 0x49, 0x48, 0x76, 0x2b, 0xdf, 0x7b, 0x55, 0xc3, 0xaa, + 0xdd, 0xaf, 0x72, 0x32, 0xd7, 0x63, 0xf7, 0x8b, 0x34, 0x96, 0x8b, 0x8c, + 0xfb, 0xdd, 0xc7, 0x49, 0x2f, 0x19, 0x57, 0xe2, 0x89, 0x29, 0x97, 0x9c, + 0x84, 0x72, 0x90, 0x4c, 0x87, 0xcb, 0x50, 0xae, 0x05, 0x6e, 0xf5, 0x03, + 0xe7, 0x40, 0x1e, 0x3b, 0x00, 0x8b, 0x32, 0xee, 0x8b, 0x9d, 0x93, 0xa0, + 0xd9, 0x5c, 0xeb, 0x15, 0x09, 0x37, 0x0f, 0x06, 0xef, 0x95, 0x64, 0x4c, + 0xea, 0xb0, 0xe7, 0xb2, 0xda, 0x67, 0x0a, 0x45, 0xbd, 0x8c, 0xbc, 0xfd, + 0xf2, 0xef, 0x64, 0xeb, 0xb6, 0xa6, 0xde, 0x8c, 0x14, 0xfd, 0x8f, 0x8a, + 0x73, 0xa9, 0xf0, 0x66, 0xa6, 0xd5, 0xca, 0x29, 0xac, 0x83, 0x8c, 0xb3, + 0xd4, 0x55, 0x87, 0x54, 0xe2, 0x45, 0xaa, 0x9f, 0x51, 0xa0, 0xc3, 0x37, + 0x04, 0x58, 0xe9, 0x37, 0x92, 0x2f, 0x3f, 0xbc, 0x1d, 0x1a, 0x9e, 0xc0, + 0x9b, 0x9a, 0x80, 0xf4, 0xf1, 0xa6, 0x62, 0x98, 0x26, 0x0a, 0xd2, 0xc1, + 0x0b, 0xf3, 0xb7, 0x5e, 0x71, 0xb3, 0x26, 0x85, 0x6e, 0xca, 0x1a, 0x85, + 0x23, 0x9b, 0xab, 0xb8, 0x4e, 0xd9, 0xef, 0x86, 0x6e, 0x8d, 0x75, 0x1e, + 0x3a, 0x54, 0x8c, 0x2a, 0xd4, 0x53, 0x9b, 0xe4, 0x9b, 0x81, 0x3f, 0x59, + 0x46, 0x58, 0x6b, 0x60, 0xa4, 0x98, 0xb5, 0x70, 0x0c, 0xb4, 0x4a, 0x39, + 0xbb, 0x6c, 0x7f, 0xf7, 0xb4, 0x3a, 0x3c, 0x28, 0x32, 0x5c, 0x63, 0x4b, + 0x9a, 0x85, 0x67, 0xb9, 0xc1, 0x23, 0xe5, 0xa9, 0xb6, 0x8a, 0x89, 0x0d, + 0x5f, 0xb6, 0x1a, 0x10, 0xd7, 0x41, 0xca, 0x4d, 0x32, 0xea, 0xc3, 0x21, + 0x33, 0x69, 0xfb, 0x1b, 0x5a, 0x74, 0x9e, 0x6a, 0xaa, 0x9a, 0x96, 0xad, + 0x0c, 0xcb, 0x9b, 0x77, 0xf3, 0x77, 0x04, 0x49, 0x81, 0xb7, 0x78, 0xca, + 0x5e, 0x0d, 0xd8, 0x70, 0x45, 0x1f, 0xbd, 0xcf, 0x84, 0x49, 0xf5, 0x15, + 0x1c, 0xa7, 0xd4, 0x37, 0x2d, 0xbf, 0xf1, 0x1b, 0x78, 0xb8, 0x14, 0xa1, + 0x42, 0x93, 0xd3, 0x2d, 0x1d, 0x83, 0x35, 0x18, 0xb4, 0x48, 0xce, 0xa5, + 0x4c, 0x10, 0x8d, 0xdc, 0xf3, 0xe8, 0x69, 0x2d, 0x25, 0xfb, 0xf0, 0xce, + 0xb9, 0x09, 0x67, 0x14, 0xce, 0xa1, 0x21, 0x0f, 0xc1, 0x41, 0x39, 0x0f, + 0xe6, 0xe2, 0xd6, 0x1d, 0xb4, 0x36, 0x87, 0xb9, 0x81, 0x93, 0x98, 0x67, + 0x5b, 0x17, 0xbf, 0xad, 0x65, 0xed, 0xb5, 0xcb, 0x2e, 0x35, 0x52, 0x8a, + 0xac, 0x0e, 0xe6, 0xfc, 0x7c, 0x73, 0xcf, 0x25, 0x9a, 0x58, 0x9d, 0xab, + 0x12, 0x38, 0x4c, 0xc6, 0x20, 0xf1, 0xc0, 0x15, 0x3b, 0x6d, 0x84, 0x98, + 0xc5, 0x8d, 0xc5, 0x39, 0x35, 0x83, 0x08, 0x0c, 0xab, 0xb8, 0xb6, 0x2e, + 0xd1, 0x14, 0x7e, 0x51, 0x46, 0x2f, 0xa2, 0x16, 0x9f, 0x14, 0x18, 0xe7, + 0x61, 0xdc, 0xeb, 0xbe, 0x19, 0x2d, 0x32, 0x39, 0x25, 0xcc, 0x31, 0x5f, + 0x48, 0xe2, 0xda, 0x79, 0x97, 0x0f, 0xbe, 0x7f, 0x33, 0x64, 0x17, 0x37, + 0x10, 0x2d, 0xdd, 0x41, 0xa1, 0xe4, 0xd5, 0x19, 0xff, 0xd2, 0x96, 0x5a, + 0xc8, 0xb1, 0x1b, 0xe0, 0x29, 0x53, 0x74, 0xa2, 0x0d, 0xd9, 0xbe, 0x50, + 0x6d, 0x83, 0xf8, 0x5a, 0x61, 0x26, 0x9c, 0xf8, 0x2b, 0x8a, 0xf5, 0x70, + 0x05, 0x09, 0x21, 0xe5, 0x90, 0xa4, 0x4b, 0xfe, 0x4f, 0x23, 0x43, 0x83, + 0xfe, 0xfb, 0x4e, 0xd6, 0x39, 0x39, 0x29, 0x1b, 0x87, 0xdc, 0x84, 0x44, + 0xdd, 0x92, 0x9b, 0xcb, 0x12, 0xc2, 0xbc, 0x15, 0xe8, 0x44, 0x83, 0xea, + 0xcc, 0x1b, 0x90, 0x87, 0xf5, 0x9d, 0x5e, 0x6e, 0xf9, 0xcd, 0xc7, 0x68, + 0xd0, 0x8e, 0x89, 0xdf, 0x64, 0xb7, 0xb6, 0x46, 0xe8, 0x6c, 0x09, 0x22, + 0x1f, 0xda, 0xaa, 0x1d, 0xc8, 0xc5, 0x1d, 0xc1, 0xbf, 0x31, 0xc3, 0x69, + 0x28, 0x36, 0x9f, 0x32, 0x1c, 0x72, 0xd0, 0xd5, 0x6f, 0x53, 0x55, 0xed, + 0xd6, 0xa2, 0xc4, 0x24, 0xc3, 0x8b, 0xd2, 0xf2, 0xa1, 0x29, 0xca, 0xb3, + 0x61, 0xc6, 0xa1, 0xd9, 0x77, 0xb9, 0x6c, 0xa1, 0xde, 0x67, 0x8e, 0x14, + 0x4e, 0x38, 0x75, 0x71, 0x1c, 0xd1, 0xa4, 0x67, 0x4f, 0xeb, 0x6f, 0xf3, + 0x7b, 0x28, 0x4b, 0xa9, 0xa5, 0xcf, 0xe6, 0x4d, 0x39, 0x82, 0x12, 0x87, + 0x88, 0x6a, 0x03, 0x78, 0xfb, 0x6a, 0xd4, 0x75, 0xa1, 0x66, 0x0f, 0xef, + 0xad, 0xbb, 0x28, 0x0b, 0xbe, 0x8e, 0xb2, 0x59, 0x65, 0x9a, 0x5f, 0xb0, + 0xb0, 0x7c, 0xff, 0x36, 0x49, 0x84, 0xb8, 0xd5, 0x4f, 0x4d, 0xf8, 0x69, + 0x26, 0x5e, 0x69, 0x72, 0xc1, 0x36, 0xd3, 0xcc, 0x33, 0xaf, 0x2b, 0xb2, + 0x54, 0x24, 0x04, 0x0b, 0x0d, 0x8d, 0xb7, 0xbf, 0xa1, 0x1a, 0x02, 0xcf, + 0xe2, 0x02, 0xf1, 0xf4, 0x93, 0xc3, 0x11, 0x98, 0x82, 0xaf, 0x6c, 0xf1, + 0xe4, 0xb2, 0xbf, 0xbb, 0x3e, 0x46, 0x96, 0x98, 0xad, 0x2f, 0x95, 0x23, + 0x18, 0x14, 0xf9, 0x66, 0x1c, 0x19, 0x36, 0xa9, 0x26, 0xc3, 0xd7, 0xa6, + 0x29, 0xb6, 0x5a, 0xe4, 0xd6, 0x12, 0x2c, 0xea, 0xab, 0x99, 0x4f, 0x3a, + 0xc0, 0xb9, 0x53, 0x3d, 0x3d, 0x89, 0xcd, 0xca, 0x26, 0xaf, 0x8a, 0x25, + 0xed, 0x7f, 0xa9, 0x20, 0xa4, 0x78, 0xb6, 0x1b, 0xea, 0x7c, 0xa8, 0x8a, + 0x9e, 0x21, 0x81, 0xe3, 0xbf, 0xd8, 0x6b, 0x6b, 0x4c, 0xae, 0xd4, 0xdf, + 0x35, 0x63, 0x1e, 0x79, 0x2d, 0xa9, 0x34, 0xa8, 0x43, 0xb1, 0x34, 0x1c, + 0xf2, 0xdd, 0x37, 0x24, 0x44, 0xf8, 0x2b, 0x92, 0xe0, 0x15, 0x2c, 0x6b, + 0x13, 0xb3, 0x43, 0x35, 0xd3, 0x9f, 0x83, 0xfc, 0x22, 0xbd, 0xef, 0x1e, + 0xc4, 0x1a, 0xf7, 0xdb, 0xb4, 0x2d, 0xb1, 0x71, 0x15, 0x2d, 0x72, 0x20, + 0x9d, 0x58, 0xba, 0xd8, 0xec, 0xf4, 0xa4, 0x25, 0xc0, 0x8e, 0xaa, 0xc2, + 0x02, 0x48, 0x81, 0xf3, 0xbf, 0xee, 0x8f, 0x8a, 0x11, 0x9d, 0xf3, 0x38, + 0x2c, 0x45, 0x38, 0xaa, 0xe7, 0xf3, 0x9d, 0x34, 0x90, 0x63, 0xd1, 0xe5, + 0xa5, 0x7e, 0x62, 0x68, 0x6a, 0x21, 0x21, 0x1c, 0x02, 0xfd, 0x18, 0xc3, + 0x77, 0xa5, 0x2d, 0x57, 0x72, 0xb7, 0x2d, 0x0d, 0x83, 0x45, 0x5d, 0xb1, + 0x70, 0xea, 0x0b, 0x8e, 0x0d, 0x5b, 0x48, 0x95, 0x4a, 0x1e, 0x20, 0x5b, + 0x24, 0x1a, 0x76, 0xbe, 0x69, 0xb0, 0x53, 0xed, 0x03, 0x8e, 0x07, 0xa5, + 0x80, 0x63, 0x46, 0x56, 0x07, 0xa6, 0x13, 0x21, 0x75, 0x43, 0x89, 0xf0, + 0x8c, 0x39, 0x37, 0x2e, 0xe0, 0xed, 0x3e, 0x92, 0xaa, 0x00, 0x73, 0xdb, + 0x13, 0x4d, 0x87, 0xb1, 0x2d, 0x1c, 0x56, 0xf4, 0xdb, 0x8a, 0x48, 0x0d, + 0x2d, 0x66, 0x4c, 0x3e, 0xa3, 0x68, 0x65, 0x83, 0x3d, 0x37, 0x9c, 0xf6, + 0xe9, 0xbc, 0x05, 0x05, 0x75, 0xdb, 0x98, 0x65, 0x7a, 0xc4, 0xab, 0x7d, + 0x77, 0x8d, 0xaa, 0xf7, 0x59, 0x8a, 0xb2, 0x3a, 0x8d, 0xb4, 0xa3, 0xe9, + 0xb1, 0x32, 0xe4, 0x6e, 0x44, 0x1e, 0x56, 0xb6, 0x26, 0x2c, 0xb5, 0xe9, + 0xc5, 0x7a, 0xba, 0x32, 0x7a, 0x10, 0x45, 0x91, 0x96, 0x97, 0xd2, 0xe2, + 0x18, 0x6d, 0x11, 0x5d, 0xb6, 0x45, 0x99, 0x65, 0xf5, 0x50, 0xe0, 0xe9, + 0xc1, 0x6c, 0x57, 0xa0, 0xda, 0x32, 0xd4, 0x93, 0x1d, 0xc4, 0x14, 0x6e, + 0xfa, 0x18, 0xa1, 0x9a, 0x5f, 0x4f, 0x77, 0xe0, 0xb7, 0x3d, 0xc3, 0x05, + 0xa8, 0x95, 0xae, 0x6a, 0xe2, 0xd5, 0x6d, 0x86, 0x6a, 0x5b, 0x34, 0x93, + 0x4a, 0x1f, 0x61, 0x8d, 0x0a, 0x8b, 0x2e, 0x7e, 0xa4, 0x5e, 0xa4, 0xdc, + 0xae, 0x40, 0x5c, 0xb8, 0x1e, 0x9c, 0xc5, 0x53, 0x5f, 0xc5, 0x97, 0xe0, + 0xb0, 0xb8, 0x7e, 0xd2, 0xe9, 0xd5, 0x87, 0xe1, 0x49, 0x8a, 0x54, 0x8a, + 0x1e, 0x31, 0x48, 0x0c, 0x5d, 0x05, 0xbb, 0xa7, 0xba, 0x1c, 0x30, 0xa7, + 0xe5, 0xc2, 0x4a, 0x10, 0xeb, 0xcf, 0xa5, 0x2f, 0xbb, 0xb6, 0xbe, 0x9f, + 0xa4, 0xb5, 0x11, 0x42, 0x11, 0x9c, 0xd6, 0x5b, 0x9e, 0x08, 0xa5, 0x10, + 0x2a, 0x70, 0x73, 0xc5, 0x8c, 0x75, 0xd3, 0xb3, 0x4a, 0xfd, 0x73, 0xfd, + 0xaf, 0xea, 0x56, 0x6d, 0x2d, 0xa7, 0x9a, 0x64, 0x68, 0x80, 0xfa, 0xdc, + 0x00, 0xc8, 0xaf, 0xa2, 0x51, 0xb2, 0x2c, 0x89, 0x21, 0x02, 0xc9, 0x60, + 0x71, 0x71, 0x26, 0xa2, 0xbf, 0x50, 0xf8, 0x08, 0x53, 0x70, 0x26, 0xd5, + 0xe5, 0x9a, 0x60, 0x2a, 0xb4, 0xa2, 0xd6, 0x00, 0x9f, 0xfa, 0x8d, 0xee, + 0x8e, 0xf9, 0x45, 0x72, 0x71, 0xc7, 0x48, 0x7d, 0x79, 0x97, 0x30, 0x1e, + 0x89, 0x24, 0xde, 0xc8, 0xaf, 0x9a, 0x7f, 0x8c, 0x86, 0x2e, 0xde, 0xde, + 0x31, 0xb9, 0x3e, 0x79, 0x8c, 0x99, 0x01, 0xe4, 0x1c, 0x34, 0x6d, 0x88, + 0x5d, 0x27, 0x34, 0x46, 0x2a, 0xf7, 0x68, 0xd2, 0x04, 0xb9, 0x95, 0x1b, + 0x39, 0x5d, 0xd2, 0xfd, 0x2d, 0x0d, 0x1f, 0x6c, 0xd3, 0xdb, 0x81, 0x01, + 0x24, 0x63, 0x05, 0x3a, 0x12, 0x4d, 0x13, 0x4b, 0xd5, 0x21, 0xe2, 0x4c, + 0x52, 0xd9, 0xc1, 0xc5, 0x53, 0x8c, 0xfb, 0x09, 0x3d, 0xfe, 0x37, 0x83, + 0x32, 0x37, 0x0c, 0x50, 0x35, 0x64, 0x90, 0x85, 0x69, 0xa0, 0xb6, 0x47, + 0x0e, 0x34, 0x30, 0xc5, 0x76, 0x4b, 0x3e, 0xf3, 0x6c, 0xf5, 0x41, 0xe9, + 0x78, 0x7a, 0x94, 0x7f, 0x51, 0x42, 0x6a, 0x50, 0x11, 0xd3, 0xf5, 0xc1, + 0x0b, 0x53, 0xca, 0xc4, 0xe5, 0xca, 0xfb, 0x89, 0x0e, 0x19, 0x95, 0x97, + 0xdc, 0xad, 0xf7, 0x04, 0x6f, 0x54, 0xe1, 0x94, 0xd8, 0xa8, 0xf4, 0x81, + 0x17, 0x73, 0xe0, 0xfb, 0xee, 0x94, 0xba, 0x8a, 0xcf, 0xa7, 0xe0, 0x23, + 0x2e, 0x96, 0xca, 0x5b, 0x14, 0x06, 0x77, 0xb4, 0x4c, 0x2a, 0x4e, 0xc5, + 0x4d, 0x53, 0xa7, 0x03, 0x44, 0x81, 0x19, 0x5c, 0xdc, 0x65, 0x8f, 0x62, + 0x3e, 0xfd, 0x91, 0xe9, 0xd4, 0x49, 0x7d, 0x97, 0xd5, 0x22, 0x20, 0x25, + 0x5c, 0xc2, 0xc6, 0xb3, 0xa6, 0xac, 0x22, 0x55, 0xb4, 0xee, 0x96, 0x0c, + 0xf8, 0xb2, 0xec, 0x0d, 0xc7, 0x31, 0x5d, 0x67, 0x24, 0x0d, 0x60, 0xd3, + 0xc8, 0x9a, 0x19, 0xe0, 0xdb, 0x38, 0x72, 0xb8, 0x9d, 0xce, 0xd7, 0x18, + 0x83, 0x2f, 0xa2, 0x30, 0x4e, 0x85, 0x02, 0x90, 0x6e, 0x10, 0x3c, 0x0c, + 0x0d, 0x6a, 0x8e, 0x37, 0x61, 0x3e, 0x78, 0x15, 0xd5, 0x4d, 0xa8, 0xdb, + 0xd4, 0x36, 0x29, 0x55, 0xcb, 0x9b, 0x99, 0x2e, 0x77, 0xe4, 0xaa, 0x1c, + 0x52, 0x56, 0x08, 0xda, 0x87, 0xc5, 0x3b, 0xe2, 0x8c, 0xa2, 0x7a, 0x6c, + 0xb5, 0x19, 0x28, 0x4c, 0xbc, 0xa6, 0xf5, 0x97, 0x80, 0xd0, 0x0f, 0x1e, + 0x81, 0xea, 0xb4, 0x7f, 0x8d, 0x23, 0x8b, 0x05, 0x9a, 0xbb, 0x03, 0x53, + 0x87, 0xa2, 0x84, 0x03, 0x9a, 0xcd, 0xdf, 0xfd, 0x5c, 0xe8, 0x49, 0xd9, + 0x11, 0x40, 0x85, 0x4e, 0x41, 0xe2, 0x1a, 0x9c, 0x7c, 0xc7, 0xa0, 0x65, + 0xfa, 0xd0, 0xbb, 0xc2, 0xd3, 0x94, 0xe1, 0xc3, 0x05, 0x9d, 0x41, 0x1b, + 0x0d, 0x7e, 0x5e, 0x8e, 0xc5, 0xec, 0xb7, 0x64, 0x0c, 0xa9, 0x46, 0xda, + 0x35, 0x09, 0xa5, 0x6a, 0x66, 0xfa, 0x38, 0x3d, 0xa4, 0xe0, 0x10, 0x8a, + 0x9d, 0xa5, 0xe1, 0xd4, 0xad, 0x59, 0xf0, 0x41, 0x09, 0x26, 0x39, 0x22, + 0x6f, 0x18, 0x77, 0x6d, 0x4d, 0x9f, 0x0b, 0x68, 0x0c, 0x72, 0xc2, 0xee, + 0x83, 0x60, 0x80, 0x29, 0x4d, 0x8b, 0x9a, 0xe9, 0x9f, 0x12, 0xf2, 0xae, + 0x7a, 0x3d, 0x4b, 0xea, 0x01, 0xfe, 0x5e, 0xee, 0x84, 0xb8, 0x9c, 0xb9, + 0x14, 0x7e, 0xe7, 0x07, 0xe7, 0x7e, 0x64, 0xf7, 0x54, 0x2c, 0x2c, 0x9c, + 0x48, 0xf8, 0x98, 0xda, 0xc4, 0x44, 0x2a, 0x4e, 0xe2, 0x6a, 0xcb, 0x42, + 0x48, 0xe4, 0xc8, 0xa7, 0x6e, 0x90, 0x60, 0x39, 0xd9, 0xa2, 0x2b, 0xd4, + 0x5c, 0x43, 0xaf, 0xec, 0xd8, 0xe7, 0xce, 0xe1, 0x16, 0x79, 0xc3, 0x61, + 0xee, 0x98, 0xe1, 0x8f, 0x2c, 0xd4, 0xbf, 0x44, 0x6f, 0xd1, 0x8b, 0xad, + 0x78, 0xb6, 0x9d, 0xd7, 0xc5, 0x0d, 0x37, 0xc4, 0x18, 0xb2, 0x90, 0x9d, + 0x77, 0xc5, 0x48, 0x01, 0xce, 0x66, 0x52, 0x65, 0x10, 0x90, 0x91, 0x9d, + 0xcc, 0x6f, 0xae, 0xe9, 0x4b, 0x29, 0x53, 0x1f, 0x56, 0x6f, 0x35, 0x11, + 0x01, 0x28, 0xec, 0x0d, 0x3e, 0x70, 0x12, 0x92, 0x91, 0x37, 0x79, 0x56, + 0x2a, 0x34, 0xb4, 0x79, 0x64, 0x73, 0xe3, 0xa5, 0x96, 0x61, 0xa0, 0xa9, + 0x0c, 0xd0, 0x29, 0xdc, 0xa7, 0x63, 0xba, 0xb5, 0x36, 0x23, 0xf4, 0xf0, + 0xfe, 0xaf, 0xf1, 0x40, 0xa0, 0x69, 0xd4, 0x02, 0x66, 0x5e, 0x05, 0x8d, + 0x1f, 0x03, 0x72, 0xcd, 0x30, 0x91, 0x19, 0x2d, 0xab, 0x24, 0xf4, 0x82, + 0x61, 0x86, 0x8f, 0x40, 0x42, 0x4b, 0xc1, 0x96, 0x6b, 0x81, 0x75, 0x29, + 0x56, 0x49, 0x23, 0x20, 0x8c, 0x23, 0x50, 0xe7, 0xe5, 0xdb, 0x81, 0x6b, + 0x1c, 0x50, 0x32, 0xb9, 0x37, 0x06, 0x89, 0x27, 0x8e, 0x37, 0xb0, 0x4a, + 0x69, 0xe1, 0x11, 0x91, 0xf5, 0x61, 0x47, 0x59, 0x29, 0xe0, 0x23, 0x27, + 0x64, 0x43, 0x52, 0xac, 0xa9, 0x05, 0x90, 0x60, 0x52, 0x2c, 0x53, 0x6b, + 0x6f, 0x19, 0xed, 0x17, 0x35, 0x0d, 0xb6, 0x05, 0x1e, 0x06, 0x91, 0xb8, + 0x48, 0xc9, 0x28, 0x5d, 0x4d, 0x50, 0x66, 0xfe, 0xa4, 0x40, 0x1b, 0xcb, + 0x90, 0xe9, 0x61, 0xbd, 0x8d, 0x4a, 0x31, 0xbe, 0x80, 0xa3, 0x5f, 0xf3, + 0x57, 0x40, 0x68, 0xfc, 0x36, 0x99, 0xc8, 0xb5, 0x4b, 0x04, 0x89, 0x4b, + 0xcb, 0xde, 0x78, 0x3a, 0xd5, 0xd5, 0xd3, 0x72, 0x1d, 0x9d, 0xa0, 0x2c, + 0xe0, 0x99, 0x3c, 0xe1, 0x5f, 0x8d, 0xfb, 0x6f, 0x68, 0x7f, 0xf8, 0x2f, + 0x06, 0x36, 0x79, 0x9d, 0x6e, 0x0b, 0xa9, 0x11, 0x85, 0x6b, 0x73, 0x49, + 0xf9, 0x1d, 0xc8, 0x6a, 0x6a, 0xbc, 0x79, 0xd8, 0x58, 0x68, 0xbf, 0xbf, + 0x83, 0xee, 0x89, 0x8c, 0x5d, 0x41, 0x5f, 0x2e, 0x69, 0x10, 0x36, 0x5d, + 0x41, 0x84, 0x58, 0x4c, 0x38, 0x86, 0xf9, 0xc9, 0xc8, 0x38, 0xe3, 0xdd, + 0x50, 0x2a, 0x45, 0x19, 0x06, 0x98, 0x80, 0x6c, 0xf3, 0xff, 0x05, 0x89, + 0xaf, 0x1b, 0x58, 0x74, 0xf2, 0xac, 0x85, 0xc5, 0xc6, 0xab, 0xd0, 0x07, + 0xc3, 0x65, 0xb9, 0xcb, 0x44, 0x4f, 0xda, 0x6b, 0xef, 0x36, 0x50, 0x6a, + 0x8c, 0xec, 0xd4, 0x74, 0xd4, 0xda, 0x28, 0x51, 0xd9, 0x80, 0xdf, 0xfb, + 0x25, 0x28, 0x15, 0x4b, 0x5d, 0x05, 0xbe, 0x0c, 0x71, 0x2a, 0x22, 0xa7, + 0xee, 0x17, 0xb3, 0x07, 0xcc, 0xa3, 0x67, 0xc9, 0x12, 0x46, 0x7a, 0x74, + 0x84, 0x99, 0xb9, 0x5c, 0x2b, 0x60, 0xd9, 0x47, 0x2a, 0xd0, 0x65, 0x43, + 0xff, 0x58, 0x4b, 0x8a, 0x16, 0xc1, 0x01, 0x2c, 0x72, 0x56, 0xe4, 0x50, + 0xd8, 0x68, 0x0c, 0x3c, 0xfb, 0x53, 0x79, 0xbe, 0xaf, 0xb5, 0xba, 0x6a, + 0x5f, 0x69, 0xa5, 0x24, 0x14, 0xee, 0x0b, 0x92, 0x0f, 0xae, 0xb0, 0x7c, + 0x8c, 0x2b, 0xd1, 0x5d, 0x38, 0x21, 0x9c, 0x57, 0x9e, 0x85, 0xc4, 0x1e, + 0x65, 0x47, 0x79, 0x51, 0xcc, 0x09, 0xac, 0x1d, 0xac, 0x0e, 0x09, 0x49, + 0x2f, 0xd0, 0xc1, 0x0e, 0xc9, 0xce, 0xe0, 0xec, 0x52, 0xb6, 0xf8, 0xcc, + 0x11, 0x06, 0x12, 0xee, 0xfa, 0x0f, 0x11, 0x7e, 0x4b, 0xce, 0x3a, 0x4e, + 0xab, 0x9f, 0xef, 0xa5, 0xea, 0x1d, 0xa2, 0x84, 0x82, 0x53, 0xaa, 0xcf, + 0x8e, 0xed, 0xd2, 0xf5, 0x11, 0x05, 0x55, 0xa9, 0x6a, 0x22, 0xf0, 0xdf, + 0xbf, 0x15, 0xac, 0x38, 0x8b, 0x2e, 0xf6, 0x5d, 0xff, 0x04, 0x20, 0xd1, + 0xc1, 0x3e, 0xc3, 0x8f, 0xa2, 0x3f, 0x19, 0xa9, 0xd4, 0x7f, 0xe5, 0xa4, + 0xd3, 0x4d, 0xae, 0xc6, 0x71, 0xb9, 0x28, 0x0e, 0x53, 0x42, 0x33, 0x9a, + 0xb5, 0x68, 0xa1, 0xc0, 0xe6, 0x17, 0xbc, 0x92, 0x43, 0xaf, 0x77, 0xdd, + 0x6e, 0x6e, 0x53, 0xa1, 0x0a, 0xd1, 0x10, 0xe7, 0x0d, 0x45, 0x45, 0x7d, + 0xe7, 0xc9, 0x73, 0x45, 0xfe, 0x70, 0x6f, 0x13, 0xb1, 0xcf, 0x02, 0x82, + 0xea, 0xde, 0x44, 0x70, 0x18, 0xf9, 0x2b, 0xed, 0x02, 0xef, 0x24, 0x3e, + 0x4b, 0xe7, 0x41, 0x60, 0x2f, 0x32, 0x93, 0x36, 0xd9, 0x9c, 0xae, 0x19, + 0xa6, 0x11, 0x24, 0x2d, 0xc2, 0xa0, 0x2c, 0x17, 0x2d, 0xa5, 0xca, 0x4a, + 0xb5, 0xe3, 0xcb, 0x49, 0xa0, 0x8c, 0x29, 0x8b, 0x8e, 0x8e, 0xa0, 0x1b, + 0x59, 0xf2, 0xb8, 0xa6, 0xf3, 0xd5, 0xa1, 0x7a, 0x36, 0x04, 0x58, 0xb0, + 0x9b, 0x30, 0x13, 0x45, 0x36, 0xde, 0x77, 0xfe, 0xc6, 0x06, 0xc5, 0xbf, + 0x7c, 0x82, 0x5b, 0xc2, 0x84, 0xa9, 0xd8, 0xb9, 0xc1, 0x2a, 0xde, 0x9a, + 0x35, 0x45, 0x1b, 0xc5, 0x9b, 0x9d, 0x9c, 0x4c, 0xd6, 0x7f, 0x4c, 0xb1, + 0xc6, 0xa6, 0x90, 0x3b, 0x33, 0xb8, 0xff, 0xfa, 0xdc, 0x76, 0x37, 0xef, + 0xa2, 0x66, 0x50, 0xdb, 0xfc, 0x71, 0xaa, 0x73, 0x98, 0xed, 0xdc, 0x1e, + 0xe0, 0x6f, 0xe6, 0xdd, 0x72, 0x12, 0xaa, 0x2a, 0x6d, 0xd8, 0xa7, 0x9b, + 0x07, 0x45, 0x26, 0x83, 0xce, 0x9a, 0x34, 0xc8, 0xde, 0xc1, 0x8f, 0xcc, + 0x07, 0x22, 0xa9, 0x22, 0x1d, 0xa8, 0x67, 0x7c, 0xff, 0x0e, 0xc9, 0x10, + 0x4e, 0x89, 0x85, 0x14, 0xf8, 0xd9, 0x4c, 0xc3, 0x64, 0xe3, 0xfd, 0x33, + 0x3f, 0xff, 0xd9, 0xe9, 0x20, 0x9b, 0x76, 0x8f, 0xe6, 0xcb, 0xc6, 0x85, + 0x1d, 0xdc, 0xf9, 0xff, 0x8a, 0x4d, 0xb3, 0x94, 0xe1, 0x9f, 0x71, 0xb4, + 0x5e, 0x69, 0x1f, 0x10, 0x93, 0x11, 0x61, 0x31, 0x77, 0x43, 0x11, 0x8a, + 0xef, 0x3d, 0x77, 0x5a, 0x0d, 0x3a, 0x5c, 0x79, 0xb5, 0x74, 0xce, 0xb0, + 0xa0, 0x56, 0x1f, 0x81, 0x39, 0x36, 0xba, 0x05, 0x73, 0xe7, 0x86, 0xda, + 0x40, 0xd3, 0x17, 0x2b, 0xb4, 0x1d, 0xe9, 0x54, 0x61, 0x91, 0x48, 0x6d, + 0x0d, 0x9a, 0x6e, 0xa6, 0x73, 0xa0, 0x0d, 0x26, 0x94, 0xee, 0x46, 0x7d, + 0xc6, 0x41, 0xf7, 0xe8, 0x14, 0xf2, 0xa6, 0xdd, 0xdb, 0xa0, 0x81, 0x46, + 0x8b, 0xdc, 0xb8, 0x56, 0x5a, 0x49, 0x3e, 0x90, 0x68, 0x7c, 0x8f, 0x6e, + 0xe7, 0x9e, 0xee, 0xa1, 0xce, 0xe7, 0xa6, 0xdd, 0xe1, 0x22, 0xbb, 0x08, + 0x9d, 0xd2, 0xe0, 0xe3, 0x17, 0xa8, 0xc5, 0x91, 0x60, 0x18, 0xd5, 0x30, + 0xe2, 0xc5, 0x63, 0x0c, 0x44, 0x23, 0x2b, 0x14, 0xfa, 0xd2, 0xfa, 0x8b, + 0xe9, 0x51, 0x0d, 0x19, 0xb2, 0xa7, 0xf4, 0x5e, 0x85, 0xa8, 0xd0, 0x79, + 0xd8, 0x44, 0x4f, 0xed, 0x9e, 0x07, 0xa0, 0x1f, 0xd5, 0xd5, 0x27, 0x24, + 0x9d, 0x87, 0x93, 0x30, 0xd0, 0x3e, 0x98, 0x6d, 0x64, 0xab, 0xa2, 0xb0, + 0x42, 0xfa, 0x91, 0x85, 0xe2, 0xcc, 0xfe, 0x29, 0xdf, 0x5b, 0x6e, 0x45, + 0x31, 0x27, 0xd7, 0xe2, 0x24, 0x92, 0x59, 0x3e, 0xe8, 0x2f, 0x3f, 0xc6, + 0xe7, 0xd6, 0xf9, 0xfa, 0x1d, 0x4a, 0x3d, 0xbb, 0x6e, 0x58, 0x4f, 0x81, + 0x01, 0x37, 0x73, 0xbb, 0xb9, 0xd3, 0x8e, 0x7a, 0x8a, 0xd0, 0x39, 0x2c, + 0xfa, 0xb9, 0x08, 0x8b, 0x80, 0x3b, 0x59, 0xec, 0x8d, 0xb5, 0xd7, 0x2b, + 0x03, 0x75, 0xaa, 0xfa, 0xc7, 0xd3, 0xbc, 0x48, 0x6b, 0x12, 0xb1, 0xcb, + 0x01, 0x43, 0x9f, 0x92, 0x41, 0xfe, 0xa3, 0xd5, 0xf9, 0xd4, 0x4a, 0x20, + 0x33, 0xd4, 0xb3, 0x18, 0x88, 0x56, 0xed, 0xb3, 0x2f, 0x16, 0xa5, 0x07, + 0x9a, 0x48, 0xb1, 0x0d, 0x54, 0x50, 0xe4, 0x45, 0x24, 0xf0, 0x76, 0xcd, + 0x0e, 0x5c, 0x4a, 0x7f, 0xa9, 0x7b, 0xbf, 0x5f, 0x6a, 0xa3, 0xa6, 0x5c, + 0xe1, 0xb0, 0xb3, 0xe1, 0xc1, 0xd5, 0x6b, 0x4e, 0xcf, 0x67, 0xd2, 0x36, + 0x88, 0x4d, 0xa5, 0xe9, 0x5f, 0xd0, 0x85, 0xb6, 0x26, 0xe8, 0x0d, 0x11, + 0xc6, 0x7b, 0x7b, 0x62, 0xa7, 0xa0, 0x33, 0x40, 0xad, 0x35, 0x20, 0x49, + 0xf0, 0x27, 0x0a, 0x65, 0x84, 0xc5, 0xd2, 0x4f, 0x3e, 0xcb, 0xe3, 0x04, + 0x03, 0xe3, 0x91, 0x1a, 0x7e, 0x62, 0x96, 0x9f, 0x43, 0x5a, 0xe4, 0xa8, + 0x8b, 0x78, 0x52, 0x3a, 0x29, 0x50, 0x4c, 0xa5, 0x6d, 0xf5, 0x6e, 0x26, + 0x9f, 0x55, 0xc5, 0x10, 0xbe, 0xf9, 0xab, 0x37, 0x36, 0xbd, 0x6e, 0x10, + 0xc4, 0xdc, 0xbc, 0x56, 0x72, 0x6e, 0x68, 0xbd, 0xf8, 0xee, 0xc8, 0xde, + 0x5e, 0xf7, 0x2e, 0x90, 0x05, 0xb4, 0x2b, 0x84, 0x9a, 0x3a, 0x3d, 0x46, + 0xbe, 0xb8, 0xd4, 0xd1, 0xb3, 0x60, 0xc0, 0x40, 0x53, 0x62, 0x28, 0x40, + 0xcc, 0xc7, 0x9b, 0xe1, 0xd5, 0x9a, 0x3f, 0xfa, 0xb9, 0x75, 0x49, 0x15, + 0x57, 0x39, 0xb0, 0x6f, 0x2a, 0x54, 0x80, 0x05, 0x29, 0xbd, 0xb8, 0x92, + 0x91, 0x63, 0xd3, 0x09, 0x93, 0xab, 0x8a, 0x74, 0xf9, 0xc3, 0x63, 0x00, + 0x6c, 0xd5, 0xe3, 0xc1, 0xd7, 0xa0, 0x1d, 0x8d, 0xea, 0x4a, 0xb2, 0x4c, + 0xdb, 0x63, 0x8f, 0x12, 0xf3, 0x9c, 0x90, 0x7d, 0x32, 0xd0, 0xb9, 0x96, + 0xfd, 0x57, 0x58, 0xdc, 0x26, 0xa5, 0x7b, 0x6a, 0xf2, 0x32, 0x79, 0xbe, + 0x0d, 0x21, 0x4e, 0xe6, 0x71, 0xd0, 0x08, 0x2f, 0xb9, 0x13, 0x01, 0x23, + 0xa0, 0xe7, 0x63, 0x99, 0x3f, 0x3c, 0x0b, 0x57, 0x7f, 0x27, 0x7c, 0x4c, + 0x44, 0xa3, 0xae, 0xef, 0xdb, 0xd1, 0xc8, 0xf4, 0xa2, 0xa1, 0xc6, 0x3c, + 0x7d, 0x51, 0xce, 0xff, 0x50, 0xb3, 0x19, 0x60, 0x50, 0xbd, 0x7b, 0x59, + 0x40, 0x1b, 0x73, 0x2d, 0x39, 0xa2, 0xd5, 0xd5, 0x0f, 0x9d, 0xd8, 0x9d, + 0x37, 0xb8, 0x87, 0xc5, 0x28, 0x11, 0x2c, 0x19, 0x97, 0x15, 0x53, 0xfb, + 0x81, 0x46, 0x89, 0x93, 0xf4, 0x58, 0xc0, 0xc4, 0x34, 0xf3, 0x34, 0xa7, + 0x7e, 0xec, 0x57, 0x09, 0x83, 0xf2, 0x6d, 0x2f, 0x54, 0xf6, 0x0b, 0x99, + 0xe1, 0xac, 0x5d, 0x4e, 0xde, 0xdb, 0x15, 0x46, 0x05, 0x18, 0xb4, 0x2d, + 0x4d, 0xca, 0x18, 0xbb, 0x66, 0xa3, 0x49, 0x3a, 0x22, 0x7b, 0x3f, 0x18, + 0x5e, 0x40, 0xe6, 0xd2, 0x42, 0xa1, 0x8e, 0x38, 0x52, 0xb7, 0xc3, 0x53, + 0x33, 0x92, 0xc7, 0xb1, 0x08, 0x61, 0x65, 0x8c, 0xa2, 0xd7, 0x28, 0xb6, + 0x7d, 0xd2, 0xd1, 0xf0, 0xc6, 0xd8, 0x05, 0x74, 0x07, 0xa9, 0x61, 0x18, + 0x7a, 0xb5, 0x03, 0x1d, 0x4d, 0xf9, 0x8a, 0xa9, 0x47, 0xfd, 0x3e, 0xf3, + 0xa0, 0x89, 0x22, 0xf9, 0x8c, 0xe8, 0x3a, 0xdd, 0xb4, 0xca, 0x12, 0x85, + 0x4c, 0xb9, 0x0d, 0x37, 0x0a, 0xe2, 0xe2, 0x2e, 0xa5, 0x93, 0xb4, 0x9a, + 0x16, 0xfc, 0x53, 0x48, 0xed, 0xbc, 0x49, 0xa2, 0x78, 0x3b, 0x90, 0xdf, + 0xa5, 0xdf, 0xc1, 0xba, 0x30, 0x96, 0x5b, 0x97, 0x7a, 0x84, 0x80, 0xa9, + 0xea, 0x99, 0x19, 0xd9, 0x4d, 0x74, 0x0a, 0x63, 0x3d, 0x1c, 0xc9, 0xf8, + 0x66, 0x02, 0x7a, 0xae, 0xa8, 0xdb, 0x8c, 0xc4, 0x39, 0x92, 0x6c, 0xc3, + 0xcc, 0xdb, 0x2d, 0xa3, 0x4d, 0x21, 0xca, 0xee, 0xd4, 0x20, 0x80, 0xd7, + 0xb6, 0x54, 0x10, 0xba, 0x6e, 0x55, 0xe1, 0x02, 0xe0, 0x63, 0x54, 0x3e, + 0xf7, 0xd4, 0x28, 0x7a, 0x54, 0x61, 0xda, 0x2c, 0x87, 0x31, 0x15, 0x0d, + 0xfe, 0x4e, 0x53, 0xf0, 0x20, 0x98, 0xf0, 0x8d, 0x3b, 0x46, 0x19, 0x41, + 0x59, 0xa8, 0x64, 0xac, 0x81, 0x29, 0x62, 0x01, 0xae, 0xd1, 0x4f, 0x02, + 0xef, 0x4c, 0x34, 0xb6, 0xb1, 0xcc, 0x11, 0x98, 0x49, 0xdc, 0xf3, 0xa9, + 0xd4, 0xfa, 0xfc, 0x7f, 0x15, 0xed, 0xd3, 0xea, 0x6d, 0x90, 0x92, 0xb5, + 0x26, 0x6a, 0x34, 0xc7, 0xdd, 0x69, 0x17, 0x04, 0xa6, 0xc3, 0x91, 0x6b, + 0xaf, 0xb7, 0x5a, 0xca, 0x6b, 0x9a, 0x62, 0x0d, 0xfe, 0x2c, 0x2c, 0x82, + 0x6d, 0xc0, 0x5f, 0x15, 0x0e, 0x9f, 0xd6, 0x57, 0x12, 0x83, 0x85, 0x2d, + 0x6a, 0x07, 0x71, 0xba, 0xbc, 0x5a, 0x62, 0x7c, 0x40, 0xca, 0x61, 0x99, + 0x5c, 0x28, 0x23, 0x9d, 0x3b, 0x29, 0x7f, 0x96, 0x2c, 0x9c, 0xcb, 0x9f, + 0xee, 0x06, 0x63, 0xfb, 0xb1, 0x33, 0xc2, 0xec, 0x9d, 0xdd, 0xcb, 0xb1, + 0xd6, 0xd8, 0x8c, 0x35, 0xb4, 0x2b, 0xf9, 0x03, 0x1d, 0x1f, 0xad, 0xf6, + 0x74, 0xba, 0x6b, 0xa8, 0xbb, 0xf3, 0x00, 0x43, 0x2a, 0xa5, 0x16, 0x6b, + 0x3b, 0xb5, 0x3f, 0xac, 0x5e, 0x0c, 0x70, 0x96, 0x80, 0x23, 0x79, 0x15, + 0x6a, 0x87, 0x72, 0xe7, 0xee, 0x57, 0x18, 0x65, 0x91, 0xf6, 0xd0, 0x43, + 0x5a, 0x00, 0xc7, 0xf2, 0xdf, 0xe4, 0xcb, 0xea, 0x20, 0x7d, 0x50, 0xa4, + 0x3b, 0xfa, 0x85, 0xdb, 0x45, 0xe5, 0xd9, 0xb6, 0x48, 0x54, 0x4a, 0xab, + 0x67, 0xf2, 0x68, 0x50, 0x98, 0xb6, 0xdf, 0xa6, 0xbb, 0x3c, 0x39, 0x7b, + 0x09, 0x2c, 0x9d, 0x60, 0xbc, 0x1d, 0xb1, 0x8d, 0x10, 0x39, 0x54, 0x8d, + 0xb2, 0xf1, 0x48, 0xce, 0x54, 0xf6, 0x48, 0x55, 0xab, 0xeb, 0x81, 0x87, + 0xc4, 0x3c, 0xaf, 0x47, 0x1d, 0xb6, 0xd0, 0x08, 0xba, 0x13, 0x46, 0xb2, + 0x50, 0xbd, 0xe2, 0xe5, 0xae, 0xb9, 0x79, 0x47, 0xed, 0x3d, 0x9b, 0x0e, + 0x17, 0x40, 0xab, 0x7c, 0x08, 0xc1, 0x6b, 0x57, 0xa0, 0xba, 0x9b, 0x48, + 0xa7, 0xd2, 0xfa, 0xc3, 0x35, 0xce, 0xbd, 0xc5, 0x77, 0x2b, 0xbb, 0x91, + 0x1a, 0x8a, 0x4a, 0xe8, 0x3f, 0x05, 0xef, 0xf4, 0x6b, 0x2e, 0xac, 0xd9, + 0xc8, 0x9e, 0x30, 0xb2, 0x4b, 0xea, 0x8e, 0x61, 0x98, 0x4f, 0x93, 0x7c, + 0x1d, 0xa6, 0xa0, 0xc2, 0x77, 0xd9, 0xe8, 0xfc, 0x8f, 0x6c, 0xb7, 0xed, + 0xd4, 0x70, 0x26, 0xf5, 0x61, 0x3a, 0xd4, 0x09, 0x07, 0x42, 0x41, 0x10, + 0x4f, 0x4b, 0x2f, 0xc7, 0xec, 0x4f, 0xe4, 0xdc, 0xa6, 0xdb, 0xb3, 0xcc, + 0xfc, 0x0f, 0x3d, 0xba, 0x03, 0x91, 0x3d, 0xac, 0x69, 0xfa, 0x7e, 0x4f, + 0xeb, 0x23, 0x5c, 0x5a, 0x55, 0x7e, 0xaa, 0x36, 0xd6, 0xc9, 0x9d, 0x24, + 0x1f, 0x21, 0x61, 0xe5, 0xfb, 0xde, 0x89, 0x8c, 0x38, 0xb1, 0x86, 0x9a, + 0x88, 0x49, 0x53, 0xcf, 0x14, 0xdf, 0x92, 0x0a, 0xd3, 0x42, 0x67, 0x11, + 0x9b, 0x1c, 0xa8, 0x41, 0x68, 0xda, 0x77, 0x71, 0xb5, 0x6d, 0x47, 0x5f, + 0x25, 0xa1, 0x50, 0xb4, 0xb0, 0x4a, 0x0d, 0x2f, 0x2b, 0x8a, 0x4a, 0x5f, + 0x06, 0x6c, 0x6f, 0x4e, 0x8c, 0x44, 0x69, 0x5e, 0x3f, 0xed, 0x4a, 0x5c, + 0x88, 0x5d, 0x0f, 0x48, 0xea, 0x53, 0x9b, 0x63, 0x2e, 0xf3, 0x78, 0x3e, + 0xa7, 0xb0, 0x12, 0xcd, 0x64, 0xfb, 0x88, 0x24, 0x37, 0x8c, 0xb0, 0x57, + 0x52, 0x20, 0xe2, 0x3e, 0xf8, 0x68, 0x98, 0x6c, 0x40, 0x56, 0x76, 0xfb, + 0xd1, 0xde, 0x18, 0x24, 0xf4, 0x21, 0x65, 0x68, 0xe9, 0x49, 0x0d, 0xdc, + 0xa9, 0x96, 0xe8, 0x1a, 0x48, 0xc0, 0x28, 0x84, 0x56, 0x89, 0x2c, 0xf6, + 0xe3, 0xb7, 0x35, 0xc2, 0xf0, 0x66, 0x78, 0x2c, 0x0e, 0xb5, 0xac, 0xe1, + 0x51, 0xac, 0x78, 0x97, 0xd9, 0xb7, 0xfb, 0x31, 0x0e, 0xee, 0x12, 0xa4, + 0x41, 0x67, 0x28, 0xdf, 0x78, 0xb4, 0x71, 0x10, 0x8b, 0xe6, 0x35, 0x05, + 0x04, 0x57, 0xde, 0xdd, 0x60, 0x9d, 0xaa, 0xd6, 0x07, 0x93, 0x96, 0x96, + 0x1b, 0xf9, 0xe2, 0xf2, 0x23, 0x3e, 0x68, 0x82, 0x99, 0x65, 0x3a, 0x51, + 0x91, 0x51, 0x46, 0x58, 0x4a, 0xf4, 0xd3, 0xa0, 0xc3, 0x21, 0xfd, 0x07, + 0x43, 0x33, 0x03, 0xaa, 0x3f, 0x0c, 0x16, 0x02, 0xb5, 0x2c, 0x8e, 0xe8, + 0xa0, 0x7e, 0x22, 0xd2, 0x0b, 0x80, 0x64, 0xe7, 0x11, 0x6c, 0xfa, 0x20, + 0x17, 0x1c, 0x7e, 0x20, 0x91, 0x17, 0x5e, 0x66, 0xa3, 0xc8, 0x05, 0x66, + 0x5c, 0x96, 0x81, 0xac, 0xc3, 0x54, 0x4c, 0x05, 0xe5, 0x65, 0x21, 0x5a, + 0x87, 0xc0, 0x50, 0x08, 0xaa, 0x9a, 0x4a, 0x22, 0x7b, 0x3b, 0x6c, 0x67, + 0x21, 0xa9, 0x22, 0xef, 0xeb, 0x5c, 0x6d, 0x97, 0xf3, 0x5b, 0x7e, 0x07, + 0xe7, 0x74, 0x1d, 0xd8, 0xf5, 0x14, 0x09, 0x71, 0xa3, 0xab, 0x9a, 0x91, + 0x18, 0xc3, 0x03, 0x03, 0xa2, 0x9d, 0xa9, 0x1a, 0x44, 0x93, 0x50, 0x95, + 0xd0, 0xca, 0x5a, 0xfe, 0xa7, 0x40, 0xf5, 0xae, 0xcc, 0x88, 0x5a, 0xa5, + 0xfe, 0xe4, 0x9c, 0x5a, 0x28, 0x26, 0xe6, 0xb4, 0xb1, 0xa8, 0x80, 0xa2, + 0xe1, 0xbb, 0xe5, 0xa4, 0x2d, 0x7d, 0x9a, 0xfd, 0xcc, 0x66, 0x30, 0xf8, + 0xf5, 0x9a, 0x16, 0xa7, 0x4f, 0xdf, 0x7b, 0x39, 0xdb, 0x8d, 0x21, 0xdd, + 0x05, 0xc7, 0xd4, 0x4c, 0xaa, 0xce, 0x9a, 0xd0, 0x2c, 0xd6, 0x7d, 0xf1, + 0x3b, 0x23, 0x20, 0x50, 0xcf, 0x4a, 0xd9, 0xc5, 0x99, 0xb6, 0xe9, 0xb8, + 0x4d, 0x44, 0x0f, 0x46, 0xe0, 0xe4, 0x11, 0xbb, 0x22, 0xdf, 0x9d, 0xc0, + 0x33, 0x38, 0xaf, 0x0f, 0x3d, 0xf4, 0xe0, 0x4b, 0x49, 0x7f, 0xdf, 0x61, + 0x64, 0x7e, 0x84, 0x7e, 0x07, 0xe1, 0x7a, 0x00, 0x55, 0xf2, 0x80, 0xa9, + 0x2e, 0x29, 0x78, 0x2d, 0xe9, 0x0f, 0x31, 0x21, 0xd8, 0x7b, 0xc0, 0x9c, + 0x74, 0xec, 0x6e, 0xaa, 0xf6, 0x04, 0xb7, 0x67, 0x97, 0xfb, 0x21, 0xc6, + 0x94, 0xcc, 0xdb, 0xe2, 0x9b, 0x59, 0x98, 0x84, 0x6d, 0xc6, 0xac, 0xdb, + 0xc0, 0xed, 0x48, 0x98, 0xc0, 0x7d, 0xa3, 0xe3, 0x96, 0xa6, 0x15, 0xc9, + 0x33, 0x63, 0x28, 0xf4, 0x30, 0x47, 0x75, 0x50, 0xf5, 0xeb, 0x82, 0xd3, + 0xd3, 0xa6, 0x5a, 0xf0, 0x18, 0xd6, 0x19, 0x56, 0xb1, 0x5d, 0x2a, 0x71, + 0x90, 0x41, 0xef, 0xa1, 0x97, 0x97, 0xec, 0x6d, 0x46, 0x79, 0x2c, 0xf0, + 0xc5, 0xe2, 0x81, 0xa1, 0xea, 0xa5, 0x51, 0x06, 0xb0, 0x60, 0x06, 0x34, + 0x82, 0x54, 0x50, 0x4f, 0xa4, 0x0b, 0x3b, 0x6e, 0xf0, 0xe0, 0x4a, 0x90, + 0x48, 0x73, 0x69, 0xe4, 0xb7, 0x0b, 0xdc, 0xa5, 0xd9, 0xf7, 0x4c, 0x7c, + 0x7e, 0xe9, 0x35, 0x97, 0x2a, 0x65, 0x38, 0xee, 0xec, 0x68, 0xa4, 0x17, + 0x1f, 0xa7, 0x95, 0xc8, 0xfd, 0x25, 0x7d, 0x1d, 0xf0, 0xb7, 0x31, 0x5f, + 0xe5, 0xc4, 0x58, 0x61, 0x69, 0x45, 0xac, 0xb9, 0x65, 0xd0, 0xb2, 0x6d, + 0xd9, 0x69, 0xdc, 0x29, 0x17, 0xaf, 0xf0, 0x10, 0x4f, 0xe6, 0xa3, 0x8a, + 0x74, 0x02, 0xd0, 0x1b, 0xbd, 0x33, 0xe2, 0x59, 0xbf, 0x36, 0xca, 0x32, + 0x5c, 0xc4, 0x96, 0x0e, 0x65, 0x3d, 0xc3, 0xbc, 0xea, 0x7a, 0x6c, 0x6b, + 0x6e, 0x82, 0xdf, 0x20, 0x9b, 0x73, 0x2f, 0x24, 0x56, 0x22, 0x47, 0xdb, + 0xba, 0x6d, 0x7b, 0xbc, 0x26, 0x14, 0xfa, 0x16, 0x80, 0xf6, 0x6c, 0x06, + 0x1f, 0x3c, 0x95, 0xfe, 0x6b, 0xe6, 0xd8, 0x12, 0xf0, 0x11, 0x9f, 0xca, + 0x5c, 0xd7, 0x85, 0x14, 0x97, 0x4f, 0xf2, 0x47, 0xd0, 0x30, 0x2e, 0x28, + 0x4d, 0x32, 0xce, 0x74, 0x0d, 0x10, 0xe4, 0xd4, 0x59, 0x5d, 0xa6, 0xc3, + 0x8d, 0x0e, 0x37, 0x27, 0xa7, 0x83, 0x1c, 0x7a, 0xb7, 0x32, 0x1a, 0xf9, + 0x34, 0x2c, 0x26, 0xe9, 0x8b, 0x95, 0xcd, 0x2f, 0x87, 0xa9, 0xcc, 0xb2, + 0x3d, 0x40, 0x96, 0x52, 0x0e, 0x42, 0x07, 0xf7, 0x8d, 0x02, 0x22, 0x7f, + 0x86, 0x79, 0x6f, 0x8c, 0xe8, 0x1f, 0x5e, 0x6c, 0x1a, 0xb2, 0x5b, 0x66, + 0x71, 0x10, 0x3b, 0xac, 0x86, 0x62, 0x09, 0x65, 0x36, 0x52, 0x9a, 0x80, + 0x9d, 0x80, 0x29, 0x4d, 0xf3, 0x0b, 0x47, 0xfb, 0x7c, 0x4b, 0x7e, 0xaf, + 0x39, 0x65, 0x79, 0x9f, 0x33, 0x78, 0xaa, 0x34, 0x65, 0x3f, 0x88, 0x68, + 0x89, 0x31, 0xcd, 0x94, 0xd7, 0x4e, 0xdf, 0xe0, 0x1c, 0x14, 0x8b, 0xa5, + 0xd0, 0x77, 0x0a, 0x46, 0xc0, 0x8e, 0xae, 0x54, 0xa1, 0xa7, 0xdb, 0xfb, + 0x79, 0x9a, 0x14, 0x13, 0x82, 0x76, 0xa3, 0x2c, 0xfc, 0xd4, 0x76, 0x81, + 0x8c, 0x46, 0x1a, 0x97, 0x22, 0x59, 0x42, 0x63, 0x2e, 0x46, 0xdb, 0xfe, + 0x24, 0x36, 0x7b, 0x8d, 0x23, 0x6a, 0x77, 0x83, 0xd8, 0x72, 0xb4, 0x8c, + 0x68, 0x59, 0x87, 0x73, 0xc4, 0xca, 0x24, 0x1f, 0x9b, 0x98, 0xcc, 0x19, + 0xab, 0xc2, 0xa3, 0x84, 0x07, 0x90, 0x2e, 0x45, 0x67, 0x52, 0x50, 0x6e, + 0xed, 0x7e, 0xdd, 0x08, 0x49, 0xcf, 0x9f, 0xfe, 0x1b, 0x3b, 0xa8, 0x8b, + 0xd1, 0x37, 0x4b, 0x4e, 0xda, 0x4e, 0xbc, 0xf9, 0x8d, 0xed, 0x76, 0x96, + 0x72, 0x3e, 0xce, 0x16, 0x2d, 0xcb, 0x20, 0x3e, 0x1c, 0xcc, 0x8f, 0xa5, + 0x1b, 0xd5, 0x78, 0x61, 0x74, 0x49, 0x51, 0x22, 0xf8, 0x78, 0xc2, 0x37, + 0x7d, 0x40, 0x1d, 0xd4, 0x3f, 0xcb, 0x70, 0x95, 0x8b, 0x15, 0xac, 0x00, + 0x2c, 0x44, 0xfd, 0x67, 0xa6, 0x41, 0x63, 0x28, 0x9c, 0x85, 0x99, 0x05, + 0x18, 0xb2, 0x2f, 0x2c, 0xb3, 0xe8, 0x8d, 0xf8, 0x91, 0x22, 0x32, 0x94, + 0xe4, 0x7f, 0xac, 0xb0, 0xf9, 0x6f, 0x5d, 0xa9, 0xda, 0xa2, 0x2f, 0xc4, + 0x36, 0x6c, 0x2c, 0x6f, 0xa5, 0x1d, 0xb5, 0x74, 0x0e, 0x70, 0x09, 0x04, + 0xcb, 0xc2, 0xa4, 0x91, 0x6f, 0x91, 0x8b, 0x4f, 0x99, 0x53, 0xe3, 0x6a, + 0x5e, 0x0f, 0x4d, 0x00, 0xd0, 0x90, 0x8a, 0x00, 0xfb, 0xc7, 0x26, 0x9c, + 0xa5, 0xab, 0x07, 0xd8, 0x09, 0xaf, 0x88, 0xd8, 0x27, 0x22, 0x41, 0x3b, + 0x2b, 0x0f, 0xfa, 0x56, 0x3e, 0x4b, 0xb1, 0x9c, 0x5b, 0xb9, 0xe6, 0xe0, + 0x5f, 0x71, 0x5c, 0x17, 0x6f, 0x37, 0xee, 0x73, 0x9a, 0xe0, 0x43, 0xf6, + 0x40, 0xcd, 0x6d, 0x6d, 0x4f, 0x6d, 0x81, 0xfe, 0x59, 0x51, 0x8e, 0xcc, + 0x58, 0x08, 0x2e, 0x42, 0x50, 0x78, 0x91, 0x6a, 0x7a, 0xec, 0x85, 0xcf, + 0x9d, 0x25, 0x8e, 0xdc, 0x4f, 0x6d, 0xc2, 0x70, 0x83, 0x77, 0xe1, 0x2a, + 0xe4, 0x09, 0x5d, 0xa4, 0x93, 0xcf, 0xcb, 0x65, 0x2c, 0xd1, 0x6f, 0xa3, + 0x55, 0x13, 0x9e, 0x2c, 0xea, 0xbc, 0x0a, 0x79, 0xbd, 0xfb, 0xd0, 0x81, + 0x49, 0x21, 0x64, 0x5a, 0xad, 0xb7, 0x8f, 0x93, 0xd7, 0xe2, 0x50, 0xc3, + 0x03, 0xe3, 0x42, 0xbc, 0xa4, 0x67, 0x84, 0x35, 0x73, 0xca, 0x9f, 0x9a, + 0xce, 0x1e, 0xe1, 0xfe, 0xa5, 0xf4, 0x59, 0xdd, 0xba, 0x08, 0x94, 0x66, + 0x53, 0xb5, 0xd1, 0xc0, 0x27, 0xd7, 0xf1, 0x50, 0x4d, 0x0f, 0x5d, 0xce, + 0x31, 0x9c, 0x10, 0xc8, 0x29, 0x8e, 0x5c, 0x8d, 0x78, 0xc4, 0xad, 0xf6, + 0xbe, 0x5c, 0x71, 0xce, 0xd1, 0xde, 0x90, 0x0c, 0xb2, 0x34, 0x51, 0x01, + 0xbe, 0xe2, 0x24, 0x0f, 0xe0, 0x27, 0x81, 0x13, 0x25, 0xe0, 0x6c, 0x72, + 0x6f, 0x97, 0x06, 0xa8, 0x1a, 0x7d, 0x0b, 0xa5, 0xa6, 0x1c, 0x97, 0xe5, + 0xff, 0x24, 0xc5, 0xca, 0x90, 0xbd, 0x90, 0x7e, 0x4c, 0xb7, 0xcf, 0x6d, + 0x8d, 0x31, 0xe2, 0x4f, 0xd2, 0xc7, 0xb1, 0x5f, 0x7e, 0x1d, 0xb4, 0xd3, + 0x5d, 0x00, 0x1d, 0x68, 0xfb, 0x8d, 0x65, 0xa4, 0x9f, 0x60, 0x47, 0x42, + 0xfe, 0x75, 0x67, 0xb8, 0xb3, 0xe1, 0x4e, 0xa5, 0x04, 0x7c, 0xcc, 0xaa, + 0x2e, 0xf8, 0x89, 0x3f, 0x32, 0x91, 0x18, 0xee, 0x77, 0x62, 0x39, 0xd3, + 0x9a, 0x1e, 0x83, 0x0f, 0xa2, 0x6d, 0xa4, 0x0c, 0x8a, 0xde, 0x4a, 0xf3, + 0x28, 0x19, 0x54, 0x26, 0x1f, 0x86, 0x83, 0x5b, 0xc3, 0xed, 0x3f, 0x57, + 0xab, 0x17, 0x54, 0xab, 0xcf, 0xfd, 0x81, 0x66, 0xe4, 0x28, 0x7e, 0x18, + 0x0e, 0xda, 0x9b, 0xb0, 0x64, 0xcc, 0x85, 0xbd, 0x52, 0xdd, 0x28, 0x73, + 0x64, 0x43, 0x64, 0xe6, 0x1c, 0xdf, 0x2e, 0xae, 0xee, 0xd6, 0x50, 0x92, + 0x08, 0xdf, 0xf8, 0x59, 0xd8, 0xee, 0xec, 0x2f, 0x10, 0x57, 0x8c, 0xa8, + 0xf9, 0xbc, 0x12, 0xdb, 0x0d, 0x1d, 0x1e, 0x49, 0xbc, 0x3c, 0x3b, 0x16, + 0x41, 0xc9, 0x6f, 0x99, 0xa4, 0x57, 0x6b, 0xd1, 0xfb, 0x0b, 0xca, 0xdc, + 0x74, 0x54, 0xb2, 0x7d, 0x68, 0x0b, 0xab, 0xc4, 0x0d, 0x5a, 0xf1, 0x3a, + 0xca, 0xa9, 0x60, 0x7f, 0x62, 0xec, 0x80, 0xed, 0xc4, 0x62, 0xe8, 0xf6, + 0x1c, 0xd1, 0x39, 0x33, 0x2a, 0x24, 0xa7, 0x76, 0x45, 0x1a, 0x30, 0xa9, + 0x07, 0x17, 0x6c, 0xb4, 0x74, 0x57, 0x9c, 0x3c, 0x76, 0x00, 0x8c, 0xfc, + 0xb8, 0x4d, 0x8c, 0x5a, 0x2c, 0x55, 0x9d, 0xcc, 0x04, 0x3d, 0x40, 0x76, + 0xbb, 0x12, 0x83, 0x54, 0x02, 0xb2, 0x87, 0x44, 0x06, 0x11, 0x29, 0xcd, + 0x1b, 0x98, 0x52, 0xf1, 0x72, 0x8d, 0xad, 0xbf, 0xfb, 0x12, 0xc2, 0x89, + 0x93, 0xc8, 0xb5, 0x01, 0xe4, 0xeb, 0x35, 0x3b, 0xde, 0x7f, 0x71, 0x37, + 0xc8, 0xc1, 0xe9, 0x17, 0x76, 0x3c, 0x24, 0xb1, 0x5d, 0x88, 0x28, 0xef, + 0x30, 0xd8, 0xac, 0x69, 0xf8, 0xc6, 0x89, 0xc8, 0xc0, 0xd0, 0x35, 0xf9, + 0x97, 0xa8, 0x71, 0x2e, 0x84, 0x14, 0x26, 0x74, 0x09, 0x31, 0xcd, 0x94, + 0xdb, 0xb7, 0x33, 0xb3, 0xf0, 0x06, 0x3d, 0xff, 0x25, 0xf5, 0xfd, 0x8a, + 0x2b, 0x04, 0x29, 0x58, 0xdd, 0xf0, 0x54, 0x3d, 0xbe, 0x0a, 0x65, 0x29, + 0x1f, 0xf4, 0x06, 0xe4, 0x50, 0x41, 0x4c, 0x93, 0x62, 0x84, 0xbc, 0x85, + 0xe5, 0x8d, 0x40, 0x62, 0x9d, 0x2f, 0xbd, 0xa0, 0xbf, 0xad, 0xf8, 0x53, + 0x3b, 0x35, 0xbf, 0x37, 0x53, 0xc2, 0xa2, 0xf4, 0xe0, 0x22, 0xf6, 0x8d, + 0x7b, 0x15, 0xa9, 0xf8, 0x87, 0xf4, 0x23, 0xc4, 0x4d, 0xf7, 0x6a, 0x24, + 0xa8, 0xcb, 0x6a, 0xb7, 0x11, 0xbb, 0xcb, 0xab, 0x78, 0x08, 0x32, 0x95, + 0xa7, 0xb3, 0x44, 0xc6, 0x4d, 0x9c, 0xa0, 0x64, 0x21, 0x0e, 0x0b, 0x60, + 0x48, 0xd9, 0x26, 0x56, 0x1c, 0x05, 0xa8, 0xf5, 0x79, 0xb6, 0xab, 0xc5, + 0x70, 0xa9, 0xc4, 0x0c, 0x12, 0xf7, 0xcf, 0x05, 0x1d, 0x4e, 0x3e, 0xa5, + 0x08, 0xb0, 0xdc, 0xd6, 0x46, 0x3e, 0xed, 0x1b, 0xef, 0x4e, 0x5b, 0x91, + 0x13, 0x9b, 0x57, 0x67, 0x6f, 0xf2, 0x90, 0x75, 0xce, 0xc8, 0x15, 0xb3, + 0x8a, 0xcb, 0x15, 0xa2, 0x18, 0xa3, 0x79, 0xc0, 0xb1, 0x01, 0x57, 0x79, + 0x77, 0x00, 0x7b, 0x80, 0x1d, 0xc9, 0x9e, 0xa5, 0x61, 0x88, 0x6b, 0x0b, + 0x18, 0x75, 0x08, 0xfe, 0x3c, 0xb3, 0xde, 0xaa, 0x7e, 0x94, 0xeb, 0xb1, + 0x3d, 0x45, 0x17, 0xc7, 0xd5, 0xff, 0xca, 0xa6, 0x8c, 0xc8, 0xaf, 0x4d, + 0xbf, 0x0c, 0xe3, 0x24, 0x54, 0x27, 0x74, 0x11, 0x73, 0xfd, 0x39, 0xd2, + 0xc0, 0x7f, 0x48, 0x78, 0xd9, 0x29, 0xea, 0x47, 0x80, 0x7b, 0x0a, 0x63, + 0x6d, 0x4e, 0x34, 0x28, 0x17, 0xe2, 0xb7, 0x50, 0x2a, 0xac, 0xe0, 0xce, + 0x34, 0xba, 0x95, 0xa2, 0xb0, 0x0c, 0x2f, 0xc8, 0xb1, 0x36, 0x21, 0x77, + 0xeb, 0xa7, 0x9c, 0xa1, 0xea, 0xd8, 0x15, 0xae, 0x16, 0x74, 0x32, 0x06, + 0x27, 0xc2, 0xb9, 0x86, 0x9d, 0x58, 0x9d, 0x25, 0x42, 0x67, 0x81, 0x64, + 0xd6, 0xec, 0x5e, 0xe6, 0x33, 0x58, 0xd0, 0x03, 0x99, 0x05, 0x1b, 0xc6, + 0x77, 0x48, 0x28, 0x6d, 0xa1, 0xcf, 0x5a, 0x59, 0x3b, 0x47, 0xc3, 0xc0, + 0x68, 0x56, 0x96, 0x6b, 0xe6, 0xff, 0xfd, 0xd3, 0xb6, 0x23, 0x9d, 0x51, + 0x8c, 0xbd, 0xbd, 0x39, 0xa6, 0xff, 0xa2, 0x2e, 0x18, 0x98, 0x46, 0x7c, + 0x4a, 0xe0, 0xef, 0xe0, 0xf8, 0x12, 0x70, 0x3a, 0x87, 0x16, 0xa6, 0x36, + 0xb7, 0xca, 0xa4, 0xfb, 0xc6, 0x96, 0x0c, 0xb7, 0x86, 0x8a, 0x95, 0xd2, + 0x52, 0xc6, 0x6b, 0x0b, 0x7e, 0x8a, 0x85, 0x0a, 0xb8, 0xdd, 0xd9, 0xbc, + 0x07, 0x7e, 0x0e, 0x7a, 0x85, 0x3e, 0x28, 0xaa, 0x4e, 0x4d, 0x47, 0x33, + 0xa4, 0x76, 0xe2, 0xd1, 0x45, 0x5e, 0x16, 0x7f, 0x5d, 0xd1, 0x47, 0xba, + 0x88, 0x0c, 0x0c, 0x54, 0xc9, 0x15, 0xe6, 0x2e, 0xd5, 0x26, 0x6f, 0xe8, + 0x37, 0xe1, 0xdc, 0xe2, 0xc5, 0x96, 0x60, 0xd5, 0xff, 0x2a, 0x4b, 0x8e, + 0x5f, 0x95, 0x44, 0x46, 0xe3, 0x62, 0x77, 0xf9, 0x25, 0x9f, 0x26, 0x72, + 0xf1, 0x43, 0xc8, 0x7d, 0xf9, 0xcf, 0x8a, 0xab, 0x16, 0x4d, 0x8c, 0x99, + 0xc0, 0x8f, 0xd4, 0x7f, 0x59, 0x1d, 0xd6, 0x5e, 0x10, 0x12, 0x23, 0x89, + 0xa2, 0x89, 0xf6, 0xdf, 0xe2, 0x33, 0x2a, 0x8f, 0x36, 0x90, 0xc7, 0xb7, + 0xdf, 0xae, 0x1c, 0x58, 0xa7, 0x84, 0x4e, 0x09, 0xbf, 0x7d, 0xda, 0x41, + 0x96, 0xc0, 0x14, 0xa3, 0xe1, 0x8e, 0xd7, 0x80, 0x1e, 0xcd, 0x7a, 0xc1, + 0x8a, 0x41, 0x95, 0xed, 0x36, 0xb1, 0xfc, 0x2b, 0x19, 0xd6, 0x83, 0xe0, + 0xa8, 0x2d, 0x89, 0xb1, 0x04, 0x42, 0xd0, 0x7b, 0xdb, 0x04, 0x8d, 0xcd, + 0xd0, 0xc7, 0xb5, 0x4d, 0x1c, 0x11, 0xd2, 0xbd, 0x57, 0x21, 0xf1, 0x86, + 0xa5, 0x0c, 0x90, 0x02, 0xe2, 0x60, 0x6c, 0x27, 0xf1, 0x88, 0x3d, 0x3d, + 0xba, 0x93, 0xa6, 0x67, 0x8a, 0x13, 0x96, 0x0f, 0x69, 0x29, 0xd7, 0x42, + 0x53, 0x58, 0xae, 0x5c, 0x9c, 0xab, 0x3c, 0xd9, 0x4a, 0x3f, 0x21, 0xc9, + 0x72, 0xee, 0x21, 0x07, 0xfc, 0x50, 0x2a, 0xea, 0x07, 0x47, 0xe2, 0xd3, + 0xf8, 0xf2, 0x1d, 0xd6, 0xec, 0xe1, 0x43, 0xb5, 0xba, 0xc8, 0xe3, 0x97, + 0xa3, 0x70, 0x91, 0xef, 0xfd, 0x0e, 0xf3, 0x7e, 0x1f, 0xe0, 0xc4, 0x24, + 0x77, 0x91, 0x50, 0x39, 0xac, 0x42, 0x2f, 0xff, 0xaa, 0x7a, 0x97, 0x64, + 0xe6, 0x79, 0x00, 0x2b, 0x60, 0x1a, 0x75, 0x9a, 0x7a, 0x4e, 0x1a, 0x55, + 0x54, 0x94, 0xb8, 0x44, 0x58, 0x73, 0x28, 0x80, 0xed, 0x50, 0xa6, 0x10, + 0x16, 0x2d, 0x47, 0x7f, 0x33, 0x03, 0xdc, 0x3b, 0x6f, 0xc1, 0x96, 0xec, + 0xd4, 0x29, 0x67, 0xde, 0xa4, 0x48, 0x17, 0x8c, 0xcb, 0x71, 0x5d, 0x59, + 0x75, 0xa9, 0x48, 0x6a, 0x6d, 0x31, 0x43, 0x9f, 0x39, 0x53, 0x60, 0xee, + 0x67, 0x4b, 0xed, 0x28, 0xfc, 0xd9, 0x46, 0x5f, 0x6f, 0x59, 0x4c, 0x45, + 0x64, 0x89, 0x29, 0x4a, 0x77, 0x98, 0x47, 0xdf, 0xb1, 0x06, 0x1e, 0x9a, + 0x45, 0x39, 0x30, 0xc8, 0x1a, 0xec, 0xdf, 0xbb, 0x06, 0x81, 0xdc, 0xa7, + 0xa0, 0x9f, 0x1d, 0xe2, 0xc3, 0x52, 0x98, 0x85, 0xfa, 0x09, 0x91, 0x25, + 0x56, 0x08, 0x45, 0xda, 0xff, 0x8c, 0x20, 0xa6, 0xed, 0x75, 0x1d, 0x21, + 0x41, 0x43, 0x87, 0x2f, 0xc5, 0xe0, 0xa2, 0x70, 0xb8, 0x93, 0xf7, 0x2d, + 0xe7, 0xd2, 0x64, 0xb4, 0xe9, 0xbb, 0xfd, 0xf0, 0xde, 0xab, 0x0d, 0x05, + 0x84, 0xed, 0x4e, 0x03, 0x4c, 0x46, 0x43, 0x97, 0xfc, 0x3d, 0x69, 0xd0, + 0x23, 0xc8, 0xc4, 0xd3, 0x4d, 0xbd, 0x35, 0x05, 0xc7, 0xa4, 0x5a, 0x24, + 0x9e, 0x9c, 0x51, 0x0e, 0x27, 0x68, 0xfb, 0x97, 0xe0, 0xfe, 0x91, 0x0e, + 0xab, 0x16, 0x96, 0xed, 0x1e, 0xde, 0xb1, 0x25, 0x93, 0xdf, 0xd9, 0xa3, + 0x60, 0x4e, 0xeb, 0x64, 0x4d, 0x25, 0xa2, 0x38, 0x35, 0x3a, 0xc3, 0x79, + 0x87, 0xc2, 0x89, 0x7b, 0x2d, 0x8b, 0x74, 0x69, 0x47, 0xd7, 0x78, 0x8d, + 0xe5, 0x23, 0x08, 0x64, 0x78, 0x49, 0xf9, 0x13, 0x8a, 0xa7, 0x64, 0x9f, + 0x61, 0x0d, 0xa4, 0x79, 0x6b, 0x87, 0xca, 0x2d, 0xd6, 0xee, 0xdd, 0x6f, + 0x82, 0x2d, 0x19, 0xc4, 0x14, 0x9e, 0x80, 0xf3, 0x78, 0xf5, 0x82, 0x88, + 0x93, 0xbf, 0xea, 0xb0, 0xb3, 0xa1, 0x4c, 0xb4, 0xd8, 0xb8, 0x1a, 0xd5, + 0x21, 0x82, 0xc0, 0xd0, 0xf8, 0x67, 0x14, 0xbb, 0xc6, 0x23, 0x60, 0x67, + 0xcf, 0x35, 0xf9, 0x9e, 0x78, 0x85, 0x44, 0x81, 0xf8, 0x51, 0xe0, 0xd1, + 0x2a, 0xe8, 0x75, 0x35, 0x96, 0xf1, 0xac, 0x2b, 0x09, 0x92, 0x30, 0x00, + 0x6c, 0x79, 0x11, 0x51, 0x41, 0x89, 0x34, 0x8f, 0x48, 0x41, 0x61, 0xaf, + 0x4b, 0x8e, 0x44, 0x82, 0x30, 0x30, 0x1b, 0x82, 0x83, 0x04, 0x4e, 0xbd, + 0x23, 0x50, 0x7f, 0xbe, 0x13, 0xdc, 0xc7, 0x80, 0x36, 0xf0, 0xad, 0x85, + 0xae, 0x68, 0xc0, 0xb0, 0x8e, 0x7d, 0x86, 0xea, 0x72, 0x32, 0xfa, 0x82, + 0x09, 0x38, 0x48, 0x8b, 0x60, 0x90, 0x22, 0x54, 0x17, 0xf1, 0x57, 0xbf, + 0xf7, 0x64, 0xc6, 0xbf, 0x5b, 0xb7, 0xa4, 0xa4, 0xb7, 0xd2, 0x2e, 0xe8, + 0xe8, 0x81, 0xda, 0x1f, 0xa7, 0x98, 0xa5, 0xf2, 0x51, 0x58, 0x52, 0xb5, + 0xa1, 0x89, 0xe7, 0x92, 0xdc, 0x76, 0x40, 0x30, 0xcd, 0xcb, 0xf6, 0x9a, + 0x44, 0x3c, 0x44, 0x9d, 0xc8, 0x77, 0xa5, 0x02, 0x8e, 0xae, 0x2a, 0xe6, + 0x7c, 0x50, 0x6a, 0xd9, 0x15, 0x15, 0x81, 0x89, 0xc5, 0x03, 0x6d, 0xe6, + 0x2d, 0x60, 0x2e, 0x48, 0x02, 0x05, 0x71, 0xf7, 0x13, 0x47, 0xd5, 0x97, + 0xda, 0x4a, 0x21, 0xe2, 0xe4, 0x5d, 0x75, 0x3b, 0x8a, 0x8d, 0x45, 0x06, + 0xb6, 0xc3, 0xf9, 0x4e, 0x0d, 0xed, 0xd3, 0x14, 0x75, 0xe9, 0xde, 0x05, + 0xb0, 0x7c, 0x90, 0x87, 0xff, 0xa7, 0x5a, 0xd7, 0x37, 0xf7, 0xd7, 0xc0, + 0xb8, 0x2c, 0x00, 0x0a, 0x4d, 0xd0, 0xdd, 0xfc, 0xd0, 0x61, 0x01, 0x3c, + 0x82, 0x40, 0x59, 0x82, 0x8e, 0xad, 0x4e, 0x63, 0xda, 0xbf, 0x45, 0x8c, + 0xd5, 0x09, 0x58, 0x8d, 0x42, 0x8c, 0x71, 0xf2, 0x0a, 0xde, 0x78, 0xd4, + 0x19, 0x84, 0x6e, 0x61, 0x85, 0xb3, 0x29, 0x03, 0xf0, 0xb1, 0x6c, 0x83, + 0x6b, 0x16, 0xe0, 0x0b, 0xcc, 0x0e, 0xf1, 0xef, 0x83, 0x8b, 0x12, 0x60, + 0xed, 0xef, 0x7a, 0xc5, 0x97, 0x0e, 0x97, 0x17, 0x90, 0xca, 0xce, 0x56, + 0xe8, 0xb2, 0xc5, 0x34, 0x68, 0x6b, 0x3f, 0xaa, 0xc4, 0x0a, 0xf3, 0x7e, + 0x31, 0x33, 0x2b, 0x7b, 0x18, 0xfc, 0x1f, 0x27, 0xb2, 0x8f, 0xf2, 0x9c, + 0x51, 0xd4, 0x8e, 0x1f, 0x2d, 0x4b, 0x3c, 0x4e, 0xf3, 0x42, 0xb6, 0xfb, + 0xbd, 0x5b, 0x35, 0x98, 0x0f, 0x91, 0x4a, 0xd6, 0x21, 0xd6, 0xdc, 0xeb, + 0xc2, 0x77, 0x02, 0x80, 0xba, 0xab, 0xcd, 0x80, 0xca, 0x85, 0xd5, 0xc4, + 0xe0, 0x79, 0x9b, 0x98, 0xed, 0xfa, 0xdd, 0x21, 0x33, 0xf2, 0x74, 0x10, + 0x21, 0x7a, 0xb2, 0xda, 0xb6, 0xbc, 0xe5, 0x09, 0x60, 0x39, 0x93, 0x0e, + 0x4a, 0x6e, 0xd2, 0x30, 0xc2, 0xef, 0x81, 0x9b, 0xde, 0xb4, 0xe3, 0x71, + 0x4f, 0x65, 0x5c, 0x6a, 0x8c, 0xd5, 0xb7, 0x00, 0x75, 0xc8, 0xfe, 0x28, + 0xb5, 0xda, 0xa3, 0x4b, 0x86, 0x0c, 0x66, 0x37, 0x57, 0xea, 0x16, 0x53, + 0xac, 0xdb, 0xd9, 0x46, 0xde, 0xee, 0xe1, 0x75, 0x07, 0x98, 0x53, 0x0d, + 0x91, 0x21, 0x7b, 0xab, 0x49, 0x83, 0x5d, 0x7d, 0x75, 0x04, 0x79, 0xf5, + 0x11, 0x5f, 0x25, 0xb0, 0x83, 0x36, 0x08, 0x0d, 0x82, 0x66, 0x6e, 0x88, + 0xbb, 0x10, 0x29, 0x9b, 0x9c, 0xb0, 0xe8, 0x2f, 0xd3, 0xd2, 0x97, 0x72, + 0x31, 0x5d, 0x80, 0x58, 0xc2, 0xc5, 0x7b, 0xc1, 0x32, 0x74, 0x10, 0x5d, + 0x63, 0x34, 0x04, 0xf3, 0xc8, 0xa8, 0xb3, 0xdf, 0xd5, 0x6d, 0x8b, 0x5c, + 0xd1, 0x76, 0x3e, 0x58, 0x41, 0xac, 0x3b, 0x8b, 0xda, 0x63, 0x43, 0xc4, + 0xbd, 0xd9, 0xf1, 0xfc, 0x0b, 0x1c, 0x0b, 0xe0, 0x44, 0x5e, 0x6c, 0x16, + 0xeb, 0xd4, 0x78, 0xa9, 0x12, 0x2f, 0x83, 0x77, 0xb2, 0xa8, 0xf2, 0xa4, + 0xe9, 0xf3, 0x83, 0x55, 0xe3, 0xab, 0x79, 0xac, 0x2e, 0xf8, 0xe2, 0x0d, + 0xea, 0x49, 0x80, 0xa5, 0x81, 0x30, 0x12, 0xe0, 0x4d, 0xbf, 0xbc, 0x7a, + 0x64, 0x60, 0xc2, 0x38, 0x83, 0x4f, 0x69, 0x50, 0x1a, 0x7f, 0xe4, 0xa4, + 0xad, 0x88, 0x76, 0x16, 0x97, 0x2b, 0x25, 0x63, 0x51, 0x8a, 0xb1, 0xe0, + 0x6b, 0x81, 0xb3, 0x40, 0x50, 0xa9, 0x86, 0x16, 0x2e, 0x7f, 0x1b, 0xab, + 0xa2, 0xed, 0x2f, 0x46, 0x99, 0x91, 0x68, 0x11, 0xe8, 0x3b, 0x73, 0x10, + 0x58, 0x2d, 0xb7, 0x05, 0x4f, 0xc4, 0xb4, 0x70, 0xe3, 0x1d, 0xb2, 0xac, + 0x9c, 0x10, 0x75, 0x18, 0xc6, 0x88, 0x9c, 0x51, 0xfd, 0xfe, 0x6d, 0x2e, + 0x41, 0xc6, 0x84, 0x10, 0xb1, 0x9d, 0x8d, 0xa4, 0xd7, 0x59, 0x57, 0x35, + 0x01, 0x73, 0x11, 0x5e, 0x37, 0x9a, 0x5f, 0x9d, 0x08, 0xc1, 0x25, 0x0a, + 0x78, 0xab, 0x24, 0x11, 0xe2, 0xe9, 0x06, 0x6f, 0xda, 0x66, 0xd3, 0x41, + 0x76, 0x39, 0xa5, 0x6a, 0x10, 0x11, 0x8a, 0x6a, 0x2d, 0xb7, 0x70, 0xb3, + 0x14, 0x5f, 0x56, 0x36, 0x84, 0x0f, 0x0b, 0xd4, 0xf3, 0x42, 0x42, 0xe4, + 0xb0, 0xb8, 0xcb, 0x2d, 0x51, 0xeb, 0x97, 0xaf, 0x89, 0x2c, 0xa2, 0xf3, + 0xc9, 0x5b, 0xb9, 0xf7, 0x59, 0x48, 0x50, 0x48, 0x7d, 0xb7, 0x09, 0xdc, + 0x6c, 0x42, 0x68, 0x21, 0x1d, 0x21, 0x87, 0x90, 0xb1, 0x77, 0x75, 0x84, + 0xdc, 0x2e, 0xeb, 0x08, 0xc9, 0x77, 0x8a, 0x51, 0x84, 0xcd, 0xaf, 0xc2, + 0x1d, 0xbc, 0x5e, 0x97, 0xdb, 0xea, 0x3b, 0x62, 0x93, 0x32, 0x1d, 0x2e, + 0xe7, 0x80, 0x5a, 0xe5, 0x49, 0xf0, 0xc2, 0xa2, 0x5c, 0x53, 0x7d, 0xc8, + 0xf1, 0x93, 0x02, 0x2f, 0x36, 0xa6, 0xdf, 0x7c, 0x3f, 0x56, 0x67, 0x48, + 0x4b, 0x20, 0x3a, 0xef, 0x61, 0xc5, 0x68, 0x67, 0xe8, 0xaa, 0x59, 0xda, + 0xe0, 0x89, 0xf8, 0xf9, 0x0d, 0x79, 0x37, 0xf2, 0xad, 0xeb, 0x72, 0x63, + 0xaa, 0xdd, 0x3c, 0x62, 0xe4, 0x80, 0x3e, 0x91, 0xc6, 0x4a, 0xbb, 0x2c, + 0x63, 0xb3, 0x06, 0x03, 0x73, 0x3a, 0x54, 0xf3, 0x1a, 0xdc, 0x66, 0x01, + 0x16, 0xb8, 0x3b, 0xd6, 0x24, 0x4e, 0x30, 0x3e, 0x7b, 0xf0, 0x4e, 0x37, + 0xb0, 0xce, 0xb9, 0xf9, 0xd4, 0xd5, 0xe5, 0xf3, 0xfb, 0x42, 0xdf, 0xf4, + 0xc5, 0x33, 0xcb, 0x5c, 0xc2, 0x68, 0x6f, 0x7d, 0x56, 0xff, 0xcf, 0x40, + 0x2b, 0xeb, 0x96, 0x8c, 0x66, 0x63, 0xa2, 0x8f, 0xa9, 0xc5, 0x13, 0xaf, + 0xa5, 0x3e, 0x98, 0x51, 0xa8, 0x3e, 0x5b, 0x69, 0xd9, 0x5b, 0x2c, 0xd2, + 0x9b, 0x03, 0x29, 0x8a, 0xf1, 0xf7, 0xae, 0xa6, 0x77, 0x03, 0xf9, 0xe0, + 0x07, 0x66, 0x0c, 0xb1, 0xf8, 0x11, 0x10, 0x0f, 0x3d, 0x69, 0xd5, 0x43, + 0xb1, 0xd2, 0xc8, 0x03, 0x47, 0x8d, 0x5d, 0x7a, 0xf5, 0xfc, 0x2e, 0xed, + 0x1f, 0xcd, 0xb7, 0x0c, 0xe9, 0x19, 0x0f, 0x63, 0x5f, 0xef, 0xaf, 0xe1, + 0x6d, 0x37, 0x74, 0xec, 0xca, 0x79, 0x6d, 0x31, 0x3c, 0x68, 0x01, 0xd9, + 0xcf, 0x76, 0xc6, 0x2f, 0x87, 0xb3, 0x12, 0xe0, 0x1e, 0x64, 0xd3, 0xe4, + 0x94, 0xf9, 0x0b, 0x49, 0x47, 0xd5, 0x36, 0x53, 0xd7, 0xfe, 0x9e, 0x20, + 0x40, 0xea, 0x56, 0x5e, 0xcc, 0x1e, 0x28, 0xc6, 0x24, 0x54, 0xec, 0xd9, + 0xbe, 0xc7, 0x14, 0x84, 0x1a, 0x9f, 0xdc, 0x6a, 0xeb, 0x8e, 0xde, 0x94, + 0xa7, 0xb6, 0x35, 0xa9, 0x93, 0xea, 0x37, 0xa0, 0x86, 0x4e, 0x5a, 0xbd, + 0xaf, 0x2c, 0x73, 0x08, 0x12, 0x76, 0x92, 0x7a, 0x1d, 0x19, 0x25, 0x96, + 0xad, 0xfe, 0xc0, 0xcc, 0x19, 0x8f, 0xc0, 0x21, 0x7b, 0xd6, 0x3e, 0xfe, + 0x76, 0xf4, 0x23, 0x7b, 0x5f, 0x0b, 0xa7, 0x55, 0xe4, 0x17, 0xbd, 0xea, + 0xbd, 0x31, 0x4d, 0x0e, 0x34, 0x01, 0xde, 0xd0, 0xe2, 0xe6, 0xf4, 0x53, + 0x3a, 0x3c, 0x8e, 0x2c, 0x04, 0xbc, 0x48, 0xb5, 0xc0, 0x1e, 0xb7, 0x1f, + 0x77, 0x81, 0xdd, 0x8d, 0x38, 0x19, 0x1e, 0xf4, 0x85, 0xf9, 0x4c, 0x30, + 0xe1, 0x8f, 0xaf, 0x27, 0xab, 0x52, 0x01, 0x64, 0xd2, 0x4c, 0xf8, 0x23, + 0xdf, 0x3c, 0x54, 0x5e, 0x42, 0x3b, 0xbf, 0x38, 0xcb, 0x73, 0x74, 0x00, + 0xa5, 0x24, 0xf5, 0xbb, 0x69, 0x02, 0xf0, 0x1b, 0x39, 0xb0, 0x1c, 0x2a, + 0x57, 0x96, 0xc4, 0x5f, 0xb6, 0x92, 0xb8, 0xbe, 0x40, 0xc2, 0x84, 0x3c, + 0x45, 0xca, 0xe2, 0xd0, 0xdc, 0x8e, 0x32, 0xfe, 0xcc, 0x83, 0x23, 0x6a, + 0xc3, 0x11, 0xe2, 0xa1, 0x25, 0xe2, 0x1f, 0xf6, 0x59, 0xb2, 0x17, 0xbb, + 0x99, 0x44, 0x68, 0xdd, 0x97, 0xc1, 0x12, 0xbe, 0x64, 0x54, 0x4c, 0x2c, + 0x7c, 0x74, 0x68, 0x42, 0xbc, 0x6f, 0x22, 0x4e, 0xa6, 0x15, 0x0b, 0x06, + 0xa7, 0xb0, 0x36, 0xbe, 0x53, 0x13, 0xff, 0x8b, 0x96, 0x3a, 0x21, 0x0d, + 0xaa, 0xa7, 0x39, 0xe1, 0x4a, 0xae, 0xf6, 0xa5, 0x18, 0x1f, 0xf2, 0x34, + 0x8f, 0xde, 0x67, 0x2c, 0xc7, 0xcb, 0xd8, 0xf2, 0x2e, 0x41, 0xe3, 0x11, + 0x59, 0xbf, 0x8f, 0x03, 0x71, 0xae, 0x94, 0xd3, 0x0b, 0x37, 0x81, 0x5d, + 0x70, 0xc5, 0xd9, 0x19, 0x50, 0xf7, 0x94, 0xe6, 0x9f, 0x97, 0x8c, 0x54, + 0x36, 0x49, 0xd0, 0x7b, 0x18, 0x3f, 0x93, 0xbc, 0xf6, 0xc0, 0xec, 0xdc, + 0x53, 0x4f, 0x98, 0xf1, 0x69, 0xda, 0x0f, 0x24, 0xe4, 0x9b, 0x67, 0x8e, + 0x11, 0x9e, 0x73, 0x72, 0x3b, 0x4d, 0x5e, 0x99, 0x84, 0x6c, 0x6b, 0x58, + 0x38, 0x80, 0x63, 0xba, 0x64, 0x87, 0x20, 0x5d, 0x06, 0x6b, 0x74, 0xec, + 0xda, 0x01, 0x65, 0xd1, 0xc1, 0x35, 0x5e, 0xf9, 0x27, 0x5d, 0x0a, 0x31, + 0xa3, 0xa8, 0x7a, 0x25, 0xee, 0x7c, 0x15, 0x2a, 0xb5, 0x6b, 0x24, 0x70, + 0xf5, 0xa7, 0x20, 0x32, 0x10, 0x81, 0xb3, 0x65, 0x7a, 0xfd, 0x54, 0xd7, + 0x2d, 0x7f, 0xb2, 0x1d, 0xcd, 0x20, 0xa2, 0xbf, 0xbc, 0x21, 0xf4, 0xc0, + 0x97, 0x3b, 0x2c, 0xfa, 0x35, 0x14, 0x85, 0xea, 0x1e, 0x29, 0xeb, 0x4e, + 0xa2, 0x13, 0xb0, 0xf9, 0xf0, 0x52, 0xe1, 0x7a, 0xa9, 0x34, 0x67, 0x53, + 0x0e, 0x10, 0xe0, 0x02, 0x92, 0xb1, 0xf3, 0xc1, 0x3e, 0x88, 0xe3, 0x90, + 0xa8, 0x37, 0x3f, 0x31, 0xab, 0x9b, 0x9d, 0x1d, 0x28, 0x56, 0x51, 0x32, + 0xc3, 0xe9, 0x97, 0xf5, 0x92, 0xba, 0x34, 0xa1, 0xa4, 0x57, 0x5a, 0xf2, + 0x89, 0xd6, 0x1a, 0x4b, 0x16, 0xef, 0x06, 0xec, 0x8d, 0x06, 0x02, 0x8d, + 0x2d, 0xfc, 0xe1, 0x8e, 0x81, 0x86, 0xa9, 0x58, 0xbd, 0xd4, 0x10, 0xa2, + 0xab, 0x57, 0x76, 0xba, 0x6b, 0xcc, 0xc5, 0x4c, 0xc2, 0xd0, 0x76, 0xa6, + 0x80, 0x71, 0x76, 0xf7, 0x74, 0x21, 0x9f, 0xb5, 0x16, 0xaf, 0x9a, 0x89, + 0x49, 0xc6, 0x52, 0x85, 0x03, 0x56, 0x05, 0x6f, 0xc6, 0x0c, 0x7f, 0x6c, + 0x06, 0x59, 0xa1, 0xeb, 0x99, 0x9e, 0xe2, 0xd3, 0xcc, 0x8e, 0x70, 0x46, + 0x23, 0x40, 0x9d, 0xb7, 0x0b, 0x01, 0x79, 0x54, 0x74, 0x50, 0xcf, 0xb7, + 0x8c, 0x71, 0x99, 0x1d, 0xe0, 0x9b, 0x1c, 0x65, 0x65, 0x6f, 0xbf, 0x98, + 0xcd, 0x80, 0xc7, 0xe5, 0xe4, 0x72, 0x5d, 0x42, 0x16, 0x4c, 0xc4, 0x1e, + 0x95, 0xbf, 0x3d, 0x8d, 0x4e, 0x0e, 0x51, 0x58, 0xcf, 0xab, 0x96, 0xc0, + 0xbb, 0x0a, 0xca, 0x57, 0xc2, 0x95, 0x02, 0x97, 0x5a, 0xec, 0x05, 0x6c, + 0xff, 0xc6, 0xd4, 0x88, 0x29, 0xe8, 0xb9, 0x79, 0xac, 0x3e, 0x96, 0x7a, + 0xb1, 0x7b, 0x09, 0xca, 0x1c, 0xca, 0xd8, 0x43, 0xbd, 0x57, 0x26, 0xe2, + 0x59, 0x52, 0xc5, 0x1b, 0xeb, 0x38, 0x3e, 0x4b, 0x12, 0x29, 0xc0, 0xb8, + 0x0d, 0x52, 0xb0, 0xab, 0xdd, 0x99, 0x9d, 0x14, 0x37, 0xf7, 0xc6, 0x37, + 0xe6, 0x5d, 0xdf, 0xd8, 0x2a, 0xc0, 0x94, 0xbf, 0x0d, 0x95, 0x4c, 0x46, + 0x46, 0x5f, 0x03, 0xd2, 0xe7, 0x3d, 0x97, 0xae, 0x12, 0x4e, 0xe1, 0x66, + 0x62, 0xb4, 0x84, 0x55, 0x28, 0xdd, 0x53, 0x0f, 0xf4, 0x51, 0xe2, 0x08, + 0x81, 0xd1, 0x0f, 0x23, 0x62, 0x37, 0xf8, 0x08, 0x5f, 0xc7, 0x34, 0xdd, + 0xe9, 0x80, 0x09, 0x7a, 0x0d, 0xde, 0x14, 0xcd, 0xe7, 0x8e, 0xc0, 0xa8, + 0x9f, 0x75, 0x94, 0x47, 0xa5, 0x8a, 0x49, 0x14, 0x98, 0xce, 0x4d, 0xab, + 0xab, 0x65, 0xc1, 0x41, 0x14, 0x9e, 0x8d, 0x8b, 0x41, 0x50, 0x08, 0xa8, + 0x84, 0xf7, 0xd6, 0xf0, 0x07, 0x12, 0x3a, 0x1d, 0x51, 0x7d, 0x13, 0x78, + 0xa4, 0x9e, 0xf5, 0x3b, 0x0e, 0x38, 0x44, 0x34, 0xc2, 0x00, 0x87, 0x30, + 0xde, 0xe2, 0x24, 0x71, 0xcb, 0xd2, 0xb1, 0x09, 0x31, 0xc2, 0xdb, 0x9a, + 0xbe, 0xd6, 0x05, 0x7c, 0xb8, 0xc0, 0xeb, 0x5c, 0x20, 0x14, 0x18, 0x99, + 0xb3, 0xe4, 0x13, 0x90, 0xc3, 0x50, 0xa4, 0xef, 0x23, 0x7a, 0x98, 0x85, + 0x48, 0xa9, 0x55, 0xa0, 0x38, 0xd4, 0xd9, 0xd1, 0x83, 0x7a, 0xae, 0xc2, + 0x29, 0x69, 0x49, 0xf9, 0x4b, 0xba, 0xd0, 0xef, 0x1f, 0x30, 0x64, 0x05, + 0xbe, 0x35, 0x27, 0xb0, 0xc4, 0xfd, 0xfd, 0x8b, 0x82, 0xbc, 0x19, 0x5c, + 0x9b, 0x88, 0x88, 0x93, 0x26, 0xcd, 0xbd, 0x69, 0x6f, 0x07, 0xb1, 0x5f, + 0xae, 0x73, 0x17, 0xb7, 0x5a, 0xf1, 0xec, 0x1e, 0x7e, 0x1d, 0x9c, 0x42, + 0x9b, 0x54, 0xff, 0x0b, 0x26, 0x55, 0x17, 0x2e, 0x8b, 0x81, 0x3a, 0xf8, + 0x78, 0x1c, 0x59, 0x88, 0xff, 0xff, 0xe7, 0x19, 0x37, 0xe1, 0x88, 0x5d, + 0xc3, 0x3c, 0x4f, 0x86, 0x0e, 0xaf, 0xee, 0x46, 0x9a, 0x3c, 0x5f, 0x72, + 0x4d, 0x5e, 0x16, 0x6b, 0x3c, 0x42, 0xba, 0xce, 0x43, 0x10, 0xf8, 0xdc, + 0x08, 0x93, 0xb6, 0x00, 0xda, 0xb3, 0xe2, 0xb4, 0x81, 0x2d, 0x7e, 0x66, + 0x83, 0xf5, 0xe9, 0x63, 0xe0, 0xad, 0xc4, 0xf8, 0x9d, 0xf8, 0xbc, 0xe6, + 0x9e, 0x5f, 0x66, 0xa4, 0x1c, 0x73, 0x90, 0x5e, 0x15, 0x2a, 0xf9, 0xf4, + 0x24, 0xaf, 0xbb, 0xc0, 0x4e, 0x9e, 0x37, 0x5b, 0xc3, 0xc9, 0x86, 0x9b, + 0x8c, 0xac, 0x43, 0x5b, 0x32, 0x93, 0x31, 0x73, 0xb8, 0xd8, 0x39, 0x8a, + 0xe5, 0x86, 0x43, 0xc7, 0xf6, 0xeb, 0x4b, 0xca, 0xf9, 0x41, 0x57, 0x10, + 0x34, 0xd4, 0x57, 0x8c, 0xa4, 0xe0, 0xf6, 0x4d, 0x5c, 0x19, 0x47, 0x05, + 0xfa, 0xa0, 0x8d, 0x3a, 0x9a, 0x9f, 0x98, 0x0c, 0x98, 0xc4, 0x87, 0x11, + 0xa8, 0x63, 0xbb, 0x04, 0x40, 0xb0, 0xe4, 0xbe, 0x2a, 0xb0, 0xc3, 0x0b, + 0xa3, 0x9e, 0x35, 0x6a, 0x18, 0x1a, 0x4a, 0xc4, 0x36, 0x1b, 0xc7, 0x32, + 0xe1, 0x49, 0x6d, 0xd6, 0xb1, 0xf6, 0xba, 0xed, 0x55, 0xd5, 0xde, 0x5a, + 0xc7, 0xb0, 0x9a, 0x6d, 0xdb, 0x1e, 0xac, 0xf6, 0x0d, 0x61, 0xac, 0xaa, + 0x37, 0x0f, 0x0e, 0xa7, 0xaa, 0x75, 0xad, 0x02, 0xe3, 0x13, 0x70, 0x18, + 0x0c, 0x2e, 0x1a, 0xc6, 0x35, 0x73, 0x6f, 0xb5, 0x4d, 0x18, 0x86, 0x0e, + 0x93, 0xdc, 0x6d, 0x75, 0x00, 0xc6, 0x59, 0x48, 0x01, 0x94, 0x9f, 0x1a, + 0x58, 0xd7, 0x35, 0x1c, 0x13, 0xb1, 0x4a, 0x8b, 0xa7, 0x7f, 0x34, 0xff, + 0x4d, 0x62, 0x9a, 0xda, 0xc5, 0x18, 0xac, 0xcc, 0x93, 0x70, 0x7c, 0x26, + 0x6b, 0x0a, 0xc8, 0x2e, 0xbf, 0x85, 0x58, 0x9e, 0x0c, 0x69, 0xcb, 0x5a, + 0x61, 0x34, 0x30, 0x13, 0x44, 0x5e, 0x38, 0x90, 0xa2, 0x94, 0x7c, 0x89, + 0xbf, 0xa6, 0x82, 0xdb, 0xa1, 0x77, 0x06, 0x96, 0x18, 0x05, 0xe7, 0xcd, + 0xef, 0x50, 0x73, 0x34, 0xd4, 0x35, 0xd0, 0x05, 0xbc, 0x8f, 0xd8, 0x52, + 0x9e, 0x0f, 0x9e, 0x1b, 0x3e, 0x78, 0x13, 0x2a, 0xb8, 0x89, 0xe4, 0x4f, + 0x2e, 0x23, 0x56, 0xda, 0xa9, 0x12, 0xd2, 0xce, 0xbf, 0xc8, 0x49, 0xf9, + 0xf9, 0x4e, 0x08, 0xbd, 0x8a, 0xa4, 0x53, 0x29, 0xe8, 0x50, 0x92, 0x67, + 0xf8, 0x01, 0x20, 0x15, 0x8a, 0xd7, 0x87, 0x98, 0x23, 0x36, 0xf8, 0x14, + 0x88, 0xf3, 0x73, 0x3e, 0x1c, 0x50, 0xeb, 0x52, 0xf1, 0xbb, 0xfe, 0xe3, + 0x45, 0x83, 0xe4, 0xcf, 0x80, 0x7a, 0x6a, 0x81, 0xfa, 0x43, 0x43, 0xfa, + 0xf9, 0x45, 0x8c, 0x3f, 0x26, 0x09, 0x1a, 0x8e, 0xbf, 0x2e, 0xd4, 0xe7, + 0x32, 0x7a, 0xde, 0x7b, 0xdf, 0xeb, 0xe1, 0x40, 0xac, 0xa5, 0x62, 0xeb, + 0x00, 0x09, 0x30, 0xf9, 0x62, 0x38, 0x02, 0x8d, 0x56, 0x84, 0x4b, 0xd2, + 0xdc, 0xf9, 0x38, 0xd9, 0xf5, 0x79, 0xc5, 0xb4, 0xeb, 0x8f, 0x6d, 0x37, + 0x39, 0x4c, 0x7e, 0x1b, 0xa7, 0xe3, 0xec, 0xa8, 0x90, 0xbe, 0xc0, 0xfe, + 0x7f, 0xc2, 0x79, 0xd8, 0x76, 0xad, 0x3a, 0xf4, 0xa1, 0x24, 0x03, 0x16, + 0x78, 0x65, 0xc9, 0xf6, 0xe5, 0x4d, 0x2e, 0x2c, 0x4d, 0xcf, 0xb2, 0x34, + 0x70, 0x43, 0xcd, 0xe4, 0xcf, 0x04, 0xa3, 0x05, 0xb9, 0xa6, 0x0a, 0x88, + 0x02, 0x9a, 0x10, 0x4c, 0x23, 0x39, 0x40, 0x75, 0xc5, 0x9e, 0xec, 0xf2, + 0x74, 0xd6, 0xec, 0x0d, 0x97, 0x07, 0xbe, 0x82, 0xbb, 0x4c, 0x20, 0x39, + 0xed, 0xfc, 0xc4, 0x6d, 0x1c, 0x37, 0xbb, 0xe8, 0x3d, 0xbc, 0x0c, 0xcf, + 0x78, 0x5f, 0x5a, 0x13, 0xe4, 0x6b, 0xbd, 0xf8, 0x0f, 0x27, 0x54, 0x11, + 0xd8, 0x1c, 0x5b, 0xa5, 0x7a, 0xaf, 0x5e, 0xbc, 0x8d, 0xe9, 0x9a, 0xc2, + 0x5f, 0x0e, 0x8d, 0x9d, 0xa6, 0x00, 0xf3, 0x09, 0xa9, 0x22, 0xe6, 0x07, + 0x7d, 0xdd, 0x67, 0x24, 0x2f, 0xf8, 0xef, 0xdb, 0x13, 0xeb, 0x86, 0x16, + 0x1e, 0xf3, 0xc5, 0x87, 0xf0, 0x66, 0xa6, 0xbe, 0xd6, 0x4e, 0xa7, 0xb6, + 0x8c, 0x99, 0x1a, 0xc0, 0x32, 0xec, 0x14, 0x17, 0xf8, 0x53, 0x99, 0xc7, + 0x98, 0x4b, 0x86, 0x7b, 0x8b, 0x90, 0x8d, 0x6a, 0xd9, 0xad, 0xb5, 0x3b, + 0xae, 0x32, 0x5d, 0x28, 0x4c, 0xe0, 0xbe, 0x94, 0xe9, 0x11, 0xe5, 0x75, + 0x05, 0x9c, 0xc7, 0x80, 0x0f, 0x07, 0x2f, 0x05, 0x27, 0x22, 0xa8, 0x1a, + 0xec, 0xcf, 0xee, 0xc0, 0x10, 0x9a, 0x95, 0x08, 0x40, 0x36, 0x01, 0xc5, + 0xf4, 0x40, 0x7f, 0xc8, 0x37, 0x9e, 0x91, 0x50, 0x00, 0x5f, 0x9d, 0xf4, + 0x57, 0x99, 0xde, 0xb8, 0x46, 0xfa, 0x77, 0x28, 0xb4, 0x2d, 0x3f, 0xc1, + 0xd5, 0x8c, 0x57, 0x7c, 0xc9, 0x03, 0x7a, 0x10, 0x33, 0xca, 0xf4, 0x82, + 0x62, 0x0d, 0x23, 0x08, 0xae, 0xa5, 0x17, 0x46, 0xa6, 0xf3, 0x19, 0xb3, + 0x53, 0x38, 0x1f, 0xe1, 0x27, 0xc0, 0x57, 0x64, 0x64, 0x55, 0x98, 0xa4, + 0x3a, 0xe3, 0x71, 0x87, 0x29, 0x73, 0xf2, 0xfe, 0x89, 0xe0, 0x27, 0xb4, + 0x44, 0xd3, 0x4c, 0x34, 0x6c, 0x30, 0x88, 0xaf, 0xe0, 0xd1, 0xef, 0x16, + 0x4b, 0xcf, 0x7a, 0xa6, 0x6b, 0xc1, 0x0a, 0xe9, 0xcc, 0xb2, 0x51, 0x8b, + 0x4c, 0x03, 0x56, 0x3f, 0x76, 0x3d, 0xe2, 0xf6, 0xd4, 0x50, 0xf1, 0x01, + 0xb9, 0x7e, 0x56, 0x5d, 0x5e, 0x3d, 0xb6, 0x77, 0x9f, 0xc3, 0xdd, 0xbd, + 0x02, 0x94, 0xeb, 0x89, 0xf5, 0x1f, 0x5e, 0x3e, 0x36, 0x5e, 0xf7, 0xc9, + 0x88, 0x93, 0x02, 0x2e, 0x58, 0x16, 0x42, 0xa7, 0x64, 0x2e, 0x69, 0x6a, + 0x5b, 0xb7, 0xbd, 0x86, 0x75, 0x62, 0x4a, 0x55, 0x0f, 0x39, 0x3d, 0x16, + 0xc9, 0x25, 0xe0, 0x50, 0x85, 0x2c, 0x2a, 0xcb, 0x47, 0x09, 0xf2, 0x61, + 0x68, 0xed, 0xc6, 0xf0, 0xa3, 0x03, 0x2b, 0xc4, 0x14, 0xde, 0x2e, 0x18, + 0x61, 0x9b, 0x54, 0x94, 0x34, 0x91, 0x15, 0xd3, 0x13, 0xba, 0xa7, 0x9c, + 0x72, 0x76, 0x2e, 0x55, 0x11, 0xa0, 0xe1, 0xeb, 0xd0, 0x23, 0xa0, 0x60, + 0x23, 0x1a, 0x36, 0x2f, 0xc4, 0x71, 0x23, 0xfb, 0x9c, 0x87, 0x79, 0x21, + 0x6e, 0xf1, 0x11, 0x6e, 0xd1, 0xd2, 0xbd, 0x1b, 0x17, 0xa1, 0x5f, 0x48, + 0x23, 0x17, 0x18, 0x62, 0x42, 0x36, 0xdd, 0xba, 0xb7, 0x5c, 0x66, 0x92, + 0x61, 0xe0, 0x98, 0x5f, 0xaf, 0x1f, 0x2a, 0xd4, 0x54, 0xb5, 0x02, 0x0c, + 0x29, 0xca, 0x23, 0x6b, 0xbd, 0x1f, 0xbf, 0xf8, 0x1c, 0x6a, 0xc5, 0xac, + 0x8f, 0x1e, 0x73, 0xfb, 0xa4, 0xab, 0xbc, 0x9d, 0xcc, 0xfe, 0xb3, 0x9c, + 0xe5, 0x41, 0xe6, 0x0e, 0x12, 0x34, 0xb5, 0xf4, 0xc4, 0x49, 0x76, 0x64, + 0x35, 0xa3, 0x42, 0xed, 0x43, 0xa5, 0x4a, 0x0a, 0x29, 0xe5, 0x71, 0x23, + 0x2d, 0x80, 0x03, 0x54, 0x8a, 0xff, 0xb7, 0x46, 0x4a, 0x1c, 0x96, 0xc4, + 0x7c, 0xf5, 0x5d, 0x3d, 0xcf, 0xaa, 0xf4, 0x9b, 0x1b, 0xd5, 0x3f, 0xbb, + 0xdc, 0x5f, 0x2f, 0x6b, 0xad, 0xeb, 0x6d, 0x65, 0x64, 0xb2, 0x0b, 0xf7, + 0x7c, 0x6e, 0x3e, 0x1a, 0xbc, 0xa5, 0x41, 0xdc, 0xcc, 0x46, 0xfa, 0x9a, + 0x5e, 0xf1, 0x99, 0xae, 0xd2, 0x6b, 0x79, 0x28, 0xf2, 0x17, 0x92, 0x97, + 0x32, 0x3d, 0x12, 0x4b, 0x35, 0x71, 0x78, 0xe7, 0xf4, 0x56, 0xab, 0x5c, + 0x87, 0x7d, 0x25, 0xb6, 0x6e, 0x43, 0xa3, 0xed, 0x91, 0x45, 0xac, 0x38, + 0x5a, 0xac, 0xed, 0xf2, 0xba, 0xf9, 0x34, 0x12, 0xb5, 0xe9, 0x9c, 0x15, + 0x0c, 0x6b, 0xd5, 0xdb, 0x5e, 0x37, 0xb9, 0x9c, 0x0d, 0x4a, 0xa7, 0x2d, + 0xb5, 0x1b, 0xe3, 0x09, 0x72, 0xdb, 0x2d, 0xd1, 0x4c, 0xc5, 0x5a, 0x33, + 0x70, 0xb9, 0x58, 0xcc, 0x81, 0x40, 0x6a, 0x96, 0xeb, 0x0d, 0x98, 0x2c, + 0x0f, 0xc1, 0xeb, 0x55, 0x34, 0xc7, 0x30, 0xa7, 0x49, 0x3b, 0x51, 0xff, + 0xff, 0xcd, 0x28, 0xe4, 0xb5, 0x41, 0x68, 0x35, 0xab, 0x74, 0x4a, 0x6a, + 0x98, 0xd8, 0x74, 0x5d, 0xea, 0xb4, 0xc5, 0xf3, 0xc1, 0x79, 0xc9, 0x2e, + 0x5d, 0xa6, 0xea, 0x6f, 0xba, 0x52, 0xc4, 0xe3, 0xad, 0x12, 0xba, 0x40, + 0xb9, 0xbc, 0x01, 0x30, 0x9a, 0x24, 0x37, 0x0f, 0x24, 0x36, 0x56, 0x2f, + 0x8c, 0x03, 0xcc, 0x55, 0xdd, 0x2b, 0xb6, 0xe7, 0xed, 0x28, 0xf1, 0xd5, + 0x6a, 0xf7, 0x03, 0xfe, 0x03, 0x1b, 0xd0, 0x6b, 0xde, 0x39, 0x3c, 0x92, + 0xf5, 0x12, 0x12, 0x9b, 0x33, 0xfc, 0xcb, 0xc5, 0x10, 0x85, 0x7a, 0x89, + 0xdd, 0xbe, 0x65, 0x78, 0x76, 0xe1, 0x3b, 0xe1, 0xcf, 0x22, 0xe2, 0x2e, + 0x6e, 0x1b, 0x3e, 0xb9, 0x35, 0x37, 0xca, 0xf6, 0x72, 0x32, 0x6d, 0xed, + 0xa2, 0x46, 0xa2, 0x34, 0x2a, 0xcb, 0x49, 0x9f, 0x4b, 0xc1, 0xa2, 0x18, + 0xc2, 0x34, 0xde, 0x6a, 0x58, 0x2a, 0xf5, 0x66, 0x64, 0x21, 0xbd, 0xc1, + 0x6e, 0x37, 0x84, 0x67, 0x2c, 0x68, 0xae, 0xe1, 0xb6, 0x53, 0xd4, 0xa2, + 0xa3, 0x22, 0xaa, 0x95, 0x7c, 0x21, 0x85, 0xdd, 0x54, 0x9e, 0xe3, 0xd3, + 0xa0, 0xc8, 0x3e, 0x2a, 0xba, 0x72, 0x21, 0xfe, 0x37, 0xcb, 0xb1, 0x96, + 0x92, 0x6a, 0xc6, 0x48, 0x24, 0xbc, 0xc2, 0x50, 0x80, 0x38, 0xdb, 0x6e, + 0x21, 0xdd, 0x7e, 0x2d, 0xf8, 0xe3, 0x39, 0x43, 0x4f, 0x6c, 0xe8, 0x8d, + 0x14, 0x79, 0x1f, 0x0a, 0xbf, 0xae, 0xeb, 0x41, 0xac, 0xc7, 0xf0, 0xb8, + 0x95, 0xe7, 0x9d, 0xd9, 0xf6, 0x39, 0xac, 0xec, 0xe3, 0x40, 0x03, 0x74, + 0xae, 0x12, 0xac, 0x00, 0x60, 0x3f, 0x55, 0xde, 0xc3, 0x10, 0x63, 0xf5, + 0x41, 0xa3, 0x69, 0x38, 0xb1, 0xa3, 0xb3, 0x89, 0x1e, 0xcf, 0x31, 0x75, + 0x33, 0x30, 0xcc, 0x71, 0x05, 0x3c, 0xdd, 0x81, 0xe2, 0xde, 0x2d, 0xca, + 0x9e, 0xa5, 0x07, 0x51, 0xc5, 0x28, 0x09, 0x3c, 0x5b, 0xe8, 0x75, 0xb7, + 0x70, 0xf7, 0x9a, 0x9b, 0xdf, 0xb2, 0x4e, 0xc0, 0x51, 0x1e, 0xa6, 0xd4, + 0x95, 0x9c, 0x70, 0x7d, 0x70, 0xed, 0x23, 0x53, 0xf8, 0xf7, 0x45, 0xf1, + 0x00, 0xc0, 0xef, 0x83, 0x2f, 0xff, 0x4a, 0xde, 0x2a, 0x9b, 0x8f, 0xcb, + 0xc3, 0x4d, 0x47, 0x31, 0xb5, 0x65, 0x34, 0xe7, 0xf0, 0x7d, 0x2a, 0xc3, + 0x72, 0x71, 0xe3, 0xfa, 0x0f, 0x54, 0xfe, 0xc8, 0x68, 0x7c, 0x10, 0xb2, + 0xad, 0x2b, 0x86, 0x12, 0x12, 0x83, 0x53, 0xe9, 0x4c, 0xd6, 0x08, 0xf6, + 0x48, 0xc6, 0xb9, 0x6d, 0xfe, 0xcf, 0x63, 0xcb, 0x01, 0xf9, 0xa3, 0xfb, + 0x4e, 0xdd, 0x98, 0x56, 0x7d, 0x06, 0xdc, 0x23, 0x5d, 0x3d, 0xa8, 0x3d, + 0x99, 0x77, 0x33, 0x9a, 0x89, 0x7b, 0xe7, 0x4f, 0xa5, 0xdc, 0xb3, 0x49, + 0xff, 0x84, 0xdf, 0x92, 0xee, 0x74, 0xda, 0x32, 0x23, 0xb3, 0xf1, 0xf7, + 0xca, 0x8e, 0x30, 0xaf, 0x68, 0xc1, 0xe8, 0xe8, 0x1f, 0x97, 0xa3, 0x7c, + 0x10, 0x67, 0x85, 0x94, 0x60, 0x5b, 0x0e, 0x5a, 0xe7, 0xc4, 0x99, 0x56, + 0xac, 0xba, 0x84, 0x63, 0xc5, 0x49, 0xaa, 0x61, 0x22, 0xd7, 0x4f, 0x4c, + 0x21, 0x10, 0xe7, 0x26, 0x07, 0x55, 0xd1, 0x12, 0x9e, 0x2c, 0xee, 0x83, + 0xb6, 0x79, 0x21, 0xb9, 0xe8, 0xeb, 0x5a, 0x24, 0xda, 0xa7, 0xa0, 0x29, + 0xe4, 0xb6, 0x04, 0x86, 0x71, 0xf6, 0x7e, 0x07, 0xf8, 0x39, 0x2a, 0xd1, + 0x6e, 0x31, 0x0e, 0xdd, 0x71, 0xc9, 0x35, 0x29, 0xaf, 0xed, 0xd2, 0x17, + 0xad, 0x12, 0xce, 0xff, 0x57, 0x83, 0x37, 0x1e, 0x02, 0x9e, 0x19, 0x3b, + 0xce, 0xa2, 0x1d, 0xcf, 0xa2, 0x97, 0x15, 0x94, 0xa5, 0x30, 0xce, 0xe6, + 0x59, 0xff, 0x14, 0x1b, 0xeb, 0xf1, 0x8f, 0x97, 0xe2, 0xc9, 0x61, 0x85, + 0x36, 0x72, 0x5b, 0x36, 0xc9, 0x40, 0x76, 0xee, 0xeb, 0x82, 0x42, 0x2b, + 0xd3, 0x36, 0x4f, 0x66, 0x0c, 0x78, 0x50, 0x70, 0xb8, 0x0a, 0xb3, 0x28, + 0x3e, 0xa1, 0x39, 0x92, 0xf4, 0x89, 0xac, 0x45, 0x69, 0x24, 0xcc, 0x04, + 0x0c, 0xf5, 0x73, 0x33, 0x49, 0x30, 0xc0, 0x0c, 0x06, 0xbf, 0xa2, 0x28, + 0x7e, 0x2a, 0x19, 0xf1, 0x14, 0x33, 0x0a, 0xfa, 0xe8, 0x2b, 0x33, 0x5f, + 0xe9, 0x73, 0x83, 0x29, 0xb0, 0xe5, 0x99, 0xd2, 0x09, 0x78, 0x55, 0x31, + 0x7d, 0x33, 0xda, 0x37, 0xbe, 0x04, 0x95, 0x2b, 0xef, 0x4b, 0x2e, 0xda, + 0x87, 0xdc, 0xa4, 0xf4, 0x25, 0x77, 0xca, 0x7c, 0x0c, 0x85, 0x32, 0xa7, + 0x95, 0x87, 0xe2, 0x9e, 0xe0, 0xba, 0x0a, 0xd4, 0x94, 0x28, 0x05, 0xa8, + 0xdb, 0x48, 0xce, 0x88, 0xd5, 0x0b, 0x1d, 0xfd, 0x18, 0xa7, 0x58, 0x3c, + 0x3e, 0x8f, 0xd6, 0xd5, 0x9f, 0x5f, 0x64, 0xb8, 0x61, 0xb9, 0xd7, 0x78, + 0x21, 0x37, 0x3e, 0x4f, 0x95, 0x38, 0xf4, 0x94, 0xd4, 0x10, 0xc3, 0xb0, + 0x4a, 0xcb, 0x36, 0x6f, 0xa8, 0xbe, 0xe9, 0xe8, 0x6f, 0x6f, 0x64, 0x84, + 0xa5, 0x14, 0x4d, 0x8c, 0xaa, 0xb5, 0x6c, 0xee, 0x45, 0x66, 0x9b, 0xb4, + 0x9b, 0x4c, 0xb5, 0xae, 0x92, 0x5b, 0xe7, 0x63, 0x04, 0x52, 0x0e, 0xcc, + 0xe7, 0x2f, 0x8b, 0x79, 0x64, 0x91, 0xf7, 0xbd, 0x4d, 0xa1, 0x66, 0xa7, + 0x8c, 0x3c, 0x4f, 0x19, 0xaa, 0xe7, 0xa9, 0x18, 0x7c, 0x76, 0x6d, 0xa4, + 0x18, 0x79, 0x72, 0xb5, 0x75, 0x07, 0xf0, 0xaa, 0x55, 0xea, 0x25, 0x68, + 0xe2, 0x42, 0xfc, 0xb4, 0x27, 0x90, 0x0f, 0x70, 0x75, 0xf8, 0xe4, 0xdb, + 0xa6, 0x8b, 0xda, 0xd8, 0x22, 0x88, 0x21, 0x78, 0x55, 0x51, 0x85, 0xd2, + 0x80, 0x37, 0xa1, 0x10, 0x55, 0xd7, 0xc8, 0xda, 0x7a, 0x89, 0x5d, 0x70, + 0xc2, 0x77, 0xc6, 0x06, 0x3f, 0x20, 0x4b, 0xc6, 0xab, 0x54, 0xca, 0xa6, + 0x23, 0xe4, 0x8b, 0xac, 0xbd, 0x21, 0xb0, 0xfe, 0x4b, 0xdb, 0xf0, 0xff, + 0x4e, 0x08, 0xbf, 0xac, 0x12, 0x6f, 0x01, 0x23, 0xda, 0xfc, 0xc8, 0x8b, + 0x74, 0x01, 0x23, 0xfd, 0x3c, 0xe6, 0x31, 0x3e, 0x1e, 0x5c, 0x01, 0xc2, + 0x03, 0x13, 0x43, 0x5a, 0x79, 0x11, 0xb5, 0x3d, 0x0b, 0x19, 0x9b, 0xb4, + 0x78, 0x2c, 0xbb, 0x3e, 0xfc, 0x8f, 0x90, 0x13, 0x85, 0xf1, 0x7c, 0x32, + 0xfa, 0xba, 0xe7, 0x3e, 0x4e, 0x67, 0xed, 0xb9, 0x7e, 0x83, 0xba, 0x2b, + 0xce, 0x83, 0x0a, 0xa7, 0xfe, 0x0d, 0x37, 0x99, 0x61, 0x30, 0x01, 0x1f, + 0x82, 0xd3, 0xf3, 0xce, 0xa0, 0x0b, 0xe6, 0x11, 0x5f, 0xe4, 0x0d, 0xd3, + 0x1b, 0x44, 0xc0, 0xb2, 0x0b, 0x15, 0x3e, 0x18, 0x2a, 0x60, 0xa3, 0xc2, + 0xf8, 0x12, 0x7a, 0xd1, 0xd1, 0x8a, 0x76, 0x89, 0x5d, 0xb0, 0x49, 0xa5, + 0x48, 0x15, 0x25, 0x73, 0x25, 0x5c, 0x4a, 0xc4, 0x7d, 0xdf, 0x71, 0xf9, + 0xdd, 0xbc, 0x50, 0xa4, 0x53, 0xfd, 0xcc, 0x54, 0x19, 0xc6, 0xa8, 0xd3, + 0x19, 0x39, 0xef, 0xd4, 0x64, 0x03, 0xf7, 0xa1, 0xd0, 0x8c, 0x6c, 0xd2, + 0xc5, 0x6a, 0xf0, 0x89, 0x13, 0xf9, 0xbe, 0xba, 0x36, 0xe5, 0x6f, 0x09, + 0x0e, 0xd5, 0x23, 0x1c, 0x71, 0x0f, 0xd2, 0xd4, 0xaf, 0xcd, 0x9a, 0xf4, + 0xc6, 0x90, 0x41, 0x42, 0xc2, 0x3a, 0x19, 0x1d, 0x46, 0xe0, 0x5e, 0x4c, + 0x1b, 0x26, 0x9e, 0x46, 0x69, 0xda, 0xdf, 0x37, 0x16, 0x28, 0x85, 0x4d, + 0x5c, 0x42, 0xd4, 0x18, 0x7c, 0x3c, 0x4a, 0x34, 0x3f, 0x72, 0x15, 0x9a, + 0x21, 0x1c, 0xc7, 0x0d, 0x81, 0x6d, 0x6e, 0x69, 0x2d, 0x08, 0x21, 0x2c, + 0x61, 0xc7, 0x0a, 0xf0, 0xd0, 0x18, 0xe7, 0x50, 0x70, 0x4d, 0x4c, 0xf7, + 0x0d, 0xf2, 0x14, 0x2e, 0x6a, 0x34, 0xd4, 0x01, 0x33, 0xd2, 0x4c, 0x13, + 0xe4, 0x1c, 0xe5, 0xf2, 0x80, 0xaa, 0xfa, 0xf9, 0x8f, 0x91, 0x3d, 0x1f, + 0x89, 0xe6, 0x66, 0x08, 0x3d, 0xeb, 0x9b, 0x8c, 0x22, 0xdb, 0xc1, 0x21, + 0xd2, 0x00, 0xba, 0x5a, 0x18, 0x54, 0xf5, 0x22, 0x07, 0x1e, 0x37, 0xf2, + 0x65, 0x23, 0xe8, 0x62, 0x70, 0xab, 0x17, 0x72, 0x8e, 0x92, 0x3e, 0xe5, + 0x3b, 0x53, 0x06, 0x2b, 0x1d, 0xbc, 0x16, 0xf4, 0x7e, 0xb6, 0x79, 0xa8, + 0x37, 0x66, 0x43, 0x7d, 0xb7, 0xd6, 0xc2, 0xdd, 0xc3, 0x31, 0x2f, 0xf0, + 0x20, 0x6e, 0x04, 0xf8, 0x8f, 0x3c, 0x19, 0xa9, 0x7c, 0x2e, 0x47, 0x14, + 0x59, 0x39, 0x24, 0x5a, 0x7d, 0x09, 0xe7, 0xa5, 0xa2, 0x0e, 0xd9, 0x74, + 0x92, 0x90, 0x6a, 0x25, 0x49, 0xe7, 0x72, 0x2a, 0x76, 0x5b, 0x72, 0x8b, + 0x21, 0xaa, 0x9f, 0xa6, 0xe2, 0x04, 0xcd, 0xec, 0x35, 0x6c, 0xdf, 0x12, + 0x65, 0xad, 0x86, 0x68, 0x03, 0xe7, 0xad, 0x0b, 0x62, 0x29, 0x48, 0xd3, + 0x71, 0xec, 0xf3, 0x5a, 0x75, 0xad, 0x7c, 0x8c, 0xcd, 0x8a, 0xcc, 0x18, + 0x6f, 0xc1, 0x95, 0x08, 0xa8, 0x97, 0x86, 0x00, 0xb2, 0x5a, 0x8a, 0x4d, + 0x8a, 0xc1, 0xb2, 0xb8, 0xde, 0x4a, 0xf0, 0x6f, 0x5e, 0xbe, 0x69, 0x27, + 0x12, 0x1a, 0x17, 0x9c, 0x19, 0x56, 0x08, 0x6b, 0x8c, 0xf3, 0x35, 0xde, + 0xf2, 0xa2, 0xd7, 0x40, 0x0b, 0x89, 0x10, 0x73, 0x09, 0xdd, 0x31, 0x6d, + 0xd3, 0x56, 0x31, 0xda, 0xfd, 0xae, 0x71, 0xa4, 0x08, 0xa0, 0x1e, 0x7c, + 0x81, 0xa0, 0x58, 0xa6, 0xe9, 0x76, 0x79, 0xfa, 0xce, 0x78, 0x8c, 0x04, + 0x66, 0xec, 0x9e, 0x2b, 0x66, 0xa3, 0xdd, 0x7b, 0x40, 0x66, 0x5a, 0xdf, + 0x9b, 0x2d, 0xd8, 0x99, 0xbe, 0x7b, 0xbc, 0x90, 0xdd, 0xa6, 0xbd, 0x49, + 0x0d, 0x7f, 0x18, 0x04, 0xf0, 0x17, 0x41, 0x22, 0xd8, 0x5b, 0xad, 0x01, + 0x03, 0x1a, 0x81, 0xe8, 0x98, 0xfe, 0x8b, 0xd7, 0x60, 0x0a, 0x24, 0x81, + 0x46, 0x70, 0xca, 0x88, 0x0a, 0xfd, 0x84, 0x7e, 0xb2, 0x67, 0xf5, 0xf4, + 0x89, 0x36, 0x64, 0x10, 0xff, 0x71, 0x37, 0x6e, 0x98, 0x7f, 0x03, 0xf9, + 0xee, 0x79, 0xad, 0x8a, 0x60, 0xc4, 0x49, 0x61, 0xe5, 0xba, 0xe6, 0x3d, + 0xc5, 0x35, 0x76, 0xe9, 0x79, 0xde, 0xbd, 0xa2, 0xcb, 0x38, 0x22, 0xd8, + 0x14, 0x4c, 0x9f, 0x06, 0x79, 0x8c, 0xb9, 0x51, 0xfa, 0x3c, 0x4d, 0x1f, + 0xe5, 0x07, 0x81, 0x76, 0xdd, 0x84, 0x03, 0xf3, 0xc8, 0x55, 0xe8, 0xd1, + 0xec, 0x45, 0x6a, 0xde, 0x36, 0x27, 0x38, 0x5a, 0x99, 0x4f, 0x01, 0xac, + 0x0a, 0x50, 0x67, 0x9d, 0x47, 0xa8, 0xdd, 0x30, 0xd7, 0xae, 0xe1, 0x48, + 0x44, 0x13, 0x20, 0x76, 0x06, 0x4f, 0xcd, 0x36, 0x92, 0x56, 0xa8, 0x6a, + 0xfc, 0xe6, 0x72, 0xaf, 0xb1, 0x5b, 0xcf, 0xb0, 0xe3, 0x92, 0x59, 0x0c, + 0xae, 0x42, 0x6b, 0x91, 0xa2, 0x33, 0xd5, 0x59, 0x02, 0xce, 0xf6, 0xc5, + 0xe0, 0x1b, 0xd2, 0x1b, 0x03, 0x93, 0x50, 0x0b, 0xff, 0x0c, 0x64, 0xac, + 0x27, 0xf1, 0xd8, 0x6d, 0x6a, 0x12, 0xa2, 0x5c, 0x35, 0x3d, 0x9b, 0x42, + 0x39, 0x62, 0xb2, 0x65, 0x07, 0x21, 0x80, 0xc2, 0x3e, 0x06, 0xfa, 0xa4, + 0xdc, 0xf0, 0x23, 0xf0, 0x3a, 0x25, 0xb5, 0xbb, 0x44, 0x8b, 0x81, 0x25, + 0x98, 0x2a, 0xfd, 0x5e, 0x0f, 0x35, 0x5c, 0x6e, 0x98, 0xe5, 0x93, 0xab, + 0x46, 0x7d, 0xbe, 0x10, 0x92, 0xf8, 0x21, 0x6d, 0x7f, 0xbb, 0x22, 0x53, + 0x39, 0x95, 0x49, 0x0b, 0x73, 0xcb, 0x43, 0x9e, 0x27, 0x4c, 0xd8, 0x00, + 0x68, 0xea, 0xea, 0xe3, 0xfc, 0xc6, 0x76, 0x70, 0xfb, 0xfe, 0xae, 0x20, + 0xe2, 0x5f, 0x22, 0x1d, 0x26, 0x5e, 0x18, 0xef, 0x25, 0xe7, 0x88, 0x3a, + 0xd9, 0x22, 0xc2, 0x76, 0xb7, 0xfb, 0xb4, 0xfc, 0x38, 0x14, 0xbf, 0x27, + 0x80, 0xc9, 0xac, 0x10, 0x04, 0x7d, 0x17, 0x03, 0x05, 0x6c, 0xb1, 0xa2, + 0x4f, 0x6e, 0xfe, 0x14, 0xd3, 0x9f, 0x1b, 0x53, 0x32, 0xb0, 0x7d, 0x74, + 0x65, 0xee, 0xea, 0x0f, 0x65, 0xf3, 0x33, 0x8f, 0xac, 0xcb, 0xe5, 0xf3, + 0x66, 0xd5, 0x27, 0xb3, 0xa9, 0x5a, 0x96, 0xcf, 0x75, 0x3e, 0x31, 0x22, + 0x92, 0xe9, 0xf3, 0xb5, 0x7a, 0x86, 0xb7, 0xa3, 0x67, 0x20, 0x5a, 0xa9, + 0x39, 0x39, 0x38, 0x9a, 0x0f, 0x9e, 0x9d, 0x23, 0x17, 0x64, 0x29, 0x1e, + 0x5a, 0x66, 0xb8, 0x4a, 0x90, 0xd9, 0x7a, 0xe2, 0xb4, 0xa4, 0x3b, 0xe4, + 0x50, 0x78, 0x6a, 0x5a, 0xaa, 0x7c, 0xc0, 0x91, 0x9b, 0x7f, 0x28, 0x5d, + 0xca, 0x04, 0x3b, 0xeb, 0x3e, 0x8a, 0x09, 0xa5, 0x83, 0x43, 0xb7, 0x7e, + 0xd4, 0x9f, 0xff, 0x5e, 0x03, 0x04, 0xa1, 0xa0, 0xa2, 0x63, 0x2b, 0x7d, + 0x19, 0x9f, 0x29, 0x12, 0x07, 0x59, 0xd4, 0x6b, 0xb6, 0x60, 0xd1, 0x66, + 0xa5, 0x0d, 0x8c, 0xd2, 0x4c, 0x04, 0x78, 0x7f, 0x66, 0x71, 0x68, 0x5a, + 0x88, 0xdc, 0xee, 0xf0, 0x22, 0x20, 0x4d, 0x63, 0x26, 0x11, 0x84, 0x64, + 0xbb, 0x0b, 0x3c, 0x6e, 0xf2, 0xe2, 0x6d, 0xb5, 0x8f, 0x6d, 0x8b, 0x7c, + 0x4a, 0xe4, 0x99, 0xf0, 0x25, 0xe4, 0xdc, 0xaf, 0xfc, 0x67, 0x42, 0xa9, + 0xf8, 0x58, 0x51, 0x2c, 0x47, 0xa5, 0x7a, 0x3f, 0xa0, 0xfe, 0x7d, 0x75, + 0x63, 0x75, 0x59, 0x1d, 0x8b, 0x77, 0xbe, 0x0e, 0x3b, 0x68, 0xd0, 0xa0, + 0xc0, 0x70, 0xe6, 0xee, 0x79, 0x92, 0x58, 0x6f, 0x28, 0xa5, 0x5d, 0xb4, + 0xd0, 0x83, 0x0f, 0x5a, 0xdd, 0xa0, 0x2f, 0xb6, 0xa1, 0xfa, 0xcf, 0x00, + 0xc6, 0x5c, 0x7e, 0xd1, 0x07, 0x85, 0xc4, 0xf0, 0xd3, 0xcb, 0x18, 0x38, + 0xd6, 0x34, 0x10, 0xab, 0x73, 0x38, 0x83, 0xf8, 0xa7, 0x42, 0x11, 0xae, + 0x97, 0xce, 0x18, 0x75, 0xb3, 0x55, 0xf1, 0x3f, 0x65, 0x08, 0xe2, 0x81, + 0x84, 0xc1, 0x88, 0x5f, 0x18, 0x6d, 0x21, 0x07, 0xc7, 0xbf, 0x9e, 0x5e, + 0x89, 0x6f, 0xa9, 0x9c, 0x4a, 0xd2, 0xf5, 0x68, 0x1c, 0xe5, 0xa4, 0xff, + 0x52, 0xfd, 0xed, 0x20, 0xe4, 0x93, 0x0d, 0x91, 0x84, 0x51, 0x18, 0x0a, + 0x7b, 0xaf, 0x70, 0xfe, 0x12, 0x35, 0x25, 0xe6, 0x91, 0x20, 0xf2, 0xd3, + 0x62, 0x19, 0xfe, 0x70, 0x24, 0x06, 0xee, 0xc9, 0x91, 0xec, 0xcf, 0x1a, + 0x00, 0xe5, 0x99, 0x04, 0x6b, 0x7b, 0xc3, 0x9c, 0x07, 0x27, 0xce, 0x5d, + 0xab, 0x6b, 0x36, 0x1e, 0x43, 0xf8, 0x48, 0x24, 0xef, 0x24, 0x2b, 0x70, + 0x49, 0x2e, 0xd2, 0x6f, 0xd1, 0x9b, 0x36, 0x77, 0x85, 0x6c, 0xc7, 0x62, + 0xcf, 0x92, 0x66, 0xd4, 0x98, 0xa4, 0x05, 0x79, 0x1d, 0xb9, 0xf3, 0xf2, + 0x62, 0xce, 0xee, 0x4c, 0x1d, 0xbc, 0x9b, 0x72, 0xea, 0xe3, 0xad, 0x2c, + 0x9f, 0x71, 0x53, 0xb6, 0x5a, 0xd5, 0xdd, 0x46, 0x5f, 0xc6, 0x53, 0x3b, + 0x56, 0xf5, 0x8a, 0x5c, 0x9a, 0x65, 0x8e, 0x2a, 0x3e, 0x7d, 0xa2, 0xb3, + 0x87, 0x34, 0x77, 0xf2, 0xc2, 0x96, 0x7c, 0x99, 0xe0, 0xd0, 0x5b, 0xbe, + 0x57, 0x13, 0x78, 0x18, 0x48, 0x40, 0x85, 0x34, 0x27, 0x8d, 0x0c, 0x78, + 0xce, 0x75, 0x64, 0x7c, 0x1d, 0xd0, 0x23, 0x28, 0x6e, 0x62, 0x21, 0x46, + 0x5e, 0x48, 0x83, 0xcb, 0xb4, 0x7b, 0x16, 0x23, 0xbc, 0xbc, 0xee, 0x43, + 0x3e, 0x43, 0x0e, 0x95, 0x44, 0x55, 0x4d, 0xd4, 0xfa, 0x97, 0xf5, 0x04, + 0x35, 0x38, 0x70, 0xef, 0xd4, 0xc4, 0x66, 0xa6, 0xbc, 0xba, 0x20, 0xc8, + 0x97, 0x63, 0xc9, 0x6f, 0x1d, 0x74, 0xdd, 0x37, 0x49, 0xa9, 0xe6, 0xae, + 0xf2, 0xf3, 0x4f, 0x38, 0x3c, 0x1d, 0x8d, 0x12, 0xe3, 0xf8, 0x7e, 0xc2, + 0x3d, 0xf6, 0x10, 0xcd, 0xc8, 0x67, 0x96, 0xad, 0x41, 0x7e, 0x60, 0xa0, + 0x48, 0x42, 0x22, 0x41, 0x97, 0xc5, 0x76, 0x5a, 0x6a, 0x63, 0x5e, 0xd4, + 0x62, 0xe5, 0xde, 0x20, 0x2f, 0xba, 0x07, 0xbe, 0x33, 0xd3, 0x9b, 0x33, + 0xef, 0x39, 0x0d, 0x79, 0x85, 0x93, 0x91, 0xc4, 0xf1, 0x54, 0x4d, 0xe4, + 0x1c, 0x99, 0xd0, 0x09, 0x52, 0x02, 0xd1, 0x16, 0x22, 0x8c, 0x99, 0xfe, + 0x13, 0x91, 0x1b, 0x43, 0x42, 0xaf, 0xcf, 0x39, 0xf1, 0x2f, 0x27, 0xc8, + 0x31, 0x90, 0xf9, 0x40, 0x5a, 0x26, 0x16, 0xa0, 0x26, 0x57, 0x84, 0x8b, + 0x58, 0xd0, 0xc3, 0x40, 0x6c, 0xed, 0xe8, 0x1d, 0xc2, 0x92, 0xc5, 0x54, + 0x47, 0x27, 0xc1, 0x20, 0x6d, 0xdf, 0xe2, 0x0e, 0xda, 0x68, 0xa0, 0xfc, + 0x23, 0x4b, 0xa1, 0x26, 0x55, 0x4c, 0x57, 0x6f, 0x7a, 0x28, 0xd2, 0x34, + 0x2a, 0x69, 0x0e, 0x68, 0xdd, 0x3f, 0xa2, 0xf1, 0x66, 0x4c, 0x3e, 0x03, + 0x73, 0x30, 0xbf, 0x70, 0x7b, 0xb8, 0x34, 0x8d, 0x47, 0x32, 0x51, 0x88, + 0x91, 0x0b, 0xf1, 0x2f, 0xb4, 0x37, 0xca, 0x86, 0x68, 0x45, 0xbd, 0xb9, + 0x86, 0x41, 0x33, 0x3d, 0xd9, 0xc7, 0x86, 0x9e, 0x0e, 0x5a, 0xc6, 0x8a, + 0xd2, 0xca, 0xe3, 0x09, 0x67, 0x74, 0x08, 0xf7, 0xf0, 0x44, 0x04, 0x15, + 0x31, 0xa3, 0x86, 0x7c, 0xa6, 0x2c, 0x8f, 0x43, 0x18, 0x16, 0x32, 0xdd, + 0xbd, 0x2a, 0x39, 0x6f, 0x9c, 0xdc, 0xac, 0x7f, 0x5c, 0x15, 0xfe, 0xa6, + 0x96, 0xdd, 0x22, 0x2f, 0xae, 0xe4, 0x02, 0xde, 0x3d, 0x8a, 0x50, 0x73, + 0x62, 0xac, 0x04, 0x13, 0x5e, 0x85, 0x40, 0x68, 0x6f, 0x13, 0x64, 0xb7, + 0x64, 0xd0, 0xdd, 0x5f, 0xa6, 0xd1, 0x68, 0x45, 0xf2, 0xef, 0xc4, 0xe9, + 0xfc, 0xae, 0x49, 0x01, 0x07, 0x17, 0x30, 0x23, 0x55, 0x87, 0x01, 0xe3, + 0x6b, 0xa2, 0xc2, 0xa2, 0x3f, 0x8a, 0x62, 0x78, 0xb0, 0xbd, 0xce, 0x92, + 0xb6, 0x35, 0x2f, 0xd9, 0x7b, 0xbd, 0x4c, 0xdd, 0x05, 0x45, 0x92, 0x35, + 0x16, 0xd3, 0xb0, 0x8c, 0xd3, 0x84, 0x1b, 0x5c, 0x20, 0x43, 0xd9, 0x3f, + 0x6c, 0x4c, 0x01, 0x4c, 0x52, 0xf8, 0x42, 0x9e, 0x1c, 0x9f, 0xaf, 0x88, + 0xdd, 0x9a, 0x85, 0xc1, 0x54, 0xd6, 0x79, 0x80, 0x1a, 0x9e, 0xf8, 0xea, + 0xd9, 0x3b, 0xfd, 0x5a, 0xa0, 0x6f, 0x5b, 0xf7, 0x13, 0x00, 0x9e, 0xc0, + 0xee, 0x36, 0x17, 0xc3, 0xa5, 0x5a, 0xe7, 0x19, 0x73, 0xcb, 0x7f, 0x14, + 0xc1, 0x7d, 0x55, 0xe4, 0x20, 0xe6, 0x98, 0xdc, 0x96, 0xe8, 0x53, 0x51, + 0x3b, 0x8c, 0xec, 0xcd, 0x8f, 0x90, 0xd9, 0x3a, 0x35, 0x37, 0xe2, 0x53, + 0x7c, 0x58, 0x7f, 0x57, 0xee, 0xa3, 0x6a, 0x54, 0xc4, 0x48, 0xb4, 0xa4, + 0x75, 0xf4, 0x3e, 0x8e, 0x55, 0x5e, 0x27, 0xc9, 0xb8, 0xc8, 0x44, 0x2a, + 0x97, 0x40, 0x75, 0x23, 0x28, 0x45, 0x1c, 0x00, 0xd2, 0xcd, 0xd9, 0x44, + 0xbe, 0x21, 0x6b, 0xf9, 0x78, 0x87, 0xd4, 0x46, 0x59, 0xf2, 0xbc, 0xd5, + 0xc1, 0x50, 0x87, 0xeb, 0xab, 0x73, 0xd5, 0x04, 0x31, 0x95, 0x14, 0x5f, + 0xd8, 0xe6, 0x14, 0x74, 0x9d, 0xef, 0x0e, 0xee, 0x11, 0xf7, 0x7b, 0xda, + 0xda, 0x97, 0xe6, 0x97, 0xb4, 0xe6, 0x1a, 0xd0, 0xa2, 0x72, 0x77, 0x4d, + 0x86, 0x42, 0x13, 0x75, 0x8b, 0x6c, 0xc7, 0x3d, 0xb2, 0x08, 0xa7, 0x72, + 0x53, 0xb2, 0x3f, 0xee, 0x75, 0x29, 0xcd, 0x6f, 0xba, 0x0c, 0xba, 0x6b, + 0x26, 0xa3, 0xbe, 0xf9, 0x75, 0xcd, 0x33, 0xdb, 0x6a, 0xdf, 0xae, 0xbe, + 0xae, 0x0f, 0xdf, 0x22, 0x37, 0x4b, 0x61, 0x0e, 0x38, 0xe7, 0x61, 0xbb, + 0xe4, 0xc5, 0x29, 0x54, 0x1b, 0x48, 0xa3, 0x27, 0xf0, 0xed, 0x54, 0x9d, + 0x14, 0xf9, 0xb1, 0x27, 0x07, 0x09, 0xc7, 0xa0, 0xf2, 0xe5, 0x6d, 0xb2, + 0x1f, 0x70, 0x0b, 0xd9, 0x52, 0x22, 0xa8, 0x2c, 0x11, 0xfd, 0x63, 0x6b, + 0x88, 0xb8, 0x0d, 0xd5, 0x2f, 0x9a, 0x47, 0x97, 0x44, 0x2b, 0x8a, 0xec, + 0x25, 0x06, 0xe5, 0x3f, 0x5a, 0x0e, 0xe7, 0xd5, 0x7c, 0x4d, 0x34, 0x3e, + 0xdd, 0x95, 0xbe, 0xce, 0xaf, 0x3d, 0xe8, 0xe9, 0x29, 0x5a, 0x90, 0xcf, + 0xc2, 0xc7, 0xd5, 0x97, 0xde, 0xde, 0x15, 0x30, 0xf2, 0x11, 0x2c, 0x6b, + 0x1d, 0x39, 0x25, 0x91, 0x34, 0x58, 0x92, 0xc7, 0x40, 0x23, 0x23, 0x94, + 0x2c, 0xf9, 0x0e, 0xf2, 0x3f, 0x5c, 0xb7, 0xfd, 0x65, 0xd4, 0xc7, 0x56, + 0x93, 0x19, 0xca, 0x2d, 0x31, 0xc1, 0xb4, 0x66, 0xe2, 0x83, 0x4f, 0x9b, + 0xf0, 0x65, 0x4e, 0x8e, 0xe8, 0x7d, 0xf0, 0x34, 0x8b, 0x30, 0xa4, 0x86, + 0xa0, 0xc4, 0xe9, 0x6c, 0xe3, 0x48, 0x04, 0x00, 0x46, 0xf4, 0xe4, 0x89, + 0xad, 0x3d, 0x11, 0x31, 0x4e, 0xe3, 0x2e, 0x4c, 0x7f, 0x98, 0x88, 0x67, + 0xa1, 0xbf, 0x17, 0xe7, 0x37, 0x7e, 0x88, 0x33, 0xd5, 0x35, 0x63, 0xdf, + 0x58, 0xd5, 0x35, 0x0d, 0x22, 0x43, 0x51, 0xe4, 0xf1, 0x17, 0xcb, 0x42, + 0x4f, 0x3c, 0xd1, 0xfc, 0x3d, 0xf2, 0x04, 0xa0, 0x31, 0x92, 0x74, 0x01, + 0x24, 0xc7, 0x4f, 0xd4, 0x25, 0x7b, 0x0a, 0x5c, 0x15, 0x15, 0x86, 0xfb, + 0x67, 0xe8, 0xb1, 0xb3, 0x51, 0x48, 0x07, 0x00, 0xc1, 0xbc, 0x9e, 0x05, + 0xb1, 0xa5, 0x1c, 0x3c, 0x6b, 0x13, 0x1b, 0x6b, 0x86, 0xe7, 0x44, 0x22, + 0x3f, 0x4c, 0x12, 0x7e, 0x18, 0x64, 0x63, 0x13, 0x33, 0xe0, 0xd6, 0x8f, + 0xf7, 0xd3, 0x1f, 0x37, 0xdd, 0xa5, 0xef, 0x98, 0x32, 0x6c, 0x8b, 0x2c, + 0x40, 0x94, 0x0f, 0x82, 0xb6, 0x23, 0x5d, 0xd3, 0xfb, 0x80, 0xbf, 0xf6, + 0x70, 0xaf, 0xa7, 0x1f, 0x16, 0x14, 0x5d, 0x9e, 0x43, 0x3d, 0xc2, 0xf3, + 0x72, 0x0e, 0x6f, 0xd2, 0x2e, 0x4c, 0x5e, 0x05, 0x5c, 0xcb, 0xfb, 0xd9, + 0xac, 0xfe, 0xe9, 0x6d, 0xfb, 0xb1, 0xc1, 0x15, 0xd4, 0x20, 0x46, 0xf1, + 0xd3, 0x8d, 0x6b, 0x36, 0x87, 0x05, 0x33, 0x9c, 0x05, 0x01, 0xfc, 0xcb, + 0xa9, 0xe5, 0x4c, 0xb1, 0xf0, 0x82, 0x43, 0x50, 0xf0, 0x0b, 0x33, 0x79, + 0x4d, 0xde, 0x18, 0x65, 0x54, 0x5b, 0x47, 0xde, 0x8a, 0x28, 0xbe, 0xc3, + 0x85, 0xb1, 0x59, 0xeb, 0xc5, 0x6a, 0x22, 0x4a, 0x8d, 0xf4, 0xbe, 0xb5, + 0x02, 0x48, 0x07, 0xff, 0xb4, 0x2c, 0x39, 0x13, 0x18, 0x58, 0x3e, 0xb5, + 0xda, 0x55, 0xad, 0x1c, 0x5e, 0x80, 0x5e, 0xae, 0x80, 0xc9, 0x1f, 0xf5, + 0xdd, 0xd2, 0x72, 0x5c, 0x75, 0x67, 0x64, 0x74, 0x5b, 0xa9, 0xa3, 0xe5, + 0x53, 0x19, 0x15, 0xb3, 0xeb, 0xb3, 0x80, 0x6d, 0x6c, 0xfd, 0xb3, 0x82, + 0x0c, 0x34, 0x30, 0xae, 0x5f, 0x33, 0xf2, 0x4f, 0x35, 0xee, 0x4c, 0x50, + 0x88, 0x46, 0xa1, 0x65, 0x1a, 0x3f, 0x44, 0x3c, 0x59, 0x75, 0x88, 0x20, + 0x1c, 0x04, 0xc3, 0x47, 0x5d, 0xcb, 0xe8, 0x4a, 0x39, 0x49, 0xcf, 0xfe, + 0xdc, 0xb2, 0xb9, 0x5f, 0x02, 0xc7, 0x31, 0xc6, 0x27, 0x8b, 0x81, 0x8c, + 0xee, 0xe5, 0x1d, 0x21, 0x8a, 0xca, 0xcf, 0x69, 0xc6, 0x45, 0xc2, 0x0b, + 0xd3, 0x45, 0x69, 0x65, 0xf3, 0x0d, 0x1a, 0xf2, 0x73, 0xe0, 0x4c, 0x6e, + 0x5e, 0x33, 0x80, 0x7f, 0x7c, 0xa3, 0x53, 0xf6, 0x57, 0xd2, 0x9f, 0x92, + 0x1c, 0x3a, 0xea, 0xbf, 0xe2, 0x40, 0xc5, 0xae, 0x16, 0xea, 0x61, 0xe6, + 0x0f, 0x5b, 0x89, 0x7e, 0x85, 0xb2, 0x35, 0x72, 0xac, 0xfa, 0xe1, 0x3f, + 0xfe, 0x69, 0x85, 0x84, 0x3a, 0x16, 0xc2, 0x75, 0xb0, 0x33, 0x1f, 0x84, + 0x0c, 0x1c, 0x1a, 0x0b, 0xa6, 0x3c, 0xa1, 0x6c, 0x95, 0x85, 0x4f, 0xe2, + 0xb7, 0x82, 0x3b, 0x66, 0x4b, 0x52, 0xab, 0xd0, 0x3b, 0x50, 0xbb, 0xe3, + 0x8a, 0x05, 0xb8, 0x35, 0xfb, 0x9d, 0xc1, 0xf2, 0x18, 0xfc, 0x94, 0xc0, + 0xb5, 0x8d, 0x31, 0x07, 0x92, 0x37, 0x48, 0xa7, 0x89, 0xe3, 0xe5, 0xde, + 0x7d, 0x64, 0xd5, 0x13, 0xc4, 0x9f, 0x88, 0x67, 0xc1, 0x65, 0x2e, 0x26, + 0xdb, 0x8d, 0xf4, 0x3f, 0xbd, 0x9e, 0x4b, 0x3c, 0x34, 0x6b, 0x2c, 0x3f, + 0x78, 0x2d, 0x8c, 0xf0, 0xf6, 0x7e, 0xf9, 0x15, 0xdf, 0x6a, 0x03, 0x59, + 0xb6, 0xf6, 0x9a, 0xb8, 0x55, 0xa9, 0x70, 0xb3, 0xc0, 0x1b, 0xe7, 0xaf, + 0xbf, 0xd2, 0xdc, 0x4e, 0x9a, 0x93, 0xb9, 0xd2, 0x6a, 0x62, 0xca, 0x64, + 0x34, 0xed, 0x5a, 0x3f, 0x56, 0xa5, 0xc8, 0x4d, 0xd9, 0x2f, 0x33, 0xc2, + 0x44, 0xd6, 0xc4, 0xe8, 0xcc, 0xa1, 0x93, 0xc6, 0xb6, 0x30, 0x21, 0x0c, + 0x44, 0x75, 0xd8, 0x67, 0x68, 0x8f, 0xc7, 0xb3, 0x84, 0xa4, 0x12, 0x53, + 0x74, 0xd9, 0x4a, 0xb0, 0x53, 0xde, 0x78, 0x28, 0x67, 0xf0, 0x08, 0x7f, + 0x96, 0x29, 0x8d, 0x39, 0xb0, 0xcb, 0xb5, 0xa6, 0x91, 0x9c, 0x1c, 0xe9, + 0x39, 0x49, 0xa5, 0x16, 0x16, 0x28, 0xfb, 0xf8, 0xfd, 0x4d, 0x21, 0x0e, + 0xc0, 0x15, 0xcf, 0x7e, 0x2b, 0x48, 0x18, 0x4e, 0xf3, 0xd1, 0xa2, 0x69, + 0x0f, 0x1e, 0x4c, 0x89, 0xf9, 0x2c, 0xff, 0x7d, 0xde, 0x5e, 0x24, 0x59, + 0x57, 0x47, 0xf2, 0x5c, 0x27, 0x82, 0x03, 0xef, 0xb8, 0x5a, 0x86, 0xa6, + 0xf9, 0xb4, 0x44, 0xfb, 0x39, 0x9b, 0xc8, 0xce, 0x9f, 0x36, 0x16, 0x11, + 0x5c, 0x8c, 0x57, 0xef, 0x52, 0xc1, 0x61, 0xed, 0xab, 0x22, 0x93, 0x78, + 0xa5, 0xe7, 0x53, 0x96, 0xdc, 0xcf, 0xe6, 0xab, 0xa7, 0x35, 0x33, 0x58, + 0x2f, 0x6d, 0x0c, 0xce, 0xe0, 0xd5, 0x08, 0x08, 0x2e, 0x81, 0x14, 0x26, + 0x11, 0x19, 0xbe, 0x82, 0x6a, 0x62, 0x35, 0x27, 0x26, 0x2d, 0x14, 0x15, + 0x77, 0xb3, 0xf0, 0x8e, 0x79, 0x20, 0x12, 0xd0, 0xa2, 0xe5, 0x66, 0x46, + 0xe4, 0xe3, 0x9a, 0x13, 0xac, 0x9b, 0x28, 0xd9, 0x92, 0x7e, 0xb9, 0x24, + 0xb5, 0x36, 0x28, 0xcf, 0xeb, 0x6f, 0xb8, 0x70, 0xb6, 0x21, 0x33, 0xb6, + 0xaa, 0x41, 0x94, 0xbd, 0x0b, 0x60, 0xc9, 0x6e, 0x23, 0x12, 0xb4, 0x4c, + 0x8d, 0xba, 0x5d, 0x05, 0xb1, 0xd5, 0x14, 0x1d, 0x98, 0x70, 0x1d, 0x66, + 0xdb, 0x52, 0x98, 0xd0, 0xab, 0xd9, 0xd3, 0xbe, 0x7e, 0x72, 0xd2, 0x02, + 0x05, 0x22, 0xbb, 0x98, 0xca, 0xa2, 0xab, 0x4c, 0x71, 0x1b, 0x27, 0x29, + 0x93, 0xff, 0xd1, 0xb6, 0x8d, 0xa1, 0xe7, 0x09, 0x3a, 0x95, 0x1e, 0x17, + 0x53, 0xe9, 0x13, 0x52, 0x4d, 0x7d, 0xa4, 0x33, 0x47, 0x8c, 0x28, 0x5a, + 0x2c, 0x12, 0x88, 0xb3, 0xf7, 0x1e, 0xdf, 0x93, 0x3a, 0xed, 0xe2, 0xc6, + 0x42, 0xa1, 0x66, 0x58, 0xa9, 0x4a, 0x6b, 0x78, 0xb5, 0x8a, 0x6e, 0x2e, + 0xd1, 0x15, 0x86, 0x51, 0x18, 0x90, 0x50, 0x71, 0xbd, 0x6b, 0xd4, 0x4d, + 0x6c, 0x43, 0xc0, 0x55, 0x88, 0x5a, 0x2d, 0x2d, 0xcc, 0xc6, 0x9e, 0xb8, + 0xcf, 0xf2, 0x9a, 0xd0, 0xbe, 0xf0, 0xb7, 0xa9, 0x5c, 0xb4, 0x1c, 0xd4, + 0xdd, 0x45, 0xe4, 0xfa, 0x59, 0xbb, 0x50, 0x7c, 0xa3, 0x65, 0x9e, 0x3f, + 0x14, 0x34, 0xf4, 0x06, 0x20, 0x5f, 0x96, 0x46, 0x58, 0x78, 0x58, 0x3d, + 0x19, 0x40, 0x13, 0x0b, 0xbd, 0xdf, 0xe4, 0xc0, 0x9b, 0x4d, 0x73, 0xe4, + 0xa3, 0x79, 0x3d, 0xa1, 0x55, 0x47, 0xb3, 0x41, 0x34, 0x61, 0xe3, 0x65, + 0x86, 0xbb, 0x30, 0x8d, 0xc5, 0x3e, 0x3d, 0x14, 0x12, 0x9e, 0x63, 0x99, + 0xb7, 0xc3, 0x36, 0xb0, 0x6e, 0xce, 0x9f, 0xe6, 0xa9, 0x02, 0x11, 0xda, + 0x2b, 0x7c, 0xf5, 0xbb, 0x90, 0xf0, 0xe7, 0x47, 0xba, 0x32, 0x26, 0x96, + 0x56, 0x52, 0x14, 0x2c, 0x26, 0x97, 0xd8, 0xf8, 0x99, 0xea, 0xee, 0x7e, + 0xc2, 0xb8, 0xfe, 0x48, 0xe4, 0xe2, 0x8d, 0x44, 0x42, 0x0b, 0x8c, 0x5d, + 0xe9, 0x67, 0x63, 0x66, 0x5b, 0x38, 0xd4, 0x99, 0x31, 0xeb, 0x66, 0x18, + 0x36, 0x52, 0xca, 0xad, 0x0f, 0x6b, 0x80, 0x94, 0xfa, 0xf6, 0xba, 0x68, + 0x1a, 0xd5, 0xf2, 0x5f, 0x6f, 0x19, 0xff, 0x74, 0x35, 0x1d, 0xe5, 0x6a, + 0x3f, 0xa4, 0x5c, 0x49, 0x0c, 0xa5, 0x6c, 0xce, 0xda, 0xa3, 0xa9, 0x3e, + 0x6e, 0x7a, 0x35, 0xe8, 0x46, 0x3f, 0xab, 0x23, 0x95, 0xba, 0x2c, 0xa9, + 0xdf, 0x78, 0xeb, 0x8b, 0x57, 0x9b, 0xf5, 0x3b, 0x56, 0x3f, 0x80, 0x74, + 0x8d, 0x87, 0x98, 0xe0, 0x63, 0x5a, 0x7b, 0xfd, 0x01, 0x83, 0x25, 0xda, + 0xbc, 0xf4, 0xcf, 0xb7, 0x4c, 0xe6, 0xf5, 0x9c, 0x5c, 0x57, 0xb3, 0xc9, + 0xc6, 0x99, 0x29, 0x40, 0x73, 0xf5, 0x15, 0x02, 0x4a, 0x3c, 0x54, 0xc9, + 0xc5, 0xd1, 0xa3, 0x5a, 0xf7, 0x07, 0xbe, 0xe7, 0x7c, 0x04, 0xf1, 0xb4, + 0x92, 0xce, 0x5a, 0xce, 0xe9, 0x44, 0x9e, 0xb9, 0x36, 0xc7, 0x1e, 0x05, + 0x69, 0xaf, 0xe5, 0x0c, 0x51, 0x7f, 0x3a, 0x86, 0xf3, 0x75, 0xcd, 0x5e, + 0x97, 0x89, 0xa3, 0xb0, 0x98, 0xfd, 0x01, 0xb3, 0xd2, 0x65, 0x1f, 0x6e, + 0x6d, 0x8c, 0x7b, 0x6a, 0xdc, 0x7e, 0x0e, 0xe5, 0x64, 0xdf, 0x7f, 0x47, + 0xce, 0x87, 0xf4, 0xe1, 0x39, 0x78, 0x21, 0xfb, 0x17, 0x30, 0x0c, 0xa3, + 0x4f, 0x92, 0xa8, 0xe8, 0xf4, 0x2a, 0x30, 0xb6, 0xe5, 0xb4, 0x3e, 0x6b, + 0x25, 0x74, 0x3e, 0x50, 0x0a, 0x03, 0xd2, 0x77, 0x5b, 0x3e, 0xc1, 0xee, + 0xd7, 0x59, 0x81, 0x93, 0x83, 0xd5, 0x9f, 0xa0, 0xc5, 0x8c, 0x80, 0x0e, + 0xa4, 0x4e, 0x59, 0xba, 0x1a, 0x95, 0x49, 0xe2, 0x1d, 0x0b, 0xcf, 0xe2, + 0x0b, 0x0a, 0xae, 0x38, 0x61, 0x3c, 0xb4, 0xc7, 0x0a, 0xd3, 0xfa, 0x6e, + 0xe6, 0xc9, 0xf3, 0x95, 0x1f, 0x43, 0x31, 0x0d, 0x0a, 0x2b, 0x2b, 0x9d, + 0xb2, 0x6b, 0xe4, 0xd4, 0x8d, 0x23, 0x77, 0xfb, 0xa2, 0xfe, 0xdc, 0x79, + 0xab, 0x53, 0x39, 0x5a, 0xf0, 0xef, 0xa0, 0xbd, 0xd5, 0xbb, 0x94, 0xea, + 0xe3, 0x5d, 0xa1, 0xf1, 0x65, 0xca, 0x52, 0x26, 0x53, 0x2d, 0xbd, 0x6e, + 0x6f, 0x1d, 0xd4, 0x2f, 0x56, 0x79, 0xf1, 0x71, 0x7d, 0x85, 0xb8, 0x26, + 0xb7, 0x13, 0xbf, 0xa3, 0xd1, 0xe1, 0x65, 0xb7, 0xb7, 0x85, 0x57, 0x09, + 0xc8, 0xde, 0xa3, 0x53, 0x10, 0xa6, 0x53, 0x62, 0xd4, 0x1d, 0xb2, 0xae, + 0xd2, 0x8f, 0x33, 0xc6, 0x6c, 0xcf, 0x96, 0x44, 0x9b, 0xb2, 0xbe, 0x8f, + 0x90, 0xee, 0x7b, 0x59, 0x93, 0x7f, 0x64, 0x5b, 0x49, 0x5f, 0x12, 0x8d, + 0x9b, 0x0b, 0x6d, 0xa9, 0xe8, 0x74, 0xf8, 0x35, 0xeb, 0x55, 0x60, 0x21, + 0x85, 0xde, 0x8f, 0xe5, 0x55, 0x7c, 0x5f, 0x7e, 0xbc, 0x03, 0xc9, 0xbe, + 0xbb, 0xbe, 0x16, 0x57, 0xf8, 0xc0, 0x05, 0x8f, 0xb8, 0x88, 0xc5, 0x9e, + 0xdb, 0x4a, 0x55, 0x77, 0x2d, 0x2d, 0x3b, 0xc4, 0xd3, 0xda, 0x3e, 0x37, + 0x62, 0x2a, 0x53, 0xf0, 0xd0, 0x46, 0x34, 0x8f, 0x71, 0xf6, 0xe0, 0x63, + 0x6d, 0xef, 0x32, 0xe5, 0x2c, 0x67, 0xf5, 0x50, 0x3d, 0xad, 0xad, 0x23, + 0xa9, 0xe9, 0xf8, 0x11, 0x86, 0xde, 0x4e, 0xd6, 0x26, 0xb4, 0x60, 0x19, + 0x88, 0x46, 0x83, 0xbb, 0x85, 0x00, 0x41, 0x5b, 0x4a, 0x80, 0x0a, 0x92, + 0x74, 0xea, 0xa7, 0xcd, 0xc3, 0x84, 0x11, 0x74, 0x4a, 0xb2, 0x24, 0x9b, + 0xbd, 0x61, 0x19, 0x65, 0x87, 0xd9, 0xd3, 0x03, 0xe9, 0x6a, 0xb7, 0x58, + 0xac, 0xc1, 0x75, 0x1c, 0x6e, 0xd9, 0xe5, 0xf5, 0xff, 0x63, 0xf7, 0x4a, + 0x17, 0x12, 0xf7, 0xdb, 0xd5, 0xd8, 0x8d, 0x7c, 0xb7, 0xed, 0x5e, 0xc1, + 0xa6, 0xcf, 0xce, 0x3d, 0x09, 0x46, 0x32, 0x77, 0xff, 0xf2, 0x9f, 0x4a, + 0x6d, 0x29, 0x4d, 0xa8, 0xf0, 0x01, 0x77, 0x13, 0x5d, 0x48, 0xc0, 0x00, + 0x73, 0xa8, 0xe0, 0xf7, 0x46, 0xd8, 0x9e, 0xe4, 0xf3, 0x54, 0xef, 0xfd, + 0xce, 0x49, 0x26, 0x6c, 0xa2, 0xb8, 0xf5, 0x94, 0xed, 0x16, 0x4d, 0xa2, + 0x33, 0x50, 0x91, 0x3a, 0xb5, 0x03, 0x20, 0x15, 0x9e, 0xc0, 0x3f, 0x63, + 0xd3, 0xb5, 0xba, 0xe8, 0x23, 0xb2, 0xff, 0x35, 0xf2, 0x3d, 0x53, 0x58, + 0x6d, 0xb6, 0xd1, 0xd7, 0x37, 0xfd, 0x2d, 0x97, 0x94, 0x3e, 0x0c, 0x4c, + 0xfe, 0xd4, 0xa1, 0xe3, 0x0c, 0x3a, 0xab, 0x91, 0x0d, 0x50, 0x63, 0x34, + 0xb9, 0x0f, 0xf6, 0x4b, 0x9c, 0xf6, 0xb9, 0x3f, 0xc4, 0x01, 0x8c, 0x1a, + 0xd5, 0x25, 0xdd, 0xbe, 0x93, 0x75, 0x5e, 0xc4, 0xf2, 0x8a, 0xb3, 0xc1, + 0x4a, 0xe5, 0x37, 0x53, 0x8b, 0x13, 0x9a, 0x47, 0x6e, 0x4a, 0x14, 0x99, + 0x64, 0x9d, 0x4f, 0xaf, 0xd5, 0x73, 0x57, 0x1e, 0xba, 0xa8, 0x80, 0x4c, + 0xb6, 0xca, 0x37, 0xed, 0x3d, 0xde, 0xbf, 0xc1, 0x7b, 0x34, 0xfd, 0xc4, + 0x37, 0xf9, 0x92, 0x86, 0xfa, 0x7a, 0x29, 0xce, 0xd6, 0x33, 0x09, 0x9c, + 0x89, 0xf9, 0xdc, 0x95, 0x72, 0x84, 0x2e, 0x67, 0x46, 0x42, 0x76, 0xe7, + 0x44, 0x2f, 0xf1, 0xd0, 0xe5, 0x90, 0x43, 0xa2, 0xf7, 0xd1, 0x16, 0x59, + 0xb2, 0xea, 0x12, 0x69, 0x9d, 0xc4, 0x6e, 0xbd, 0xd8, 0x97, 0x34, 0x25, + 0x7c, 0x88, 0x7e, 0x09, 0x09, 0x84, 0x3d, 0x7d, 0xac, 0xff, 0xf6, 0x68, + 0x8a, 0x9b, 0x50, 0xa0, 0x6a, 0x05, 0x91, 0x77, 0xd8, 0x53, 0x73, 0x4d, + 0x50, 0x9f, 0xa1, 0x74, 0x93, 0x36, 0xd2, 0xd9, 0xe5, 0x11, 0x79, 0x05, + 0x5c, 0x32, 0x27, 0x99, 0x3d, 0x78, 0x02, 0x5c, 0x2d, 0x7c, 0xfd, 0x66, + 0x8c, 0xd2, 0xa9, 0x1e, 0x85, 0x23, 0xb7, 0xa6, 0xdf, 0x76, 0x9b, 0x07, + 0x0c, 0xde, 0xde, 0x35, 0x06, 0x4e, 0x29, 0xa8, 0xfc, 0xfc, 0x30, 0x07, + 0xbf, 0xa9, 0xf6, 0x6b, 0x31, 0xff, 0xdb, 0xac, 0x60, 0x80, 0x35, 0x83, + 0x66, 0xb4, 0x3f, 0xef, 0x1e, 0x3a, 0xb4, 0x81, 0x9d, 0xe2, 0x26, 0x66, + 0x5f, 0x3c, 0x8d, 0x07, 0x55, 0x2c, 0x4b, 0xd6, 0xae, 0xa7, 0x70, 0x7b, + 0x7f, 0x97, 0xe9, 0xe0, 0xab, 0x1d, 0xc3, 0x95, 0x9e, 0x79, 0xff, 0xc8, + 0x91, 0x26, 0xd8, 0xb3, 0x7e, 0xbf, 0xa1, 0x16, 0x5c, 0xab, 0x2f, 0xe7, + 0x6a, 0xb5, 0x13, 0x4f, 0xc2, 0x24, 0xc2, 0xb9, 0x6c, 0x4a, 0x46, 0xda, + 0x76, 0x6c, 0x49, 0x7d, 0x3c, 0xbe, 0xe9, 0x92, 0x1c, 0xf3, 0xc3, 0xd6, + 0x8d, 0x2b, 0xcd, 0x17, 0x03, 0x43, 0xe7, 0x1c, 0x14, 0x1f, 0x8d, 0x95, + 0xba, 0xf0, 0x70, 0x24, 0x9e, 0x06, 0x12, 0x14, 0x5d, 0x69, 0x09, 0xef, + 0xde, 0xd9, 0x80, 0xeb, 0x7b, 0x7e, 0x0a, 0x9f, 0x70, 0x9d, 0x1c, 0x1b, + 0xa3, 0xa4, 0x42, 0xa4, 0x09, 0xe9, 0xec, 0xda, 0x6a, 0x90, 0x9c, 0xb1, + 0x55, 0xe9, 0xe8, 0xff, 0x2e, 0xdf, 0x72, 0xb7, 0x1b, 0xe4, 0xa7, 0x5d, + 0x28, 0xd0, 0xc7, 0x53, 0x74, 0x13, 0xd1, 0x5f, 0x2b, 0x21, 0x89, 0xe9, + 0xc5, 0xb7, 0x60, 0x1f, 0xf3, 0x02, 0x63, 0xe1, 0x49, 0x5e, 0x95, 0xd7, + 0xfb, 0xc2, 0xfe, 0x46, 0x1c, 0xea, 0xe2, 0xe8, 0xdd, 0xba, 0x00, 0xc9, + 0x22, 0xd6, 0xfa, 0xd7, 0xc6, 0xee, 0x08, 0xd0, 0xce, 0x5a, 0x24, 0x77, + 0xb2, 0x27, 0xd5, 0x7b, 0xb1, 0x04, 0xd5, 0x91, 0x3b, 0xdb, 0xec, 0x30, + 0xca, 0xb4, 0x65, 0x50, 0xd6, 0xc5, 0xad, 0x3f, 0x97, 0x83, 0xad, 0x8e, + 0xbb, 0x23, 0xdc, 0x06, 0x86, 0xd7, 0x89, 0xd9, 0xae, 0x2d, 0xf2, 0x6b, + 0xb2, 0x4d, 0x74, 0x4c, 0x7e, 0x03, 0xed, 0xe9, 0xed, 0x8b, 0x16, 0x82, + 0xc5, 0x28, 0x33, 0x87, 0xcf, 0x9d, 0xc9, 0x55, 0x8b, 0x83, 0x17, 0x58, + 0xce, 0xca, 0xbc, 0x42, 0x89, 0x97, 0xc5, 0x1f, 0x27, 0x67, 0x9c, 0xf4, + 0x0d, 0xc9, 0xbe, 0x9a, 0x53, 0x14, 0x56, 0x9b, 0xe9, 0xc3, 0x9b, 0x3f, + 0xdd, 0x3e, 0x1a, 0x68, 0x37, 0x57, 0x5a, 0x69, 0x9a, 0xeb, 0xd1, 0x02, + 0x21, 0xc5, 0x7b, 0x7b, 0xd3, 0x95, 0x5a, 0xca, 0xf8, 0x72, 0xaa, 0xe1, + 0x9a, 0x15, 0x5b, 0xa6, 0xf0, 0x06, 0x9d, 0x89, 0xb0, 0xab, 0x6c, 0xfd, + 0xbe, 0x53, 0x6c, 0x1b, 0x7e, 0xc1, 0x5e, 0xb9, 0x4a, 0x2e, 0xe7, 0xba, + 0xf5, 0xad, 0x2b, 0x56, 0x80, 0xd3, 0xdb, 0xc5, 0xee, 0x02, 0x80, 0xa0, + 0xb6, 0xc4, 0x9c, 0xf1, 0x85, 0x03, 0xb9, 0x69, 0xc5, 0x20, 0x14, 0x56, + 0x64, 0xb4, 0xc9, 0xeb, 0x34, 0xf4, 0xb7, 0x60, 0xff, 0xe3, 0x07, 0x6f, + 0xab, 0x98, 0x8b, 0x0e, 0xc2, 0x83, 0x13, 0xff, 0xbe, 0x7e, 0x29, 0x92, + 0x0d, 0xdd, 0x32, 0x72, 0x33, 0x07, 0x08, 0xd2, 0x88, 0xde, 0xbe, 0x2c, + 0xc9, 0x16, 0x9b, 0xa1, 0xe5, 0x02, 0x91, 0x0d, 0xfe, 0x1f, 0x67, 0x67, + 0xd0, 0x4c, 0xb2, 0x2d, 0x60, 0xfc, 0x91, 0x37, 0xe1, 0x30, 0xe2, 0x34, + 0xf2, 0x1a, 0x5f, 0x37, 0x98, 0x1d, 0xdc, 0xda, 0x2a, 0x51, 0xcd, 0xf8, + 0xd8, 0xd7, 0xe8, 0xf3, 0x2c, 0xf6, 0xa8, 0xb4, 0x63, 0x8f, 0x48, 0x01, + 0x85, 0xf0, 0xe9, 0x32, 0x9f, 0x6a, 0xd6, 0xe2, 0xcb, 0xcd, 0xb5, 0xf2, + 0x52, 0x63, 0x92, 0x56, 0xa8, 0x96, 0x18, 0x8c, 0x28, 0xa9, 0x7d, 0x4a, + 0x7e, 0xde, 0x46, 0x9f, 0xd0, 0x3f, 0x9b, 0xb9, 0x9f, 0x01, 0x32, 0xd3, + 0x63, 0x3b, 0x2c, 0xd3, 0x5b, 0x60, 0xf1, 0x85, 0x85, 0xa2, 0x80, 0xc0, + 0x2e, 0xa9, 0x35, 0x23, 0x9c, 0x6b, 0x5f, 0xac, 0xb0, 0xb2, 0x94, 0x5c, + 0x30, 0x74, 0xf3, 0x24, 0xb1, 0x05, 0xb5, 0x4c, 0x6e, 0x97, 0x75, 0x4b, + 0xac, 0x6a, 0xb4, 0x5c, 0x82, 0x22, 0x5e, 0xc5, 0x6f, 0x47, 0x4c, 0x89, + 0xe6, 0xea, 0xfc, 0xf7, 0x87, 0x9a, 0x60, 0xc4, 0x6a, 0x3b, 0x3b, 0xc5, + 0x88, 0xcc, 0xa6, 0x7c, 0xba, 0x3e, 0xcb, 0x32, 0x77, 0x5e, 0x54, 0x3f, + 0x0d, 0x07, 0x1a, 0x6b, 0xce, 0x4f, 0xd1, 0xff, 0xa6, 0x54, 0x19, 0x0a, + 0xc4, 0xcc, 0x0a, 0x21, 0x53, 0x10, 0x9a, 0x3a, 0xad, 0x75, 0x61, 0xbd, + 0xe2, 0x20, 0x11, 0xc4, 0x04, 0x72, 0x15, 0x24, 0x67, 0x2c, 0x9e, 0x3c, + 0x3d, 0x31, 0xe5, 0x18, 0xc0, 0x68, 0x9d, 0x03, 0x89, 0x02, 0x0d, 0x89, + 0x49, 0x92, 0x4f, 0x0e, 0x13, 0x7a, 0x70, 0xd9, 0x77, 0x51, 0x6e, 0xc1, + 0xcc, 0xc5, 0xf8, 0xa1, 0x35, 0x9e, 0xc6, 0xdb, 0xc9, 0x87, 0x80, 0xdd, + 0x94, 0x42, 0x46, 0x4f, 0xfe, 0x7f, 0x0d, 0x05, 0xca, 0x21, 0x33, 0x64, + 0x63, 0xe0, 0x62, 0xde, 0xb3, 0x1e, 0xa3, 0xc5, 0x9d, 0x5c, 0x58, 0x5d, + 0x76, 0x42, 0xcb, 0xda, 0xc6, 0x12, 0x7e, 0x97, 0xa6, 0xf7, 0xcb, 0xed, + 0x5d, 0x3a, 0x87, 0x31, 0x05, 0x1d, 0x8b, 0xba, 0x75, 0x33, 0x8d, 0x86, + 0x04, 0xec, 0xbe, 0x7c, 0xa1, 0x5a, 0x1a, 0x77, 0x79, 0x18, 0x4e, 0x8b, + 0x85, 0xe0, 0xac, 0x0f, 0x71, 0x4f, 0xd1, 0x19, 0xb8, 0x31, 0x3e, 0x8d, + 0x47, 0x86, 0x06, 0x36, 0xa2, 0xde, 0xe1, 0x14, 0x5a, 0xf2, 0xb2, 0xc5, + 0x48, 0x89, 0x69, 0xf1, 0x2f, 0x96, 0x25, 0x44, 0x9c, 0x5e, 0xe6, 0x63, + 0x8c, 0xd1, 0xa6, 0x2f, 0xfc, 0x79, 0xe2, 0xbb, 0x42, 0xde, 0x69, 0x19, + 0x52, 0xda, 0x68, 0xd2, 0x4e, 0xe6, 0xd3, 0x07, 0x1f, 0x8e, 0x48, 0x70, + 0xdb, 0xc8, 0x6d, 0xb6, 0xe4, 0xed, 0x4d, 0x99, 0x19, 0x47, 0x2b, 0x53, + 0xf6, 0x7f, 0xbe, 0x18, 0x8f, 0xe8, 0xa5, 0x70, 0x20, 0xfc, 0x67, 0xee, + 0x6e, 0xbd, 0x4e, 0x3e, 0xc3, 0x35, 0x91, 0x51, 0x9a, 0xdf, 0x45, 0xb0, + 0x67, 0xf4, 0x0b, 0x8c, 0xcd, 0x37, 0xf6, 0xee, 0x96, 0xa0, 0xc1, 0xd3, + 0x3c, 0xa1, 0x6d, 0xbd, 0x50, 0xa6, 0xa8, 0xba, 0x7a, 0xbf, 0x4a, 0x5e, + 0xb3, 0x26, 0x9a, 0xb2, 0x5c, 0xe3, 0xf0, 0x1d, 0x4d, 0x6a, 0xbc, 0xc0, + 0x21, 0xb4, 0x09, 0x54, 0xdc, 0xf1, 0x47, 0x67, 0xc1, 0x8c, 0x95, 0x80, + 0x2b, 0x13, 0x40, 0x9a, 0x67, 0x8e, 0xc1, 0x87, 0xee, 0x4d, 0x80, 0xa6, + 0xe6, 0xfc, 0x4d, 0xc9, 0xf2, 0x33, 0x66, 0x34, 0xda, 0xfe, 0x38, 0x29, + 0x77, 0xc7, 0x8f, 0x4c, 0x4e, 0x36, 0x8f, 0xa2, 0x56, 0x3a, 0xdd, 0xea, + 0xcb, 0x52, 0x49, 0x67, 0xcf, 0xae, 0x71, 0x1d, 0xc2, 0xeb, 0x1f, 0x3b, + 0x99, 0x1e, 0x7c, 0xb4, 0x90, 0xaa, 0xc2, 0x73, 0xc9, 0xbf, 0x72, 0x9a, + 0xb5, 0x94, 0xab, 0x98, 0xfd, 0x3e, 0xb2, 0xb6, 0xf1, 0xb9, 0x12, 0xf2, + 0xa2, 0x5d, 0x28, 0xdd, 0xfa, 0x90, 0x6f, 0xad, 0x86, 0xb7, 0x9c, 0x15, + 0xea, 0xe4, 0x41, 0x8f, 0xff, 0x89, 0x7f, 0xe7, 0xa8, 0xce, 0xf0, 0xaa, + 0x11, 0x57, 0xa3, 0xa6, 0xc8, 0xb7, 0x80, 0x4d, 0x3c, 0xf1, 0xf2, 0x94, + 0xa6, 0x45, 0x57, 0x54, 0xc6, 0x4d, 0x15, 0xd6, 0x67, 0xae, 0x30, 0x8f, + 0x9c, 0x1c, 0x46, 0xa7, 0x8e, 0xf9, 0xe8, 0x4c, 0xd0, 0x0e, 0x81, 0xc3, + 0xf2, 0xee, 0xd1, 0xa5, 0xa7, 0x22, 0xe9, 0xca, 0x05, 0xcb, 0x38, 0x60, + 0x22, 0x1d, 0xb8, 0x7e, 0x4c, 0x8a, 0x18, 0x35, 0xd1, 0x98, 0xfb, 0x2b, + 0x52, 0x15, 0xe7, 0x2b, 0x9a, 0x10, 0x83, 0x4b, 0xa9, 0xfc, 0x9a, 0xd0, + 0xeb, 0x98, 0x56, 0xa9, 0xd2, 0x5d, 0x0f, 0xf4, 0xcc, 0x48, 0xe8, 0x1d, + 0xc4, 0x31, 0x05, 0x91, 0xf2, 0xaf, 0x41, 0x9b, 0xb7, 0xd4, 0xcc, 0x3c, + 0x68, 0x73, 0x24, 0xbe, 0x91, 0x42, 0xab, 0x8e, 0x95, 0x78, 0x15, 0x92, + 0x2b, 0x63, 0x2c, 0x4d, 0xd4, 0xe8, 0x67, 0xe4, 0x3a, 0xbb, 0x08, 0xf2, + 0xe1, 0x25, 0xe3, 0x24, 0x5f, 0x96, 0x26, 0xed, 0x03, 0x9f, 0xe5, 0x3f, + 0x28, 0xf0, 0xea, 0x87, 0xb2, 0x8b, 0x8f, 0xf8, 0x17, 0xf2, 0x3f, 0x83, + 0x59, 0xab, 0xfc, 0x05, 0xab, 0x2a, 0x6f, 0x83, 0x89, 0x82, 0xa1, 0x0d, + 0xb2, 0x00, 0xb1, 0xd1, 0x7b, 0x70, 0xcf, 0x5d, 0xa3, 0x61, 0x11, 0x11, + 0x87, 0x02, 0x51, 0xbe, 0x35, 0xc6, 0x13, 0xe5, 0xc5, 0x3f, 0x9f, 0xe0, + 0x97, 0x4c, 0xd6, 0xaf, 0x37, 0x02, 0x90, 0xa4, 0x79, 0x16, 0x55, 0xad, + 0x74, 0x33, 0x4d, 0x41, 0xdb, 0x93, 0xbf, 0x12, 0xf4, 0xf4, 0x54, 0x85, + 0x6d, 0x49, 0x00, 0x26, 0x36, 0xa5, 0xd6, 0x1c, 0x48, 0x9d, 0x66, 0xcb, + 0xf4, 0x25, 0xf7, 0xbf, 0x16, 0xb9, 0x21, 0xac, 0xb4, 0xcd, 0x67, 0x79, + 0x71, 0x21, 0x48, 0x87, 0x38, 0x25, 0xd6, 0x2c, 0xb7, 0xbf, 0xdf, 0x06, + 0x3d, 0x26, 0x3c, 0x2f, 0x16, 0x30, 0xb3, 0x6b, 0x86, 0xda, 0x52, 0xbb, + 0x3d, 0xe4, 0x46, 0xb7, 0xc7, 0xb7, 0x15, 0xcc, 0x35, 0xdc, 0x32, 0x82, + 0x66, 0xc0, 0x23, 0xf9, 0x61, 0xa8, 0x8d, 0x8e, 0x9d, 0xc8, 0xe7, 0x1a, + 0x2b, 0xf8, 0x2f, 0x4c, 0xc7, 0x05, 0x20, 0xd0, 0x52, 0xb6, 0xea, 0x2f, + 0xc0, 0xc4, 0xe5, 0x22, 0x45, 0x55, 0x29, 0xf2, 0xcb, 0xd7, 0x43, 0x5a, + 0xa5, 0x55, 0xcd, 0x0b, 0x6d, 0x51, 0x26, 0x63, 0xb6, 0xb7, 0xa1, 0x28, + 0xb9, 0xbc, 0xa0, 0x3a, 0xd6, 0xa6, 0xfc, 0xbb, 0xf8, 0x9b, 0xf1, 0x44, + 0x16, 0xe5, 0x6e, 0xd7, 0xc1, 0xdc, 0xfa, 0xe4, 0x57, 0xd2, 0x27, 0x6e, + 0x8f, 0x03, 0xfa, 0xef, 0xd9, 0xf2, 0xb4, 0xe5, 0x68, 0x2b, 0x2c, 0xdd, + 0x07, 0x46, 0x6c, 0xab, 0xc1, 0x4c, 0xe4, 0xac, 0x71, 0xdc, 0x0b, 0x8a, + 0x1d, 0xf3, 0xa5, 0x82, 0x45, 0x5e, 0x20, 0x4a, 0xb1, 0x0c, 0xc1, 0x63, + 0xaf, 0xfa, 0x2d, 0x49, 0x09, 0x65, 0xa1, 0x1a, 0xc7, 0x12, 0xa7, 0x18, + 0xdb, 0xb8, 0x3a, 0xa8, 0x4d, 0x96, 0x88, 0xc3, 0x31, 0x15, 0x04, 0xd0, + 0x30, 0xe2, 0x09, 0xaa, 0x48, 0xf6, 0x0f, 0x20, 0xab, 0x05, 0x41, 0x43, + 0x23, 0x52, 0x28, 0x2f, 0xf9, 0x97, 0x0d, 0x00, 0xcd, 0x74, 0x4d, 0x10, + 0x80, 0x28, 0x79, 0x8b, 0x6d, 0x43, 0xe0, 0x79, 0x2e, 0x3d, 0xd7, 0x81, + 0xf8, 0x64, 0x34, 0x87, 0xbc, 0x00, 0x09, 0x07, 0xbb, 0x19, 0xa2, 0x29, + 0xbf, 0x57, 0xd4, 0x00, 0xe3, 0x20, 0x2e, 0xae, 0xc6, 0xfd, 0x4e, 0x97, + 0xee, 0x97, 0x71, 0x72, 0x8d, 0x81, 0xfc, 0x72, 0xa8, 0x8a, 0x20, 0xef, + 0xcb, 0xa5, 0x4c, 0x26, 0x47, 0xab, 0x17, 0x1b, 0x41, 0xd7, 0xbf, 0x19, + 0x6b, 0xa2, 0x89, 0xcd, 0x58, 0x83, 0x11, 0xb3, 0x42, 0x8e, 0x93, 0xed, + 0x17, 0x1b, 0x14, 0xe6, 0x48, 0xae, 0x88, 0x0c, 0xf2, 0x26, 0x53, 0x20, + 0x67, 0xd7, 0xae, 0x6b, 0xc1, 0x4b, 0x82, 0x5d, 0xec, 0x9e, 0x46, 0xfb, + 0xf1, 0xe8, 0x86, 0x10, 0x83, 0x88, 0x12, 0xf5, 0xb6, 0xdd, 0x00, 0x26, + 0x83, 0xf4, 0x2c, 0x98, 0x7d, 0xbd, 0xda, 0xdb, 0x42, 0xca, 0x37, 0x1b, + 0xd1, 0xe5, 0x85, 0xea, 0x5b, 0x80, 0xcc, 0xc5, 0xe7, 0x03, 0x2a, 0xdb, + 0xbc, 0x26, 0x24, 0xfa, 0xaa, 0xa2, 0x51, 0xbe, 0xd8, 0x5f, 0xfe, 0x09, + 0x06, 0xa0, 0x99, 0x11, 0xeb, 0xf8, 0xa4, 0xaf, 0xec, 0x7f, 0x89, 0x0e, + 0xd6, 0x3d, 0x4e, 0xed, 0xb3, 0x5b, 0xe1, 0x04, 0xd7, 0xdf, 0x5b, 0x56, + 0x4e, 0xc5, 0x34, 0xc5, 0xf5, 0x69, 0x91, 0xe3, 0xb2, 0x11, 0xb8, 0x51, + 0x7c, 0xae, 0x87, 0xeb, 0xb5, 0x73, 0xac, 0xce, 0x90, 0xc3, 0x41, 0x89, + 0x94, 0x0c, 0x6d, 0x3d, 0xe2, 0xfe, 0x80, 0x65, 0x0b, 0xa0, 0x80, 0x99, + 0xaa, 0xa2, 0x9f, 0x73, 0xb6, 0x7a, 0x3b, 0x00, 0x5a, 0xc1, 0x02, 0x4d, + 0x21, 0x2d, 0x03, 0xd1, 0x5e, 0xa6, 0xa8, 0x3c, 0x1b, 0xec, 0x5d, 0xa4, + 0xd7, 0x3d, 0x2b, 0xa6, 0xd4, 0xc0, 0xa7, 0xd3, 0xb9, 0x00, 0xef, 0x10, + 0xd5, 0x2c, 0xbc, 0xa4, 0xa8, 0xe1, 0xb0, 0x24, 0x70, 0xc4, 0x7c, 0x2a, + 0x39, 0xc8, 0xed, 0x68, 0x7e, 0x0d, 0x6f, 0xc3, 0x90, 0x5d, 0x59, 0xe8, + 0x7b, 0x0d, 0xa6, 0x5f, 0x62, 0x2d, 0xfc, 0xad, 0x6e, 0xf2, 0x26, 0x6f, + 0x0c, 0x5f, 0xce, 0x4a, 0xcc, 0x11, 0x1d, 0x91, 0x57, 0x74, 0x68, 0x7f, + 0x01, 0xde, 0x17, 0x2d, 0x70, 0x6c, 0x29, 0x47, 0xc3, 0xea, 0x82, 0x13, + 0x43, 0x3b, 0x2b, 0x3b, 0x60, 0x6e, 0xc2, 0x43, 0xfb, 0x34, 0x68, 0xbb, + 0x93, 0x40, 0xbd, 0xd8, 0xb6, 0x73, 0x66, 0x65, 0x01, 0xff, 0x65, 0xb6, + 0xda, 0xf6, 0xbb, 0xce, 0x2a, 0x78, 0x03, 0xb9, 0x6e, 0xd4, 0xc2, 0xf3, + 0x5f, 0xd4, 0x4a, 0x09, 0xa2, 0x36, 0xd5, 0xac, 0x67, 0x4d, 0xb7, 0xfa, + 0x59, 0x6b, 0xf9, 0x26, 0xd7, 0x9f, 0xbc, 0x1e, 0xba, 0x1b, 0xff, 0xbf, + 0x42, 0xe7, 0x2c, 0x21, 0x79, 0xbf, 0xd6, 0xac, 0xb5, 0x2e, 0x50, 0xa5, + 0x19, 0x46, 0x6d, 0xc1, 0x03, 0x05, 0xe9, 0x36, 0x22, 0x85, 0x8f, 0x46, + 0xba, 0x2a, 0xc4, 0x9d, 0x6a, 0x1f, 0x1d, 0x52, 0xbb, 0xc2, 0x17, 0x39, + 0x59, 0x66, 0x8d, 0x26, 0x89, 0x37, 0x12, 0x9d, 0xd2, 0x2c, 0x05, 0xc4, + 0xaf, 0x73, 0x62, 0xcb, 0xaf, 0xef, 0x20, 0x17, 0xd9, 0x3c, 0x4c, 0xe5, + 0x3f, 0x78, 0x04, 0x81, 0xdd, 0x68, 0x49, 0xc9, 0x26, 0x04, 0x21, 0xf0, + 0x4a, 0x4e, 0x28, 0x96, 0xb2, 0xf8, 0xaa, 0x25, 0x65, 0x85, 0x24, 0xd6, + 0x49, 0x11, 0xf9, 0x40, 0x30, 0x38, 0x9b, 0xc7, 0xde, 0x6b, 0xf6, 0x5d, + 0x06, 0x48, 0x56, 0xab, 0x38, 0x46, 0x65, 0x1a, 0x6c, 0x77, 0x9a, 0x20, + 0x18, 0x95, 0x2f, 0xff, 0x0b, 0xf0, 0x1d, 0xce, 0xb3, 0x89, 0xb4, 0x5a, + 0xbb, 0x2d, 0x46, 0xc1, 0xe0, 0x38, 0xb3, 0x1f, 0xd2, 0xa3, 0x97, 0x86, + 0xb2, 0x79, 0x39, 0xf2, 0x17, 0x53, 0x30, 0x8b, 0xbb, 0x6e, 0x6a, 0x0b, + 0x48, 0x9b, 0xc4, 0x06, 0xea, 0xb9, 0x0e, 0xb4, 0x78, 0xa5, 0x0a, 0x47, + 0x43, 0x78, 0x23, 0xb6, 0x8e, 0xfe, 0xd2, 0xdf, 0xbe, 0xe8, 0xf1, 0x40, + 0x70, 0x50, 0x34, 0x23, 0x2e, 0x60, 0x95, 0x6a, 0x66, 0xfd, 0xab, 0xbf, + 0xe6, 0x66, 0xc6, 0x71, 0xa4, 0x9f, 0x67, 0x80, 0x6e, 0x8c, 0x82, 0x22, + 0xb9, 0xe3, 0xae, 0x35, 0x21, 0x51, 0x02, 0x99, 0x60, 0x3f, 0x67, 0x56, + 0x03, 0x79, 0x2b, 0x37, 0x4f, 0x95, 0x99, 0xc8, 0x73, 0x25, 0x31, 0x64, + 0xf8, 0xb5, 0xb4, 0x65, 0x9e, 0xab, 0x38, 0x95, 0x79, 0x2f, 0xc0, 0xa2, + 0xe8, 0xd5, 0xa7, 0x9f, 0x93, 0x96, 0x6d, 0xda, 0xc6, 0xc6, 0x60, 0x13, + 0xe2, 0xcf, 0x27, 0x06, 0xe6, 0xd5, 0xd9, 0x8b, 0xd5, 0x28, 0xf6, 0xab, + 0x2f, 0x37, 0x57, 0xcc, 0x83, 0x00, 0xaa, 0xdb, 0x57, 0x1d, 0x44, 0x95, + 0x25, 0xcd, 0x6a, 0xcc, 0xa9, 0x9c, 0x67, 0xd2, 0x93, 0x14, 0x40, 0xc5, + 0x4a, 0xbc, 0xab, 0xcf, 0xa9, 0xe7, 0x94, 0x26, 0xb6, 0x28, 0xd1, 0xc0, + 0xf9, 0xc5, 0x66, 0xb0, 0x3a, 0x0f, 0x03, 0x26, 0x3a, 0xc2, 0x8c, 0xf5, + 0x59, 0x5c, 0xd7, 0x03, 0x52, 0xee, 0x05, 0x0b, 0x3e, 0xfb, 0x57, 0xac, + 0x8b, 0xb7, 0x0e, 0x99, 0xa1, 0xed, 0x63, 0x6c, 0x70, 0x45, 0x05, 0x22, + 0xb5, 0x63, 0x31, 0xfc, 0xce, 0xde, 0x7b, 0xe8, 0x15, 0x1c, 0xcc, 0x58, + 0xc1, 0x77, 0x59, 0x74, 0x5c, 0xf9, 0x5f, 0xfc, 0xdd, 0x3b, 0x52, 0x5e, + 0x4b, 0x1d, 0xe9, 0x26, 0x96, 0xff, 0x4d, 0x8b, 0x4b, 0x12, 0x59, 0x3c, + 0x30, 0x07, 0xb4, 0xb0, 0xc7, 0x55, 0x23, 0x21, 0x5c, 0x64, 0x46, 0x96, + 0x1a, 0xbd, 0x6b, 0x64, 0x73, 0x2b, 0xe5, 0xba, 0x96, 0x08, 0x43, 0x61, + 0x4b, 0x2a, 0x9b, 0x4e, 0x23, 0x48, 0xdf, 0x95, 0xe7, 0x81, 0xe7, 0x3d, + 0x03, 0xb3, 0xd7, 0x12, 0xfb, 0xe4, 0x31, 0xcc, 0x58, 0x1c, 0x5b, 0xab, + 0x3e, 0xb0, 0xa8, 0x5a, 0xde, 0xb6, 0x71, 0x42, 0xf2, 0xbd, 0xc9, 0xb8, + 0xfe, 0xc2, 0x2f, 0xc9, 0x3e, 0xfc, 0xce, 0xa8, 0x9c, 0xab, 0x77, 0xf2, + 0x4d, 0x02, 0xe8, 0xa9, 0x06, 0x08, 0xac, 0xf6, 0xf2, 0x70, 0x3f, 0x82, + 0x5b, 0x28, 0xe7, 0x6b, 0xc4, 0x08, 0x3e, 0xa0, 0x32, 0x58, 0x3a, 0xe4, + 0x20, 0x70, 0xfc, 0xce, 0xe8, 0xa3, 0x5b, 0xd8, 0xe3, 0xcb, 0x70, 0xc5, + 0x09, 0x40, 0xb3, 0xf6, 0xc0, 0x52, 0xa9, 0xbd, 0x02, 0xbc, 0x48, 0x0f, + 0x61, 0x1b, 0x3c, 0xb0, 0x90, 0xe1, 0xdc, 0x3c, 0xfd, 0x84, 0xb5, 0xfe, + 0xa8, 0xde, 0x48, 0x55, 0xc6, 0xe4, 0x3f, 0x2b, 0x32, 0xbe, 0xcf, 0xd7, + 0xb1, 0x19, 0x36, 0x3d, 0x18, 0x22, 0xd4, 0xf7, 0xa1, 0x59, 0xd8, 0x0f, + 0xd1, 0x04, 0x9e, 0x13, 0xeb, 0xca, 0x0e, 0x8e, 0x92, 0xe3, 0x1a, 0x70, + 0xac, 0x48, 0x64, 0x27, 0x1e, 0xdb, 0x6e, 0xb0, 0x17, 0xad, 0xb2, 0x51, + 0xc4, 0x76, 0xb0, 0x28, 0x45, 0x0f, 0x47, 0x8e, 0x27, 0x16, 0xc4, 0x4e, + 0xdd, 0x5a, 0x39, 0xdb, 0x80, 0xa9, 0xfb, 0xa6, 0x5f, 0xf5, 0x97, 0xb9, + 0xc3, 0xef, 0xf5, 0xaf, 0x89, 0x83, 0x4a, 0x1a, 0xdf, 0xaa, 0x7f, 0x0e, + 0x89, 0x65, 0xd2, 0x6e, 0xfd, 0xad, 0x14, 0xf3, 0xd7, 0x96, 0x2c, 0x08, + 0xee, 0xc6, 0x53, 0x3e, 0xd9, 0x62, 0x45, 0x3d, 0x3b, 0x27, 0x72, 0x1d, + 0x21, 0x8e, 0x5f, 0x53, 0x86, 0x6a, 0xa9, 0x79, 0xbd, 0x01, 0x68, 0x69, + 0x06, 0xa3, 0xe3, 0x43, 0x24, 0x02, 0x26, 0x6b, 0xe3, 0xab, 0x85, 0x59, + 0x2c, 0x49, 0x3a, 0x32, 0xc7, 0x0c, 0xbc, 0x09, 0x42, 0x4f, 0x37, 0x5a, + 0xcd, 0xc5, 0xa2, 0x4c, 0x4b, 0xd1, 0x05, 0x98, 0xb0, 0x49, 0xe6, 0x08, + 0xbb, 0x82, 0xe8, 0x21, 0x10, 0xd6, 0xbd, 0x11, 0xec, 0x0e, 0xdf, 0x99, + 0x30, 0x9e, 0x55, 0xcc, 0x2d, 0x2b, 0x5d, 0x02, 0xb6, 0x62, 0x76, 0x64, + 0x8a, 0x5c, 0x69, 0xf9, 0xc7, 0x9e, 0x5d, 0x39, 0x7d, 0xea, 0xdd, 0xfd, + 0x70, 0x85, 0x37, 0xb2, 0x2e, 0x21, 0x7f, 0xca, 0x11, 0x68, 0x33, 0xf9, + 0xe4, 0x4b, 0xc3, 0x0b, 0x06, 0xb4, 0xaa, 0x67, 0x57, 0x85, 0xb8, 0x06, + 0xa4, 0x04, 0x3b, 0x9c, 0x17, 0x72, 0x3a, 0x3b, 0x6e, 0x82, 0x5a, 0x2e, + 0x04, 0x62, 0xe9, 0xa1, 0xa3, 0xb6, 0xd4, 0x6e, 0x93, 0xc5, 0xa2, 0x81, + 0xb7, 0xe2, 0x31, 0x42, 0xb6, 0xdd, 0x28, 0x69, 0x10, 0x46, 0x3f, 0x14, + 0x07, 0x99, 0x83, 0xdf, 0x1f, 0x82, 0xc8, 0x2b, 0x5f, 0xcc, 0x49, 0x77, + 0x53, 0xb1, 0xed, 0x75, 0x9c, 0x5a, 0x80, 0x27, 0x18, 0x1f, 0x03, 0xcc, + 0x01, 0x3a, 0x32, 0xea, 0x29, 0x79, 0x16, 0xfd, 0xa9, 0x84, 0xe5, 0xe3, + 0x3f, 0xf6, 0xd7, 0x01, 0x35, 0xb9, 0x54, 0x73, 0xef, 0xdc, 0xc4, 0xb9, + 0xfd, 0xa9, 0xa9, 0xa4, 0x65, 0xba, 0x80, 0xfc, 0x1a, 0x6c, 0x3f, 0xc4, + 0xe1, 0xb3, 0xff, 0x58, 0x5f, 0xcd, 0x53, 0x88, 0xf8, 0xed, 0xa9, 0x91, + 0x00, 0xf0, 0x43, 0x87, 0xa8, 0xec, 0x56, 0x95, 0xad, 0x3e, 0x7c, 0x0a, + 0x73, 0x0a, 0xcc, 0x1d, 0x4a, 0x9a, 0x66, 0x42, 0xb5, 0x33, 0x5f, 0x65, + 0xe8, 0x33, 0x80, 0xc3, 0x9f, 0x36, 0xf3, 0xd3, 0x97, 0x05, 0x59, 0x80, + 0x69, 0x65, 0xd6, 0x6d, 0x9d, 0x84, 0xe0, 0x42, 0xbc, 0x2e, 0x43, 0x63, + 0x90, 0x6d, 0xfb, 0x01, 0x5f, 0xc2, 0xfd, 0xa2, 0xbb, 0x3d, 0x61, 0xe2, + 0xbc, 0x7d, 0xe6, 0x5f, 0x71, 0xf7, 0x63, 0x91, 0x4b, 0x3c, 0x9b, 0x23, + 0x7b, 0xb6, 0x8e, 0x7c, 0x4a, 0x13, 0xa2, 0xdd, 0xa6, 0xd9, 0xfc, 0xbc, + 0x27, 0x1a, 0x4b, 0x98, 0x29, 0x33, 0x84, 0x2e, 0x7b, 0x06, 0x69, 0xef, + 0x7a, 0x6c, 0x4b, 0xcb, 0x9c, 0x45, 0x45, 0x55, 0x2a, 0x18, 0xd0, 0x3a, + 0x54, 0x21, 0x5a, 0x10, 0xb8, 0xe5, 0x5a, 0x16, 0xa0, 0x58, 0xf7, 0x14, + 0x21, 0x8d, 0x70, 0x59, 0xa0, 0xf2, 0x61, 0x4e, 0x5a, 0xb6, 0xec, 0x44, + 0xda, 0xe9, 0x37, 0xf4, 0xa9, 0xee, 0x03, 0x61, 0x11, 0x7d, 0x36, 0x2a, + 0x62, 0xba, 0x43, 0x46, 0x81, 0x82, 0xd7, 0xf0, 0xb5, 0x75, 0x81, 0xed, + 0x4b, 0x83, 0x62, 0xed, 0xa3, 0x91, 0x44, 0x7d, 0x5b, 0x4f, 0x99, 0x65, + 0x96, 0xf6, 0x51, 0x21, 0x2c, 0xa4, 0x4c, 0x0b, 0x6d, 0xb1, 0x37, 0x36, + 0x8f, 0x3f, 0x0e, 0xb6, 0xd2, 0xa6, 0xd1, 0xb2, 0x71, 0xa0, 0x14, 0x7b, + 0x1f, 0xaa, 0xd7, 0x34, 0xd6, 0x20, 0xe1, 0xb0, 0xd4, 0x04, 0x3e, 0xd3, + 0x59, 0x7f, 0x1a, 0x42, 0x7a, 0x81, 0x93, 0x34, 0x1b, 0x49, 0xef, 0xfc, + 0xed, 0x29, 0x24, 0xf8, 0xad, 0xf3, 0x75, 0xe2, 0x45, 0x71, 0xb8, 0xeb, + 0x03, 0xcc, 0x21, 0xde, 0x55, 0x17, 0x02, 0x6b, 0x57, 0xe1, 0xd3, 0x89, + 0x10, 0xb7, 0x1b, 0x43, 0x8b, 0x72, 0xc6, 0xc8, 0x6a, 0x50, 0x1f, 0x33, + 0xec, 0x65, 0x1e, 0x11, 0xeb, 0xb6, 0x32, 0x3e, 0xd6, 0x96, 0x43, 0xd0, + 0xa3, 0x22, 0x72, 0xbb, 0xcb, 0xce, 0x31, 0x32, 0xa6, 0x35, 0x19, 0x8d, + 0x2a, 0xb4, 0xd9, 0x14, 0x07, 0xa1, 0xa8, 0x61, 0x90, 0xd3, 0x83, 0xef, + 0xdd, 0xda, 0x83, 0x94, 0x51, 0x04, 0x43, 0xb3, 0xa8, 0xfa, 0x85, 0xb0, + 0x96, 0xda, 0x71, 0x79, 0x85, 0x1e, 0xe2, 0xd4, 0x5e, 0xa3, 0x6a, 0x96, + 0xc8, 0x4f, 0x99, 0xc8, 0xbd, 0x5a, 0x09, 0xa2, 0x19, 0xda, 0x9b, 0x88, + 0x85, 0xb0, 0x44, 0x83, 0x50, 0x6d, 0x13, 0xa0, 0x7e, 0xaa, 0x33, 0x18, + 0x62, 0x83, 0xa5, 0xf0, 0xc1, 0xdf, 0xe2, 0x09, 0x25, 0x89, 0x30, 0x2a, + 0x68, 0x98, 0xa8, 0xb8, 0x18, 0x3d, 0x0c, 0x43, 0x05, 0xf7, 0x2f, 0xcb, + 0x52, 0xa7, 0xee, 0x0a, 0xe1, 0xc8, 0x82, 0xcc, 0x36, 0x93, 0xba, 0xc8, + 0x3c, 0xa0, 0x77, 0xd8, 0xbf, 0xe8, 0xdc, 0x22, 0x98, 0x16, 0xff, 0x84, + 0xb2, 0x19, 0x7c, 0xd2, 0xdf, 0x8a, 0x35, 0x33, 0xfb, 0x32, 0x47, 0xc5, + 0xc3, 0xc0, 0xb1, 0x77, 0xaa, 0x72, 0x3f, 0x6e, 0xfa, 0x69, 0x1c, 0x98, + 0x94, 0x39, 0x4c, 0xca, 0xc8, 0x80, 0x44, 0x50, 0x77, 0x8d, 0xee, 0xae, + 0x3d, 0x8a, 0xb8, 0x5a, 0xb5, 0x05, 0x04, 0x85, 0x2a, 0xea, 0xc6, 0x4a, + 0xa7, 0xf6, 0xc6, 0xdc, 0x81, 0x60, 0x43, 0xc0, 0xad, 0x74, 0x9c, 0x8b, + 0x98, 0x8c, 0x65, 0x4d, 0xea, 0xce, 0xd5, 0x0c, 0x5c, 0x35, 0xb5, 0x3a, + 0x10, 0x8c, 0xf2, 0x71, 0x4d, 0x86, 0xe9, 0x2f, 0x76, 0xc0, 0xfb, 0x3c, + 0x1c, 0x03, 0x55, 0x4d, 0xac, 0x73, 0xd7, 0xa6, 0xb3, 0xd5, 0xef, 0x21, + 0xb5, 0x0c, 0x05, 0x04, 0x56, 0x38, 0xad, 0xc0, 0x2f, 0x22, 0x90, 0x4c, + 0x05, 0x63, 0xce, 0x02, 0xc3, 0x31, 0x90, 0x65, 0x10, 0xe7, 0xa3, 0x86, + 0xb1, 0x39, 0x3f, 0x59, 0xb5, 0x26, 0xbf, 0x92, 0xdb, 0xa5, 0xa3, 0xa0, + 0xbd, 0x57, 0x91, 0x3e, 0x0a, 0x3c, 0x77, 0x38, 0xd6, 0x85, 0xb9, 0xeb, + 0x18, 0x30, 0x6b, 0xc9, 0x49, 0xb5, 0xb3, 0x88, 0x5e, 0xa6, 0xf8, 0xa7, + 0x90, 0x0f, 0x0a, 0xed, 0xba, 0xbf, 0x51, 0xd1, 0x3a, 0x1a, 0x26, 0x82, + 0xef, 0xac, 0x3f, 0x3a, 0x52, 0xaf, 0x2f, 0x50, 0x4b, 0x01, 0xaa, 0xe1, + 0x5d, 0x71, 0xf4, 0xcb, 0x9b, 0x9a, 0x2d, 0x9a, 0xa5, 0x48, 0x8b, 0xde, + 0x1e, 0xfd, 0xef, 0x8c, 0x74, 0xe9, 0x3a, 0x79, 0x7d, 0x8a, 0x56, 0xb1, + 0x4f, 0x06, 0xa9, 0x6d, 0x65, 0x9f, 0x72, 0xf1, 0xed, 0xfa, 0x8d, 0x65, + 0x52, 0xb0, 0x30, 0xe6, 0x45, 0x31, 0x27, 0x01, 0x34, 0xf2, 0xed, 0xc2, + 0x8e, 0xd4, 0x28, 0x27, 0x5b, 0xd0, 0x6c, 0x2c, 0x2e, 0x70, 0x88, 0xb3, + 0x49, 0x72, 0x3a, 0x50, 0xb0, 0x7a, 0x09, 0xd2, 0x60, 0xb7, 0x28, 0x2b, + 0x09, 0xca, 0x7c, 0x9c, 0xac, 0x0b, 0xdc, 0xf5, 0x08, 0x7d, 0xd4, 0x6f, + 0x86, 0x66, 0xcd, 0xe9, 0x71, 0xa3, 0x1c, 0xf5, 0x62, 0x36, 0x6b, 0xb7, + 0xc1, 0x4a, 0xa6, 0xd5, 0xce, 0x19, 0x5e, 0xbb, 0x70, 0xf6, 0x9e, 0x00, + 0xc9, 0x3c, 0xf7, 0x6b, 0x38, 0xc1, 0x34, 0x9c, 0x31, 0xaa, 0x1a, 0x0e, + 0x72, 0x9a, 0x26, 0xfb, 0xa0, 0x4c, 0x0e, 0xdf, 0x4e, 0x46, 0xb8, 0x6e, + 0x1e, 0xc7, 0x0f, 0x0d, 0x9b, 0x71, 0xbe, 0xc5, 0x97, 0x80, 0x28, 0xa2, + 0x92, 0xa4, 0xe8, 0x7d, 0x02, 0x43, 0x1c, 0x4e, 0x7d, 0xf0, 0xb9, 0x99, + 0x21, 0x8c, 0x96, 0xe4, 0xcd, 0x07, 0xe0, 0x2f, 0x42, 0x1b, 0x5b, 0xa5, + 0x73, 0x3c, 0xe4, 0xae, 0x24, 0xc9, 0x05, 0x5c, 0x64, 0xb7, 0x31, 0x53, + 0x0f, 0xd5, 0x43, 0x4d, 0xc9, 0xbd, 0x42, 0x81, 0xd7, 0x16, 0x0a, 0x18, + 0x55, 0xfb, 0xa9, 0xff, 0x51, 0xf4, 0x25, 0xc1, 0x3c, 0x88, 0x6f, 0x66, + 0x6f, 0x26, 0x6e, 0x3c, 0xe6, 0x3a, 0x5d, 0x35, 0x1f, 0x88, 0x26, 0xdf, + 0xa2, 0x9f, 0xf4, 0x16, 0x22, 0x0c, 0xff, 0xbc, 0xe6, 0x10, 0xa8, 0x60, + 0x3a, 0x36, 0x41, 0xaf, 0x58, 0xb5, 0x26, 0x6e, 0x6e, 0xb3, 0xad, 0x71, + 0xdb, 0xd7, 0x7e, 0x53, 0x8d, 0x89, 0xb8, 0x18, 0x05, 0x63, 0x2e, 0xfd, + 0xfc, 0xb4, 0xa8, 0xeb, 0xc5, 0x30, 0x14, 0x1e, 0xc3, 0x9d, 0xb0, 0x0e, + 0x8c, 0xf3, 0xdc, 0xd8, 0x4c, 0x19, 0x91, 0x88, 0x09, 0xdd, 0x6f, 0xc0, + 0x69, 0x50, 0x36, 0xf9, 0x8d, 0xdb, 0x3a, 0xdf, 0xad, 0x38, 0x05, 0x3b, + 0xcd, 0x97, 0x12, 0xbb, 0xc1, 0xd5, 0xe9, 0x6b, 0x9f, 0xcb, 0x5b, 0xed, + 0xb7, 0xd9, 0x67, 0xa7, 0x5a, 0x33, 0x23, 0x9d, 0xd3, 0xc3, 0x66, 0x8e, + 0x82, 0x3f, 0x04, 0xe8, 0xb7, 0x07, 0xed, 0xaf, 0x54, 0xca, 0x43, 0xe3, + 0xa7, 0x02, 0x3d, 0x60, 0xad, 0xfa, 0x93, 0xaf, 0x75, 0x18, 0x2b, 0x7c, + 0xb0, 0x62, 0x6c, 0xc6, 0xc8, 0xa5, 0x75, 0xe1, 0x3e, 0xa3, 0x80, 0x16, + 0x7d, 0x25, 0x78, 0x56, 0x0a, 0xe8, 0xe2, 0x2d, 0xb4, 0x2c, 0x1c, 0x89, + 0x8d, 0xca, 0x41, 0xfc, 0x28, 0x9d, 0xf3, 0x8d, 0x97, 0x60, 0x54, 0x01, + 0x90, 0xd7, 0x05, 0x30, 0x54, 0x3d, 0xe2, 0x50, 0xac, 0x21, 0x43, 0x21, + 0xe3, 0x82, 0x14, 0xf1, 0x26, 0x79, 0xf3, 0xfc, 0x85, 0x6b, 0x23, 0x2f, + 0xe2, 0xd6, 0x6f, 0x92, 0xcf, 0x1b, 0x9b, 0x5b, 0xe9, 0xec, 0x03, 0x1a, + 0x55, 0x84, 0x4d, 0xc5, 0x1a, 0x75, 0x2f, 0x28, 0x40, 0xc5, 0x10, 0x7f, + 0xa0, 0xe2, 0x55, 0x01, 0xf9, 0x1d, 0x3f, 0x64, 0xf2, 0xa0, 0x27, 0xd8, + 0x9e, 0xd1, 0xe5, 0xe0, 0x06, 0x82, 0x47, 0x40, 0x4f, 0x04, 0x8b, 0x86, + 0x7d, 0xaf, 0x19, 0x09, 0x9c, 0xc1, 0x9e, 0xa0, 0x27, 0xcc, 0xc6, 0x82, + 0x65, 0xc4, 0x00, 0xef, 0xa1, 0x25, 0x4c, 0x9f, 0x36, 0x73, 0x43, 0xb7, + 0x0f, 0xa3, 0x94, 0x47, 0x52, 0xc0, 0x8a, 0x13, 0x2d, 0x96, 0x36, 0x07, + 0x25, 0x20, 0xc3, 0xe4, 0x35, 0xf3, 0x53, 0xd4, 0xbd, 0xf1, 0x32, 0x06, + 0xc5, 0xea, 0xa5, 0x41, 0xa5, 0xe4, 0xa8, 0xb9, 0x47, 0xd7, 0x9c, 0x9d, + 0xa9, 0xee, 0x6b, 0xd6, 0xac, 0x06, 0x53, 0xda, 0x95, 0x80, 0xa5, 0x81, + 0x71, 0x82, 0xf1, 0x07, 0xfe, 0x78, 0x19, 0x0c, 0xa0, 0xa1, 0x4e, 0xfc, + 0x1d, 0xa5, 0x26, 0x84, 0x02, 0x84, 0x9a, 0x5f, 0xf9, 0x92, 0x58, 0x79, + 0x0a, 0x4c, 0xaf, 0xfc, 0x5f, 0x63, 0x79, 0xc6, 0xf5, 0x13, 0xa0, 0x91, + 0x45, 0x64, 0x97, 0xc8, 0xb7, 0x3f, 0x26, 0xc0, 0x99, 0x50, 0x7f, 0xce, + 0x5d, 0xaa, 0xcf, 0x93, 0xff, 0x64, 0x1a, 0x97, 0xe8, 0x09, 0x3a, 0x6f, + 0x41, 0xed, 0x1d, 0x62, 0x30, 0xf8, 0xf7, 0x65, 0x57, 0x79, 0x18, 0x6e, + 0x44, 0x61, 0x97, 0x9f, 0x5a, 0x3c, 0x88, 0xcf, 0xe3, 0xbd, 0xc6, 0x6b, + 0xf6, 0xc6, 0xc4, 0x18, 0x44, 0x3c, 0x41, 0xcb, 0xfa, 0x17, 0x1a, 0xb5, + 0x77, 0xe7, 0x12, 0xaa, 0xc2, 0x7d, 0x65, 0x37, 0x25, 0xc5, 0xcc, 0x86, + 0x69, 0xbe, 0x72, 0x78, 0x35, 0x00, 0xc7, 0x2b, 0xa6, 0x6e, 0xed, 0x86, + 0x2a, 0xcb, 0x88, 0x21, 0x10, 0xd1, 0xbd, 0xa3, 0xe4, 0x9c, 0xe1, 0x18, + 0xf6, 0xb2, 0x9c, 0x99, 0xf6, 0xad, 0x3c, 0x3d, 0xd1, 0xe4, 0x1b, 0xfa, + 0x66, 0x84, 0xc5, 0xb8, 0x01, 0x68, 0x44, 0x2f, 0xa2, 0x00, 0x87, 0x87, + 0xcd, 0xbf, 0x6e, 0xf2, 0xa3, 0x1b, 0x45, 0x8a, 0x25, 0x17, 0xa3, 0xef, + 0x00, 0x17, 0x43, 0xef, 0xe6, 0xc1, 0x36, 0x2b, 0xfe, 0xe8, 0x1a, 0xb3, + 0x34, 0x66, 0x15, 0xe0, 0xe6, 0x4c, 0xa3, 0xc6, 0xe2, 0xa0, 0xd1, 0xdd, + 0x3f, 0x85, 0xd3, 0x5b, 0x24, 0xca, 0xee, 0xc7, 0x10, 0x78, 0xe0, 0x10, + 0x25, 0x97, 0xc9, 0x93, 0xdd, 0xe8, 0x64, 0x08, 0x3c, 0x8c, 0x08, 0x23, + 0xc6, 0x74, 0x6d, 0x14, 0xbf, 0xac, 0x61, 0x4b, 0xe8, 0xdb, 0x51, 0xd0, + 0xa8, 0x85, 0x07, 0x9e, 0x1f, 0x9d, 0x3e, 0x02, 0xdb, 0x36, 0x0b, 0x41, + 0xe1, 0x8e, 0x78, 0xd2, 0x49, 0x9e, 0xaf, 0xdc, 0x81, 0xcd, 0x40, 0x7f, + 0xf6, 0x28, 0xd1, 0x2c, 0xcb, 0x90, 0x69, 0x09, 0x16, 0x1e, 0x7e, 0x9b, + 0x27, 0xb3, 0x9f, 0xbf, 0x50, 0xbe, 0x8c, 0x12, 0xf8, 0xf3, 0x56, 0x6b, + 0xc0, 0x2d, 0x52, 0xe0, 0xb2, 0xa1, 0xb8, 0x77, 0xa2, 0x31, 0x53, 0x21, + 0x1d, 0x80, 0x3a, 0x63, 0x50, 0x9c, 0x3d, 0x49, 0xd9, 0x88, 0x1b, 0x97, + 0x9d, 0x6a, 0x74, 0x50, 0x4b, 0xbe, 0x24, 0xd8, 0x67, 0xac, 0x9c, 0x2d, + 0x0d, 0x8f, 0xdb, 0x7c, 0x9d, 0xe5, 0x92, 0xdc, 0x90, 0x83, 0x04, 0x3d, + 0xe2, 0x55, 0x8d, 0x7a, 0x5a, 0x58, 0xc2, 0x8d, 0x27, 0x53, 0x37, 0x47, + 0x48, 0x17, 0xca, 0xcd, 0x62, 0xe6, 0xb3, 0x86, 0x8a, 0x7c, 0xb0, 0x77, + 0xed, 0xb5, 0x29, 0x7e, 0xe0, 0x66, 0x37, 0xdc, 0x54, 0xa0, 0x16, 0x71, + 0xb7, 0x3e, 0x79, 0xa7, 0xa3, 0x4d, 0x25, 0x03, 0x54, 0xd6, 0x03, 0x71, + 0xda, 0xf0, 0xd9, 0xaf, 0x93, 0x8e, 0x9c, 0x72, 0x31, 0x7b, 0x8d, 0x97, + 0x69, 0x29, 0x21, 0xca, 0x02, 0x1c, 0x1b, 0xed, 0x22, 0x47, 0xce, 0x54, + 0xaf, 0xee, 0x47, 0xdf, 0x9b, 0x95, 0x8b, 0x3a, 0x85, 0xdc, 0x6e, 0x9a, + 0x77, 0xd9, 0x90, 0xc7, 0xea, 0x9c, 0x0d, 0xa2, 0x9b, 0x15, 0x93, 0xe9, + 0x25, 0x14, 0xb7, 0x28, 0x29, 0xaf, 0xdd, 0xbe, 0x15, 0x82, 0xa9, 0x01, + 0xe9, 0xf8, 0x99, 0x4d, 0xa6, 0x4e, 0x1d, 0xc9, 0xd0, 0xf9, 0x13, 0xb9, + 0xd5, 0xc8, 0xd7, 0x97, 0xad, 0xea, 0x0d, 0xad, 0xeb, 0x91, 0xdc, 0x63, + 0x5a, 0x83, 0x14, 0xcf, 0x3b, 0x4d, 0x3a, 0xfe, 0xc3, 0x04, 0xbd, 0xc2, + 0x8c, 0x41, 0x1b, 0xb3, 0xfe, 0xdf, 0xf8, 0xa1, 0xd2, 0xef, 0x0d, 0xb1, + 0xae, 0x1a, 0x69, 0xd6, 0x31, 0x01, 0xd5, 0xf1, 0x54, 0x3a, 0x7a, 0xc8, + 0xc9, 0x21, 0x95, 0x5a, 0xbf, 0x2f, 0x4e, 0x8e, 0x59, 0xb4, 0x25, 0x40, + 0x65, 0x5d, 0xe2, 0xd2, 0x06, 0xb5, 0x3c, 0x0f, 0xcb, 0xe0, 0xea, 0x0f, + 0x0a, 0x37, 0xea, 0xd6, 0x18, 0x09, 0x98, 0xf1, 0x78, 0xc7, 0x69, 0x62, + 0x62, 0x93, 0x61, 0x5c, 0x67, 0x9e, 0xfd, 0xca, 0x60, 0x29, 0x12, 0xcf, + 0xc7, 0x40, 0x40, 0xf3, 0x74, 0x0d, 0xeb, 0xc5, 0x3c, 0xd7, 0xd6, 0x09, + 0x34, 0xf9, 0x3a, 0x10, 0xf7, 0xbd, 0x48, 0xb7, 0x58, 0xc6, 0x3d, 0x32, + 0x61, 0xed, 0x92, 0xa6, 0xca, 0x5b, 0x9e, 0x36, 0x8b, 0x38, 0xe4, 0x3b, + 0x4b, 0x5d, 0x60, 0x43, 0xa1, 0x24, 0xfe, 0x06, 0x7c, 0xa3, 0xbc, 0xcb, + 0x17, 0x81, 0x4b, 0x4b, 0xe9, 0xf8, 0xf2, 0x00, 0x7a, 0x1a, 0x03, 0x88, + 0x4a, 0xb2, 0x53, 0x8f, 0xf9, 0x68, 0xe3, 0x30, 0xb7, 0xa6, 0x3f, 0xc3, + 0x29, 0xc5, 0x6f, 0x9c, 0xa6, 0xf1, 0xa1, 0x50, 0x60, 0x6d, 0xf2, 0x1b, + 0xb6, 0x7f, 0x3f, 0x1c, 0x20, 0xc1, 0x6d, 0xdc, 0x12, 0x5c, 0x09, 0x68, + 0x77, 0x03, 0xea, 0x79, 0xba, 0xc1, 0xf5, 0x23, 0x6b, 0x6c, 0x47, 0x13, + 0xbb, 0x39, 0x28, 0x25, 0x9f, 0x48, 0x42, 0x3c, 0x69, 0xf0, 0xcb, 0xa3, + 0xa9, 0x04, 0xda, 0x97, 0x3c, 0xfc, 0xf4, 0x96, 0x38, 0x37, 0x57, 0x08, + 0x79, 0x72, 0xb0, 0x79, 0xe5, 0x29, 0xd5, 0x6a, 0x6e, 0xf3, 0xf7, 0xcd, + 0xc6, 0x15, 0x77, 0xfe, 0x39, 0xe8, 0x01, 0x24, 0xd4, 0xac, 0x08, 0x91, + 0x05, 0x51, 0xf9, 0x50, 0x46, 0x7c, 0x58, 0x86, 0x94, 0x9c, 0x93, 0x98, + 0x6c, 0x11, 0xf8, 0x2e, 0x6c, 0x62, 0xe7, 0x22, 0x0c, 0x28, 0xb7, 0x0e, + 0x8f, 0xce, 0xf6, 0xd2, 0x47, 0x1c, 0x0d, 0x09, 0xf1, 0xf1, 0xe8, 0xdc, + 0x83, 0x4a, 0xba, 0xb1, 0xcd, 0x79, 0x89, 0x80, 0x17, 0x62, 0x64, 0xbe, + 0x22, 0x42, 0x6d, 0x0f, 0x39, 0x2e, 0x8b, 0xc0, 0xdb, 0x9d, 0xc3, 0x14, + 0x95, 0x6a, 0x6b, 0xeb, 0xe5, 0x13, 0xc9, 0x28, 0x50, 0x03, 0x14, 0x96, + 0x8d, 0x32, 0x83, 0xb0, 0xe7, 0x13, 0x4a, 0x05, 0xf8, 0xd8, 0x7f, 0x3a, + 0x66, 0xcc, 0x6f, 0x80, 0xe2, 0xab, 0xb7, 0x9a, 0xcc, 0x6a, 0x78, 0x70, + 0xeb, 0xed, 0x3f, 0xd0, 0x50, 0xe0, 0x9f, 0x33, 0xd9, 0x60, 0xea, 0x31, + 0xf0, 0xdb, 0x44, 0x16, 0x2e, 0xd3, 0x65, 0x6e, 0xa1, 0x65, 0x1b, 0x10, + 0xfb, 0xba, 0xce, 0xad, 0x00, 0x19, 0x66, 0xf3, 0xf7, 0xce, 0x4e, 0xa4, + 0xc4, 0x8a, 0xb7, 0x70, 0xb8, 0x32, 0xf6, 0xb9, 0x2b, 0x84, 0x2c, 0xbf, + 0x78, 0x5e, 0x60, 0xde, 0xb3, 0x82, 0xa5, 0x7d, 0x55, 0x94, 0x95, 0x56, + 0x69, 0x4f, 0x40, 0x05, 0xf3, 0x0d, 0xde, 0x1b, 0x9a, 0xf0, 0x26, 0x7d, + 0xc5, 0xb6, 0x54, 0x05, 0x32, 0xc7, 0x9d, 0x23, 0x5d, 0x5d, 0x11, 0xa9, + 0xac, 0x82, 0x38, 0x66, 0x9e, 0x19, 0xa9, 0xd2, 0xb6, 0xa2, 0x61, 0xeb, + 0x40, 0xcd, 0x25, 0xd8, 0x1b, 0x28, 0x6c, 0xa3, 0x9a, 0x81, 0xd5, 0x6a, + 0x67, 0x97, 0x8f, 0x7f, 0x9e, 0xa7, 0x3f, 0x20, 0xf5, 0x71, 0x13, 0x49, + 0xa1, 0x3c, 0x0e, 0x84, 0x84, 0x80, 0x83, 0x65, 0xfd, 0x87, 0xe4, 0xd5, + 0xf3, 0xab, 0x37, 0x28, 0xd7, 0xc3, 0x35, 0xcf, 0xc9, 0x63, 0xe8, 0x99, + 0x09, 0xbd, 0x51, 0xaf, 0x93, 0x38, 0xef, 0xdf, 0x1a, 0xc1, 0x00, 0x06, + 0x75, 0x5b, 0xd3, 0x2b, 0xa0, 0xae, 0x2e, 0x8b, 0xa0, 0x79, 0x9c, 0x2c, + 0x88, 0xc3, 0x79, 0x67, 0xf2, 0x9e, 0x20, 0xed, 0xf7, 0x2d, 0x3e, 0x18, + 0x23, 0xd0, 0x24, 0x9d, 0x6d, 0x24, 0x5d, 0x5a, 0x63, 0xfe, 0x74, 0x57, + 0x04, 0xaa, 0x98, 0x3d, 0x86, 0x4b, 0xff, 0x51, 0xd4, 0x4b, 0xbc, 0xa7, + 0x72, 0xc1, 0x46, 0x7e, 0x1f, 0x12, 0xc9, 0x0c, 0x35, 0xba, 0x42, 0x97, + 0x0c, 0xb9, 0x60, 0x8c, 0x3e, 0xe2, 0x66, 0x60, 0x10, 0x94, 0x2f, 0x75, + 0xae, 0x57, 0x1c, 0xe7, 0x5c, 0xe9, 0x19, 0x42, 0x9f, 0x83, 0x29, 0x92, + 0x89, 0x86, 0x6d, 0x8a, 0xd4, 0x11, 0x46, 0x32, 0x85, 0x4f, 0x08, 0x96, + 0x8d, 0x16, 0x1c, 0xdc, 0xa2, 0x70, 0x93, 0x19, 0xa9, 0x15, 0x0e, 0xf6, + 0xf0, 0x86, 0xf9, 0x6e, 0xff, 0xc5, 0xe9, 0xeb, 0xd7, 0x5e, 0x43, 0xe1, + 0x92, 0xdc, 0xe4, 0x44, 0x09, 0xd0, 0xe4, 0x08, 0xd0, 0x5c, 0x3d, 0x44, + 0x1e, 0xdf, 0x53, 0x0e, 0x2a, 0x57, 0x93, 0xa8, 0x61, 0x24, 0xea, 0x3b, + 0x93, 0x52, 0xa0, 0xbf, 0x26, 0x0d, 0xbb, 0x55, 0xd1, 0xbd, 0xf3, 0xb1, + 0x75, 0xa2, 0x71, 0x09, 0xb1, 0x98, 0x7a, 0x21, 0x20, 0x98, 0x65, 0xf2, + 0x4d, 0x3b, 0x5f, 0x7b, 0xd2, 0x34, 0x07, 0x60, 0xcd, 0x1c, 0x6c, 0x44, + 0x0e, 0x6a, 0x6d, 0xc7, 0xe0, 0xfe, 0xb7, 0xfd, 0x78, 0x3e, 0x23, 0x39, + 0x73, 0x38, 0xf1, 0xd4, 0xe3, 0xbc, 0x12, 0x4a, 0x7c, 0xa7, 0x65, 0xe8, + 0xe6, 0xe6, 0x15, 0x35, 0xae, 0x93, 0x9e, 0xf3, 0xb8, 0x6e, 0xc0, 0x16, + 0xc2, 0xfd, 0xee, 0x05, 0xb9, 0x01, 0x9a, 0x79, 0xd9, 0xd8, 0x46, 0xf4, + 0xaf, 0x27, 0x5e, 0xf4, 0x89, 0x11, 0x83, 0x35, 0xe2, 0x50, 0xdb, 0x86, + 0x80, 0x3c, 0x98, 0x0d, 0x47, 0x07, 0xfa, 0xa7, 0xee, 0x08, 0x91, 0x67, + 0xc4, 0x24, 0x47, 0x09, 0x6b, 0xad, 0xa7, 0x88, 0xae, 0xaf, 0x20, 0x4f, + 0x79, 0xc8, 0x6a, 0x9c, 0x59, 0x2f, 0xa8, 0x16, 0x7a, 0xb9, 0x85, 0x62, + 0x16, 0x22, 0x1c, 0xa8, 0x99, 0xb2, 0xbc, 0x2e, 0xe2, 0xe0, 0x19, 0xb3, + 0x09, 0x54, 0x07, 0x62, 0xc6, 0xd3, 0xcb, 0x97, 0xe9, 0x65, 0x4e, 0x4e, + 0x22, 0xc5, 0x4f, 0x91, 0xda, 0xd1, 0x0d, 0x6b, 0x1a, 0x3e, 0x0a, 0x8d, + 0x3f, 0xa2, 0x9e, 0xf1, 0x95, 0xea, 0x50, 0xa5, 0x12, 0xea, 0x4b, 0x66, + 0x46, 0x22, 0x82, 0xff, 0xbd, 0x37, 0x07, 0x94, 0x3c, 0x26, 0x3a, 0x31, + 0x06, 0xc3, 0x39, 0xa3, 0x3a, 0x7e, 0x13, 0xd6, 0xf4, 0x1a, 0x27, 0x67, + 0xe9, 0xc2, 0xf5, 0xd1, 0xf6, 0xce, 0x55, 0xe8, 0xcc, 0x6a, 0x3f, 0x99, + 0xe9, 0xf1, 0xa8, 0x87, 0x07, 0xbb, 0x78, 0xad, 0xcf, 0xd4, 0x8e, 0x91, + 0xdf, 0x41, 0x03, 0xab, 0xae, 0xdd, 0xbe, 0x99, 0x9f, 0x05, 0x29, 0x52, + 0xe1, 0x7e, 0x89, 0x13, 0x0a, 0xf1, 0x0f, 0x87, 0xf4, 0x20, 0xbf, 0xb2, + 0x51, 0x24, 0x59, 0x20, 0x7d, 0xa3, 0xc3, 0x0f, 0xc8, 0xa1, 0xd3, 0x25, + 0x40, 0x03, 0xb4, 0x5d, 0x60, 0x43, 0xb5, 0xa8, 0x00, 0x28, 0x23, 0x08, + 0x0d, 0xf5, 0x04, 0x23, 0x41, 0x48, 0x72, 0x16, 0x23, 0x7b, 0x5b, 0x09, + 0x5e, 0xa5, 0x77, 0x14, 0x1c, 0x99, 0x65, 0xca, 0x00, 0x3e, 0xa2, 0xb3, + 0x2c, 0xf2, 0x2a, 0x18, 0x75, 0x39, 0xe7, 0xc5, 0xd3, 0x52, 0x7b, 0xa8, + 0x1d, 0xa2, 0x9e, 0xc2, 0xe1, 0xa3, 0xba, 0xba, 0xe9, 0x0e, 0x02, 0xa4, + 0xe5, 0x08, 0x1c, 0xce, 0x26, 0x8a, 0xbd, 0x66, 0x8b, 0xad, 0x66, 0x85, + 0x3f, 0x77, 0x86, 0xe3, 0x7a, 0x4f, 0xa6, 0x59, 0x13, 0x75, 0xaf, 0xd8, + 0x53, 0x72, 0x6c, 0x56, 0x5d, 0x94, 0x69, 0x7b, 0x42, 0xe0, 0x25, 0x88, + 0x7c, 0xaf, 0x91, 0x9e, 0xc3, 0xba, 0xb5, 0xe7, 0xfb, 0x8f, 0xbe, 0xd2, + 0x8e, 0xb1, 0xef, 0x27, 0xd1, 0x0c, 0x5c, 0xcd, 0xed, 0x48, 0x1e, 0x53, + 0x0b, 0x28, 0x3d, 0xff, 0x3d, 0x5c, 0xb0, 0x37, 0xca, 0x12, 0x53, 0xaa, + 0x94, 0xd0, 0x21, 0x5a, 0xc1, 0x81, 0xeb, 0x94, 0x3b, 0x6e, 0x9d, 0x74, + 0xba, 0xe8, 0xa0, 0x97, 0xa3, 0xae, 0x90, 0x4c, 0x5a, 0x5e, 0x2c, 0xc3, + 0x53, 0xa9, 0x05, 0x97, 0x69, 0xd7, 0x28, 0xb8, 0xdc, 0xed, 0x57, 0xcc, + 0x58, 0x35, 0x87, 0x6e, 0xe8, 0x42, 0x7a, 0x04, 0x47, 0x5f, 0x3c, 0x5d, + 0x76, 0xc6, 0x2e, 0x24, 0x6e, 0x1d, 0x81, 0xab, 0xd4, 0xf3, 0x6b, 0xf3, + 0xc0, 0x3a, 0xd8, 0x4c, 0x3e, 0x02, 0x41, 0x10, 0x47, 0x29, 0xe3, 0x00, + 0x04, 0x20, 0xfd, 0xe9, 0xa1, 0x1c, 0xab, 0xd6, 0x48, 0xce, 0x11, 0xf6, + 0xa9, 0x67, 0xaa, 0xed, 0x5c, 0xdf, 0x8b, 0x03, 0x95, 0x7a, 0xd2, 0x16, + 0x39, 0x88, 0x3c, 0x84, 0xec, 0x87, 0xfc, 0x53, 0x0d, 0x2d, 0xaa, 0xa1, + 0x3a, 0xec, 0x46, 0x0d, 0x5e, 0xeb, 0x1f, 0x76, 0xc7, 0x0b, 0xa7, 0x49, + 0xad, 0x0f, 0x6f, 0x12, 0x61, 0x5d, 0x06, 0x80, 0x42, 0x0e, 0x4c, 0x79, + 0x19, 0x90, 0x0a, 0x52, 0x88, 0x98, 0xc2, 0x44, 0x46, 0xf3, 0x16, 0x14, + 0x68, 0xd9, 0x8f, 0xd2, 0xd6, 0x3f, 0x74, 0xea, 0x4c, 0xbf, 0x73, 0x2c, + 0xfa, 0xe4, 0x45, 0xec, 0x01, 0x00, 0xaa, 0x76, 0x6d, 0xed, 0x3a, 0x31, + 0x87, 0xf6, 0xd1, 0xfb, 0x67, 0x88, 0x42, 0x64, 0x3b, 0x29, 0x84, 0xfb, + 0x23, 0x85, 0x78, 0x2c, 0x90, 0xa2, 0x4a, 0x3a, 0x54, 0x72, 0x9f, 0x6c, + 0xd1, 0x91, 0xf3, 0xcc, 0x81, 0x42, 0x1a, 0xdd, 0xdd, 0x28, 0xaa, 0x97, + 0xc3, 0x68, 0x35, 0x7b, 0x98, 0xa0, 0xc4, 0x0f, 0x97, 0x4d, 0xb5, 0xe9, + 0xcd, 0xea, 0x25, 0x31, 0x58, 0x2b, 0x50, 0x08, 0x55, 0x11, 0x38, 0x8e, + 0x4b, 0x7e, 0x63, 0x3b, 0xdd, 0x02, 0x4c, 0x3a, 0x97, 0x75, 0xd6, 0x7c, + 0x9e, 0x30, 0xc7, 0xeb, 0xbc, 0xe8, 0x09, 0x01, 0x48, 0xb2, 0xf6, 0xe6, + 0xcb, 0xbe, 0xe1, 0xd3, 0x5a, 0x72, 0xd1, 0xb9, 0x20, 0xc3, 0x35, 0x01, + 0xe5, 0x56, 0x9c, 0xe0, 0x87, 0xc7, 0x9f, 0x4a, 0x33, 0x35, 0xdf, 0x83, + 0x56, 0x34, 0x1a, 0x71, 0x7f, 0x8e, 0xed, 0x5c, 0x87, 0x1a, 0x7d, 0xa2, + 0xb6, 0xf1, 0x6b, 0x0d, 0x92, 0xd1, 0x6f, 0x39, 0xf9, 0xa9, 0xef, 0x64, + 0x41, 0x22, 0xc3, 0xa3, 0x81, 0xef, 0x89, 0xf3, 0x1f, 0x91, 0x3c, 0x2e, + 0x1a, 0xce, 0x77, 0x3a, 0xbc, 0x66, 0x73, 0xc8, 0xb6, 0x78, 0x96, 0xe1, + 0xe5, 0x93, 0xb8, 0x9d, 0xf6, 0x88, 0x6c, 0x6c, 0x61, 0x21, 0x0c, 0x0e, + 0x4e, 0xd6, 0x01, 0x18, 0x03, 0x96, 0x6e, 0xb3, 0x89, 0xc2, 0xff, 0x97, + 0x16, 0xfd, 0xf2, 0x25, 0xc7, 0xb5, 0x93, 0xf1, 0x8d, 0xbe, 0xeb, 0xa4, + 0xe4, 0xcf, 0x3a, 0x5b, 0xaa, 0x6e, 0xf2, 0xea, 0x1c, 0x57, 0xc6, 0x9c, + 0xd5, 0x6b, 0x1e, 0x80, 0x86, 0x89, 0x54, 0xfd, 0xdd, 0x78, 0xd5, 0xb6, + 0x91, 0x1e, 0x13, 0xdb, 0x3a, 0xe1, 0x66, 0xab, 0x51, 0x69, 0x90, 0xf5, + 0x79, 0xba, 0x95, 0x1b, 0xad, 0x6a, 0x05, 0xe6, 0xe0, 0x79, 0x48, 0xd8, + 0x06, 0xe8, 0x6b, 0x36, 0xe5, 0x8a, 0x39, 0x9e, 0x82, 0x41, 0x45, 0x78, + 0x2e, 0x97, 0xe3, 0x4e, 0x2c, 0x96, 0xfb, 0x64, 0xc5, 0x02, 0x7e, 0xee, + 0xf3, 0x49, 0xa9, 0x6c, 0x5e, 0xbd, 0x33, 0x29, 0xd9, 0xa1, 0xe4, 0x2c, + 0x4e, 0x99, 0x13, 0x09, 0x72, 0x09, 0xc9, 0xed, 0x51, 0xf7, 0x88, 0x5e, + 0x16, 0xa0, 0x2d, 0xaf, 0x90, 0xaf, 0xfb, 0xb2, 0xed, 0x1e, 0xe3, 0x2b, + 0xb5, 0xd1, 0xc3, 0xbf, 0x07, 0xf1, 0x93, 0x18, 0x6f, 0xfa, 0xec, 0xf1, + 0xd6, 0x8d, 0xa1, 0x6e, 0x79, 0x4f, 0x9b, 0xfc, 0xaf, 0xd8, 0x41, 0xce, + 0x9e, 0x15, 0x9c, 0xce, 0x30, 0x88, 0xbb, 0x54, 0x61, 0x1f, 0xb5, 0x08, + 0x97, 0x93, 0x18, 0x7c, 0x00, 0x4c, 0xf0, 0x05, 0x03, 0x95, 0x3b, 0xcc, + 0x83, 0xed, 0x0f, 0xd3, 0x56, 0xfb, 0xb8, 0x62, 0xe1, 0xbc, 0x85, 0xeb, + 0x96, 0x9f, 0x54, 0x55, 0x27, 0x38, 0xbd, 0x2c, 0xce, 0xe8, 0x30, 0x46, + 0xdf, 0x0a, 0x1a, 0x59, 0xfa, 0x05, 0xda, 0xf5, 0x80, 0x82, 0x38, 0x22, + 0xa9, 0xe1, 0x42, 0x88, 0x31, 0xdd, 0x20, 0x48, 0x73, 0xed, 0xc8, 0xb8, + 0x90, 0x60, 0xc3, 0xe5, 0x9f, 0x80, 0xe1, 0x42, 0x35, 0x87, 0x06, 0xe2, + 0xfc, 0xdb, 0xa4, 0x38, 0x88, 0x02, 0xdc, 0x5d, 0x45, 0x4f, 0xe8, 0xf0, + 0xaf, 0x11, 0xa1, 0xcc, 0xe3, 0x1b, 0xb0, 0xf5, 0x35, 0x9a, 0x08, 0x79, + 0x5f, 0xe5, 0x7e, 0x94, 0xcb, 0xe5, 0x2d, 0x2d, 0xf6, 0x35, 0x00, 0xb9, + 0x42, 0x94, 0x9f, 0xe9, 0xe7, 0x39, 0x47, 0x31, 0xc6, 0xdf, 0x45, 0xa4, + 0x7c, 0x38, 0xed, 0x3c, 0x15, 0xcc, 0x34, 0xd7, 0xb6, 0x70, 0x4b, 0x00, + 0x9f, 0x7c, 0xf3, 0xe4, 0xba, 0xe9, 0x35, 0x72, 0x71, 0xb6, 0x94, 0xce, + 0xf9, 0xfe, 0x63, 0xbd, 0x30, 0x55, 0x7b, 0x2a, 0xa1, 0x1a, 0xe5, 0xd4, + 0x97, 0x52, 0xd3, 0xfd, 0xb3, 0x58, 0x7b, 0x6b, 0xbc, 0x65, 0x5d, 0xa1, + 0x50, 0x00, 0x79, 0x44, 0x87, 0xe4, 0x73, 0xc4, 0x82, 0x05, 0x14, 0xc8, + 0x22, 0xd6, 0x13, 0x7e, 0x7f, 0x16, 0xf5, 0x92, 0x1e, 0x2e, 0x01, 0x31, + 0x94, 0xb4, 0x6a, 0xbd, 0x45, 0x1d, 0xcd, 0x8e, 0xeb, 0x45, 0xcb, 0x35, + 0x83, 0xa6, 0xa7, 0xd8, 0xb5, 0x41, 0x86, 0x06, 0xbe, 0x00, 0x38, 0x64, + 0x2f, 0xaf, 0xe2, 0xb0, 0x7f, 0xaa, 0x8b, 0x4d, 0xf7, 0x76, 0xa2, 0x95, + 0x20, 0x6d, 0x08, 0xa0, 0xe4, 0xb5, 0x56, 0x91, 0xb3, 0x71, 0xf0, 0x64, + 0x4c, 0x18, 0xec, 0x2f, 0xf0, 0xf0, 0x2d, 0xa6, 0xe3, 0x11, 0x7f, 0xd4, + 0x5b, 0x71, 0x70, 0xe1, 0xce, 0xde, 0xc9, 0x00, 0xe7, 0xf5, 0x4a, 0x3b, + 0x99, 0x3d, 0x18, 0x23, 0xbb, 0xfd, 0x07, 0xbb, 0x59, 0xaa, 0x78, 0x4e, + 0xba, 0xbe, 0x78, 0x10, 0xf2, 0x47, 0x8c, 0x31, 0x4c, 0x16, 0x6b, 0x15, + 0x64, 0x8c, 0x3a, 0x69, 0x26, 0x71, 0xac, 0x92, 0x01, 0x00, 0x1b, 0x2e, + 0xf7, 0x6a, 0xcc, 0x58, 0x0d, 0x05, 0x5f, 0x36, 0x20, 0xdf, 0x5f, 0x18, + 0x10, 0x11, 0x2f, 0x6e, 0x9e, 0x3b, 0x73, 0x16, 0x21, 0xd8, 0x52, 0xc5, + 0x12, 0x28, 0x64, 0xad, 0x1d, 0x0c, 0x1d, 0x72, 0x9e, 0x4e, 0xd4, 0xfa, + 0x03, 0xfc, 0x0b, 0xf2, 0xe3, 0x2f, 0x7e, 0x0f, 0x3a, 0x30, 0xfc, 0x51, + 0x1a, 0x75, 0x47, 0x24, 0xd4, 0x7f, 0x31, 0xc7, 0x4b, 0x1a, 0x55, 0x3b, + 0xba, 0x2e, 0x6f, 0x24, 0x8d, 0xe0, 0x2e, 0x48, 0xf7, 0x28, 0xea, 0xea, + 0x1c, 0x03, 0xb4, 0x1c, 0x4d, 0xe8, 0x44, 0xb5, 0xe9, 0x02, 0x46, 0x14, + 0x7d, 0x48, 0x13, 0x40, 0x74, 0x5d, 0x75, 0x24, 0x9c, 0x97, 0xdc, 0x8b, + 0x15, 0x3d, 0x7d, 0xd3, 0xc9, 0xc2, 0x35, 0x88, 0x5f, 0xb0, 0x35, 0x32, + 0xbb, 0x7a, 0xbf, 0x5a, 0x1c, 0xcb, 0xa8, 0xcb, 0xdf, 0x1d, 0xf1, 0x79, + 0x7b, 0x93, 0x60, 0x4d, 0x98, 0x73, 0x11, 0x92, 0xfc, 0x29, 0xe2, 0x5b, + 0x53, 0xea, 0xe1, 0x0e, 0x13, 0x14, 0x40, 0x28, 0xac, 0xd3, 0x5f, 0x99, + 0x7c, 0x5b, 0x7b, 0xf4, 0x0d, 0x5b, 0xcf, 0x35, 0x49, 0x5d, 0x7d, 0x44, + 0x9f, 0xcb, 0xb2, 0x56, 0x2e, 0x54, 0x40, 0x35, 0x0b, 0x26, 0x53, 0xc8, + 0xd8, 0x79, 0xd4, 0xda, 0x49, 0xd7, 0xc9, 0xf7, 0xaa, 0xcb, 0x32, 0xbe, + 0x6b, 0x40, 0x81, 0x17, 0xdb, 0x07, 0x99, 0xd5, 0x36, 0x97, 0x6e, 0x64, + 0x01, 0x95, 0x9b, 0x22, 0xcf, 0x8a, 0x5b, 0x47, 0xbd, 0x0b, 0x22, 0xec, + 0x1d, 0x35, 0x42, 0xad, 0x09, 0xd2, 0xa5, 0x2b, 0xc4, 0xdb, 0xbe, 0x5a, + 0x3f, 0x82, 0xd4, 0x55, 0x5b, 0xd0, 0xed, 0x7c, 0x15, 0x47, 0xcd, 0xf2, + 0x07, 0xae, 0x0b, 0xe6, 0x06, 0x8f, 0x2f, 0xe7, 0xeb, 0x11, 0xa0, 0x3c, + 0xac, 0x8e, 0xff, 0x7b, 0xd0, 0x67, 0x7f, 0xe6, 0xfd, 0x51, 0x5a, 0x13, + 0xd8, 0xe5, 0xd1, 0x97, 0x02, 0xfd, 0x34, 0x6f, 0x89, 0xa7, 0xc9, 0x35, + 0xd8, 0xa8, 0x62, 0x23, 0xe2, 0xa7, 0x98, 0x7e, 0xed, 0xb7, 0xa5, 0xdf, + 0xa0, 0x85, 0x9a, 0x43, 0xf9, 0x9c, 0x86, 0x89, 0x63, 0xf5, 0x3c, 0x95, + 0xbf, 0x54, 0x6b, 0x2c, 0x4e, 0x50, 0xfb, 0xb7, 0xf9, 0xa5, 0x2a, 0xe8, + 0x00, 0x6a, 0x52, 0x35, 0x92, 0x7b, 0x8b, 0xfc, 0x53, 0xc3, 0x91, 0x6c, + 0x81, 0xae, 0x7a, 0x3a, 0x97, 0xd1, 0x77, 0xac, 0x1f, 0x86, 0xa8, 0xff, + 0x52, 0xf9, 0xc3, 0x4d, 0x14, 0xdf, 0xa2, 0xaf, 0x7f, 0x3e, 0x41, 0xf4, + 0x27, 0xf8, 0x3a, 0xc4, 0x15, 0xde, 0x8a, 0xb7, 0xe3, 0x9b, 0x71, 0x43, + 0x45, 0xfa, 0x18, 0x66, 0x73, 0x35, 0xf3, 0x11, 0x31, 0x5f, 0xa0, 0x62, + 0x24, 0x39, 0xc1, 0xcc, 0x49, 0x19, 0x6f, 0xd3, 0xe1, 0x39, 0x82, 0x9b, + 0x36, 0x71, 0x21, 0xad, 0x2c, 0x60, 0x71, 0xe8, 0x5c, 0x14, 0x9c, 0xdc, + 0xeb, 0x42, 0xdf, 0xcd, 0x6c, 0x85, 0xaf, 0x5c, 0x66, 0x3a, 0x65, 0xb0, + 0xe9, 0x93, 0xb9, 0x0a, 0xb2, 0xeb, 0x57, 0xe8, 0x1e, 0xd3, 0xc2, 0x99, + 0x7e, 0x09, 0xe5, 0x2f, 0x0d, 0xa0, 0x97, 0xbb, 0x10, 0xec, 0xf0, 0xd6, + 0xa6, 0x19, 0xc2, 0x26, 0xc5, 0x6b, 0x76, 0x7e, 0xa1, 0x2a, 0x91, 0xa5, + 0x3c, 0x0d, 0xe5, 0xad, 0xb0, 0x68, 0x61, 0x23, 0x26, 0x90, 0xe3, 0xf6, + 0xae, 0x8a, 0x58, 0xe8, 0x47, 0x67, 0xc0, 0x06, 0x3d, 0xc1, 0x70, 0xc6, + 0x50, 0x43, 0x4e, 0x6e, 0x0a, 0xa0, 0xf5, 0xd1, 0x52, 0x78, 0x49, 0xc0, + 0x82, 0x74, 0xce, 0x7f, 0x4b, 0x61, 0xe6, 0x63, 0x6d, 0xbc, 0x48, 0x8e, + 0xb4, 0x63, 0xff, 0x2b, 0x34, 0x54, 0xd0, 0x0a, 0xd0, 0x1a, 0x20, 0xd6, + 0xd1, 0xf5, 0xd0, 0x8c, 0xfe, 0x22, 0x5f, 0x7c, 0xa8, 0x87, 0xfe, 0x5e, + 0x6b, 0xb0, 0xa0, 0x36, 0x62, 0xfa, 0xdd, 0x1d, 0x84, 0x58, 0xe7, 0x70, + 0x40, 0xea, 0x2c, 0xed, 0xfb, 0x53, 0x73, 0xea, 0xee, 0xe7, 0xdc, 0x10, + 0xa2, 0xd3, 0xf8, 0xc3, 0xba, 0xf1, 0x1d, 0x75, 0x36, 0xff, 0xd5, 0xe5, + 0x20, 0x94, 0x04, 0x21, 0xc9, 0x4a, 0xf5, 0xa2, 0x99, 0x6f, 0x4a, 0x51, + 0x11, 0x0c, 0xde, 0xed, 0xdf, 0xa9, 0xfc, 0xd5, 0xa4, 0x44, 0xdb, 0xa0, + 0x61, 0xb1, 0x8c, 0xc8, 0x1e, 0x8e, 0xb6, 0x51, 0x6d, 0xe2, 0xb7, 0x34, + 0x7d, 0xb9, 0xd2, 0x44, 0xda, 0x8c, 0x01, 0xed, 0x57, 0x9f, 0x10, 0x17, + 0xfc, 0x35, 0xb3, 0xfc, 0x35, 0xe8, 0x21, 0x9d, 0x2d, 0x6b, 0xad, 0xe6, + 0xe5, 0x62, 0xc5, 0xef, 0xe9, 0x49, 0xd9, 0x8f, 0x88, 0x31, 0x44, 0xb7, + 0x99, 0xc9, 0x89, 0xde, 0x55, 0x76, 0x35, 0x76, 0x36, 0xaa, 0x6f, 0x32, + 0x81, 0xbe, 0xd0, 0x09, 0x5a, 0x80, 0x1a, 0x73, 0x61, 0xa2, 0x47, 0xb5, + 0x4d, 0xb3, 0x45, 0x09, 0x2f, 0x39, 0x75, 0x2b, 0x4f, 0x83, 0x72, 0xa5, + 0x67, 0x34, 0x3d, 0xe7, 0x12, 0x37, 0xec, 0x67, 0x80, 0xae, 0xa6, 0xec, + 0x29, 0x98, 0xc5, 0xc5, 0xaf, 0x4a, 0x96, 0x3e, 0x9d, 0xd8, 0x89, 0xdf, + 0x8f, 0x60, 0x0b, 0xcb, 0x1f, 0x8a, 0x68, 0x90, 0x85, 0xc0, 0x46, 0x58, + 0x97, 0x8d, 0x32, 0x06, 0x3c, 0x6e, 0x42, 0x6f, 0x67, 0x4c, 0xc0, 0x82, + 0x53, 0xf8, 0x8b, 0x49, 0x78, 0x41, 0xbe, 0x88, 0x99, 0x79, 0x7e, 0xae, + 0x95, 0x95, 0xb1, 0xaa, 0xd3, 0x74, 0xfa, 0x2e, 0xd7, 0xf5, 0xed, 0xe3, + 0xf3, 0xc0, 0x39, 0x61, 0x01, 0xca, 0x0c, 0x77, 0xb1, 0xa4, 0x36, 0xa8, + 0x0b, 0x29, 0xc0, 0x6a, 0x97, 0x14, 0x5f, 0xe9, 0x39, 0xba, 0xa6, 0x73, + 0xde, 0x8d, 0x1e, 0x41, 0x2f, 0xd0, 0x89, 0x9c, 0x1e, 0x85, 0x8d, 0x32, + 0x88, 0x41, 0x83, 0x85, 0x04, 0xff, 0x4b, 0x55, 0xcf, 0x46, 0x3e, 0x7b, + 0x85, 0x84, 0x1f, 0x9a, 0xf5, 0x2f, 0x20, 0x7a, 0xff, 0x10, 0xb2, 0x81, + 0x3c, 0x70, 0xe4, 0xbe, 0xe1, 0xb8, 0x96, 0x7b, 0x0f, 0x83, 0x68, 0x7c, + 0x4c, 0x40, 0xc1, 0xfa, 0x1e, 0x76, 0x63, 0x3f, 0xd2, 0x7a, 0x02, 0xe0, + 0x51, 0x34, 0xf4, 0xaf, 0x74, 0xe3, 0x43, 0x9e, 0x93, 0xfd, 0x59, 0x51, + 0x76, 0xf0, 0x8c, 0x2c, 0x4c, 0xd5, 0xf5, 0x86, 0x4f, 0x32, 0x33, 0xb3, + 0x82, 0xdc, 0x87, 0x1b, 0x4c, 0xf7, 0x83, 0x54, 0x07, 0xa5, 0x4d, 0xec, + 0xd1, 0x9d, 0x53, 0x42, 0xae, 0x8d, 0xa9, 0xd6, 0xad, 0xe9, 0x0e, 0x6b, + 0x5c, 0x0c, 0x7e, 0x7d, 0xab, 0xc6, 0xe9, 0x1a, 0x85, 0x65, 0x81, 0x92, + 0xf0, 0xa0, 0x71, 0x90, 0xe2, 0x20, 0x83, 0x9c, 0x6d, 0x7c, 0xc0, 0xf7, + 0xa4, 0x02, 0x46, 0xef, 0x3e, 0x16, 0xe7, 0x94, 0x48, 0x85, 0xfb, 0x44, + 0xa3, 0xf5, 0x12, 0x87, 0x01, 0x01, 0x16, 0xbf, 0xa9, 0x09, 0x88, 0xd3, + 0x96, 0x86, 0x67, 0xc4, 0x83, 0x0b, 0xae, 0x6e, 0xe3, 0x9e, 0x31, 0xc6, + 0x7d, 0xf6, 0x59, 0x39, 0xb9, 0x45, 0xee, 0x72, 0xb3, 0x6f, 0x6d, 0xa0, + 0x30, 0xad, 0x3e, 0xe8, 0x4e, 0x70, 0xbd, 0xa1, 0xb3, 0xa6, 0x79, 0xff, + 0x2e, 0xc3, 0xca, 0x90, 0xc6, 0xba, 0xad, 0x72, 0xd1, 0xd9, 0xed, 0x29, + 0x6c, 0x0d, 0x70, 0xa9, 0x86, 0x84, 0xfb, 0x72, 0x79, 0x1e, 0x82, 0x86, + 0xaf, 0x10, 0xdc, 0x56, 0x9f, 0x6c, 0x95, 0x49, 0x43, 0x67, 0xd6, 0xcf, + 0x8e, 0x29, 0x04, 0x5c, 0x92, 0xe8, 0x73, 0x70, 0x8c, 0x03, 0x6d, 0x77, + 0x31, 0xf4, 0x03, 0x8a, 0x5a, 0x4b, 0x1b, 0xc3, 0x13, 0x3d, 0x8b, 0x6c, + 0x5b, 0x94, 0x1c, 0x19, 0xc8, 0x01, 0x3c, 0x5e, 0x5f, 0xdf, 0xab, 0xcd, + 0x04, 0xbc, 0x3d, 0x6c, 0xaf, 0x80, 0xff, 0x18, 0x32, 0x85, 0x13, 0xb8, + 0xa1, 0x38, 0x31, 0xde, 0x11, 0x21, 0xb1, 0xc5, 0x42, 0x6e, 0x2f, 0x65, + 0x37, 0xf3, 0xec, 0xe2, 0x51, 0x5b, 0xdc, 0x4a, 0x15, 0x58, 0xcb, 0x28, + 0x9d, 0x9f, 0x33, 0x09, 0xb2, 0xf9, 0x07, 0x77, 0x42, 0xc4, 0x05, 0x8a, + 0x14, 0xb6, 0xf8, 0xfa, 0xcb, 0x04, 0x76, 0x3c, 0x5d, 0x09, 0xc6, 0xeb, + 0x4e, 0x89, 0x53, 0xce, 0xab, 0x34, 0x1b, 0xa3, 0xe9, 0x03, 0x7a, 0xb5, + 0x6a, 0xfa, 0x82, 0x41, 0xf2, 0x76, 0x03, 0x13, 0x62, 0x0c, 0xfb, 0x46, + 0x8f, 0x7a, 0xd6, 0x89, 0xc4, 0x3f, 0xe1, 0xad, 0xf6, 0x8e, 0xb0, 0xfb, + 0xbd, 0x73, 0x8f, 0xbd, 0x03, 0x22, 0x45, 0xdd, 0xe4, 0x43, 0x5f, 0x37, + 0xa6, 0x96, 0xd1, 0x1d, 0x01, 0x7a, 0x95, 0x13, 0xbb, 0x1b, 0x31, 0x44, + 0x46, 0xe7, 0x29, 0xd2, 0x65, 0x67, 0x09, 0xe4, 0x65, 0x5e, 0x62, 0xbc, + 0x6c, 0x22, 0x7d, 0x1b, 0xf8, 0x7c, 0x59, 0x16, 0x69, 0x0d, 0x13, 0x7d, + 0x54, 0x30, 0x49, 0x1b, 0xf6, 0x41, 0x6d, 0x32, 0x9f, 0xf3, 0x4a, 0x8c, + 0x6f, 0x1e, 0x80, 0x98, 0x25, 0xa9, 0x49, 0x6f, 0xfe, 0xcd, 0xce, 0x01, + 0x2a, 0x4d, 0x92, 0x36, 0x92, 0xcf, 0x44, 0x81, 0xba, 0x93, 0x23, 0x78, + 0xd9, 0x97, 0xa6, 0xec, 0xab, 0x45, 0x8c, 0x9a, 0x75, 0xbc, 0x3f, 0x55, + 0x57, 0x10, 0x31, 0xdd, 0x09, 0x1a, 0xd4, 0x9f, 0x82, 0xdf, 0x00, 0xa4, + 0x59, 0x68, 0xd0, 0x69, 0x8c, 0xf9, 0x6d, 0x95, 0xb5, 0x01, 0x68, 0xe2, + 0xc9, 0x26, 0x61, 0x93, 0xb0, 0x67, 0x3c, 0x47, 0xdc, 0x65, 0xb7, 0xf4, + 0x46, 0xc4, 0x2f, 0x57, 0x4d, 0xcd, 0x47, 0x4c, 0x04, 0x47, 0xde, 0xbd, + 0xf2, 0x66, 0xd6, 0xb3, 0x7d, 0x5c, 0xdf, 0x5d, 0xc3, 0xf9, 0x02, 0x6e, + 0xe3, 0x32, 0x53, 0x30, 0x9a, 0x6a, 0x8e, 0x85, 0x15, 0x4a, 0x0e, 0x79, + 0x1e, 0xd9, 0x9e, 0x4f, 0xd9, 0x2c, 0xfa, 0xaf, 0x98, 0xda, 0x92, 0x03, + 0x3d, 0x12, 0x5f, 0xe1, 0x21, 0xf1, 0xda, 0x63, 0xb3, 0xda, 0x65, 0x15, + 0x47, 0x63, 0x13, 0x8d, 0x91, 0xb1, 0x79, 0xd8, 0x60, 0xaa, 0x31, 0x7c, + 0x4a, 0x38, 0x58, 0xf3, 0x23, 0x4f, 0x30, 0x31, 0x18, 0xb2, 0xcb, 0x85, + 0xf1, 0xac, 0x0b, 0x74, 0xfb, 0x3c, 0x0e, 0x96, 0x60, 0x01, 0xc9, 0x86, + 0xd1, 0x64, 0xab, 0x7b, 0xb9, 0xe8, 0x00, 0x6c, 0x92, 0x47, 0xcd, 0x48, + 0x17, 0x07, 0xf8, 0xd4, 0x5f, 0xd6, 0x9e, 0x8d, 0xff, 0x24, 0xb0, 0x83, + 0xeb, 0x62, 0x5b, 0xf3, 0x7e, 0x0c, 0xdd, 0xcb, 0xf7, 0x9b, 0x68, 0xf0, + 0x39, 0xc0, 0x2d, 0x72, 0x20, 0xf0, 0x44, 0x30, 0x5f, 0x8f, 0x62, 0xcf, + 0x3c, 0xe1, 0xbd, 0x31, 0x93, 0x7e, 0xe9, 0x2f, 0xb4, 0x95, 0x52, 0x55, + 0x8c, 0x98, 0xa8, 0xa8, 0x5c, 0xa0, 0x33, 0x89, 0x06, 0x65, 0x4e, 0xb6, + 0x41, 0xc6, 0x4e, 0xaf, 0xc0, 0xd3, 0xcd, 0xcf, 0x05, 0x44, 0x8c, 0x93, + 0xfc, 0x45, 0xe7, 0x1c, 0x0a, 0x3b, 0x85, 0xd6, 0xb1, 0xeb, 0xa7, 0x54, + 0x07, 0x87, 0x2a, 0x14, 0xca, 0x93, 0x50, 0xff, 0x68, 0xc1, 0xcd, 0x58, + 0x8d, 0xd9, 0xb1, 0x22, 0xed, 0xd2, 0xf0, 0xf2, 0x8f, 0x11, 0xe0, 0xae, + 0x38, 0x38, 0x6d, 0x66, 0xc7, 0xe5, 0xe8, 0x6c, 0xae, 0x5f, 0x38, 0x76, + 0xaa, 0x3f, 0x93, 0x60, 0x58, 0xba, 0x4b, 0x7b, 0xa7, 0xdf, 0x28, 0x71, + 0xe9, 0xb0, 0x30, 0x91, 0x21, 0x90, 0x71, 0x41, 0xd8, 0xbe, 0xf0, 0x82, + 0x8a, 0xc5, 0xe4, 0xf6, 0x0e, 0x3e, 0xb6, 0xeb, 0x75, 0x31, 0xad, 0xa2, + 0xd9, 0x05, 0x80, 0xf2, 0xfc, 0x8c, 0x31, 0xd7, 0x79, 0x34, 0x4d, 0x27, + 0xa0, 0xdb, 0xdf, 0x26, 0x2f, 0x76, 0x3a, 0x7b, 0x71, 0x35, 0xb6, 0x48, + 0x34, 0x24, 0x76, 0xe7, 0x25, 0x8b, 0x20, 0x66, 0xac, 0x17, 0x11, 0xd5, + 0x6c, 0xf7, 0x07, 0xdb, 0x98, 0x1e, 0xe5, 0x3c, 0x90, 0x14, 0x31, 0xdf, + 0xe9, 0x1c, 0x12, 0xa8, 0x23, 0xd4, 0x57, 0xb2, 0xc3, 0x1f, 0x3c, 0xba, + 0x35, 0x08, 0xce, 0xff, 0x89, 0x20, 0xc3, 0x5a, 0x77, 0x30, 0xe6, 0x54, + 0x59, 0xfe, 0x9f, 0x29, 0xd4, 0x65, 0x2d, 0x0e, 0x15, 0x9d, 0xc2, 0x0d, + 0xff, 0x6f, 0x06, 0x66, 0xb3, 0xe5, 0xcd, 0x61, 0xe2, 0x51, 0xbe, 0x5f, + 0xf9, 0x99, 0xd7, 0x26, 0x35, 0xe7, 0x3a, 0x01, 0x8a, 0x0b, 0xf8, 0x60, + 0x70, 0x91, 0xdb, 0xb4, 0x4c, 0xf2, 0xff, 0xfb, 0x79, 0x40, 0x56, 0xa1, + 0x7d, 0xc2, 0x49, 0x27, 0xb6, 0x1b, 0xf5, 0xaf, 0xef, 0x0a, 0xca, 0x8c, + 0xd3, 0x34, 0xf3, 0x36, 0x12, 0xcf, 0x80, 0x3e, 0xf7, 0xc6, 0x0d, 0xf6, + 0x87, 0xc9, 0xd7, 0xf1, 0xd3, 0x6f, 0x59, 0xf8, 0x05, 0x4a, 0x97, 0xfa, + 0x70, 0x39, 0x2c, 0x68, 0x19, 0xee, 0xd1, 0x6d, 0x82, 0xa5, 0xce, 0x08, + 0x6c, 0xf7, 0x9a, 0x0a, 0x0d, 0x78, 0xef, 0xf2, 0x9c, 0x57, 0x72, 0x1f, + 0x1c, 0x3b, 0xf4, 0x3a, 0x56, 0xec, 0x18, 0x44, 0x50, 0xe1, 0x38, 0x02, + 0xe6, 0x59, 0x1e, 0xcd, 0x2c, 0x28, 0xe6, 0x07, 0x9e, 0xd2, 0x1c, 0xa1, + 0xe9, 0x6d, 0x71, 0x5b, 0xa1, 0x85, 0xe8, 0x2b, 0xec, 0x6e, 0x28, 0x65, + 0x6f, 0xf6, 0x14, 0x44, 0x97, 0x13, 0x37, 0xe0, 0xb7, 0x1a, 0x26, 0x8f, + 0xf0, 0x23, 0x77, 0x60, 0x7d, 0x84, 0xb2, 0xc6, 0xab, 0x94, 0x7d, 0x73, + 0x12, 0x25, 0xd1, 0xf9, 0xd0, 0xe9, 0x3f, 0x21, 0x39, 0xd4, 0x0a, 0xa1, + 0x77, 0x6e, 0x26, 0x03, 0x0c, 0xcc, 0x93, 0x7b, 0xf9, 0xbf, 0xa4, 0x2d, + 0xbe, 0x73, 0x28, 0x54, 0xbd, 0x66, 0x93, 0xd2, 0x37, 0x34, 0x7b, 0x8f, + 0x25, 0x0c, 0x9e, 0xee, 0x27, 0x24, 0x9f, 0x07, 0xda, 0xdf, 0x23, 0x61, + 0x7b, 0xc6, 0x81, 0xa5, 0x26, 0x57, 0xd5, 0x08, 0xa1, 0x17, 0x46, 0x48, + 0x6d, 0xcb, 0x34, 0x09, 0x37, 0xe9, 0xa5, 0xa3, 0x90, 0xe6, 0x5c, 0x38, + 0x8a, 0x0f, 0xa0, 0xe1, 0xe0, 0xf6, 0x37, 0xe7, 0x97, 0x96, 0x58, 0x3d, + 0x09, 0x86, 0x92, 0xf4, 0x3e, 0xa6, 0x08, 0x7d, 0x39, 0xad, 0x75, 0x57, + 0x1e, 0x4a, 0xb2, 0x6f, 0xed, 0xca, 0x39, 0xf5, 0xfa, 0x27, 0x42, 0x9b, + 0x4c, 0xd4, 0xcd, 0x8d, 0xc9, 0x3e, 0xd7, 0x42, 0x92, 0x40, 0x54, 0x37, + 0xf1, 0x90, 0x89, 0x7d, 0x6a, 0x63, 0x11, 0x2c, 0xf1, 0xaa, 0x14, 0x0b, + 0x13, 0x6e, 0x70, 0xbf, 0x79, 0x2d, 0xc2, 0xe9, 0xaa, 0x1e, 0x30, 0x9f, + 0x49, 0x0f, 0x1b, 0x79, 0xba, 0x60, 0x3c, 0x80, 0xc7, 0x13, 0x6b, 0x6f, + 0xd1, 0xf0, 0x1d, 0xe9, 0xb5, 0x74, 0x74, 0x24, 0x8a, 0x5c, 0xd8, 0x79, + 0x12, 0xf0, 0x08, 0x61, 0x7c, 0x4a, 0x3d, 0x7e, 0xec, 0xf6, 0x98, 0x91, + 0x22, 0x70, 0x8e, 0x8f, 0xfc, 0x2d, 0xf9, 0x86, 0xee, 0xec, 0xb7, 0x52, + 0xba, 0x7e, 0xe7, 0x51, 0x5a, 0xb9, 0x67, 0x3c, 0x86, 0xd5, 0x1f, 0x4e, + 0xee, 0x4b, 0xa7, 0x0a, 0x2c, 0x35, 0xf1, 0xf2, 0x7b, 0xf4, 0x87, 0x58, + 0x5e, 0xf8, 0x8d, 0x64, 0x8a, 0x03, 0x48, 0x22, 0xb9, 0x56, 0x53, 0x78, + 0xb7, 0xe6, 0xf2, 0xe9, 0x05, 0x9e, 0xf9, 0x07, 0x85, 0x2d, 0xf4, 0x41, + 0x88, 0xeb, 0x09, 0xa2, 0xdc, 0x63, 0x6e, 0xf1, 0x67, 0x63, 0x4b, 0x1f, + 0x9e, 0x9d, 0xb0, 0x47, 0x17, 0x14, 0x9d, 0x60, 0xa5, 0x9b, 0x68, 0x52, + 0x0b, 0x32, 0x4b, 0xdf, 0xb5, 0xbd, 0xa2, 0xb8, 0x52, 0x09, 0x59, 0xf9, + 0x66, 0x55, 0xd8, 0x2b, 0x79, 0x44, 0x7a, 0x5d, 0x3c, 0x72, 0x62, 0x68, + 0x15, 0x48, 0x5c, 0xc2, 0x1b, 0xd7, 0x53, 0x03, 0x7c, 0xc9, 0xe7, 0xd9, + 0x75, 0xcb, 0x6c, 0xe6, 0x30, 0xc1, 0x41, 0x55, 0x8f, 0xd2, 0xec, 0xcf, + 0x36, 0x50, 0x33, 0xd0, 0xb1, 0x5a, 0x6c, 0x5f, 0x2e, 0x61, 0x3e, 0xd1, + 0x15, 0xd7, 0xdf, 0xb9, 0x2d, 0x5c, 0x94, 0x59, 0x64, 0x91, 0x55, 0xaa, + 0x79, 0x67, 0x2b, 0xa2, 0x1b, 0x3e, 0xe5, 0x54, 0x32, 0xf0, 0xda, 0xee, + 0x27, 0xd1, 0x4d, 0x02, 0xaa, 0x86, 0x6e, 0xf3, 0x49, 0x73, 0xfc, 0x95, + 0x7d, 0xfc, 0xed, 0x23, 0xb6, 0x18, 0xe7, 0x00, 0x4e, 0xb3, 0x01, 0x68, + 0x10, 0x2b, 0xc4, 0x5a, 0xad, 0x96, 0xfe, 0xc2, 0xa7, 0x7d, 0x9e, 0xc7, + 0x25, 0x10, 0x83, 0xcc, 0xee, 0x9a, 0x7a, 0x6a, 0x48, 0x8a, 0xd9, 0xaf, + 0x1f, 0xe0, 0x14, 0xde, 0x93, 0x53, 0x63, 0xc3, 0x09, 0x58, 0x79, 0x53, + 0x4c, 0x15, 0x08, 0x9a, 0x1e, 0x9a, 0xca, 0x18, 0x78, 0x0e, 0xb0, 0xe2, + 0x4a, 0x18, 0xb6, 0x50, 0x45, 0x8f, 0xfe, 0x2a, 0xbd, 0x8e, 0xd3, 0xb1, + 0x62, 0x33, 0x96, 0xb6, 0x8f, 0xe6, 0xbb, 0xc4, 0x3c, 0x8e, 0x3b, 0x20, + 0x3b, 0x12, 0xf5, 0xd3, 0x2f, 0xcb, 0x34, 0x3b, 0x79, 0x03, 0xa1, 0x94, + 0xe1, 0xd2, 0x39, 0x25, 0x9a, 0xdb, 0xae, 0xcf, 0x13, 0x2e, 0x7a, 0x4b, + 0xa9, 0xb9, 0x13, 0xb8, 0x1e, 0x40, 0x0f, 0x2c, 0x84, 0x29, 0xdf, 0x19, + 0x3b, 0x5d, 0x1f, 0xd5, 0x2f, 0x15, 0x36, 0x9f, 0x2a, 0xaa, 0xd6, 0xd1, + 0xa7, 0xaa, 0xe4, 0x86, 0x22, 0x7b, 0x2f, 0x60, 0x3a, 0x9a, 0x70, 0x2c, + 0x38, 0x7a, 0xb2, 0x74, 0x0b, 0x69, 0x63, 0x1e, 0xe0, 0x88, 0xdb, 0xa8, + 0x21, 0xf3, 0xa3, 0xa5, 0x89, 0xa9, 0x78, 0xd6, 0x61, 0x16, 0x7a, 0x26, + 0xae, 0xec, 0x74, 0x54, 0x5a, 0x77, 0xf5, 0x08, 0x38, 0x40, 0x88, 0x8c, + 0x46, 0xf4, 0xeb, 0xdd, 0xd1, 0x65, 0x10, 0xb5, 0x31, 0x4f, 0x40, 0x1b, + 0x81, 0x36, 0xa9, 0xfe, 0x6c, 0xc7, 0xc3, 0xb1, 0x7f, 0x0c, 0xd2, 0xce, + 0x06, 0x7e, 0x64, 0xbd, 0x5a, 0xb6, 0x71, 0x53, 0x6d, 0x84, 0x63, 0xb8, + 0x8b, 0xa8, 0x2a, 0x38, 0x92, 0x77, 0x19, 0x6b, 0x00, 0xb7, 0x2c, 0x0f, + 0xbd, 0x5b, 0xc1, 0xea, 0x50, 0x6f, 0xfa, 0x75, 0x7b, 0xec, 0x2b, 0xd7, + 0x99, 0x64, 0x04, 0x29, 0x80, 0xb1, 0x8b, 0x90, 0x72, 0xd2, 0x1f, 0x06, + 0x8f, 0x45, 0x83, 0xeb, 0x8e, 0x9e, 0x75, 0x2a, 0xa2, 0x64, 0x42, 0xb2, + 0x1d, 0x97, 0x8a, 0xe6, 0x4d, 0xed, 0x83, 0x34, 0x59, 0x98, 0xa8, 0x38, + 0x94, 0x07, 0x89, 0x2e, 0x26, 0x15, 0x2e, 0xb5, 0xab, 0xbf, 0x8e, 0xba, + 0xfe, 0x58, 0x0c, 0x03, 0xd6, 0x51, 0x3f, 0x3c, 0xa5, 0xa8, 0x91, 0xaf, + 0x0f, 0xf5, 0x87, 0xc7, 0x98, 0xf1, 0x90, 0xfe, 0xdf, 0xf8, 0x4a, 0xdd, + 0x4b, 0x2e, 0x72, 0xbc, 0x15, 0xcb, 0x94, 0x83, 0x17, 0x23, 0x3e, 0x59, + 0x7b, 0x2b, 0x34, 0xeb, 0x9e, 0x28, 0x48, 0x34, 0x4d, 0x61, 0x07, 0xa0, + 0x5c, 0x63, 0x49, 0x32, 0x55, 0xf2, 0xfc, 0x75, 0x59, 0x72, 0xfe, 0xc7, + 0x17, 0x1e, 0x68, 0x6d, 0x22, 0x08, 0x82, 0xa4, 0xf1, 0xab, 0xc3, 0x89, + 0xa9, 0xa4, 0x65, 0xb3, 0x0c, 0xc3, 0x84, 0x43, 0xd6, 0x24, 0x60, 0x76, + 0x0d, 0x43, 0x9a, 0x7f, 0xa4, 0xca, 0x1d, 0xb9, 0xe2, 0x1b, 0x56, 0x67, + 0xb5, 0xa0, 0x1d, 0x6d, 0xde, 0x8c, 0x21, 0x25, 0x19, 0x9d, 0x33, 0x40, + 0x9e, 0xcc, 0x92, 0xad, 0x9d, 0x1d, 0x39, 0x9e, 0xc3, 0x1f, 0x71, 0x10, + 0xe4, 0xbd, 0x36, 0x28, 0xbc, 0xac, 0x93, 0x61, 0x97, 0x2a, 0x71, 0x00, + 0x74, 0x09, 0x6d, 0x5d, 0x7d, 0x57, 0xaa, 0x69, 0x3c, 0x7a, 0x8c, 0x9b, + 0x06, 0xf3, 0x3b, 0x26, 0xe6, 0x2c, 0x6e, 0x8a, 0x6d, 0x94, 0x96, 0xbf, + 0x01, 0xad, 0xdd, 0xd1, 0x70, 0x4f, 0xb1, 0x70, 0x0f, 0xab, 0xe0, 0xe2, + 0xbf, 0xc5, 0x0f, 0x50, 0xf0, 0x02, 0x4c, 0xd8, 0x0a, 0x27, 0xe6, 0xfb, + 0x17, 0xc6, 0x9f, 0x73, 0x03, 0x53, 0x06, 0xf1, 0xd8, 0x5e, 0x87, 0xb7, + 0x43, 0x72, 0xb6, 0x15, 0x47, 0x8f, 0x05, 0x2c, 0x93, 0x58, 0xdf, 0x3c, + 0xca, 0x00, 0xb9, 0x5d, 0x60, 0x69, 0x87, 0xef, 0x92, 0xa5, 0x3d, 0x3e, + 0x0c, 0x2a, 0x9e, 0x28, 0xab, 0x7e, 0x91, 0x6e, 0xdc, 0x65, 0xe5, 0xac, + 0xfa, 0xba, 0x2f, 0xf1, 0x7c, 0x6e, 0x77, 0x6f, 0x1a, 0xdb, 0x24, 0x4f, + 0x3b, 0x0e, 0xd5, 0x09, 0xb0, 0xed, 0xa3, 0x1d, 0xc1, 0x95, 0xef, 0x86, + 0x83, 0x03, 0xe1, 0x9f, 0x19, 0x6f, 0x36, 0x27, 0x2d, 0x14, 0x0a, 0xe4, + 0xe0, 0x41, 0xc0, 0xb9, 0x64, 0x5b, 0x55, 0x38, 0x01, 0xbb, 0x99, 0x79, + 0xf5, 0x25, 0xdd, 0x13, 0x43, 0x3c, 0xb8, 0xe3, 0x3f, 0xcb, 0xa6, 0x03, + 0x7d, 0x5e, 0x89, 0x90, 0x6c, 0xb7, 0x80, 0x95, 0xbf, 0xe0, 0x53, 0x50, + 0x34, 0x0b, 0x08, 0xcb, 0x59, 0x5f, 0x3b, 0x8e, 0x03, 0x12, 0xfe, 0x25, + 0xa4, 0x2f, 0x0c, 0x07, 0x19, 0x7f, 0x4c, 0xe0, 0x71, 0x97, 0xd6, 0x7e, + 0x4c, 0x0f, 0x25, 0xa8, 0x82, 0x07, 0x31, 0x0a, 0x8d, 0x96, 0x0b, 0x63, + 0x6a, 0x31, 0x8b, 0x0b, 0x49, 0x65, 0x37, 0x17, 0x79, 0x33, 0xa3, 0x6f, + 0xd2, 0xaa, 0x7c, 0x51, 0xeb, 0xf2, 0x3a, 0x14, 0xf7, 0xf0, 0x5c, 0xc5, + 0x36, 0xfd, 0x4d, 0xdf, 0x2b, 0xf3, 0xae, 0x7f, 0xac, 0x90, 0x97, 0x2b, + 0xa0, 0xa7, 0x43, 0xc0, 0x3a, 0x30, 0x91, 0x7f, 0x0a, 0x69, 0x0e, 0x59, + 0x16, 0xd0, 0x64, 0xeb, 0x2c, 0x23, 0xd4, 0x49, 0x76, 0x06, 0xef, 0x05, + 0xb0, 0xb6, 0x7b, 0x51, 0x76, 0xab, 0x32, 0x0a, 0x23, 0x13, 0xaa, 0x89, + 0xd9, 0x39, 0x63, 0xe1, 0xae, 0x7d, 0x66, 0xd3, 0xd0, 0xe9, 0x93, 0xe4, + 0x36, 0x83, 0x65, 0x6b, 0xce, 0x71, 0x06, 0x53, 0x73, 0x0e, 0x55, 0xd9, + 0xb2, 0x10, 0x3f, 0x64, 0xf9, 0xb9, 0x0a, 0x89, 0xae, 0x0a, 0x4e, 0x95, + 0xde, 0x49, 0xf1, 0xf5, 0xf2, 0xea, 0x73, 0x76, 0x3e, 0x47, 0xdf, 0x97, + 0xa4, 0x56, 0x7a, 0xa2, 0x53, 0xe7, 0x31, 0x7a, 0x6b, 0x8a, 0x0f, 0x30, + 0x88, 0xf8, 0x92, 0x6b, 0x29, 0x0c, 0xc5, 0x79, 0x6e, 0x0a, 0x05, 0xd5, + 0x53, 0xf1, 0x36, 0x93, 0xe2, 0xd7, 0xfd, 0xb8, 0x56, 0x6a, 0xd9, 0xc4, + 0x24, 0x51, 0xee, 0xea, 0xdb, 0xb0, 0x35, 0x7b, 0xaf, 0xdd, 0x49, 0xa0, + 0x47, 0x79, 0xb5, 0x8d, 0x2a, 0x91, 0x1c, 0xdb, 0x82, 0xdb, 0x87, 0x54, + 0x0a, 0x94, 0x8c, 0x9f, 0x96, 0xdf, 0x31, 0x30, 0x26, 0x8c, 0xe2, 0x50, + 0x76, 0x7a, 0x48, 0x12, 0xb9, 0x82, 0x79, 0xdd, 0x6e, 0xec, 0xda, 0xc1, + 0x86, 0xf3, 0x37, 0x8f, 0x54, 0x83, 0xb8, 0x1e, 0x26, 0x8e, 0x4b, 0x6c, + 0xef, 0x3e, 0xff, 0x5e, 0x78, 0x8d, 0x14, 0x8b, 0xee, 0x63, 0x8b, 0x89, + 0x72, 0xd6, 0xb7, 0x1d, 0x9d, 0x7f, 0xe5, 0xd4, 0xb6, 0x6e, 0x5c, 0xb1, + 0xd4, 0x72, 0x91, 0xbd, 0x98, 0x66, 0x28, 0xb2, 0x9f, 0xc6, 0xeb, 0x65, + 0xc4, 0x89, 0x29, 0x72, 0xd4, 0xf2, 0x16, 0x9b, 0x96, 0xc5, 0x18, 0x33, + 0xdf, 0x4a, 0x98, 0xfb, 0xff, 0x64, 0x1f, 0xbd, 0x3a, 0x98, 0xec, 0x58, + 0xc8, 0xec, 0xc5, 0xf5, 0x78, 0xfb, 0xa2, 0xf2, 0xa8, 0xda, 0xd7, 0x40, + 0x12, 0xec, 0xf0, 0xdc, 0x6b, 0xe1, 0x1c, 0x9f, 0xaa, 0x2b, 0x0d, 0xe3, + 0x0c, 0xe9, 0xcd, 0x54, 0x20, 0x0c, 0x69, 0xe1, 0x8f, 0x5c, 0x13, 0x6b, + 0x85, 0xf2, 0x87, 0x82, 0xeb, 0x46, 0x0f, 0x37, 0xfe, 0xb5, 0x01, 0x5a, + 0x56, 0x74, 0xa6, 0xc0, 0xdb, 0xf6, 0x15, 0x79, 0xae, 0xb3, 0x8f, 0xd6, + 0xc8, 0xb9, 0x30, 0x7a, 0x9c, 0xef, 0xc7, 0x5f, 0x8c, 0x7d, 0xb9, 0xdf, + 0x72, 0x66, 0xef, 0xf2, 0x58, 0xaf, 0x32, 0xd6, 0x7b, 0xc4, 0xab, 0xbe, + 0x03, 0x1e, 0xba, 0x0b, 0x5c, 0x46, 0xc4, 0x99, 0x9d, 0xfc, 0xc4, 0xd9, + 0x7c, 0xc3, 0x85, 0x84, 0x27, 0xad, 0x06, 0x6b, 0xc7, 0x25, 0xc5, 0xe7, + 0x58, 0x4f, 0x81, 0x66, 0x66, 0xa4, 0x05, 0xe0, 0xa2, 0x8b, 0x09, 0x18, + 0x7d, 0x11, 0x94, 0x2e, 0x37, 0xe8, 0xd2, 0x7f, 0x7f, 0xdc, 0x0c, 0xaf, + 0x35, 0xc7, 0x6d, 0xf9, 0xc6, 0x66, 0xf8, 0x11, 0x34, 0xb7, 0x7c, 0x2f, + 0xd4, 0x80, 0x14, 0x21, 0xc8, 0x13, 0x67, 0x29, 0x64, 0xd9, 0x5d, 0x70, + 0x27, 0x12, 0x4f, 0xfb, 0x55, 0xa8, 0xf8, 0x2e, 0x04, 0xd9, 0x70, 0x43, + 0x36, 0x8f, 0xe8, 0x33, 0xa2, 0xbb, 0x1a, 0xb2, 0x5d, 0xfb, 0x25, 0x00, + 0x36, 0xf2, 0x47, 0x26, 0x24, 0x01, 0xc0, 0x0a, 0xd4, 0x54, 0x84, 0x84, + 0xc8, 0xfb, 0x68, 0x54, 0x1c, 0xb1, 0xa4, 0x58, 0x9b, 0x10, 0xd5, 0xea, + 0xdc, 0x47, 0x5d, 0xc2, 0xa8, 0x31, 0xf5, 0x0d, 0x15, 0xcd, 0x10, 0x13, + 0x5a, 0xad, 0x50, 0x7b, 0x26, 0xea, 0x4e, 0x95, 0xa2, 0xd5, 0x1f, 0x5c, + 0x6f, 0x05, 0x18, 0x8f, 0xf4, 0xb9, 0x7c, 0x73, 0xd9, 0xa5, 0x44, 0xfb, + 0x0e, 0x01, 0xb5, 0x9e, 0xa2, 0xc8, 0xe3, 0x0a, 0x00, 0xda, 0x10, 0xd5, + 0xaf, 0x61, 0x6e, 0xbe, 0xf0, 0x19, 0xdb, 0x4b, 0x04, 0x2a, 0x61, 0xc9, + 0x01, 0x80, 0x45, 0x7d, 0xe9, 0x24, 0x4d, 0x35, 0x8c, 0x78, 0xcf, 0xbc, + 0x6e, 0x29, 0x8e, 0xf1, 0xe1, 0x62, 0x50, 0x33, 0xfc, 0x40, 0x1e, 0xc7, + 0xef, 0x8f, 0x58, 0xc2, 0x29, 0xbd, 0x2b, 0x3e, 0x3e, 0x51, 0x78, 0xb5, + 0xf4, 0x17, 0x45, 0xd3, 0xd4, 0xd0, 0x73, 0x4d, 0xc0, 0x89, 0xf6, 0x26, + 0xf5, 0x4d, 0x9d, 0x71, 0x62, 0x64, 0x77, 0xec, 0x1c, 0x82, 0x16, 0x64, + 0x8c, 0x04, 0x1a, 0x38, 0xc5, 0xb7, 0x33, 0x1b, 0x4d, 0x65, 0x3b, 0x28, + 0x12, 0xb5, 0xfe, 0x6a, 0xa6, 0xae, 0x28, 0xe7, 0xc8, 0xa6, 0x8c, 0xcf, + 0x13, 0x79, 0x71, 0x69, 0xff, 0xc5, 0xeb, 0xd1, 0xf1, 0xaf, 0xf9, 0xb3, + 0xca, 0x52, 0xe9, 0xf5, 0xf1, 0x23, 0x0a, 0x04, 0x2a, 0x7f, 0x89, 0x15, + 0x59, 0x0a, 0x47, 0x68, 0x0f, 0x83, 0x7c, 0xff, 0x66, 0xe1, 0x54, 0x0b, + 0xb9, 0x54, 0x67, 0x66, 0x8e, 0x44, 0x1d, 0xbf, 0x54, 0x08, 0xc8, 0xcc, + 0xd2, 0xb2, 0x73, 0x1e, 0xb8, 0x44, 0x9e, 0xad, 0xd6, 0xe0, 0x32, 0x1c, + 0x12, 0x72, 0x5f, 0x1b, 0x7b, 0xdc, 0x91, 0x60, 0x37, 0x9d, 0xa9, 0xe9, + 0x3f, 0x5e, 0xde, 0x65, 0xde, 0xc6, 0x80, 0x93, 0x3d, 0x7b, 0xdd, 0x43, + 0xf2, 0x99, 0xbf, 0x7e, 0xff, 0xed, 0x05, 0x9a, 0xa1, 0x20, 0x28, 0x9a, + 0x31, 0x5a, 0xff, 0x40, 0x4b, 0xc9, 0x6d, 0xa6, 0x03, 0xab, 0xbc, 0x63, + 0x98, 0x6c, 0x93, 0xe1, 0xf7, 0xed, 0x2e, 0x9d, 0x30, 0x55, 0xab, 0x08, + 0x84, 0x77, 0x11, 0x72, 0xee, 0x85, 0xaf, 0xdf, 0xf2, 0x5c, 0x5d, 0x59, + 0x9f, 0xa1, 0x54, 0x1b, 0x38, 0xf9, 0x51, 0xbd, 0x7a, 0x91, 0x95, 0x68, + 0xc6, 0xec, 0x8a, 0x07, 0x8f, 0x73, 0xf2, 0xbd, 0x42, 0x7e, 0xe3, 0x69, + 0x2d, 0x30, 0x23, 0xe7, 0x7b, 0x50, 0x8e, 0xa7, 0x92, 0xdc, 0x06, 0xa8, + 0xdd, 0xe7, 0x9e, 0xf8, 0x7f, 0xf6, 0x8b, 0xb4, 0x06, 0x4f, 0x28, 0x6b, + 0xd5, 0x4d, 0x32, 0x69, 0x9e, 0x34, 0x8b, 0x3a, 0x05, 0x06, 0x87, 0x40, + 0x7b, 0x79, 0x0a, 0xb6, 0x30, 0x4c, 0x43, 0xc5, 0x13, 0xcf, 0x29, 0xd1, + 0x1c, 0xbb, 0x93, 0xa4, 0xec, 0xe5, 0x3b, 0xe4, 0x01, 0x59, 0x50, 0x24, + 0x6e, 0xc5, 0x13, 0xd1, 0x60, 0x22, 0x88, 0x0a, 0x51, 0xc0, 0xae, 0x97, + 0x9b, 0x14, 0xdf, 0x0a, 0x3a, 0x0c, 0xbf, 0xef, 0x98, 0xd4, 0xee, 0x86, + 0xf6, 0xc9, 0xe0, 0xd8, 0xd7, 0xd0, 0x70, 0x61, 0x11, 0x30, 0xc7, 0xdd, + 0x5f, 0xf6, 0x97, 0x26, 0x9d, 0xcf, 0x1e, 0x23, 0x1d, 0x58, 0xe4, 0xa3, + 0x5a, 0x41, 0x5e, 0x85, 0x08, 0x26, 0x45, 0x6b, 0x8b, 0x4c, 0xba, 0x46, + 0xee, 0x53, 0xba, 0x72, 0x55, 0xa5, 0x29, 0x04, 0x94, 0x05, 0xb7, 0x4e, + 0xe1, 0x04, 0xd0, 0xa2, 0x4d, 0x92, 0xb3, 0x72, 0x96, 0x14, 0x2b, 0x63, + 0xef, 0xdd, 0x9c, 0xc2, 0x16, 0x36, 0xb3, 0x03, 0x09, 0x15, 0x15, 0x33, + 0xeb, 0x16, 0xb9, 0xdc, 0x75, 0x26, 0xfe, 0xf5, 0xd6, 0xe5, 0xa0, 0x02, + 0x58, 0x12, 0x85, 0x8d, 0x2c, 0xad, 0x7b, 0x84, 0x6b, 0xe7, 0xaa, 0x02, + 0x10, 0x21, 0x09, 0x55, 0xdb, 0xfb, 0x95, 0x66, 0x28, 0xa6, 0xc4, 0x8b, + 0x41, 0x60, 0x10, 0xce, 0xe0, 0xf3, 0x93, 0x2e, 0x62, 0xe6, 0xde, 0xf2, + 0xad, 0x61, 0x6e, 0xa7, 0x26, 0x4e, 0xbc, 0x0a, 0xe6, 0xd2, 0xee, 0x0e, + 0x94, 0x1b, 0x85, 0x8f, 0xa7, 0x1d, 0x28, 0x7a, 0xc9, 0x77, 0x43, 0x9b, + 0xfc, 0x9e, 0xb9, 0x79, 0x38, 0x5e, 0xf4, 0xd1, 0x53, 0xd7, 0xe1, 0xec, + 0xa3, 0x31, 0x3c, 0x3c, 0x15, 0x51, 0xb8, 0x79, 0xa0, 0xce, 0x8c, 0x9c, + 0x1d, 0xd9, 0x51, 0x3e, 0xe7, 0x73, 0x03, 0x2e, 0xc0, 0xe0, 0xbc, 0xc6, + 0xbc, 0x94, 0x58, 0x0b, 0xe4, 0x30, 0x4b, 0xfa, 0xfc, 0xe0, 0xee, 0x93, + 0x18, 0x8b, 0x47, 0x1e, 0xd2, 0x12, 0x46, 0x59, 0x02, 0xff, 0xb2, 0x29, + 0xd4, 0xa2, 0x8f, 0xda, 0xc1, 0x1b, 0xf5, 0x85, 0xf4, 0xfb, 0x7d, 0x23, + 0x8f, 0x44, 0x9e, 0x47, 0x9e, 0xae, 0xfa, 0x84, 0xe5, 0x4f, 0xc6, 0x35, + 0xbc, 0xce, 0x33, 0xbd, 0x46, 0x71, 0x2b, 0x88, 0xc9, 0xd4, 0xf7, 0x04, + 0x42, 0xcd, 0xeb, 0xdf, 0x9c, 0x6d, 0x7b, 0x89, 0x3c, 0x52, 0xfd, 0xfe, + 0x38, 0x5a, 0x07, 0x0f, 0x93, 0x98, 0x27, 0xfc, 0x32, 0x75, 0x9e, 0x4d, + 0x3c, 0x73, 0x42, 0x8d, 0x04, 0x40, 0x4b, 0x22, 0x05, 0x65, 0x79, 0x87, + 0xf7, 0x48, 0x65, 0x08, 0xd3, 0xd7, 0x9e, 0xfa, 0xfc, 0x0d, 0x44, 0x2d, + 0xf2, 0xc2, 0x60, 0x13, 0x62, 0xee, 0x17, 0x15, 0x61, 0xae, 0x62, 0xca, + 0xab, 0x66, 0x31, 0xd8, 0xb5, 0xad, 0x36, 0x21, 0x56, 0xb6, 0x42, 0x77, + 0x4c, 0x83, 0xbe, 0xbd, 0x61, 0x8a, 0xbc, 0x3f, 0x45, 0xc0, 0x85, 0x63, + 0x85, 0x99, 0x45, 0x2c, 0x65, 0x13, 0x58, 0xcf, 0xc6, 0xe8, 0x52, 0x64, + 0x0d, 0xbb, 0xdc, 0xec, 0x6c, 0xba, 0x81, 0xf7, 0x5f, 0x24, 0x53, 0x70, + 0xed, 0x31, 0x46, 0x0f, 0xaf, 0x42, 0x03, 0x7e, 0x9e, 0xeb, 0xd6, 0x55, + 0xb0, 0xb7, 0xcf, 0x2f, 0xd2, 0xa7, 0x80, 0xc6, 0xdb, 0xf9, 0x34, 0x9a, + 0xee, 0xf6, 0x13, 0x28, 0xa5, 0x7e, 0xed, 0xb9, 0x95, 0x3d, 0x33, 0xce, + 0x87, 0xba, 0x45, 0xb9, 0x52, 0x7f, 0xc1, 0xe4, 0xd0, 0xd9, 0xc8, 0xcf, + 0xaf, 0x75, 0x8f, 0x9d, 0xf6, 0xa2, 0x81, 0x28, 0x43, 0x29, 0x6c, 0xd5, + 0x1a, 0x3a, 0x20, 0x6c, 0x6e, 0x61, 0x2f, 0x0c, 0x6f, 0xa9, 0x72, 0xc9, + 0x2f, 0xbc, 0x64, 0xb8, 0x08, 0xdb, 0x8a, 0xc5, 0xdd, 0xf6, 0x73, 0x25, + 0x45, 0x5d, 0xb9, 0x23, 0xbe, 0x2f, 0x62, 0xde, 0xa7, 0xe6, 0xb9, 0x78, + 0x31, 0xe1, 0xe0, 0x4c, 0xfe, 0x11, 0x13, 0x61, 0xb5, 0xfc, 0x61, 0x61, + 0xca, 0x30, 0xf5, 0xc9, 0x60, 0x86, 0x6d, 0x1f, 0xfb, 0x5e, 0xb0, 0x5a, + 0x98, 0x92, 0xd2, 0x4a, 0x95, 0xad, 0x9c, 0xca, 0xa3, 0xe1, 0x44, 0xa0, + 0xe3, 0x7a, 0xaf, 0xac, 0x22, 0x23, 0x8e, 0x03, 0x76, 0xe3, 0x60, 0x05, + 0xe8, 0x6b, 0x41, 0x00, 0xef, 0xed, 0x81, 0x9d, 0x0a, 0xb6, 0xd9, 0x14, + 0xd5, 0xfd, 0xcb, 0x9e, 0x14, 0x21, 0x44, 0x8d, 0xe2, 0xc8, 0xcd, 0x8d, + 0x2d, 0xb1, 0x7d, 0xb6, 0x3f, 0xeb, 0x40, 0x6d, 0x5f, 0x17, 0xc5, 0x5f, + 0x8a, 0x32, 0x4a, 0xc6, 0x17, 0x7d, 0x78, 0xb2, 0x50, 0x2f, 0xea, 0x34, + 0x71, 0xbf, 0x6d, 0xf1, 0xb7, 0xd3, 0x64, 0xde, 0xe4, 0xb0, 0x01, 0x87, + 0x63, 0x07, 0x5f, 0x55, 0x63, 0x39, 0x63, 0x9f, 0x65, 0xe0, 0x23, 0x27, + 0xc1, 0xac, 0x94, 0x47, 0x5b, 0x56, 0x49, 0x62, 0x8e, 0xf9, 0x46, 0x79, + 0x9d, 0xad, 0x43, 0xc0, 0x2c, 0x6b, 0xb0, 0xf6, 0x18, 0xa6, 0x15, 0x23, + 0xc6, 0x08, 0x41, 0xb4, 0xdd, 0xab, 0xdc, 0x7e, 0x8b, 0x62, 0xa4, 0xcd, + 0x5f, 0x3c, 0x88, 0x1d, 0x57, 0x9b, 0x0a, 0xbc, 0x1d, 0xd5, 0x89, 0xa7, + 0x05, 0x57, 0x34, 0xd4, 0x77, 0x61, 0x66, 0xc5, 0xb1, 0x08, 0x2b, 0x32, + 0x84, 0xd0, 0x2f, 0x16, 0x88, 0x43, 0x03, 0x55, 0x2d, 0xa2, 0x9b, 0xfa, + 0xe1, 0x4a, 0xf1, 0x0c, 0x86, 0x2d, 0xc7, 0x8c, 0x2b, 0x3a, 0x60, 0xe1, + 0x81, 0xd7, 0xac, 0xb4, 0x42, 0x86, 0xa1, 0x0d, 0x0d, 0x1b, 0x72, 0xce, + 0xe2, 0x52, 0x77, 0xfc, 0xb1, 0x18, 0xf9, 0xe9, 0xc6, 0x6b, 0x19, 0xdb, + 0x14, 0x11, 0x73, 0xe5, 0x88, 0xf8, 0xd9, 0xc8, 0x66, 0xbb, 0xb5, 0xbd, + 0xa4, 0xef, 0xea, 0x6e, 0xfa, 0x36, 0x56, 0xed, 0x9e, 0xcb, 0x63, 0x4b, + 0x1a, 0x32, 0x5a, 0xde, 0x7a, 0xe0, 0xf7, 0xe1, 0x0c, 0x3b, 0xe7, 0x1a, + 0x82, 0x14, 0xc3, 0xa8, 0x2e, 0xf3, 0x77, 0xae, 0x66, 0xae, 0x70, 0x11, + 0xb7, 0xc4, 0xc2, 0x6a, 0x6a, 0x93, 0xae, 0xba, 0xc5, 0x64, 0x84, 0xd9, + 0x49, 0xa8, 0x13, 0xcc, 0x77, 0xc7, 0x0c, 0xed, 0x7e, 0xe1, 0xc2, 0x3e, + 0xe0, 0x72, 0x22, 0x64, 0x82, 0x98, 0xc8, 0x50, 0x60, 0xf4, 0xe5, 0x82, + 0xa7, 0x77, 0x35, 0xcd, 0xff, 0xae, 0x7e, 0xe7, 0x72, 0xa3, 0xad, 0x17, + 0xa1, 0x73, 0x53, 0x50, 0x1d, 0x66, 0x2c, 0x89, 0x68, 0x48, 0x36, 0xa3, + 0xc6, 0x8d, 0xa8, 0x4b, 0x95, 0x8f, 0x76, 0x0a, 0x4a, 0x9f, 0xf8, 0x0a, + 0x20, 0x28, 0xc4, 0x4e, 0x65, 0xe2, 0xf8, 0x83, 0x38, 0xcf, 0xd1, 0xda, + 0xad, 0xb8, 0x8a, 0x55, 0x60, 0x7f, 0xaa, 0xd6, 0x69, 0xef, 0xe1, 0xf2, + 0x7f, 0x3f, 0xe0, 0x3c, 0xfd, 0x00, 0xaa, 0x43, 0xef, 0xfa, 0x6b, 0x7f, + 0x21, 0xc8, 0x11, 0x0b, 0x30, 0x68, 0x9d, 0x22, 0x40, 0x19, 0x81, 0xfe, + 0xbc, 0x79, 0x18, 0x7d, 0x3b, 0x99, 0x06, 0x7d, 0xf8, 0x77, 0xf8, 0x1f, + 0x67, 0x75, 0x28, 0xdf, 0x79, 0x62, 0xf2, 0xe7, 0xed, 0x2d, 0x1e, 0xc9, + 0x2a, 0x0d, 0xb1, 0xa2, 0xc2, 0x67, 0x88, 0xb5, 0x9e, 0x4d, 0x43, 0x72, + 0xae, 0x62, 0x75, 0x9c, 0xb3, 0xe4, 0x9d, 0x14, 0x98, 0x1c, 0x80, 0x11, + 0xc5, 0x01, 0x03, 0x66, 0x90, 0xd1, 0xaa, 0xc5, 0x15, 0x29, 0xd6, 0x57, + 0x23, 0xca, 0x04, 0xd4, 0x43, 0x8f, 0x6d, 0xf0, 0x74, 0x45, 0xc7, 0x1a, + 0x88, 0x2d, 0xe9, 0xf4, 0x38, 0x16, 0x71, 0xa6, 0x70, 0x79, 0x78, 0x54, + 0xb9, 0xdb, 0x51, 0x9b, 0x8d, 0xa7, 0x9b, 0x04, 0xd1, 0x2e, 0x88, 0x8f, + 0x6b, 0xf3, 0xcf, 0xe4, 0x39, 0x60, 0xac, 0x6a, 0x7a, 0x4f, 0x69, 0x17, + 0x3a, 0x95, 0x06, 0x69, 0x7c, 0xa1, 0x75, 0xf8, 0x2a, 0x4f, 0xc0, 0x66, + 0x25, 0x5f, 0x7b, 0x92, 0x34, 0xcd, 0x04, 0x71, 0x68, 0x3c, 0x6d, 0x65, + 0x83, 0x71, 0x7d, 0x66, 0x99, 0x31, 0xf7, 0xde, 0xcd, 0x12, 0xd7, 0xed, + 0xbd, 0x2f, 0x65, 0x4a, 0x4e, 0xf1, 0x9a, 0x8c, 0x9d, 0x0f, 0xe8, 0xf7, + 0x25, 0x2f, 0xd0, 0xf5, 0x59, 0xef, 0x20, 0xfa, 0x9f, 0xeb, 0x68, 0xff, + 0x48, 0xc6, 0xad, 0x5c, 0x53, 0x9f, 0x5e, 0xc3, 0x5c, 0xe8, 0xdb, 0x79, + 0x85, 0x4b, 0x99, 0x87, 0x42, 0xa5, 0xec, 0x2a, 0xf9, 0x9c, 0x91, 0x27, + 0x0f, 0x4c, 0x18, 0x40, 0x70, 0x01, 0x72, 0x84, 0xd8, 0xe3, 0x20, 0x0d, + 0x74, 0xb3, 0x69, 0x4b, 0x45, 0x0b, 0x88, 0x3a, 0x53, 0x97, 0x3b, 0x47, + 0x1c, 0xc4, 0xb7, 0x70, 0xad, 0xab, 0x9a, 0xfc, 0x51, 0xe3, 0x4b, 0x50, + 0x43, 0x7f, 0x2b, 0x74, 0xe8, 0xce, 0x85, 0xe4, 0xe6, 0xe7, 0xa1, 0x57, + 0x13, 0x57, 0xf9, 0x76, 0x11, 0x75, 0x52, 0xd7, 0x00, 0xc0, 0x11, 0x1d, + 0x3e, 0x09, 0xb9, 0x20, 0x44, 0x42, 0xf3, 0x9b, 0x2a, 0xfb, 0x5d, 0x94, + 0xa6, 0xaa, 0x06, 0x8f, 0x8f, 0x6f, 0x45, 0xe0, 0xd4, 0x92, 0xd8, 0x76, + 0x74, 0x0b, 0x22, 0x6f, 0xab, 0x6d, 0x68, 0x13, 0x65, 0xe3, 0x6b, 0x04, + 0xe1, 0xe6, 0xc6, 0x29, 0x1c, 0x38, 0x75, 0x3e, 0xdd, 0x17, 0x9d, 0x6e, + 0x5b, 0x17, 0xaf, 0x48, 0x31, 0x0a, 0x8d, 0xe3, 0x89, 0xe3, 0xf4, 0x19, + 0xde, 0x49, 0xde, 0xd0, 0x46, 0x48, 0xdb, 0x4d, 0x15, 0x36, 0xb9, 0xaf, + 0xa4, 0xe2, 0xa0, 0x26, 0xe2, 0x69, 0x43, 0x7a, 0x22, 0x5d, 0x0f, 0x0d, + 0xa4, 0x57, 0x67, 0x79, 0xea, 0x0a, 0x7f, 0x48, 0xfe, 0x7c, 0xe4, 0xc4, + 0x9d, 0x74, 0xd7, 0xfb, 0x8c, 0x41, 0xc4, 0x6d, 0x96, 0x4c, 0xcb, 0x6e, + 0xf4, 0x18, 0x16, 0x59, 0x62, 0x36, 0x2f, 0x68, 0xd5, 0xb4, 0xbb, 0x71, + 0xa4, 0x6b, 0xd5, 0x7b, 0x45, 0xcb, 0x06, 0x80, 0x99, 0xaf, 0x06, 0x09, + 0x97, 0xab, 0xf5, 0xb7, 0x02, 0x01, 0x81, 0x65, 0x7d, 0xae, 0x09, 0x69, + 0xcb, 0x31, 0x6c, 0x9d, 0x9e, 0x9a, 0x85, 0xc5, 0x3e, 0x21, 0xf3, 0xc3, + 0x73, 0x82, 0x26, 0x5f, 0x58, 0x73, 0x3b, 0xf6, 0xce, 0xf6, 0x83, 0xe1, + 0x30, 0x59, 0xea, 0xc3, 0xfc, 0x9c, 0xa8, 0x28, 0xa4, 0xf5, 0x2f, 0xb1, + 0xcf, 0xaa, 0xa6, 0xb8, 0x6b, 0xcc, 0x10, 0xa0, 0x87, 0x1a, 0xa9, 0x5b, + 0xcc, 0xe4, 0x40, 0xd5, 0x50, 0x12, 0x35, 0x8a, 0x31, 0xf5, 0xd0, 0x5b, + 0x0d, 0x85, 0xd2, 0x59, 0x5f, 0x1b, 0x15, 0x4a, 0x1c, 0x01, 0x3d, 0x03, + 0x40, 0x32, 0xa3, 0xe9, 0x50, 0x96, 0x1a, 0xc6, 0xd6, 0x65, 0x3b, 0x26, + 0xb4, 0x8d, 0x5a, 0xc4, 0x0d, 0xc2, 0x95, 0xa0, 0x67, 0x97, 0xe9, 0x07, + 0x6c, 0x7e, 0x85, 0x6a, 0xc9, 0x38, 0xf3, 0x25, 0xca, 0xd6, 0x8a, 0x49, + 0x16, 0xa5, 0x43, 0x5c, 0x6e, 0xc9, 0xfa, 0x1e, 0x0c, 0x79, 0x3e, 0x2d, + 0x12, 0x37, 0xa9, 0x1c, 0x5b, 0xe5, 0x3e, 0x6e, 0x67, 0x69, 0xcd, 0xac, + 0x59, 0xec, 0x5c, 0x1e, 0x1a, 0xd1, 0x99, 0x7e, 0x68, 0x53, 0xa5, 0xda, + 0x0e, 0xb1, 0x94, 0x04, 0xc2, 0xd6, 0x2f, 0xb0, 0x33, 0x8e, 0xbc, 0x50, + 0x6a, 0x4c, 0x46, 0x76, 0x03, 0x05, 0x3d, 0x21, 0x9d, 0x1a, 0xbd, 0x61, + 0x36, 0x6d, 0xe6, 0x79, 0x00, 0x98, 0xe9, 0xde, 0x33, 0x50, 0xc5, 0x70, + 0xc9, 0x52, 0xc8, 0xf9, 0x21, 0xe7, 0x60, 0xbf, 0x63, 0x0f, 0x9f, 0x31, + 0x5b, 0xdb, 0xb8, 0xe7, 0x31, 0x4c, 0xbe, 0x33, 0x98, 0x50, 0xa9, 0x0a, + 0x91, 0x6c, 0x37, 0xdf, 0xda, 0xa4, 0x51, 0x02, 0x1c, 0x0c, 0x7c, 0x64, + 0x65, 0x90, 0xdf, 0x91, 0x92, 0xd2, 0xee, 0xb0, 0x28, 0x90, 0x3b, 0x3b, + 0x17, 0x75, 0x2d, 0xbc, 0x5b, 0x38, 0x9d, 0x7a, 0x79, 0x86, 0x73, 0x78, + 0x30, 0xbd, 0x23, 0xa3, 0x68, 0xc8, 0x92, 0x60, 0x59, 0xf6, 0x9d, 0xfb, + 0x4c, 0xee, 0x93, 0x15, 0xad, 0x17, 0x27, 0xd7, 0xe4, 0x61, 0xcf, 0xd5, + 0xad, 0xac, 0x3c, 0xc5, 0x38, 0x10, 0x36, 0xeb, 0xb2, 0x4f, 0x01, 0x89, + 0x84, 0xfb, 0x03, 0x2c, 0xf0, 0xeb, 0x09, 0x0b, 0x48, 0xf0, 0x1e, 0x30, + 0x22, 0xcd, 0x40, 0xa9, 0xb9, 0x27, 0x99, 0xc6, 0xc7, 0x7e, 0x69, 0xdd, + 0x58, 0xdb, 0x82, 0x94, 0x26, 0xe8, 0x08, 0x32, 0xc6, 0xe8, 0x6f, 0xbf, + 0xe6, 0x68, 0xb6, 0xdc, 0x20, 0xc8, 0x8f, 0x7d, 0xd5, 0x16, 0xcf, 0x72, + 0x23, 0xd8, 0x81, 0x18, 0x30, 0x31, 0x91, 0xbc, 0xd9, 0xbd, 0xdf, 0x62, + 0xfd, 0x91, 0x14, 0xe4, 0xcc, 0x18, 0xf2, 0x5e, 0x1a, 0xb7, 0x40, 0x76, + 0xed, 0x43, 0x4d, 0xb5, 0x3f, 0xf2, 0xb9, 0x8d, 0x3d, 0xe7, 0x6f, 0x97, + 0x21, 0x4d, 0xec, 0xc0, 0x60, 0x6f, 0x6b, 0x62, 0xc9, 0xed, 0xc4, 0x51, + 0xd7, 0x93, 0xda, 0x35, 0x25, 0x64, 0x3b, 0xd8, 0x25, 0x51, 0xfd, 0x0f, + 0x72, 0x53, 0x09, 0xad, 0x71, 0x7a, 0xf0, 0x5e, 0xdf, 0x3c, 0x1b, 0x57, + 0x77, 0xc1, 0x94, 0x4c, 0xbf, 0xb7, 0x62, 0x77, 0x00, 0x94, 0xf7, 0x00, + 0x64, 0x91, 0xc1, 0x3c, 0xe2, 0x59, 0xe9, 0x08, 0x82, 0xa7, 0x3b, 0x2e, + 0x5f, 0xc0, 0x1b, 0xdf, 0x89, 0xfb, 0xe3, 0x9f, 0x56, 0xc8, 0x65, 0x91, + 0xd8, 0x79, 0xbb, 0x25, 0xcc, 0xd4, 0xc7, 0x2f, 0x1c, 0x25, 0xba, 0x0a, + 0xde, 0x75, 0x1b, 0x05, 0xd5, 0x50, 0xc4, 0x3d, 0xb4, 0x02, 0x6b, 0x5a, + 0x8a, 0x37, 0xee, 0xfb, 0xf0, 0x72, 0x07, 0x5e, 0x5f, 0x2a, 0xd5, 0xb8, + 0xcd, 0xef, 0x36, 0x47, 0x33, 0x58, 0xf6, 0x43, 0xf8, 0xe0, 0xf2, 0x81, + 0x3a, 0xf2, 0x56, 0x69, 0x01, 0xb4, 0xdf, 0x45, 0x8c, 0x42, 0xf8, 0xd2, + 0xd9, 0x47, 0x3b, 0xd1, 0xe8, 0xcf, 0x6e, 0x49, 0xf1, 0x29, 0x23, 0x8c, + 0x78, 0x76, 0x99, 0x19, 0xb9, 0x5f, 0x59, 0x97, 0xce, 0x7b, 0x74, 0x51, + 0xb6, 0x67, 0x89, 0x72, 0x4c, 0xf3, 0x76, 0x00, 0x8e, 0x5b, 0xc0, 0x28, + 0xdd, 0x41, 0xbc, 0x49, 0x04, 0x5c, 0x56, 0xac, 0x93, 0x66, 0x3c, 0x54, + 0x3d, 0xf3, 0x3d, 0xe3, 0x74, 0xa3, 0x7b, 0xbc, 0xc6, 0x1d, 0x53, 0xc9, + 0xcd, 0x45, 0x80, 0x0f, 0xb3, 0x25, 0xdf, 0xaa, 0x22, 0x5e, 0x7d, 0x57, + 0x9f, 0x3d, 0x35, 0x11, 0x91, 0xc9, 0x6c, 0xee, 0x64, 0x92, 0x2f, 0xe6, + 0xdc, 0x6f, 0x9d, 0x78, 0x04, 0xd5, 0xf2, 0xe6, 0x18, 0x90, 0xbb, 0x6d, + 0xaa, 0x54, 0xc4, 0x74, 0x5d, 0xc8, 0x8b, 0x21, 0xb2, 0x03, 0x9a, 0x52, + 0x79, 0x9d, 0x1a, 0x0b, 0x5a, 0x94, 0x76, 0x6c, 0x1d, 0xd2, 0x70, 0xc7, + 0xd4, 0x03, 0x05, 0x7c, 0x06, 0x8d, 0xa4, 0x41, 0xea, 0xa2, 0x8b, 0x5c, + 0xe5, 0x45, 0x9b, 0x33, 0x9a, 0x07, 0x3f, 0x61, 0x67, 0xba, 0x41, 0x64, + 0x36, 0x20, 0xc8, 0x7e, 0xea, 0xb5, 0x44, 0xed, 0xe6, 0xfe, 0xa9, 0xf4, + 0x04, 0x07, 0xc0, 0xd9, 0x29, 0x7c, 0x77, 0x3a, 0x60, 0x41, 0xce, 0x20, + 0x60, 0xfe, 0x3c, 0x29, 0xc1, 0xa2, 0x0c, 0x25, 0xb2, 0x37, 0x69, 0xd0, + 0x2e, 0x52, 0xc0, 0xdf, 0x6b, 0x7e, 0xfc, 0x0e, 0x3a, 0xcb, 0x48, 0x9d, + 0x88, 0x43, 0x50, 0x31, 0x50, 0x53, 0x8d, 0x05, 0xa6, 0xd5, 0xf5, 0x0a, + 0x2f, 0x97, 0x65, 0xe8, 0xb2, 0x5c, 0x52, 0xd2, 0x1a, 0x8e, 0xa9, 0xb4, + 0xae, 0x75, 0x3b, 0xbd, 0x47, 0x3b, 0x1c, 0x5d, 0x38, 0x5d, 0xe8, 0xf5, + 0x66, 0x29, 0xae, 0xd8, 0xda, 0xd9, 0xce, 0x47, 0x54, 0x8e, 0xbc, 0x93, + 0xc4, 0xc0, 0xef, 0x12, 0xbc, 0xc0, 0x3a, 0xbd, 0x2d, 0xf1, 0x7e, 0x98, + 0xf5, 0x6a, 0xe7, 0xc9, 0xad, 0xaa, 0xe7, 0xc9, 0x36, 0x81, 0x33, 0x6e, + 0x24, 0x46, 0xe8, 0xb2, 0x4a, 0x3b, 0x51, 0xb3, 0xfa, 0xd4, 0x2b, 0x1a, + 0x07, 0x6f, 0xfb, 0xe9, 0x2a, 0xc4, 0x15, 0x89, 0x76, 0x35, 0x70, 0xf9, + 0x4e, 0xf3, 0x2f, 0xea, 0x11, 0x8c, 0x94, 0xb6, 0x00, 0x36, 0x2d, 0xba, + 0x72, 0xcc, 0xf5, 0xb4, 0xa5, 0x64, 0xd1, 0xfc, 0x85, 0x2d, 0xce, 0x99, + 0xb3, 0x0f, 0x93, 0x4b, 0xd6, 0xd3, 0xb4, 0xaa, 0xdf, 0xec, 0x2d, 0x13, + 0xeb, 0x80, 0x1e, 0x2f, 0xdf, 0x01, 0x38, 0x6f, 0xa6, 0x06, 0xd7, 0xfd, + 0x61, 0x65, 0xf7, 0x00, 0x93, 0xa7, 0x88, 0x22, 0x8f, 0x3d, 0x1a, 0xdc, + 0x45, 0x5b, 0xea, 0x99, 0x43, 0xd2, 0xda, 0xa0, 0x4c, 0x56, 0xa5, 0xa5, + 0x2b, 0x80, 0xd8, 0x15, 0xbe, 0x0d, 0x1e, 0x19, 0xbe, 0xda, 0x84, 0x7f, + 0xf5, 0x82, 0x87, 0xa1, 0x1c, 0xfe, 0xdc, 0xe7, 0xd1, 0x4f, 0xe7, 0x75, + 0x34, 0x26, 0xe8, 0xdb, 0x6d, 0x5f, 0x1f, 0xea, 0x43, 0x5b, 0x40, 0x35, + 0xd3, 0xb6, 0x89, 0x6e, 0xa8, 0x32, 0x13, 0x66, 0xe4, 0xd4, 0x4a, 0x54, + 0xc6, 0xda, 0x20, 0x4f, 0x6f, 0x94, 0xb3, 0x76, 0xb9, 0x08, 0x16, 0x39, + 0xbb, 0xf8, 0x29, 0x8f, 0xd9, 0x89, 0x3e, 0xa0, 0x50, 0x54, 0x7c, 0x0d, + 0x93, 0x38, 0x18, 0xc7, 0x48, 0x17, 0x2b, 0x03, 0x66, 0x5a, 0x45, 0x3e, + 0x1a, 0xc6, 0xa6, 0xd3, 0x2a, 0xb2, 0xe8, 0x2f, 0xa4, 0xfa, 0xc2, 0x6d, + 0xfd, 0xcd, 0xa9, 0x71, 0xb4, 0x46, 0x85, 0x41, 0xce, 0x10, 0x23, 0xe1, + 0xb9, 0x11, 0xe9, 0xba, 0x85, 0xd4, 0x17, 0xd1, 0x80, 0x5c, 0xbc, 0x7b, + 0x41, 0x21, 0xf2, 0x65, 0x77, 0xb6, 0x06, 0x69, 0x9e, 0x7a, 0x35, 0xf8, + 0xfd, 0x00, 0x4d, 0x0a, 0x27, 0x91, 0x35, 0x16, 0xce, 0xdd, 0xe4, 0xd2, + 0xd3, 0x83, 0xb9, 0x94, 0x60, 0xd1, 0xb9, 0x3a, 0xdd, 0xd0, 0x47, 0x2f, + 0x0a, 0xea, 0x12, 0x33, 0x8f, 0xae, 0x44, 0xc9, 0x80, 0x5a, 0x05, 0x26, + 0x8b, 0xbd, 0xd0, 0x01, 0xe9, 0xbb, 0x3f, 0x6a, 0x80, 0x84, 0x96, 0x1d, + 0x9a, 0x7e, 0xec, 0x64, 0x70, 0x54, 0x5a, 0xe4, 0x7a, 0x91, 0x58, 0x21, + 0xd3, 0xe8, 0xce, 0xe0, 0x2a, 0x43, 0x1f, 0x1e, 0xde, 0xa5, 0xfc, 0xb5, + 0x50, 0x6d, 0xcf, 0x37, 0xec, 0x76, 0xd7, 0xca, 0x52, 0xd0, 0x52, 0x7d, + 0xe7, 0x81, 0x5c, 0xd4, 0xbf, 0x28, 0x7e, 0xa9, 0x02, 0x7f, 0xd8, 0xd1, + 0x60, 0x3e, 0xd1, 0xc1, 0x88, 0xe7, 0x9d, 0x55, 0xde, 0xd9, 0xba, 0xc0, + 0x72, 0x1e, 0xda, 0x22, 0xb0, 0xba, 0xb5, 0xe1, 0xac, 0x48, 0x32, 0xbc, + 0x55, 0xad, 0x66, 0xfb, 0xd2, 0x74, 0xd4, 0x06, 0xbc, 0xeb, 0x42, 0xe7, + 0x94, 0x46, 0x0f, 0xeb, 0xa7, 0xf0, 0xe0, 0xd6, 0xfc, 0x34, 0x82, 0x0d, + 0xd1, 0x8d, 0x4c, 0x70, 0x74, 0x84, 0xdb, 0x2a, 0xb5, 0x88, 0xe4, 0xaa, + 0x0e, 0xc0, 0xc3, 0xc7, 0xf6, 0xa9, 0xa0, 0xab, 0x29, 0xc0, 0x09, 0xe7, + 0x26, 0xad, 0x8b, 0x95, 0x23, 0x47, 0xf9, 0xe6, 0x1f, 0x4a, 0x06, 0x6f, + 0xee, 0x8b, 0xde, 0x79, 0x38, 0x23, 0x19, 0x0d, 0x37, 0xcb, 0xcb, 0xad, + 0x44, 0x7c, 0x53, 0x1d, 0x3e, 0x90, 0x65, 0xdc, 0x8e, 0x06, 0xf6, 0x68, + 0x1d, 0x71, 0x88, 0xa8, 0x5f, 0xbf, 0x0c, 0xd8, 0x6e, 0xc2, 0xe3, 0x52, + 0x5b, 0x2d, 0xe3, 0xec, 0x73, 0x7c, 0x4d, 0x59, 0x64, 0x90, 0xb3, 0x8c, + 0x37, 0x47, 0xf9, 0x53, 0x47, 0x54, 0xad, 0x0b, 0xf8, 0x73, 0xfa, 0x45, + 0x86, 0xf9, 0xd4, 0x1a, 0x57, 0x90, 0x5a, 0x6b, 0x4b, 0x59, 0x87, 0xfb, + 0x38, 0xc4, 0xbf, 0x87, 0xf2, 0x13, 0x13, 0x7c, 0x88, 0xdf, 0x92, 0xae, + 0x3a, 0x08, 0xd7, 0xb5, 0xa7, 0xd9, 0xab, 0x08, 0x34, 0xcf, 0xfd, 0x4b, + 0xbf, 0x92, 0x43, 0x8c, 0x4f, 0x4e, 0xca, 0x70, 0xfb, 0xf0, 0x1c, 0x71, + 0xa0, 0x06, 0xfd, 0xf1, 0xf4, 0x3d, 0xff, 0xc2, 0xc2, 0xfc, 0x5a, 0x23, + 0xe8, 0xc9, 0xde, 0xeb, 0xd2, 0xaa, 0x49, 0x80, 0xe1, 0x17, 0x27, 0x59, + 0x43, 0xa1, 0xbf, 0x62, 0xd3, 0xa0, 0x5e, 0x1c, 0x66, 0x7b, 0xa1, 0x99, + 0x15, 0x7a, 0x9a, 0xb3, 0xe9, 0x8b, 0xff, 0xbb, 0x22, 0x2e, 0x83, 0x03, + 0xe8, 0x8a, 0xa3, 0x94, 0x3e, 0x8f, 0x08, 0x16, 0xe5, 0x43, 0xa6, 0xb7, + 0xc5, 0xd0, 0x08, 0x12, 0x90, 0x46, 0xc9, 0x1b, 0x1b, 0x62, 0x41, 0xaf, + 0x69, 0xd9, 0x93, 0x74, 0x6e, 0x77, 0x94, 0x36, 0x84, 0xd6, 0x34, 0x0d, + 0xa0, 0x78, 0xb6, 0xdc, 0x65, 0x61, 0x5a, 0x58, 0x2a, 0x80, 0xba, 0x58, + 0xff, 0xa5, 0x77, 0x01, 0x50, 0xbb, 0x97, 0x0b, 0x70, 0xef, 0x21, 0x99, + 0x1c, 0xcb, 0xb6, 0x85, 0x75, 0x79, 0xb9, 0xfa, 0x3b, 0xe8, 0xd8, 0x51, + 0xe2, 0xaa, 0xb9, 0x84, 0x62, 0x0e, 0xae, 0xfb, 0xea, 0x34, 0xae, 0x80, + 0xfb, 0x09, 0x41, 0x83, 0x6d, 0x64, 0xa2, 0x38, 0x94, 0x00, 0x60, 0x86, + 0xa3, 0xe7, 0x32, 0xd6, 0xb0, 0x4a, 0xe3, 0x7f, 0xdd, 0x6f, 0x76, 0x98, + 0x86, 0xac, 0xec, 0xc1, 0xec, 0x4c, 0x75, 0xd1, 0x55, 0xd6, 0xc8, 0x18, + 0x1c, 0x31, 0x9f, 0xdf, 0x5b, 0x8e, 0xbb, 0xaa, 0xa6, 0x3e, 0xfc, 0x35, + 0xd1, 0x36, 0x17, 0xa1, 0x38, 0xd4, 0x1d, 0x7f, 0xd4, 0xf6, 0xff, 0x34, + 0x5d, 0xac, 0x5c, 0x87, 0x62, 0xd2, 0x53, 0x19, 0x74, 0xb2, 0x68, 0x84, + 0xe9, 0xc2, 0x88, 0x05, 0x32, 0x11, 0x42, 0x5e, 0x22, 0xaf, 0xdd, 0x81, + 0x80, 0x12, 0x05, 0xf8, 0x88, 0x91, 0x71, 0xd6, 0x6b, 0x85, 0x2b, 0xc2, + 0x5d, 0xfc, 0x16, 0x99, 0x41, 0xaf, 0x27, 0x7b, 0xdd, 0x11, 0x99, 0x3f, + 0x71, 0x95, 0x53, 0x5a, 0x28, 0x0f, 0x8e, 0xd4, 0x09, 0x6c, 0x64, 0x41, + 0x25, 0xd5, 0xac, 0x5c, 0xa4, 0x26, 0x63, 0x1c, 0x5d, 0x04, 0xe4, 0x5c, + 0x73, 0x88, 0x32, 0xd5, 0x02, 0x46, 0x9d, 0xa9, 0xde, 0xf3, 0x67, 0x26, + 0xb2, 0x94, 0xea, 0xce, 0x26, 0x49, 0xcc, 0x78, 0x02, 0x91, 0xb9, 0xcf, + 0x3e, 0xcc, 0xc5, 0xde, 0x76, 0x8c, 0x04, 0x48, 0x5e, 0x4b, 0x55, 0x11, + 0x7c, 0xb2, 0x54, 0xac, 0x71, 0xcf, 0x88, 0xb1, 0x91, 0x84, 0x37, 0xfb, + 0x9f, 0x68, 0xad, 0xf1, 0x65, 0xe9, 0x74, 0xd1, 0xb3, 0x1a, 0xb3, 0xd4, + 0x72, 0x23, 0xe6, 0x82, 0x3e, 0x08, 0x43, 0xb7, 0xa2, 0x29, 0x3f, 0x3c, + 0x06, 0xa0, 0x31, 0xda, 0xee, 0xdd, 0xcb, 0xed, 0x0c, 0xaa, 0x60, 0x7e, + 0x9b, 0xa1, 0x66, 0xf1, 0x0a, 0xcd, 0x5a, 0xc6, 0xa5, 0x24, 0xfb, 0xf4, + 0x88, 0x04, 0xb4, 0x0a, 0xac, 0x41, 0xdd, 0x85, 0xef, 0xe1, 0x9f, 0xc6, + 0x92, 0x14, 0xbc, 0x2f, 0x5e, 0x9b, 0x29, 0x3d, 0xd8, 0x90, 0x4f, 0xa5, + 0xc8, 0x75, 0x0f, 0xb1, 0xce, 0x50, 0x45, 0x4d, 0xcc, 0x23, 0xe5, 0xcd, + 0xef, 0x6c, 0x1b, 0x07, 0x51, 0x76, 0x00, 0x4d, 0xd4, 0xb1, 0xcb, 0x2b, + 0x5b, 0xd2, 0x47, 0xd6, 0x90, 0x13, 0xdb, 0x9c, 0x71, 0x68, 0x25, 0x90, + 0x40, 0x73, 0xfd, 0x6c, 0x19, 0x2f, 0xd2, 0xba, 0x4c, 0x67, 0x75, 0x3d, + 0x67, 0x48, 0xf4, 0xab, 0x1c, 0x9f, 0xc0, 0x91, 0xc5, 0xdf, 0x3b, 0x03, + 0xa4, 0x18, 0x2e, 0xb2, 0x75, 0x80, 0x06, 0xf8, 0xfe, 0x2d, 0xf2, 0x68, + 0x88, 0x7e, 0x6d, 0x03, 0x72, 0x81, 0x41, 0xe3, 0x9f, 0x95, 0xe3, 0xcf, + 0x69, 0x2a, 0x4f, 0x5f, 0x5a, 0x04, 0x9e, 0x22, 0x58, 0x71, 0x76, 0x87, + 0xa2, 0x63, 0x60, 0x9a, 0x85, 0x7f, 0x92, 0x55, 0x45, 0xf4, 0xeb, 0xf0, + 0x89, 0xe8, 0x8f, 0xf5, 0x2a, 0x15, 0x1b, 0x58, 0x30, 0x08, 0xc4, 0x24, + 0xb9, 0x13, 0x09, 0x0b, 0x32, 0x91, 0x9f, 0x81, 0xa5, 0x12, 0x3c, 0x42, + 0xaa, 0x48, 0x57, 0x7c, 0xf5, 0x74, 0x2d, 0x80, 0xc1, 0x0b, 0xe9, 0x92, + 0xb6, 0x85, 0x8c, 0xb8, 0x44, 0x12, 0xf0, 0xe4, 0x76, 0x82, 0xe8, 0x41, + 0x5e, 0x2c, 0xf6, 0x77, 0x0d, 0xc8, 0x5f, 0x9b, 0x4a, 0x7f, 0xa5, 0xfd, + 0x2e, 0xf1, 0x57, 0x39, 0xbe, 0xd1, 0xae, 0x03, 0x27, 0xa4, 0x86, 0xb9, + 0x23, 0xe1, 0xa6, 0xaa, 0x69, 0x42, 0x06, 0x5a, 0x96, 0x50, 0x22, 0x2e, + 0x0b, 0x23, 0x83, 0xe1, 0x3d, 0xa4, 0x44, 0x29, 0x79, 0x98, 0xb7, 0x60, + 0x78, 0x6d, 0xee, 0x84, 0x92, 0x59, 0x54, 0xd6, 0xe5, 0x15, 0x9b, 0xdd, + 0xa3, 0x48, 0x70, 0x67, 0xb1, 0xfa, 0x7e, 0x55, 0x26, 0xee, 0xca, 0x2e, + 0x44, 0xb9, 0x15, 0xa3, 0x21, 0x0e, 0xd5, 0xab, 0x74, 0xdc, 0xa3, 0x4b, + 0x73, 0x37, 0xaa, 0x97, 0x96, 0xf1, 0xc2, 0x91, 0xcd, 0x3f, 0x42, 0xed, + 0x28, 0xf7, 0x65, 0x8c, 0xb7, 0x79, 0x9d, 0x3f, 0x62, 0x38, 0x00, 0x4f, + 0x44, 0xf4, 0xc7, 0xdd, 0x99, 0x2b, 0x7a, 0x4f, 0xc1, 0x1d, 0xdb, 0xec, + 0x23, 0x1f, 0xbb, 0xe2, 0xd2, 0x2c, 0xf6, 0xb4, 0x5c, 0xdc, 0xab, 0xd0, + 0x67, 0x5d, 0x12, 0xf6, 0xf8, 0x5b, 0x94, 0xea, 0x07, 0xff, 0x83, 0x33, + 0x59, 0x83, 0xe2, 0xb8, 0x85, 0x83, 0x35, 0xe1, 0xa8, 0xc5, 0x1d, 0x67, + 0x3b, 0x90, 0x9f, 0x69, 0x2f, 0x35, 0xe4, 0x7d, 0xc5, 0xdb, 0xe0, 0xef, + 0xfc, 0xca, 0x66, 0x10, 0xbf, 0xe7, 0xc3, 0xf2, 0xdf, 0x90, 0x78, 0xda, + 0x41, 0x19, 0xa7, 0xf0, 0x9c, 0x09, 0xb7, 0x36, 0x2f, 0xfc, 0x0f, 0xc5, + 0x76, 0x24, 0xa7, 0x30, 0xd6, 0x69, 0x0b, 0xd4, 0x99, 0x80, 0x03, 0xa6, + 0xaf, 0xac, 0xa7, 0xe9, 0xa9, 0x0c, 0xc3, 0x76, 0x1e, 0x52, 0x3e, 0xff, + 0xba, 0x1c, 0x9e, 0x3a, 0x06, 0xc4, 0x4a, 0x7f, 0x7e, 0x5d, 0x31, 0x83, + 0xaf, 0x0c, 0x76, 0xcc, 0x9d, 0x6a, 0x2c, 0x41, 0x6a, 0xf8, 0x41, 0x68, + 0x51, 0x6b, 0xdf, 0xe8, 0xdf, 0x72, 0x78, 0x43, 0x0b, 0x64, 0xf8, 0x18, + 0xc7, 0x1c, 0x8e, 0x2f, 0x2a, 0x38, 0x5f, 0x16, 0xfb, 0x3c, 0x70, 0x82, + 0xcb, 0x34, 0x83, 0x64, 0x30, 0xaf, 0x85, 0xee, 0x41, 0x54, 0xab, 0x7e, + 0x5d, 0x8f, 0xa9, 0x36, 0x25, 0xda, 0x85, 0x2f, 0xf0, 0x26, 0xb7, 0xd8, + 0x93, 0x39, 0x0d, 0xfa, 0x1a, 0x6d, 0x8f, 0x80, 0x1d, 0x30, 0x84, 0x8b, + 0x78, 0x40, 0x10, 0xae, 0xf3, 0x88, 0x12, 0x7c, 0x93, 0x94, 0x49, 0xdd, + 0x6f, 0x2c, 0x9e, 0x7d, 0x36, 0xf7, 0x33, 0xa6, 0x61, 0x40, 0x14, 0x44, + 0x7e, 0xe6, 0xe9, 0x01, 0xdd, 0x63, 0x7d, 0x6e, 0x51, 0xeb, 0x18, 0xc7, + 0x58, 0xd0, 0x27, 0xab, 0x11, 0x10, 0x97, 0x5e, 0xd4, 0xec, 0xfe, 0x61, + 0xd1, 0x29, 0xc8, 0x50, 0x1c, 0xee, 0xf3, 0xc2, 0x54, 0x24, 0x04, 0xdd, + 0xe5, 0x5a, 0xed, 0x96, 0xb4, 0xaa, 0xb0, 0x69, 0x37, 0x84, 0x48, 0x78, + 0x74, 0xfc, 0x98, 0xd4, 0xe8, 0x7f, 0x26, 0x3c, 0x54, 0x25, 0x4e, 0x74, + 0xed, 0xc3, 0x00, 0x39, 0x7d, 0xeb, 0xf0, 0x64, 0x1f, 0x70, 0x7d, 0xc8, + 0xb8, 0xfe, 0x30, 0x01, 0xa5, 0xef, 0x23, 0x7c, 0x9c, 0x14, 0x9b, 0x21, + 0x5d, 0x09, 0xd2, 0x60, 0x3a, 0x23, 0x72, 0xed, 0x16, 0x03, 0xa5, 0x14, + 0xba, 0x73, 0xf2, 0x89, 0xf6, 0xf5, 0x67, 0x98, 0x8a, 0x65, 0x38, 0xec, + 0xac, 0x5e, 0x2b, 0xe8, 0x31, 0xf6, 0x27, 0xe8, 0xeb, 0x63, 0x01, 0xb5, + 0x2b, 0xd8, 0xd5, 0xe0, 0xfd, 0x03, 0xc6, 0xd4, 0xeb, 0x16, 0x19, 0xee, + 0x35, 0x01, 0xc8, 0x7a, 0x45, 0x06, 0x30, 0xe7, 0x4b, 0x91, 0xad, 0x95, + 0xf1, 0x0b, 0x7a, 0xe9, 0x10, 0xe9, 0x46, 0x1b, 0x8b, 0xa0, 0xab, 0x5d, + 0xd7, 0x83, 0x30, 0x13, 0x65, 0x64, 0x5a, 0x5f, 0xe3, 0xfe, 0x39, 0xdc, + 0xd2, 0x76, 0xac, 0x10, 0x57, 0xa4, 0x3b, 0x3f, 0x67, 0x1b, 0x34, 0x1c, + 0x04, 0xd1, 0x9a, 0xb9, 0xc3, 0x0d, 0x2e, 0xf3, 0x37, 0x0f, 0x26, 0xf1, + 0xe6, 0xae, 0xd7, 0x84, 0x72, 0x68, 0x87, 0xe7, 0xa0, 0x6b, 0xb7, 0x5f, + 0xbf, 0xf4, 0xf0, 0x3d, 0xc9, 0x49, 0x0b, 0xdb, 0x0c, 0xf3, 0xf7, 0xee, + 0x7b, 0x5f, 0xb6, 0x96, 0x56, 0x3c, 0x9f, 0xad, 0x34, 0xef, 0x56, 0x34, + 0x3d, 0xd8, 0xec, 0xe4, 0x76, 0x3b, 0x63, 0x9b, 0x23, 0x43, 0xe5, 0x8c, + 0xb0, 0xa4, 0x40, 0xcd, 0x69, 0x7f, 0x3a, 0xfa, 0xb6, 0x8c, 0xe3, 0x0d, + 0xff, 0x95, 0xa0, 0x06, 0x11, 0xed, 0x34, 0xba, 0xe3, 0xf6, 0x23, 0x1a, + 0x6f, 0xe6, 0xcc, 0xa5, 0x34, 0x5f, 0x0b, 0xa7, 0xcd, 0x00, 0x92, 0xf7, + 0x47, 0x8f, 0x14, 0x84, 0x3d, 0xbc, 0x03, 0x94, 0x3b, 0x30, 0x2a, 0xdc, + 0x4b, 0x0c, 0xbf, 0xe8, 0xcb, 0x53, 0x71, 0xb4, 0xce, 0x92, 0xa9, 0x90, + 0xca, 0x74, 0x69, 0xcf, 0xeb, 0xb1, 0x79, 0x27, 0xbf, 0x12, 0xfd, 0xe9, + 0x85, 0x40, 0x67, 0xbc, 0x49, 0x84, 0x05, 0x5e, 0x5d, 0xea, 0xc7, 0x13, + 0xd3, 0x83, 0x90, 0xa6, 0x33, 0xff, 0x85, 0xc4, 0x59, 0x4a, 0xb5, 0x84, + 0xad, 0x1e, 0xd2, 0xd4, 0xae, 0x00, 0x9a, 0x63, 0x19, 0xe2, 0x4a, 0x6c, + 0x90, 0x5d, 0x02, 0xe1, 0x8e, 0x5c, 0x14, 0xf2, 0x89, 0x9e, 0x1a, 0x59, + 0xd5, 0x86, 0x44, 0xb7, 0x19, 0x63, 0xe5, 0x3d, 0x40, 0x76, 0x9e, 0x8a, + 0xea, 0x80, 0x8e, 0x11, 0xbf, 0x4e, 0xb8, 0xbf, 0xb0, 0xa3, 0xa1, 0x74, + 0x31, 0x8f, 0xe9, 0x88, 0x43, 0xf1, 0x71, 0xd7, 0x01, 0x67, 0x62, 0x84, + 0x1d, 0x92, 0x86, 0xf8, 0xc1, 0xa9, 0x0f, 0x94, 0x7b, 0x97, 0xec, 0x8d, + 0xdf, 0x26, 0x52, 0xb9, 0xc4, 0x79, 0x58, 0x46, 0xf9, 0x3a, 0x2b, 0xd1, + 0x76, 0xd5, 0xbd, 0x4e, 0x37, 0x4d, 0x06, 0x22, 0x45, 0x20, 0xe9, 0xc7, + 0xbd, 0xa0, 0x9e, 0x26, 0xab, 0xaa, 0x2d, 0xa9, 0xd5, 0xfe, 0xcf, 0x6e, + 0x9c, 0x8d, 0xff, 0x57, 0x2b, 0xf0, 0x51, 0x47, 0x00, 0x8e, 0x80, 0x1a, + 0xa6, 0x57, 0x0c, 0xa0, 0x36, 0xdc, 0x51, 0xed, 0x9a, 0x3f, 0xf2, 0xa4, + 0xb6, 0x06, 0x48, 0xf0, 0x7d, 0x18, 0x2a, 0x6c, 0x44, 0x87, 0x23, 0xf1, + 0x35, 0x0c, 0xa2, 0xb8, 0x2a, 0x37, 0xfb, 0x53, 0x64, 0x98, 0xa8, 0x25, + 0x30, 0xb9, 0x98, 0xdd, 0x52, 0x86, 0xbc, 0x63, 0x22, 0xc1, 0xac, 0x7a, + 0x0b, 0x57, 0x1b, 0xf7, 0x2f, 0xc7, 0xe6, 0x62, 0xaf, 0xc9, 0xf2, 0xc1, + 0x72, 0xf0, 0x20, 0x83, 0x57, 0x58, 0xf1, 0xee, 0xcf, 0x2b, 0x32, 0x45, + 0xf7, 0xda, 0x98, 0x54, 0xa6, 0x87, 0xff, 0xed, 0x01, 0x50, 0xea, 0x53, + 0x32, 0xfa, 0xfa, 0xbb, 0x45, 0x5b, 0x78, 0x69, 0xe1, 0xd1, 0x65, 0xea, + 0x80, 0xd0, 0xf3, 0xf2, 0x32, 0x04, 0xe4, 0x3a, 0xcb, 0x49, 0x6c, 0x90, + 0xcc, 0xc9, 0x34, 0xe2, 0x11, 0x94, 0x19, 0x5b, 0xb2, 0xc1, 0x7d, 0x73, + 0xc3, 0x2b, 0x9e, 0x93, 0xc0, 0xf8, 0x90, 0x3b, 0x0b, 0x9d, 0xa7, 0x5f, + 0x16, 0xdf, 0x09, 0xbe, 0x69, 0x32, 0xc6, 0xb6, 0x4f, 0xeb, 0xb5, 0x0f, + 0x4e, 0x28, 0x3d, 0x25, 0x49, 0xcb, 0xfe, 0xd2, 0xef, 0xd9, 0x82, 0xbb, + 0x66, 0x0c, 0xa1, 0xdd, 0x91, 0xa8, 0xd2, 0x33, 0xd9, 0x7f, 0x93, 0xda, + 0x95, 0xa7, 0xe9, 0x3e, 0x07, 0x5a, 0x04, 0xb0, 0xd9, 0xc9, 0xa9, 0x03, + 0x54, 0x20, 0xd4, 0xd5, 0x53, 0xb7, 0x15, 0x02, 0xd5, 0xc6, 0xfa, 0x8f, + 0xe8, 0x76, 0x6a, 0x4c, 0xc5, 0xe3, 0xc7, 0x77, 0x3b, 0xf2, 0xe2, 0x46, + 0x66, 0x93, 0x7a, 0xa9, 0xc1, 0x05, 0x85, 0xc1, 0x4f, 0xd6, 0x40, 0x14, + 0x05, 0x1a, 0xfc, 0x6d, 0x5d, 0xf3, 0xb4, 0x02, 0x7c, 0x0d, 0x86, 0x81, + 0x50, 0x97, 0xdc, 0x41, 0xb7, 0x02, 0xa8, 0x90, 0x11, 0xc8, 0x5c, 0x5d, + 0xa9, 0x09, 0xe6, 0xba, 0xb9, 0x7c, 0xad, 0x58, 0xdb, 0xa0, 0x1c, 0x11, + 0xed, 0x17, 0x48, 0x65, 0x70, 0x09, 0x57, 0x16, 0x69, 0xc6, 0x27, 0x48, + 0x41, 0x47, 0xd9, 0x32, 0x6d, 0xa7, 0x5a, 0x16, 0x86, 0x6f, 0x32, 0x3c, + 0x4e, 0xe7, 0x0c, 0xdf, 0xcc, 0x7a, 0xe2, 0x93, 0x2b, 0x93, 0x95, 0x9d, + 0x92, 0xb3, 0x7b, 0xf9, 0xaf, 0x01, 0xd3, 0x4a, 0x4e, 0x1a, 0xa9, 0xdb, + 0x13, 0x8c, 0x39, 0xe0, 0xc6, 0x90, 0x46, 0x0f, 0xbc, 0x18, 0xb7, 0xee, + 0x1d, 0xfa, 0x66, 0x61, 0xcc, 0x9a, 0x43, 0x4c, 0x1a, 0xe1, 0xee, 0x49, + 0x17, 0x0a, 0xd5, 0x34, 0xcc, 0x1b, 0x5b, 0x2d, 0xa3, 0xac, 0x01, 0x27, + 0xe2, 0xaa, 0x26, 0xa7, 0x7f, 0x25, 0x30, 0x97, 0x81, 0x58, 0xb0, 0xe4, + 0x8b, 0xe1, 0xba, 0x01, 0x6d, 0x69, 0xdd, 0x8b, 0xe7, 0x66, 0x86, 0x4f, + 0x5a, 0x48, 0xea, 0x3d, 0x30, 0x50, 0x40, 0xef, 0xf3, 0x5f, 0x04, 0xbd, + 0x61, 0x14, 0x54, 0x06, 0x9b, 0x8b, 0xf5, 0x44, 0xcc, 0xdd, 0xc0, 0x34, + 0xef, 0x72, 0x2c, 0xc1, 0x70, 0xf4, 0xf0, 0x25, 0xaf, 0xcb, 0x03, 0xea, + 0xe9, 0x33, 0x51, 0x4a, 0xb8, 0xb3, 0xbe, 0x4e, 0x29, 0xe6, 0x43, 0x6b, + 0x98, 0x10, 0xe1, 0x2f, 0x02, 0x75, 0xc1, 0xf5, 0xb0, 0xbc, 0x01, 0xed, + 0xce, 0xa7, 0xf6, 0x56, 0x40, 0x4a, 0xac, 0x92, 0xf1, 0x56, 0x00, 0x48, + 0x49, 0x1b, 0xf1, 0x59, 0xb3, 0xd2, 0xca, 0xd0, 0x91, 0x28, 0xe5, 0xed, + 0x51, 0xbc, 0x48, 0x89, 0x66, 0xea, 0x80, 0xdd, 0x4d, 0x2a, 0xeb, 0xda, + 0x63, 0x35, 0x26, 0x93, 0xe3, 0x28, 0xb0, 0xff, 0x12, 0xa1, 0x82, 0x39, + 0x37, 0xf8, 0x30, 0x9a, 0x84, 0xf6, 0x72, 0xe5, 0xa6, 0x98, 0xde, 0x63, + 0x79, 0xc1, 0x26, 0x85, 0x03, 0x83, 0x7f, 0xf2, 0x8d, 0x26, 0x99, 0x49, + 0x03, 0xca, 0xaa, 0x01, 0x45, 0x93, 0x2e, 0xd4, 0xa4, 0xb4, 0x24, 0xfa, + 0xd1, 0xa4, 0x4e, 0x9a, 0xef, 0xe6, 0xfa, 0xac, 0x1d, 0x2f, 0x96, 0x65, + 0x95, 0x47, 0xc5, 0x1b, 0xad, 0x08, 0x97, 0x5d, 0x0e, 0x72, 0x35, 0x8b, + 0xd5, 0xbf, 0x5c, 0x51, 0x0c, 0x47, 0x24, 0x99, 0x3e, 0x8a, 0x0f, 0xc4, + 0x18, 0xdf, 0x6d, 0xbc, 0x73, 0x02, 0xfd, 0x18, 0x47, 0xff, 0x79, 0xe9, + 0x7b, 0x6a, 0xaf, 0x0f, 0xf8, 0x15, 0x19, 0x06, 0x59, 0x78, 0x44, 0x12, + 0x64, 0x8d, 0xf5, 0xcd, 0x52, 0x3d, 0x84, 0x9d, 0xab, 0xbd, 0x0d, 0x4e, + 0xc6, 0xcb, 0xcb, 0x9a, 0x60, 0x26, 0x0c, 0xf1, 0x87, 0xec, 0xcf, 0x05, + 0x65, 0xc5, 0x3a, 0x4f, 0xfb, 0x57, 0x3d, 0xda, 0x28, 0xaf, 0x23, 0x7a, + 0x29, 0x48, 0xb9, 0xad, 0xa7, 0x91, 0x4e, 0xf7, 0x89, 0xe2, 0x90, 0xa3, + 0xf8, 0x1f, 0x27, 0xb8, 0x71, 0x8f, 0x8c, 0xfe, 0xa0, 0x9f, 0xcf, 0x09, + 0xb5, 0x68, 0x39, 0xcf, 0xc7, 0xd5, 0xb0, 0x6f, 0xbb, 0x85, 0x11, 0x43, + 0xb4, 0xfa, 0xf8, 0xec, 0x3e, 0x81, 0x1a, 0xc6, 0x15, 0x23, 0xe5, 0xe5, + 0x47, 0x3d, 0xd9, 0xef, 0x8e, 0x34, 0x3c, 0xb0, 0x10, 0x45, 0x75, 0xe5, + 0x5c, 0x0a, 0x9c, 0xe5, 0x3c, 0xc2, 0x46, 0x6c, 0x6d, 0xfd, 0x33, 0xc5, + 0x41, 0xd7, 0x55, 0xa8, 0x8c, 0x5c, 0x30, 0xc6, 0x12, 0x68, 0x89, 0x1d, + 0x49, 0x47, 0xed, 0xc3, 0xd3, 0x6f, 0x08, 0xf3, 0xfe, 0x71, 0x4d, 0x69, + 0x83, 0xde, 0x3a, 0xe4, 0xa1, 0xc4, 0xfb, 0xc2, 0x10, 0x95, 0xe0, 0xa4, + 0x31, 0x4d, 0xf8, 0xf4, 0xf7, 0x5c, 0x06, 0x2d, 0x49, 0x44, 0x0c, 0x00, + 0xb7, 0x5c, 0xda, 0x17, 0xcd, 0x68, 0xcf, 0x33, 0x99, 0x03, 0xb4, 0x84, + 0x62, 0x6c, 0x52, 0xad, 0xff, 0x02, 0x55, 0xe4, 0xe0, 0xf6, 0x8b, 0xf3, + 0x19, 0xfc, 0x92, 0x11, 0x55, 0xcf, 0x8a, 0x2b, 0xdb, 0xc6, 0xd5, 0xfb, + 0x26, 0x2e, 0x49, 0xfa, 0x24, 0xaa, 0x65, 0xbe, 0x3a, 0xe6, 0x9a, 0xb1, + 0xd9, 0x0e, 0x72, 0xaa, 0xb3, 0xc6, 0xae, 0xe4, 0x66, 0xc4, 0x70, 0x2c, + 0x15, 0x97, 0xca, 0xbe, 0xcd, 0xee, 0xe8, 0xab, 0xa6, 0xc2, 0x3c, 0xa3, + 0x89, 0x11, 0x56, 0xd4, 0x7a, 0x28, 0xac, 0xec, 0x96, 0x10, 0x15, 0xc3, + 0x4b, 0xa7, 0xe9, 0x70, 0x8f, 0x76, 0x94, 0xf5, 0x1a, 0x30, 0x45, 0x0b, + 0xc6, 0x52, 0x11, 0xe0, 0x4c, 0x80, 0x97, 0x30, 0xbd, 0x6f, 0x7e, 0x8b, + 0xd9, 0x45, 0x0f, 0xa3, 0x4c, 0xdb, 0x44, 0x0f, 0x33, 0x9b, 0x68, 0x54, + 0x22, 0x01, 0xaf, 0x98, 0x71, 0x76, 0xf9, 0xf9, 0x91, 0x6d, 0x7e, 0xcf, + 0xf0, 0x51, 0x04, 0xdc, 0x7d, 0x10, 0x6b, 0x0d, 0x71, 0x23, 0xb8, 0x26, + 0x70, 0x09, 0x12, 0xc2, 0x9b, 0x6b, 0x85, 0xf9, 0x02, 0xa4, 0x4e, 0x16, + 0x43, 0x34, 0x78, 0xbe, 0x2f, 0xd1, 0x44, 0x06, 0xf9, 0x0e, 0x4e, 0x01, + 0x39, 0x2d, 0x17, 0xee, 0x8e, 0x7d, 0xc7, 0x85, 0x63, 0xb5, 0x84, 0x1d, + 0x8f, 0xfc, 0xda, 0xc8, 0x06, 0x46, 0x99, 0x0c, 0xf9, 0xef, 0xd7, 0x6b, + 0x17, 0xa4, 0xa8, 0xed, 0xc8, 0xf4, 0x86, 0x53, 0xea, 0x14, 0xf3, 0x60, + 0xe1, 0xe3, 0xdd, 0xeb, 0xcd, 0x92, 0x54, 0x7f, 0x0a, 0x14, 0x2e, 0x03, + 0x96, 0xda, 0x19, 0x82, 0x19, 0x94, 0x3f, 0xbe, 0xea, 0xcb, 0x55, 0x68, + 0xbe, 0xbc, 0x5c, 0xe4, 0xdb, 0xf9, 0xc0, 0x17, 0x7c, 0xe1, 0x26, 0xe2, + 0xe3, 0x14, 0x13, 0x69, 0x27, 0x68, 0x8e, 0xe3, 0xa0, 0x3a, 0xe5, 0xc9, + 0x48, 0xd2, 0xbc, 0x30, 0xa8, 0xd3, 0xdf, 0x8b, 0x22, 0xcd, 0x41, 0x14, + 0x05, 0xe8, 0x50, 0xd4, 0x3a, 0xdf, 0xce, 0xe8, 0x43, 0xa3, 0x05, 0x8e, + 0xae, 0x36, 0xc8, 0xd2, 0x52, 0x73, 0xa1, 0xaf, 0xc0, 0x3f, 0x0f, 0x59, + 0xe4, 0x98, 0x69, 0xf3, 0xf3, 0x4d, 0x56, 0x45, 0xfd, 0x50, 0x2b, 0xfc, + 0x77, 0xa1, 0xef, 0x73, 0x01, 0x2e, 0x0a, 0x3a, 0x18, 0x02, 0x53, 0x49, + 0x52, 0xaa, 0xf9, 0xb6, 0x54, 0xaa, 0x1b, 0x47, 0x76, 0xee, 0x3f, 0xfb, + 0x53, 0xee, 0xf5, 0x6a, 0xac, 0x94, 0x6b, 0xc6, 0x61, 0x74, 0x69, 0x39, + 0x54, 0x1a, 0xf4, 0x2d, 0x1b, 0xac, 0xbd, 0xbb, 0x06, 0x11, 0xe5, 0x15, + 0x83, 0xaf, 0x47, 0x2a, 0x98, 0x68, 0xdd, 0xa1, 0x7f, 0x8f, 0x5c, 0x7d, + 0x23, 0x8c, 0x28, 0x30, 0x26, 0xbc, 0x02, 0x69, 0x87, 0xd1, 0x89, 0xbc, + 0x2d, 0x9a, 0x29, 0xd9, 0x20, 0x8a, 0x9f, 0xf0, 0x6f, 0x17, 0x66, 0xef, + 0x5a, 0x7d, 0x88, 0x3b, 0xe6, 0x23, 0x87, 0xa2, 0x3f, 0xfa, 0x70, 0x3a, + 0x1d, 0xf0, 0x80, 0xfd, 0xfb, 0x79, 0xcc, 0xc6, 0x5d, 0xd4, 0xc3, 0x5f, + 0xf9, 0xad, 0xed, 0x59, 0x66, 0xcb, 0x37, 0x9e, 0x0b, 0x66, 0xd9, 0x6a, + 0xc8, 0x96, 0x3c, 0x1b, 0x65, 0x07, 0x99, 0x9d, 0x62, 0x6d, 0xca, 0x59, + 0x37, 0x3f, 0x18, 0x4f, 0x04, 0x7e, 0x14, 0x4c, 0xe2, 0x00, 0x3e, 0xa8, + 0x9c, 0x3c, 0x37, 0x59, 0xb5, 0x85, 0x17, 0x51, 0x30, 0x3c, 0x6e, 0xe3, + 0xdd, 0x4d, 0x4a, 0xfb, 0xbb, 0x5a, 0x5c, 0xea, 0x8f, 0x90, 0x27, 0x8d, + 0xb4, 0x08, 0x56, 0x8b, 0x44, 0xa6, 0x52, 0xd6, 0x6c, 0x45, 0xe7, 0x77, + 0xe5, 0xe7, 0xb9, 0xdd, 0x1c, 0x74, 0xfc, 0xe5, 0xb4, 0x8b, 0xcf, 0xaa, + 0xec, 0x77, 0x9c, 0x10, 0xc7, 0x56, 0xd0, 0x0a, 0x2d, 0xec, 0x1c, 0x93, + 0x60, 0x97, 0x19, 0xbd, 0xbc, 0x83, 0x56, 0x3a, 0x81, 0x47, 0x69, 0xe7, + 0x20, 0x54, 0x26, 0x50, 0x3d, 0x37, 0xe9, 0x68, 0x5c, 0x9c, 0x94, 0xab, + 0xd0, 0x08, 0x82, 0x8c, 0xf5, 0x02, 0x9b, 0xc5, 0x19, 0xde, 0x54, 0x80, + 0x6c, 0x39, 0xe8, 0xb1, 0x6e, 0xd0, 0x98, 0x66, 0xb8, 0x21, 0xde, 0x40, + 0x94, 0x79, 0xf3, 0x54, 0x79, 0x05, 0xbb, 0x60, 0x0a, 0x60, 0x34, 0x9b, + 0xde, 0xac, 0x24, 0xce, 0xc3, 0xc5, 0x8f, 0x25, 0x97, 0x7f, 0xae, 0xc4, + 0x5f, 0x94, 0x23, 0x71, 0xaa, 0xeb, 0x58, 0x96, 0xd0, 0x8e, 0xa6, 0x6b, + 0xb1, 0x47, 0x3f, 0xf3, 0xe0, 0xec, 0x50, 0xab, 0xfd, 0xcb, 0x6b, 0x04, + 0x8a, 0xfa, 0x5b, 0x30, 0x1b, 0x68, 0x48, 0xbb, 0x5f, 0x79, 0x83, 0x52, + 0xd3, 0x4a, 0xb5, 0x3c, 0x5e, 0xf6, 0xc5, 0xa3, 0xf8, 0x06, 0x9f, 0x63, + 0xbb, 0x04, 0xfe, 0x66, 0xb5, 0xf5, 0x36, 0xf8, 0xea, 0x0b, 0xcc, 0xdf, + 0x79, 0x34, 0x0b, 0xab, 0x8d, 0xf2, 0xd3, 0x58, 0xb0, 0xc8, 0x38, 0xf6, + 0x07, 0x1d, 0xc0, 0x73, 0x3e, 0xa3, 0x66, 0x21, 0x20, 0xd7, 0xe2, 0xc5, + 0x89, 0x3f, 0xa5, 0xab, 0x15, 0x5f, 0x2f, 0xfc, 0x9f, 0xbf, 0xcb, 0xfc, + 0x00, 0xe4, 0x64, 0xba, 0x14, 0xfb, 0x4c, 0x7e, 0x6d, 0x3c, 0x9b, 0xdd, + 0xa5, 0xe4, 0xa3, 0xb5, 0xd9, 0x98, 0x8c, 0xe8, 0x9f, 0x5d, 0x84, 0xda, + 0x47, 0x7e, 0xe4, 0xaa, 0x21, 0xbd, 0x5c, 0x32, 0x93, 0xdd, 0xc9, 0x7e, + 0xb6, 0x63, 0xbd, 0xde, 0xd3, 0x6f, 0x9a, 0xfe, 0x3a, 0x0f, 0x49, 0x1e, + 0x6c, 0x98, 0x5b, 0xad, 0x21, 0x12, 0xd2, 0xd3, 0x23, 0xda, 0x9e, 0xd9, + 0xd5, 0xca, 0x77, 0x5e, 0x00, 0xea, 0xa5, 0x12, 0x65, 0xc1, 0xf6, 0xdb, + 0x29, 0xbc, 0x0b, 0xef, 0xd5, 0xad, 0x3e, 0x28, 0x08, 0x70, 0x7a, 0x44, + 0xd6, 0xd0, 0x1a, 0x86, 0x5b, 0x8f, 0xb4, 0x81, 0xfe, 0xc1, 0x92, 0xe4, + 0x8a, 0xa1, 0xf5, 0x5d, 0x7a, 0x59, 0xb5, 0x7b, 0xee, 0xb4, 0x03, 0x56, + 0x93, 0xf6, 0xba, 0x28, 0xeb, 0xde, 0x58, 0xe2, 0xff, 0xe0, 0x8f, 0x9a, + 0xc2, 0x02, 0x72, 0x79, 0xc1, 0xa9, 0x69, 0x95, 0x58, 0x09, 0x21, 0x9b, + 0xd0, 0xf7, 0xb5, 0x7c, 0x1b, 0x4b, 0x04, 0x4f, 0x1d, 0x79, 0xa9, 0xf8, + 0xcb, 0x95, 0x77, 0xcf, 0x52, 0x61, 0x3f, 0xf5, 0x6b, 0x11, 0xf2, 0x08, + 0x84, 0xab, 0x53, 0x56, 0x0f, 0x82, 0x7f, 0x86, 0xa4, 0x4a, 0xec, 0x55, + 0x0d, 0xd6, 0x9e, 0x08, 0xc7, 0x38, 0x62, 0xcc, 0xb3, 0xbd, 0x0b, 0x3b, + 0xf7, 0x34, 0x08, 0xcc, 0xa1, 0x55, 0x05, 0xda, 0xbd, 0x02, 0x97, 0x74, + 0xe7, 0xca, 0xca, 0x3e, 0x31, 0x31, 0x98, 0x0a, 0x1e, 0x96, 0x1e, 0xa6, + 0x6e, 0x83, 0x3d, 0x0e, 0x27, 0x6b, 0xf8, 0x49, 0xd0, 0xe8, 0xb3, 0x2d, + 0x50, 0xb5, 0xb0, 0xfe, 0xda, 0xe3, 0x28, 0x03, 0xee, 0x15, 0x64, 0x04, + 0x49, 0xd0, 0xac, 0x90, 0xa5, 0x85, 0xda, 0xd0, 0x4b, 0x0a, 0x1e, 0xcc, + 0x2d, 0xfc, 0x5b, 0x98, 0x9c, 0x71, 0x35, 0x42, 0xbc, 0xb7, 0x27, 0x63, + 0x20, 0xbe, 0x82, 0xe1, 0x43, 0xae, 0x3c, 0xfd, 0x88, 0x95, 0xe6, 0xae, + 0xf4, 0xe4, 0x3d, 0x88, 0xf0, 0x08, 0x3e, 0x7a, 0x73, 0xc7, 0x9b, 0xa6, + 0xd3, 0x3f, 0x2f, 0xef, 0xd2, 0x55, 0x2c, 0x3b, 0xf8, 0xa8, 0x8a, 0x98, + 0x8f, 0xf0, 0xd1, 0x06, 0x88, 0xc1, 0xf3, 0x1c, 0xff, 0x19, 0xcc, 0xd2, + 0x63, 0xfe, 0x4b, 0x9e, 0x60, 0xf5, 0x90, 0xcc, 0xf1, 0x20, 0x64, 0xc9, + 0x3a, 0x1a, 0xf9, 0xc6, 0xd4, 0x13, 0xd7, 0xcc, 0xe1, 0x09, 0xa5, 0x67, + 0xbc, 0xc0, 0x59, 0x4a, 0x32, 0x0e, 0xeb, 0x25, 0x45, 0x6c, 0x38, 0x33, + 0xf4, 0x15, 0x2c, 0xe7, 0x03, 0x63, 0xa5, 0xf5, 0xfe, 0x06, 0x99, 0x64, + 0x5f, 0x8a, 0xd5, 0x10, 0xd8, 0x30, 0x91, 0x85, 0xf5, 0x1e, 0x28, 0x89, + 0x48, 0xd4, 0x78, 0x74, 0x14, 0x59, 0x1e, 0x3f, 0x61, 0x99, 0x1a, 0xba, + 0xfa, 0x4e, 0xd0, 0x68, 0x46, 0x75, 0xd4, 0x06, 0x98, 0x58, 0xe7, 0x2b, + 0xcc, 0xc5, 0xe1, 0xbf, 0x4a, 0xd7, 0x94, 0xc6, 0x95, 0xe1, 0x37, 0x16, + 0x0b, 0x38, 0xf4, 0x89, 0x25, 0x32, 0x56, 0x08, 0x2e, 0x17, 0xc9, 0x71, + 0x53, 0xc5, 0x33, 0x44, 0x6a, 0x2a, 0x24, 0x00, 0x66, 0x11, 0xa2, 0x10, + 0x01, 0x5d, 0x53, 0x69, 0xfb, 0x91, 0x4b, 0x87, 0x8c, 0xb4, 0x29, 0x63, + 0x35, 0x99, 0xf3, 0x59, 0x72, 0x3f, 0x76, 0xbf, 0xe5, 0x27, 0x37, 0xdf, + 0x6f, 0x5b, 0x7d, 0x67, 0x65, 0xd6, 0x0b, 0x08, 0x37, 0xbd, 0xff, 0xf8, + 0xd5, 0x1e, 0x07, 0xcd, 0xf7, 0x1d, 0x2a, 0x86, 0xb0, 0x34, 0xee, 0x62, + 0x45, 0x7a, 0x7f, 0x70, 0x35, 0x3a, 0x96, 0x85, 0xa4, 0x7b, 0x43, 0x5f, + 0x80, 0xd2, 0x00, 0xf8, 0x32, 0x72, 0xe5, 0xde, 0x12, 0x60, 0xec, 0x5b, + 0xe7, 0x9e, 0x40, 0x8f, 0xab, 0xea, 0x56, 0xb8, 0x9b, 0x0f, 0x66, 0x0b, + 0x95, 0x9a, 0x47, 0x06, 0x6a, 0x95, 0x5c, 0x52, 0x1f, 0x8d, 0xf1, 0xcb, + 0xe2, 0x5d, 0x44, 0x01, 0x77, 0xaa, 0xfb, 0x30, 0x46, 0xf9, 0x5f, 0x88, + 0xf7, 0x78, 0x4a, 0xce, 0x1d, 0x14, 0x20, 0xeb, 0x83, 0x1b, 0xc5, 0xf8, + 0x3f, 0xea, 0x85, 0x7c, 0xba, 0x2c, 0x12, 0xd8, 0x7e, 0x7d, 0x3e, 0x0a, + 0x65, 0x66, 0x16, 0xdc, 0x9f, 0x0c, 0x3c, 0x76, 0xd0, 0x06, 0xb4, 0x8d, + 0x0a, 0xbc, 0xfe, 0x4f, 0xc6, 0x57, 0xec, 0x84, 0x9e, 0x56, 0x90, 0xda, + 0x8a, 0x7e, 0x84, 0x67, 0xc8, 0xa5, 0xfd, 0xef, 0xe3, 0xc3, 0x5d, 0xda, + 0xad, 0x03, 0xd9, 0x1c, 0xed, 0xe5, 0x6e, 0xbc, 0xb8, 0xde, 0x77, 0x91, + 0x4c, 0xea, 0x3c, 0xc4, 0xe8, 0x8a, 0xd9, 0x5b, 0xac, 0xdf, 0xa3, 0xb4, + 0x50, 0x64, 0x06, 0x42, 0xe1, 0x8b, 0xae, 0xec, 0xe7, 0x1f, 0xc5, 0x73, + 0xc4, 0x28, 0xe7, 0xd2, 0x1e, 0xaf, 0xfc, 0x7a, 0x41, 0x3a, 0x91, 0xfd, + 0x4f, 0xff, 0x65, 0x04, 0x65, 0x9a, 0xd0, 0x7f, 0xb3, 0x8c, 0x57, 0x18, + 0x05, 0x0b, 0xc4, 0x38, 0x95, 0x16, 0x71, 0x49, 0xb5, 0x33, 0xec, 0xc4, + 0x04, 0x62, 0xee, 0xb5, 0x76, 0x29, 0x6f, 0xeb, 0x8b, 0xba, 0x18, 0x29, + 0xa6, 0x53, 0x81, 0x02, 0x80, 0xdb, 0x58, 0x29, 0x8f, 0xe9, 0x5d, 0x80, + 0x21, 0xe1, 0xcc, 0x2c, 0x4b, 0x64, 0xdc, 0xdc, 0x20, 0xfc, 0x8b, 0x65, + 0x12, 0xe0, 0x87, 0x8f, 0xf8, 0xa9, 0xb4, 0xbf, 0xb0, 0x9b, 0x04, 0xb0, + 0x62, 0x1c, 0x2b, 0x8c, 0x77, 0xb4, 0xc2, 0x54, 0x7f, 0x91, 0x43, 0x1e, + 0xd8, 0x70, 0x32, 0x17, 0x26, 0x3d, 0x2a, 0x05, 0x98, 0x9a, 0x2f, 0xc9, + 0xeb, 0x3f, 0x9f, 0xf2, 0x55, 0x6f, 0x89, 0xa0, 0x23, 0x6b, 0xc2, 0xee, + 0x3c, 0x68, 0x1c, 0x16, 0x84, 0x26, 0x1c, 0xf4, 0xdc, 0xed, 0xc0, 0xbb, + 0x80, 0x64, 0xf9, 0xb1, 0x55, 0x2e, 0x7c, 0x37, 0x35, 0x90, 0x48, 0x19, + 0xaa, 0xf1, 0xc5, 0x83, 0x35, 0xd2, 0x92, 0xa6, 0x6d, 0xe7, 0x01, 0xb5, + 0x61, 0xad, 0xc0, 0x59, 0xac, 0x5c, 0x69, 0xe2, 0x6a, 0x77, 0x87, 0x96, + 0x46, 0x66, 0xab, 0x3b, 0x8a, 0x7a, 0x46, 0xba, 0x7e, 0x42, 0xa7, 0xa6, + 0x72, 0x5c, 0xd6, 0xcd, 0x1c, 0xa7, 0xfa, 0xaf, 0x57, 0x63, 0x51, 0xd7, + 0xec, 0x72, 0x5d, 0x60, 0x1b, 0x2d, 0xd8, 0x08, 0x48, 0x96, 0xac, 0xd4, + 0x38, 0x39, 0xf1, 0x9b, 0xf8, 0x79, 0xe7, 0xf0, 0x7d, 0xe3, 0x44, 0x9e, + 0x19, 0xc5, 0x5f, 0x2e, 0x96, 0x40, 0x09, 0xb0, 0xd2, 0x0e, 0xde, 0x4b, + 0x5f, 0x12, 0x00, 0x55, 0xa2, 0x81, 0x93, 0x3d, 0x11, 0x41, 0x18, 0xf5, + 0x51, 0xbd, 0x12, 0xcf, 0x06, 0x56, 0x0b, 0x34, 0x4e, 0xce, 0x97, 0x93, + 0xf5, 0xed, 0x19, 0x46, 0xa5, 0xaa, 0xbc, 0xab, 0x35, 0x26, 0x77, 0x22, + 0xea, 0x54, 0xa3, 0x0d, 0xc4, 0xf0, 0xa6, 0xae, 0xe3, 0xb3, 0x1d, 0xf9, + 0xa5, 0x9e, 0x01, 0x4f, 0x1a, 0xe9, 0x1e, 0x24, 0xdc, 0xf4, 0x06, 0x3f, + 0x2b, 0x38, 0x5a, 0x49, 0x86, 0x33, 0x93, 0xc9, 0xae, 0x37, 0xf4, 0x39, + 0x9d, 0x6a, 0x09, 0x36, 0xd8, 0x32, 0x89, 0xc6, 0x45, 0xb8, 0x25, 0xd3, + 0x02, 0xcc, 0x22, 0x22, 0x2a, 0x56, 0x6b, 0x6d, 0xce, 0xb8, 0x35, 0x4e, + 0x01, 0x02, 0xa0, 0x61, 0xe6, 0xcc, 0xae, 0x0c, 0x44, 0xc4, 0x88, 0xab, + 0x78, 0x34, 0xbe, 0x5b, 0xc8, 0x8f, 0xe8, 0xce, 0xc0, 0xd6, 0x19, 0xd9, + 0x5a, 0x9d, 0xca, 0xa7, 0xa8, 0x40, 0xaf, 0x01, 0x20, 0x76, 0xc6, 0xf5, + 0xd9, 0x41, 0x97, 0x5f, 0x45, 0x32, 0x45, 0xf5, 0xf0, 0x21, 0x18, 0xcd, + 0x52, 0xbd, 0x98, 0xb4, 0x40, 0xb4, 0xa0, 0xdd, 0x9e, 0x55, 0x9b, 0x75, + 0x78, 0xa5, 0xa4, 0x38, 0x9a, 0xb5, 0x1f, 0xa9, 0x50, 0x90, 0xee, 0x44, + 0xf1, 0xfd, 0x87, 0x7c, 0xb8, 0x82, 0x1d, 0x2d, 0xfd, 0x1d, 0x2e, 0xbb, + 0xac, 0x5a, 0x7f, 0x0b, 0xd2, 0xec, 0x7e, 0x74, 0xe4, 0x55, 0x31, 0xe7, + 0xe7, 0xf0, 0xb3, 0x1e, 0xed, 0x36, 0x1a, 0xec, 0xb5, 0x0c, 0xc2, 0x08, + 0xd8, 0x46, 0xed, 0xed, 0xd9, 0xae, 0x72, 0x27, 0xed, 0x8b, 0xcf, 0x97, + 0x41, 0x54, 0x56, 0xe4, 0xe3, 0xa2, 0x96, 0x17, 0x91, 0x25, 0xa0, 0x7b, + 0x2c, 0x14, 0xc3, 0x7e, 0xd1, 0xb8, 0x0f, 0xc4, 0x0e, 0x5d, 0xd7, 0x7a, + 0x1f, 0x75, 0xf1, 0x56, 0xda, 0x5d, 0x23, 0x8f, 0x99, 0x66, 0xbd, 0xf0, + 0xa5, 0xf6, 0x43, 0xae, 0xb1, 0x23, 0x77, 0x76, 0x4c, 0x19, 0x58, 0x7a, + 0x02, 0xb5, 0x27, 0x5f, 0xa0, 0xa3, 0xec, 0x49, 0x5c, 0xec, 0xd4, 0x25, + 0xfc, 0x23, 0x3b, 0x79, 0xd3, 0x81, 0x3f, 0xa0, 0xa5, 0xc1, 0xe2, 0xac, + 0x6c, 0x7b, 0xa9, 0x5b, 0x11, 0x5d, 0xd0, 0xd4, 0xf6, 0x60, 0x9a, 0x60, + 0x19, 0xa8, 0x94, 0x78, 0x5f, 0x9b, 0xf2, 0x99, 0x12, 0xdd, 0x51, 0x3b, + 0x88, 0xe2, 0x98, 0x1d, 0x08, 0xb8, 0x8e, 0xea, 0x71, 0x98, 0x97, 0x6b, + 0x52, 0x51, 0x97, 0x2e, 0x82, 0x1b, 0xd6, 0x2a, 0xdb, 0x3b, 0x64, 0xc5, + 0x88, 0x8e, 0x13, 0x93, 0xd0, 0x26, 0x8f, 0x69, 0xa2, 0xcf, 0xac, 0xdf, + 0x95, 0x4b, 0xda, 0xfe, 0x30, 0x54, 0x9b, 0xb8, 0xa3, 0xfa, 0x47, 0x40, + 0xeb, 0x90, 0xb1, 0x34, 0x7f, 0x63, 0xdb, 0x52, 0xd2, 0x82, 0x92, 0xcb, + 0xec, 0xee, 0xcc, 0xab, 0x43, 0xbf, 0x6e, 0x7c, 0x29, 0x3a, 0x68, 0x9d, + 0x37, 0x5f, 0x26, 0x99, 0x48, 0x35, 0x1f, 0xe1, 0xb4, 0x9d, 0x5f, 0x6c, + 0x1c, 0x50, 0xf9, 0x2a, 0xaf, 0x0c, 0xfe, 0x63, 0x11, 0xc7, 0xe3, 0x1a, + 0xda, 0x68, 0xd4, 0xee, 0x21, 0xda, 0xef, 0xd0, 0x04, 0x6a, 0x84, 0x63, + 0x94, 0x76, 0x98, 0x9c, 0xb3, 0xe8, 0x20, 0xd9, 0xb4, 0xca, 0x45, 0xde, + 0x50, 0xb0, 0xe7, 0x33, 0x82, 0x17, 0xa6, 0x19, 0x77, 0x42, 0x22, 0xf4, + 0x52, 0xd5, 0x97, 0x5c, 0xbc, 0x58, 0xf8, 0xe9, 0x16, 0xc8, 0xf1, 0xb6, + 0x33, 0x2f, 0xb4, 0x7e, 0x43, 0x80, 0x29, 0xbd, 0x19, 0xde, 0x8d, 0x43, + 0x9c, 0x80, 0xe7, 0xd7, 0x4d, 0x14, 0xeb, 0xb4, 0xb0, 0xdd, 0x61, 0x51, + 0xf2, 0xfe, 0x94, 0x73, 0x44, 0x20, 0xde, 0x76, 0xcc, 0x89, 0xa2, 0x3f, + 0xb0, 0x41, 0x40, 0x9c, 0x71, 0xc6, 0x77, 0x3f, 0xf6, 0xa0, 0x85, 0xd7, + 0x4e, 0x85, 0x38, 0xf8, 0xc5, 0xd0, 0xe3, 0xdb, 0xa1, 0xbc, 0x97, 0xad, + 0x94, 0x50, 0xb0, 0x61, 0xc1, 0x95, 0x2f, 0xff, 0xcd, 0x02, 0x5f, 0x8d, + 0x5b, 0xaf, 0xb8, 0x45, 0x13, 0xb9, 0xf5, 0x57, 0x2a, 0x1e, 0x61, 0x21, + 0xe4, 0xfc, 0xc5, 0xf5, 0x25, 0x31, 0xaf, 0xe3, 0x8e, 0x02, 0x44, 0xd1, + 0x0f, 0x3e, 0x7b, 0xb0, 0xe5, 0x3a, 0x08, 0x78, 0x14, 0x48, 0xe1, 0x96, + 0x38, 0xf3, 0x22, 0x35, 0x66, 0x37, 0x5f, 0x8d, 0xdd, 0x5d, 0x09, 0x9b, + 0xa0, 0xf0, 0xd1, 0xc4, 0x04, 0x76, 0x95, 0x81, 0x52, 0xba, 0x14, 0xdc, + 0x53, 0xef, 0x4c, 0xc9, 0x03, 0xfe, 0x08, 0x35, 0x4a, 0xd2, 0xef, 0x25, + 0x5b, 0xa8, 0x4c, 0xe6, 0x4c, 0x9c, 0x97, 0x47, 0x5e, 0xd9, 0xc6, 0x58, + 0x52, 0xe5, 0xe6, 0x72, 0xac, 0xb5, 0x5f, 0x04, 0x9a, 0x87, 0x88, 0x88, + 0x1a, 0x7c, 0x8f, 0x19, 0xdd, 0x7f, 0xb4, 0x1f, 0xde, 0xc9, 0x1e, 0xce, + 0xcb, 0xdd, 0xb5, 0x64, 0x2d, 0x5d, 0xd8, 0xb5, 0xd2, 0xb1, 0xe0, 0xa6, + 0xd8, 0xde, 0x90, 0x0b, 0x0f, 0xc8, 0xd4, 0xba, 0x26, 0xbe, 0xef, 0x73, + 0x1d, 0x27, 0x8f, 0x36, 0x6d, 0x13, 0x62, 0xfd, 0x16, 0xfb, 0xbe, 0xb0, + 0x03, 0x22, 0xe3, 0x60, 0xca, 0x34, 0xf9, 0xa7, 0xd6, 0x82, 0xd0, 0x5f, + 0x19, 0x14, 0x74, 0xa3, 0x6d, 0xff, 0x06, 0x60, 0x3f, 0xb5, 0x50, 0xf8, + 0x47, 0x2f, 0xc5, 0x9f, 0xbc, 0xcd, 0x8f, 0xd5, 0x7b, 0x33, 0x72, 0xc7, + 0xc6, 0x8a, 0x64, 0x72, 0x4f, 0x3b, 0xfd, 0xf5, 0x62, 0x06, 0xe1, 0x1d, + 0x78, 0x8e, 0x28, 0xe7, 0x43, 0x52, 0x42, 0x5a, 0x93, 0x9f, 0x65, 0xe9, + 0xf6, 0x18, 0x42, 0x45, 0x63, 0xe8, 0x27, 0x44, 0xf4, 0xfa, 0x76, 0x5a, + 0x6b, 0x0d, 0x96, 0x90, 0x7b, 0x3a, 0x40, 0x90, 0xf2, 0x15, 0x9e, 0x81, + 0xa6, 0x39, 0xc2, 0xd8, 0x7c, 0xba, 0xe7, 0xd9, 0x67, 0x73, 0xc8, 0x68, + 0x79, 0xc9, 0xda, 0xb0, 0x5c, 0xa2, 0xbd, 0x0c, 0x50, 0xd3, 0xb4, 0x11, + 0x5d, 0x3c, 0x29, 0xfe, 0xb8, 0x5b, 0xf8, 0x43, 0x87, 0x11, 0x3f, 0x58, + 0x15, 0xac, 0x4a, 0xc4, 0xc6, 0x7f, 0x28, 0xf0, 0x5a, 0x37, 0xe0, 0x93, + 0x2e, 0x51, 0xa3, 0x69, 0x7a, 0x2c, 0x86, 0x43, 0xe2, 0x7a, 0x6e, 0xf9, + 0x90, 0x20, 0x7e, 0xd1, 0x15, 0x41, 0x5a, 0x69, 0xc7, 0xb2, 0x2a, 0x12, + 0x4d, 0x87, 0x76, 0xc0, 0x74, 0xd7, 0x41, 0x4a, 0x54, 0x92, 0xdb, 0x2a, + 0xda, 0x61, 0xbe, 0xa1, 0xe3, 0x76, 0x8d, 0xe4, 0x32, 0x61, 0x8d, 0x1a, + 0xbf, 0xc2, 0xd2, 0x81, 0x72, 0x04, 0xc1, 0x35, 0xdd, 0xa1, 0xdb, 0xa2, + 0xe7, 0xc7, 0xf4, 0xa7, 0xc4, 0x97, 0xbe, 0xec, 0x64, 0x30, 0xb5, 0x54, + 0x74, 0x3f, 0x94, 0x8d, 0xbe, 0x78, 0xd0, 0x86, 0x30, 0x4d, 0x32, 0x03, + 0x46, 0x03, 0x75, 0x00, 0xf0, 0xf2, 0xb6, 0x31, 0x31, 0x66, 0x29, 0xaf, + 0xee, 0xc8, 0xb3, 0x06, 0x01, 0x02, 0x7e, 0x1f, 0x56, 0x6f, 0xec, 0x98, + 0xb6, 0x23, 0x3e, 0x7b, 0xaa, 0xd6, 0xe5, 0xd3, 0x7f, 0x35, 0x79, 0xa5, + 0xf0, 0x97, 0xa7, 0x8a, 0xba, 0x18, 0xba, 0x20, 0x34, 0x4d, 0xe3, 0x98, + 0x42, 0xca, 0xb7, 0xbc, 0xe4, 0x7a, 0xd4, 0x34, 0xef, 0x60, 0xf2, 0x8c, + 0x3c, 0x23, 0x23, 0xeb, 0xdc, 0x85, 0x5d, 0xdc, 0x1b, 0xe8, 0xd0, 0x9d, + 0x77, 0x0a, 0x7e, 0x89, 0xad, 0x22, 0xfe, 0xfe, 0x74, 0x53, 0x5b, 0x97, + 0xcc, 0x31, 0x8c, 0x80, 0x72, 0xe3, 0x66, 0x0f, 0x43, 0xc5, 0x01, 0x11, + 0xd0, 0x70, 0x9e, 0x1b, 0xa2, 0x16, 0xa2, 0xce, 0xb4, 0xd4, 0xfe, 0x89, + 0x1f, 0x42, 0x1e, 0xdd, 0xcd, 0x3e, 0x02, 0x87, 0x70, 0xb5, 0xb1, 0x7d, + 0xe1, 0x20, 0x02, 0xc4, 0x77, 0x20, 0x50, 0x90, 0x35, 0x2a, 0x4d, 0x04, + 0x47, 0x0d, 0x56, 0x0a, 0xf9, 0xe3, 0x0b, 0xfc, 0xae, 0x06, 0x0c, 0x3c, + 0xac, 0x8a, 0xb6, 0x5c, 0xca, 0xbd, 0x86, 0x83, 0xe1, 0x8f, 0xe2, 0x95, + 0xbe, 0x88, 0x31, 0x29, 0x37, 0x3e, 0x8c, 0x11, 0xc1, 0xfd, 0x41, 0x06, + 0x5b, 0xd7, 0xb1, 0x36, 0xbd, 0x9e, 0x09, 0x80, 0x08, 0xd6, 0x31, 0xe7, + 0x43, 0x3d, 0x56, 0x84, 0x12, 0xb5, 0xfc, 0x63, 0x6d, 0x68, 0x6d, 0xcf, + 0xac, 0x2a, 0x02, 0xf6, 0x67, 0x9e, 0x3f, 0xba, 0x47, 0xf9, 0x22, 0x51, + 0x2e, 0x6f, 0xdf, 0x43, 0x4c, 0x97, 0x54, 0xd4, 0xfd, 0xb2, 0x42, 0xa2, + 0x2d, 0x92, 0xbe, 0x4a, 0x00, 0x82, 0x91, 0xea, 0xcc, 0xca, 0x03, 0x0b, + 0xed, 0xee, 0x72, 0x3f, 0xc8, 0x3e, 0xbf, 0xa6, 0x87, 0xdf, 0x63, 0x7a, + 0x83, 0x07, 0x90, 0xdf, 0x47, 0x87, 0x37, 0xc4, 0xbb, 0x10, 0xaa, 0x0c, + 0x53, 0x17, 0x81, 0x75, 0x1e, 0xbe, 0x03, 0xdd, 0x5d, 0xe1, 0x74, 0x72, + 0xcc, 0xb5, 0xb5, 0xbd, 0xd7, 0xd6, 0xf4, 0x3d, 0xfe, 0xd5, 0xd1, 0xa6, + 0xfc, 0x0a, 0xdc, 0x62, 0x18, 0x25, 0xb1, 0x67, 0xbd, 0xed, 0xd5, 0x9a, + 0xaa, 0x89, 0xe8, 0x98, 0x16, 0xfe, 0x41, 0xb5, 0x77, 0xd1, 0x50, 0xfc, + 0x59, 0x4d, 0x04, 0xf0, 0x39, 0x23, 0x97, 0x7e, 0x09, 0x30, 0xaf, 0xf4, + 0x66, 0x6a, 0xc2, 0x54, 0x71, 0x2d, 0x7b, 0x2a, 0xcd, 0x3c, 0x80, 0x79, + 0xc3, 0x3e, 0x5a, 0x1d, 0x4d, 0xfe, 0x92, 0x47, 0x26, 0xa5, 0xfe, 0xfb, + 0xc7, 0xda, 0x3e, 0xc7, 0xa1, 0xe2, 0x0f, 0x91, 0x0a, 0x83, 0xbe, 0x0f, + 0x5c, 0x9a, 0x63, 0x64, 0x7a, 0x5e, 0xb7, 0x5e, 0xad, 0x18, 0xc3, 0x71, + 0x54, 0xc7, 0x53, 0x39, 0xb2, 0x7c, 0xd4, 0x1e, 0x6c, 0xde, 0xeb, 0x62, + 0x5a, 0x3b, 0xdd, 0x15, 0xad, 0xbc, 0x6e, 0x02, 0xb7, 0x21, 0x0e, 0x2f, + 0xcb, 0x3e, 0xad, 0xb2, 0xc4, 0xe3, 0x38, 0x7e, 0x39, 0xf8, 0x1e, 0x28, + 0xe9, 0x6b, 0x89, 0x76, 0xf1, 0xaf, 0xe1, 0x82, 0xe1, 0x58, 0xe5, 0x68, + 0xdc, 0x6e, 0x8e, 0xe0, 0x4a, 0x2c, 0x61, 0x35, 0x11, 0x42, 0xd5, 0x3d, + 0x7d, 0xa0, 0x26, 0x52, 0x59, 0x40, 0xe5, 0xfe, 0x8b, 0x22, 0xe9, 0x3b, + 0x5a, 0x26, 0x5d, 0x9b, 0x93, 0xd0, 0xe6, 0xb8, 0x0a, 0x2e, 0xad, 0xa2, + 0xd2, 0xe9, 0xa4, 0xb6, 0x30, 0xf3, 0x37, 0xb1, 0x19, 0x99, 0xc7, 0x7e, + 0xe5, 0xbf, 0x4a, 0xdb, 0x1d, 0x3f, 0xbf, 0x0a, 0xc9, 0xfc, 0xe3, 0x9f, + 0x00, 0x94, 0xa0, 0x9b, 0xf2, 0x99, 0x1a, 0x1d, 0xe8, 0x93, 0x93, 0xa6, + 0xc4, 0x74, 0x20, 0x3a, 0xf3, 0x5c, 0x9e, 0x8e, 0xba, 0xe7, 0x59, 0xa9, + 0xcd, 0xc9, 0x23, 0x73, 0x44, 0xf0, 0x4b, 0x49, 0xb2, 0xc0, 0xa0, 0xd4, + 0xf2, 0x05, 0xbc, 0xd8, 0x04, 0x1b, 0x6d, 0x6e, 0x6e, 0x25, 0x16, 0xf3, + 0x4e, 0xfc, 0xae, 0x7a, 0xa0, 0x8a, 0x94, 0xbf, 0x0a, 0xe7, 0xd0, 0xca, + 0xa7, 0xa7, 0x4f, 0xc5, 0xbc, 0xe6, 0x5f, 0xec, 0xda, 0x9b, 0xd1, 0xd0, + 0x97, 0x69, 0x8e, 0x0f, 0xea, 0x2c, 0x66, 0x0d, 0x2a, 0x99, 0xb0, 0x4d, + 0x4c, 0x85, 0x55, 0x5d, 0x68, 0xfc, 0x36, 0xce, 0xa7, 0xfc, 0xac, 0x61, + 0x90, 0xf3, 0x88, 0xf4, 0x5d, 0x32, 0x00, 0xae, 0x27, 0xd3, 0xc0, 0xf7, + 0x04, 0xb1, 0x23, 0x66, 0xc2, 0x7e, 0x2b, 0xdc, 0x9e, 0x7f, 0xf6, 0xef, + 0xc8, 0x87, 0x09, 0x56, 0xd7, 0x68, 0x92, 0xef, 0x63, 0x03, 0xb6, 0xac, + 0x14, 0xc3, 0x7d, 0xa8, 0xc1, 0x79, 0x1b, 0xb5, 0x12, 0xa3, 0x30, 0x16, + 0xe4, 0xd3, 0xb3, 0xf2, 0x12, 0xc6, 0x56, 0x7e, 0x54, 0xc9, 0xa9, 0xf6, + 0x84, 0x0b, 0xe2, 0xaa, 0x8c, 0x5e, 0x4d, 0xed, 0x28, 0x97, 0x7d, 0xc2, + 0x28, 0x43, 0xc5, 0x2e, 0xe0, 0x73, 0x16, 0x4b, 0x60, 0x7d, 0x9c, 0x08, + 0x08, 0x46, 0x2f, 0x45, 0x44, 0x79, 0xc8, 0xb7, 0xb8, 0x04, 0x7e, 0x95, + 0x3d, 0x18, 0x6b, 0x89, 0x8f, 0xb0, 0x8e, 0x2c, 0x71, 0x9e, 0x9a, 0xc0, + 0x88, 0x3f, 0x18, 0x4d, 0x25, 0x3e, 0x45, 0x53, 0x2b, 0xad, 0x26, 0x85, + 0xdf, 0x53, 0x4b, 0x58, 0x08, 0x70, 0xd0, 0xfb, 0xd7, 0x57, 0x70, 0x15, + 0x9b, 0x3b, 0xf0, 0xa2, 0x54, 0x41, 0xa4, 0xbb, 0x2e, 0x16, 0xb9, 0x24, + 0x8d, 0x7c, 0xed, 0x39, 0xd9, 0x9e, 0x28, 0x7f, 0xe7, 0x9f, 0x8b, 0xf2, + 0x09, 0x88, 0xdf, 0xdf, 0x32, 0xef, 0xb7, 0x7d, 0x8b, 0xac, 0x9d, 0x82, + 0xcf, 0x82, 0x74, 0x3c, 0x16, 0x9e, 0x9f, 0x39, 0xff, 0x4d, 0x70, 0xce, + 0xa3, 0x16, 0xee, 0x4d, 0xe9, 0x23, 0x75, 0xf7, 0x60, 0x11, 0xa1, 0xcb, + 0x02, 0x3a, 0xa0, 0x4f, 0x12, 0x88, 0x00, 0x00, 0x00, 0x18, 0xe1, 0xb4, + 0x31, 0xe9, 0x60, 0x80, 0x11, 0x4f, 0x1c, 0x1f, 0x3f, 0xa8, 0xbe, 0xc7, + 0x3b, 0xc3, 0x5e, 0xc6, 0x1e, 0x23, 0xbd, 0x5c, 0x99, 0x2b, 0x4e, 0x5c, + 0xac, 0x24, 0xe1, 0x73, 0x4f, 0x24, 0xa3, 0x69, 0xad, 0xa6, 0xe5, 0xff, + 0x02, 0x6a, 0x8c, 0x99, 0x96, 0xa5, 0x8c, 0x35, 0x9e, 0xdf, 0x56, 0xd0, + 0x5f, 0x99, 0xce, 0x32, 0xe2, 0x43, 0x9f, 0xe0, 0x8b, 0x0b, 0xad, 0x05, + 0x8f, 0x0a, 0x1a, 0x0e, 0x04, 0x47, 0x9d, 0x86, 0xd3, 0xdd, 0xd2, 0xd5, + 0x1a, 0xf2, 0x08, 0xe7, 0xfe, 0x99, 0x1b, 0xc7, 0x2d, 0x2b, 0x40, 0x8a, + 0x86, 0xc6, 0x7d, 0x0e, 0x6e, 0xdb, 0x0a, 0xd9, 0x45, 0x5e, 0xd6, 0xc6, + 0x2b, 0x54, 0x97, 0x74, 0xc2, 0xd2, 0xb2, 0x80, 0x04, 0x96, 0x3d, 0x21, + 0x60, 0x3b, 0xf2, 0xc0, 0xda, 0x22, 0xb6, 0x7a, 0x82, 0x7d, 0xd1, 0x53, + 0x7b, 0xd0, 0xd6, 0xb5, 0x97, 0x9c, 0x1e, 0x0e, 0x67, 0xc9, 0x5f, 0x3e, + 0x73, 0x10, 0xc8, 0x43, 0xfd, 0x08, 0x43, 0x03, 0xd3, 0x1e, 0xbc, 0x08, + 0x3f, 0x42, 0xb3, 0x25, 0x9d, 0xa8, 0xed, 0xc2, 0x5b, 0x65, 0x67, 0x11, + 0xc8, 0x9d, 0xd9, 0xab, 0x1e, 0x42, 0x57, 0x18, 0x8d, 0x10, 0x84, 0x65, + 0x36, 0xda, 0x68, 0x03, 0x95, 0xe8, 0x72, 0x01, 0x4f, 0x0a, 0xb4, 0xa4, + 0x2f, 0x4b, 0x84, 0x07, 0x64, 0xdb, 0x77, 0x7e, 0xb8, 0x23, 0x71, 0xef, + 0xae, 0x4f, 0x60, 0x10, 0xc1, 0x31, 0x28, 0x49, 0xdf, 0x32, 0xfb, 0x6a, + 0xd3, 0x9e, 0x29, 0x7e, 0x3e, 0x4c, 0xdf, 0xa7, 0xf0, 0x87, 0x3a, 0x28, + 0xd1, 0xfe, 0xfb, 0x76, 0xf0, 0xd2, 0x25, 0x67, 0x79, 0x55, 0x09, 0x1c, + 0x4e, 0xcb, 0x2c, 0x4c, 0x10, 0xfd, 0x9b, 0x3d, 0x94, 0x90, 0x13, 0xf9, + 0x1c, 0xd6, 0x20, 0x92, 0xc9, 0x4f, 0x1e, 0x84, 0x82, 0x41, 0xbd, 0xc2, + 0x90, 0xae, 0xb4, 0x4b, 0x1a, 0x44, 0xfa, 0x40, 0x6d, 0x4e, 0x0a, 0x01, + 0xe3, 0x8c, 0x35, 0x9a, 0x9f, 0x60, 0xfd, 0x90, 0x40, 0xd8, 0x58, 0x41, + 0xca, 0xa0, 0x70, 0xb6, 0xff, 0xc0, 0x07, 0x92, 0x41, 0xa1, 0x78, 0xc8, + 0x6b, 0x1a, 0x33, 0x88, 0x8d, 0xd1, 0x9c, 0x08, 0x6c, 0xfd, 0x0a, 0x92, + 0x05, 0xdd, 0x4c, 0xae, 0x9d, 0xc2, 0x7d, 0x01, 0x35, 0x6e, 0xa7, 0xf4, + 0x34, 0xa2, 0x11, 0xa8, 0xe9, 0xbb, 0xc5, 0x52, 0x7c, 0x82, 0x42, 0x3e, + 0x41, 0x45, 0x33, 0xed, 0xad, 0xc9, 0x79, 0x14, 0xff, 0xe4, 0x2c, 0x83, + 0xc4, 0xbe, 0x06, 0xfd, 0x06, 0x01, 0x99, 0xf8, 0x0a, 0x53, 0x3e, 0x15, + 0x04, 0x92, 0x56, 0x00, 0xe8, 0x91, 0xbb, 0xb9, 0x9b, 0x00, 0x60, 0xab, + 0x63, 0x54, 0x82, 0x25, 0x63, 0xf1, 0x14, 0x22, 0x58, 0x50, 0x38, 0x7d, + 0x13, 0x60, 0x8f, 0x27, 0x89, 0xc1, 0x9c, 0xa2, 0xd8, 0x0f, 0x9d, 0x0f, + 0x86, 0xf9, 0x8e, 0xa6, 0xb3, 0x3a, 0x3e, 0xce, 0x9f, 0x22, 0x4f, 0x9c, + 0xea, 0x60, 0x25, 0x6f, 0x4e, 0xe0, 0x91, 0x9f, 0x2c, 0x9e, 0x2b, 0x8b, + 0xac, 0xf1, 0x41, 0x6a, 0x82, 0xbc, 0x62, 0x64, 0xda, 0xf1, 0xdf, 0xe2, + 0x23, 0x5d, 0x18, 0x7c, 0xbd, 0x23, 0xfb, 0x67, 0x9e, 0xe4, 0x45, 0x65, + 0x92, 0xec, 0xd4, 0xe3, 0x10, 0x35, 0x84, 0x3b, 0x2d, 0x9c, 0x3f, 0xbb, + 0x50, 0x23, 0xc2, 0x0d, 0xed, 0x2a, 0x34, 0xc9, 0x9e, 0x53, 0x58, 0x8b, + 0x35, 0x1a, 0x81, 0xd9, 0xda, 0xc6, 0x42, 0xbb, 0x8d, 0x56, 0x4b, 0xc1, + 0x70, 0x45, 0x14, 0x75, 0x06, 0xf5, 0x2b, 0xed, 0xeb, 0xce, 0x2d, 0x82, + 0x82, 0x18, 0x19, 0x15, 0xb9, 0x87, 0x40, 0xb7, 0xe7, 0x6d, 0xbc, 0xf2, + 0x41, 0xd0, 0x8f, 0xe0, 0xba, 0x3f, 0x7f, 0xbb, 0x1d, 0xc3, 0xef, 0x5c, + 0xc1, 0xc4, 0x21, 0x90, 0xf2, 0x0c, 0x5d, 0x4e, 0xa9, 0x28, 0x6d, 0x3e, + 0x9a, 0x82, 0x31, 0x52, 0xe4, 0xf9, 0x14, 0x5d, 0x02, 0x0a, 0x22, 0xc2, + 0x63, 0xc0, 0xea, 0x9b, 0x70, 0x2f, 0xa2, 0xb1, 0xc7, 0x31, 0xe1, 0xb0, + 0xd2, 0xe8, 0x14, 0xf8, 0x55, 0x46, 0x22, 0x88, 0x79, 0x89, 0x7a, 0x44, + 0x42, 0xf2, 0xd6, 0x9a, 0xaf, 0x70, 0x86, 0xa5, 0x6b, 0x45, 0x9d, 0xc9, + 0x8f, 0x12, 0xcb, 0x7a, 0xb5, 0x76, 0x46, 0x1f, 0xa1, 0xdc, 0xe1, 0x53, + 0x5e, 0x62, 0xe3, 0x84, 0xf3, 0x5a, 0x00, 0x32, 0xe0, 0x1a, 0x9f, 0x48, + 0x8e, 0x4b, 0x3d, 0xc4, 0x5a, 0xa8, 0x91, 0x49, 0x74, 0xc9, 0x6e, 0x44, + 0x79, 0x5b, 0xf2, 0xc3, 0xb1, 0x58, 0x1f, 0xfc, 0xef, 0x4d, 0x4b, 0x64, + 0x5c, 0xbc, 0x55, 0xfa, 0x76, 0x14, 0x40, 0xdb, 0x60, 0x27, 0x25, 0xfc, + 0x8f, 0xdf, 0x36, 0x74, 0x1c, 0x51, 0x4d, 0xb3, 0x75, 0x13, 0x37, 0xfd, + 0x9b, 0x3c, 0x9f, 0x26, 0x3b, 0xf5, 0xd6, 0x98, 0x9d, 0x23, 0x19, 0x07, + 0xa8, 0xad, 0x69, 0xe9, 0x8a, 0xcb, 0x2f, 0xc2, 0xeb, 0x26, 0x5b, 0x36, + 0xbc, 0x8b, 0xc8, 0x00, 0xdc, 0xf6, 0xba, 0x81, 0xa3, 0xf2, 0xa9, 0x71, + 0x27, 0x43, 0x96, 0x0b, 0x11, 0x91, 0x17, 0x5c, 0x38, 0x74, 0xbe, 0xe1, + 0xb1, 0xbb, 0xec, 0x3b, 0xf2, 0xf4, 0x35, 0xd1, 0x5d, 0x19, 0x0f, 0x6d, + 0xdf, 0x87, 0xf7, 0x44, 0xd8, 0x4d, 0x8a, 0x0f, 0x51, 0x86, 0xca, 0xa3, + 0x2c, 0xa7, 0xad, 0x9b, 0xf6, 0xb6, 0xc4, 0x15, 0xff, 0xf5, 0x33, 0xee, + 0xf5, 0x0d, 0x9d, 0xda, 0x6c, 0xd0, 0xf1, 0xf5, 0x23, 0x08, 0x46, 0x02, + 0x0a, 0x2d, 0xc8, 0x95, 0x56, 0x5c, 0xd4, 0x4a, 0xa1, 0xf2, 0x94, 0xe5, + 0x1e, 0x38, 0xc8, 0x52, 0xfc, 0x46, 0x64, 0xff, 0x24, 0x74, 0xdd, 0x39, + 0x51, 0x98, 0x72, 0x3c, 0xdf, 0xbb, 0x5f, 0x0b, 0x96, 0x63, 0xc0, 0x76, + 0xd8, 0x08, 0xa6, 0xd7, 0x77, 0xd3, 0xd0, 0x9d, 0x7f, 0x76, 0x4d, 0xee, + 0xc3, 0x99, 0xf7, 0x1e, 0x92, 0xf2, 0xe4, 0x85, 0x2c, 0xee, 0x1d, 0xfc, + 0xf6, 0x3c, 0x4b, 0x03, 0x5e, 0xde, 0x17, 0x1b, 0x43, 0xb0, 0xce, 0x0f, + 0x6d, 0x90, 0x42, 0xf2, 0xd6, 0x1d, 0xe7, 0xef, 0xfa, 0x84, 0x44, 0xe5, + 0xaa, 0xf1, 0x32, 0xa9, 0xf1, 0xe1, 0x1f, 0xe3, 0xd2, 0x6d, 0x39, 0x4a, + 0x51, 0xe1, 0x88, 0x38, 0xd2, 0x60, 0xea, 0xf8, 0xf5, 0x9a, 0xa1, 0x69, + 0x5d, 0xa9, 0xdb, 0xeb, 0x1c, 0xc8, 0x90, 0xf2, 0xf7, 0xe7, 0xd2, 0xe2, + 0x2a, 0x23, 0x61, 0x4e, 0xf4, 0x84, 0x71, 0x4d, 0x05, 0xbd, 0x64, 0x29, + 0x6d, 0x76, 0xed, 0x61, 0xfa, 0x92, 0x9d, 0x90, 0x4e, 0x1d, 0xd1, 0x7f, + 0x98, 0xd9, 0x41, 0x04, 0x74, 0x3a, 0x33, 0x5a, 0xcc, 0x1f, 0x3f, 0x25, + 0x72, 0xcc, 0x06, 0xe0, 0x27, 0x86, 0xee, 0xd7, 0x29, 0x14, 0xa5, 0x13, + 0xe9, 0x74, 0x94, 0x62, 0x14, 0x24, 0xe3, 0xa0, 0x2b, 0xc1, 0x48, 0xf6, + 0xb1, 0xd5, 0x74, 0x76, 0x25, 0xef, 0x28, 0x9a, 0x55, 0x2c, 0x4c, 0xfe, + 0xf8, 0x7f, 0xfe, 0x6a, 0x9c, 0xa3, 0xd5, 0x03, 0x5f, 0x71, 0xb2, 0x30, + 0x3a, 0xa7, 0xc4, 0xf2, 0x36, 0x43, 0x01, 0xbd, 0x97, 0xde, 0x8e, 0x4a, + 0x0e, 0x70, 0x41, 0xbf, 0x07, 0xd9, 0x41, 0x11, 0x86, 0x48, 0x66, 0xc1, + 0x95, 0x0f, 0xee, 0x35, 0xdf, 0x13, 0xc0, 0x59, 0xb2, 0x63, 0x28, 0x54, + 0x82, 0xb8, 0xb8, 0xb6, 0xf8, 0xbd, 0xc8, 0xe6, 0x0d, 0xc4, 0xb9, 0xb7, + 0x80, 0x57, 0x56, 0xdb, 0xaf, 0xce, 0x6d, 0x54, 0xb0, 0x62, 0xe1, 0xfd, + 0xab, 0x64, 0xc2, 0x1c, 0x18, 0xd0, 0x7a, 0x3c, 0xfd, 0xa0, 0x98, 0x77, + 0xfe, 0x0f, 0x17, 0x6d, 0x4e, 0x0c, 0x2d, 0x8e, 0x09, 0x97, 0xeb, 0xed, + 0x87, 0xa5, 0x28, 0x7a, 0x9e, 0xc4, 0xd6, 0xfb, 0xd1, 0x2b, 0xd9, 0x60, + 0x56, 0xf9, 0x9a, 0xb7, 0x49, 0xd0, 0xaf, 0xe7, 0xc7, 0x8a, 0xf5, 0x87, + 0x43, 0xc3, 0x85, 0x73, 0xbb, 0x20, 0x2e, 0x99, 0x95, 0xbe, 0xc7, 0xc1, + 0xe2, 0x11, 0x1c, 0xe7, 0x04, 0xe4, 0x70, 0x2e, 0xc4, 0xc7, 0xdf, 0x77, + 0x6c, 0x6a, 0xc5, 0x0b, 0x15, 0x46, 0xee, 0x2d, 0x10, 0x67, 0x3c, 0xc2, + 0x6b, 0xb8, 0x99, 0x8e, 0xc9, 0x33, 0xb3, 0x0c, 0x9f, 0x7b, 0x34, 0x19, + 0xe6, 0x62, 0x85, 0x1e, 0x8c, 0x33, 0x63, 0xb1, 0x38, 0xc4, 0x5c, 0x41, + 0xf5, 0xcc, 0x43, 0xd8, 0xef, 0x26, 0x13, 0xec, 0xf2, 0xd6, 0x71, 0x27, + 0x4b, 0xe1, 0xa9, 0x06, 0xe4, 0x0e, 0xf8, 0xb7, 0x34, 0x4a, 0x14, 0x95, + 0xf8, 0x66, 0xc7, 0xfd, 0xca, 0xd0, 0x2c, 0xcf, 0x24, 0x82, 0x89, 0x96, + 0x3c, 0x6b, 0x34, 0x6b, 0x9a, 0x3c, 0xe3, 0xff, 0x01, 0x01, 0xb2, 0x18, + 0x60, 0x51, 0x30, 0x8c, 0xc6, 0x4d, 0x34, 0x80, 0x17, 0x8e, 0xd3, 0x43, + 0x40, 0x63, 0xab, 0x37, 0x7b, 0x5e, 0x08, 0x15, 0xbb, 0xd1, 0x3a, 0x67, + 0xc3, 0x64, 0x4e, 0x94, 0x0d, 0x6e, 0x12, 0xc9, 0xa8, 0x1c, 0xb7, 0x46, + 0xf8, 0xdf, 0x81, 0xdd, 0xe4, 0xad, 0x39, 0x2a, 0x63, 0xed, 0xad, 0x14, + 0xad, 0xe9, 0x21, 0xfa, 0xe1, 0xf0, 0x78, 0x75, 0xe0, 0xde, 0x7b, 0x6f, + 0xe5, 0x90, 0x52, 0x74, 0x40, 0xc3, 0x87, 0x66, 0x53, 0xff, 0xa0, 0xd1, + 0x79, 0x64, 0xa6, 0x6e, 0x7c, 0xa1, 0x6f, 0xcf, 0x48, 0x91, 0x43, 0xb0, + 0xcf, 0x33, 0x3f, 0x46, 0x40, 0xcc, 0x6d, 0xea, 0x09, 0x09, 0x27, 0xdc, + 0xe5, 0x64, 0x10, 0xca, 0xe1, 0x1f, 0x7e, 0xe4, 0x1f, 0x1e, 0xa2, 0x49, + 0xcd, 0x78, 0x89, 0xb5, 0xf5, 0x26, 0x6b, 0x8b, 0x3e, 0x17, 0x28, 0xe2, + 0xf6, 0xdd, 0xd5, 0xcb, 0x2d, 0x82, 0x8a, 0x3c, 0xbe, 0xba, 0x23, 0x5f, + 0xd5, 0x75, 0x84, 0x77, 0x19, 0xbf, 0xa0, 0xe1, 0x9a, 0x7e, 0x0b, 0xf8, + 0xbd, 0x9f, 0x8a, 0xf8, 0xb5, 0xa1, 0x65, 0x3b, 0xf7, 0x0b, 0x63, 0x53, + 0xf8, 0x7d, 0xa2, 0xe7, 0x41, 0xe3, 0x60, 0xe6, 0xb4, 0x1b, 0xc1, 0x46, + 0x56, 0xad, 0x7c, 0xfb, 0x05, 0x69, 0x33, 0x90, 0xdf, 0x33, 0xef, 0x13, + 0x44, 0x26, 0xae, 0xa1, 0x6b, 0x44, 0xbe, 0x1c, 0xff, 0xed, 0x6b, 0x36, + 0x30, 0x83, 0xce, 0x47, 0x5e, 0xbc, 0x7a, 0x86, 0x84, 0x67, 0x05, 0xf5, + 0x78, 0x95, 0x40, 0x61, 0x55, 0x1b, 0xa2, 0xfa, 0x97, 0x59, 0x54, 0x19, + 0x34, 0x6a, 0xe8, 0xc5, 0xd3, 0x21, 0xb3, 0x11, 0x9d, 0xea, 0x93, 0x01, + 0x96, 0x49, 0xae, 0xa8, 0x15, 0x87, 0xaa, 0xa9, 0xac, 0x38, 0x4a, 0x07, + 0x74, 0xcf, 0x99, 0xc4, 0x58, 0x91, 0xde, 0xc9, 0xec, 0x85, 0xde, 0x1a, + 0x91, 0x04, 0x8c, 0x86, 0xae, 0x29, 0x6e, 0x00, 0x39, 0xa9, 0x1e, 0x8e, + 0x21, 0xba, 0x0f, 0xb3, 0x35, 0xc2, 0xce, 0xfa, 0x93, 0x41, 0x1b, 0xee, + 0x47, 0xf6, 0x09, 0xa1, 0xe9, 0xd9, 0xd0, 0xdd, 0xef, 0xf8, 0x06, 0x3d, + 0xea, 0xaf, 0x7c, 0xff, 0xf2, 0x43, 0x65, 0xd6, 0x81, 0xa8, 0x33, 0xf3, + 0x16, 0x94, 0x46, 0x67, 0xa4, 0x14, 0xbc, 0xe9, 0xd2, 0xd0, 0x17, 0x12, + 0x0a, 0xf8, 0x72, 0x26, 0x66, 0xd6, 0xb6, 0xcc, 0x53, 0x5f, 0x8b, 0x56, + 0xe2, 0x92, 0xba, 0xff, 0x09, 0xf2, 0xe4, 0xc7, 0xd3, 0xd5, 0xb1, 0xf3, + 0x34, 0x8e, 0x60, 0x96, 0x3b, 0x31, 0x00, 0x16, 0x16, 0xac, 0xd8, 0x39, + 0x8b, 0xec, 0xd9, 0x02, 0xfd, 0x3d, 0x7d, 0xe3, 0xda, 0x14, 0x48, 0x17, + 0xe4, 0x95, 0x29, 0xb0, 0x3b, 0x22, 0x96, 0x04, 0x5c, 0xee, 0x17, 0x58, + 0x96, 0x70, 0x77, 0x0c, 0x5e, 0x47, 0x64, 0xe5, 0x42, 0x77, 0xd9, 0x3d, + 0x38, 0xc3, 0xb9, 0x33, 0x6c, 0xff, 0xac, 0x8c, 0x05, 0x4c, 0x42, 0x3d, + 0x13, 0x6d, 0x9c, 0x73, 0x71, 0x60, 0x9f, 0xdc, 0xa4, 0x60, 0x24, 0x7f, + 0x23, 0x01, 0x96, 0x42, 0xb5, 0x04, 0xfe, 0xca, 0xe0, 0x57, 0x73, 0x59, + 0xb4, 0xdf, 0x1d, 0xf0, 0xe6, 0x93, 0x6f, 0x13, 0xd7, 0x66, 0x78, 0xab, + 0xf7, 0xe4, 0xfa, 0xf9, 0x13, 0x5a, 0x68, 0x9f, 0xff, 0x9d, 0xb9, 0xcd, + 0x16, 0x3c, 0x1d, 0x66, 0x7c, 0xb5, 0x91, 0xfc, 0x0a, 0xd7, 0x78, 0x8a, + 0xa1, 0xad, 0xab, 0x7f, 0xd4, 0x63, 0x33, 0x8c, 0x6c, 0xc8, 0x12, 0x10, + 0x78, 0x62, 0x68, 0xf7, 0xba, 0x36, 0xc8, 0x2b, 0x76, 0x72, 0xa3, 0xdc, + 0x8c, 0x03, 0x25, 0x72, 0x97, 0xe7, 0xcb, 0x34, 0xdb, 0x87, 0x2c, 0x34, + 0xa5, 0x92, 0xc0, 0xdc, 0xed, 0xff, 0xf2, 0xca, 0xb6, 0xe7, 0xb4, 0x04, + 0x8d, 0xb0, 0x6a, 0x40, 0x80, 0x14, 0xee, 0xb1, 0x54, 0xb3, 0xf4, 0x6f, + 0x72, 0x1a, 0x91, 0x4e, 0x19, 0x7a, 0x87, 0xed, 0x5a, 0x1e, 0x1c, 0xa4, + 0xc9, 0x59, 0x97, 0x77, 0xc8, 0x36, 0x50, 0x36, 0x0a, 0x8b, 0xd7, 0x89, + 0x14, 0x34, 0xd0, 0xed, 0x88, 0x25, 0x1e, 0x75, 0x40, 0xa1, 0xb5, 0x1a, + 0x9e, 0x5e, 0xd1, 0x65, 0xa7, 0x49, 0x9b, 0xbe, 0xfc, 0xf2, 0xb0, 0xc9, + 0x3d, 0x16, 0x52, 0xc3, 0x0d, 0x51, 0x3d, 0xa4, 0x78, 0x8c, 0x26, 0xb8, + 0x3e, 0xa7, 0x2c, 0xa0, 0x84, 0x69, 0x3f, 0x25, 0x5b, 0xbe, 0x50, 0x8f, + 0x5b, 0x7d, 0xa3, 0xb0, 0xbc, 0xe1, 0xef, 0x6e, 0x86, 0x8c, 0xcb, 0x19, + 0x14, 0x4c, 0x23, 0x4e, 0x35, 0xc6, 0x95, 0x71, 0x1f, 0x1c, 0x68, 0x54, + 0x21, 0x37, 0x08, 0xb1, 0x99, 0xbf, 0x37, 0x42, 0x17, 0x16, 0xff, 0xdd, + 0xe9, 0x42, 0xa2, 0x27, 0x8e, 0x21, 0xdd, 0xd6, 0x6a, 0x3f, 0x80, 0x89, + 0x5a, 0xa7, 0x9c, 0x0f, 0xfa, 0x3c, 0x23, 0x7c, 0xd9, 0x2b, 0xdc, 0xdc, + 0x64, 0x09, 0x7e, 0xab, 0x96, 0x50, 0x53, 0x9c, 0xde, 0xb5, 0x26, 0x3d, + 0x19, 0x48, 0xfb, 0xdd, 0x73, 0x94, 0x7e, 0xcb, 0x84, 0x10, 0xb0, 0xe2, + 0x93, 0xe4, 0x7b, 0x86, 0xc1, 0xbc, 0x55, 0xb7, 0x8d, 0x0e, 0x43, 0xae, + 0x66, 0xec, 0xb6, 0x44, 0xee, 0x5f, 0x16, 0x88, 0x3e, 0xf9, 0xb1, 0xec, + 0x31, 0x24, 0xb5, 0x64, 0x78, 0xb3, 0x24, 0xcf, 0x59, 0x27, 0xac, 0xd0, + 0x53, 0x3a, 0xd3, 0x45, 0xc8, 0x42, 0x97, 0x8f, 0x16, 0x53, 0x5e, 0x0a, + 0xc7, 0x74, 0x82, 0x4f, 0x6f, 0x1e, 0xb2, 0x0d, 0x8e, 0x17, 0xc0, 0xda, + 0xb7, 0xad, 0xc0, 0xf8, 0x87, 0xf4, 0x2d, 0xba, 0xf9, 0x49, 0xcc, 0x40, + 0x4c, 0xac, 0xe5, 0xb2, 0x9e, 0x8f, 0x75, 0xea, 0x0a, 0x0e, 0xf8, 0xf3, + 0xec, 0x0c, 0x26, 0x92, 0x64, 0xeb, 0x83, 0x73, 0xec, 0x76, 0x30, 0x6d, + 0xea, 0xd1, 0x39, 0x84, 0x42, 0x3b, 0xef, 0x86, 0xe2, 0x9e, 0x84, 0xd1, + 0xd2, 0xc6, 0xf7, 0x59, 0x3c, 0xb8, 0x9f, 0xb4, 0x05, 0xc9, 0xe0, 0x0c, + 0x8f, 0x36, 0x82, 0xb4, 0x35, 0x4a, 0x5e, 0x25, 0x38, 0xfe, 0xb3, 0x68, + 0x9a, 0x04, 0xe9, 0x58, 0x6c, 0x93, 0xd3, 0x40, 0xca, 0xf8, 0x8c, 0xb1, + 0x63, 0x0f, 0x03, 0x97, 0x6c, 0x1b, 0x38, 0x71, 0x1b, 0x5d, 0x43, 0xee, + 0xb2, 0xf2, 0x87, 0xe3, 0x97, 0x6e, 0x7c, 0xcc, 0x0c, 0xec, 0xcf, 0x66, + 0xec, 0xd4, 0x64, 0x88, 0xa8, 0xb2, 0x09, 0x2b, 0xae, 0xc1, 0xdd, 0x8b, + 0x90, 0x36, 0x15, 0x53, 0xf3, 0x6d, 0x3b, 0xaa, 0x5f, 0xa7, 0xcc, 0x1c, + 0x0b, 0xde, 0x89, 0xca, 0x71, 0xf2, 0x55, 0xc4, 0xa7, 0xc5, 0xfe, 0x3e, + 0x74, 0xe4, 0xac, 0x9e, 0x47, 0xe8, 0xe2, 0x8f, 0x48, 0x5c, 0x52, 0xf7, + 0x67, 0x18, 0xe1, 0x53, 0x35, 0x45, 0xd4, 0x38, 0x41, 0x34, 0x5a, 0x2b, + 0x55, 0xe7, 0xae, 0x3b, 0x7a, 0xd5, 0x79, 0x34, 0x33, 0x4e, 0xcc, 0x23, + 0x6e, 0x45, 0xe9, 0xd4, 0xf8, 0x84, 0x30, 0x70, 0xa2, 0xc8, 0x30, 0xb7, + 0xdc, 0x8f, 0x93, 0xc3, 0x04, 0x15, 0x50, 0x1e, 0x18, 0x12, 0x28, 0x23, + 0x65, 0xa5, 0x81, 0x0e, 0x52, 0x8a, 0x69, 0x01, 0xa2, 0x29, 0xf5, 0xe4, + 0x36, 0x11, 0x94, 0x1b, 0xc5, 0x5d, 0x6e, 0x3f, 0x1f, 0x16, 0x64, 0x6c, + 0x00, 0x87, 0xfc, 0x05, 0xd1, 0x7a, 0x23, 0xa1, 0xb1, 0xfb, 0x08, 0x24, + 0xa1, 0x61, 0x08, 0x37, 0x44, 0xf2, 0xbf, 0xc1, 0x04, 0x1d, 0x59, 0x16, + 0x67, 0x0a, 0x9c, 0x46, 0x4e, 0x1e, 0xeb, 0x90, 0x09, 0x6c, 0x26, 0xb0, + 0xaa, 0xf8, 0x46, 0xde, 0x3d, 0xac, 0x78, 0x92, 0x3e, 0x20, 0xe0, 0x8b, + 0xad, 0x5e, 0x97, 0xf6, 0x43, 0xb1, 0x99, 0x6c, 0x06, 0x28, 0x71, 0xa5, + 0xad, 0x18, 0xa2, 0x60, 0x5d, 0x37, 0x3f, 0x9e, 0x1f, 0x67, 0xa1, 0x41, + 0x23, 0x44, 0x38, 0x7e, 0x56, 0xde, 0xfd, 0x9d, 0x96, 0x8f, 0x56, 0x20, + 0xe3, 0xa1, 0x67, 0xb8, 0x44, 0xf3, 0xb7, 0x87, 0x47, 0x89, 0x69, 0x90, + 0x5b, 0x9f, 0xbc, 0x51, 0x82, 0x5f, 0x09, 0x30, 0x35, 0x3f, 0xde, 0xda, + 0xef, 0x86, 0x79, 0xb0, 0xf0, 0x9b, 0x32, 0x70, 0x56, 0xdb, 0xfa, 0x7c, + 0xe5, 0x8a, 0xcc, 0xae, 0x0a, 0xef, 0xb8, 0x46, 0x12, 0x0f, 0x5f, 0x75, + 0x0c, 0xf0, 0xb9, 0x94, 0x27, 0x8e, 0x10, 0xc4, 0x61, 0xf3, 0xe9, 0x60, + 0x46, 0x9e, 0x43, 0x33, 0xa1, 0xde, 0xca, 0x43, 0x56, 0x1a, 0x03, 0x1d, + 0x82, 0x69, 0x3f, 0xf1, 0xbb, 0x49, 0x9d, 0x09, 0xb4, 0x17, 0x52, 0x80, + 0x10, 0x23, 0x06, 0xc5, 0xbe, 0x88, 0x39, 0xc1, 0xb2, 0xda, 0x7e, 0xed, + 0x1c, 0x13, 0xc4, 0x38, 0x09, 0xf9, 0x28, 0x31, 0x85, 0xe4, 0x84, 0xc1, + 0x07, 0xb9, 0xea, 0x8c, 0xfa, 0xd9, 0x7f, 0xe4, 0xb9, 0x2f, 0x83, 0x4d, + 0x21, 0xc7, 0x44, 0xe5, 0xa4, 0x96, 0xd4, 0x09, 0xe3, 0xa7, 0xdc, 0x80, + 0x27, 0x50, 0x68, 0x32, 0x27, 0x77, 0xc1, 0x06, 0x69, 0x06, 0xdd, 0x2c, + 0x87, 0x3d, 0xdb, 0xec, 0x5b, 0x4d, 0x31, 0x51, 0x17, 0xbf, 0xf0, 0xbb, + 0x82, 0x52, 0xbf, 0xcd, 0x08, 0x7e, 0x64, 0xeb, 0x5a, 0x3b, 0xfa, 0x26, + 0xc5, 0xcd, 0x25, 0x3a, 0x8d, 0xf2, 0xde, 0x98, 0x50, 0x2e, 0x47, 0x69, + 0x33, 0xaf, 0x79, 0xf9, 0x4b, 0x7d, 0xdf, 0x13, 0xdf, 0x05, 0x94, 0x15, + 0x53, 0x59, 0xce, 0xad, 0xd6, 0xae, 0x05, 0x37, 0x34, 0x5b, 0xc6, 0x11, + 0x19, 0x12, 0xed, 0xd9, 0x3a, 0x72, 0x85, 0xa9, 0x54, 0xab, 0x03, 0xe4, + 0xa4, 0x1e, 0x8b, 0x03, 0x5a, 0x14, 0xa7, 0x52, 0x10, 0x3e, 0x8b, 0xda, + 0x78, 0x3b, 0x09, 0xc9, 0xb3, 0x6f, 0x05, 0x5b, 0xd5, 0x6c, 0x34, 0x3b, + 0xdf, 0x0e, 0xd8, 0x91, 0x4b, 0xd3, 0x87, 0xa1, 0x8a, 0x3e, 0x4f, 0x85, + 0xea, 0x18, 0xa4, 0x6b, 0xe9, 0x90, 0x00, 0xaa, 0x1b, 0x90, 0xfa, 0xe4, + 0x01, 0x8e, 0xb0, 0x86, 0x8e, 0xf6, 0x25, 0xd0, 0x58, 0x77, 0xf1, 0x6e, + 0xd6, 0xe6, 0x82, 0x76, 0xff, 0x8c, 0x56, 0xf4, 0x0f, 0xa9, 0xe6, 0x0c, + 0x91, 0x65, 0xef, 0xd8, 0x23, 0xea, 0x03, 0x80, 0x12, 0xee, 0x1d, 0x9f, + 0xdd, 0xfa, 0xd8, 0x0e, 0x0d, 0x7e, 0x4d, 0x5e, 0xd5, 0x65, 0x15, 0x49, + 0xa8, 0xf2, 0xfd, 0xc3, 0xcc, 0xa4, 0xd9, 0x1f, 0x1f, 0x3b, 0x79, 0x79, + 0xbc, 0x76, 0x23, 0xc8, 0xb9, 0xa1, 0xc9, 0xdc, 0x0f, 0x66, 0x5c, 0x36, + 0x06, 0x95, 0x6e, 0x23, 0x37, 0x4f, 0x8c, 0x84, 0x34, 0x5a, 0x1f, 0x26, + 0x9b, 0xbe, 0xf0, 0xb9, 0x30, 0xff, 0xb8, 0xdd, 0x9a, 0xaa, 0xe1, 0xb2, + 0x85, 0x83, 0x83, 0x77, 0xa7, 0x5f, 0x0c, 0xf7, 0x31, 0x97, 0x70, 0x49, + 0x4f, 0x3d, 0x1f, 0x93, 0x3f, 0x7b, 0x87, 0x56, 0x1d, 0xc8, 0xc6, 0xb4, + 0x75, 0xc4, 0xc5, 0xd5, 0x27, 0xc3, 0xdb, 0xe3, 0x1b, 0xf5, 0xec, 0x83, + 0xb3, 0xa0, 0x2e, 0x25, 0x18, 0x6b, 0x98, 0x21, 0x18, 0x37, 0x09, 0xcd, + 0x03, 0x6a, 0xdd, 0x29, 0x70, 0xf1, 0x13, 0x3f, 0x25, 0x55, 0x75, 0x8c, + 0x2f, 0x3d, 0xec, 0xf1, 0xb3, 0x0c, 0x2d, 0x3e, 0xb3, 0x61, 0x95, 0x0e, + 0xa5, 0xf1, 0xce, 0xd1, 0x75, 0xa2, 0x1b, 0xe8, 0x32, 0x7e, 0xca, 0xc9, + 0x03, 0xb3, 0xd8, 0x99, 0x76, 0xa5, 0xbc, 0xd8, 0xd6, 0x63, 0x28, 0x41, + 0xdc, 0x2d, 0xa2, 0x49, 0x4d, 0x49, 0xe2, 0x7e, 0x36, 0x85, 0x5d, 0x1e, + 0x35, 0xc6, 0x66, 0xdb, 0x61, 0xbc, 0x73, 0xfd, 0x5f, 0xee, 0x3e, 0x95, + 0x01, 0xb0, 0x1d, 0x0a, 0x6b, 0x31, 0x33, 0x6e, 0x27, 0x9b, 0x86, 0x5a, + 0x3f, 0xf6, 0x97, 0xdc, 0x3f, 0x52, 0x5d, 0xec, 0xbf, 0xfd, 0x98, 0xbc, + 0x24, 0x6f, 0x40, 0x37, 0x78, 0x6f, 0x51, 0xe7, 0xa9, 0x9a, 0x02, 0xcc, + 0x5a, 0x93, 0x4f, 0xae, 0x4e, 0xcf, 0xa6, 0x0c, 0x3f, 0x87, 0x4f, 0xf8, + 0x94, 0xa1, 0xa9, 0x5d, 0xa1, 0x40, 0xd8, 0xf5, 0xb9, 0xd6, 0x28, 0x40, + 0x7d, 0xcf, 0x3c, 0x56, 0x36, 0x7f, 0x9f, 0x16, 0x97, 0xb2, 0x30, 0x38, + 0x01, 0x24, 0xce, 0xa1, 0x69, 0x01, 0xf5, 0xbc, 0x82, 0xbe, 0x44, 0xb6, + 0x2b, 0xc4, 0xae, 0x27, 0x2d, 0x91, 0x26, 0x69, 0x39, 0xc3, 0xf0, 0xb7, + 0xb0, 0xaa, 0x9e, 0xad, 0x33, 0xa8, 0x71, 0x76, 0x10, 0xac, 0xa7, 0x24, + 0x97, 0x6c, 0x94, 0x68, 0xff, 0x6a, 0xb4, 0xd0, 0xbf, 0x8c, 0xaf, 0x60, + 0x27, 0x96, 0xbd, 0xfc, 0xec, 0xa6, 0xfb, 0xbd, 0xcf, 0xb5, 0x24, 0xc8, + 0x32, 0xa5, 0x07, 0xce, 0xf3, 0x12, 0x92, 0x9e, 0x2f, 0x94, 0xd9, 0x7b, + 0x43, 0xbf, 0x38, 0x09, 0xff, 0xb5, 0x81, 0xb2, 0x96, 0x3d, 0x1a, 0xfe, + 0x9f, 0x1f, 0x8a, 0x15, 0x5b, 0x97, 0x6b, 0x1f, 0x9b, 0xb2, 0x7c, 0x22, + 0x86, 0xed, 0x85, 0x11, 0xa8, 0x18, 0x3a, 0x52, 0x7a, 0xb5, 0x62, 0xa1, + 0xc4, 0x1a, 0x79, 0x44, 0xc1, 0xd9, 0xb1, 0xc7, 0x6b, 0x36, 0x66, 0xdf, + 0x4a, 0x2a, 0x2b, 0x1e, 0x0f, 0x15, 0x01, 0x63, 0xec, 0x2c, 0x5d, 0x34, + 0x2c, 0x62, 0xb5, 0x6b, 0xcc, 0xe4, 0xcb, 0x2a, 0x11, 0xf7, 0xab, 0x83, + 0xf9, 0x47, 0x69, 0xb9, 0xda, 0x29, 0x75, 0x76, 0x04, 0x4e, 0xe0, 0x11, + 0xd9, 0xea, 0xb7, 0xea, 0x75, 0x92, 0xd6, 0x1b, 0x7d, 0x65, 0xa9, 0x61, + 0x73, 0x2c, 0x08, 0x4e, 0x81, 0x3f, 0x05, 0x8c, 0x96, 0x27, 0x32, 0xdb, + 0x80, 0xbe, 0x2d, 0xa7, 0xb3, 0xcf, 0x49, 0x04, 0xa4, 0x0f, 0x89, 0x61, + 0xf0, 0x5a, 0x85, 0xe1, 0x85, 0x5d, 0x82, 0x39, 0x32, 0x82, 0xf5, 0xb5, + 0x5b, 0x0d, 0x06, 0xcb, 0x30, 0x77, 0x01, 0xfb, 0x36, 0xd9, 0x44, 0x03, + 0xee, 0x89, 0x31, 0xcb, 0x31, 0xa2, 0xd1, 0xcd, 0xbf, 0x52, 0x12, 0x14, + 0xa8, 0xaf, 0xf7, 0x6a, 0xc3, 0x0e, 0x13, 0x20, 0xb6, 0xb3, 0x86, 0xa1, + 0xfc, 0xa8, 0x79, 0x05, 0xe4, 0x3f, 0x5f, 0x30, 0x67, 0x46, 0x01, 0x17, + 0x33, 0xb2, 0xe7, 0x49, 0xea, 0xd9, 0x58, 0xa2, 0xfe, 0x22, 0xcc, 0x53, + 0xd6, 0x76, 0x6b, 0x3c, 0xcb, 0xed, 0x87, 0x53, 0x83, 0xbf, 0x60, 0x5d, + 0x42, 0x8d, 0x28, 0xcb, 0x13, 0x04, 0xd2, 0x0c, 0xde, 0x98, 0xa0, 0x35, + 0xed, 0x63, 0xde, 0x9a, 0xda, 0xf1, 0xce, 0xb1, 0x70, 0xeb, 0xb3, 0x7b, + 0x3c, 0xc9, 0x85, 0xdf, 0x10, 0xbe, 0xf3, 0xbb, 0x42, 0xee, 0x71, 0x60, + 0x76, 0xc7, 0x29, 0x2b, 0xc3, 0xf3, 0x76, 0xde, 0xb1, 0xb7, 0xf2, 0xe2, + 0xf8, 0x37, 0xce, 0x1f, 0x26, 0xf4, 0xc1, 0x0a, 0x2e, 0x33, 0x36, 0x0a, + 0x7a, 0xf9, 0x99, 0x06, 0x61, 0xd5, 0xad, 0xa0, 0x59, 0x64, 0x77, 0x78, + 0x3b, 0xb6, 0x51, 0x2a, 0xc4, 0x7e, 0xe6, 0xc5, 0x17, 0x59, 0x6c, 0x50, + 0x96, 0x06, 0x92, 0x76, 0xa2, 0x11, 0xad, 0xfe, 0xaf, 0x02, 0x9d, 0x70, + 0x46, 0xe3, 0x6d, 0x9c, 0x63, 0xa4, 0x76, 0x23, 0xb2, 0x5f, 0xc0, 0xad, + 0x30, 0x50, 0xad, 0x14, 0x09, 0xc3, 0x4c, 0x72, 0x2c, 0xe5, 0x78, 0xfd, + 0x38, 0x60, 0x8c, 0x98, 0x7d, 0xd8, 0x04, 0xc0, 0xf0, 0xcb, 0x33, 0xc4, + 0xd1, 0x55, 0x97, 0xc3, 0x3c, 0xdb, 0xea, 0x8d, 0x5e, 0xec, 0x48, 0x5d, + 0xbd, 0xc4, 0xae, 0x72, 0xef, 0x5d, 0xf4, 0x7e, 0x4e, 0x3f, 0x5b, 0x16, + 0x84, 0x36, 0x62, 0xce, 0x0e, 0x6a, 0xcb, 0xae, 0x44, 0xdb, 0xf7, 0xa6, + 0xf4, 0x60, 0x99, 0xd2, 0x82, 0x3d, 0xe7, 0xfc, 0xef, 0x38, 0x4b, 0x15, + 0x91, 0x41, 0x34, 0x42, 0x73, 0x2b, 0x8c, 0xd1, 0x76, 0xf8, 0x24, 0x08, + 0x0f, 0x46, 0x7a, 0x73, 0xd3, 0x13, 0xb8, 0x06, 0x2f, 0x1e, 0x3b, 0xe0, + 0x2d, 0xcc, 0xd8, 0x35, 0xa2, 0x55, 0x4f, 0x3c, 0xd7, 0xb5, 0x68, 0x8f, + 0x07, 0x09, 0x7e, 0x1e, 0xd8, 0xa3, 0xe3, 0xb0, 0x0c, 0x58, 0x50, 0xdc, + 0xfc, 0x57, 0xb1, 0xea, 0xab, 0x43, 0x10, 0x78, 0x8e, 0xfb, 0x13, 0xb8, + 0x26, 0x3d, 0xc8, 0xcb, 0x18, 0x29, 0x95, 0x2f, 0x29, 0x12, 0x56, 0x8e, + 0x56, 0xa3, 0xce, 0xdd, 0x25, 0x92, 0x7c, 0x43, 0xce, 0x3d, 0x57, 0xb1, + 0x29, 0xb3, 0x17, 0xd0, 0xf5, 0x8e, 0xf8, 0x64, 0x63, 0x5a, 0xc4, 0x26, + 0xfe, 0x7d, 0xf7, 0x9b, 0xb0, 0x35, 0xb1, 0x5f, 0x1c, 0xf2, 0x5b, 0xec, + 0xad, 0x04, 0x8e, 0xa5, 0x9a, 0xd8, 0x21, 0xe3, 0xdd, 0x4c, 0x59, 0xf0, + 0x9b, 0xcd, 0xaf, 0x4f, 0xc6, 0x8d, 0x11, 0x6a, 0xfb, 0xdf, 0xcf, 0x4a, + 0x7d, 0x30, 0x3a, 0x1b, 0x52, 0xa3, 0x95, 0x0d, 0xc4, 0xce, 0xf4, 0xf8, + 0xff, 0x6c, 0x86, 0xa4, 0x63, 0x79, 0xcd, 0xcd, 0x40, 0xfd, 0xdc, 0x79, + 0x33, 0x97, 0xcc, 0x18, 0xc8, 0x12, 0xa2, 0x0f, 0xf9, 0x34, 0x7b, 0x07, + 0x6c, 0x6b, 0xc8, 0xa3, 0x4d, 0x02, 0x6c, 0xc8, 0x9e, 0x27, 0x62, 0x2d, + 0x90, 0x10, 0x9f, 0x25, 0xe0, 0x4f, 0xeb, 0xaf, 0x14, 0xd0, 0xff, 0x5d, + 0xd4, 0xb0, 0xec, 0x6e, 0x90, 0x78, 0x78, 0x58, 0x5c, 0xaf, 0x06, 0xe0, + 0x8c, 0x8b, 0x44, 0x22, 0x79, 0xc8, 0x97, 0xee, 0x71, 0xb2, 0x6b, 0x7a, + 0x45, 0x6a, 0x4b, 0xfe, 0x3c, 0x51, 0xcf, 0x87, 0x65, 0xd1, 0x51, 0x0c, + 0xed, 0x23, 0xe7, 0x97, 0xd6, 0xb4, 0xad, 0xcb, 0xce, 0x56, 0xb2, 0xb4, + 0x7c, 0x3a, 0x82, 0xef, 0xfb, 0xc9, 0x91, 0xb2, 0x84, 0x5d, 0xfd, 0xac, + 0xd2, 0x30, 0xda, 0xc8, 0x5c, 0x15, 0xfd, 0x91, 0x1f, 0x24, 0xd4, 0x76, + 0xe7, 0xcf, 0x92, 0xfa, 0x9c, 0x13, 0x1c, 0xf8, 0x24, 0x14, 0x62, 0x2a, + 0x5f, 0x40, 0x9f, 0x16, 0x17, 0x05, 0xc0, 0xa7, 0x67, 0x72, 0x82, 0xc2, + 0xda, 0x05, 0x69, 0x56, 0xff, 0xd5, 0x5a, 0xe7, 0xd0, 0xf1, 0x75, 0xeb, + 0xec, 0xf5, 0x2f, 0xb1, 0x5b, 0x27, 0xb3, 0x54, 0x02, 0x41, 0x9f, 0xd4, + 0x50, 0x7d, 0xbf, 0xfa, 0xc9, 0xc8, 0x7a, 0xb8, 0xe6, 0x9b, 0x3b, 0x28, + 0x32, 0x6f, 0x72, 0x09, 0x04, 0x3e, 0xf8, 0xd8, 0xb3, 0xed, 0xa7, 0x25, + 0xf8, 0x90, 0xfd, 0xcb, 0x40, 0xe8, 0x7a, 0x2c, 0x2b, 0xcc, 0x53, 0xd4, + 0xab, 0x2c, 0x83, 0xae, 0x77, 0xf6, 0x9c, 0x48, 0x1e, 0x71, 0x8e, 0x78, + 0x4d, 0x0b, 0x2e, 0x0d, 0x53, 0x71, 0xc6, 0x56, 0x69, 0x83, 0x34, 0xa1, + 0xca, 0x95, 0x93, 0x10, 0x77, 0x3e, 0xc4, 0xac, 0x8e, 0xcd, 0xf0, 0x15, + 0xa2, 0xc5, 0x8e, 0xf1, 0x99, 0xf6, 0x49, 0x62, 0x4c, 0x1a, 0x25, 0x7a, + 0xb0, 0xaf, 0x0a, 0x4f, 0xa1, 0xee, 0x4d, 0xd4, 0x4e, 0x2b, 0x31, 0x77, + 0x86, 0x21, 0xd1, 0xa1, 0xf6, 0x6a, 0x93, 0x48, 0x49, 0xd8, 0x54, 0xe8, + 0x48, 0x28, 0x3f, 0x1c, 0xe6, 0x8d, 0xd6, 0xeb, 0x75, 0xd7, 0xc1, 0x69, + 0x3e, 0xf3, 0xce, 0x4c, 0xb2, 0x03, 0x82, 0x22, 0x91, 0x2f, 0x07, 0x97, + 0x23, 0x67, 0x3d, 0x62, 0xb4, 0x4a, 0xdf, 0xcf, 0x86, 0x10, 0x11, 0x14, + 0x14, 0x2c, 0xa7, 0xaa, 0x26, 0x1d, 0x37, 0xd1, 0x27, 0x85, 0xa8, 0xca, + 0xff, 0x50, 0xff, 0x93, 0x5f, 0x40, 0x5a, 0x4a, 0x02, 0xe5, 0xe7, 0x4b, + 0x56, 0x50, 0x5b, 0x1a, 0x95, 0xbc, 0xa8, 0x91, 0x46, 0x4e, 0x3a, 0xb3, + 0x76, 0xeb, 0xc4, 0x8e, 0x2c, 0x03, 0xe0, 0x4f, 0x3a, 0x01, 0xf5, 0xdf, + 0x84, 0xe9, 0xc8, 0x67, 0xc4, 0x6c, 0x65, 0x09, 0x22, 0x71, 0xf7, 0x6b, + 0xf7, 0x6e, 0xe2, 0xeb, 0xcf, 0x02, 0xd2, 0x3c, 0xac, 0xb1, 0x8b, 0xde, + 0x8e, 0x84, 0xaf, 0x74, 0x52, 0xb1, 0x52, 0xec, 0xab, 0x8d, 0xe3, 0xb0, + 0xd3, 0x25, 0x9b, 0x22, 0x63, 0x39, 0xae, 0x4b, 0xf9, 0x4b, 0x53, 0x2e, + 0x5e, 0xb6, 0xa5, 0xc8, 0x09, 0x03, 0x2e, 0xeb, 0x1e, 0x50, 0xf0, 0xd8, + 0x92, 0x75, 0x3e, 0xf2, 0xb7, 0x24, 0x10, 0x5e, 0xcb, 0x8c, 0x08, 0x1f, + 0x07, 0x5f, 0x2a, 0xef, 0x18, 0xec, 0x7a, 0x6d, 0x38, 0x12, 0x4a, 0xa5, + 0x8e, 0x3d, 0xc6, 0x64, 0x80, 0xfc, 0x2c, 0x2b, 0xb7, 0x08, 0xc0, 0x88, + 0x15, 0xe9, 0xe8, 0x42, 0xbc, 0xbb, 0x40, 0x3f, 0x90, 0xee, 0xa8, 0x98, + 0xda, 0x24, 0x39, 0x4c, 0x1a, 0xe2, 0x85, 0x3b, 0x03, 0x2c, 0x4e, 0x18, + 0xe8, 0xff, 0x37, 0x81, 0xd6, 0x83, 0xce, 0x0a, 0xf1, 0x80, 0xb1, 0x8c, + 0x38, 0x1e, 0xbf, 0x3f, 0xb9, 0x39, 0x43, 0xf4, 0xcc, 0x1b, 0x9f, 0x97, + 0x6b, 0x15, 0xe4, 0x0f, 0x71, 0x66, 0x10, 0xda, 0xe9, 0x2d, 0x27, 0x77, + 0x00, 0x3f, 0x1a, 0xd5, 0xba, 0x3d, 0x39, 0x72, 0xc1, 0xa5, 0xb2, 0xc6, + 0x1f, 0xba, 0x72, 0x07, 0xd4, 0x09, 0xb4, 0x8f, 0x61, 0x04, 0x18, 0xc4, + 0x31, 0xbc, 0x92, 0xce, 0x5d, 0xf1, 0x7a, 0x53, 0xa1, 0xdc, 0xd9, 0xf6, + 0x3b, 0x59, 0xe6, 0xb7, 0x56, 0xcc, 0x98, 0x54, 0xbf, 0x56, 0xf8, 0x78, + 0x4e, 0xf7, 0x48, 0xc4, 0x65, 0x8a, 0xe9, 0x97, 0x83, 0xe0, 0x58, 0xd1, + 0x70, 0x3b, 0xa6, 0x73, 0x1b, 0x00, 0x9a, 0xe9, 0xaa, 0x13, 0x95, 0x83, + 0x85, 0xa8, 0x76, 0x44, 0xcb, 0xbc, 0x9c, 0xce, 0xa8, 0xa8, 0x02, 0x4b, + 0x63, 0x21, 0x56, 0x18, 0x61, 0x3d, 0x5b, 0xad, 0x77, 0x31, 0xc1, 0x68, + 0x10, 0x50, 0x01, 0x80, 0x1e, 0x13, 0x0f, 0x68, 0xae, 0xe2, 0x6a, 0xca, + 0x88, 0x0a, 0xdf, 0x36, 0xfe, 0x96, 0x35, 0x43, 0xc3, 0xfb, 0x9d, 0xdc, + 0x16, 0x7f, 0x28, 0x44, 0xdc, 0xb4, 0x29, 0x13, 0x99, 0xf2, 0xea, 0x1e, + 0x52, 0xcc, 0x2b, 0x25, 0xb1, 0xd4, 0xf0, 0x61, 0x59, 0x1d, 0xc3, 0x22, + 0x0a, 0x2a, 0x92, 0x8b, 0x7b, 0xbf, 0xf6, 0x52, 0x41, 0x10, 0x54, 0x13, + 0xcf, 0x14, 0xf5, 0xea, 0x08, 0x35, 0x35, 0x8e, 0xb4, 0xa3, 0x4e, 0x6d, + 0x3e, 0x9e, 0xc3, 0x2d, 0xff, 0xbc, 0xa3, 0x44, 0xf1, 0xa0, 0xa1, 0x5d, + 0x7d, 0xfe, 0x8a, 0x15, 0xa0, 0xa3, 0x8d, 0x8d, 0xbe, 0x0f, 0x3d, 0x19, + 0x70, 0x9e, 0x7c, 0xbd, 0x63, 0xe7, 0x9f, 0xa9, 0xbb, 0x63, 0x0e, 0x6f, + 0x20, 0x78, 0xac, 0xf9, 0x37, 0x46, 0x39, 0x5e, 0x69, 0xe7, 0xa3, 0xf9, + 0xca, 0xdd, 0xde, 0x10, 0x8a, 0x39, 0xdd, 0x92, 0xb7, 0xf9, 0x17, 0x46, + 0x18, 0xed, 0x84, 0x20, 0xb8, 0x93, 0x36, 0x6e, 0x69, 0x35, 0x0c, 0x3c, + 0x37, 0xf9, 0x65, 0x59, 0x54, 0x2d, 0x70, 0x5b, 0x6f, 0xde, 0xa2, 0x1b, + 0x80, 0x2f, 0xfa, 0x8c, 0x22, 0xb0, 0x66, 0x7c, 0x39, 0xf7, 0x06, 0xcd, + 0xe3, 0x89, 0xa8, 0xc1, 0xe0, 0xc4, 0xdc, 0x79, 0x7d, 0xa0, 0x5d, 0x26, + 0x7c, 0xa8, 0x71, 0x48, 0xc2, 0x43, 0x28, 0x2e, 0xab, 0x02, 0x3a, 0xad, + 0x37, 0xb1, 0xb8, 0x17, 0xa4, 0xaa, 0xdc, 0x20, 0x38, 0x39, 0xb0, 0x82, + 0x80, 0x58, 0x80, 0xd2, 0x2f, 0x10, 0xd5, 0x8b, 0x59, 0xbd, 0x25, 0x7c, + 0x41, 0x5b, 0x0a, 0xdc, 0xde, 0x38, 0xc2, 0x62, 0xdc, 0x77, 0xa3, 0x79, + 0x0d, 0x94, 0xa3, 0x10, 0x4a, 0x39, 0xfb, 0x1b, 0xa7, 0x7f, 0x8c, 0x96, + 0x18, 0xd0, 0x0c, 0x2a, 0x23, 0x78, 0xa9, 0x4d, 0x88, 0x33, 0x58, 0xc8, + 0xfc, 0x25, 0x13, 0x8b, 0x0f, 0x11, 0xf2, 0x83, 0x76, 0xb8, 0x4e, 0xad, + 0x4d, 0x53, 0xdd, 0xbe, 0xed, 0xc8, 0xc2, 0xea, 0x51, 0x7b, 0xd8, 0x38, + 0x73, 0x3a, 0x34, 0x40, 0x38, 0x99, 0x51, 0x7b, 0xa8, 0x05, 0xf0, 0xb6, + 0x70, 0xce, 0x3d, 0xd6, 0x9a, 0x99, 0x1d, 0x3c, 0x96, 0xe6, 0x2f, 0x51, + 0x16, 0xe0, 0xf4, 0xca, 0x1d, 0x39, 0x30, 0xd9, 0x43, 0x33, 0x8d, 0xe1, + 0x47, 0x2e, 0xfc, 0x69, 0xa2, 0x49, 0x8b, 0xb3, 0xb4, 0x86, 0x6d, 0x35, + 0x25, 0xe7, 0xbb, 0xbe, 0x5c, 0x8e, 0x24, 0x18, 0xfd, 0xbe, 0x0a, 0x8b, + 0x7b, 0x59, 0x65, 0x1b, 0x79, 0xf8, 0x9f, 0x31, 0xb8, 0x69, 0xb2, 0x7d, + 0xce, 0x5c, 0x87, 0x0d, 0x76, 0x7a, 0xa7, 0xe8, 0x5a, 0xa8, 0x78, 0x53, + 0x0b, 0xfd, 0x81, 0x5d, 0x97, 0x2a, 0x20, 0xfd, 0xf0, 0x14, 0x2b, 0xd8, + 0x38, 0x09, 0xf9, 0x22, 0xd4, 0x56, 0x3d, 0xca, 0xd6, 0x01, 0x1a, 0x3b, + 0x50, 0x3f, 0x3e, 0x3d, 0x2c, 0x5a, 0x24, 0xd7, 0x2f, 0x60, 0x1e, 0x70, + 0xd5, 0xb6, 0x9f, 0x7e, 0x6e, 0x0f, 0x27, 0x9d, 0x53, 0x72, 0xc6, 0xb1, + 0xf8, 0x6c, 0xb1, 0x0f, 0xd2, 0x53, 0x6f, 0x6b, 0x2f, 0x27, 0x4d, 0x44, + 0x8f, 0xb0, 0x3b, 0xf6, 0xa5, 0xa2, 0xf1, 0x54, 0x9b, 0xad, 0xaf, 0xb6, + 0x01, 0x55, 0x6a, 0xb1, 0x0c, 0xcd, 0x86, 0x53, 0x03, 0x63, 0xaa, 0xa6, + 0x94, 0x3b, 0xb7, 0xc6, 0xb8, 0xde, 0x5e, 0xed, 0xbb, 0x1f, 0x20, 0x86, + 0x6e, 0xd2, 0x36, 0xb1, 0x98, 0x75, 0xeb, 0xde, 0xf6, 0x26, 0x90, 0xbd, + 0x0a, 0xe2, 0x21, 0x23, 0x14, 0x95, 0x0f, 0xfa, 0xf5, 0x8a, 0x5b, 0xaa, + 0x00, 0x51, 0x84, 0x1e, 0x12, 0x02, 0x62, 0x8c, 0x87, 0x40, 0x13, 0x1b, + 0x72, 0x49, 0x1a, 0x07, 0xa0, 0x73, 0xef, 0xc5, 0xfb, 0x44, 0x67, 0xb7, + 0x30, 0x8f, 0x2f, 0xd2, 0x22, 0xfa, 0x07, 0xff, 0x8d, 0xad, 0xd2, 0x6b, + 0x79, 0xf3, 0xdb, 0xcb, 0xc4, 0xf4, 0x19, 0x8a, 0xa8, 0xfe, 0x13, 0x09, + 0xc1, 0xcd, 0x2f, 0x0c, 0x69, 0xb5, 0xf6, 0x2f, 0x8d, 0xb6, 0x6a, 0xb0, + 0x9e, 0x7a, 0xa7, 0x4a, 0x84, 0x53, 0x7d, 0x1c, 0xc9, 0x32, 0x2f, 0xe2, + 0x81, 0x59, 0xe8, 0xf9, 0x53, 0x1e, 0xc8, 0xb4, 0xd8, 0x10, 0xca, 0x23, + 0xc9, 0x3a, 0x02, 0x04, 0xa1, 0x39, 0xc2, 0xa9, 0x30, 0x69, 0x92, 0x0c, + 0x56, 0x8e, 0x48, 0x59, 0x51, 0x9a, 0x25, 0x77, 0x00, 0x15, 0xbe, 0x78, + 0x33, 0x1b, 0x8b, 0x1b, 0xca, 0x78, 0xe1, 0x35, 0x2e, 0x33, 0xa2, 0x75, + 0x72, 0x18, 0x9c, 0x2f, 0xe7, 0xad, 0x1a, 0x3b, 0x23, 0x05, 0xa2, 0xa9, + 0x06, 0x90, 0x89, 0xa9, 0x7d, 0xe2, 0x6f, 0x44, 0xc7, 0x08, 0x91, 0x88, + 0xbe, 0xce, 0x4b, 0xdd, 0xab, 0x25, 0x5d, 0x4c, 0xbb, 0x13, 0x26, 0x73, + 0xc0, 0x95, 0x1d, 0xd1, 0x16, 0x68, 0x00, 0x16, 0x83, 0x65, 0x97, 0xc6, + 0xf3, 0x05, 0x35, 0xf0, 0xcc, 0x01, 0x36, 0xe1, 0x5b, 0xd5, 0xc0, 0x0b, + 0x8b, 0x5b, 0x1f, 0x83, 0x86, 0xda, 0x1e, 0x7d, 0x0c, 0xbf, 0xc9, 0xf1, + 0xa0, 0x21, 0x04, 0xd2, 0x0a, 0x12, 0xce, 0x11, 0x13, 0x73, 0x66, 0x46, + 0x41, 0x2d, 0x1c, 0x93, 0x71, 0xe7, 0x06, 0x67, 0x00, 0x49, 0xcc, 0x57, + 0x86, 0xf8, 0x35, 0x47, 0xe6, 0xe4, 0x0e, 0x78, 0x58, 0xec, 0x52, 0xa0, + 0x2b, 0x2a, 0xbe, 0x4e, 0xd6, 0x6f, 0x3a, 0x19, 0xed, 0x21, 0x78, 0x3b, + 0x27, 0x57, 0x42, 0x0a, 0x3c, 0x01, 0x14, 0x65, 0xb6, 0x61, 0xcf, 0x82, + 0x8a, 0xa7, 0x29, 0x29, 0x53, 0x0d, 0x90, 0x99, 0x3c, 0xf2, 0xc6, 0x10, + 0xb5, 0x5a, 0x86, 0x20, 0x34, 0xfb, 0x70, 0x6d, 0xcc, 0xeb, 0x36, 0xa1, + 0x84, 0xf7, 0x9f, 0x75, 0x1e, 0xb1, 0x7b, 0xe6, 0x99, 0xd0, 0x62, 0xb2, + 0xc3, 0x68, 0x18, 0xcf, 0xba, 0xcb, 0xf2, 0x83, 0x5b, 0x36, 0x7d, 0xc6, + 0x59, 0x09, 0x85, 0xad, 0x52, 0x47, 0x59, 0x28, 0x2b, 0x90, 0x8c, 0x71, + 0x0f, 0x34, 0x80, 0xc4, 0x3a, 0xb4, 0x5f, 0x50, 0xb3, 0x08, 0x47, 0x5f, + 0x81, 0x52, 0x71, 0x78, 0x5e, 0xba, 0x5b, 0x54, 0xc5, 0xa3, 0x9d, 0x55, + 0xb7, 0x86, 0x4e, 0x84, 0x6d, 0x34, 0xdf, 0x29, 0x15, 0x3a, 0x38, 0x77, + 0xab, 0xc4, 0x8e, 0xa2, 0x55, 0xed, 0x2b, 0x83, 0xe9, 0xaf, 0xe3, 0xe3, + 0xc2, 0xc2, 0x46, 0x80, 0xad, 0x54, 0x09, 0x4f, 0x1c, 0x71, 0xa4, 0xf0, + 0x91, 0x01, 0x41, 0x6d, 0xea, 0x01, 0xc8, 0xef, 0xfc, 0x85, 0x1e, 0xff, + 0xef, 0xfc, 0xc6, 0x67, 0x8b, 0x67, 0xce, 0x67, 0xc9, 0x74, 0x3c, 0xd5, + 0x17, 0xc4, 0xd4, 0x67, 0x5f, 0x30, 0xe9, 0x71, 0xa0, 0xd3, 0x13, 0x9b, + 0x65, 0x90, 0x3b, 0xec, 0xac, 0x69, 0xa3, 0x50, 0xa8, 0x6e, 0xc2, 0xdc, + 0x12, 0x21, 0x7e, 0x71, 0xd0, 0xef, 0xaf, 0xec, 0x6d, 0x69, 0x48, 0xed, + 0x6f, 0x8b, 0x87, 0x4b, 0x63, 0x12, 0x30, 0x96, 0x51, 0x12, 0xb8, 0xf8, + 0x27, 0xdc, 0x92, 0x19, 0xaf, 0xc7, 0x70, 0x81, 0xf4, 0xb3, 0xa3, 0xeb, + 0xf5, 0x5f, 0x8a, 0x36, 0x31, 0xe4, 0x08, 0x2d, 0x2f, 0x30, 0x6c, 0xad, + 0x57, 0xb8, 0x1a, 0x47, 0x9e, 0x67, 0x2f, 0x89, 0xb0, 0x93, 0x6e, 0x9f, + 0xeb, 0x62, 0xe5, 0xcc, 0x55, 0xcc, 0xf1, 0x81, 0xcc, 0x94, 0xae, 0x7b, + 0x92, 0x96, 0xb9, 0x5a, 0x85, 0x0b, 0x1f, 0x85, 0xc5, 0x15, 0x0e, 0xff, + 0xbf, 0xd8, 0x18, 0xf3, 0x19, 0xe4, 0x5d, 0x7f, 0xb3, 0xb4, 0x38, 0x84, + 0x99, 0x51, 0xd8, 0x1e, 0x1c, 0x64, 0xbf, 0x01, 0x4d, 0x47, 0x7f, 0x1e, + 0x15, 0xa6, 0x4d, 0xf2, 0xc3, 0xdc, 0xfd, 0xe9, 0x1b, 0x82, 0xd3, 0xb4, + 0xa3, 0x8a, 0x4a, 0x86, 0x7c, 0xe9, 0xa1, 0x17, 0x60, 0xdf, 0x03, 0x83, + 0x34, 0xe9, 0xf0, 0x14, 0xd4, 0x18, 0x0b, 0x32, 0x5a, 0xe6, 0x58, 0xd8, + 0x12, 0xbc, 0xfb, 0xf8, 0x26, 0x01, 0x87, 0x04, 0xcc, 0xc2, 0x8f, 0xa8, + 0x05, 0x3c, 0x12, 0xdb, 0x55, 0xe1, 0x7b, 0x47, 0x8a, 0x41, 0x9b, 0xd4, + 0xf0, 0x33, 0xd5, 0xb7, 0x09, 0xff, 0x28, 0x35, 0x33, 0x34, 0x53, 0xfb, + 0x51, 0x72, 0xc3, 0x77, 0xa5, 0x02, 0xe0, 0x78, 0xc2, 0x4d, 0x0a, 0x10, + 0xbc, 0xf5, 0x80, 0x0b, 0xdb, 0x0c, 0x79, 0x0b, 0xd5, 0xad, 0x1e, 0x3a, + 0x09, 0x5e, 0x17, 0x90, 0x71, 0x70, 0x75, 0x14, 0xa4, 0xd6, 0xdb, 0x5a, + 0x54, 0x5d, 0x84, 0x1a, 0xc6, 0xf0, 0xc6, 0x60, 0xb0, 0x43, 0x71, 0x25, + 0xfe, 0xaf, 0xbc, 0x01, 0xe3, 0x86, 0x56, 0x42, 0x15, 0x6f, 0x27, 0xfd, + 0x28, 0xcb, 0xcb, 0xbb, 0xca, 0x5a, 0xc7, 0x68, 0xb1, 0xd1, 0x53, 0x62, + 0xda, 0x15, 0x01, 0xea, 0x6e, 0x08, 0x8f, 0x3d, 0xcf, 0x9e, 0x2d, 0x88, + 0xd7, 0x0e, 0xb0, 0x13, 0x2b, 0x5b, 0xd3, 0x3f, 0x58, 0x92, 0x5d, 0xb8, + 0xb1, 0x24, 0xeb, 0xa4, 0xaf, 0x71, 0xac, 0x86, 0x1d, 0x20, 0x8f, 0x12, + 0x72, 0x54, 0xac, 0xf5, 0x88, 0x5c, 0xbe, 0xa2, 0xc9, 0xe6, 0x0b, 0x46, + 0xf5, 0x45, 0xd5, 0x2e, 0xc4, 0xc1, 0x8d, 0x4f, 0x2b, 0x7a, 0xc0, 0x0e, + 0xd8, 0xf4, 0x2b, 0x59, 0x64, 0x85, 0xa3, 0x2a, 0xbc, 0xa9, 0xce, 0x79, + 0x68, 0xc8, 0xc1, 0xf1, 0x6e, 0x8d, 0xcb, 0x2a, 0xbf, 0xa8, 0xaf, 0x15, + 0x11, 0x9f, 0x1d, 0xc7, 0xc1, 0x4e, 0x8a, 0x39, 0xfe, 0xb3, 0x5d, 0x6b, + 0x8b, 0xf7, 0x11, 0x99, 0xc0, 0x7a, 0x62, 0x94, 0xd8, 0xbb, 0xe4, 0xe9, + 0xa0, 0xb7, 0x5d, 0x96, 0x3b, 0x48, 0xae, 0x94, 0x4b, 0x2e, 0xba, 0x63, + 0xbc, 0x68, 0x5a, 0xe4, 0xa6, 0x67, 0x0b, 0x7b, 0x0f, 0x60, 0x0f, 0x67, + 0x66, 0x83, 0xb4, 0xab, 0xc9, 0xb4, 0xe4, 0x9a, 0xaf, 0xaf, 0x6d, 0x2c, + 0xa4, 0x0a, 0x07, 0x4b, 0x42, 0xdf, 0x27, 0x9f, 0x52, 0xa9, 0x36, 0x8d, + 0x36, 0x29, 0x7b, 0xb5, 0x1c, 0x4b, 0x8b, 0xae, 0xc4, 0x0a, 0x5e, 0xf7, + 0xd3, 0x82, 0x39, 0x4e, 0x5e, 0x00, 0xc7, 0x05, 0xf2, 0x8c, 0x28, 0x8e, + 0xd2, 0x0e, 0xef, 0x9d, 0xb3, 0x7a, 0x61, 0xdd, 0x43, 0x23, 0xad, 0x6f, + 0x3e, 0xd5, 0x5e, 0x02, 0xc7, 0x99, 0x70, 0x74, 0x30, 0xd5, 0x81, 0x82, + 0xa6, 0x0a, 0x93, 0x77, 0xe7, 0x90, 0x91, 0x06, 0xe7, 0x18, 0x00, 0xcf, + 0x55, 0x1a, 0x6c, 0x5f, 0xa4, 0x4d, 0x35, 0xb9, 0xdc, 0xc2, 0x26, 0x9d, + 0x6c, 0x9d, 0x3c, 0xf4, 0x96, 0x80, 0x00, 0x80, 0x25, 0xf8, 0x12, 0x2c, + 0x3c, 0x39, 0x5d, 0xdb, 0x22, 0x3a, 0x97, 0x3c, 0xc5, 0xea, 0xa5, 0x7d, + 0x36, 0x3d, 0x17, 0x58, 0x56, 0xfe, 0x3c, 0xfd, 0x21, 0xfe, 0xda, 0x35, + 0x4d, 0x3d, 0xc3, 0x97, 0xe9, 0xbf, 0x94, 0xdf, 0x7a, 0xee, 0x44, 0x12, + 0x1b, 0x54, 0x00, 0x77, 0x17, 0xad, 0xc9, 0xd8, 0xce, 0x56, 0x63, 0x1a, + 0xaa, 0xfb, 0x15, 0x0f, 0x2d, 0xbe, 0xc8, 0x7d, 0xea, 0x33, 0x23, 0x17, + 0x03, 0x19, 0x6e, 0x4a, 0x9b, 0x5a, 0xe0, 0x4e, 0x5d, 0x38, 0xba, 0x7e, + 0xe5, 0x8c, 0x40, 0xfa, 0xb0, 0x9f, 0x77, 0xa5, 0xee, 0x66, 0x61, 0x4c, + 0x51, 0x1f, 0x8e, 0x51, 0x70, 0x15, 0x66, 0x80, 0xc3, 0xb9, 0x6c, 0xa2, + 0x90, 0x8e, 0x66, 0x55, 0xd0, 0xc7, 0x10, 0x38, 0x3b, 0x28, 0x0b, 0xdc, + 0xed, 0x02, 0x10, 0x55, 0x32, 0xb0, 0xfa, 0xd9, 0x0f, 0x19, 0xaf, 0xfb, + 0x88, 0x82, 0x9b, 0xc4, 0xa4, 0xee, 0x0f, 0x91, 0xd5, 0x04, 0xa6, 0x37, + 0xa3, 0xd4, 0xb6, 0xf9, 0x97, 0xb7, 0xca, 0xa4, 0x10, 0xb8, 0x46, 0x72, + 0xe6, 0x49, 0x86, 0xee, 0xfd, 0xd2, 0x47, 0xc2, 0x6e, 0x2d, 0x35, 0xd6, + 0x11, 0xd5, 0xd3, 0x3f, 0x70, 0x32, 0x52, 0xda, 0x28, 0xf7, 0x55, 0xd7, + 0x6e, 0xf0, 0x8a, 0x27, 0xb1, 0xce, 0x52, 0xe3, 0xcb, 0x37, 0x8c, 0xfb, + 0x6b, 0xb2, 0x98, 0xbd, 0x39, 0x72, 0x5e, 0xb6, 0x1b, 0x6c, 0xde, 0x25, + 0x25, 0x88, 0x7c, 0xbd, 0x1c, 0x86, 0x88, 0x90, 0x34, 0x6f, 0x35, 0x41, + 0x78, 0x69, 0x4c, 0x27, 0x37, 0x90, 0x97, 0x73, 0x3d, 0x2f, 0x95, 0x1d, + 0x38, 0x3d, 0x76, 0x0d, 0x58, 0x59, 0x90, 0x51, 0xb9, 0x0c, 0xd7, 0x6d, + 0x38, 0x71, 0xf4, 0x0f, 0x48, 0x2f, 0xec, 0xb0, 0xad, 0x39, 0x9a, 0x9b, + 0xd0, 0xad, 0x17, 0x76, 0x30, 0x0a, 0x2f, 0x16, 0xe0, 0x9c, 0x8a, 0xb3, + 0x3d, 0xa6, 0x64, 0x39, 0x91, 0xdc, 0x2b, 0x24, 0xf2, 0x70, 0x1c, 0x57, + 0x8b, 0x5f, 0xa7, 0x3c, 0x4a, 0x4a, 0xdf, 0x21, 0xde, 0x6f, 0xba, 0xc4, + 0xee, 0x14, 0x87, 0x8e, 0xea, 0xaf, 0x74, 0x02, 0x26, 0xb6, 0xde, 0x56, + 0x34, 0x3e, 0xf5, 0x3f, 0x7d, 0xb7, 0x90, 0xf3, 0x82, 0x7f, 0x79, 0x7f, + 0x9e, 0x8e, 0x58, 0x84, 0x4e, 0x0d, 0x16, 0x11, 0x62, 0xa1, 0x51, 0x72, + 0x1b, 0xf8, 0x28, 0xd9, 0x52, 0x93, 0x04, 0x75, 0x7d, 0x0b, 0x92, 0xa7, + 0x39, 0xd5, 0x4b, 0xba, 0x2b, 0x01, 0x72, 0xb0, 0xae, 0x53, 0x0a, 0xa1, + 0xa6, 0x9d, 0x00, 0x47, 0x65, 0x5e, 0x92, 0x9a, 0x69, 0xe8, 0xa1, 0x8f, + 0x42, 0x9f, 0x62, 0x91, 0x46, 0x28, 0x15, 0x00, 0x08, 0x22, 0xd5, 0x65, + 0x1d, 0x27, 0x87, 0x9f, 0x99, 0x9c, 0x4b, 0xad, 0x6d, 0xe0, 0x68, 0xdb, + 0x84, 0xd2, 0x5e, 0xe6, 0x47, 0x97, 0xf9, 0xf7, 0x2e, 0xbc, 0x8c, 0x58, + 0x78, 0x5b, 0xd2, 0x44, 0xfd, 0x46, 0xbf, 0x31, 0xfe, 0xfb, 0xff, 0x09, + 0x1e, 0x08, 0x05, 0xb5, 0x4f, 0xac, 0x5d, 0x0b, 0x1f, 0x5f, 0xad, 0x95, + 0x3f, 0x86, 0x31, 0xcc, 0x3e, 0x8b, 0x04, 0x5f, 0x9a, 0x0a, 0xfc, 0xfe, + 0xdd, 0x9f, 0x0d, 0x9b, 0x06, 0x4f, 0x0e, 0x3b, 0xc3, 0x2b, 0xf4, 0xfb, + 0xa1, 0xc4, 0xdd, 0xc7, 0x5a, 0x05, 0xe8, 0xcb, 0xcc, 0x7a, 0x24, 0xb8, + 0xe4, 0xfe, 0xa0, 0x60, 0x88, 0x05, 0x9e, 0x9a, 0x28, 0x8d, 0x8e, 0x02, + 0x63, 0x69, 0xd5, 0x91, 0xb0, 0x7f, 0x84, 0x0a, 0xf3, 0xe9, 0x3d, 0x70, + 0xa1, 0x6e, 0x9a, 0xeb, 0xea, 0x62, 0x01, 0xa7, 0x7c, 0x76, 0x59, 0x49, + 0x31, 0x89, 0x10, 0x75, 0x87, 0x51, 0x61, 0xe6, 0xee, 0x50, 0x40, 0xf4, + 0x61, 0xe5, 0x48, 0x7d, 0x38, 0x1e, 0xf5, 0xf4, 0xf6, 0x58, 0x50, 0x40, + 0x2c, 0xca, 0x24, 0xc3, 0x46, 0x19, 0x3c, 0x39, 0x0e, 0xd8, 0x59, 0xe1, + 0x16, 0xc6, 0xac, 0xd0, 0xbc, 0x6c, 0x28, 0xd0, 0x18, 0xfe, 0xd7, 0x95, + 0x5f, 0x3f, 0xaa, 0xeb, 0x4a, 0x29, 0x77, 0xc6, 0xf8, 0xbf, 0x4a, 0x39, + 0x65, 0xa9, 0x00, 0x3a, 0x68, 0x81, 0xb5, 0x1e, 0xd3, 0x3d, 0x99, 0xd7, + 0x4d, 0x3f, 0x16, 0xfe, 0xb4, 0x82, 0xd7, 0xe9, 0x50, 0xb2, 0x7d, 0x0b, + 0x28, 0x16, 0xaa, 0x4f, 0x63, 0xd7, 0x0e, 0x6c, 0x35, 0x63, 0xb2, 0xdc, + 0x24, 0xf2, 0x25, 0x79, 0xea, 0xbe, 0x5d, 0xc1, 0xb3, 0x4f, 0xc4, 0x64, + 0x47, 0x9f, 0xea, 0xb1, 0xc8, 0x96, 0x15, 0x3e, 0xb0, 0xba, 0x09, 0xbb, + 0x52, 0xc5, 0xa2, 0x16, 0xff, 0xa3, 0x8f, 0xbf, 0xfe, 0x4d, 0x4f, 0x6d, + 0x80, 0x45, 0xdd, 0x34, 0x12, 0x15, 0x41, 0x32, 0x75, 0x6c, 0xfb, 0x74, + 0x1d, 0x24, 0xfe, 0xc7, 0x7d, 0x6c, 0x78, 0xc9, 0xb9, 0x7b, 0xb0, 0x32, + 0xf2, 0xc1, 0x28, 0xf5, 0x65, 0xed, 0x5e, 0x78, 0x59, 0xdf, 0x2e, 0xc2, + 0x5e, 0x92, 0xe6, 0x99, 0x54, 0x27, 0x78, 0x97, 0x51, 0x9e, 0x24, 0x23, + 0xad, 0x0d, 0x0d, 0xce, 0x15, 0x2e, 0x44, 0x84, 0xd4, 0x2b, 0xf5, 0xee, + 0x85, 0xb9, 0xd0, 0xba, 0xd3, 0x4e, 0x86, 0x4c, 0xc0, 0xc9, 0x16, 0xbe, + 0x62, 0x6b, 0x4b, 0xae, 0xc1, 0x4e, 0x50, 0x0f, 0x41, 0x96, 0xd5, 0x01, + 0x71, 0xcd, 0x8d, 0x40, 0xa6, 0x7b, 0xca, 0xb2, 0x55, 0x33, 0x4b, 0x4d, + 0x8b, 0x89, 0x9c, 0x38, 0x3d, 0x13, 0xa5, 0x31, 0x3e, 0x44, 0xf3, 0x41, + 0xc4, 0x1b, 0x6c, 0x48, 0x59, 0x84, 0x33, 0x1e, 0x27, 0xbd, 0xfe, 0x3c, + 0x71, 0x5a, 0x6b, 0x20, 0x3e, 0x0b, 0xf4, 0x67, 0x09, 0x13, 0x1d, 0x03, + 0x0e, 0x2e, 0x8c, 0xd3, 0x13, 0xcc, 0x0a, 0x6b, 0x34, 0xe4, 0xe8, 0x36, + 0x90, 0xcf, 0x05, 0x5f, 0x80, 0x14, 0x6d, 0x74, 0xac, 0x02, 0x8c, 0xa7, + 0xc5, 0x9a, 0xcd, 0xc1, 0xe3, 0x98, 0x55, 0x67, 0x95, 0xb6, 0xe3, 0xb4, + 0x87, 0x1a, 0x99, 0xaa, 0x40, 0x34, 0xe6, 0xf6, 0xe6, 0xd8, 0xa3, 0xdd, + 0xc8, 0x84, 0x75, 0xc4, 0x07, 0x14, 0x53, 0xf5, 0xba, 0x69, 0xb6, 0x03, + 0x2b, 0xc1, 0x8e, 0x6f, 0xa3, 0x7f, 0x5d, 0x17, 0xec, 0x87, 0xed, 0xbb, + 0x64, 0xfb, 0xaf, 0x81, 0xf2, 0x27, 0xa7, 0x3a, 0x54, 0x93, 0x1d, 0x94, + 0xbc, 0xd6, 0x03, 0xa6, 0x30, 0x71, 0x8e, 0x94, 0xfc, 0x95, 0xca, 0xb2, + 0x73, 0x0f, 0xda, 0x1c, 0xff, 0x82, 0xdb, 0x93, 0x14, 0x48, 0xc6, 0xa1, + 0xb1, 0xb4, 0xaf, 0x84, 0xc8, 0xbe, 0x3a, 0x7b, 0xf6, 0x2f, 0xb0, 0x4d, + 0x1f, 0x46, 0xb4, 0x68, 0x7f, 0xc4, 0x0d, 0x1a, 0xcc, 0xd7, 0xff, 0x66, + 0x2d, 0xfe, 0x7c, 0xb2, 0x3c, 0x74, 0x15, 0xe1, 0x26, 0xbf, 0x90, 0x99, + 0x00, 0xb1, 0x51, 0xc6, 0x7e, 0x0b, 0xec, 0x25, 0x4f, 0x06, 0xa4, 0xa1, + 0x42, 0x8c, 0x02, 0xf0, 0xeb, 0xd5, 0x13, 0xdd, 0xe2, 0xcd, 0xc8, 0x69, + 0x80, 0x4e, 0xdb, 0xf6, 0x42, 0xe0, 0x7b, 0x40, 0xa4, 0x2c, 0xdf, 0x57, + 0xc6, 0x6c, 0xcf, 0x53, 0x0a, 0x1d, 0x5d, 0x97, 0xe3, 0x51, 0x2f, 0x16, + 0x0a, 0x7c, 0x04, 0xa5, 0x52, 0xbc, 0x46, 0x6c, 0x98, 0x8a, 0x06, 0x37, + 0x55, 0xd7, 0x3c, 0xf9, 0xf3, 0xb4, 0xca, 0xc8, 0x63, 0xb2, 0x31, 0xa0, + 0x5f, 0x94, 0xb3, 0x42, 0x00, 0xcd, 0x0e, 0xda, 0xd3, 0xa0, 0x2c, 0xc7, + 0xa5, 0xd3, 0x68, 0xb1, 0xe4, 0x58, 0x5a, 0x44, 0x65, 0x4a, 0xed, 0x73, + 0xbf, 0x5a, 0x2a, 0x7c, 0x20, 0xda, 0x9d, 0x90, 0x3c, 0x32, 0x1b, 0x7b, + 0x1e, 0xd6, 0x78, 0x74, 0x3b, 0x8e, 0x36, 0x78, 0x9c, 0x30, 0x38, 0x1a, + 0x60, 0xac, 0x88, 0xf0, 0xf9, 0x2b, 0xf0, 0xa7, 0xf1, 0x51, 0xb4, 0xa9, + 0x2f, 0x36, 0xdb, 0x4d, 0xa5, 0xee, 0x8b, 0xaa, 0x96, 0xa9, 0x50, 0x37, + 0xb8, 0x4c, 0xb3, 0x7b, 0x49, 0x8c, 0x04, 0x39, 0x98, 0x08, 0xb7, 0xb1, + 0x40, 0xcb, 0x9a, 0x77, 0x7b, 0x45, 0x12, 0x91, 0x2e, 0xf6, 0xea, 0x16, + 0x4d, 0xb6, 0x65, 0x71, 0x3b, 0x3e, 0xf9, 0x61, 0xe6, 0x71, 0x77, 0xde, + 0x67, 0x8b, 0x91, 0x2a, 0x37, 0x30, 0x5f, 0xf4, 0x5f, 0x6e, 0xc5, 0x49, + 0x53, 0x75, 0x90, 0x9c, 0x97, 0x3a, 0xe1, 0x33, 0xd5, 0x85, 0xaa, 0x46, + 0xc7, 0x88, 0xc6, 0x7a, 0x04, 0x95, 0x06, 0x29, 0x35, 0x74, 0xa0, 0x51, + 0x4e, 0xee, 0x78, 0xf3, 0x16, 0xe8, 0x94, 0x16, 0xf5, 0x2b, 0x06, 0x05, + 0xc7, 0x0c, 0xc8, 0xa7, 0x71, 0xae, 0x55, 0xeb, 0x3c, 0x95, 0x9b, 0xbf, + 0xb6, 0xd3, 0x53, 0x37, 0x13, 0x56, 0x19, 0x95, 0x50, 0xa6, 0x9b, 0x88, + 0x62, 0xdb, 0x7b, 0x8d, 0x23, 0x29, 0x28, 0x2c, 0xd8, 0x1f, 0xdf, 0x4c, + 0xf9, 0x6e, 0xc8, 0xc3, 0x1f, 0x84, 0xa5, 0xf3, 0xa1, 0x3d, 0x87, 0x45, + 0xea, 0x6f, 0xdd, 0x64, 0xfc, 0xb4, 0xc2, 0x30, 0x68, 0x5d, 0x8a, 0xee, + 0xca, 0xfc, 0x26, 0x73, 0x3c, 0x25, 0x80, 0x11, 0xe8, 0x11, 0x8f, 0xfa, + 0xe9, 0x20, 0x6a, 0x35, 0x9d, 0x4a, 0x98, 0x94, 0x3a, 0xff, 0xf8, 0x89, + 0xa5, 0xba, 0xda, 0x52, 0x07, 0x99, 0x7d, 0x77, 0xbc, 0xa5, 0xf7, 0xf5, + 0x06, 0x8e, 0x6f, 0x22, 0xd2, 0x4f, 0x95, 0xb1, 0xa5, 0xf1, 0x1a, 0xbf, + 0x40, 0x70, 0x2d, 0xcf, 0xdd, 0xea, 0xdb, 0x56, 0x3b, 0xcf, 0xf4, 0x12, + 0x2b, 0x4e, 0x0e, 0xcb, 0x4c, 0xe2, 0x85, 0xf2, 0x4d, 0xdf, 0xd9, 0x35, + 0x46, 0x21, 0xb9, 0xda, 0x98, 0x69, 0xf9, 0x88, 0x67, 0xcd, 0x41, 0x01, + 0x5b, 0x36, 0x17, 0xb8, 0x3c, 0xdc, 0xa3, 0x1d, 0xb8, 0xba, 0x78, 0x84, + 0x38, 0x28, 0x71, 0x43, 0xbf, 0xdf, 0xc9, 0x97, 0x35, 0x32, 0x77, 0x41, + 0x42, 0xfd, 0xa3, 0xc1, 0x91, 0x54, 0xb3, 0x0a, 0x65, 0xa0, 0xea, 0x6c, + 0x9a, 0x01, 0xa7, 0x89, 0x8c, 0xdd, 0x9b, 0x91, 0x77, 0x98, 0x37, 0x7b, + 0xbd, 0x98, 0xdf, 0x70, 0x8f, 0x3f, 0xc8, 0x3d, 0x2e, 0xb4, 0x5b, 0x00, + 0x57, 0x4b, 0x6b, 0xed, 0x60, 0x79, 0x02, 0x95, 0x11, 0x97, 0x1f, 0xe7, + 0x4e, 0x5a, 0xf0, 0x14, 0x6a, 0x22, 0x6d, 0x6b, 0x64, 0xd1, 0xed, 0x2b, + 0x42, 0x99, 0x2d, 0xf3, 0x25, 0xb8, 0xea, 0x7a, 0x4c, 0x4b, 0xca, 0x64, + 0xc1, 0x39, 0x94, 0x42, 0xfb, 0x4c, 0xde, 0x5c, 0xf4, 0xe0, 0xba, 0x3c, + 0xcb, 0x1b, 0x0e, 0x7a, 0x05, 0xdc, 0x4a, 0x24, 0xe2, 0xe4, 0x72, 0xc1, + 0x42, 0x6b, 0x2c, 0xff, 0x0c, 0x9e, 0x75, 0x96, 0x41, 0x21, 0x61, 0x77, + 0xcf, 0x2d, 0x21, 0xbd, 0xa8, 0xd0, 0x76, 0xbd, 0x1b, 0xb5, 0x7e, 0xd3, + 0xc7, 0x79, 0x6a, 0x19, 0x24, 0xc0, 0x8f, 0x2c, 0x98, 0x0c, 0x7d, 0xdc, + 0x64, 0x0c, 0xae, 0x39, 0x33, 0x24, 0xb7, 0x8f, 0xa3, 0xa3, 0x18, 0xcf, + 0xba, 0x23, 0xcb, 0xfb, 0xd3, 0x89, 0x28, 0xa0, 0x08, 0xd8, 0x5a, 0x7e, + 0x90, 0xd4, 0x21, 0x45, 0xc6, 0xc2, 0xa5, 0x27, 0x1c, 0x86, 0x01, 0x3f, + 0x79, 0xb8, 0xf2, 0x83, 0xb2, 0x67, 0x40, 0xc4, 0xb6, 0x21, 0x14, 0xe9, + 0x5f, 0xa1, 0x9b, 0xd9, 0xc4, 0x26, 0x02, 0x36, 0xa7, 0x20, 0x6c, 0x24, + 0xa2, 0x84, 0x90, 0x99, 0xd2, 0x22, 0xc4, 0x1c, 0x7c, 0xca, 0x5d, 0xc4, + 0x7e, 0x1f, 0x31, 0x41, 0x70, 0xf5, 0x24, 0x36, 0x44, 0x2d, 0x6c, 0x98, + 0x62, 0xa2, 0xe5, 0x7c, 0x1f, 0xd8, 0xa2, 0x8a, 0xbe, 0xba, 0x9b, 0xab, + 0x31, 0x6d, 0xa6, 0xd8, 0x7e, 0xa9, 0xcd, 0xfb, 0xc5, 0xda, 0xe8, 0xc0, + 0x12, 0x0a, 0x52, 0xb1, 0xa1, 0x20, 0x0e, 0xb2, 0xd4, 0xae, 0x55, 0xf2, + 0x32, 0xfd, 0x18, 0x51, 0xe7, 0x97, 0x2c, 0x72, 0x66, 0x45, 0x2f, 0xad, + 0xf5, 0x48, 0xe9, 0xfc, 0x6a, 0x93, 0x5a, 0x9a, 0x8e, 0x8e, 0xa1, 0x2c, + 0xf0, 0x4c, 0xb8, 0x31, 0x8b, 0x48, 0x00, 0x1a, 0xb5, 0xf8, 0xa0, 0x00, + 0xe5, 0xef, 0xc1, 0xa4, 0x21, 0x3a, 0xf2, 0x09, 0xf4, 0x99, 0x5e, 0x68, + 0xe0, 0xa8, 0x3a, 0xf7, 0x3e, 0xd6, 0x24, 0xa8, 0xf8, 0xae, 0x99, 0x38, + 0x6b, 0xd5, 0x14, 0xff, 0xde, 0x34, 0x67, 0xbf, 0x06, 0xa7, 0x5b, 0x1b, + 0xc6, 0x3e, 0xaf, 0x0a, 0x04, 0x5b, 0xf6, 0x4d, 0x24, 0x3a, 0xae, 0x42, + 0xba, 0xc4, 0xbc, 0xe7, 0x72, 0x8c, 0xc0, 0xcf, 0x30, 0x12, 0x0a, 0x66, + 0xb2, 0x2a, 0x39, 0x9f, 0x8a, 0x9a, 0x58, 0x3a, 0xfa, 0xc2, 0xec, 0xe4, + 0x87, 0x33, 0xdc, 0x2f, 0x59, 0x3e, 0xeb, 0xe8, 0x1f, 0xb9, 0x71, 0x3d, + 0x92, 0x53, 0xf8, 0xc0, 0xd1, 0xbf, 0xce, 0xf4, 0x23, 0xb0, 0x35, 0x18, + 0x0d, 0xdf, 0xd2, 0x25, 0x20, 0x71, 0x18, 0x92, 0x9d, 0x68, 0xf6, 0xf1, + 0xad, 0x77, 0x8e, 0xf6, 0xa4, 0x3a, 0xec, 0xfe, 0xb1, 0xf5, 0x9f, 0xfa, + 0x2f, 0xb5, 0xc5, 0xe5, 0x7e, 0xdc, 0xe0, 0x6c, 0xe2, 0x03, 0xdc, 0x0a, + 0x03, 0xdd, 0xf1, 0x11, 0x24, 0xd9, 0xd0, 0x71, 0x39, 0x93, 0x7d, 0x9e, + 0xb5, 0x41, 0xae, 0x22, 0xc3, 0xe1, 0x90, 0x17, 0xd5, 0x00, 0x3c, 0x84, + 0xfe, 0x18, 0x18, 0xf3, 0x80, 0xee, 0x28, 0x0d, 0x9d, 0x58, 0xfe, 0x15, + 0x02, 0xf5, 0x78, 0x9b, 0x21, 0x2b, 0x1f, 0xa9, 0xcc, 0x30, 0xf8, 0xcf, + 0x7d, 0xff, 0x2c, 0xdb, 0x9c, 0xcb, 0x14, 0x5e, 0x54, 0x56, 0x21, 0xb5, + 0x0d, 0x30, 0x93, 0x8e, 0x1d, 0xe4, 0x21, 0x66, 0xab, 0xda, 0x45, 0xc9, + 0xe5, 0xb0, 0x89, 0x88, 0x86, 0xf6, 0x1d, 0xbe, 0x26, 0x59, 0x66, 0xd6, + 0x07, 0x56, 0xbf, 0x33, 0x13, 0x9d, 0x85, 0xb0, 0x14, 0x34, 0x70, 0xac, + 0xc9, 0x5d, 0x11, 0x3d, 0x01, 0xe7, 0xb1, 0x52, 0xb4, 0xbd, 0x77, 0xc7, + 0x12, 0x83, 0x56, 0x69, 0x00, 0x55, 0x35, 0xc1, 0x4b, 0xd2, 0x1e, 0x38, + 0x29, 0x2d, 0x08, 0x57, 0x0b, 0xd9, 0x2a, 0x5a, 0xc7, 0x9c, 0x71, 0x1c, + 0x90, 0xd3, 0x1c, 0xcf, 0x19, 0xf6, 0xe7, 0x31, 0xc4, 0xe0, 0x38, 0xa2, + 0x96, 0xfc, 0x9d, 0x70, 0x22, 0x78, 0x54, 0xd8, 0xca, 0x81, 0xc6, 0xec, + 0x4d, 0x48, 0xe3, 0x78, 0x21, 0xb0, 0xd1, 0xff, 0x60, 0x56, 0x40, 0xbe, + 0xbb, 0xf2, 0x50, 0xdc, 0x04, 0x9d, 0x2c, 0x9a, 0x54, 0x71, 0xab, 0xf5, + 0xc8, 0x18, 0xbb, 0x6b, 0x0a, 0xdb, 0xcf, 0xa4, 0x87, 0x1b, 0xe4, 0xdf, + 0xe8, 0x4e, 0x3e, 0xc4, 0x62, 0x80, 0x61, 0x61, 0x02, 0xa2, 0x89, 0xc5, + 0xfb, 0x4f, 0xcc, 0xa4, 0x5d, 0xbf, 0x22, 0x3e, 0xf9, 0x0e, 0xd2, 0x81, + 0xcb, 0xbb, 0x95, 0xe1, 0xfc, 0x69, 0x26, 0xc1, 0xbc, 0xde, 0x11, 0x19, + 0x2b, 0x9d, 0x1b, 0xe3, 0x8c, 0x75, 0xea, 0x70, 0xd9, 0x50, 0x85, 0xcd, + 0xa9, 0xea, 0x53, 0x84, 0x87, 0xde, 0x13, 0xd1, 0x24, 0xf9, 0x26, 0x42, + 0x92, 0x71, 0x06, 0xbc, 0x50, 0xcd, 0x11, 0x76, 0x3c, 0x6d, 0x16, 0xd1, + 0x15, 0x57, 0x48, 0x09, 0x1a, 0x02, 0xd7, 0x1e, 0xf2, 0x04, 0x38, 0x8d, + 0x3d, 0x90, 0x8b, 0xea, 0xda, 0xf2, 0x96, 0x80, 0x7d, 0x99, 0x3a, 0x0e, + 0xa9, 0x85, 0xda, 0x8b, 0x6c, 0xb5, 0x41, 0x4c, 0x19, 0x10, 0xa7, 0x43, + 0xe0, 0x26, 0x94, 0x9b, 0x32, 0xbc, 0x65, 0x6f, 0x9c, 0xa3, 0x2b, 0x4c, + 0xcd, 0xc3, 0x37, 0xdb, 0x68, 0x30, 0xfb, 0x1c, 0xe9, 0x28, 0xa8, 0x67, + 0x4d, 0x0f, 0xba, 0x25, 0xac, 0x43, 0x91, 0x28, 0xe0, 0x32, 0x9a, 0x41, + 0x76, 0xae, 0xcd, 0x8d, 0xf7, 0x24, 0x92, 0x06, 0x39, 0x12, 0x16, 0x93, + 0x2b, 0x50, 0xbe, 0x60, 0xd5, 0x17, 0xa9, 0xde, 0x4d, 0x0f, 0x7f, 0x03, + 0x46, 0x5f, 0x9b, 0x51, 0x66, 0xb3, 0x57, 0xc9, 0xf1, 0x80, 0xef, 0x27, + 0xa4, 0x48, 0x49, 0x0d, 0x96, 0xed, 0x39, 0x1c, 0x83, 0x8f, 0x40, 0x6f, + 0xcc, 0x74, 0x76, 0x01, 0x21, 0x8d, 0x76, 0x95, 0x0d, 0xbb, 0x03, 0x97, + 0xb7, 0x06, 0x34, 0x65, 0x29, 0x7a, 0xec, 0xfa, 0xb9, 0x18, 0x44, 0x8d, + 0xd8, 0xb5, 0x1e, 0x3d, 0xd8, 0x5f, 0x1c, 0x60, 0xbb, 0x01, 0x26, 0x47, + 0xe9, 0xb8, 0x34, 0x5c, 0x63, 0x13, 0xc0, 0x72, 0x52, 0x02, 0x4d, 0x7c, + 0x29, 0x4b, 0xe4, 0x1c, 0x7f, 0x53, 0x94, 0xbe, 0x97, 0x3d, 0x3d, 0x01, + 0xb6, 0x1f, 0x97, 0xdd, 0xe9, 0xd5, 0x5c, 0x88, 0x23, 0x81, 0x46, 0xea, + 0x43, 0x6e, 0x1f, 0x0a, 0xad, 0xfa, 0x83, 0xe7, 0x9a, 0x3b, 0xca, 0xf9, + 0x9e, 0xe6, 0x01, 0x32, 0x54, 0x3f, 0xfb, 0x7e, 0x14, 0xad, 0xec, 0xc9, + 0x6f, 0xbd, 0x76, 0x46, 0x3a, 0x23, 0x2f, 0x17, 0x41, 0xc8, 0x82, 0x45, + 0x5b, 0xf0, 0xce, 0x3f, 0x86, 0x11, 0x66, 0xfa, 0xf7, 0xe6, 0x58, 0xc3, + 0xd0, 0x2d, 0xec, 0x92, 0xd6, 0xe5, 0xe6, 0xa4, 0x0d, 0x15, 0x84, 0x43, + 0x74, 0x90, 0xea, 0x0a, 0x20, 0x64, 0x94, 0x55, 0xe3, 0x4e, 0x5d, 0xcb, + 0x98, 0xfa, 0x8f, 0xe3, 0xdd, 0xd9, 0xa8, 0xc1, 0xb3, 0x9e, 0xf5, 0x1e, + 0x66, 0xa4, 0x41, 0x46, 0xf8, 0xf9, 0xf8, 0x4f, 0xc3, 0xae, 0x31, 0x05, + 0x22, 0xdc, 0x63, 0x00, 0x2e, 0xdc, 0xe2, 0xc4, 0x07, 0x25, 0xfd, 0xbd, + 0x9b, 0xa5, 0xd4, 0xb4, 0xa8, 0xf7, 0x05, 0x7a, 0xda, 0x35, 0x69, 0x8f, + 0x85, 0x9f, 0xff, 0xce, 0x13, 0x06, 0xf2, 0x8a, 0x04, 0x03, 0xa6, 0x89, + 0x3e, 0x47, 0xcb, 0x6c, 0x0a, 0xb8, 0xe4, 0x1e, 0xad, 0x43, 0x0a, 0xa3, + 0x7a, 0xf6, 0x34, 0x95, 0x53, 0x07, 0x36, 0x07, 0xfc, 0x32, 0x9f, 0xa4, + 0x7c, 0x9a, 0xc3, 0x2d, 0x42, 0x31, 0xd1, 0x4b, 0xfc, 0x78, 0x5c, 0x9f, + 0xd3, 0xb2, 0x3d, 0xd1, 0xa7, 0xe8, 0x23, 0xb7, 0xa6, 0x0c, 0xaf, 0x9d, + 0x89, 0xb8, 0xab, 0x7b, 0x00, 0x0e, 0xd4, 0x4c, 0x6b, 0x61, 0xbc, 0x41, + 0xfa, 0xc3, 0x9b, 0x10, 0x02, 0x64, 0xa0, 0xd4, 0xb3, 0x3a, 0x5a, 0xb3, + 0xc6, 0xe2, 0x6b, 0xee, 0xa8, 0x95, 0xf8, 0x39, 0xf3, 0xf2, 0xb4, 0x3c, + 0xbc, 0xa1, 0xc6, 0x0c, 0xc2, 0x0b, 0x2a, 0xa1, 0x58, 0x95, 0xc1, 0x1f, + 0xa8, 0x5e, 0x30, 0x1f, 0x10, 0x00, 0x69, 0x3e, 0x8d, 0x9c, 0xac, 0xaa, + 0xf8, 0xb7, 0xad, 0xcf, 0x31, 0xbb, 0x81, 0xf9, 0x96, 0x9b, 0xce, 0xf7, + 0x6b, 0xab, 0x52, 0x6d, 0xc5, 0x1e, 0x4f, 0xe3, 0x0f, 0xfb, 0x30, 0xde, + 0x69, 0xa5, 0xd1, 0xf8, 0xc8, 0xa9, 0x70, 0x47, 0x20, 0x6f, 0xa7, 0x46, + 0xba, 0x70, 0xa3, 0xe6, 0xcd, 0xbc, 0xbe, 0xbf, 0xff, 0x6c, 0x44, 0x24, + 0x17, 0x74, 0x24, 0xdb, 0xd4, 0xf5, 0x51, 0xf1, 0xf2, 0x99, 0x68, 0x85, + 0x39, 0x4b, 0xcc, 0xa3, 0xd7, 0xc0, 0x65, 0xfa, 0x61, 0x4a, 0x13, 0x79, + 0x26, 0xe8, 0x75, 0x27, 0xcd, 0x87, 0x13, 0x85, 0x71, 0x90, 0xac, 0x7f, + 0xdd, 0xc2, 0x8a, 0x95, 0xb0, 0xee, 0xf3, 0xeb, 0x76, 0x6f, 0x32, 0xf0, + 0x23, 0xb5, 0x09, 0x6e, 0xb5, 0xe5, 0x5d, 0x84, 0x5c, 0x4c, 0x25, 0xb3, + 0x2a, 0x1e, 0x4f, 0xa1, 0x9a, 0x6a, 0x4f, 0xfc, 0x82, 0x5b, 0xa0, 0xed, + 0x45, 0x99, 0x37, 0x83, 0x51, 0xdf, 0xa0, 0xe5, 0xbf, 0x06, 0xf2, 0x3a, + 0x7f, 0x7a, 0x46, 0x3c, 0x17, 0x60, 0xaa, 0x69, 0x38, 0x26, 0xd1, 0x3e, + 0x47, 0x32, 0xa9, 0x7b, 0x74, 0xa6, 0x8b, 0x34, 0x2b, 0xc5, 0x47, 0x73, + 0x6e, 0x6d, 0x31, 0xc2, 0xf1, 0x33, 0xcc, 0xde, 0xef, 0x06, 0x18, 0x5d, + 0xa9, 0x60, 0xcb, 0x3a, 0x68, 0x27, 0x50, 0xe3, 0x56, 0xc8, 0xe3, 0x69, + 0x7f, 0x02, 0xa8, 0x15, 0x53, 0x46, 0x92, 0x5d, 0xc2, 0x9b, 0x7d, 0x5d, + 0x55, 0x80, 0xa7, 0x48, 0xc4, 0xff, 0x05, 0x76, 0x3f, 0x9e, 0x8b, 0xbe, + 0x84, 0xef, 0x0e, 0x21, 0x14, 0xc9, 0x9b, 0x6c, 0xbb, 0x41, 0x5b, 0xfb, + 0x65, 0x19, 0xd3, 0x24, 0x8b, 0xb3, 0xe6, 0xe2, 0x2a, 0x1f, 0xe3, 0x72, + 0xe8, 0xeb, 0x83, 0x83, 0x63, 0x1a, 0x4d, 0xa7, 0xc9, 0x11, 0xc5, 0xbf, + 0x60, 0xbf, 0x2b, 0x56, 0x30, 0x6e, 0xd0, 0xee, 0xf2, 0x5c, 0x24, 0x1e, + 0xd8, 0xd3, 0xd8, 0x9e, 0xd1, 0x2a, 0xbb, 0x59, 0x99, 0xf4, 0x36, 0xa1, + 0x70, 0xf7, 0xe9, 0xe5, 0x4c, 0x13, 0x53, 0xb7, 0x7c, 0x81, 0x53, 0x12, + 0x75, 0x4f, 0x5a, 0xa1, 0xe8, 0x49, 0xc4, 0x44, 0xe0, 0x2e, 0xcb, 0xcc, + 0x3e, 0xad, 0x65, 0x1d, 0x82, 0x4d, 0x65, 0x7d, 0x6e, 0x7c, 0xd4, 0x09, + 0xc5, 0x4c, 0x43, 0xf2, 0x49, 0xb3, 0x6c, 0x70, 0x77, 0x8d, 0xb3, 0x74, + 0x2a, 0x69, 0xbd, 0xd5, 0x16, 0x76, 0xc3, 0x1d, 0x22, 0x04, 0x63, 0x30, + 0x2c, 0x79, 0x50, 0x3c, 0x7b, 0x08, 0x03, 0xbd, 0x62, 0xfa, 0x11, 0x0b, + 0xfb, 0x3c, 0x40, 0x2c, 0x54, 0xa6, 0x19, 0xff, 0xe8, 0x28, 0x17, 0xb9, + 0x1d, 0xee, 0x3f, 0x40, 0xbd, 0xa1, 0x1e, 0xdc, 0x51, 0x6d, 0xe6, 0x02, + 0x56, 0xe1, 0xc5, 0x46, 0xc6, 0x67, 0x33, 0x01, 0x99, 0xcf, 0x31, 0xe6, + 0x33, 0x56, 0xbd, 0x40, 0x7e, 0x0c, 0x77, 0x1e, 0x2f, 0x72, 0x98, 0xf2, + 0x05, 0xf8, 0x7e, 0x95, 0x92, 0x92, 0x52, 0xff, 0x45, 0xde, 0xa6, 0xee, + 0x70, 0x02, 0x2a, 0x1f, 0x0d, 0xb2, 0x16, 0xee, 0xfe, 0x01, 0x9a, 0x1d, + 0xaa, 0x83, 0xb3, 0x35, 0x55, 0xf7, 0xbb, 0x5f, 0x0b, 0xbe, 0xd5, 0x18, + 0x83, 0x36, 0x13, 0x05, 0x54, 0xca, 0xa9, 0x2c, 0x54, 0x3a, 0x37, 0x91, + 0xf4, 0xa0, 0xb5, 0x55, 0x87, 0xb6, 0x47, 0xd1, 0x92, 0xe8, 0x90, 0x65, + 0xc9, 0x1a, 0x9f, 0x0f, 0x9f, 0x37, 0x48, 0x21, 0x5e, 0x43, 0xe1, 0x36, + 0x9b, 0x5c, 0xc7, 0x9c, 0x74, 0x27, 0x7f, 0xd5, 0x4d, 0xcd, 0xe3, 0xf1, + 0xe2, 0xa3, 0x05, 0x00, 0xa4, 0xad, 0xb3, 0x16, 0xb1, 0x4a, 0xdf, 0x59, + 0xa8, 0x01, 0x91, 0xde, 0x12, 0xc0, 0x11, 0x16, 0x62, 0xc1, 0x27, 0x32, + 0x97, 0x82, 0x50, 0x0f, 0xbf, 0xa1, 0x71, 0x8a, 0x03, 0x19, 0x51, 0x9d, + 0x38, 0x9d, 0x75, 0xe7, 0x18, 0x02, 0xd1, 0x2b, 0x63, 0xa0, 0x74, 0xe8, + 0x66, 0x6a, 0xfd, 0x63, 0x51, 0x09, 0xe2, 0xdf, 0x99, 0xbc, 0x4a, 0xd1, + 0x00, 0x38, 0xf5, 0x6d, 0xb4, 0x63, 0x7f, 0x19, 0xbb, 0xbe, 0x42, 0xcc, + 0xa1, 0x6d, 0x07, 0x08, 0xf3, 0xa2, 0xd2, 0xd4, 0x5a, 0x4c, 0x14, 0xd3, + 0x5b, 0xda, 0x93, 0xc7, 0xab, 0x68, 0x46, 0x87, 0x64, 0xe7, 0x11, 0x76, + 0x8b, 0x11, 0x3f, 0x84, 0x21, 0x35, 0x7d, 0x1d, 0xab, 0x09, 0xa1, 0x2e, + 0x69, 0xb3, 0xf0, 0x53, 0x7a, 0x11, 0x26, 0x7c, 0xe4, 0x2e, 0x7d, 0x4f, + 0xb5, 0x39, 0xf4, 0x9c, 0x13, 0xde, 0xaa, 0xe7, 0x8e, 0x9a, 0x26, 0xd5, + 0xcd, 0x5a, 0x35, 0x69, 0x88, 0xb7, 0x54, 0xf4, 0x46, 0xc7, 0x70, 0x1e, + 0x93, 0xf8, 0xd9, 0x21, 0x63, 0xc8, 0xb8, 0x70, 0x73, 0x01, 0x5f, 0xed, + 0xd3, 0xc7, 0xcb, 0x7f, 0x22, 0xbb, 0x77, 0xb8, 0xbc, 0x99, 0x88, 0xbe, + 0x49, 0x50, 0x52, 0x4f, 0x27, 0x23, 0x29, 0x58, 0xef, 0xbd, 0x65, 0x8e, + 0x19, 0x3f, 0xc8, 0x25, 0x91, 0x8d, 0xb9, 0x2a, 0xe4, 0x43, 0x93, 0x34, + 0x31, 0xfb, 0x8f, 0xf4, 0x6a, 0xde, 0xc1, 0xf0, 0xd1, 0x3a, 0xd2, 0x38, + 0x3b, 0xc5, 0xc6, 0x19, 0x78, 0xb0, 0xc8, 0x42, 0x17, 0xba, 0x03, 0x2a, + 0x6a, 0x9e, 0x5c, 0x4f, 0x65, 0x3f, 0x88, 0x7b, 0xa6, 0x36, 0x09, 0xa1, + 0xbc, 0x44, 0x7a, 0x9f, 0x3d, 0xf6, 0x00, 0x4b, 0xfe, 0x27, 0xd9, 0x1c, + 0x95, 0x39, 0x2a, 0xb3, 0x16, 0xa4, 0x08, 0xf4, 0x42, 0x95, 0x88, 0x8a, + 0x77, 0xd0, 0xcf, 0xa3, 0xe7, 0x51, 0x86, 0x17, 0x20, 0x48, 0x47, 0xef, + 0xbc, 0x1c, 0xa1, 0x22, 0x7c, 0x3f, 0x45, 0x44, 0xb7, 0xba, 0xc7, 0x34, + 0xd2, 0x5a, 0x08, 0x47, 0x4f, 0xcb, 0xa3, 0x7b, 0xb1, 0xf3, 0xed, 0x3a, + 0x43, 0x8e, 0x52, 0x92, 0xc0, 0x5f, 0x9d, 0x0a, 0x76, 0x65, 0xb7, 0xa4, + 0x0e, 0x67, 0xaf, 0x3f, 0xc6, 0x8b, 0x97, 0xcb, 0xe9, 0xfe, 0x59, 0x15, + 0x47, 0x08, 0xc4, 0x11, 0x13, 0x3b, 0x4c, 0xb9, 0xa8, 0x17, 0x3f, 0x7d, + 0x39, 0x92, 0x1a, 0x1d, 0xe7, 0x74, 0x7f, 0x68, 0x0f, 0x8c, 0xcd, 0x2a, + 0x6f, 0xc4, 0xfd, 0xa8, 0x36, 0xb3, 0xc6, 0x69, 0x74, 0xd5, 0xf6, 0x8c, + 0xa2, 0xbf, 0x09, 0x64, 0xd1, 0x17, 0x92, 0x1f, 0x1b, 0x71, 0x37, 0x98, + 0x0f, 0xec, 0x4c, 0xa2, 0x0a, 0x8c, 0x51, 0x0b, 0xa4, 0xc6, 0x33, 0xa0, + 0xba, 0xa7, 0x0f, 0x0d, 0x10, 0xf1, 0xb8, 0xa8, 0x0d, 0x8f, 0xad, 0x7c, + 0x4b, 0xa4, 0x71, 0x45, 0x01, 0x6b, 0xbe, 0x45, 0xb1, 0x03, 0x70, 0x01, + 0xcb, 0x45, 0xd1, 0x57, 0xb7, 0x63, 0xb8, 0x7e, 0x9e, 0xbc, 0x4a, 0x31, + 0xed, 0xe8, 0x82, 0xd0, 0x99, 0x67, 0x02, 0xb9, 0xbc, 0x08, 0x42, 0xc9, + 0x7a, 0xae, 0xfe, 0xce, 0x04, 0x31, 0xc4, 0x73, 0xec, 0x46, 0x1f, 0xde, + 0xca, 0x1b, 0x2d, 0x96, 0xb2, 0x3c, 0x98, 0xd9, 0x24, 0x6a, 0x13, 0x83, + 0x67, 0xbe, 0x10, 0x64, 0x53, 0x79, 0x07, 0xbe, 0xae, 0xdf, 0x81, 0xa2, + 0x7f, 0x18, 0x03, 0xc4, 0xc2, 0x56, 0x0e, 0x57, 0x56, 0x8e, 0xd3, 0xe8, + 0xaf, 0xd0, 0x9b, 0xa5, 0xf9, 0x6e, 0xd3, 0x94, 0xb0, 0x31, 0xe3, 0xd3, + 0x38, 0x62, 0xbc, 0xbf, 0xa5, 0x78, 0xe5, 0xa7, 0x6c, 0x39, 0xd4, 0x85, + 0x7b, 0xaa, 0x32, 0x18, 0x4d, 0xa5, 0x24, 0x01, 0x13, 0xf5, 0x2d, 0x48, + 0x61, 0x85, 0x1c, 0xfc, 0xf4, 0x03, 0xbc, 0xb7, 0xd5, 0x81, 0x9a, 0x4f, + 0xde, 0x08, 0xe7, 0xb2, 0x90, 0xe7, 0x29, 0x26, 0x46, 0x8b, 0xe7, 0xe4, + 0x4a, 0x47, 0xdd, 0xf4, 0x95, 0xeb, 0x31, 0x86, 0x88, 0xa5, 0x7f, 0x0e, + 0x0a, 0x77, 0x97, 0x7a, 0x7b, 0x73, 0xbf, 0x90, 0xdc, 0xf8, 0x1d, 0x15, + 0x44, 0x65, 0x68, 0xd4, 0x6e, 0x25, 0x16, 0x48, 0xfd, 0xc1, 0x08, 0x06, + 0xe9, 0x77, 0x1d, 0x8a, 0x90, 0xf1, 0xcb, 0x98, 0x9b, 0xb1, 0xa0, 0x59, + 0x5c, 0x43, 0xc8, 0x1f, 0x11, 0x2e, 0x74, 0x52, 0xf2, 0x97, 0xa5, 0xbe, + 0xe2, 0x99, 0x0e, 0x0b, 0xb0, 0x9f, 0x39, 0xf6, 0x7b, 0x3c, 0x1d, 0x12, + 0xee, 0x08, 0xf5, 0x5d, 0x55, 0xf2, 0x2d, 0x69, 0xa6, 0xae, 0xfa, 0x03, + 0xfe, 0xe9, 0x0b, 0x68, 0x9d, 0xaa, 0xca, 0x35, 0x60, 0x9d, 0x2c, 0xa6, + 0x29, 0xcb, 0x1d, 0x50, 0xd2, 0xcf, 0x6a, 0x44, 0xdd, 0x4c, 0xe2, 0x5a, + 0x34, 0x91, 0x58, 0xf5, 0xdb, 0x4d, 0xce, 0x74, 0x9a, 0xdb, 0x12, 0x83, + 0xab, 0x50, 0xa8, 0xb2, 0x7a, 0x87, 0xb2, 0x20, 0x3e, 0xba, 0xaa, 0xaa, + 0x88, 0x67, 0x1f, 0x99, 0x64, 0xaf, 0xc5, 0xa1, 0x45, 0x2c, 0x03, 0xda, + 0x08, 0xde, 0xe3, 0x23, 0x71, 0x7e, 0x54, 0xff, 0xfa, 0x09, 0x73, 0x0f, + 0x67, 0x55, 0x4c, 0x8f, 0x83, 0x58, 0x7e, 0xb9, 0x9b, 0x41, 0x70, 0x82, + 0x7e, 0xfc, 0xeb, 0xb4, 0x86, 0xb9, 0xe3, 0x5a, 0xab, 0x92, 0xb0, 0xaf, + 0x94, 0x2f, 0x28, 0xca, 0x38, 0x08, 0xdf, 0xc3, 0x8f, 0x6e, 0x4e, 0xad, + 0x6e, 0x87, 0xa5, 0x2e, 0x4a, 0xaa, 0xbd, 0xc4, 0x86, 0xb6, 0x50, 0xa1, + 0xaa, 0xa0, 0x02, 0x76, 0xd4, 0x89, 0x44, 0x5d, 0x66, 0xa5, 0xf2, 0x2b, + 0x60, 0xe0, 0xc7, 0x25, 0x3d, 0x5c, 0x3c, 0x86, 0xe2, 0xb7, 0x6f, 0x0b, + 0x2b, 0xcb, 0xf7, 0xf5, 0x95, 0x35, 0xa5, 0x38, 0xd1, 0xf5, 0xf8, 0xa8, + 0x72, 0xc7, 0xc0, 0x2f, 0x1e, 0xdb, 0x47, 0xe1, 0xf5, 0xe3, 0x29, 0xb0, + 0x1e, 0xb6, 0x15, 0x96, 0x5d, 0x60, 0x90, 0x1e, 0xd1, 0x5c, 0xa0, 0xf7, + 0x02, 0x8e, 0xfe, 0xef, 0xc5, 0xa4, 0x52, 0xfe, 0x93, 0x6c, 0xb0, 0xac, + 0x1e, 0x33, 0x4f, 0xc5, 0x18, 0x5c, 0xd0, 0x86, 0x00, 0xb2, 0xcc, 0x75, + 0x20, 0x06, 0xf6, 0xd4, 0x58, 0x4d, 0x8e, 0x99, 0x8c, 0x5f, 0xe6, 0xb3, + 0x1a, 0x7d, 0xab, 0x1d, 0x96, 0xdc, 0x64, 0xb9, 0xe5, 0x16, 0xcd, 0xa7, + 0x73, 0x38, 0x43, 0x4b, 0x5f, 0x64, 0x7f, 0x10, 0xd6, 0xa4, 0x45, 0x16, + 0x15, 0xd4, 0x62, 0x3c, 0xea, 0x7b, 0x9c, 0xb2, 0x13, 0x93, 0x18, 0xc8, + 0xfe, 0xfa, 0xa8, 0x71, 0x37, 0xeb, 0x96, 0x1d, 0xdb, 0x50, 0x29, 0xcf, + 0x49, 0x86, 0xd5, 0xa2, 0x1f, 0x38, 0x8d, 0xb9, 0xf6, 0xb6, 0xd1, 0x95, + 0xd9, 0x75, 0x20, 0x02, 0x4c, 0x11, 0xa9, 0xc1, 0x7b, 0x47, 0x16, 0xdf, + 0x70, 0xf9, 0x72, 0x74, 0xc9, 0x28, 0x4d, 0x8a, 0x18, 0xe3, 0xb1, 0x6d, + 0x58, 0xcb, 0x1b, 0x04, 0x7d, 0xda, 0x9f, 0x0d, 0x4e, 0x45, 0x0e, 0x5c, + 0x32, 0xce, 0x1d, 0xba, 0x5a, 0xb8, 0xc9, 0x73, 0x63, 0xf7, 0xae, 0x6b, + 0xfc, 0xd9, 0x86, 0xa6, 0xc8, 0x00, 0x37, 0xcb, 0x02, 0x82, 0x08, 0x1a, + 0x5b, 0xa4, 0x60, 0x55, 0x02, 0x91, 0x69, 0x6b, 0x45, 0x54, 0x2d, 0x1c, + 0xac, 0x53, 0xbd, 0x87, 0x46, 0x67, 0xd4, 0xef, 0xcb, 0x24, 0x28, 0xbd, + 0x9a, 0x6e, 0xd4, 0x7f, 0x0a, 0x16, 0xe3, 0xe7, 0x14, 0xf8, 0x3d, 0xa9, + 0x84, 0x08, 0x50, 0xbf, 0x65, 0xe3, 0x94, 0xe2, 0x3d, 0xe4, 0x25, 0xad, + 0x9a, 0x1c, 0x6e, 0x6f, 0xef, 0xaf, 0x1d, 0xec, 0x86, 0xee, 0x78, 0x3d, + 0x30, 0x7a, 0x7b, 0x56, 0xfc, 0xfd, 0x53, 0xb0, 0x5b, 0x3b, 0x3c, 0xcb, + 0xea, 0xe9, 0xc4, 0x56, 0x82, 0xca, 0xb8, 0xac, 0x3e, 0x6d, 0x50, 0x3c, + 0x04, 0xe6, 0x55, 0x50, 0xde, 0x83, 0x6f, 0x48, 0x74, 0xb5, 0x39, 0x5a, + 0x86, 0xea, 0x61, 0xb6, 0xa0, 0xa3, 0x4d, 0x9b, 0x8c, 0x1f, 0xd7, 0xfb, + 0x5c, 0x12, 0xff, 0xaf, 0xef, 0x94, 0x75, 0x33, 0xbd, 0x42, 0x19, 0x41, + 0x7c, 0xe3, 0x0f, 0x69, 0xae, 0x11, 0x65, 0xfc, 0x42, 0x64, 0xab, 0x18, + 0xe0, 0x10, 0x83, 0x16, 0x80, 0x07, 0x9e, 0x26, 0xb7, 0x55, 0xcc, 0xde, + 0x3a, 0x63, 0xb1, 0x3f, 0x2a, 0x08, 0x1e, 0x40, 0xcb, 0x69, 0xc0, 0x3a, + 0x7e, 0x40, 0xa6, 0xab, 0x1f, 0xf4, 0x79, 0x8a, 0x55, 0x90, 0x26, 0x0c, + 0x72, 0xd9, 0xff, 0x3a, 0xca, 0x31, 0x99, 0x36, 0x98, 0xf2, 0xad, 0x8f, + 0x4d, 0xde, 0x32, 0x22, 0x14, 0xf0, 0x7b, 0xfa, 0xee, 0x91, 0x99, 0x46, + 0x75, 0xf3, 0x5e, 0x99, 0x90, 0x78, 0xf2, 0x07, 0x3d, 0x2d, 0x30, 0x2b, + 0xee, 0x04, 0x3f, 0xc0, 0xe8, 0x88, 0x71, 0xda, 0xcb, 0x7a, 0x46, 0x5f, + 0xd4, 0x12, 0x78, 0xc4, 0x51, 0x6e, 0x80, 0x27, 0x8f, 0xcb, 0x54, 0x2e, + 0xbb, 0x6a, 0x46, 0x63, 0xfb, 0x66, 0x2b, 0x4f, 0xb4, 0x84, 0xc5, 0x28, + 0x8a, 0xdd, 0x4a, 0x3e, 0x47, 0x56, 0xbe, 0x9d, 0x62, 0xf3, 0x6a, 0x76, + 0x71, 0x8c, 0x50, 0x6f, 0x44, 0x2e, 0xf4, 0xbf, 0x9c, 0x64, 0x35, 0xd9, + 0xec, 0x92, 0x71, 0x58, 0xaf, 0xfd, 0xb7, 0xaf, 0x19, 0x26, 0x95, 0x05, + 0x32, 0xe6, 0x09, 0xcd, 0x21, 0x97, 0xe0, 0x01, 0xa7, 0xda, 0x4d, 0x2c, + 0x01, 0xbc, 0x45, 0xd3, 0xe6, 0xb0, 0x87, 0x11, 0x41, 0x8c, 0x14, 0x09, + 0x88, 0x1a, 0x9b, 0xa4, 0x1c, 0x8a, 0x25, 0xb8, 0x82, 0x95, 0xac, 0x28, + 0xa6, 0xeb, 0xf8, 0xe4, 0x44, 0x20, 0xf4, 0x0d, 0xe9, 0x54, 0x04, 0x15, + 0x12, 0xd9, 0xa0, 0xe0, 0xa0, 0x88, 0xf5, 0xd7, 0x97, 0x19, 0xca, 0x8f, + 0xa8, 0x29, 0xca, 0x7f, 0xee, 0x7b, 0xda, 0xda, 0xbe, 0x87, 0xc7, 0x89, + 0x60, 0x24, 0x13, 0xa4, 0xc0, 0x1a, 0x8f, 0x99, 0x75, 0x11, 0x34, 0x5a, + 0xde, 0xf2, 0xa8, 0xda, 0x79, 0x88, 0x97, 0x72, 0xcc, 0xda, 0x71, 0x5b, + 0x97, 0x20, 0xf8, 0x64, 0x2e, 0xaf, 0x9e, 0x4c, 0x6e, 0xa2, 0x28, 0xfe, + 0x6e, 0xe7, 0x89, 0x2f, 0x0c, 0x98, 0x53, 0xb6, 0x12, 0x70, 0x1f, 0x54, + 0xde, 0x17, 0x70, 0x82, 0x0e, 0x95, 0xa6, 0x0f, 0x40, 0x4f, 0xbe, 0xc3, + 0x17, 0xbf, 0x1c, 0xb6, 0x2c, 0xc0, 0x0f, 0x6b, 0x63, 0x61, 0x20, 0x70, + 0x67, 0x75, 0x3c, 0x77, 0x3d, 0x9a, 0xa9, 0xb4, 0x8b, 0x66, 0x15, 0x74, + 0x72, 0xaa, 0xf6, 0x3e, 0xb0, 0xa5, 0x92, 0x18, 0x55, 0x1e, 0x1c, 0x52, + 0xa0, 0xa4, 0xd0, 0x43, 0x78, 0xa2, 0x35, 0xcc, 0x58, 0x96, 0x46, 0x95, + 0xb3, 0x64, 0x30, 0x61, 0x4f, 0x03, 0x23, 0x20, 0x7c, 0x06, 0x6a, 0x22, + 0x29, 0x52, 0xcf, 0x45, 0xfa, 0x87, 0x4f, 0xba, 0x29, 0x40, 0x74, 0x8b, + 0xed, 0xbe, 0x3d, 0x6c, 0xc9, 0xa4, 0xa7, 0x8b, 0x30, 0xf3, 0xd2, 0x5b, + 0x74, 0x22, 0x37, 0xde, 0x67, 0xa5, 0x47, 0x37, 0xc3, 0x5c, 0x7a, 0xb3, + 0xc6, 0x33, 0x73, 0x20, 0xe3, 0xbe, 0x10, 0x07, 0x6f, 0x6e, 0x52, 0x00, + 0xe2, 0xf2, 0x15, 0x6c, 0x44, 0xf3, 0x3f, 0x9e, 0xd6, 0xf8, 0xfa, 0x3d, + 0xa4, 0xc9, 0x60, 0x05, 0xef, 0x4f, 0xd5, 0xd7, 0x35, 0x5f, 0x49, 0xc6, + 0x48, 0x0a, 0x29, 0x37, 0xf3, 0x00, 0x99, 0x63, 0x90, 0x1d, 0xbc, 0x93, + 0x37, 0x36, 0x36, 0x7f, 0x15, 0x73, 0x98, 0x35, 0xe7, 0xee, 0x77, 0x50, + 0xdd, 0xdc, 0x0a, 0x3a, 0x9a, 0x04, 0x2e, 0x71, 0x4c, 0x0a, 0x6a, 0x57, + 0x99, 0x2d, 0x47, 0xe1, 0xb3, 0xec, 0x2d, 0x07, 0xec, 0xee, 0xf2, 0x6a, + 0xe9, 0xbe, 0x78, 0x4d, 0x78, 0x1a, 0xc5, 0x3c, 0x44, 0xb9, 0x19, 0xb7, + 0x88, 0xc7, 0xaf, 0x5e, 0xa3, 0x5b, 0x7e, 0x87, 0x00, 0x41, 0x35, 0x3b, + 0x74, 0x26, 0x6b, 0xe5, 0xfd, 0x3d, 0xf7, 0x52, 0x68, 0x30, 0x80, 0xa0, + 0x65, 0xc2, 0xf5, 0x45, 0xfe, 0x7f, 0xe4, 0x78, 0xd9, 0x11, 0x18, 0x26, + 0xf0, 0x64, 0x8f, 0x1c, 0xe4, 0xa5, 0x3c, 0x14, 0xb6, 0x6a, 0x0e, 0x41, + 0x44, 0x9a, 0x25, 0x23, 0xe6, 0xee, 0x5c, 0x23, 0xb7, 0x42, 0x36, 0x1e, + 0xa2, 0x6a, 0x64, 0x10, 0x4a, 0x77, 0x8e, 0x71, 0xbb, 0x8b, 0x2a, 0x4b, + 0xf3, 0xe7, 0x61, 0x66, 0xa5, 0xa5, 0xa6, 0x48, 0x8a, 0x26, 0xfb, 0x7d, + 0xe7, 0x7c, 0x51, 0xf0, 0xb9, 0xe8, 0xa8, 0x72, 0x8c, 0xa1, 0x79, 0x82, + 0x81, 0x8f, 0x49, 0xfa, 0x86, 0xb5, 0xf7, 0x65, 0x4b, 0x12, 0xfb, 0x2e, + 0xce, 0x2f, 0x6f, 0x39, 0x5d, 0x91, 0x1a, 0xcd, 0x44, 0xc9, 0x61, 0x28, + 0x3f, 0xbc, 0x2e, 0xa5, 0x18, 0xff, 0xc0, 0x5d, 0x7d, 0xab, 0x0f, 0xc4, + 0x98, 0x85, 0xbc, 0x49, 0x9f, 0x5c, 0x14, 0x82, 0xb5, 0x54, 0xf8, 0xd2, + 0xc7, 0xc5, 0xf4, 0x41, 0x82, 0x3c, 0x41, 0xa2, 0x53, 0xf8, 0x06, 0x65, + 0xcd, 0xc5, 0x42, 0x75, 0xd5, 0x18, 0xd1, 0x78, 0xe6, 0x3e, 0x38, 0x00, + 0x2e, 0x4f, 0x6c, 0xd5, 0xcf, 0x2e, 0x24, 0x75, 0xd9, 0x63, 0x85, 0xbc, + 0xd0, 0x2c, 0x13, 0xa4, 0xc2, 0xd5, 0x48, 0x32, 0x0d, 0xdf, 0x90, 0x1f, + 0x14, 0x04, 0xe6, 0x6a, 0x99, 0x5f, 0x7e, 0x3a, 0x13, 0x4c, 0xcf, 0xc6, + 0x8d, 0xf7, 0x71, 0xc6, 0x75, 0x71, 0xd4, 0x5c, 0x5b, 0xa9, 0x0c, 0x9d, + 0x63, 0x0a, 0xe0, 0xa3, 0xc8, 0x04, 0x78, 0x33, 0xf4, 0x66, 0x83, 0xbe, + 0x8c, 0x6c, 0xe8, 0x40, 0xf9, 0xcc, 0x39, 0xd8, 0x19, 0x51, 0xb0, 0xe3, + 0x85, 0x16, 0xfe, 0x43, 0x0a, 0x99, 0x13, 0xef, 0xe7, 0xcb, 0xfc, 0x97, + 0xd6, 0xee, 0x1d, 0xa5, 0x7b, 0xc4, 0x43, 0xf1, 0x20, 0x9a, 0x07, 0x78, + 0x6c, 0x50, 0xb1, 0x02, 0x3e, 0x74, 0xbb, 0x31, 0x3a, 0x1a, 0xa1, 0x37, + 0x9d, 0x50, 0xab, 0x1d, 0x58, 0x00, 0x78, 0xa5, 0x36, 0xbc, 0xb6, 0x6b, + 0x85, 0xbe, 0xde, 0x2c, 0x3c, 0x9f, 0x02, 0x5c, 0x82, 0x49, 0xb3, 0xe0, + 0x94, 0x25, 0xb3, 0x24, 0xf6, 0x8a, 0x7c, 0xb7, 0x26, 0x15, 0x36, 0x35, + 0x23, 0x91, 0x31, 0x10, 0xe1, 0xa8, 0x22, 0xc2, 0xb3, 0x5e, 0x4d, 0x27, + 0xeb, 0x4a, 0xf6, 0x08, 0x87, 0x3d, 0x2b, 0xb3, 0x7d, 0x4c, 0xc8, 0x3b, + 0x9c, 0x59, 0xf2, 0x29, 0x12, 0x6f, 0xf1, 0x30, 0xd5, 0x59, 0x3e, 0x44, + 0xe9, 0x3a, 0xf9, 0x42, 0x43, 0x1c, 0xf6, 0x7d, 0x44, 0x16, 0x57, 0x95, + 0x6e, 0xf3, 0x61, 0xf0, 0x50, 0x89, 0x43, 0xbd, 0x38, 0xc5, 0x6a, 0xd8, + 0x92, 0xff, 0xa5, 0xd2, 0x64, 0x7d, 0x3f, 0x55, 0x19, 0x8d, 0x13, 0xb1, + 0x56, 0x80, 0xb0, 0x89, 0x13, 0xe5, 0x14, 0xc7, 0x84, 0x39, 0x18, 0x60, + 0x92, 0x55, 0x0d, 0x79, 0x72, 0xe1, 0x20, 0xd7, 0x92, 0xfc, 0x3f, 0xa2, + 0x22, 0x79, 0x2c, 0x2c, 0xcd, 0xc1, 0xdc, 0x54, 0x09, 0xa1, 0xf9, 0x98, + 0xb2, 0x07, 0x30, 0x31, 0x74, 0x29, 0xf9, 0x50, 0x87, 0xf4, 0x83, 0x0b, + 0xd9, 0x39, 0x09, 0x95, 0x44, 0xd6, 0xf1, 0xd0, 0xb1, 0x20, 0xc1, 0xa9, + 0x23, 0x6a, 0xb1, 0x35, 0x53, 0x04, 0xbc, 0x0f, 0x6c, 0xb0, 0xae, 0x3b, + 0x9f, 0xc9, 0x5e, 0xd3, 0x92, 0xb5, 0x43, 0xec, 0xdd, 0x3d, 0xcb, 0x7c, + 0xca, 0xa8, 0x7a, 0x49, 0x89, 0x56, 0x96, 0x3b, 0xda, 0x97, 0x93, 0x5f, + 0xca, 0xd6, 0x53, 0xa3, 0xc3, 0x82, 0x78, 0xed, 0x68, 0x09, 0x78, 0x95, + 0x1c, 0x7d, 0x0b, 0xa4, 0xa6, 0xab, 0xaf, 0xaa, 0x78, 0x45, 0x67, 0x34, + 0xc4, 0x4e, 0x23, 0x0f, 0x16, 0x6e, 0xef, 0x22, 0x24, 0xdd, 0xb3, 0x73, + 0x6d, 0x57, 0xcf, 0x8e, 0x38, 0x80, 0x05, 0xb3, 0x4a, 0xa9, 0x5d, 0xd3, + 0x50, 0x8d, 0x6e, 0x2d, 0x93, 0x86, 0x69, 0xeb, 0x46, 0xc1, 0x9e, 0x19, + 0x01, 0xff, 0xbf, 0x08, 0xd8, 0x80, 0x60, 0xc0, 0xd4, 0xaf, 0x49, 0xa1, + 0x41, 0x2b, 0x00, 0x3b, 0xab, 0x21, 0xf6, 0x6f, 0xa4, 0x16, 0x8b, 0x2c, + 0xdf, 0x98, 0xc4, 0x6e, 0x35, 0x47, 0x06, 0x3e, 0x31, 0x81, 0x50, 0xa8, + 0x64, 0x36, 0x04, 0x70, 0x9a, 0x7e, 0x41, 0x66, 0x54, 0x03, 0x6c, 0xc7, + 0x57, 0x1c, 0xac, 0xd9, 0xd6, 0xca, 0xf3, 0x8a, 0x5c, 0xe9, 0x06, 0xd5, + 0x94, 0x72, 0x93, 0x42, 0xdb, 0xef, 0x80, 0xbf, 0x9c, 0x4e, 0x14, 0x6f, + 0xb4, 0x91, 0x58, 0x01, 0x6d, 0x89, 0x62, 0x71, 0xe1, 0x98, 0x9f, 0x69, + 0x7a, 0xca, 0xe4, 0xdb, 0x1a, 0x06, 0x9a, 0x0a, 0x03, 0x4f, 0x07, 0x6d, + 0xe6, 0x37, 0xbf, 0x00, 0x69, 0x26, 0x01, 0x10, 0xa2, 0x6c, 0xf6, 0xe0, + 0x68, 0x8b, 0x1f, 0x31, 0x9d, 0x74, 0xb2, 0xf4, 0x56, 0xc6, 0x02, 0x7b, + 0xd7, 0x9d, 0x38, 0x74, 0x2b, 0x32, 0x4a, 0x88, 0x12, 0xe5, 0x8d, 0xc8, + 0xc6, 0xed, 0x90, 0x5c, 0xd2, 0x76, 0x50, 0xd2, 0x13, 0x61, 0xf8, 0x96, + 0x73, 0xf3, 0x48, 0xa1, 0x2d, 0x6f, 0x3a, 0x9c, 0xf8, 0x3c, 0x06, 0xef, + 0xfc, 0x52, 0xfb, 0xbc, 0xf2, 0x21, 0x15, 0x57, 0xc2, 0x0c, 0x7c, 0x1b, + 0xbd, 0x38, 0xb8, 0x11, 0xbf, 0x25, 0x99, 0x13, 0x5d, 0x0e, 0xd9, 0x2c, + 0x0a, 0x7c, 0x99, 0x72, 0x33, 0xa6, 0x5f, 0xf7, 0xf9, 0xba, 0x1a, 0x78, + 0x37, 0x81, 0x4e, 0x83, 0x92, 0xa9, 0x36, 0x88, 0xf0, 0x19, 0x88, 0xa6, + 0x9e, 0x09, 0xce, 0x61, 0x37, 0x2c, 0xfb, 0xd4, 0x78, 0x41, 0x23, 0x0b, + 0xb8, 0x40, 0x8e, 0xc4, 0x43, 0xa6, 0x19, 0xc2, 0x75, 0xb2, 0x82, 0xa5, + 0x84, 0x70, 0x1d, 0x22, 0x4d, 0x72, 0xfc, 0x37, 0x33, 0xc7, 0x9d, 0xe5, + 0x6e, 0x97, 0xc9, 0x68, 0x7d, 0xf4, 0x17, 0xa9, 0xa6, 0x4f, 0xdd, 0xa2, + 0xf4, 0x4d, 0x02, 0x66, 0x3c, 0x2f, 0x9a, 0x1c, 0x01, 0xdd, 0x2d, 0x2c, + 0xb6, 0xac, 0xfc, 0x21, 0x35, 0x9a, 0xe1, 0x99, 0x2a, 0x5a, 0xc9, 0x81, + 0x5e, 0xb6, 0x9d, 0xe0, 0x85, 0x3a, 0x02, 0xfe, 0x72, 0x42, 0x67, 0xe1, + 0xdb, 0xd2, 0x1c, 0x4f, 0x62, 0x15, 0x83, 0x51, 0xfd, 0x49, 0xc5, 0xb2, + 0x60, 0xba, 0xa3, 0xeb, 0xf7, 0xb4, 0x21, 0x20, 0x4c, 0x14, 0x10, 0x9e, + 0x78, 0xa8, 0xa6, 0x08, 0x7b, 0xf8, 0x1b, 0x88, 0x5f, 0x99, 0xa8, 0x1d, + 0x8d, 0x59, 0x79, 0x04, 0x8b, 0xc4, 0x19, 0x36, 0xd0, 0xa0, 0xc2, 0x66, + 0x1b, 0x86, 0xad, 0x81, 0xbd, 0x22, 0x3d, 0xa7, 0x3f, 0xd1, 0xf7, 0x99, + 0xda, 0x6c, 0xaa, 0xdc, 0xe6, 0x99, 0x27, 0x12, 0x80, 0x01, 0x4b, 0x48, + 0x4e, 0x2a, 0x2e, 0x51, 0x07, 0xc6, 0x24, 0xe2, 0x08, 0x74, 0xfc, 0x43, + 0xb6, 0x15, 0x5b, 0x24, 0x29, 0xef, 0x6b, 0xf1, 0x8b, 0x88, 0x62, 0xb6, + 0x99, 0x91, 0xf1, 0xa8, 0x80, 0x7e, 0x65, 0x74, 0x9d, 0xcf, 0x4b, 0xa9, + 0xba, 0x55, 0x09, 0xfe, 0xe7, 0x1b, 0x17, 0xa8, 0xeb, 0x13, 0xfa, 0x20, + 0x25, 0x37, 0xbc, 0xe1, 0xba, 0x1c, 0x65, 0xbe, 0x3d, 0x9b, 0x78, 0xcd, + 0x03, 0xf0, 0xac, 0xfc, 0x5a, 0x98, 0x30, 0x90, 0xe0, 0x9f, 0xb2, 0x3a, + 0x8f, 0x6e, 0x76, 0x1d, 0x36, 0x35, 0xf7, 0x7f, 0xfa, 0x4d, 0xa0, 0xb5, + 0x4b, 0x0b, 0xc3, 0x45, 0xc3, 0x08, 0x50, 0xfd, 0x6f, 0x18, 0x3d, 0xa6, + 0x3e, 0x57, 0xf8, 0x51, 0x24, 0x26, 0x79, 0x4f, 0x34, 0x0b, 0x4e, 0xd2, + 0xc7, 0x4c, 0x3f, 0x3d, 0x82, 0xdf, 0x0f, 0xa2, 0x94, 0x3f, 0x2f, 0x11, + 0x30, 0x92, 0x56, 0x44, 0x05, 0x82, 0x01, 0x48, 0xfe, 0x1d, 0x5f, 0xbd, + 0x14, 0x5b, 0x27, 0x3c, 0x55, 0x09, 0x02, 0x65, 0x60, 0xad, 0x81, 0xe2, + 0xe4, 0x40, 0x7c, 0xa9, 0xec, 0x2f, 0x72, 0xed, 0x28, 0xb7, 0x4a, 0x0a, + 0x70, 0xaf, 0x14, 0x04, 0xd5, 0xb8, 0xad, 0x16, 0x5a, 0xad, 0xfd, 0xfa, + 0x5e, 0xd5, 0x39, 0x87, 0x08, 0x70, 0x23, 0xce, 0xc3, 0x41, 0xdb, 0x84, + 0x36, 0xd8, 0x9f, 0xb9, 0x2f, 0xa2, 0x89, 0xaa, 0xca, 0x33, 0xcf, 0x4b, + 0x56, 0x20, 0xc0, 0xb2, 0xf6, 0xb4, 0xdb, 0xd9, 0x6c, 0x68, 0xb4, 0xc1, + 0xe9, 0xfb, 0x6e, 0x52, 0x9a, 0x45, 0x33, 0x69, 0x30, 0x9c, 0x4b, 0x40, + 0x09, 0xab, 0xf7, 0x78, 0x1a, 0x99, 0x8e, 0xbd, 0x7c, 0xc8, 0x6c, 0xcb, + 0x3b, 0x67, 0x9a, 0x8e, 0x12, 0x6e, 0x28, 0x05, 0x1b, 0xd6, 0x68, 0xe7, + 0xdb, 0x83, 0x14, 0x16, 0xde, 0x03, 0x04, 0xb2, 0x8d, 0x82, 0x5b, 0x7b, + 0x5b, 0x73, 0xa3, 0xa2, 0xdd, 0x88, 0x44, 0x5f, 0x94, 0xd2, 0xe4, 0xd8, + 0xa5, 0x25, 0xf6, 0x5b, 0x12, 0x09, 0x39, 0x29, 0xf4, 0xa2, 0x84, 0x41, + 0x26, 0x5d, 0x92, 0x00, 0x14, 0x61, 0xd8, 0x33, 0x80, 0x35, 0x75, 0x7c, + 0xe5, 0xb7, 0xaf, 0xa9, 0xe3, 0x0c, 0x50, 0xea, 0xd6, 0xbd, 0x04, 0xc7, + 0x9b, 0x02, 0x3e, 0xb9, 0xf3, 0x24, 0x1d, 0x1f, 0xb7, 0x23, 0x8d, 0x23, + 0x17, 0xd9, 0x24, 0xe4, 0x65, 0xb9, 0x9b, 0x48, 0x5a, 0x6d, 0x24, 0xaf, + 0x66, 0xe7, 0xc6, 0x05, 0x92, 0x3e, 0x15, 0xb5, 0xf9, 0x3d, 0x89, 0xf3, + 0x5a, 0xbd, 0x89, 0x9a, 0x67, 0x40, 0x3a, 0x37, 0xa7, 0x50, 0xbe, 0x56, + 0xd2, 0x70, 0xef, 0xf8, 0x44, 0x76, 0x0b, 0x3c, 0x50, 0x17, 0xd3, 0x23, + 0x63, 0x4f, 0xa4, 0xc1, 0x77, 0x80, 0xcb, 0xa7, 0x5a, 0xe0, 0x97, 0xb3, + 0x88, 0xd5, 0x8d, 0x12, 0xe7, 0x9e, 0x97, 0x74, 0x29, 0xc9, 0xb2, 0xe0, + 0xc6, 0xe7, 0x5f, 0x07, 0x1a, 0x7d, 0xde, 0x1b, 0x56, 0xfd, 0xb2, 0xc2, + 0x83, 0x9a, 0x6b, 0x37, 0x2c, 0x43, 0x6f, 0x39, 0x22, 0x37, 0x81, 0x63, + 0xbd, 0x30, 0xac, 0x4e, 0x80, 0xaa, 0xa7, 0x30, 0x76, 0xa7, 0x6b, 0x72, + 0x72, 0xef, 0xd0, 0xde, 0x15, 0x07, 0xc7, 0xd2, 0xfd, 0x61, 0xb8, 0xd4, + 0x52, 0xd0, 0xa7, 0x46, 0x62, 0x57, 0xc0, 0xda, 0x35, 0x56, 0x1b, 0x0e, + 0x27, 0xe1, 0x4d, 0x41, 0x49, 0x36, 0x9c, 0xcb, 0xc5, 0xe5, 0x23, 0x81, + 0xbe, 0x29, 0x9b, 0x4e, 0x2c, 0x8a, 0xa3, 0x0e, 0x1f, 0x66, 0x44, 0x24, + 0x6f, 0x96, 0x4f, 0xd4, 0xf5, 0xa1, 0xea, 0x4e, 0x0d, 0xe2, 0xe6, 0xd8, + 0x7e, 0x73, 0xf4, 0x3e, 0x56, 0xbf, 0xa6, 0xa7, 0x98, 0x57, 0x88, 0xd8, + 0x68, 0x58, 0xc6, 0x58, 0x65, 0x0d, 0xf7, 0x49, 0x49, 0x37, 0x6e, 0x38, + 0xc7, 0x94, 0xb5, 0x3c, 0x29, 0x30, 0xdd, 0xcf, 0x68, 0x9d, 0x54, 0x04, + 0x6d, 0xd6, 0x43, 0x0f, 0xbd, 0x98, 0x8a, 0xd6, 0x82, 0x03, 0x1d, 0x6d, + 0xea, 0x68, 0xfa, 0xc0, 0x08, 0xd6, 0x4d, 0x52, 0x36, 0x97, 0xd1, 0x68, + 0x9c, 0xe9, 0x2b, 0x21, 0x43, 0x33, 0x7c, 0x46, 0x08, 0x52, 0xd9, 0x25, + 0x38, 0x8c, 0xbb, 0xfe, 0xa1, 0xc6, 0x90, 0xbc, 0xaa, 0x7b, 0xb7, 0x46, + 0x43, 0x59, 0x1d, 0xad, 0xb6, 0xa6, 0xcb, 0x27, 0x5c, 0xbc, 0x12, 0x6c, + 0x82, 0xd2, 0x0e, 0x7b, 0x64, 0xe6, 0x12, 0xa1, 0x0f, 0x23, 0xfe, 0x54, + 0x51, 0xb5, 0x2e, 0x17, 0x7b, 0x8b, 0x66, 0xc2, 0xd3, 0x4e, 0x6d, 0xca, + 0xa5, 0x7c, 0xcb, 0x51, 0x11, 0xc2, 0x61, 0x19, 0x56, 0x73, 0xda, 0x04, + 0x36, 0xe6, 0xd5, 0x38, 0x9e, 0x86, 0x1a, 0x82, 0xd4, 0x9f, 0x13, 0xad, + 0x54, 0x0f, 0x53, 0xf9, 0x59, 0x57, 0xe6, 0x45, 0xc8, 0x96, 0xb5, 0xd7, + 0x36, 0x4e, 0xc7, 0xad, 0xc5, 0xdf, 0xa8, 0xb0, 0x32, 0x77, 0x56, 0x43, + 0x31, 0xd7, 0x28, 0xc3, 0xf2, 0x6d, 0x53, 0x2c, 0x33, 0x7d, 0x6c, 0x0b, + 0xbf, 0x0b, 0xf5, 0x60, 0xad, 0x17, 0x5b, 0xc9, 0x48, 0x15, 0x9a, 0x4e, + 0x3e, 0x75, 0x09, 0xab, 0x6a, 0x89, 0x1e, 0x12, 0x29, 0xf6, 0xc3, 0x1b, + 0x48, 0xb7, 0xf6, 0x28, 0xeb, 0x05, 0x97, 0xdd, 0x0d, 0x45, 0xfc, 0x9a, + 0x71, 0x3e, 0x58, 0x35, 0xd6, 0x79, 0x42, 0xa0, 0xac, 0x97, 0x2b, 0x2d, + 0x62, 0x8a, 0xa7, 0xe1, 0x45, 0x6e, 0x1f, 0xf6, 0x11, 0xa2, 0x58, 0xbe, + 0xfc, 0xb7, 0x94, 0xe7, 0x2b, 0x20, 0xc9, 0x7d, 0xb6, 0xb9, 0x93, 0x43, + 0x5e, 0x20, 0xa8, 0x2e, 0x79, 0x8e, 0xd8, 0x26, 0xc0, 0x5e, 0x46, 0xcd, + 0xaa, 0x6d, 0xb6, 0x6c, 0xa4, 0x77, 0xc6, 0x1f, 0x13, 0x82, 0x20, 0x8c, + 0x4a, 0x72, 0xcd, 0x35, 0xd3, 0x65, 0x98, 0xe8, 0xd8, 0xaa, 0xd1, 0x68, + 0x0a, 0x32, 0x00, 0xad, 0xac, 0xe1, 0x69, 0x51, 0x94, 0xe0, 0xbf, 0x7e, + 0xb6, 0x95, 0xb8, 0xba, 0xe6, 0xdf, 0xff, 0x7a, 0xf0, 0x68, 0xb9, 0x15, + 0x7f, 0x85, 0xe7, 0x0f, 0xff, 0x80, 0xbc, 0x60, 0xf6, 0x8c, 0x66, 0xf9, + 0x3e, 0x0b, 0x91, 0x14, 0x20, 0x2a, 0xd3, 0x88, 0x67, 0x8b, 0xdc, 0x8f, + 0x4e, 0x0a, 0xe9, 0x06, 0x88, 0x76, 0x5f, 0x93, 0xdb, 0x51, 0x4b, 0xa4, + 0x93, 0x64, 0x55, 0xc7, 0x1a, 0xcc, 0x9f, 0x7d, 0x0e, 0x53, 0x22, 0x31, + 0xa5, 0x74, 0x24, 0x4b, 0x2c, 0xf0, 0x22, 0x02, 0xa3, 0x2a, 0xe4, 0x2b, + 0x1a, 0x74, 0x8a, 0xf9, 0x25, 0x9e, 0x60, 0x1a, 0x4f, 0x6e, 0x2d, 0x0a, + 0x77, 0x1e, 0x36, 0x20, 0x22, 0x30, 0x93, 0xc7, 0x65, 0xd0, 0xe5, 0x94, + 0x8c, 0x3c, 0x0b, 0x21, 0x97, 0xd9, 0x0f, 0x08, 0x62, 0x0c, 0x44, 0x41, + 0x4f, 0xd7, 0xee, 0x41, 0xc4, 0xa2, 0xdb, 0xf7, 0x3b, 0x30, 0x70, 0x66, + 0x67, 0xa5, 0x54, 0xaf, 0x6d, 0x21, 0x48, 0x50, 0xc0, 0x57, 0x08, 0xf5, + 0x18, 0xb9, 0xf8, 0xaf, 0x26, 0x33, 0x29, 0x5d, 0x6f, 0x39, 0x63, 0x21, + 0x66, 0x49, 0x48, 0x51, 0xe9, 0xc7, 0xa4, 0x22, 0xa2, 0x59, 0x37, 0x80, + 0x81, 0x45, 0x21, 0x44, 0xb5, 0x7f, 0x61, 0x07, 0xfd, 0xb5, 0xde, 0xbc, + 0xff, 0xd2, 0x25, 0x94, 0x2f, 0x2c, 0xaa, 0xf0, 0x48, 0xe8, 0x21, 0xc5, + 0x9d, 0xde, 0x7a, 0xd5, 0x03, 0x6b, 0x3d, 0x34, 0xfc, 0x60, 0x82, 0xf3, + 0xe9, 0x7b, 0x46, 0xf7, 0x8c, 0x0b, 0x55, 0xdf, 0x66, 0x83, 0xa1, 0xde, + 0x03, 0xda, 0xf2, 0x95, 0xc3, 0x96, 0xfc, 0x06, 0xd1, 0x6e, 0xe0, 0x4b, + 0x6e, 0xd5, 0xae, 0x74, 0xad, 0x59, 0x19, 0x6d, 0xb6, 0x8b, 0xa1, 0xfa, + 0x94, 0x17, 0x00, 0x65, 0x7e, 0xe0, 0xea, 0xa0, 0x56, 0x52, 0x44, 0xc2, + 0x68, 0x86, 0xbe, 0x55, 0xd3, 0x79, 0xb4, 0xb2, 0x80, 0x80, 0xa4, 0xee, + 0x1c, 0x23, 0xe9, 0xbd, 0x1b, 0x23, 0x3a, 0x30, 0xbd, 0x1a, 0x4c, 0x96, + 0x0f, 0xfd, 0x34, 0xe5, 0xa4, 0x08, 0x73, 0x72, 0xa4, 0x73, 0x03, 0xcf, + 0xbd, 0x8c, 0x47, 0x81, 0xc8, 0x8e, 0xa3, 0x21, 0xde, 0x07, 0x00, 0x3b, + 0x5d, 0x4d, 0x1f, 0x6b, 0xb0, 0xf9, 0x78, 0xbf, 0xe3, 0xaf, 0xd6, 0xda, + 0x35, 0x79, 0x0d, 0x52, 0xa3, 0x23, 0x18, 0x25, 0x3d, 0x85, 0xf0, 0x01, + 0xc2, 0x93, 0xbf, 0xdb, 0x43, 0xb2, 0x19, 0x5a, 0xce, 0xd4, 0x3c, 0xbe, + 0x00, 0x94, 0x71, 0xc7, 0xb0, 0x5c, 0xa1, 0xdb, 0x04, 0x4e, 0x89, 0x9f, + 0x53, 0x06, 0xcc, 0x1a, 0xba, 0x82, 0x88, 0x8b, 0xd1, 0x30, 0x3e, 0x03, + 0x59, 0x2d, 0xef, 0x8f, 0x1f, 0x4b, 0x6b, 0x5b, 0xa7, 0xa3, 0xb6, 0xfd, + 0xa9, 0x16, 0x1b, 0xec, 0x70, 0xa8, 0xcb, 0x37, 0x4f, 0x76, 0x6d, 0x5b, + 0x58, 0xf2, 0xcf, 0x02, 0x15, 0x54, 0xa2, 0xd3, 0x86, 0xbf, 0x43, 0x22, + 0xcc, 0x80, 0xfe, 0x95, 0xfd, 0x6b, 0x48, 0xec, 0x10, 0xc8, 0x2a, 0x73, + 0x7d, 0xff, 0xa9, 0xe4, 0x05, 0x64, 0x50, 0xb7, 0xb9, 0x43, 0xbb, 0xa7, + 0xee, 0x6e, 0x1e, 0x99, 0xee, 0x5b, 0x19, 0x29, 0x0d, 0x57, 0x29, 0xe9, + 0xc5, 0x81, 0x92, 0x33, 0x92, 0xfc, 0xdd, 0x80, 0x4e, 0x9d, 0x7f, 0x5a, + 0xd1, 0xbe, 0x02, 0x23, 0x2e, 0x54, 0x17, 0x51, 0x6f, 0xb5, 0x7e, 0xd4, + 0xc4, 0x88, 0xea, 0x38, 0xaf, 0x38, 0xfc, 0xf0, 0x98, 0x2f, 0x00, 0xef, + 0x38, 0x35, 0x29, 0x72, 0xc6, 0xf5, 0x0b, 0xfb, 0xa8, 0x5a, 0xe3, 0x53, + 0xa7, 0xb5, 0x3f, 0x3d, 0x5b, 0xdd, 0xfa, 0x43, 0xe6, 0x7a, 0x23, 0x0e, + 0x43, 0x30, 0x8d, 0x11, 0x02, 0x99, 0x1e, 0x0c, 0x41, 0xd4, 0x07, 0x8d, + 0xaa, 0x03, 0x15, 0x10, 0x06, 0xe2, 0xfc, 0x58, 0x5b, 0x07, 0x5b, 0x3c, + 0x41, 0x84, 0xa4, 0xd2, 0xcc, 0x29, 0xe9, 0xd5, 0xa5, 0xa2, 0xc1, 0x72, + 0xa3, 0x70, 0x43, 0x48, 0x7d, 0x43, 0x64, 0x76, 0x5f, 0xb9, 0x24, 0xe0, + 0x79, 0x8c, 0xf8, 0x21, 0xbb, 0xfe, 0x2d, 0xbe, 0xf3, 0xc3, 0xdb, 0x2a, + 0xfb, 0x77, 0xac, 0x6d, 0xc9, 0xd1, 0x0b, 0xba, 0x8c, 0x91, 0x1e, 0xf4, + 0x9b, 0x73, 0x1e, 0x77, 0x50, 0x29, 0x0a, 0xf2, 0x3d, 0x36, 0x71, 0x9e, + 0xc9, 0xc9, 0x27, 0x5d, 0x8a, 0x12, 0xca, 0x78, 0x51, 0x77, 0xfc, 0x97, + 0xfe, 0xf4, 0x12, 0xce, 0x15, 0xfd, 0x9f, 0xfb, 0x86, 0x3b, 0x5b, 0xbe, + 0x17, 0xa4, 0x80, 0x92, 0x25, 0x41, 0x0b, 0x7a, 0x1d, 0xb5, 0x55, 0x14, + 0xb3, 0x86, 0x3c, 0x1b, 0x9f, 0xc8, 0x7d, 0xbb, 0xaf, 0x00, 0xad, 0x9e, + 0x89, 0x45, 0x7d, 0xfd, 0xc3, 0x65, 0xd1, 0x8d, 0xf2, 0xd6, 0x7b, 0x0f, + 0xea, 0xa9, 0xd3, 0x5d, 0xe8, 0x7e, 0xe2, 0xf2, 0xab, 0x22, 0xa8, 0x9d, + 0x6a, 0x62, 0x65, 0x67, 0x3d, 0x67, 0xf9, 0xdd, 0x53, 0x3f, 0x52, 0x80, + 0xb3, 0xb7, 0x28, 0x12, 0xac, 0x37, 0x7a, 0x6a, 0x49, 0x22, 0x08, 0x2f, + 0x52, 0xbf, 0x6e, 0xec, 0xef, 0xd7, 0x38, 0xc5, 0x59, 0xce, 0x91, 0xfc, + 0xc9, 0x5c, 0x45, 0x46, 0xf3, 0xee, 0xea, 0xae, 0xc6, 0x49, 0x79, 0xf0, + 0x24, 0x35, 0x71, 0x6f, 0xbe, 0x61, 0xab, 0x9c, 0xc8, 0x66, 0xd4, 0x7d, + 0xb0, 0x78, 0xa2, 0x72, 0xad, 0x8a, 0x48, 0x8e, 0xbb, 0x12, 0xa6, 0x7f, + 0xc8, 0x15, 0xec, 0xf4, 0xd6, 0xcd, 0xfc, 0x7b, 0xdf, 0xe1, 0x57, 0xb2, + 0x47, 0x9d, 0x5f, 0x91, 0x08, 0x92, 0xbb, 0x4d, 0x3d, 0x80, 0x96, 0xf8, + 0xb4, 0xf3, 0x6c, 0x3b, 0x8f, 0xb3, 0x68, 0xe2, 0xf2, 0xed, 0xc5, 0x79, + 0xed, 0x7b, 0x6b, 0xcc, 0xe4, 0x26, 0x24, 0x93, 0x87, 0xd8, 0x5f, 0x96, + 0x29, 0x60, 0x42, 0x5c, 0x2e, 0x56, 0xab, 0xad, 0x97, 0x16, 0xab, 0x8b, + 0x75, 0x49, 0xd2, 0xdd, 0xe5, 0xbf, 0xd0, 0x54, 0x83, 0x72, 0x6b, 0x1f, + 0x05, 0x96, 0x9b, 0x1c, 0xac, 0x6f, 0x62, 0xd1, 0x0b, 0xfb, 0x23, 0x21, + 0x98, 0x31, 0x11, 0x6e, 0xf6, 0x6d, 0x77, 0x7f, 0x61, 0x38, 0xc1, 0x4e, + 0x85, 0x83, 0x90, 0xc9, 0x2c, 0x4e, 0x79, 0x6e, 0x28, 0x26, 0xc0, 0xfd, + 0x82, 0xcb, 0xe0, 0x70, 0xbf, 0x4b, 0x0f, 0x31, 0x36, 0xef, 0x93, 0xfd, + 0xf8, 0xf8, 0xa4, 0x25, 0x89, 0x19, 0xcd, 0x65, 0x7f, 0x70, 0x42, 0xe2, + 0xe4, 0xd0, 0x9c, 0x0d, 0x9d, 0xae, 0xab, 0x9f, 0xa4, 0x78, 0x71, 0x48, + 0xdf, 0x3a, 0x6c, 0x1f, 0xad, 0x27, 0x0d, 0x81, 0x4a, 0x0f, 0x3f, 0xcb, + 0x17, 0x1e, 0x89, 0x6c, 0x97, 0x3d, 0xa6, 0x2a, 0x73, 0xff, 0x8a, 0x42, + 0xeb, 0x54, 0x5e, 0xb8, 0xe0, 0x4d, 0x17, 0x58, 0xf3, 0x68, 0xaa, 0xae, + 0x08, 0xef, 0x65, 0x51, 0xfa, 0xa1, 0x92, 0x5d, 0x10, 0x00, 0x75, 0x07, + 0xa9, 0x1f, 0x05, 0xca, 0x58, 0x14, 0xad, 0x7e, 0x36, 0xe7, 0x51, 0xe2, + 0xb6, 0x01, 0x0c, 0xe1, 0xbe, 0x0c, 0x84, 0x88, 0x67, 0x04, 0x40, 0xdf, + 0x70, 0xe0, 0xda, 0x47, 0xc3, 0x1b, 0x20, 0x09, 0x08, 0x8b, 0xf2, 0x6f, + 0x27, 0x1a, 0x19, 0xd7, 0x84, 0x64, 0x58, 0xba, 0xaa, 0xe3, 0xbd, 0xea, + 0xb8, 0xb1, 0xb4, 0x6a, 0x45, 0x94, 0x32, 0xf2, 0x0e, 0x54, 0x9b, 0xb6, + 0xc6, 0xc0, 0x35, 0x9b, 0xb1, 0xdf, 0x60, 0x42, 0xfb, 0x7e, 0xc4, 0x39, + 0x62, 0x9b, 0x99, 0x00, 0x24, 0xa1, 0x95, 0x1f, 0x75, 0x89, 0x34, 0x98, + 0x31, 0x5a, 0xf3, 0x7b, 0x70, 0x1a, 0xdb, 0xc4, 0x46, 0x27, 0xc1, 0x88, + 0xf4, 0x7e, 0x96, 0xcf, 0x39, 0x86, 0xc2, 0x10, 0xf7, 0xb7, 0x73, 0x16, + 0xb0, 0xc2, 0x31, 0x2f, 0xfe, 0x79, 0x0f, 0xd2, 0x2b, 0x34, 0xd5, 0x43, + 0x64, 0x61, 0x2b, 0xed, 0x9b, 0x6a, 0x17, 0x75, 0x1f, 0xa9, 0xdd, 0x10, + 0xf2, 0x28, 0x49, 0xba, 0x9d, 0x3b, 0x8c, 0x65, 0x07, 0x84, 0x9d, 0x93, + 0x6e, 0x9b, 0x47, 0x0f, 0x0d, 0x0f, 0xa7, 0xb0, 0xf6, 0xeb, 0x89, 0x12, + 0x4e, 0xc5, 0x04, 0x8c, 0xf7, 0x86, 0x13, 0x19, 0xff, 0xfc, 0x44, 0x4f, + 0x33, 0xb1, 0x76, 0xe3, 0x44, 0xc5, 0x4e, 0x89, 0x02, 0x40, 0x79, 0x7a, + 0x48, 0xde, 0x41, 0xb9, 0x1c, 0xc6, 0x93, 0xa1, 0x8b, 0xe7, 0x93, 0x6a, + 0x65, 0xf0, 0x66, 0xfa, 0x38, 0x0d, 0xbe, 0xc0, 0x42, 0x09, 0xf0, 0x75, + 0x01, 0xbf, 0x02, 0x05, 0x7e, 0x36, 0x44, 0x8e, 0x28, 0x58, 0xe5, 0x93, + 0xeb, 0xec, 0x83, 0xb0, 0x0a, 0xf8, 0x75, 0xf3, 0xc4, 0x5a, 0x82, 0xfa, + 0x8a, 0x87, 0x0c, 0x39, 0xfb, 0x32, 0xf5, 0xb3, 0x87, 0x74, 0xe1, 0xc2, + 0x76, 0x5f, 0xe0, 0x29, 0x63, 0xcc, 0xd9, 0x9e, 0x54, 0x06, 0xa2, 0x69, + 0x7d, 0xf7, 0x92, 0x7a, 0xc0, 0xb9, 0x38, 0xd2, 0xc3, 0xa9, 0x26, 0x85, + 0xa2, 0x97, 0x0a, 0xeb, 0x93, 0x3e, 0xf8, 0xa4, 0xa1, 0x74, 0xe6, 0x67, + 0xfa, 0x42, 0x03, 0x35, 0x50, 0x60, 0x68, 0x8f, 0x1e, 0x63, 0x70, 0x6a, + 0x43, 0xc8, 0xc8, 0xef, 0xfd, 0xf2, 0x4a, 0xbe, 0x6c, 0x86, 0xfd, 0x0e, + 0xbb, 0x63, 0xc0, 0x47, 0x32, 0x02, 0x45, 0x2a, 0x4e, 0xab, 0x85, 0x5d, + 0x37, 0x87, 0x6b, 0x2d, 0x15, 0x50, 0x7e, 0x33, 0xbe, 0x47, 0x51, 0xba, + 0x2a, 0x08, 0x09, 0xe6, 0xbf, 0x40, 0xc2, 0x2f, 0xb0, 0x3c, 0xef, 0x10, + 0xec, 0x4c, 0x4b, 0xe8, 0xee, 0xa6, 0x40, 0xc8, 0x48, 0xe6, 0xab, 0x11, + 0x0f, 0x5a, 0x8f, 0x35, 0x4d, 0xaa, 0x9b, 0xb6, 0x94, 0xd1, 0x9c, 0xbe, + 0xdd, 0xa3, 0x69, 0xa2, 0x53, 0x44, 0x6a, 0x95, 0xd9, 0xfa, 0x3f, 0x8f, + 0xfc, 0x33, 0x74, 0xbc, 0x3e, 0x7d, 0xeb, 0x2f, 0x65, 0x17, 0xf3, 0xf2, + 0xd7, 0x18, 0xa8, 0xc5, 0x22, 0xf0, 0x55, 0xa8, 0x25, 0x17, 0xf2, 0x57, + 0x73, 0xd7, 0x99, 0xec, 0xdf, 0xb2, 0x39, 0xb9, 0x2e, 0xbe, 0x15, 0x36, + 0xbf, 0xd6, 0x29, 0x8e, 0xdf, 0xa0, 0xf5, 0x9b, 0x29, 0x7a, 0xf8, 0xe5, + 0xf9, 0x59, 0x10, 0xbd, 0x87, 0x28, 0xbe, 0x61, 0xea, 0x32, 0x12, 0x7f, + 0xab, 0xef, 0xf0, 0x2a, 0x0c, 0x2c, 0x88, 0xe5, 0x90, 0xe6, 0xea, 0x44, + 0x36, 0x39, 0xc4, 0x3f, 0x4c, 0x2c, 0x8f, 0x04, 0x85, 0xf9, 0x34, 0x13, + 0x4a, 0x8b, 0xa1, 0x10, 0xf8, 0x44, 0xaf, 0x6b, 0xb2, 0x19, 0x83, 0x5a, + 0x8f, 0x42, 0xd8, 0x10, 0x31, 0x53, 0xe1, 0xe8, 0x93, 0xe9, 0x89, 0x6e, + 0x47, 0x89, 0xc0, 0xf4, 0x0e, 0xb6, 0xad, 0xb8, 0x03, 0x51, 0x0a, 0x7f, + 0xba, 0x1f, 0xab, 0x53, 0xed, 0x70, 0x02, 0x06, 0x51, 0x04, 0x4f, 0xeb, + 0x75, 0xbe, 0xca, 0x9c, 0x11, 0xff, 0x1d, 0xd6, 0xc0, 0xd1, 0xa8, 0x5b, + 0xc5, 0x8d, 0x76, 0xb5, 0xbf, 0x29, 0xf7, 0x08, 0x26, 0x68, 0x50, 0x73, + 0x95, 0x93, 0x28, 0xc0, 0x83, 0x16, 0xf1, 0x8c, 0xa6, 0x51, 0x56, 0xcd, + 0xbe, 0x89, 0x13, 0xa6, 0xbf, 0x0d, 0x75, 0xd1, 0xa5, 0x94, 0xa9, 0xfe, + 0xf9, 0x24, 0x61, 0x87, 0xa6, 0x34, 0x73, 0xf3, 0xe8, 0xcc, 0x66, 0xa1, + 0xb4, 0x63, 0x6b, 0x27, 0xa4, 0x4e, 0x61, 0x30, 0xb5, 0xa8, 0x23, 0x54, + 0xb0, 0x6d, 0x3b, 0x69, 0x23, 0x7b, 0xb4, 0x04, 0xce, 0x16, 0x77, 0x0d, + 0xf3, 0xc8, 0xfe, 0x84, 0xc8, 0xe9, 0x82, 0xdd, 0x18, 0x1e, 0x60, 0x31, + 0x55, 0x91, 0xc5, 0xa3, 0x56, 0xee, 0x00, 0xd5, 0x74, 0x29, 0x17, 0x2a, + 0xfb, 0xbc, 0x80, 0xd3, 0x89, 0xf9, 0xa1, 0x42, 0xc2, 0xe4, 0x40, 0x57, + 0x91, 0x7e, 0x83, 0xe1, 0x34, 0x59, 0xb6, 0x9a, 0x37, 0xb3, 0x28, 0xc8, + 0x90, 0xb8, 0xe1, 0x2b, 0x92, 0x9d, 0x38, 0xe7, 0x0a, 0x4e, 0x81, 0x64, + 0x51, 0x8b, 0x8e, 0x10, 0xa6, 0x08, 0x12, 0xef, 0x9d, 0x6b, 0x0c, 0x17, + 0x32, 0xcc, 0x5a, 0x4d, 0xbe, 0x75, 0x6d, 0x15, 0xdd, 0xab, 0x96, 0x2a, + 0x7d, 0x6d, 0x84, 0x34, 0xe8, 0xd7, 0xc8, 0xfc, 0x74, 0xe0, 0x1a, 0x2c, + 0x39, 0xdd, 0xed, 0x88, 0x8e, 0x73, 0x02, 0xd0, 0x47, 0x4c, 0x5c, 0xfe, + 0x33, 0x12, 0xbc, 0x0f, 0xd3, 0x9d, 0x6d, 0x31, 0xff, 0xb2, 0xf9, 0x05, + 0xac, 0x95, 0xbc, 0x45, 0x34, 0xde, 0x72, 0x82, 0x1c, 0x14, 0x27, 0x1e, + 0xfa, 0x4d, 0x88, 0xa8, 0x1a, 0x67, 0x60, 0x8e, 0xfa, 0xf7, 0x73, 0x15, + 0xf1, 0x27, 0x25, 0x99, 0xaf, 0x84, 0x3f, 0x87, 0x15, 0x11, 0xbf, 0x83, + 0xd9, 0xc8, 0x69, 0xd3, 0xcf, 0x06, 0x0d, 0x21, 0x50, 0x83, 0x60, 0xf2, + 0x14, 0x80, 0x7e, 0x83, 0x03, 0x3f, 0xee, 0x7a, 0xc8, 0x0f, 0xe2, 0xfc, + 0x3a, 0x10, 0x56, 0x39, 0xe3, 0x7d, 0x63, 0xd5, 0x7c, 0x4d, 0xb3, 0x3f, + 0x39, 0xaf, 0xf9, 0x5f, 0x7b, 0xa0, 0xe1, 0xcf, 0xf5, 0x56, 0x23, 0x08, + 0x9c, 0x41, 0x90, 0x6b, 0xfe, 0xac, 0x17, 0x4c, 0xc0, 0xc0, 0x9b, 0xfe, + 0xad, 0x1b, 0xb8, 0x78, 0xf6, 0x20, 0xdf, 0xc4, 0x9d, 0xc0, 0xf1, 0xb2, + 0xc2, 0x8e, 0x65, 0x9f, 0xb4, 0xf0, 0xdf, 0xa2, 0x32, 0x2e, 0x81, 0xe8, + 0x6f, 0x30, 0x9f, 0x67, 0xc3, 0xb6, 0x72, 0x65, 0x26, 0x56, 0x43, 0x66, + 0x1f, 0x6b, 0x9e, 0xc6, 0xec, 0xc2, 0x47, 0x7d, 0xd5, 0x9f, 0x96, 0x6e, + 0xfe, 0x50, 0x71, 0xb0, 0xce, 0x64, 0x25, 0xaa, 0xfe, 0x7c, 0xdb, 0xb0, + 0x98, 0x74, 0xfe, 0xa3, 0xb8, 0x90, 0x47, 0x78, 0x42, 0x7b, 0x78, 0xfe, + 0xf7, 0x2a, 0x83, 0xdd, 0xbd, 0x18, 0x2b, 0x19, 0xef, 0x55, 0x9f, 0x3e, + 0xf6, 0x89, 0xfd, 0x44, 0x11, 0x3e, 0xc9, 0xbf, 0xab, 0x4e, 0x05, 0x1f, + 0x45, 0xf5, 0xa3, 0x60, 0xdc, 0x0e, 0x49, 0x25, 0x0f, 0xea, 0x01, 0x24, + 0x22, 0x69, 0x67, 0x76, 0xfb, 0x8d, 0x33, 0x62, 0xb6, 0x97, 0x2d, 0x97, + 0x6f, 0xe7, 0xce, 0xad, 0x84, 0xa0, 0x94, 0xfe, 0x43, 0xa3, 0xf6, 0x4f, + 0xbc, 0xd1, 0x76, 0x2a, 0x4f, 0x65, 0x87, 0x58, 0xf1, 0xbd, 0x9b, 0xff, + 0x9d, 0x35, 0xa2, 0x5a, 0xee, 0x45, 0x0c, 0x35, 0x20, 0x59, 0xa1, 0x24, + 0xd8, 0xfc, 0x74, 0x80, 0x1f, 0xcd, 0x41, 0x78, 0xf8, 0x60, 0xcc, 0xc4, + 0x54, 0x15, 0xcb, 0x33, 0x1d, 0xd8, 0xef, 0xba, 0xa0, 0x3a, 0x4a, 0xfa, + 0x69, 0xb8, 0x82, 0x86, 0x3e, 0x3f, 0x01, 0xa3, 0x94, 0xc4, 0x42, 0x4c, + 0xf3, 0x5b, 0x3b, 0xe9, 0x76, 0xbc, 0x48, 0xc0, 0xae, 0xbc, 0x2c, 0x67, + 0x6e, 0x93, 0x31, 0x2b, 0x2b, 0x67, 0xd0, 0x22, 0x4b, 0x47, 0x35, 0x5d, + 0xff, 0x9e, 0x4b, 0xfa, 0x54, 0x6d, 0xf3, 0xf5, 0x39, 0x5a, 0x7c, 0x05, + 0x0b, 0x10, 0x28, 0x64, 0x0c, 0xfb, 0xd8, 0x75, 0x34, 0x23, 0x37, 0xda, + 0x82, 0x97, 0x14, 0x5d, 0x27, 0x03, 0xf4, 0xb8, 0xc4, 0x72, 0xd1, 0x51, + 0x8d, 0x5f, 0xd7, 0xba, 0x60, 0x50, 0x94, 0x1c, 0x05, 0xf3, 0x63, 0x61, + 0xb2, 0x1d, 0x64, 0xd0, 0xa7, 0x5c, 0xf1, 0x35, 0x59, 0xfe, 0xed, 0xac, + 0x66, 0x5e, 0x77, 0x5f, 0x68, 0xee, 0x3e, 0xd4, 0x7f, 0xe3, 0x8c, 0xa9, + 0x9e, 0xd2, 0xb6, 0x66, 0x07, 0xdb, 0xe5, 0xd2, 0x14, 0x5f, 0x28, 0x51, + 0xa9, 0xa3, 0xec, 0x93, 0x3a, 0xbc, 0x67, 0xa0, 0xa1, 0x76, 0x98, 0x9b, + 0xbc, 0x0a, 0x35, 0x0d, 0xb6, 0x95, 0x87, 0x17, 0x12, 0x58, 0x3b, 0x28, + 0x24, 0x56, 0x7e, 0x9c, 0x4f, 0xa9, 0x2e, 0xb1, 0xe0, 0x65, 0x5b, 0x3f, + 0x18, 0x2f, 0x92, 0x0a, 0x01, 0x1d, 0x82, 0x92, 0x8b, 0xdf, 0xbe, 0xe3, + 0x57, 0xac, 0xb5, 0xf0, 0x21, 0x7e, 0x13, 0xc6, 0xa6, 0xe8, 0x2e, 0x64, + 0xcd, 0x9a, 0x11, 0x2a, 0xfe, 0x07, 0x46, 0x4b, 0xd8, 0x6b, 0x35, 0x34, + 0x8e, 0x48, 0xce, 0xc6, 0x11, 0x18, 0x68, 0x3d, 0x85, 0xfa, 0x20, 0xf7, + 0xfc, 0x41, 0x34, 0xe4, 0x62, 0x80, 0xf8, 0x6b, 0xe6, 0x13, 0x8c, 0x22, + 0x61, 0x15, 0x8e, 0xd7, 0x21, 0xbb, 0x0b, 0xdd, 0x25, 0xd6, 0x97, 0x07, + 0x21, 0x53, 0x93, 0xa2, 0xab, 0x97, 0x37, 0x96, 0x31, 0x73, 0x09, 0x33, + 0xc0, 0x34, 0x22, 0xc7, 0x16, 0x76, 0x3e, 0x88, 0xf0, 0xc5, 0xb4, 0x2f, + 0x83, 0xd1, 0x46, 0x95, 0x35, 0x31, 0xb0, 0x16, 0xbf, 0x15, 0xba, 0x5b, + 0xb9, 0x92, 0x63, 0x05, 0x42, 0xa7, 0x06, 0x52, 0xf7, 0xb3, 0x5e, 0x17, + 0xd5, 0xef, 0x94, 0x41, 0x38, 0x06, 0x09, 0x0a, 0xe8, 0xc7, 0x3a, 0xac, + 0x06, 0x04, 0xb6, 0xef, 0x40, 0x4a, 0x8c, 0xce, 0xe5, 0x3a, 0x21, 0xd9, + 0x6d, 0x98, 0xbf, 0x4c, 0x41, 0xbf, 0xb9, 0xef, 0x9b, 0xfd, 0x2d, 0xac, + 0x1f, 0x3f, 0xa8, 0x63, 0x60, 0x44, 0xd2, 0x08, 0xe9, 0xac, 0x18, 0x94, + 0x40, 0xb8, 0x56, 0xc4, 0x81, 0x26, 0x30, 0x9a, 0x67, 0x39, 0x37, 0xbf, + 0x64, 0x47, 0xbf, 0xd6, 0x88, 0xae, 0x93, 0x30, 0xa2, 0xc4, 0xb6, 0xa4, + 0x1d, 0xfa, 0x6e, 0x33, 0x83, 0x5d, 0x62, 0x7a, 0x66, 0xb6, 0x3d, 0xbc, + 0x35, 0x7b, 0x01, 0xba, 0x11, 0xac, 0x60, 0x07, 0xc3, 0xc9, 0x12, 0xbf, + 0x3b, 0xb3, 0x4d, 0x3e, 0x98, 0xd9, 0xa1, 0x86, 0x5a, 0x4b, 0x7c, 0x35, + 0x84, 0x9f, 0x29, 0x60, 0xe1, 0xbd, 0xd5, 0x66, 0x32, 0x03, 0xf2, 0x4f, + 0x31, 0xb5, 0xaa, 0x8a, 0xef, 0xd9, 0x51, 0x29, 0xe9, 0x37, 0x9a, 0x36, + 0x29, 0x18, 0xee, 0xe8, 0x17, 0x14, 0xf3, 0x39, 0x1d, 0x4a, 0xb7, 0x56, + 0x18, 0x63, 0x9c, 0x76, 0xe4, 0x75, 0xeb, 0xe7, 0xe8, 0x7c, 0xcf, 0xae, + 0x84, 0xa2, 0xba, 0x3c, 0x3c, 0x02, 0x86, 0xb9, 0x1f, 0x7c, 0x5e, 0xaf, + 0x32, 0x66, 0x62, 0x37, 0x47, 0x9e, 0x70, 0x43, 0xde, 0xb8, 0xe1, 0x94, + 0xc7, 0xc4, 0x31, 0x74, 0x62, 0x22, 0x94, 0xc3, 0x20, 0xfa, 0x93, 0x39, + 0xdd, 0x0a, 0xd3, 0xb4, 0x03, 0xd3, 0x2d, 0x09, 0x19, 0xa5, 0x98, 0xd7, + 0xa4, 0xf4, 0xca, 0x54, 0xd1, 0x27, 0xad, 0x57, 0x40, 0xfc, 0xf4, 0x2d, + 0x9f, 0x3a, 0x89, 0x94, 0x84, 0x7c, 0xee, 0xee, 0x32, 0x13, 0x4b, 0x27, + 0x35, 0xbf, 0x8f, 0x3d, 0x62, 0xf3, 0x7b, 0x4f, 0x9f, 0x12, 0xdd, 0xc4, + 0xb2, 0xe3, 0x84, 0x4d, 0x22, 0x4f, 0x61, 0xf1, 0x45, 0xbb, 0x7e, 0xb1, + 0xe1, 0xa0, 0xe7, 0xf4, 0xfd, 0x18, 0x27, 0x28, 0x3b, 0xfa, 0xd0, 0xf7, + 0xe9, 0xd2, 0xfa, 0x85, 0x7d, 0xfa, 0x5f, 0x86, 0xd3, 0x10, 0xca, 0xde, + 0x88, 0x9f, 0x6a, 0x4e, 0xee, 0x3c, 0x4c, 0xa2, 0x27, 0xff, 0x85, 0x58, + 0x8e, 0x80, 0x08, 0x3a, 0x7e, 0xff, 0x1a, 0x41, 0xc2, 0x1f, 0x7e, 0xd6, + 0xaf, 0x83, 0xce, 0xaa, 0x75, 0xdb, 0xa6, 0x99, 0x3c, 0xa0, 0xd8, 0x6d, + 0xfe, 0x97, 0x60, 0x5b, 0x3d, 0x8a, 0x8e, 0xb1, 0xc2, 0xe9, 0x43, 0x85, + 0x49, 0xce, 0xa0, 0x36, 0x41, 0x93, 0xb9, 0x21, 0x15, 0x20, 0xf6, 0x4b, + 0x3e, 0xbd, 0xee, 0x47, 0x00, 0xd0, 0x9b, 0x99, 0x7a, 0xaf, 0xaa, 0xd5, + 0xf7, 0xd0, 0x79, 0x7e, 0x3f, 0x27, 0x18, 0x6d, 0xeb, 0x03, 0x5c, 0x0c, + 0xef, 0xc0, 0xf2, 0xde, 0xb5, 0x34, 0x00, 0x87, 0x7b, 0x8c, 0x02, 0x09, + 0x3c, 0xf3, 0x28, 0x21, 0x5d, 0x9e, 0x55, 0x1b, 0x85, 0xda, 0x4c, 0x1c, + 0xfc, 0x19, 0x70, 0x9a, 0x63, 0xef, 0x1d, 0xa0, 0x45, 0x0e, 0x99, 0x66, + 0x7e, 0xc7, 0x7e, 0x89, 0x07, 0x5e, 0x4c, 0x57, 0x60, 0xd1, 0xb2, 0x4b, + 0xa6, 0x49, 0x54, 0x8d, 0x66, 0x10, 0x4f, 0x50, 0xb1, 0xc1, 0x98, 0x7a, + 0xaa, 0x49, 0x39, 0x30, 0xa7, 0xe5, 0x09, 0xac, 0x4f, 0x09, 0x53, 0xc2, + 0x34, 0xd2, 0xb2, 0x03, 0xba, 0x33, 0x9a, 0x2f, 0x14, 0x47, 0x33, 0x16, + 0xdf, 0xd3, 0xed, 0x54, 0xb9, 0x98, 0x5f, 0x0b, 0x45, 0xa5, 0xe4, 0x98, + 0xb4, 0xa2, 0xbc, 0xaf, 0x68, 0x35, 0xa4, 0x4b, 0x8f, 0xc4, 0x14, 0x53, + 0x6d, 0x30, 0x8f, 0x84, 0x1b, 0xc5, 0xeb, 0x5f, 0x87, 0xd7, 0x02, 0x7e, + 0x62, 0x72, 0x3d, 0xeb, 0x71, 0x5a, 0xa7, 0x07, 0xbb, 0x32, 0x4f, 0xf1, + 0x61, 0x3f, 0xc5, 0x39, 0x42, 0x2c, 0x2a, 0xf8, 0x5c, 0x3b, 0x7d, 0x6f, + 0x71, 0x6e, 0x64, 0xf7, 0xe5, 0xc5, 0x66, 0x1b, 0xc3, 0x3d, 0xdf, 0xdb, + 0x26, 0x79, 0xd1, 0x1a, 0xc8, 0x1f, 0x61, 0xe5, 0x8e, 0xfd, 0x8e, 0x78, + 0xb4, 0xe7, 0x9f, 0x15, 0x06, 0x21, 0x08, 0x23, 0x4d, 0x4d, 0xa5, 0x3a, + 0xc0, 0xec, 0x5b, 0x15, 0x06, 0xc5, 0xf6, 0xad, 0x97, 0x56, 0x79, 0x42, + 0x50, 0xc1, 0x5f, 0x3d, 0xda, 0x29, 0x22, 0xfd, 0x6d, 0xb3, 0x8a, 0xf0, + 0xad, 0x77, 0xe6, 0x5d, 0xc3, 0x05, 0x47, 0x76, 0x97, 0x38, 0x65, 0xf6, + 0x9b, 0xfb, 0xba, 0x43, 0xb5, 0x1a, 0xdf, 0x31, 0xd2, 0x3e, 0x17, 0x41, + 0x4d, 0x09, 0x95, 0x65, 0x9a, 0x74, 0x55, 0xca, 0x71, 0xeb, 0x33, 0x8e, + 0xcb, 0x86, 0x36, 0x7f, 0xc3, 0xdb, 0xf2, 0x7f, 0xc1, 0xa9, 0x59, 0x53, + 0x4a, 0x0a, 0x63, 0x90, 0xa7, 0xa3, 0x8e, 0x87, 0x65, 0x8a, 0xc6, 0xd3, + 0xf0, 0xd6, 0x0f, 0x44, 0x53, 0x05, 0x11, 0xa4, 0x12, 0x6b, 0xb3, 0x90, + 0x5e, 0x59, 0x76, 0xd8, 0x50, 0x88, 0xa4, 0x16, 0x9a, 0x88, 0xa5, 0x4e, + 0x64, 0x86, 0xa3, 0x22, 0xad, 0xd5, 0xbf, 0x82, 0xe9, 0xd5, 0x00, 0x6d, + 0xca, 0xf5, 0x77, 0x53, 0x59, 0xe0, 0x63, 0x5a, 0xce, 0x3f, 0x72, 0xda, + 0xe7, 0x6e, 0xe6, 0xf1, 0xdc, 0xf5, 0x21, 0xc9, 0x1a, 0xe8, 0x94, 0xc9, + 0x8a, 0x95, 0xce, 0x81, 0x9c, 0xab, 0x2d, 0xe0, 0xce, 0x01, 0x76, 0x33, + 0x64, 0xeb, 0x47, 0x0b, 0xc9, 0x6b, 0x70, 0x97, 0x50, 0x58, 0x21, 0x9d, + 0x26, 0x4b, 0x92, 0x06, 0xb0, 0x98, 0x03, 0x4b, 0xc3, 0x70, 0x64, 0x79, + 0x57, 0xd8, 0xa3, 0x89, 0xad, 0xbf, 0xe1, 0x26, 0x01, 0x8d, 0x90, 0x5c, + 0x1d, 0x34, 0xc1, 0xa2, 0xc8, 0x2a, 0x41, 0xe2, 0x4a, 0x45, 0xa8, 0xa9, + 0x70, 0x8e, 0x49, 0xec, 0x59, 0x92, 0x65, 0x46, 0xe5, 0xe0, 0xe7, 0x80, + 0xec, 0xaa, 0x1c, 0xb0, 0xb2, 0x9a, 0x2d, 0x0a, 0x97, 0x70, 0x9b, 0x73, + 0x15, 0xc3, 0xe6, 0x85, 0x07, 0x67, 0x69, 0xe1, 0x5b, 0x47, 0x2f, 0x01, + 0x9e, 0x83, 0x96, 0xa3, 0x91, 0xfe, 0x0e, 0x8b, 0x78, 0xe9, 0x83, 0xb2, + 0x97, 0xb7, 0xf0, 0xd2, 0xba, 0xea, 0xf4, 0xf2, 0x94, 0x86, 0x3c, 0x9e, + 0xda, 0x8b, 0x91, 0x26, 0x4b, 0x90, 0xb1, 0x10, 0x54, 0xe1, 0x01, 0xbc, + 0x36, 0x73, 0xae, 0xd2, 0x9f, 0x17, 0xce, 0xba, 0x8a, 0x81, 0xc9, 0xef, + 0xaa, 0x9d, 0xdd, 0xbc, 0xbb, 0x4a, 0x96, 0x08, 0xe3, 0xf0, 0xe7, 0x2f, + 0x73, 0x12, 0xe6, 0x96, 0xf4, 0x17, 0x11, 0x7a, 0xd6, 0x39, 0x6a, 0xad, + 0x71, 0x6a, 0xef, 0x5c, 0xe4, 0xb4, 0x19, 0xe9, 0xc3, 0xe2, 0x72, 0x90, + 0xad, 0x16, 0x34, 0x0e, 0xf7, 0x4d, 0x4f, 0x21, 0x65, 0x93, 0x24, 0xa9, + 0x7f, 0xdc, 0xb5, 0x85, 0x36, 0xb3, 0x3d, 0xc9, 0xbf, 0x8a, 0xf2, 0xa8, + 0x09, 0xb5, 0xe2, 0x23, 0x94, 0xa7, 0x40, 0x5a, 0xfd, 0x68, 0xef, 0xa9, + 0x14, 0x70, 0xbb, 0xf1, 0x35, 0x2c, 0xbb, 0xf2, 0xd0, 0x33, 0x4c, 0x07, + 0x09, 0x1d, 0xf0, 0x52, 0x28, 0xe8, 0x7a, 0x06, 0x8e, 0xe2, 0xef, 0x20, + 0xf2, 0xfa, 0x05, 0x04, 0x45, 0xf8, 0x06, 0xcf, 0x9a, 0xde, 0xbb, 0xfb, + 0x56, 0xa4, 0xfc, 0xdc, 0x31, 0x03, 0x88, 0xca, 0x27, 0xb5, 0xab, 0x4a, + 0x35, 0x4a, 0x27, 0x88, 0x5f, 0x7d, 0x81, 0x36, 0xed, 0x15, 0xf8, 0x2b, + 0x98, 0xc8, 0x07, 0xf3, 0x24, 0xee, 0x36, 0x10, 0xed, 0xa6, 0x48, 0xd9, + 0xcd, 0x8c, 0xbc, 0x36, 0x97, 0x6b, 0x45, 0xbc, 0xe3, 0x8f, 0x01, 0xe4, + 0x23, 0xc1, 0x06, 0xb6, 0xed, 0x46, 0x72, 0x49, 0x06, 0xbb, 0x31, 0x4e, + 0x33, 0x31, 0xb9, 0x6a, 0xdf, 0xa9, 0x7b, 0xf8, 0xe5, 0x69, 0x61, 0xe5, + 0x92, 0xd9, 0x24, 0xce, 0xd2, 0xa8, 0x14, 0x59, 0xaa, 0xf7, 0xd2, 0x5a, + 0xe8, 0x40, 0xfe, 0xa8, 0xc9, 0xb6, 0x97, 0x64, 0x0a, 0xba, 0xc6, 0x6e, + 0xe9, 0x46, 0x6d, 0x38, 0x1a, 0xe9, 0xb2, 0x4d, 0xd4, 0x25, 0xe1, 0xef, + 0x47, 0xcf, 0x2d, 0x76, 0x81, 0xab, 0x18, 0x19, 0x5b, 0x3d, 0x18, 0xd5, + 0x58, 0x73, 0x32, 0x3a, 0x0c, 0x03, 0xb9, 0x8b, 0x7b, 0x87, 0xfa, 0xdc, + 0xfb, 0xa8, 0xca, 0x27, 0xba, 0xf0, 0x9f, 0x2c, 0xfd, 0xce, 0x69, 0x51, + 0xf9, 0xb0, 0xae, 0x83, 0x01, 0x58, 0x12, 0x83, 0x02, 0xd3, 0x47, 0xc8, + 0x8b, 0xf1, 0x3f, 0x20, 0xc3, 0x7a, 0x33, 0x53, 0x05, 0x32, 0xc1, 0xfe, + 0x10, 0x66, 0x51, 0x37, 0x6e, 0xb7, 0x00, 0x48, 0x88, 0xa5, 0xe5, 0xc8, + 0xd7, 0x8b, 0x32, 0x37, 0x50, 0x19, 0x44, 0xfd, 0x4c, 0x4c, 0x3b, 0xfd, + 0x6c, 0xd9, 0x37, 0x09, 0x31, 0xef, 0x90, 0x14, 0x8a, 0x21, 0xab, 0xe2, + 0x03, 0x8f, 0x88, 0x72, 0xf9, 0x48, 0xe8, 0x7d, 0xbc, 0x34, 0x56, 0x7e, + 0x3e, 0x31, 0x99, 0x5d, 0x3b, 0x74, 0x78, 0x77, 0x56, 0xf1, 0x08, 0x7c, + 0x07, 0x77, 0x60, 0x9d, 0xd4, 0x0c, 0xc3, 0x25, 0x62, 0x6a, 0xbf, 0x31, + 0xd7, 0x85, 0x67, 0x74, 0x78, 0xb2, 0x44, 0x1e, 0xe1, 0xc5, 0x86, 0xf2, + 0x4e, 0x38, 0x82, 0x17, 0x57, 0xf2, 0xe2, 0x74, 0x6c, 0xcd, 0xe9, 0x15, + 0x05, 0x7d, 0xf6, 0x7c, 0x59, 0x6c, 0x6e, 0x61, 0x7d, 0xc2, 0x1b, 0x2c, + 0x3b, 0x4a, 0xc3, 0x80, 0x3a, 0xe8, 0xde, 0xeb, 0x07, 0x81, 0x22, 0x59, + 0x4e, 0xaa, 0xcc, 0x4f, 0xc8, 0xfe, 0xed, 0x5a, 0xf1, 0xb7, 0x00, 0xcf, + 0x68, 0xd6, 0xca, 0x79, 0xe1, 0x3e, 0x76, 0x3f, 0xb9, 0xf8, 0x3b, 0x78, + 0x6f, 0x4d, 0x11, 0x5f, 0x79, 0x10, 0xe0, 0x75, 0xca, 0x0a, 0xb1, 0x13, + 0xbe, 0x3f, 0x72, 0x16, 0x42, 0xc1, 0x54, 0xbb, 0xf7, 0x98, 0xd2, 0x6b, + 0xc6, 0x0c, 0x4e, 0x73, 0xe1, 0xb9, 0xe5, 0x2b, 0x5a, 0x1d, 0xed, 0xf7, + 0x32, 0xf2, 0x44, 0x92, 0x84, 0x62, 0x8e, 0xcc, 0x6c, 0x18, 0x35, 0x0d, + 0x7d, 0xbd, 0x9f, 0x1d, 0xe9, 0x06, 0xb4, 0xd0, 0x16, 0x56, 0x57, 0xbe, + 0x4e, 0x7d, 0xe0, 0x70, 0xcd, 0xbf, 0xdb, 0xfd, 0x9f, 0x17, 0xe7, 0x3b, + 0x98, 0x4c, 0x6a, 0x46, 0xa4, 0xfb, 0xaf, 0x27, 0xa7, 0x60, 0x20, 0xaa, + 0xae, 0xde, 0xa8, 0x42, 0x07, 0xe3, 0xf0, 0xe0, 0xd0, 0x15, 0xef, 0x8c, + 0x95, 0x73, 0x58, 0x01, 0x40, 0x2d, 0xe3, 0xca, 0x60, 0x8f, 0x5c, 0xc8, + 0x2d, 0xa7, 0xef, 0x2c, 0xfd, 0x70, 0x04, 0xdf, 0x34, 0x90, 0x2b, 0x1a, + 0x12, 0x07, 0xff, 0xd9, 0x7c, 0x5d, 0xf1, 0x5a, 0x62, 0xc8, 0x3b, 0xeb, + 0x04, 0x86, 0x48, 0x89, 0x9b, 0x95, 0xbc, 0x07, 0xc0, 0x14, 0xb9, 0x6a, + 0x28, 0xbb, 0x36, 0x55, 0x0d, 0xe1, 0x8f, 0x02, 0x78, 0x4a, 0x7c, 0x3f, + 0xf6, 0xeb, 0x5e, 0x39, 0x81, 0x7d, 0x36, 0x53, 0xfd, 0xaa, 0x8f, 0xcc, + 0xce, 0x02, 0x17, 0xe6, 0xad, 0xf5, 0x70, 0xc9, 0x58, 0xee, 0xe3, 0xa4, + 0xac, 0xd9, 0x20, 0x7b, 0xc6, 0x6f, 0x81, 0x5a, 0xd1, 0x62, 0x62, 0x76, + 0x1f, 0x22, 0xdf, 0x50, 0xa1, 0xf3, 0x47, 0xcb, 0xff, 0x71, 0xff, 0xab, + 0xef, 0x47, 0xd8, 0xbf, 0x0e, 0x3c, 0xf4, 0xd8, 0x54, 0x47, 0x9d, 0x17, + 0xad, 0xbd, 0xb4, 0x68, 0x0e, 0xf0, 0xcd, 0xad, 0x28, 0xa6, 0xf4, 0x70, + 0xd1, 0x4f, 0xb6, 0x40, 0xab, 0x80, 0xb7, 0x3e, 0x43, 0x12, 0x4a, 0xd1, + 0x10, 0xb4, 0xc2, 0x6e, 0x3e, 0x86, 0x74, 0xb7, 0xd6, 0x8c, 0x3c, 0x12, + 0xbd, 0x92, 0x98, 0xcd, 0xa4, 0xd9, 0xa5, 0xb5, 0xdd, 0x78, 0x47, 0x53, + 0x26, 0xef, 0x44, 0xda, 0x9b, 0x32, 0xb9, 0xd8, 0x36, 0xd8, 0x3a, 0xd0, + 0x2b, 0x16, 0xc5, 0x08, 0x23, 0x2f, 0xa3, 0x77, 0x6b, 0x63, 0x4f, 0xac, + 0x04, 0x43, 0x8c, 0x5c, 0xb3, 0xb8, 0x04, 0xa7, 0x93, 0x99, 0x72, 0x0a, + 0x3a, 0xef, 0xbb, 0x74, 0xb0, 0x8a, 0x3f, 0x60, 0x49, 0x30, 0x6d, 0x1b, + 0xd1, 0x13, 0xd1, 0x85, 0xd0, 0xa5, 0x8f, 0xff, 0xef, 0x2c, 0xe2, 0xc3, + 0x22, 0x25, 0xa8, 0xa4, 0xb0, 0x51, 0x89, 0xee, 0xff, 0x20, 0x0a, 0xf6, + 0x35, 0xa1, 0x76, 0x72, 0x2f, 0x85, 0x10, 0x45, 0xbf, 0xad, 0x00, 0xcd, + 0x80, 0xe2, 0xaf, 0x7a, 0xb6, 0x4d, 0xcc, 0x9f, 0x43, 0xa5, 0x50, 0xc6, + 0xd0, 0xf3, 0xa0, 0xa9, 0x5e, 0x62, 0x48, 0x90, 0x98, 0x14, 0xcc, 0xec, + 0x6c, 0x37, 0xe5, 0x60, 0xc1, 0xde, 0x2c, 0x58, 0xc4, 0x12, 0x87, 0x82, + 0x57, 0xe4, 0xe1, 0x0d, 0x69, 0x71, 0x93, 0x61, 0x03, 0xed, 0x76, 0x6e, + 0xff, 0xcb, 0x71, 0x77, 0x2a, 0x89, 0x91, 0x60, 0xef, 0x6c, 0x6d, 0x6f, + 0x4b, 0xcc, 0x62, 0xa8, 0x29, 0x19, 0x2a, 0x6c, 0x3c, 0x20, 0x7d, 0x4d, + 0x1f, 0x70, 0xbf, 0x0f, 0xc6, 0x61, 0x14, 0x35, 0xb7, 0xc4, 0x5d, 0x4a, + 0x7a, 0xdd, 0xa1, 0x55, 0x54, 0xdc, 0x3f, 0xaa, 0x77, 0x37, 0x5f, 0x42, + 0xd4, 0x45, 0xb8, 0xb6, 0x8d, 0xc9, 0xb8, 0xa5, 0xe2, 0x1d, 0xf0, 0xbc, + 0x2f, 0xef, 0x66, 0xdc, 0xcc, 0x2e, 0x47, 0xc5, 0xc5, 0xaa, 0xa6, 0xdb, + 0xb6, 0x86, 0x3b, 0xd1, 0xbc, 0x38, 0xf9, 0x33, 0x13, 0xc1, 0x38, 0xab, + 0xa6, 0x81, 0xe5, 0xb7, 0x30, 0x90, 0x87, 0x6d, 0x4f, 0x07, 0x95, 0x9b, + 0x4c, 0x63, 0x9f, 0xa7, 0xc0, 0xa0, 0x9b, 0xb5, 0x87, 0xda, 0x5d, 0x75, + 0x49, 0xea, 0x41, 0xac, 0xdb, 0xd2, 0xa9, 0x63, 0x59, 0xb5, 0x36, 0xf8, + 0xec, 0xa6, 0x47, 0xe1, 0x7b, 0x41, 0x69, 0x9c, 0xb4, 0x00, 0x9e, 0x4e, + 0x9b, 0xc3, 0x84, 0x75, 0x84, 0xe1, 0x30, 0x2b, 0xd0, 0xc2, 0x09, 0x6a, + 0x60, 0xc7, 0xed, 0x5d, 0x56, 0xe2, 0x9c, 0x91, 0x56, 0x6a, 0xd9, 0xe7, + 0x69, 0x02, 0x8a, 0x89, 0xe9, 0x40, 0x1b, 0xb3, 0x04, 0x62, 0x5a, 0x9e, + 0x6f, 0x88, 0x09, 0x07, 0xa9, 0xad, 0xd1, 0x71, 0x63, 0x9f, 0x2c, 0x8c, + 0xf2, 0x6f, 0xdf, 0xd7, 0x5f, 0x26, 0x27, 0x49, 0x7b, 0xa8, 0xdd, 0x41, + 0xf5, 0xe8, 0xd4, 0x5f, 0x2c, 0x23, 0x69, 0x75, 0x83, 0xed, 0xc8, 0xef, + 0xa6, 0xd3, 0xa4, 0x15, 0xa3, 0x77, 0xd3, 0xf4, 0xcd, 0xf5, 0xf4, 0xcd, + 0x15, 0x98, 0x79, 0xd9, 0xc7, 0xa2, 0x10, 0xb3, 0xf0, 0x6c, 0x8a, 0x2f, + 0x60, 0x8a, 0x18, 0x53, 0x3e, 0xfc, 0xe6, 0x72, 0x6f, 0xae, 0x4d, 0x53, + 0x5d, 0xbe, 0xef, 0x0c, 0x92, 0xae, 0x6b, 0x3c, 0x00, 0x0c, 0x65, 0x12, + 0x0d, 0xfd, 0x3f, 0x98, 0x84, 0x5c, 0xbf, 0xea, 0x8a, 0x21, 0x27, 0x56, + 0xf3, 0xac, 0xb7, 0x68, 0x08, 0x59, 0xd5, 0x30, 0xc6, 0x51, 0x02, 0x38, + 0xa2, 0x86, 0x25, 0xa3, 0x8b, 0x0f, 0xcf, 0x05, 0xe6, 0x95, 0xbd, 0x34, + 0x97, 0xf2, 0x84, 0xcb, 0x4f, 0x1b, 0xd7, 0xf5, 0x16, 0x39, 0x82, 0x0b, + 0x97, 0xf6, 0xed, 0x54, 0x09, 0xff, 0x76, 0x2d, 0xec, 0xe6, 0x9b, 0x1e, + 0xfc, 0xbb, 0x48, 0x6c, 0x2d, 0xbb, 0xce, 0x41, 0x69, 0xb4, 0x38, 0xf1, + 0x28, 0x6d, 0x0a, 0xe0, 0x52, 0xe2, 0x4a, 0xc4, 0xc0, 0xc6, 0x07, 0xa7, + 0x0e, 0xf0, 0x9e, 0xb0, 0x75, 0xf2, 0xf0, 0xf7, 0xa6, 0x66, 0xe4, 0x2a, + 0xa2, 0x68, 0xab, 0x1f, 0xa2, 0xf3, 0x08, 0xdb, 0xd2, 0xbc, 0x20, 0x5c, + 0x22, 0x7a, 0x31, 0x95, 0x36, 0xd3, 0x01, 0xf2, 0x50, 0x02, 0x29, 0x42, + 0xb6, 0xb8, 0xdd, 0xd6, 0xa0, 0xd4, 0x35, 0x15, 0x56, 0xd9, 0xd9, 0x10, + 0xef, 0x13, 0x75, 0x7a, 0x86, 0xfd, 0x4a, 0x88, 0xc1, 0x0c, 0x18, 0x5f, + 0x9e, 0xd3, 0x3d, 0x77, 0xcc, 0xf3, 0x39, 0xae, 0xfb, 0x47, 0x00, 0xd0, + 0x65, 0x17, 0x4e, 0x24, 0x12, 0x42, 0xd5, 0x74, 0x6a, 0x32, 0x81, 0x12, + 0x78, 0x8b, 0x20, 0xc6, 0x8f, 0x33, 0x65, 0x89, 0xbf, 0xf1, 0x90, 0x77, + 0xdd, 0x48, 0x50, 0x72, 0xdb, 0x10, 0x44, 0x58, 0xb2, 0x2a, 0xcb, 0x83, + 0xfd, 0xe4, 0xe8, 0xa5, 0x1a, 0x58, 0xa7, 0xff, 0xae, 0x19, 0x37, 0xe9, + 0x2c, 0x33, 0x77, 0xd2, 0x9c, 0xa5, 0x88, 0x33, 0x83, 0x0b, 0x89, 0xfb, + 0xa9, 0xc8, 0x5c, 0x5e, 0x58, 0xd9, 0xcd, 0x9c, 0x2f, 0xb2, 0xe0, 0x3d, + 0x6e, 0xfd, 0xe2, 0x80, 0x72, 0xe1, 0x8c, 0x23, 0x74, 0x68, 0x23, 0x13, + 0x56, 0x01, 0x7f, 0xa3, 0x8f, 0xb3, 0x63, 0x1a, 0x65, 0xa6, 0xce, 0xa1, + 0x4b, 0xd4, 0x42, 0xc6, 0xad, 0x76, 0xe7, 0x52, 0xfd, 0x15, 0x87, 0x43, + 0x61, 0x9b, 0x25, 0x15, 0x8c, 0x8b, 0x16, 0xc1, 0xb5, 0x28, 0x71, 0xa3, + 0x6a, 0xcf, 0xd0, 0x56, 0x63, 0xa9, 0xe6, 0x38, 0x74, 0x83, 0xbd, 0xdc, + 0x5b, 0x61, 0x9a, 0xca, 0xca, 0x74, 0x9c, 0x4e, 0x85, 0xd5, 0xc9, 0x3c, + 0x73, 0x24, 0x1b, 0x44, 0xc9, 0xa4, 0xb6, 0xcb, 0x02, 0xd9, 0xc0, 0x55, + 0xe2, 0x43, 0xee, 0x74, 0x54, 0xf2, 0x63, 0x86, 0x44, 0xe9, 0x6a, 0xa9, + 0x9f, 0x1e, 0x5b, 0x8c, 0xbc, 0x7b, 0x90, 0x59, 0x3c, 0x35, 0xa6, 0x58, + 0x7b, 0x76, 0x2f, 0xed, 0xf3, 0xaf, 0x5c, 0x6b, 0xf9, 0x8b, 0x5f, 0x1f, + 0x1a, 0x2b, 0x69, 0x7f, 0xe9, 0x4e, 0x4d, 0x07, 0xe4, 0xe7, 0xe6, 0xf5, + 0x0d, 0xd4, 0x03, 0xab, 0x2d, 0xcd, 0x78, 0x58, 0xfe, 0xf6, 0x94, 0x51, + 0x65, 0xa4, 0xe3, 0x16, 0x93, 0xc1, 0x2f, 0x62, 0xf5, 0xf1, 0xa3, 0x62, + 0x51, 0xbd, 0xe2, 0x1e, 0xd7, 0xfa, 0xd5, 0xb5, 0xae, 0x59, 0x7d, 0x48, + 0x9d, 0x2c, 0xde, 0x83, 0x58, 0x23, 0x38, 0xc8, 0xe0, 0x87, 0x4d, 0x16, + 0x5f, 0x16, 0x71, 0x44, 0x66, 0xe7, 0x1d, 0x30, 0xd4, 0x7b, 0x03, 0xee, + 0x26, 0x11, 0x54, 0x89, 0x62, 0x7e, 0xc3, 0x9b, 0x0d, 0x48, 0x3b, 0xf2, + 0x34, 0x46, 0xf2, 0x1d, 0x2d, 0x98, 0xca, 0xf8, 0x0c, 0x02, 0x56, 0x1d, + 0xad, 0x0a, 0x61, 0xc4, 0x1d, 0x47, 0xac, 0x6b, 0x8a, 0xea, 0x8e, 0x5a, + 0x47, 0x40, 0x87, 0x41, 0x6e, 0xce, 0xa8, 0xc0, 0xba, 0xd2, 0x9f, 0x29, + 0x84, 0xa4, 0xa8, 0xb2, 0xdd, 0xca, 0xd2, 0xfc, 0x52, 0x87, 0xb9, 0x70, + 0xdc, 0x27, 0xb8, 0xb4, 0xa6, 0xc8, 0xa9, 0x7a, 0x2d, 0x5d, 0x77, 0xf1, + 0xa2, 0x36, 0x60, 0x1b, 0xb5, 0x29, 0x0d, 0x0a, 0x3d, 0xa0, 0xda, 0x82, + 0xa2, 0x71, 0x40, 0x5e, 0x2e, 0xd9, 0x6b, 0x1e, 0x0a, 0x98, 0x53, 0x23, + 0x13, 0x13, 0xac, 0x19, 0xe5, 0x9e, 0x0a, 0x45, 0x65, 0xe1, 0xc6, 0x1a, + 0xb1, 0x4f, 0x3f, 0xcc, 0xf5, 0x5f, 0x8f, 0x97, 0x29, 0x6f, 0xa3, 0xc9, + 0x9d, 0x70, 0xd6, 0x64, 0x71, 0x99, 0xb9, 0xcd, 0xa7, 0xc7, 0xa6, 0x8f, + 0xaf, 0xb2, 0x21, 0x61, 0xf7, 0x91, 0x7e, 0x6e, 0x2e, 0x3a, 0x2f, 0x78, + 0xe2, 0xd8, 0x03, 0x2e, 0x31, 0x68, 0xad, 0xf4, 0xa2, 0x8a, 0x17, 0x7e, + 0x31, 0x4b, 0xcd, 0xd6, 0x01, 0x0b, 0x56, 0x29, 0x4a, 0x27, 0x5d, 0xdb, + 0x80, 0xbf, 0x1a, 0x02, 0xd3, 0x6f, 0xd3, 0x6e, 0x2d, 0x14, 0x78, 0xda, + 0x55, 0x1c, 0x7a, 0x4e, 0xb8, 0xa0, 0x5a, 0x5c, 0x1f, 0x5a, 0x48, 0xd1, + 0xdd, 0xae, 0x90, 0xef, 0xe3, 0x9f, 0x42, 0x05, 0x9e, 0x98, 0x8e, 0x60, + 0x87, 0x7a, 0xba, 0xe8, 0xa1, 0xfa, 0x20, 0xec, 0x68, 0xe8, 0xb5, 0x7d, + 0xca, 0x8e, 0xf3, 0x3a, 0x58, 0xbe, 0xa0, 0x7f, 0xd3, 0x46, 0x05, 0x39, + 0xca, 0x80, 0x9e, 0x53, 0x31, 0x11, 0xef, 0xfc, 0xc1, 0xc8, 0xd7, 0x26, + 0x50, 0x12, 0xd1, 0x56, 0xee, 0xf4, 0x4a, 0x39, 0x51, 0x85, 0xa9, 0x60, + 0xc1, 0xfa, 0x69, 0x4d, 0x7d, 0xf2, 0x78, 0xce, 0x90, 0x10, 0xfe, 0xc5, + 0xa0, 0xec, 0x66, 0xc8, 0xe4, 0x99, 0x32, 0xe9, 0x1f, 0x09, 0xe3, 0x47, + 0x3a, 0xc4, 0x0a, 0x80, 0xab, 0x03, 0x32, 0x51, 0x66, 0xb0, 0x3c, 0x92, + 0x05, 0xaf, 0xc8, 0x4c, 0x73, 0x9d, 0x93, 0xa0, 0x6e, 0x25, 0x5e, 0x76, + 0x8f, 0x5a, 0xb6, 0x1d, 0x36, 0xb0, 0xc1, 0xd7, 0x9d, 0xc7, 0x36, 0x26, + 0x18, 0xab, 0x34, 0x79, 0x33, 0xb6, 0xe7, 0x97, 0x81, 0xca, 0x41, 0x36, + 0x57, 0x1f, 0xe1, 0x8d, 0x5d, 0x41, 0xbe, 0x32, 0xfc, 0x85, 0x39, 0x37, + 0x4f, 0xa1, 0x14, 0xae, 0x62, 0x25, 0x73, 0x08, 0xe8, 0x51, 0x5c, 0x5a, + 0xb3, 0x72, 0xb1, 0x77, 0xd5, 0x27, 0x18, 0x24, 0x02, 0x6d, 0xa2, 0x65, + 0x52, 0xff, 0xd3, 0x19, 0x60, 0xc5, 0x36, 0x9b, 0x81, 0x30, 0x53, 0xf1, + 0xca, 0xab, 0xa5, 0xbf, 0xd9, 0xbb, 0x79, 0xb4, 0x8e, 0xa1, 0x83, 0x76, + 0x0e, 0xfd, 0xe2, 0x17, 0x88, 0xb8, 0xbe, 0xd4, 0xf5, 0x21, 0x56, 0x81, + 0xef, 0x33, 0xaf, 0x65, 0x4c, 0x1b, 0x99, 0x37, 0x47, 0x9a, 0x14, 0x41, + 0x72, 0x00, 0x67, 0x59, 0xec, 0xdf, 0xae, 0x76, 0x16, 0x12, 0x1b, 0x57, + 0xa6, 0x8e, 0x5d, 0x7e, 0x82, 0x61, 0x01, 0xe0, 0x2a, 0x1c, 0xbe, 0x9f, + 0x52, 0x2a, 0xa9, 0xe3, 0x42, 0xe2, 0x1e, 0xcd, 0xc1, 0xdb, 0x8d, 0x43, + 0x38, 0x20, 0xe8, 0x3f, 0xe1, 0xbc, 0x55, 0x47, 0xeb, 0xac, 0x02, 0xeb, + 0x75, 0x08, 0x42, 0x39, 0xbf, 0x95, 0x2c, 0x7f, 0x83, 0xd5, 0xc4, 0x3f, + 0x0c, 0x10, 0x1e, 0x5e, 0xe2, 0x42, 0x34, 0x74, 0x6c, 0x58, 0xf9, 0xe9, + 0x94, 0x33, 0x11, 0x85, 0xe2, 0x99, 0x91, 0x09, 0x2c, 0x3b, 0x8d, 0xab, + 0x91, 0xcd, 0xbc, 0x48, 0x64, 0x91, 0xa9, 0x40, 0xbc, 0x5d, 0x63, 0xb1, + 0x3b, 0xb6, 0x08, 0x19, 0xad, 0x04, 0x3c, 0xab, 0x76, 0xc4, 0x1a, 0x79, + 0xa0, 0xab, 0x17, 0xb4, 0xe7, 0xa8, 0x67, 0x64, 0xe1, 0x66, 0x9c, 0x18, + 0xae, 0xe0, 0x6e, 0x31, 0xc2, 0xb1, 0x58, 0x0e, 0xcc, 0x0c, 0x19, 0x9a, + 0x23, 0x3a, 0xc1, 0x3e, 0xd4, 0x97, 0xce, 0x9e, 0x41, 0xc8, 0x4f, 0x95, + 0x22, 0x4d, 0xa9, 0x5c, 0x00, 0x7f, 0xdd, 0xe3, 0xc3, 0x42, 0xdd, 0x43, + 0x5f, 0x00, 0xae, 0xb5, 0x1e, 0xd2, 0xe6, 0xcd, 0xe8, 0x71, 0x4d, 0xe2, + 0x49, 0xdd, 0x41, 0x9e, 0x94, 0xc0, 0xe3, 0xeb, 0x63, 0x8f, 0xd3, 0x63, + 0xff, 0xa2, 0x73, 0x99, 0x81, 0x7b, 0x0c, 0x34, 0x30, 0xc7, 0x4b, 0x92, + 0x88, 0x9f, 0xbb, 0xec, 0x12, 0x9f, 0x8e, 0x59, 0xa4, 0xed, 0xb5, 0xfb, + 0x4c, 0x49, 0xb9, 0xb3, 0xa3, 0xbc, 0x76, 0xd0, 0x22, 0x11, 0x75, 0xd6, + 0x13, 0x58, 0x18, 0x20, 0x47, 0xf6, 0x2f, 0xfd, 0xe9, 0xae, 0xe7, 0x71, + 0x82, 0x2e, 0xd4, 0xaf, 0x35, 0x8a, 0x81, 0xcb, 0x17, 0xfc, 0x0b, 0x4b, + 0x62, 0x61, 0x3a, 0x3c, 0x81, 0x6b, 0x81, 0x65, 0x3d, 0x56, 0xbb, 0x3f, + 0xaf, 0x6e, 0x7f, 0xd0, 0x76, 0x92, 0x30, 0x92, 0x02, 0x87, 0x2e, 0xb8, + 0x26, 0xfd, 0x51, 0x4b, 0xa1, 0xc2, 0xe2, 0x94, 0x8c, 0x37, 0x4a, 0xff, + 0xfe, 0x69, 0xce, 0xc8, 0x65, 0xf3, 0x9c, 0x4e, 0x82, 0xeb, 0x32, 0x14, + 0x6a, 0x97, 0xd5, 0x53, 0xa3, 0x99, 0xf0, 0x88, 0x29, 0x27, 0x5b, 0x57, + 0xa1, 0xce, 0x5e, 0x78, 0xac, 0x90, 0x14, 0x1a, 0x54, 0x38, 0xc2, 0x72, + 0x2b, 0x9e, 0x2a, 0xc1, 0xcd, 0xa6, 0x18, 0x29, 0xcc, 0xb1, 0xe9, 0x5f, + 0x86, 0x6e, 0x0f, 0x51, 0xf7, 0x40, 0xa7, 0x0b, 0x2b, 0xe8, 0x4c, 0x07, + 0xbb, 0xe9, 0x8d, 0x78, 0x04, 0x49, 0xe5, 0xd4, 0xb4, 0xb1, 0x10, 0xf4, + 0x93, 0xe7, 0xfc, 0x92, 0xbb, 0x4e, 0xf8, 0x86, 0xcd, 0x87, 0x9d, 0xb7, + 0xb1, 0x6a, 0xcf, 0x25, 0xe9, 0x6c, 0x99, 0x4e, 0xf6, 0x52, 0xbc, 0x97, + 0xbe, 0xed, 0x4a, 0xbf, 0xf9, 0xc7, 0x18, 0x4b, 0x48, 0xab, 0x9d, 0x47, + 0xf0, 0x09, 0xaa, 0x82, 0x51, 0x74, 0x8a, 0xc8, 0x31, 0x3f, 0x26, 0x93, + 0xd3, 0xe7, 0x54, 0x28, 0xfa, 0x10, 0x55, 0x87, 0x5d, 0x52, 0x09, 0xe6, + 0xa3, 0xfb, 0x4a, 0x8e, 0x2b, 0x99, 0xaa, 0x7e, 0x5a, 0x80, 0x6e, 0x04, + 0xd1, 0x1e, 0xe2, 0x71, 0xd2, 0xc8, 0xb7, 0x4d, 0xda, 0x34, 0x0e, 0x72, + 0x58, 0x4e, 0xd5, 0xca, 0x70, 0x63, 0x2c, 0xb9, 0xd6, 0x26, 0xba, 0x17, + 0x4a, 0xfb, 0x62, 0x0b, 0x83, 0xf9, 0xcb, 0xff, 0x96, 0x18, 0x4a, 0x2f, + 0x3f, 0x08, 0x23, 0xd4, 0x06, 0x35, 0xdd, 0xa8, 0x7c, 0xa2, 0x27, 0x36, + 0x09, 0xaf, 0xc3, 0x32, 0xea, 0x53, 0xb7, 0x4c, 0x09, 0x66, 0xb0, 0x25, + 0x5a, 0xff, 0x43, 0x77, 0x63, 0x86, 0x4a, 0x46, 0xe2, 0xdb, 0x93, 0x35, + 0x50, 0x19, 0xb1, 0xad, 0x7e, 0x1c, 0xad, 0xa0, 0x8c, 0x12, 0xfb, 0x29, + 0x73, 0x31, 0x5f, 0xbf, 0x52, 0xcd, 0x25, 0xf6, 0x66, 0x62, 0x03, 0x90, + 0xca, 0x25, 0x04, 0x8b, 0x16, 0xfc, 0x59, 0xf6, 0x96, 0x8e, 0x57, 0xfb, + 0x04, 0x07, 0xe7, 0x82, 0x51, 0x3b, 0x39, 0x11, 0x2a, 0xc6, 0xdf, 0x94, + 0xbf, 0x69, 0x71, 0xe9, 0xdf, 0x39, 0xc7, 0xbe, 0x0c, 0xf6, 0x4d, 0x06, + 0x2e, 0x10, 0x86, 0xe0, 0x09, 0x9f, 0x88, 0xad, 0xd4, 0x6f, 0x97, 0xbd, + 0x4a, 0xf5, 0x6e, 0x06, 0xc5, 0x42, 0x70, 0x5b, 0x83, 0x91, 0x33, 0xa0, + 0x02, 0x61, 0xdf, 0xf1, 0x2c, 0x3f, 0xd7, 0x1d, 0xe9, 0x7b, 0xfb, 0x70, + 0xcb, 0x8a, 0x06, 0x53, 0x41, 0x6d, 0xa3, 0x4a, 0xbc, 0x2e, 0xf5, 0x74, + 0xeb, 0x30, 0xcd, 0x9a, 0x26, 0xe4, 0xe3, 0xb1, 0x9c, 0x23, 0x68, 0x7e, + 0xbc, 0xbe, 0x56, 0x31, 0x10, 0xfd, 0x14, 0xb2, 0xeb, 0xd6, 0x8b, 0xad, + 0x23, 0x6f, 0xb6, 0xfa, 0xab, 0xe1, 0xb4, 0x92, 0x39, 0x93, 0xae, 0x85, + 0x96, 0x2b, 0xdb, 0x83, 0xf0, 0x49, 0x52, 0x05, 0xdd, 0xd6, 0xe9, 0x9a, + 0x9f, 0xf0, 0xb6, 0x04, 0x40, 0x60, 0xc5, 0x08, 0x96, 0x07, 0x26, 0x9a, + 0x33, 0x4b, 0x2e, 0xbb, 0x5c, 0xf9, 0x5e, 0x8c, 0xb6, 0x3c, 0x2d, 0xf4, + 0xb7, 0x2f, 0xf0, 0x29, 0xa1, 0x6f, 0xd6, 0x10, 0x78, 0x6d, 0x48, 0x0a, + 0xd4, 0xc2, 0x91, 0x68, 0x3d, 0x74, 0x0c, 0xa0, 0xa1, 0x4a, 0x4d, 0x43, + 0x97, 0xe7, 0xc6, 0x41, 0x88, 0x6b, 0xce, 0xb4, 0xf7, 0x23, 0x6e, 0x78, + 0x76, 0x18, 0x54, 0x15, 0x28, 0x2c, 0xae, 0x9f, 0xb8, 0x11, 0x5a, 0xcb, + 0x52, 0xc5, 0xfb, 0x1c, 0xa5, 0x80, 0x68, 0xe4, 0x0e, 0xb6, 0xb8, 0xc0, + 0xd7, 0x33, 0xdd, 0xa0, 0x90, 0x37, 0x80, 0x0e, 0x5f, 0x43, 0xe0, 0xfc, + 0x95, 0xd4, 0x40, 0x8a, 0xcd, 0x32, 0x0c, 0xb0, 0x12, 0x26, 0x50, 0x93, + 0xd2, 0x0c, 0x3e, 0x39, 0x31, 0xc1, 0xdf, 0x33, 0xc9, 0x14, 0x10, 0xdc, + 0x86, 0x72, 0x90, 0xa0, 0xff, 0x07, 0x56, 0xd1, 0x43, 0xba, 0x84, 0xba, + 0x74, 0x60, 0xef, 0x88, 0xb5, 0x68, 0x98, 0x03, 0x84, 0x3e, 0x81, 0xae, + 0x60, 0x6b, 0xe3, 0x50, 0x4f, 0x5c, 0xd3, 0x34, 0xc0, 0xdf, 0x7b, 0xaf, + 0x20, 0xad, 0x76, 0xc9, 0xa5, 0x89, 0x49, 0x00, 0xb7, 0xa3, 0xa6, 0xcc, + 0x85, 0xca, 0x63, 0xc3, 0x92, 0xab, 0xd6, 0x77, 0xa2, 0x61, 0x1b, 0xb4, + 0x54, 0x79, 0x7f, 0x84, 0xf2, 0x31, 0x28, 0x17, 0xe6, 0x81, 0xeb, 0x75, + 0x46, 0x34, 0x9f, 0x15, 0x94, 0xac, 0xa3, 0x78, 0xa1, 0xd5, 0x31, 0x7f, + 0x31, 0x96, 0xa9, 0x51, 0xf6, 0x58, 0x86, 0x16, 0x8b, 0x2b, 0x94, 0x46, + 0x53, 0x9e, 0x8d, 0x96, 0xfd, 0xb6, 0x4b, 0x26, 0x27, 0xd2, 0x52, 0xab, + 0xa9, 0xe1, 0x62, 0x8a, 0x3f, 0x48, 0x16, 0xc6, 0xec, 0x42, 0x4a, 0x90, + 0x69, 0x58, 0x62, 0x91, 0x42, 0x48, 0xe5, 0xf0, 0x22, 0x0e, 0xf9, 0x77, + 0xcb, 0xa2, 0x3e, 0x27, 0x20, 0xe2, 0x56, 0xcb, 0x59, 0x80, 0x0e, 0xf8, + 0x29, 0x66, 0xa6, 0xcf, 0x82, 0x4c, 0xfe, 0x10, 0x95, 0x9c, 0x30, 0x5b, + 0x02, 0xac, 0xcb, 0x86, 0x03, 0x52, 0xed, 0x00, 0xd0, 0xea, 0x29, 0xe8, + 0x1c, 0x81, 0xc4, 0x49, 0x92, 0x64, 0x69, 0x4b, 0x2c, 0x16, 0xe8, 0xda, + 0x23, 0x65, 0x7e, 0xd9, 0x17, 0xbd, 0x17, 0xe1, 0xf3, 0xd1, 0x37, 0x49, + 0xfc, 0x98, 0x12, 0x80, 0x81, 0xd9, 0x4f, 0xd5, 0x0c, 0xbb, 0x64, 0x0d, + 0x7a, 0x3f, 0xc5, 0xa3, 0xc2, 0xa2, 0x85, 0x9c, 0xd0, 0x90, 0x86, 0x88, + 0x80, 0x84, 0xb1, 0xd0, 0xaa, 0xbc, 0x61, 0x91, 0x85, 0x10, 0x6a, 0xfc, + 0x50, 0x57, 0x6d, 0x9c, 0x04, 0x71, 0xb2, 0x62, 0xcc, 0xe6, 0x4b, 0x5e, + 0x09, 0xcc, 0x58, 0xd1, 0xf5, 0xe6, 0xde, 0x61, 0xa4, 0xf7, 0x19, 0x46, + 0x64, 0x3d, 0x6a, 0x37, 0x4d, 0xa5, 0x51, 0x70, 0xaf, 0x05, 0xf7, 0x71, + 0x70, 0x9b, 0x1a, 0x6b, 0xcc, 0xa5, 0x99, 0xce, 0x27, 0x7a, 0x51, 0x76, + 0xec, 0x06, 0xe1, 0xcd, 0x5a, 0xef, 0xa6, 0xdb, 0xc3, 0x58, 0x65, 0x3d, + 0x97, 0xbe, 0x22, 0x07, 0xf7, 0xbd, 0xa5, 0x08, 0x99, 0xd7, 0xe6, 0x74, + 0x6c, 0xe7, 0xac, 0xa9, 0xab, 0x4f, 0xe5, 0x7f, 0x6e, 0xfe, 0x35, 0xfc, + 0xa6, 0xb7, 0x05, 0xab, 0x16, 0x88, 0x2a, 0x20, 0xeb, 0xbd, 0x8a, 0x5c, + 0xc1, 0xa2, 0x0a, 0x25, 0xc6, 0xae, 0x38, 0x58, 0x0f, 0x56, 0xbc, 0xf4, + 0xac, 0x55, 0x7f, 0xa7, 0x8b, 0x70, 0x71, 0xc6, 0x01, 0xa5, 0x55, 0x18, + 0x90, 0xee, 0x75, 0x69, 0xb8, 0x86, 0x76, 0x9a, 0x4e, 0xf8, 0xfe, 0xc5, + 0xed, 0xc3, 0x5e, 0x69, 0xf2, 0x85, 0x16, 0xb5, 0xa8, 0x41, 0x3d, 0x53, + 0x26, 0xfb, 0x6d, 0x20, 0x39, 0x93, 0x75, 0x01, 0xeb, 0xd9, 0x37, 0xd6, + 0xb3, 0xe5, 0x0b, 0xad, 0x96, 0x0e, 0xf8, 0x0b, 0xcb, 0x36, 0x9b, 0x47, + 0xfa, 0x79, 0x1b, 0x16, 0xa9, 0x0a, 0xec, 0x52, 0x9d, 0xa9, 0xef, 0x07, + 0xf0, 0xc9, 0x53, 0xcf, 0x06, 0xb6, 0x7b, 0x3c, 0xc7, 0x75, 0xa3, 0x3d, + 0x34, 0xc0, 0xf0, 0x26, 0x51, 0x5b, 0x88, 0x44, 0x19, 0xbe, 0xd8, 0x7b, + 0x41, 0x10, 0x8f, 0x3e, 0xc7, 0x9b, 0xc2, 0xf0, 0x7c, 0xf1, 0xca, 0x73, + 0x0b, 0x8a, 0xb4, 0x43, 0xa3, 0x7f, 0x2f, 0xb9, 0x1e, 0xcb, 0x7f, 0xab, + 0xad, 0xe2, 0x2c, 0x23, 0xcf, 0xd2, 0x8e, 0x87, 0x3f, 0x07, 0x98, 0xe3, + 0xe2, 0xfd, 0x32, 0x99, 0x7a, 0x91, 0xbc, 0x98, 0x7d, 0x86, 0x9e, 0x14, + 0xfc, 0xcd, 0x1b, 0x41, 0x6f, 0xea, 0x0c, 0xc9, 0x0a, 0xdf, 0xee, 0xcc, + 0x05, 0x13, 0x8d, 0x43, 0x72, 0x1f, 0x6f, 0x9c, 0x5d, 0xe1, 0x49, 0x45, + 0xe5, 0x80, 0xde, 0xac, 0x64, 0x55, 0x97, 0xcc, 0xe4, 0x2f, 0x7b, 0xbd, + 0xec, 0xd1, 0xd2, 0x89, 0x20, 0x65, 0x0f, 0x9f, 0x94, 0x20, 0xb8, 0x77, + 0xd8, 0x93, 0x24, 0x48, 0xd5, 0x29, 0xb0, 0x9c, 0xf3, 0x08, 0xb5, 0xa3, + 0x76, 0x04, 0x74, 0xce, 0xc1, 0xd1, 0x62, 0x78, 0xf0, 0xcd, 0x37, 0x54, + 0x60, 0x28, 0x34, 0x6c, 0x8c, 0x98, 0x95, 0xd4, 0x72, 0x61, 0x66, 0xa3, + 0xba, 0xa9, 0x1b, 0xdd, 0x76, 0x2b, 0x7d, 0xde, 0x3b, 0xb5, 0x9e, 0x23, + 0x10, 0x04, 0x8e, 0x4c, 0x4b, 0xb7, 0x6d, 0x4b, 0x6a, 0x1c, 0x2c, 0x83, + 0x62, 0x54, 0x44, 0xf3, 0x9b, 0x6d, 0xd2, 0x04, 0x11, 0xc4, 0x8a, 0xb4, + 0x96, 0x96, 0xf0, 0xcd, 0xe0, 0x38, 0xc4, 0xf2, 0xac, 0x7c, 0x12, 0x97, + 0x82, 0x6b, 0x4c, 0x6a, 0xc9, 0xfe, 0xe6, 0xe4, 0x08, 0x13, 0x30, 0x99, + 0x1c, 0x19, 0x4e, 0xf6, 0x45, 0x65, 0xc1, 0x1a, 0x5b, 0x1f, 0x75, 0x41, + 0x8d, 0xba, 0x09, 0x05, 0x56, 0xc2, 0x4a, 0xca, 0x6d, 0x66, 0xf3, 0xa5, + 0xa2, 0x7d, 0x9e, 0x69, 0x8f, 0xd1, 0x75, 0x88, 0x08, 0x88, 0xb4, 0x94, + 0x15, 0x84, 0x4b, 0xbf, 0xf4, 0x5e, 0x97, 0xec, 0x2e, 0x1a, 0xe4, 0x40, + 0xda, 0x5e, 0xae, 0x89, 0xc2, 0xb3, 0x7f, 0x59, 0xe3, 0xa9, 0x44, 0xe6, + 0x69, 0x37, 0xba, 0x66, 0xf4, 0xd4, 0x64, 0x0d, 0x3d, 0xd2, 0x6b, 0xf8, + 0xdf, 0x8e, 0x2a, 0x6f, 0x44, 0xa6, 0x7f, 0xc2, 0x0a, 0x66, 0x0c, 0x89, + 0x35, 0xdd, 0x8e, 0x2e, 0x05, 0xe8, 0x9b, 0xd8, 0x97, 0x4f, 0x45, 0xfa, + 0x77, 0x7e, 0x70, 0xd1, 0x56, 0x92, 0xeb, 0x00, 0x95, 0x6b, 0xef, 0x82, + 0x36, 0xbd, 0x14, 0x34, 0x22, 0x15, 0x54, 0x55, 0x15, 0x51, 0xee, 0x77, + 0x5c, 0xb1, 0xb4, 0x21, 0x5e, 0x1e, 0x71, 0xfa, 0xae, 0xb3, 0xca, 0x62, + 0x83, 0x98, 0x05, 0x41, 0x26, 0x06, 0x8f, 0x01, 0x7c, 0xd2, 0x40, 0x0d, + 0x3b, 0x45, 0xc1, 0xbf, 0x49, 0xff, 0xcf, 0x80, 0x1e, 0xb8, 0x19, 0xec, + 0x9b, 0x30, 0xb2, 0x9c, 0xc1, 0x38, 0x9a, 0x3a, 0x42, 0x27, 0xba, 0xa6, + 0x6c, 0x57, 0xad, 0x9d, 0x2c, 0xa6, 0x35, 0xcf, 0x3b, 0x2d, 0x7a, 0xe2, + 0xc4, 0x92, 0x62, 0x62, 0xd4, 0xc0, 0xc1, 0x16, 0x17, 0x75, 0x75, 0x81, + 0xb4, 0x9a, 0x3c, 0x76, 0x26, 0xb9, 0x9d, 0x59, 0x9e, 0xae, 0xbe, 0x8e, + 0x5a, 0x9a, 0xbb, 0x87, 0x0a, 0x46, 0xb4, 0x1e, 0x1f, 0xd0, 0x67, 0x56, + 0x1f, 0x78, 0x64, 0xbe, 0x97, 0x00, 0xeb, 0x9f, 0x43, 0x01, 0x59, 0x56, + 0x71, 0x25, 0x8f, 0x5d, 0x23, 0x2a, 0xac, 0x1e, 0x4f, 0x3a, 0x28, 0x18, + 0xdd, 0x5b, 0x54, 0x68, 0x1d, 0x7a, 0x77, 0xf1, 0xe7, 0xe1, 0x8b, 0x52, + 0xe1, 0x07, 0xab, 0x1c, 0x0c, 0x82, 0x34, 0x5c, 0x94, 0xc8, 0xe1, 0x41, + 0x93, 0xf9, 0x99, 0x75, 0xeb, 0x2c, 0xdd, 0x67, 0x3e, 0x74, 0x22, 0x51, + 0x9d, 0xb6, 0xad, 0x96, 0x25, 0x42, 0x01, 0x82, 0x38, 0x28, 0xab, 0x73, + 0x40, 0xa0, 0x43, 0x6b, 0xdb, 0xb8, 0xe4, 0x90, 0x7a, 0xe6, 0xe4, 0xe6, + 0x2a, 0xe5, 0xe4, 0x60, 0x17, 0xe9, 0x5b, 0xce, 0xcd, 0x5e, 0xbf, 0x76, + 0xa6, 0x80, 0x7a, 0x35, 0x28, 0x6d, 0xb1, 0xc4, 0x61, 0x99, 0xf0, 0xa3, + 0x89, 0x54, 0xf7, 0xe9, 0xde, 0x43, 0xc4, 0xf0, 0xbf, 0x6a, 0xee, 0x0a, + 0xcb, 0xbf, 0xf4, 0x7f, 0x7d, 0xfa, 0x64, 0xfe, 0x7b, 0xd2, 0x53, 0x49, + 0x7a, 0x3c, 0x0d, 0x20, 0xa1, 0x93, 0x55, 0x1e, 0x63, 0x26, 0x65, 0x47, + 0xa1, 0xff, 0xde, 0x73, 0x1c, 0x46, 0x9f, 0x71, 0xce, 0x59, 0x68, 0x0b, + 0x28, 0x81, 0x26, 0xfe, 0xd5, 0x06, 0x1e, 0x73, 0xf7, 0xfe, 0x70, 0x66, + 0xc8, 0xe1, 0x42, 0x02, 0xc3, 0xc1, 0xeb, 0x4e, 0x09, 0x30, 0x3c, 0x2a, + 0xe8, 0xd0, 0xac, 0x38, 0x00, 0x36, 0x4e, 0x15, 0xaf, 0x83, 0x52, 0x99, + 0x73, 0x02, 0x00, 0x0c, 0x7c, 0xc8, 0x56, 0x4b, 0xff, 0xe0, 0x4e, 0x74, + 0xc0, 0x62, 0x2a, 0x3f, 0xa1, 0x07, 0x3a, 0x28, 0xa6, 0x6f, 0x08, 0xf7, + 0x5f, 0x85, 0x30, 0x7e, 0x4b, 0x61, 0xb2, 0xba, 0x3e, 0x2d, 0xfa, 0xba, + 0xef, 0x84, 0x08, 0x9b, 0xe2, 0x36, 0x8d, 0xc2, 0xd1, 0xae, 0x67, 0xe3, + 0xec, 0x37, 0x7d, 0x74, 0x5e, 0x4b, 0x13, 0xe6, 0x46, 0x07, 0xb2, 0x9d, + 0xea, 0xf8, 0x0c, 0x6a, 0x0a, 0x6d, 0xe9, 0xd3, 0xd4, 0x36, 0xbc, 0x2e, + 0x87, 0x00, 0xd1, 0xe9, 0x92, 0x74, 0xc6, 0xc9, 0xc0, 0x0a, 0xb4, 0x6a, + 0x3c, 0xe6, 0x28, 0xac, 0xad, 0x82, 0xe3, 0x1e, 0xbb, 0x8d, 0xa6, 0xd7, + 0xf9, 0xfb, 0xa5, 0xb3, 0xdf, 0x01, 0x4b, 0xd2, 0xcb, 0xc6, 0x33, 0x22, + 0xbd, 0xcc, 0xe0, 0xeb, 0x2d, 0xf6, 0x21, 0x48, 0xb0, 0x00, 0x53, 0xb5, + 0x56, 0x5d, 0x02, 0x6a, 0xea, 0xf8, 0x13, 0x5d, 0x80, 0x5e, 0xbb, 0xbb, + 0x9d, 0x46, 0x2a, 0x1c, 0x00, 0x3a, 0x0b, 0x81, 0xb5, 0xac, 0x57, 0xdf, + 0x0b, 0x1e, 0x85, 0x49, 0x9f, 0x2f, 0x97, 0x81, 0x22, 0x90, 0x10, 0xef, + 0xf0, 0x14, 0xca, 0xb4, 0x11, 0xaf, 0xbe, 0x8d, 0xbc, 0xc1, 0x61, 0xfc, + 0x53, 0xd0, 0x3a, 0xfa, 0x84, 0xd2, 0xa4, 0x5c, 0x0a, 0xee, 0xa5, 0xe5, + 0xe4, 0x53, 0x01, 0x77, 0x49, 0x52, 0xb0, 0x05, 0xb5, 0xdb, 0x5c, 0xf1, + 0xc6, 0x3c, 0xce, 0x3a, 0x07, 0xf4, 0xd8, 0xaa, 0x6a, 0x92, 0xea, 0x3c, + 0x6e, 0xc7, 0xe4, 0xa0, 0xaa, 0x25, 0xdf, 0xc8, 0x5b, 0x3e, 0xd3, 0x57, + 0xc9, 0xfb, 0x21, 0xec, 0x04, 0xba, 0x69, 0x9c, 0x2a, 0x73, 0x86, 0x41, + 0x0e, 0xf7, 0xc0, 0x9b, 0xe1, 0x54, 0xaa, 0xc9, 0x55, 0xf2, 0x46, 0xec, + 0xac, 0xf0, 0xbb, 0x22, 0xca, 0xc1, 0xbc, 0x80, 0x06, 0x32, 0x28, 0x2f, + 0xf5, 0x03, 0x85, 0x72, 0xe7, 0xd5, 0x37, 0xdd, 0x68, 0x73, 0xfe, 0x53, + 0xe8, 0x1c, 0xb7, 0x64, 0x94, 0x36, 0xd5, 0x69, 0x52, 0x46, 0x79, 0x75, + 0x5b, 0xd7, 0x89, 0xaf, 0x9f, 0x00, 0x65, 0x7f, 0x3c, 0x9c, 0x5f, 0xa8, + 0xbb, 0xd7, 0xd8, 0xd8, 0xfc, 0x84, 0x37, 0x07, 0xff, 0xf2, 0x02, 0xd8, + 0x14, 0x61, 0xff, 0x44, 0xc6, 0x91, 0x15, 0xdc, 0xe5, 0x69, 0xe1, 0x80, + 0xd4, 0x37, 0x89, 0x0b, 0x00, 0xdf, 0x77, 0x8b, 0xeb, 0xa2, 0x81, 0xc4, + 0x88, 0x7f, 0x1b, 0x77, 0xba, 0x30, 0xb1, 0xfe, 0x5a, 0x15, 0x1b, 0x66, + 0xac, 0x54, 0x7d, 0x55, 0xa5, 0xdc, 0x6b, 0xaf, 0x8e, 0x67, 0x70, 0xce, + 0x8f, 0x75, 0x2a, 0x58, 0x10, 0xf2, 0x64, 0x0e, 0x4d, 0x71, 0x1b, 0xd3, + 0x29, 0xeb, 0x9a, 0x43, 0xa6, 0xde, 0x89, 0x80, 0x18, 0xb7, 0xbc, 0x88, + 0xa3, 0x31, 0xb1, 0x71, 0x80, 0xed, 0x7b, 0x91, 0x89, 0x8d, 0x82, 0x3c, + 0x4b, 0x0d, 0x74, 0xd2, 0x0c, 0xcb, 0x95, 0x4e, 0x1d, 0xb1, 0xa9, 0x48, + 0xfc, 0x1c, 0xd8, 0x56, 0xf6, 0xa1, 0x27, 0xd1, 0x77, 0xd3, 0x63, 0x42, + 0x96, 0xe8, 0xc7, 0x82, 0x1c, 0xa5, 0xc4, 0x7f, 0xfb, 0xab, 0x87, 0x26, + 0x2e, 0xb5, 0x96, 0x17, 0x0c, 0x7a, 0xc7, 0xb7, 0x09, 0x9a, 0x6f, 0x30, + 0x74, 0x38, 0xbf, 0xad, 0xfb, 0x9d, 0xe8, 0xd7, 0x03, 0x29, 0x87, 0x85, + 0x24, 0x08, 0xc6, 0x5f, 0xda, 0xe0, 0x41, 0x09, 0x54, 0xab, 0x79, 0xd1, + 0x7d, 0x67, 0xfd, 0xc8, 0xa4, 0xbd, 0x77, 0x32, 0xc5, 0xdc, 0xb4, 0x1a, + 0x7b, 0xec, 0xff, 0x3d, 0x1e, 0x83, 0x3b, 0x1a, 0x6b, 0x80, 0x40, 0x29, + 0x8a, 0x35, 0x2e, 0xd4, 0xa0, 0x08, 0x6c, 0x34, 0xd1, 0x43, 0x24, 0xd7, + 0xe6, 0xc9, 0x1a, 0xb2, 0xee, 0x40, 0x49, 0x66, 0x37, 0xaa, 0x08, 0x29, + 0x22, 0x55, 0x13, 0x04, 0xc2, 0xe9, 0xd8, 0xb2, 0xa6, 0x50, 0x6a, 0xbe, + 0x79, 0x41, 0xdd, 0xce, 0x19, 0xf6, 0x54, 0x4b, 0xca, 0xf8, 0xf2, 0x5d, + 0xbd, 0xca, 0xc9, 0x40, 0xec, 0xcf, 0xdb, 0xe8, 0xa9, 0xe3, 0x92, 0x7f, + 0xc8, 0xc1, 0x49, 0x79, 0x9a, 0x80, 0xf8, 0xe3, 0x60, 0xce, 0x7d, 0xf2, + 0x49, 0xbb, 0x80, 0x1f, 0x54, 0x45, 0x0b, 0x90, 0xd7, 0xf9, 0x82, 0xa5, + 0xc7, 0x49, 0xca, 0x1b, 0x12, 0x55, 0x93, 0x6f, 0xea, 0x72, 0xf9, 0x64, + 0xb8, 0xec, 0x6b, 0x78, 0x85, 0xa6, 0xd2, 0x11, 0x71, 0x06, 0x56, 0x5f, + 0xe1, 0x66, 0xea, 0x5d, 0x9e, 0x0d, 0xa4, 0xc3, 0xd6, 0xe8, 0x86, 0xa5, + 0x2a, 0xd8, 0xf0, 0xf9, 0x7b, 0xdb, 0xc1, 0x4f, 0x52, 0xba, 0xff, 0x7f, + 0x1d, 0x8d, 0x9e, 0x5d, 0x39, 0xc4, 0x01, 0xc2, 0xf9, 0x6e, 0xdb, 0x6c, + 0x32, 0x5d, 0x8e, 0xb4, 0x68, 0x88, 0x79, 0x8b, 0x72, 0x30, 0x31, 0xa9, + 0x71, 0xd4, 0xef, 0x1b, 0x69, 0x37, 0x96, 0x3c, 0x4f, 0x8f, 0x54, 0xcf, + 0x1c, 0x4d, 0x37, 0x14, 0x6e, 0x78, 0xb7, 0x20, 0xc4, 0x59, 0x6d, 0x9c, + 0x66, 0x4d, 0x6b, 0x07, 0x6a, 0x0d, 0x5c, 0xda, 0xeb, 0x8a, 0xfd, 0x68, + 0x71, 0x2d, 0x9b, 0xb9, 0xec, 0x3b, 0x72, 0x5d, 0x06, 0x2e, 0x64, 0xbc, + 0xe4, 0xd2, 0xba, 0x96, 0x29, 0x2d, 0xb7, 0xdc, 0x79, 0xbb, 0xbb, 0xd3, + 0x12, 0x20, 0xa4, 0x7c, 0x59, 0xd7, 0xa0, 0xb7, 0xbb, 0x29, 0x62, 0x9a, + 0x86, 0xf4, 0x5d, 0x60, 0x56, 0x4f, 0xf2, 0x2c, 0x2a, 0x23, 0x49, 0x32, + 0x81, 0xea, 0xed, 0x60, 0xf6, 0x9d, 0x80, 0xb6, 0x53, 0xe6, 0x6c, 0xc4, + 0x06, 0xbd, 0xa9, 0x57, 0x00, 0x24, 0x75, 0x4e, 0xdc, 0xd2, 0xc0, 0xb6, + 0x5f, 0xb5, 0xc2, 0x1e, 0x64, 0x9c, 0x9d, 0xae, 0xd3, 0xea, 0xfa, 0x4e, + 0x22, 0x9a, 0x09, 0xc7, 0xee, 0x16, 0xad, 0x16, 0x60, 0x85, 0xba, 0x82, + 0xb8, 0x99, 0xa4, 0x7c, 0x00, 0xd0, 0xe9, 0x54, 0xaf, 0xd3, 0xb5, 0x6e, + 0x21, 0x50, 0x56, 0x8b, 0x77, 0x5e, 0x0a, 0xcf, 0x93, 0xaa, 0x5a, 0xed, + 0x75, 0x9c, 0x99, 0x8c, 0xfd, 0xa8, 0x0d, 0xb4, 0xa3, 0x3e, 0x99, 0x37, + 0xc8, 0x46, 0xd8, 0x01, 0x35, 0xf2, 0xf5, 0x35, 0x46, 0x35, 0x45, 0x96, + 0x37, 0x58, 0x50, 0x4f, 0xd3, 0xdb, 0x7b, 0x8b, 0x67, 0x10, 0x35, 0x01, + 0xc2, 0xb9, 0x04, 0x15, 0x0a, 0xf0, 0xe8, 0xa3, 0xc1, 0x96, 0x7e, 0xa8, + 0xb7, 0x00, 0xac, 0x2c, 0x81, 0x2e, 0x51, 0x2a, 0xec, 0x04, 0xcd, 0xd7, + 0x7c, 0x50, 0x4a, 0x76, 0xff, 0x69, 0x1b, 0x72, 0x49, 0x5a, 0x4f, 0xd7, + 0x3a, 0xad, 0x3b, 0x28, 0x48, 0x29, 0xc8, 0x32, 0x1e, 0xf5, 0x8e, 0x5c, + 0xfd, 0xf1, 0x17, 0xde, 0x63, 0x64, 0x49, 0x03, 0xbe, 0xb7, 0xe6, 0xfa, + 0x3e, 0xbb, 0x48, 0x64, 0x0d, 0x13, 0xf6, 0xcb, 0xbf, 0x84, 0xa5, 0x14, + 0x01, 0xb8, 0x57, 0x25, 0x19, 0xe8, 0x3f, 0x9c, 0xb0, 0x0c, 0xd0, 0xe8, + 0xdf, 0x96, 0x36, 0xcf, 0x80, 0x0e, 0x7b, 0x39, 0xba, 0xbe, 0x53, 0x4b, + 0x35, 0x04, 0x32, 0xf0, 0x8a, 0xa6, 0x0a, 0x89, 0x4b, 0x04, 0x7e, 0x52, + 0x8d, 0x76, 0x01, 0x57, 0x3c, 0x73, 0x08, 0x6a, 0x41, 0x6e, 0xff, 0x67, + 0x1b, 0x62, 0x94, 0x67, 0x21, 0x8c, 0x07, 0xfb, 0x64, 0x4b, 0xee, 0x70, + 0x57, 0x0a, 0xcc, 0x4e, 0x32, 0x08, 0xf1, 0xb1, 0x3b, 0x31, 0x57, 0xd5, + 0x2a, 0x2c, 0x30, 0xa7, 0x64, 0xee, 0x53, 0x0f, 0x8a, 0xe0, 0xd4, 0xaf, + 0x4f, 0xf6, 0x2f, 0x72, 0x19, 0xff, 0x38, 0xcc, 0x7b, 0xd8, 0x39, 0x0c, + 0x18, 0xcf, 0xc5, 0x87, 0x74, 0x93, 0xe0, 0x54, 0x93, 0xd9, 0xdd, 0x69, + 0x10, 0x99, 0x15, 0xed, 0x96, 0x90, 0x83, 0x4f, 0x6a, 0xf4, 0x4a, 0xe6, + 0x8b, 0x33, 0xe1, 0x0e, 0xef, 0xaa, 0xff, 0x9f, 0x5e, 0xc6, 0x0b, 0x6d, + 0xa1, 0x1f, 0x73, 0x64, 0x55, 0x05, 0x3c, 0x2b, 0xca, 0x9d, 0x40, 0xb4, + 0xc1, 0x3b, 0xe3, 0x14, 0xff, 0x61, 0x01, 0x7a, 0x65, 0xbc, 0x49, 0xec, + 0xfc, 0x93, 0xf6, 0x16, 0xd2, 0x71, 0xa4, 0x55, 0x36, 0x4a, 0x2d, 0x5c, + 0x47, 0x27, 0x40, 0xe7, 0x2e, 0x9c, 0xa7, 0x95, 0xe3, 0x74, 0x7f, 0xae, + 0xe0, 0x84, 0x15, 0xab, 0xe8, 0x83, 0x68, 0x34, 0x0c, 0x31, 0x90, 0x54, + 0x19, 0x6b, 0x0b, 0x36, 0x09, 0xdb, 0xc3, 0xea, 0x7c, 0x47, 0x44, 0xcd, + 0x85, 0x7d, 0x04, 0x22, 0x25, 0x54, 0xaa, 0x17, 0x87, 0x66, 0x94, 0x65, + 0x24, 0xec, 0xf5, 0xf9, 0xd2, 0x2f, 0x32, 0xf6, 0xb3, 0xcc, 0xd4, 0x77, + 0x94, 0xc3, 0xbc, 0xc3, 0xfb, 0x69, 0xe8, 0x6a, 0xdd, 0x3f, 0xd3, 0xf7, + 0x6b, 0xf1, 0x7f, 0xe1, 0x88, 0xee, 0x52, 0x34, 0x74, 0xc5, 0xe8, 0x87, + 0x8a, 0x38, 0xb6, 0x47, 0x15, 0x6a, 0xc3, 0x7c, 0x7e, 0x5c, 0x51, 0x00, + 0xf6, 0xbb, 0xc9, 0xc9, 0x69, 0x90, 0xd1, 0x6c, 0x9b, 0x2a, 0x38, 0x14, + 0x05, 0x1b, 0x7a, 0x2e, 0xe1, 0x7d, 0x58, 0xd7, 0xcb, 0x0c, 0xa5, 0xf4, + 0x6f, 0xe0, 0x4d, 0x5f, 0xa5, 0xba, 0xc9, 0x74, 0xeb, 0x8d, 0x0b, 0x0b, + 0x22, 0x9e, 0x65, 0x71, 0xb2, 0xf4, 0x8f, 0x12, 0xbc, 0xff, 0xad, 0x9f, + 0x54, 0xca, 0xce, 0x85, 0xd9, 0x1d, 0x92, 0x8b, 0xf2, 0x07, 0xc4, 0x8d, + 0xef, 0x5d, 0x78, 0xc6, 0xcb, 0x90, 0xba, 0x89, 0xee, 0xae, 0xbc, 0x62, + 0x59, 0xb6, 0x80, 0x08, 0x11, 0x01, 0xc7, 0xf9, 0x57, 0xf8, 0x56, 0xe3, + 0x2f, 0x6d, 0x65, 0x6f, 0xbe, 0x6a, 0xd3, 0x28, 0x1f, 0x98, 0x9b, 0x53, + 0xc8, 0xf3, 0xb7, 0x25, 0xb6, 0x3d, 0xb8, 0xb8, 0x07, 0x64, 0x21, 0xf5, + 0x64, 0x9c, 0x6e, 0x80, 0xe8, 0xb1, 0x43, 0xf7, 0xe3, 0x42, 0xaf, 0x88, + 0x26, 0x15, 0x1e, 0xdf, 0xd8, 0x94, 0x22, 0x29, 0x07, 0x2d, 0x98, 0xa1, + 0x0d, 0x24, 0xcd, 0x70, 0x81, 0x64, 0x8a, 0x53, 0x40, 0xd1, 0x3a, 0x97, + 0x36, 0xe4, 0x03, 0x7f, 0x5d, 0x50, 0x09, 0xfc, 0xca, 0x71, 0xdc, 0x91, + 0x7d, 0xd0, 0x62, 0x16, 0x5d, 0xcb, 0xe9, 0xba, 0x1d, 0x29, 0x34, 0x68, + 0x6c, 0x47, 0x17, 0x25, 0x0b, 0x0b, 0x1a, 0x98, 0x65, 0xa5, 0x57, 0xcf, + 0x4d, 0xac, 0xba, 0xae, 0x97, 0x12, 0xa2, 0xac, 0xaa, 0x8c, 0x0d, 0x2a, + 0x18, 0x12, 0x4e, 0xd7, 0x57, 0x3f, 0x33, 0xba, 0xd3, 0xfc, 0xf0, 0x02, + 0x1c, 0x0d, 0x7b, 0x7a, 0x02, 0x46, 0xab, 0x48, 0x4d, 0x3c, 0xb2, 0xf3, + 0xcf, 0x12, 0x1f, 0x01, 0xb0, 0xc7, 0x5c, 0xf5, 0xf8, 0x46, 0x76, 0xab, + 0x66, 0x32, 0x02, 0x3c, 0x87, 0x01, 0xee, 0xf4, 0xc2, 0x3c, 0x02, 0xf4, + 0x7f, 0x89, 0xef, 0x5a, 0x5b, 0xab, 0x7d, 0xfc, 0x8d, 0x92, 0x6a, 0x0f, + 0x21, 0x9b, 0x6b, 0xc3, 0x6a, 0x31, 0x0f, 0x27, 0xbd, 0xc9, 0xb7, 0x04, + 0x4a, 0xa3, 0xf4, 0x41, 0xf6, 0x45, 0xfd, 0x55, 0xee, 0xbe, 0xf2, 0xe1, + 0x14, 0xec, 0x98, 0x6a, 0x24, 0xcc, 0x26, 0xdb, 0x9a, 0x98, 0x3f, 0xc2, + 0x51, 0x74, 0x70, 0x9a, 0x52, 0x48, 0xac, 0x56, 0x5b, 0x52, 0x8f, 0xb4, + 0x15, 0x08, 0x61, 0xaf, 0x91, 0x6f, 0xe8, 0xde, 0xd6, 0xc4, 0x1d, 0x40, + 0x64, 0x1d, 0x80, 0xc4, 0xcb, 0x24, 0x0f, 0x37, 0xfb, 0xa9, 0xbe, 0xf6, + 0x29, 0xb7, 0xf9, 0x63, 0xf6, 0x8a, 0xb3, 0x56, 0x8b, 0x1b, 0x58, 0x10, + 0xea, 0xb0, 0x7e, 0x10, 0x73, 0x6b, 0xfc, 0x13, 0xc3, 0x5a, 0xb3, 0xd0, + 0x43, 0x51, 0x58, 0x97, 0x92, 0xe6, 0xb9, 0x80, 0x23, 0x5a, 0x3e, 0x42, + 0xc4, 0x08, 0xde, 0xac, 0xf8, 0xb8, 0x63, 0xfd, 0xb1, 0x8f, 0x23, 0xf2, + 0x56, 0x68, 0xf1, 0x05, 0xc9, 0xe9, 0x3a, 0x23, 0xb3, 0x35, 0xc0, 0x59, + 0xba, 0x03, 0x11, 0x2a, 0x0c, 0xaa, 0x7b, 0x81, 0x12, 0x48, 0xab, 0xbd, + 0x76, 0x85, 0x8c, 0xa5, 0x34, 0x0f, 0x89, 0x29, 0x14, 0x32, 0x1f, 0xf1, + 0xca, 0xf6, 0x99, 0x32, 0x2a, 0x49, 0xd9, 0x46, 0x23, 0x06, 0x31, 0x21, + 0x51, 0x1f, 0x09, 0x07, 0x6b, 0xab, 0x8d, 0x6c, 0x3a, 0xa9, 0x1e, 0xd5, + 0x27, 0x64, 0xf8, 0x71, 0x0f, 0x9d, 0x10, 0x9c, 0x83, 0x30, 0x95, 0x8f, + 0xcf, 0xb7, 0xbc, 0x9f, 0xf1, 0x5a, 0x12, 0x47, 0x39, 0xd3, 0xfe, 0x17, + 0x0c, 0x47, 0x87, 0xe4, 0xe1, 0x0f, 0x8f, 0xa5, 0x8a, 0x44, 0x4f, 0x22, + 0x22, 0x4b, 0x55, 0xef, 0x09, 0x39, 0x44, 0x15, 0x0e, 0xda, 0x49, 0xa0, + 0x79, 0x20, 0x29, 0xee, 0x38, 0x68, 0x4b, 0xaa, 0xec, 0x86, 0x85, 0x16, + 0x44, 0x81, 0x36, 0x51, 0xbc, 0x19, 0xae, 0x7b, 0x71, 0x9a, 0xa2, 0x99, + 0xb0, 0xbd, 0x37, 0x7d, 0x0b, 0x1a, 0x7e, 0x51, 0xa3, 0x23, 0xe7, 0x2f, + 0xe7, 0x86, 0x04, 0xe0, 0xa5, 0xbe, 0xdc, 0x25, 0x7c, 0x7b, 0xc6, 0x09, + 0x8e, 0x54, 0x56, 0x49, 0x38, 0xdc, 0xe0, 0x9e, 0x47, 0xe0, 0x27, 0xa3, + 0xa4, 0xcb, 0xc9, 0x63, 0x45, 0x90, 0xef, 0x39, 0xb7, 0x41, 0xe9, 0x47, + 0x3f, 0xa9, 0xd6, 0x3e, 0x22, 0x7c, 0xe3, 0x27, 0x17, 0x27, 0x84, 0x09, + 0x5d, 0xae, 0x41, 0x7e, 0xca, 0x12, 0xf2, 0x3e, 0x5f, 0x07, 0xe7, 0x8a, + 0xf8, 0xa8, 0xc3, 0x48, 0x0f, 0x00, 0x00, 0x52, 0x76, 0x69, 0xa9, 0xed, + 0x83, 0x23, 0x70, 0x36, 0x5d, 0x42, 0x85, 0xc4, 0x7b, 0x82, 0xb5, 0x1d, + 0xb5, 0x72, 0xb9, 0x38, 0x36, 0xef, 0x3d, 0xdf, 0xa6, 0xad, 0x5c, 0xf9, + 0x06, 0x13, 0x2f, 0xff, 0xa9, 0x7e, 0x3b, 0x45, 0x78, 0x3d, 0x32, 0xc8, + 0x79, 0x4f, 0x78, 0x8c, 0x62, 0x63, 0x1f, 0x2f, 0x87, 0x7c, 0x8b, 0x13, + 0xe4, 0x82, 0x22, 0xe7, 0x1e, 0x9a, 0xe9, 0xe6, 0xe7, 0x4b, 0xce, 0x1e, + 0xd6, 0xfa, 0x26, 0xa3, 0x7b, 0x5e, 0x39, 0xba, 0x21, 0xfc, 0xc4, 0x8e, + 0xd3, 0x9a, 0xa9, 0x1f, 0xb0, 0x6c, 0x84, 0x50, 0x8f, 0x9b, 0xf9, 0x2b, + 0x3b, 0x9e, 0xbb, 0x91, 0xda, 0x7e, 0xb7, 0x45, 0x7b, 0x04, 0xc4, 0x23, + 0x17, 0x10, 0xb9, 0x37, 0xab, 0x09, 0x3a, 0x32, 0x90, 0x0a, 0x56, 0x9c, + 0x5a, 0x9d, 0x59, 0x27, 0x20, 0xd4, 0xe8, 0xe1, 0x88, 0xd5, 0x93, 0xea, + 0xec, 0x14, 0x2f, 0x33, 0x3c, 0x0f, 0xc0, 0x02, 0x2f, 0xb2, 0x0b, 0xbf, + 0xa6, 0xa6, 0x3b, 0xc5, 0x85, 0xf1, 0x38, 0xd0, 0x50, 0xfb, 0xa2, 0xc5, + 0xd5, 0xe5, 0x8e, 0x15, 0x97, 0xbe, 0x95, 0xb0, 0x18, 0xc5, 0x35, 0x28, + 0xf0, 0x39, 0xc7, 0xfb, 0xc8, 0xb5, 0x02, 0x22, 0x3b, 0x95, 0x9a, 0x61, + 0x1a, 0x87, 0xfa, 0xbd, 0xdb, 0xd7, 0xba, 0xb9, 0xd0, 0x04, 0xb2, 0xfb, + 0x02, 0x38, 0x2d, 0x24, 0x6e, 0x03, 0x8d, 0xa6, 0x16, 0xec, 0x55, 0xc5, + 0xbc, 0x8c, 0xd9, 0xe6, 0xc7, 0x61, 0x96, 0xdc, 0x45, 0xc3, 0x2d, 0x6b, + 0xeb, 0x8b, 0x29, 0x1c, 0xcc, 0x39, 0x53, 0x03, 0x6f, 0x85, 0x2e, 0x1e, + 0x6c, 0xeb, 0xf8, 0x7d, 0x40, 0x5f, 0xb4, 0x59, 0x97, 0x90, 0x22, 0x2c, + 0xee, 0xc4, 0x0b, 0x15, 0xd2, 0x6d, 0x30, 0xee, 0x30, 0xe0, 0x39, 0xfd, + 0x4d, 0xd7, 0xc5, 0x8e, 0xf3, 0xa3, 0x68, 0x50, 0xe5, 0x91, 0x21, 0x39, + 0x84, 0xec, 0x4c, 0x32, 0x44, 0xb7, 0x2f, 0x53, 0x5b, 0xff, 0xd0, 0x2f, + 0x34, 0xc7, 0x1f, 0x1a, 0xa5, 0x10, 0xf8, 0x5f, 0x3c, 0xa7, 0xa3, 0x24, + 0x49, 0x90, 0x20, 0x4e, 0x89, 0x86, 0x71, 0x3b, 0xd2, 0x53, 0x5c, 0xe4, + 0x7b, 0x83, 0x57, 0xbc, 0x73, 0x0b, 0xd2, 0xeb, 0x36, 0x46, 0x17, 0xd9, + 0x7a, 0x44, 0x2c, 0x1d, 0xa1, 0x3a, 0x8c, 0x1f, 0xaa, 0x7c, 0xc6, 0x15, + 0xc7, 0x67, 0x70, 0x13, 0x1d, 0x08, 0x3c, 0xc4, 0xb4, 0x3a, 0x80, 0x40, + 0xef, 0x98, 0x45, 0x06, 0x66, 0xfd, 0x3f, 0xbb, 0x12, 0x28, 0x02, 0xd1, + 0x6d, 0x3c, 0x3e, 0xda, 0xbe, 0x3a, 0x19, 0x91, 0x8a, 0x05, 0x3e, 0x91, + 0x50, 0x9b, 0x83, 0xaa, 0x55, 0x45, 0x1b, 0xdc, 0x92, 0xde, 0x48, 0x70, + 0xe9, 0x8f, 0x9e, 0x39, 0xa6, 0x14, 0xf4, 0x94, 0x3a, 0xba, 0x6d, 0x11, + 0x60, 0x99, 0x86, 0x80, 0x16, 0x9f, 0xaa, 0x88, 0xfd, 0x97, 0x4b, 0xd4, + 0xca, 0x55, 0x4a, 0x8a, 0x72, 0xf5, 0x9f, 0x63, 0xdb, 0xe0, 0x83, 0x83, + 0x39, 0x4b, 0x85, 0x28, 0x4e, 0xc9, 0x64, 0xa7, 0xad, 0x52, 0xfe, 0x79, + 0xb5, 0x0c, 0x71, 0x05, 0xa2, 0x1e, 0x4d, 0x9c, 0xd8, 0x52, 0x41, 0xf4, + 0x3c, 0xfc, 0x9b, 0x79, 0xc2, 0x58, 0x94, 0x0c, 0xb4, 0x19, 0x8d, 0xd6, + 0x60, 0xf9, 0xff, 0x43, 0xfb, 0x29, 0xf2, 0x8a, 0x3a, 0x27, 0x57, 0x07, + 0x8a, 0xaf, 0x71, 0x8d, 0x47, 0xef, 0x8b, 0x01, 0x2c, 0x79, 0x58, 0xe1, + 0x22, 0x60, 0xc4, 0x21, 0x7b, 0x5e, 0xd7, 0xe1, 0xbd, 0x58, 0x44, 0x07, + 0x9f, 0xf8, 0x30, 0xf8, 0x11, 0x32, 0xc7, 0xa6, 0xe7, 0xab, 0x8e, 0xe1, + 0x74, 0x82, 0xb3, 0xf6, 0x9e, 0x66, 0xbc, 0x24, 0x4c, 0x6b, 0x96, 0x0c, + 0xf2, 0x61, 0xb2, 0x35, 0x27, 0x22, 0x83, 0x7b, 0xb4, 0x10, 0xf1, 0x67, + 0x1e, 0xc0, 0x9e, 0x01, 0xf8, 0x28, 0xc3, 0xd3, 0x9e, 0xa2, 0x8e, 0x79, + 0xe2, 0x39, 0x8f, 0x08, 0x97, 0xd4, 0x30, 0x6e, 0xf3, 0x6f, 0x6d, 0xeb, + 0x85, 0xaf, 0x14, 0x76, 0x92, 0x8f, 0x32, 0x36, 0x06, 0xc1, 0xcd, 0x08, + 0x4b, 0x93, 0xf6, 0x43, 0x0c, 0x03, 0xa8, 0x0f, 0x1c, 0x70, 0xc3, 0x37, + 0x2e, 0x33, 0x37, 0x7a, 0x08, 0x15, 0xd8, 0xac, 0xf6, 0x5a, 0x84, 0xff, + 0xe9, 0x93, 0xa7, 0x95, 0x91, 0x0b, 0x7d, 0x1c, 0xc2, 0x7c, 0xf0, 0x90, + 0xdc, 0x80, 0x54, 0x0b, 0x7b, 0x97, 0x97, 0xe5, 0x0e, 0xe8, 0x9e, 0x5a, + 0x4c, 0x41, 0x14, 0x7f, 0xfe, 0xa9, 0xc9, 0x76, 0x9b, 0x47, 0x99, 0xc6, + 0xfc, 0xfe, 0x05, 0x22, 0xda, 0x74, 0x40, 0x3f, 0xb6, 0xf9, 0x3f, 0xbc, + 0x79, 0x63, 0x91, 0xb1, 0xae, 0x31, 0x36, 0x60, 0x6c, 0x9e, 0x77, 0xbc, + 0xb4, 0xd5, 0xfe, 0xd1, 0xf7, 0xe7, 0xd1, 0x5f, 0xcc, 0xc2, 0xbe, 0x95, + 0x00, 0x9a, 0x78, 0x91, 0xfa, 0x99, 0x52, 0x57, 0x1f, 0x71, 0xb8, 0x1e, + 0x5e, 0xd2, 0x2f, 0x9f, 0x8a, 0x26, 0x0f, 0x85, 0xa5, 0x11, 0x19, 0x18, + 0x47, 0x30, 0x1b, 0xd9, 0xb2, 0x92, 0xf8, 0x1c, 0xba, 0xa3, 0x3a, 0x12, + 0xc6, 0x1f, 0x4d, 0xf0, 0xc1, 0x2a, 0x5b, 0x0c, 0xfe, 0x89, 0x88, 0xec, + 0xd5, 0x63, 0x88, 0x8f, 0x2d, 0x5c, 0x57, 0xe4, 0xc9, 0x4c, 0xc3, 0x55, + 0xbf, 0x01, 0x74, 0xde, 0x5a, 0x5b, 0x98, 0xb9, 0xa9, 0xf5, 0xf1, 0x30, + 0x77, 0x65, 0x0c, 0x76, 0xa3, 0x29, 0xa0, 0xec, 0x1b, 0xbc, 0xdb, 0x47, + 0x7c, 0x01, 0x1f, 0x78, 0x1f, 0x26, 0x09, 0xe0, 0xfa, 0x07, 0xc0, 0xa1, + 0x96, 0x5e, 0xf1, 0x2e, 0x30, 0x55, 0x03, 0xaf, 0xf9, 0x4c, 0xc6, 0x28, + 0x56, 0xfc, 0xab, 0xe4, 0x47, 0x7b, 0x1b, 0x8a, 0x5a, 0xe3, 0x59, 0x94, + 0x03, 0x87, 0x8d, 0xad, 0x0a, 0xaf, 0x40, 0x81, 0x99, 0x5a, 0x81, 0x21, + 0x29, 0xfe, 0x58, 0x0e, 0x86, 0x85, 0xde, 0x9f, 0xa6, 0x7f, 0x90, 0xa6, + 0xde, 0xc5, 0x4d, 0x83, 0x2c, 0x6f, 0xdd, 0x58, 0x32, 0x4d, 0x29, 0x74, + 0xdd, 0x65, 0xf8, 0xea, 0xb3, 0xd3, 0x55, 0x59, 0xd9, 0xed, 0xf3, 0x01, + 0xb6, 0x55, 0x8f, 0x9b, 0x49, 0x01, 0x89, 0xdb, 0x0b, 0xbb, 0xfb, 0x8b, + 0x9d, 0xb2, 0x16, 0x0a, 0xad, 0x6f, 0x07, 0x6b, 0x2b, 0xe9, 0xc5, 0x9b, + 0x60, 0xd4, 0x6d, 0xd1, 0x14, 0xaa, 0x78, 0x7b, 0x64, 0xf7, 0xe6, 0x52, + 0xee, 0xf4, 0x7f, 0xd3, 0x5b, 0x2c, 0xf2, 0xde, 0x12, 0x7b, 0x2d, 0x0a, + 0x9a, 0x1d, 0x62, 0x4a, 0x03, 0x64, 0x66, 0xbf, 0x11, 0xaa, 0x21, 0xe2, + 0xfd, 0xf6, 0x5f, 0x3d, 0x86, 0x3c, 0x04, 0xad, 0xdd, 0x17, 0x0f, 0x68, + 0x85, 0x4f, 0xbe, 0xca, 0x8a, 0x7d, 0xd6, 0xbd, 0x54, 0xce, 0x7a, 0x3c, + 0xf3, 0x90, 0xc7, 0xc9, 0xe8, 0xe3, 0x09, 0xc5, 0x4b, 0xf4, 0xf2, 0xe0, + 0xbe, 0x75, 0x74, 0x26, 0xaf, 0xbc, 0x99, 0xbf, 0xbd, 0x5d, 0xff, 0xe3, + 0x80, 0x20, 0xae, 0x76, 0xf3, 0x0b, 0xa7, 0xab, 0x4e, 0x95, 0x79, 0xf9, + 0xf4, 0xff, 0xf3, 0x3d, 0x96, 0xff, 0x91, 0x18, 0x70, 0x20, 0x32, 0xcb, + 0xdf, 0xaf, 0x8d, 0x6c, 0x08, 0x4b, 0xd7, 0x73, 0xc9, 0xd2, 0x43, 0xeb, + 0x43, 0x2d, 0x60, 0x15, 0x29, 0xb3, 0x86, 0xca, 0xe2, 0x79, 0x8d, 0x76, + 0x93, 0x0d, 0xaa, 0x20, 0x6a, 0xfc, 0x2d, 0x53, 0x27, 0x8c, 0x54, 0x49, + 0xad, 0x26, 0xeb, 0xbe, 0x73, 0x19, 0x17, 0x64, 0xac, 0x45, 0x2c, 0xe6, + 0x54, 0xcb, 0x87, 0x39, 0x13, 0x22, 0xa3, 0x0e, 0x0e, 0x1f, 0x89, 0x4d, + 0x43, 0x91, 0x91, 0xcd, 0x9e, 0xa9, 0x2f, 0x9a, 0x23, 0x80, 0x56, 0x1e, + 0x0b, 0x80, 0xf7, 0x49, 0xad, 0x60, 0xf6, 0x53, 0x42, 0x28, 0x3c, 0x55, + 0xaf, 0xd2, 0xe6, 0xab, 0x61, 0xaa, 0x0e, 0x7c, 0x4e, 0x85, 0x4e, 0x61, + 0xb4, 0xe1, 0x51, 0xec, 0x4f, 0x38, 0x55, 0x68, 0xdc, 0xf9, 0xda, 0x05, + 0xab, 0x09, 0xdd, 0xb2, 0xd9, 0x52, 0x92, 0xce, 0xad, 0xf8, 0x7a, 0xae, + 0xb3, 0x53, 0x7b, 0xae, 0xcb, 0x2e, 0x39, 0xb5, 0xf9, 0x71, 0x78, 0x49, + 0x61, 0xfc, 0xad, 0x5c, 0xdf, 0x63, 0x28, 0x43, 0x27, 0x74, 0xab, 0xf4, + 0x3e, 0x5f, 0x9c, 0x8e, 0x61, 0x17, 0xf3, 0xbb, 0x10, 0xcf, 0x7f, 0x8e, + 0x07, 0xd5, 0xab, 0x94, 0x01, 0x00, 0x67, 0x46, 0x70, 0x4f, 0x5e, 0xd8, + 0xc6, 0x92, 0xf2, 0x2d, 0x6f, 0x46, 0xb6, 0xc2, 0xcd, 0x10, 0x1c, 0x10, + 0x87, 0x1a, 0x27, 0x32, 0xf9, 0x94, 0xf7, 0x59, 0xdd, 0x2f, 0x9f, 0xf0, + 0xba, 0x52, 0xf3, 0xfd, 0xf9, 0x88, 0x85, 0xdb, 0x1b, 0xf7, 0x22, 0xa0, + 0xc0, 0x38, 0xcf, 0x57, 0x89, 0x0a, 0xc0, 0xd2, 0x7a, 0xc5, 0x10, 0x6e, + 0xae, 0x75, 0x60, 0x9b, 0xb3, 0xb3, 0x97, 0x49, 0x95, 0x3d, 0xa4, 0x35, + 0x39, 0xde, 0xa1, 0xfd, 0x7d, 0x8b, 0xff, 0x80, 0x80, 0xfb, 0x79, 0x4d, + 0xeb, 0x66, 0x54, 0x09, 0x92, 0xcc, 0xeb, 0x32, 0x86, 0xa1, 0x96, 0x42, + 0x86, 0xcc, 0x09, 0x02, 0x3d, 0xe2, 0x46, 0x46, 0x1b, 0x6a, 0xd3, 0xf8, + 0x3d, 0x31, 0x22, 0x5b, 0x80, 0x5a, 0x67, 0x07, 0x28, 0x22, 0x01, 0x05, + 0x2d, 0x66, 0xeb, 0x95, 0x8f, 0xd5, 0xf0, 0xea, 0xfb, 0xbf, 0xdc, 0x5d, + 0xe9, 0xac, 0xcc, 0x4e, 0x4a, 0xab, 0x73, 0x16, 0xe0, 0xd3, 0xe9, 0x11, + 0x64, 0xe5, 0xf8, 0x09, 0xf0, 0x24, 0xde, 0x17, 0x1d, 0x31, 0xd2, 0xe5, + 0xfb, 0x74, 0xb7, 0xd3, 0xef, 0x3a, 0x17, 0x9a, 0xb9, 0x8a, 0x1f, 0x61, + 0xdb, 0xc0, 0x2d, 0x93, 0xab, 0xb1, 0xe6, 0x47, 0x7e, 0x1b, 0x2e, 0xa2, + 0xb2, 0x32, 0x69, 0xa3, 0xf9, 0x46, 0x98, 0x89, 0xdd, 0xd7, 0xf9, 0xc6, + 0xf0, 0x4c, 0x9d, 0xe6, 0xa0, 0xb5, 0x4b, 0x03, 0xea, 0x5f, 0x73, 0xb8, + 0x85, 0x12, 0x56, 0xf6, 0xf3, 0x31, 0xf2, 0xc5, 0xff, 0x20, 0x6e, 0x37, + 0x1e, 0x32, 0x2a, 0xf0, 0x15, 0xe7, 0xa2, 0xed, 0xa4, 0x0d, 0x14, 0x44, + 0x49, 0xca, 0x55, 0x5d, 0xe8, 0xf7, 0x86, 0x99, 0xa6, 0x86, 0x94, 0x82, + 0x22, 0x8b, 0xa4, 0x37, 0x91, 0x04, 0x86, 0x26, 0x3d, 0x45, 0x38, 0x37, + 0x05, 0xfe, 0x86, 0xd9, 0x2c, 0xd0, 0xfd, 0x1e, 0x86, 0x42, 0x71, 0xfe, + 0x29, 0xf0, 0x8b, 0xf3, 0x7a, 0x08, 0x9f, 0xa6, 0x8c, 0x43, 0xb1, 0x8c, + 0x0d, 0x07, 0x30, 0x5d, 0x8c, 0x37, 0x7b, 0x8f, 0x7f, 0x45, 0xb8, 0x91, + 0x1b, 0xa4, 0xe1, 0xb5, 0xab, 0x78, 0x70, 0x0a, 0xcf, 0x78, 0xab, 0xbd, + 0x3b, 0xb7, 0x1a, 0xde, 0x5b, 0x76, 0xa2, 0x30, 0x6a, 0x04, 0xc4, 0xac, + 0xc6, 0xfb, 0xa2, 0xb5, 0x8a, 0x84, 0xa7, 0x07, 0x6f, 0x0b, 0x85, 0x3e, + 0xbc, 0xcc, 0x6b, 0x51, 0x13, 0x47, 0x5d, 0x5f, 0xa8, 0xdd, 0xfd, 0xf0, + 0x48, 0x0d, 0x52, 0x4f, 0xad, 0x0d, 0x7a, 0xdd, 0x23, 0x15, 0x48, 0xe3, + 0x83, 0xe3, 0x58, 0x7d, 0x65, 0x8a, 0x5a, 0xb8, 0x8f, 0xdf, 0xd0, 0x15, + 0xd8, 0x80, 0x16, 0x00, 0x96, 0x3d, 0xc9, 0x2e, 0xf1, 0xfe, 0x4d, 0xf9, + 0x68, 0x3d, 0x25, 0xe0, 0x25, 0x46, 0xca, 0x4c, 0xaa, 0x01, 0x34, 0x04, + 0xb9, 0xcc, 0xc3, 0x4f, 0xca, 0xcb, 0x47, 0xf1, 0x6d, 0xb5, 0xb0, 0x9d, + 0x83, 0xe4, 0xf4, 0x0d, 0xbd, 0x76, 0x5e, 0x4a, 0xb2, 0x62, 0xbe, 0x7b, + 0x71, 0x15, 0x1c, 0x0f, 0x22, 0xd1, 0x39, 0xd4, 0x5c, 0x63, 0xec, 0x8d, + 0x98, 0x7a, 0xa2, 0x0d, 0xc1, 0x27, 0xa4, 0x8a, 0x6d, 0xc5, 0x0b, 0xb2, + 0xe7, 0x10, 0x85, 0x62, 0xe5, 0xd3, 0xe5, 0x88, 0x56, 0x1f, 0x17, 0xcb, + 0xd0, 0xde, 0x55, 0x88, 0xb1, 0xf3, 0x49, 0x7d, 0xc4, 0x19, 0x81, 0xa4, + 0xa9, 0x62, 0x9d, 0xa9, 0xe2, 0xf8, 0xcd, 0xcf, 0xeb, 0x15, 0x78, 0x3c, + 0x86, 0x34, 0x85, 0xe9, 0x17, 0xf5, 0xe2, 0xa8, 0xae, 0xc8, 0xf4, 0x13, + 0x3c, 0x06, 0x68, 0x90, 0xba, 0xba, 0xb3, 0x14, 0xe4, 0x54, 0x02, 0x01, + 0x02, 0xac, 0x89, 0xfb, 0xd8, 0x1c, 0x16, 0x67, 0xdc, 0x63, 0x52, 0x48, + 0x4e, 0xd4, 0x34, 0xa7, 0x53, 0xc6, 0x11, 0x94, 0xc7, 0xb0, 0xf8, 0x56, + 0x4e, 0x8e, 0x32, 0x7d, 0xbd, 0xb7, 0x15, 0x2d, 0xd2, 0x18, 0x0d, 0xd5, + 0xd3, 0xe9, 0x96, 0x1d, 0xab, 0xa8, 0xf8, 0x4a, 0x8f, 0x83, 0xb0, 0x85, + 0xfc, 0x8d, 0x64, 0xa4, 0x6f, 0x35, 0x94, 0x22, 0x84, 0xe9, 0x80, 0x07, + 0x59, 0x36, 0x5e, 0xf1, 0x2c, 0xc9, 0x18, 0x1f, 0x98, 0x85, 0x32, 0xb2, + 0xf0, 0xac, 0x09, 0x2d, 0x7b, 0x49, 0x6b, 0x8b, 0xa5, 0xb1, 0x11, 0xd5, + 0x61, 0xfd, 0x52, 0xbd, 0xa3, 0x29, 0x9a, 0x26, 0x10, 0x55, 0x8d, 0x5e, + 0x2c, 0xef, 0x5e, 0x71, 0xd3, 0xbb, 0x99, 0x4c, 0x3b, 0x2d, 0xfc, 0xc5, + 0xb2, 0xae, 0x94, 0x72, 0xb9, 0x0c, 0xd6, 0x69, 0x7a, 0x5f, 0x2b, 0xc1, + 0x13, 0xf5, 0x5a, 0x9d, 0x69, 0x98, 0x83, 0x54, 0x96, 0x1b, 0xba, 0x59, + 0x52, 0xd7, 0x02, 0x28, 0xf9, 0xb7, 0x32, 0x20, 0x6a, 0x46, 0x38, 0xc9, + 0xe8, 0x70, 0x01, 0x87, 0x7c, 0x9d, 0x6f, 0xe8, 0x44, 0x59, 0x3d, 0x47, + 0xd6, 0xc6, 0x9c, 0xff, 0x5e, 0xa1, 0xb7, 0x6e, 0x25, 0x49, 0xa7, 0xca, + 0x25, 0x7a, 0x77, 0x45, 0x27, 0xae, 0xa9, 0xb9, 0x28, 0x62, 0x56, 0x7a, + 0x97, 0x15, 0x30, 0xe2, 0xd8, 0xcd, 0x01, 0xa5, 0x50, 0x85, 0xe7, 0xb9, + 0x80, 0x48, 0x3f, 0x98, 0x55, 0x37, 0x52, 0x1d, 0x9c, 0x7f, 0xe9, 0x5b, + 0xed, 0xd4, 0x9e, 0x34, 0x17, 0xf9, 0x4c, 0x8b, 0x1a, 0xd5, 0x31, 0x78, + 0x52, 0xf8, 0x5e, 0x45, 0xce, 0x74, 0x0f, 0x97, 0xa5, 0x8e, 0x70, 0xc0, + 0x7c, 0xb9, 0xb2, 0x73, 0xbd, 0x9f, 0xfa, 0x5a, 0x15, 0xff, 0x87, 0x6b, + 0x7c, 0x2e, 0xac, 0x64, 0x36, 0x45, 0x88, 0x7c, 0xf7, 0x1d, 0xc9, 0x6d, + 0x76, 0xb2, 0x68, 0xc4, 0x84, 0xbf, 0x72, 0xae, 0x41, 0x03, 0xcb, 0x5d, + 0x0d, 0xc5, 0x64, 0x44, 0xed, 0x93, 0x8e, 0x0c, 0xef, 0xc7, 0x02, 0xca, + 0x34, 0x06, 0xe7, 0x2d, 0xbd, 0x41, 0xa3, 0xc4, 0x6a, 0xee, 0xa6, 0x1a, + 0x92, 0x82, 0x45, 0x74, 0x9b, 0xea, 0x2f, 0xc8, 0x47, 0x1b, 0xa8, 0x7d, + 0x4d, 0x0d, 0x09, 0xc4, 0x3a, 0x83, 0xf6, 0xd7, 0x18, 0x80, 0x11, 0x0d, + 0x7a, 0xdc, 0xb1, 0xaa, 0xb4, 0xe4, 0x93, 0x9c, 0x98, 0xc4, 0xa0, 0x5b, + 0xa4, 0x43, 0xda, 0x49, 0x1a, 0xb8, 0xd1, 0x03, 0xf8, 0xe2, 0x93, 0x70, + 0x80, 0x07, 0xea, 0xc2, 0xb6, 0x45, 0xf2, 0x07, 0xac, 0x74, 0x60, 0x8e, + 0x26, 0x7c, 0xfe, 0xd9, 0x0e, 0x42, 0x78, 0x7c, 0xfe, 0x34, 0x96, 0x89, + 0xed, 0xe2, 0x9d, 0x88, 0x70, 0x72, 0xb0, 0x62, 0xe4, 0xa9, 0x74, 0x77, + 0x45, 0xf0, 0x6e, 0x7f, 0x21, 0x50, 0x92, 0xac, 0x4c, 0x7a, 0xe1, 0x51, + 0x2b, 0x7b, 0x8d, 0xfb, 0x2e, 0xdc, 0xfa, 0x8b, 0xb6, 0x48, 0xe4, 0xcb, + 0x99, 0xf4, 0x29, 0x4b, 0xe8, 0x30, 0x02, 0xb4, 0xef, 0xc1, 0xfb, 0xea, + 0x26, 0x38, 0xf3, 0x2e, 0xbd, 0x4e, 0x16, 0x4e, 0xc7, 0x5c, 0x69, 0x10, + 0x17, 0xe2, 0xcb, 0x89, 0x82, 0x71, 0xbf, 0x65, 0x1c, 0x54, 0xaa, 0xbd, + 0x2b, 0x27, 0xc6, 0xa4, 0xe1, 0xfd, 0xbf, 0xa6, 0xc6, 0x8e, 0x11, 0x20, + 0x23, 0x7e, 0x7c, 0x04, 0x9a, 0xb3, 0x26, 0x91, 0x18, 0x1f, 0x35, 0x01, + 0x78, 0xdc, 0x93, 0xcc, 0x1a, 0xf1, 0x9a, 0x1b, 0x48, 0x43, 0x15, 0x75, + 0xba, 0xd7, 0x3c, 0xf4, 0x50, 0xd0, 0x81, 0x23, 0x37, 0x09, 0xf1, 0x52, + 0xd7, 0x9b, 0x07, 0x19, 0xed, 0x3f, 0xe7, 0xfb, 0xf3, 0x19, 0x9a, 0x3e, + 0x42, 0x1d, 0xa3, 0xf5, 0xb9, 0x8b, 0x34, 0x6c, 0x44, 0xb8, 0x01, 0x18, + 0xa0, 0x05, 0x58, 0xf3, 0x7c, 0x41, 0x51, 0x6a, 0x24, 0x8e, 0x22, 0x6a, + 0x66, 0x8d, 0x3c, 0x15, 0xed, 0x63, 0x3a, 0x33, 0x2c, 0x59, 0x64, 0x45, + 0x3f, 0x78, 0xfa, 0xb5, 0xdc, 0xa6, 0x79, 0xe1, 0xbe, 0xac, 0x57, 0xd9, + 0x45, 0xd3, 0x3d, 0xc4, 0xb7, 0xde, 0xc6, 0x65, 0x26, 0x47, 0xc2, 0xc8, + 0xd2, 0x9c, 0xc9, 0x34, 0xb7, 0x9d, 0x38, 0x54, 0x3f, 0x4a, 0xb5, 0x76, + 0x68, 0xaa, 0xa6, 0x8a, 0x57, 0x57, 0x49, 0x13, 0xf3, 0x6d, 0xb5, 0xbe, + 0xf6, 0x23, 0x74, 0x36, 0x38, 0x7c, 0x18, 0x83, 0x6f, 0xa5, 0xa4, 0xf2, + 0x57, 0xf6, 0x32, 0x2d, 0xdd, 0x76, 0x45, 0xb9, 0x72, 0xb8, 0xaf, 0x73, + 0x6c, 0x35, 0xe0, 0x19, 0x7e, 0x17, 0x05, 0x81, 0xe7, 0xa0, 0x16, 0xce, + 0xf8, 0x68, 0xd8, 0x56, 0xad, 0x52, 0x91, 0x4f, 0x54, 0xe3, 0x50, 0x9c, + 0x24, 0xbb, 0x93, 0x59, 0x59, 0x0a, 0xbf, 0xb6, 0xf7, 0x2e, 0xcd, 0x78, + 0x28, 0xa1, 0x23, 0x03, 0x4a, 0x01, 0x36, 0x67, 0x68, 0xe3, 0x4a, 0xba, + 0x13, 0x49, 0xef, 0xf6, 0x1a, 0x33, 0xe5, 0x42, 0x9a, 0x59, 0x93, 0xe1, + 0x86, 0x60, 0x39, 0x1f, 0x89, 0x18, 0x2f, 0x63, 0xee, 0x14, 0x1b, 0xe4, + 0x33, 0x89, 0x66, 0x25, 0x09, 0xde, 0xf3, 0xb3, 0x8a, 0x47, 0xab, 0x5d, + 0x0f, 0xa9, 0xd8, 0x62, 0xcb, 0x66, 0x64, 0xdd, 0x6e, 0x8f, 0x69, 0x1a, + 0xf1, 0x83, 0xe9, 0x66, 0x66, 0xb0, 0xd6, 0x55, 0x9d, 0x95, 0xc8, 0x8c, + 0xbb, 0xce, 0x78, 0x8c, 0xad, 0x72, 0x0c, 0x60, 0x24, 0x16, 0x3a, 0xa5, + 0x1a, 0xdc, 0xc5, 0xff, 0xe7, 0x2b, 0x4b, 0x69, 0x40, 0x28, 0xae, 0x43, + 0xbb, 0x21, 0x3f, 0x9f, 0x26, 0x05, 0x22, 0x26, 0xb5, 0xc9, 0xc0, 0xf9, + 0xdd, 0x2e, 0x9c, 0x1a, 0x26, 0x60, 0xcf, 0xff, 0x60, 0x7d, 0xf7, 0x83, + 0x56, 0x28, 0x67, 0x66, 0x07, 0xcd, 0x07, 0x36, 0xba, 0x72, 0x50, 0x23, + 0x93, 0x3c, 0xbd, 0x6d, 0x4a, 0xc0, 0xc6, 0xf4, 0xab, 0x51, 0x84, 0x03, + 0x26, 0x18, 0x7e, 0x99, 0xd8, 0xcb, 0x85, 0x7d, 0xa5, 0x19, 0x73, 0x90, + 0xd5, 0x86, 0xf9, 0x65, 0x12, 0xa1, 0xd0, 0x8c, 0xf7, 0x94, 0x0c, 0x1f, + 0xa5, 0x02, 0xe0, 0xbc, 0x53, 0x4b, 0x46, 0x63, 0xa8, 0x72, 0xf0, 0x24, + 0x65, 0x69, 0x13, 0xb6, 0x9f, 0x46, 0xf7, 0x26, 0x03, 0x5e, 0x83, 0x77, + 0x03, 0x32, 0x6c, 0x18, 0xc8, 0x0d, 0x96, 0x93, 0x59, 0x5f, 0xdb, 0x55, + 0x0f, 0xf2, 0x66, 0x6e, 0xf6, 0x2a, 0x3a, 0xba, 0x06, 0x64, 0x6f, 0x90, + 0x77, 0x8e, 0xda, 0xe1, 0x28, 0xb0, 0x02, 0xa5, 0x39, 0xfb, 0xa0, 0x19, + 0xee, 0x40, 0x49, 0x23, 0x6c, 0x66, 0x49, 0x2d, 0x5c, 0xf7, 0x7f, 0x63, + 0x44, 0xb7, 0x9b, 0x56, 0xae, 0xc8, 0x49, 0xbf, 0xde, 0x1f, 0xe9, 0xfc, + 0x37, 0xea, 0x96, 0x53, 0x92, 0xfe, 0x7a, 0x67, 0x3e, 0x7d, 0x2d, 0xb6, + 0x4e, 0xc6, 0x02, 0x2f, 0x13, 0x79, 0x6c, 0x93, 0x93, 0xc0, 0xaf, 0xba, + 0x1f, 0xc3, 0xfa, 0xb4, 0xeb, 0x35, 0x92, 0xdd, 0x8c, 0xdc, 0x46, 0x06, + 0x51, 0x5d, 0xdf, 0x78, 0x3b, 0xa2, 0x0e, 0x0f, 0xf7, 0x40, 0x6e, 0x90, + 0x60, 0xbf, 0x69, 0x80, 0x8c, 0xcf, 0x89, 0x23, 0x5a, 0xf0, 0xae, 0x14, + 0x63, 0x30, 0x70, 0xa8, 0x8a, 0x39, 0x6d, 0x51, 0x6e, 0x7c, 0x50, 0x2f, + 0xec, 0xb7, 0x94, 0x04, 0xdf, 0xfd, 0x56, 0x73, 0xb9, 0x71, 0x3e, 0xdd, + 0x5a, 0x99, 0x30, 0x9e, 0x67, 0x81, 0x7a, 0xd0, 0x2f, 0xd2, 0x9e, 0xbf, + 0x8c, 0x0a, 0x05, 0x05, 0x78, 0x99, 0x87, 0x36, 0xb0, 0xbe, 0xd1, 0x7a, + 0x56, 0x10, 0x9f, 0xf3, 0x5b, 0x7a, 0x0c, 0xcb, 0x43, 0xe6, 0x43, 0x75, + 0xa1, 0x7a, 0x54, 0x28, 0xd6, 0x54, 0x43, 0xaf, 0x52, 0x2f, 0xb5, 0xdf, + 0x9f, 0xcd, 0x37, 0xb9, 0xc0, 0xec, 0x00, 0xda, 0xb5, 0x9f, 0x39, 0x84, + 0x8c, 0x57, 0x59, 0x09, 0xe7, 0x1a, 0x21, 0xeb, 0xad, 0xde, 0x77, 0xe0, + 0xbf, 0x76, 0x57, 0x14, 0x12, 0xc6, 0x42, 0x1a, 0x29, 0x23, 0x57, 0x0c, + 0x03, 0x71, 0xef, 0xf0, 0x77, 0xc4, 0xb2, 0x42, 0x92, 0x12, 0xda, 0x6f, + 0x05, 0x96, 0xca, 0x1c, 0x2c, 0xa3, 0xdc, 0x9c, 0x92, 0x75, 0x44, 0xa0, + 0xbc, 0x6e, 0x19, 0x1f, 0x7d, 0x70, 0xb2, 0x0d, 0x12, 0x56, 0x64, 0xa9, + 0xa1, 0x2f, 0x7e, 0x39, 0x78, 0xf2, 0xec, 0x15, 0x41, 0x18, 0x03, 0x36, + 0x00, 0x3d, 0x61, 0xa6, 0x18, 0x69, 0x1e, 0x84, 0x60, 0x27, 0x45, 0x5b, + 0xe5, 0x2e, 0x56, 0xe6, 0x1a, 0x1c, 0x55, 0x2d, 0xf7, 0x09, 0xa1, 0x31, + 0xf7, 0x25, 0xe0, 0x4f, 0xfd, 0xaa, 0xad, 0xfe, 0xf2, 0x54, 0xc1, 0xd0, + 0x5c, 0xbc, 0xe7, 0x8a, 0x91, 0x6e, 0x9a, 0xd5, 0x85, 0x43, 0x72, 0x82, + 0x79, 0x28, 0x42, 0x35, 0x11, 0x69, 0xf5, 0x65, 0xc1, 0x61, 0xa1, 0x00, + 0x75, 0x87, 0x01, 0x17, 0x8a, 0xba, 0xa1, 0x9d, 0x97, 0x51, 0x9c, 0xdf, + 0x1d, 0xe9, 0xe2, 0x87, 0x22, 0xc7, 0x78, 0x4f, 0x6c, 0xd6, 0x75, 0xeb, + 0x0a, 0x21, 0xee, 0x40, 0x85, 0x5c, 0x60, 0x63, 0x4c, 0x54, 0x0c, 0x9b, + 0x36, 0x99, 0x22, 0xe2, 0x21, 0xb3, 0x88, 0x9d, 0x0d, 0xde, 0x4f, 0x5d, + 0x74, 0x03, 0x66, 0x99, 0x76, 0x9b, 0x87, 0x04, 0x47, 0xaa, 0x24, 0xcc, + 0x83, 0x48, 0x32, 0x17, 0x10, 0x92, 0x83, 0xf9, 0xed, 0xd3, 0xe9, 0xc4, + 0x65, 0xb4, 0xe6, 0x57, 0x1e, 0x9f, 0x69, 0x0f, 0x63, 0x76, 0x86, 0x76, + 0x70, 0xae, 0xb8, 0xd3, 0xbe, 0x94, 0x7d, 0x81, 0x2a, 0xde, 0x15, 0x88, + 0xc2, 0x15, 0xcd, 0x7f, 0xfc, 0x47, 0xd4, 0xe3, 0xfc, 0x2e, 0xa3, 0xac, + 0x62, 0x65, 0xea, 0xd8, 0xef, 0x03, 0x0e, 0xda, 0x89, 0x8c, 0xa2, 0xc3, + 0x49, 0x3e, 0xac, 0x17, 0x5f, 0xea, 0x3d, 0x6e, 0x36, 0x0c, 0x00, 0x09, + 0x2a, 0x65, 0x7d, 0x14, 0x37, 0x3f, 0x5c, 0x74, 0x14, 0x36, 0x28, 0x55, + 0x9f, 0xfc, 0xe2, 0x45, 0xb1, 0x47, 0x87, 0x88, 0x0e, 0x5b, 0xad, 0xe8, + 0x42, 0xbf, 0xd6, 0xc0, 0xbd, 0x7d, 0x1d, 0x93, 0xd1, 0x48, 0x78, 0x09, + 0xe7, 0x49, 0xaf, 0xa0, 0x5f, 0x93, 0x9b, 0xa6, 0xad, 0xa1, 0x1e, 0xf8, + 0xc5, 0x2d, 0x43, 0xb0, 0x1d, 0x71, 0xbf, 0x6b, 0xe4, 0xae, 0xe9, 0x15, + 0x34, 0x22, 0x61, 0xf5, 0x34, 0xa7, 0x7e, 0x54, 0xf1, 0x0e, 0x63, 0x1f, + 0x38, 0x93, 0xd1, 0x3d, 0x1d, 0x2c, 0xc4, 0xc0, 0x28, 0x4a, 0x8e, 0x0b, + 0x3d, 0x40, 0x8f, 0xd3, 0xf5, 0x13, 0x13, 0x6e, 0xb4, 0x5c, 0xe7, 0x38, + 0xd8, 0x60, 0xb3, 0xc1, 0xaa, 0x99, 0x0a, 0x28, 0xc9, 0x3a, 0x44, 0x64, + 0x5e, 0x6a, 0x70, 0x53, 0xd6, 0x0b, 0x71, 0x26, 0x4d, 0x0e, 0x54, 0xa2, + 0x16, 0x9f, 0x1f, 0x4a, 0x84, 0xd1, 0x53, 0xc5, 0xa0, 0xc6, 0xd1, 0x82, + 0x07, 0x8a, 0x2b, 0x32, 0x90, 0x79, 0xa3, 0xd5, 0x58, 0x75, 0x56, 0x82, + 0x10, 0x87, 0x7f, 0x36, 0xa9, 0x61, 0xde, 0xe0, 0xff, 0xcb, 0x07, 0x58, + 0x3f, 0xf1, 0x43, 0xf0, 0xc9, 0xb2, 0xb6, 0xb7, 0xfe, 0x4f, 0xae, 0x93, + 0xed, 0x44, 0xc8, 0x46, 0xa7, 0x2a, 0x0e, 0x3b, 0xc9, 0x2f, 0x61, 0x70, + 0x78, 0x73, 0xde, 0xcb, 0x7c, 0xfd, 0xbf, 0xf3, 0xc5, 0x5f, 0xe8, 0x5b, + 0xb2, 0x54, 0x9d, 0x53, 0x1f, 0x20, 0xc3, 0x2e, 0x55, 0x87, 0x3b, 0x60, + 0x7e, 0x31, 0xb1, 0x43, 0xdd, 0x86, 0x96, 0x67, 0x38, 0x46, 0x10, 0xf5, + 0x29, 0x7a, 0xb2, 0x70, 0x99, 0xf6, 0xaa, 0xd3, 0x45, 0xb8, 0x38, 0xba, + 0xd7, 0x2a, 0xdd, 0xd4, 0xbd, 0x0d, 0x8a, 0xc0, 0xb2, 0xb3, 0x2f, 0xf8, + 0xa7, 0x6d, 0x55, 0x92, 0xc1, 0x14, 0x80, 0xbf, 0xca, 0x22, 0x3f, 0xfe, + 0xf7, 0x8a, 0xa4, 0xb9, 0xb0, 0x18, 0x9d, 0x2a, 0x80, 0x14, 0xa0, 0x1e, + 0x2b, 0xa3, 0xb5, 0xa8, 0xff, 0x5e, 0xc2, 0xf1, 0x11, 0xb9, 0x2b, 0x0c, + 0x0f, 0xe5, 0x88, 0xda, 0x80, 0x8a, 0x44, 0xc3, 0xc1, 0x03, 0xc8, 0x0f, + 0x76, 0x51, 0x90, 0x0c, 0x89, 0x09, 0x70, 0x64, 0x4d, 0x46, 0xf0, 0xb7, + 0x82, 0x27, 0x60, 0x94, 0x6b, 0x58, 0x78, 0x23, 0x8b, 0xf2, 0xfa, 0xe4, + 0x1e, 0x93, 0x44, 0x1d, 0xfd, 0x95, 0xc8, 0xce, 0x78, 0x3b, 0xf4, 0xd3, + 0x38, 0x8a, 0x35, 0x90, 0x89, 0xaf, 0x23, 0x0f, 0x1b, 0x54, 0x99, 0x5a, + 0x3b, 0x4e, 0xc3, 0x5f, 0xc6, 0xc3, 0xb2, 0x97, 0x5b, 0x7a, 0x5f, 0x07, + 0x4c, 0xd1, 0x5c, 0x18, 0xd8, 0x43, 0xa1, 0xd5, 0x8b, 0x3d, 0xbc, 0x97, + 0x6f, 0xe5, 0xbc, 0xb8, 0x2a, 0x67, 0xc2, 0xaa, 0x2b, 0x0d, 0x1f, 0x9d, + 0xde, 0x39, 0xa9, 0x8d, 0xd4, 0xcc, 0xa4, 0x1a, 0xc6, 0xaa, 0x80, 0xf5, + 0x62, 0x22, 0xef, 0x49, 0xcc, 0x20, 0x9e, 0x44, 0xaa, 0x86, 0xb2, 0xcf, + 0x2d, 0x8f, 0x46, 0x15, 0xae, 0xb4, 0xc3, 0xba, 0x52, 0x53, 0x02, 0x63, + 0x1b, 0x47, 0xc8, 0xc2, 0x77, 0xbf, 0xd2, 0xb0, 0x21, 0xb6, 0xe5, 0x48, + 0xf8, 0x8c, 0x75, 0x75, 0xee, 0x82, 0x6a, 0xae, 0x65, 0xef, 0x9a, 0xcb, + 0xb1, 0xb1, 0x64, 0x44, 0xf3, 0x5a, 0xea, 0xdc, 0xba, 0x39, 0x37, 0x25, + 0x50, 0x08, 0x32, 0xbc, 0x61, 0x0b, 0x8a, 0x12, 0x28, 0x15, 0xbb, 0xfe, + 0xc3, 0x5a, 0xd5, 0x71, 0xaf, 0x0b, 0x95, 0xea, 0x3f, 0xa1, 0x09, 0x6d, + 0x02, 0x32, 0xf0, 0x29, 0xbe, 0x9f, 0xd6, 0xfc, 0x07, 0x7f, 0x34, 0xb2, + 0x78, 0xdc, 0x56, 0x3c, 0xf6, 0x31, 0x03, 0x72, 0x2a, 0xb6, 0xb1, 0x4b, + 0x05, 0x0b, 0x57, 0x86, 0x0c, 0x35, 0x9d, 0x3c, 0xca, 0x89, 0xe0, 0x55, + 0xfc, 0xfa, 0x14, 0xb4, 0xd6, 0xf8, 0x34, 0xd9, 0xa9, 0xcf, 0x4b, 0x35, + 0x5d, 0xc1, 0x05, 0x53, 0x0f, 0x9c, 0x0b, 0x10, 0xcd, 0xd4, 0x67, 0x28, + 0xa4, 0x58, 0x00, 0x9d, 0x18, 0xdb, 0x64, 0x2b, 0x6a, 0x24, 0xa3, 0xbb, + 0x90, 0x1c, 0x96, 0xda, 0x77, 0xa8, 0x4a, 0x02, 0xd5, 0x44, 0xf8, 0x58, + 0x48, 0x77, 0xec, 0x9d, 0x34, 0xdf, 0x9e, 0xb7, 0xe4, 0xc7, 0x77, 0x1e, + 0x06, 0x31, 0xdf, 0xcd, 0xdc, 0xc3, 0x6c, 0x53, 0xfe, 0x92, 0x87, 0xfe, + 0x28, 0xc8, 0x97, 0x59, 0x42, 0x42, 0xf0, 0xfd, 0x28, 0x3f, 0x24, 0x7a, + 0x09, 0x24, 0xcc, 0xb8, 0x18, 0x28, 0xa9, 0x6d, 0xa8, 0x87, 0x0f, 0x4d, + 0xf8, 0xb2, 0x92, 0x00, 0xd2, 0x0f, 0x51, 0x31, 0x75, 0xc7, 0x4e, 0x75, + 0x72, 0xb2, 0xe3, 0x2a, 0x49, 0xc7, 0x12, 0x9b, 0xb7, 0xa6, 0xd1, 0x05, + 0x0a, 0x02, 0xdf, 0x7c, 0xf0, 0x4a, 0x20, 0xce, 0x1e, 0x79, 0xae, 0x23, + 0x54, 0xb8, 0xd3, 0x8a, 0xb9, 0x9e, 0x25, 0x81, 0x4f, 0x0c, 0x6f, 0x49, + 0x95, 0xb0, 0x72, 0x5e, 0x37, 0x82, 0x79, 0x04, 0xa0, 0x47, 0x16, 0x7e, + 0x37, 0x16, 0x70, 0xf0, 0xdb, 0xe4, 0x67, 0xcd, 0xed, 0x63, 0x94, 0xe8, + 0x17, 0x20, 0x25, 0xa6, 0xd7, 0xb5, 0x09, 0x92, 0x31, 0xb6, 0x48, 0x0f, + 0x57, 0xef, 0xba, 0xe5, 0x0d, 0x31, 0x4b, 0xf7, 0x7c, 0x86, 0xbf, 0x43, + 0x83, 0xe7, 0xc2, 0xe9, 0xf5, 0xbb, 0x8e, 0xcb, 0xc7, 0xb5, 0x5d, 0xc7, + 0x7c, 0x7b, 0xa8, 0xfc, 0x92, 0x89, 0xcf, 0x5a, 0x85, 0xff, 0x0d, 0xaa, + 0x0a, 0xd8, 0x27, 0xd9, 0xbe, 0x9e, 0x4a, 0xc1, 0x55, 0xe9, 0x49, 0x19, + 0xa2, 0x65, 0xcc, 0xe6, 0x84, 0x0e, 0x0a, 0xe1, 0x70, 0x38, 0x3a, 0x7f, + 0x4f, 0xb5, 0x18, 0xc4, 0x41, 0x5a, 0x51, 0xbd, 0x14, 0xa4, 0xf8, 0xa5, + 0xc5, 0x84, 0xeb, 0x71, 0xce, 0x4c, 0x21, 0x5a, 0x03, 0x88, 0xf0, 0x4e, + 0xc3, 0x36, 0x30, 0x71, 0xf2, 0x33, 0x1e, 0x13, 0x92, 0x57, 0x54, 0x99, + 0xb0, 0xd7, 0xd7, 0x3d, 0x30, 0x90, 0xad, 0xae, 0x40, 0x21, 0xcb, 0xa1, + 0xd5, 0x58, 0x81, 0x36, 0xb9, 0x41, 0xd8, 0xd7, 0x39, 0xd9, 0xa9, 0x4c, + 0xf4, 0x91, 0x8b, 0x50, 0xcf, 0x85, 0x85, 0x54, 0x6b, 0x4c, 0xa1, 0xad, + 0x20, 0x69, 0x47, 0x51, 0xf1, 0xd3, 0x4f, 0xb2, 0xb4, 0xb1, 0x4c, 0xe4, + 0x63, 0x83, 0xd9, 0x70, 0x52, 0x25, 0xee, 0xcc, 0x51, 0xf8, 0x52, 0xd3, + 0xd5, 0x09, 0xa8, 0x5b, 0x2b, 0xad, 0x0a, 0xf3, 0x15, 0x7b, 0xed, 0xcd, + 0x74, 0x3f, 0xa2, 0x83, 0x03, 0x8c, 0x7f, 0xc8, 0x53, 0x5e, 0x6a, 0xd8, + 0x76, 0x20, 0x4e, 0xb1, 0x47, 0x2e, 0x4e, 0xc8, 0x21, 0xdd, 0xa0, 0xf5, + 0x15, 0x72, 0xc8, 0x8e, 0xbd, 0x3a, 0x7a, 0x14, 0x6d, 0xac, 0xd7, 0x0c, + 0x9f, 0x5c, 0x3c, 0xc8, 0x23, 0xe1, 0x60, 0xce, 0x0f, 0x61, 0x1e, 0x74, + 0x79, 0x79, 0x94, 0xa9, 0xf7, 0x73, 0x1c, 0xd3, 0x9d, 0x77, 0x96, 0xea, + 0x51, 0x28, 0xb3, 0xce, 0xd7, 0xcf, 0xfa, 0xca, 0x4a, 0xdc, 0x1a, 0xc5, + 0x83, 0xd0, 0xc2, 0x31, 0xe7, 0x85, 0x6d, 0x7e, 0xcd, 0x0c, 0x2e, 0xde, + 0xa2, 0x3a, 0xdb, 0x75, 0xfd, 0x3b, 0x6c, 0x53, 0x22, 0x8b, 0x63, 0x19, + 0x69, 0x3e, 0xf6, 0x3a, 0xf9, 0xc3, 0xba, 0x24, 0xd2, 0xab, 0x1a, 0xc0, + 0xf9, 0x36, 0x1c, 0xc1, 0xe8, 0x96, 0x21, 0x98, 0xfe, 0x80, 0x51, 0x87, + 0x30, 0x15, 0xaa, 0xca, 0x24, 0x40, 0x30, 0xdf, 0xef, 0x46, 0xcd, 0x5d, + 0x85, 0x84, 0xd7, 0xd9, 0x55, 0x03, 0x9a, 0xcc, 0x8e, 0x5c, 0x8b, 0x09, + 0xea, 0x6d, 0x3d, 0xb5, 0xe6, 0x74, 0xd4, 0x24, 0x31, 0x39, 0x57, 0xa1, + 0x56, 0x19, 0x99, 0x1e, 0x29, 0x7d, 0xd7, 0x98, 0x31, 0xfa, 0x2d, 0x30, + 0xaa, 0x3b, 0x96, 0x57, 0x2a, 0x75, 0xb8, 0x3c, 0xa4, 0x25, 0x20, 0xa2, + 0x95, 0x78, 0x52, 0xc7, 0x1c, 0xcb, 0x3f, 0x30, 0x84, 0xdd, 0x82, 0x70, + 0xdc, 0xbd, 0xff, 0xe4, 0x13, 0xbd, 0x68, 0xac, 0x57, 0x44, 0x45, 0xbe, + 0x34, 0x76, 0x11, 0x96, 0xcc, 0x64, 0x5e, 0x4a, 0xff, 0x21, 0x81, 0xcb, + 0x55, 0x01, 0x20, 0x04, 0x4f, 0x8b, 0x31, 0xee, 0xfc, 0x88, 0x44, 0x2c, + 0xe8, 0x18, 0xfe, 0x2d, 0x5e, 0x37, 0x95, 0x65, 0xbe, 0x37, 0xb2, 0x1f, + 0xfa, 0x0e, 0x79, 0x26, 0x30, 0x26, 0xf3, 0x12, 0xe0, 0x59, 0xfa, 0x7b, + 0xd8, 0x6e, 0x3f, 0xad, 0x76, 0x56, 0x73, 0x83, 0x7b, 0x29, 0x0e, 0xab, + 0x41, 0x99, 0x67, 0xe7, 0xdc, 0x06, 0xa9, 0x74, 0x62, 0xf3, 0x02, 0x32, + 0xaa, 0x97, 0xf4, 0xed, 0xbe, 0x97, 0x72, 0x4f, 0x1c, 0xc0, 0x7b, 0x86, + 0x76, 0x84, 0xe9, 0x21, 0xe1, 0xfd, 0x84, 0xbb, 0x36, 0x91, 0xa0, 0xa7, + 0x31, 0x36, 0xb8, 0x3e, 0x13, 0x21, 0x74, 0xd8, 0xd4, 0xdc, 0xae, 0x34, + 0x97, 0x93, 0x4c, 0x00, 0x6e, 0xee, 0x47, 0xb7, 0x27, 0xea, 0x43, 0x02, + 0x2e, 0xd7, 0xaa, 0x81, 0xfb, 0x46, 0x05, 0xbb, 0xcc, 0xfd, 0xa2, 0x59, + 0x24, 0x59, 0xe8, 0xac, 0x15, 0xda, 0x3c, 0x5b, 0x94, 0xf9, 0x05, 0xd0, + 0x88, 0x0f, 0x9e, 0xde, 0xf5, 0xff, 0x19, 0x6a, 0x6a, 0xce, 0x70, 0xdd, + 0xac, 0x19, 0x21, 0x64, 0xb6, 0x1a, 0x15, 0x36, 0x15, 0x33, 0x03, 0xdb, + 0x37, 0xa5, 0x47, 0x71, 0x77, 0xed, 0x8e, 0x7b, 0xa7, 0xd7, 0x9e, 0x90, + 0xd6, 0x5e, 0x68, 0xd8, 0x52, 0x3d, 0x93, 0x95, 0x70, 0x58, 0x95, 0x08, + 0xfa, 0x0f, 0x83, 0x9d, 0x7a, 0x3e, 0x7d, 0xa2, 0x13, 0xfa, 0x04, 0x42, + 0x06, 0xcb, 0x7f, 0x98, 0x71, 0xc1, 0xd3, 0x85, 0xf1, 0xf0, 0x84, 0x6b, + 0x00, 0x56, 0x45, 0x53, 0xee, 0xdf, 0x2a, 0x83, 0x4a, 0xbc, 0x7a, 0xc4, + 0x04, 0x37, 0x33, 0x1f, 0x97, 0x85, 0xd4, 0x7b, 0xb1, 0xb9, 0x02, 0x96, + 0x59, 0x63, 0x72, 0x1a, 0x56, 0x80, 0x52, 0x46, 0xf0, 0x64, 0x81, 0xcd, + 0xe2, 0x22, 0xe5, 0xd9, 0xe7, 0xbf, 0xa8, 0xa1, 0x73, 0xba, 0x29, 0x41, + 0xed, 0x02, 0x0b, 0xe6, 0x66, 0x4d, 0xf1, 0xd1, 0x9c, 0x8a, 0x31, 0xbb, + 0x79, 0x97, 0xcf, 0x2a, 0x04, 0x97, 0xcd, 0xe3, 0xcb, 0xf4, 0x16, 0x96, + 0x43, 0xc4, 0x6f, 0xbb, 0x37, 0xf6, 0x14, 0x34, 0x30, 0x06, 0xe0, 0xe4, + 0xc4, 0xdb, 0xc1, 0xee, 0xcb, 0x4d, 0x4c, 0xdb, 0x83, 0xdf, 0xde, 0xb5, + 0x85, 0x64, 0x8f, 0xac, 0x1a, 0x4e, 0xfb, 0x7a, 0x33, 0x79, 0xdb, 0x07, + 0xaa, 0x26, 0xcd, 0x13, 0x7d, 0xfa, 0xfd, 0xca, 0xae, 0x39, 0xb2, 0xf0, + 0x9f, 0xd3, 0xb2, 0x4c, 0xd7, 0xd9, 0xe4, 0x78, 0x96, 0xb4, 0xb8, 0x86, + 0xfe, 0xfb, 0xae, 0xee, 0x71, 0xac, 0x55, 0x90, 0xf2, 0x66, 0x60, 0x17, + 0x53, 0x2b, 0x09, 0x06, 0x28, 0x38, 0xdc, 0x62, 0xf6, 0x63, 0xe8, 0xc3, + 0xdb, 0xcb, 0xfd, 0x91, 0xef, 0x2c, 0xa0, 0xcf, 0xba, 0xbd, 0xdd, 0x9b, + 0x14, 0x30, 0x9a, 0x6a, 0x3d, 0xe6, 0xd5, 0x2d, 0xc9, 0x66, 0xbb, 0xd5, + 0xb3, 0x7e, 0x46, 0x83, 0x2f, 0x79, 0xef, 0xb9, 0xec, 0x02, 0x0f, 0x13, + 0x2d, 0x41, 0xe3, 0x54, 0x2d, 0x04, 0xc1, 0x8c, 0xed, 0x6c, 0x50, 0x0b, + 0x64, 0x41, 0x4d, 0x1e, 0x00, 0x02, 0x64, 0x81, 0x7d, 0x4e, 0xd6, 0x6f, + 0xe3, 0x31, 0xdf, 0x68, 0x4c, 0xdd, 0x1f, 0x27, 0x19, 0xfc, 0x01, 0x2b, + 0x31, 0xc4, 0xf1, 0x40, 0x86, 0x81, 0x5c, 0x91, 0x87, 0x51, 0xcc, 0x27, + 0xee, 0xc6, 0xca, 0x4d, 0x67, 0x2d, 0xca, 0xa5, 0x18, 0xd0, 0x9d, 0x97, + 0xf9, 0x34, 0x3c, 0x8e, 0xe4, 0x51, 0xab, 0xa1, 0x8b, 0x7d, 0xa8, 0xfe, + 0xea, 0xc9, 0x31, 0xa1, 0x5a, 0x19, 0x67, 0x55, 0x84, 0xc7, 0xab, 0xac, + 0x34, 0x45, 0x5b, 0x65, 0xb1, 0x8e, 0x20, 0xdc, 0x96, 0xc6, 0x11, 0xc2, + 0x95, 0x89, 0x5a, 0xeb, 0xc5, 0x6b, 0x3e, 0xf3, 0x06, 0xc0, 0x0c, 0x95, + 0x4d, 0xd4, 0x17, 0x4e, 0x0f, 0x7c, 0xd0, 0x38, 0xaf, 0xe8, 0x80, 0xe7, + 0xbb, 0x68, 0x7c, 0x5d, 0x4b, 0x8f, 0x98, 0x45, 0xe2, 0x1a, 0x33, 0x29, + 0xd2, 0x2b, 0xeb, 0xa1, 0x05, 0xa8, 0xca, 0x5f, 0x43, 0x16, 0x84, 0xf5, + 0xbe, 0x29, 0x2a, 0x22, 0x86, 0x7e, 0x50, 0xf5, 0xe9, 0xca, 0x8c, 0x0c, + 0x5a, 0xc9, 0x52, 0xc6, 0x27, 0x20, 0x0f, 0xee, 0xc4, 0xba, 0xa7, 0x63, + 0x55, 0x41, 0xda, 0x2a, 0x83, 0x1e, 0x8f, 0xe5, 0xe8, 0xca, 0x74, 0x9e, + 0x38, 0x8c, 0xb6, 0x70, 0x3e, 0xf2, 0xa1, 0x6d, 0x43, 0xb9, 0xb7, 0x4b, + 0x7e, 0x39, 0x76, 0x78, 0x1c, 0x32, 0x7b, 0xf4, 0xa0, 0x7f, 0x7d, 0xca, + 0x6f, 0xf8, 0x8e, 0xb7, 0xc7, 0x16, 0xb4, 0x83, 0xad, 0x0c, 0x35, 0xc7, + 0x3a, 0x5f, 0xdf, 0x71, 0x2f, 0xc8, 0xa8, 0x08, 0xfb, 0x64, 0x44, 0x94, + 0x6e, 0xca, 0x19, 0x5c, 0x88, 0xc5, 0x20, 0x0f, 0x31, 0x5d, 0x58, 0xf1, + 0x19, 0x4a, 0xcc, 0x9c, 0xf0, 0x52, 0x58, 0x7d, 0x4e, 0xdd, 0x52, 0x6a, + 0x5a, 0x17, 0xb9, 0xf3, 0x3c, 0xcb, 0x88, 0x4a, 0x44, 0xef, 0x3b, 0xce, + 0x9f, 0x0e, 0x75, 0x74, 0x76, 0xd1, 0xea, 0x39, 0xa2, 0x4c, 0xca, 0x56, + 0xc0, 0x7b, 0xa0, 0xad, 0x50, 0xe3, 0xb7, 0xc9, 0x38, 0x76, 0x85, 0x8a, + 0x79, 0x10, 0x38, 0x25, 0xf3, 0x4b, 0xe6, 0x8c, 0x29, 0x71, 0x2d, 0x62, + 0xec, 0x25, 0x59, 0xe6, 0xe4, 0xae, 0xd7, 0xd2, 0x9d, 0x5c, 0x82, 0x43, + 0x3d, 0x92, 0xc3, 0x1e, 0x1b, 0x7c, 0x01, 0xb2, 0x0f, 0xd2, 0x8b, 0x6c, + 0x77, 0xcc, 0xef, 0x20, 0xeb, 0x97, 0x40, 0xd4, 0xf0, 0x47, 0x95, 0x99, + 0x9c, 0x79, 0x67, 0x50, 0xdb, 0x8d, 0x98, 0xc4, 0x8f, 0x8b, 0x00, 0x5b, + 0x4f, 0x7b, 0x29, 0xfd, 0xc3, 0xa8, 0x52, 0xaa, 0xd2, 0xb9, 0x3d, 0xb6, + 0xd9, 0x5b, 0xe2, 0xe6, 0x86, 0x1f, 0x73, 0x6a, 0x92, 0x93, 0x49, 0x45, + 0x7c, 0xd8, 0xb9, 0xa1, 0x4d, 0x58, 0xb1, 0x90, 0x90, 0x36, 0x9d, 0xb1, + 0x25, 0x80, 0x45, 0x5c, 0xc0, 0x91, 0x59, 0xdd, 0x13, 0x22, 0xd2, 0xca, + 0xf3, 0xf9, 0x02, 0x87, 0x7f, 0x76, 0xba, 0x1e, 0x73, 0xe1, 0xfa, 0xcb, + 0x8c, 0x95, 0xf6, 0x21, 0xb2, 0x04, 0x86, 0x7c, 0x5f, 0xe6, 0x88, 0x7c, + 0x19, 0x37, 0x1c, 0xe0, 0x8e, 0xe0, 0x53, 0x83, 0x0b, 0x51, 0x21, 0x79, + 0x73, 0x08, 0x0c, 0x1d, 0xb2, 0x53, 0x1b, 0x11, 0xeb, 0xd1, 0xdd, 0x9e, + 0x65, 0x0e, 0x55, 0xdd, 0xbc, 0x52, 0x9f, 0x50, 0xbb, 0xa8, 0xf3, 0x64, + 0xf2, 0x76, 0x09, 0x9d, 0x27, 0x82, 0xf7, 0xa4, 0xfc, 0x06, 0x04, 0xcb, + 0x3e, 0xa5, 0x01, 0xe7, 0xe6, 0x9e, 0x38, 0x10, 0xbb, 0x24, 0x5d, 0xc6, + 0x05, 0xcc, 0xbf, 0x3b, 0xcb, 0x31, 0x6e, 0x43, 0x5d, 0x0b, 0xd9, 0x45, + 0x53, 0xdb, 0x14, 0x0f, 0x57, 0x42, 0xe1, 0xc6, 0xa5, 0xe6, 0xc4, 0x3c, + 0x02, 0x55, 0x39, 0x76, 0xb1, 0x02, 0xe8, 0x37, 0xdb, 0xa0, 0x5f, 0xc4, + 0x2b, 0x3b, 0x88, 0xfa, 0x62, 0x9c, 0xd9, 0xc5, 0x88, 0x15, 0x34, 0xab, + 0xf5, 0x23, 0x91, 0x21, 0xd1, 0x9d, 0xce, 0x60, 0xa8, 0x65, 0x4e, 0x8f, + 0x1c, 0x5f, 0xea, 0x7c, 0xfe, 0x20, 0xaa, 0xf1, 0x3a, 0x7b, 0x97, 0xc6, + 0x90, 0x93, 0x03, 0x99, 0xd3, 0x01, 0x05, 0x0d, 0x57, 0x99, 0x27, 0xbf, + 0x9b, 0xae, 0x3b, 0x95, 0xe7, 0x6e, 0x49, 0xb9, 0xe7, 0x90, 0x16, 0x65, + 0x21, 0x7b, 0x13, 0xc6, 0xd1, 0xfb, 0x86, 0xeb, 0x6e, 0x4c, 0x6f, 0x6b, + 0x91, 0x97, 0x8f, 0xa4, 0xc6, 0x4c, 0xef, 0xc1, 0x54, 0x95, 0xc6, 0x1e, + 0x62, 0x71, 0x21, 0x35, 0xf2, 0x4d, 0x9d, 0x83, 0x6c, 0xb7, 0x90, 0x19, + 0x83, 0x9f, 0x27, 0x7b, 0x54, 0x4e, 0x46, 0xdf, 0x89, 0xb4, 0xf2, 0xfe, + 0x0e, 0x3f, 0xb8, 0x5d, 0x99, 0x93, 0x92, 0x29, 0xd3, 0x3e, 0x1a, 0x30, + 0x7b, 0x5c, 0x07, 0x36, 0x37, 0x0e, 0x24, 0xd5, 0x7b, 0x9a, 0xc9, 0xeb, + 0xdf, 0x7d, 0xca, 0xd5, 0xdf, 0xb9, 0xb5, 0x8b, 0xf7, 0x4b, 0x43, 0x4d, + 0xc3, 0xf3, 0x60, 0x4e, 0x62, 0x00, 0xe6, 0x65, 0xbf, 0x34, 0xdc, 0xe4, + 0x57, 0xde, 0x3a, 0xa9, 0xc2, 0xed, 0x10, 0x6d, 0xfe, 0xa7, 0x0f, 0x07, + 0x49, 0xe6, 0xaa, 0x9d, 0x92, 0xf7, 0xf4, 0xcc, 0x68, 0xc7, 0x3d, 0x45, + 0xe4, 0x6a, 0x09, 0x87, 0x9d, 0xd4, 0x7e, 0xfa, 0x8d, 0xc6, 0x8d, 0xb8, + 0xc8, 0x71, 0xbf, 0x00, 0xab, 0x79, 0x26, 0xaa, 0x0c, 0x48, 0xe9, 0x5e, + 0x63, 0x52, 0x4f, 0x53, 0x19, 0x46, 0xa9, 0x42, 0xaa, 0xa3, 0x28, 0xf6, + 0xe5, 0xc7, 0xb7, 0x9c, 0x14, 0x6e, 0x3d, 0x03, 0x8d, 0x8f, 0x0c, 0x28, + 0x46, 0x8d, 0xbc, 0x2e, 0xfe, 0xfc, 0x8c, 0xff, 0xaf, 0xae, 0xdf, 0x4f, + 0xc2, 0xc1, 0xdb, 0x45, 0xa6, 0xf3, 0xe8, 0x2b, 0x2b, 0xf1, 0xae, 0x34, + 0x84, 0x66, 0xbf, 0xaa, 0x17, 0x17, 0x22, 0x8a, 0xc9, 0x41, 0x5a, 0x83, + 0x81, 0x11, 0xb9, 0xb5, 0x32, 0xfc, 0xdd, 0x02, 0xbc, 0x25, 0x93, 0x9e, + 0x80, 0x11, 0x1f, 0xc2, 0xc3, 0x4e, 0x7e, 0xb5, 0x1d, 0x23, 0x19, 0x29, + 0xae, 0x40, 0x0d, 0x6b, 0xed, 0x4f, 0x34, 0x49, 0x81, 0x26, 0xd4, 0xdb, + 0x8f, 0x95, 0xfb, 0xd2, 0xe1, 0xe0, 0x29, 0xd0, 0x60, 0xac, 0xb5, 0x66, + 0x2a, 0x8f, 0x9d, 0x9f, 0xe2, 0x8f, 0x6e, 0x47, 0x40, 0xb8, 0x73, 0xe8, + 0x5a, 0x58, 0x20, 0x37, 0x6b, 0x6a, 0xdf, 0xc0, 0x97, 0x36, 0x13, 0xcf, + 0x2c, 0x7d, 0x1f, 0x78, 0x0b, 0x12, 0x67, 0x59, 0xcf, 0x92, 0x99, 0xf3, + 0x4f, 0x87, 0x09, 0x68, 0xcb, 0xec, 0xa3, 0x8b, 0x29, 0x9f, 0x03, 0x18, + 0x45, 0x4e, 0x25, 0xff, 0x42, 0x6c, 0x9e, 0xcc, 0x94, 0x7e, 0x28, 0x79, + 0x43, 0x9d, 0xbb, 0x14, 0x35, 0x47, 0x09, 0xc7, 0x66, 0xd0, 0x9c, 0xfa, + 0x23, 0xa5, 0x36, 0x58, 0x7d, 0x9e, 0xec, 0xa9, 0xd0, 0x25, 0x25, 0x2e, + 0x52, 0xd9, 0x4b, 0xf4, 0xd6, 0xaf, 0x0f, 0xf2, 0xf5, 0xef, 0xd2, 0x91, + 0x85, 0x93, 0x1d, 0x4e, 0x37, 0x9e, 0x88, 0x16, 0x40, 0x43, 0xda, 0x77, + 0x4f, 0x19, 0x62, 0xfc, 0x26, 0x5f, 0xdb, 0xf8, 0x61, 0x0e, 0x7d, 0x47, + 0x08, 0x36, 0x11, 0x95, 0x35, 0xab, 0x2b, 0xa8, 0x52, 0xb7, 0x41, 0xe6, + 0x01, 0x54, 0xb8, 0xbe, 0x3d, 0x7b, 0x29, 0x50, 0x53, 0x7e, 0xf9, 0x0b, + 0xd1, 0x1a, 0x1c, 0xd0, 0x09, 0x27, 0x93, 0x37, 0xc8, 0x71, 0xc4, 0x72, + 0x52, 0xd3, 0x78, 0xbd, 0xde, 0xa7, 0x43, 0xe6, 0xb6, 0xbb, 0x15, 0x01, + 0x60, 0x1a, 0x2a, 0x0f, 0x57, 0x75, 0x6f, 0x6c, 0xc1, 0x64, 0xb0, 0x98, + 0xca, 0xfb, 0x76, 0x9b, 0x79, 0x66, 0x39, 0x87, 0x25, 0xf7, 0x14, 0xae, + 0x5f, 0xf5, 0x35, 0x18, 0x76, 0xf9, 0xb1, 0xaf, 0x12, 0x10, 0x75, 0x65, + 0xc2, 0x83, 0x37, 0x4d, 0x13, 0xbc, 0xc0, 0xad, 0x0b, 0x00, 0xf4, 0x2f, + 0xeb, 0xef, 0xa7, 0xd5, 0xbd, 0x79, 0x10, 0x98, 0x74, 0x86, 0x93, 0x6c, + 0x8c, 0xe2, 0x2e, 0x41, 0xa9, 0xc7, 0x2b, 0x21, 0xf1, 0xf9, 0xd7, 0xfe, + 0xf9, 0xc1, 0xb6, 0x55, 0x74, 0xb8, 0xbe, 0x24, 0x96, 0x32, 0xb6, 0x3f, + 0xca, 0x3e, 0xee, 0x73, 0x46, 0xfd, 0x8a, 0x6b, 0x05, 0x2c, 0xca, 0xea, + 0x95, 0x6e, 0x0d, 0x2b, 0xe3, 0xff, 0x8c, 0xaa, 0xf9, 0x4b, 0x02, 0xcb, + 0x36, 0x47, 0x31, 0x66, 0x5b, 0x2b, 0x49, 0x1c, 0x24, 0xa2, 0xc8, 0xf2, + 0x1f, 0x67, 0x4b, 0x6c, 0x9d, 0x31, 0x07, 0x23, 0x63, 0x21, 0xdf, 0x52, + 0x26, 0x03, 0x6a, 0x42, 0x40, 0x4a, 0x82, 0xc6, 0xee, 0xa4, 0x46, 0xc7, + 0xb5, 0xc7, 0xad, 0xd5, 0x81, 0xbe, 0x2a, 0x53, 0xd5, 0x9e, 0x99, 0x98, + 0xe8, 0xb7, 0x2e, 0x3f, 0xa4, 0x57, 0x46, 0x84, 0x82, 0x40, 0x2d, 0x25, + 0x29, 0x92, 0xa6, 0x5a, 0xc6, 0x45, 0xf9, 0x87, 0xbc, 0xa4, 0xd1, 0xdb, + 0x37, 0x48, 0x41, 0x83, 0xa5, 0xad, 0xc9, 0x67, 0x67, 0x45, 0x10, 0x5e, + 0x1c, 0x16, 0x49, 0x2f, 0x81, 0x99, 0xbd, 0x7b, 0x67, 0x1e, 0xb5, 0xa5, + 0xc0, 0xf1, 0x5c, 0x98, 0x50, 0xf3, 0x79, 0x48, 0x23, 0xf0, 0x0d, 0x5e, + 0x53, 0xf7, 0xcb, 0x13, 0xc5, 0x58, 0x17, 0x6f, 0x88, 0x7b, 0xa7, 0x60, + 0x8e, 0x3a, 0x33, 0xaa, 0x38, 0xcf, 0xe9, 0xe1, 0x6b, 0x2a, 0x02, 0xd9, + 0xeb, 0x74, 0x79, 0x81, 0x12, 0xa6, 0x52, 0x9c, 0x98, 0x52, 0x67, 0xe8, + 0x67, 0x94, 0xae, 0x2b, 0x9e, 0x01, 0x27, 0x90, 0x2d, 0x88, 0x9d, 0xc0, + 0x30, 0xb8, 0xa2, 0x83, 0x89, 0x86, 0x9f, 0x03, 0xa5, 0xad, 0x59, 0x6b, + 0x00, 0xb6, 0xc5, 0x22, 0x74, 0xab, 0x7f, 0x6c, 0xd3, 0x15, 0x6e, 0xe8, + 0x81, 0x4a, 0x66, 0x40, 0x0c, 0x1e, 0x13, 0xaf, 0x81, 0xd0, 0x3d, 0x3e, + 0x74, 0x39, 0xe0, 0x84, 0x52, 0x00, 0xf0, 0xf4, 0x52, 0xdf, 0x99, 0x4d, + 0x4b, 0x1c, 0x3d, 0xf3, 0x71, 0x64, 0xc6, 0x67, 0x9f, 0xec, 0xc6, 0xc5, + 0x29, 0x2c, 0x42, 0x92, 0xa2, 0xc6, 0x72, 0xf3, 0x52, 0x24, 0xc1, 0x89, + 0x4a, 0xaf, 0xbb, 0x1d, 0x6b, 0x9f, 0xc8, 0xc5, 0x31, 0xd2, 0x18, 0xef, + 0x50, 0xeb, 0xc2, 0xf8, 0x31, 0x81, 0x92, 0xd3, 0x32, 0xda, 0xab, 0x41, + 0x51, 0xc9, 0x85, 0xf8, 0x67, 0x90, 0x36, 0xc6, 0xf9, 0xdc, 0x3b, 0x82, + 0x05, 0x1a, 0x0e, 0x7b, 0xe4, 0xdc, 0xbd, 0x6e, 0x7b, 0x47, 0x8f, 0xf4, + 0x7c, 0xd8, 0xed, 0x39, 0x9f, 0x1b, 0x73, 0x49, 0xa6, 0x22, 0xaf, 0xc8, + 0x23, 0xf2, 0x3b, 0xcf, 0xda, 0xee, 0x1d, 0xa9, 0x29, 0xfc, 0x8b, 0xe6, + 0x43, 0xd7, 0xbf, 0x6d, 0xd3, 0x03, 0xee, 0x34, 0xf3, 0x28, 0x70, 0x1f, + 0x9f, 0x0a, 0x49, 0x62, 0xdc, 0x4f, 0xcb, 0x50, 0xce, 0x4f, 0x44, 0x33, + 0x7b, 0xff, 0x6f, 0x77, 0x75, 0xf9, 0x31, 0xb0, 0x14, 0xfc, 0xfe, 0x48, + 0x68, 0xb8, 0xa3, 0xe5, 0x22, 0xaf, 0xee, 0x65, 0x82, 0x0e, 0xb9, 0xb6, + 0xcf, 0x06, 0xdf, 0xab, 0xd9, 0xa4, 0x74, 0xcc, 0x64, 0x0f, 0x5d, 0x14, + 0x11, 0x7a, 0x58, 0x83, 0x2d, 0xdc, 0x26, 0x46, 0x60, 0x74, 0x38, 0xc8, + 0xef, 0xfd, 0xa1, 0x16, 0xcc, 0x05, 0xed, 0xb7, 0xe2, 0xa4, 0x1d, 0x84, + 0xac, 0xfc, 0x3c, 0x43, 0xc1, 0xaa, 0xe5, 0x4e, 0x5d, 0xb4, 0x31, 0x9e, + 0x2d, 0x65, 0xa0, 0xa4, 0xcf, 0x37, 0xbe, 0x5f, 0x6c, 0xc9, 0x46, 0xd9, + 0x35, 0xbc, 0xcb, 0xc4, 0x23, 0xa8, 0x76, 0x6a, 0xb0, 0xa5, 0x11, 0xc4, + 0x84, 0xed, 0x4e, 0xff, 0x3d, 0xaa, 0x60, 0xba, 0xc0, 0x5b, 0x94, 0x80, + 0x3f, 0x57, 0x74, 0x1c, 0x5e, 0x6c, 0xcd, 0x66, 0xfa, 0x74, 0x74, 0xec, + 0x8c, 0xfa, 0xdd, 0x64, 0xa9, 0x35, 0xbd, 0x31, 0xc4, 0xc4, 0x44, 0x7f, + 0x6f, 0xe9, 0x6f, 0x11, 0xd5, 0xc5, 0x74, 0xa8, 0xda, 0x81, 0xb5, 0xb2, + 0xfe, 0x94, 0x3a, 0x75, 0xca, 0x1f, 0x31, 0xa5, 0xe0, 0x1b, 0x74, 0x32, + 0x53, 0xb5, 0x24, 0x35, 0x9f, 0x64, 0x35, 0x5a, 0xe3, 0xe0, 0x66, 0x7e, + 0xc1, 0x82, 0x37, 0xf5, 0x70, 0x6f, 0xf0, 0x77, 0x9e, 0x4b, 0x3a, 0x06, + 0x13, 0x90, 0x4f, 0xb9, 0x16, 0x5a, 0xef, 0xcd, 0xcc, 0x82, 0x5c, 0x26, + 0x46, 0x24, 0xd1, 0x46, 0x4e, 0xb3, 0x7c, 0x80, 0x94, 0xfd, 0x40, 0x0f, + 0x34, 0x5a, 0x83, 0x17, 0x06, 0x02, 0x63, 0x35, 0xf5, 0xe2, 0x17, 0x3a, + 0xd9, 0xd8, 0x8b, 0x56, 0xe3, 0xb6, 0xe7, 0xea, 0x31, 0x96, 0x17, 0xf2, + 0xc5, 0x21, 0x77, 0x22, 0xcb, 0xbe, 0xb3, 0x87, 0x2c, 0x32, 0x40, 0x5c, + 0xf9, 0xf8, 0x48, 0x17, 0x11, 0x30, 0xe4, 0xfd, 0xa1, 0x44, 0xab, 0x00, + 0xfe, 0x0e, 0x2f, 0xa8, 0x32, 0x1b, 0x52, 0xdd, 0x8e, 0x3a, 0x41, 0xa1, + 0x3a, 0x8f, 0xbe, 0x4b, 0xb0, 0x42, 0x1e, 0x38, 0xd8, 0xf5, 0x81, 0x2b, + 0xe0, 0x28, 0xa1, 0x4f, 0x29, 0xa5, 0x49, 0x13, 0x09, 0x7c, 0x71, 0x22, + 0xd3, 0x5b, 0x4b, 0x72, 0x7f, 0x2a, 0xf4, 0x98, 0x9f, 0x17, 0x18, 0x8d, + 0x5d, 0xdf, 0xfa, 0x5c, 0xe3, 0x75, 0x19, 0xd2, 0x0d, 0x08, 0x51, 0xfc, + 0xc1, 0x60, 0x2d, 0xd4, 0xa0, 0x6c, 0xb0, 0xc6, 0x73, 0x31, 0x46, 0x26, + 0x33, 0xb9, 0x51, 0x07, 0x2e, 0x00, 0x8c, 0xe2, 0x8b, 0xc9, 0x96, 0x2f, + 0x6d, 0x01, 0x90, 0x18, 0x19, 0xae, 0x3e, 0xb8, 0xfb, 0x7b, 0xbd, 0xf9, + 0x8f, 0xfc, 0xe4, 0x9e, 0xc2, 0xc5, 0xa0, 0x14, 0x3b, 0xe8, 0x06, 0xc0, + 0xfb, 0xe8, 0xbf, 0x2d, 0x3a, 0x73, 0x0d, 0x2b, 0x66, 0x4e, 0x88, 0x18, + 0x9b, 0x4c, 0x94, 0xf4, 0x04, 0xc4, 0x48, 0xf3, 0x17, 0x83, 0xce, 0x31, + 0x36, 0x63, 0x37, 0x22, 0x7d, 0xd2, 0x9d, 0xe7, 0xd2, 0x3c, 0x77, 0x6e, + 0xb5, 0xc7, 0x42, 0xcf, 0x3b, 0x34, 0xec, 0xac, 0xbc, 0xb1, 0xd0, 0x25, + 0x0d, 0xfe, 0xa2, 0x69, 0x7d, 0x23, 0xdb, 0xab, 0x59, 0x65, 0xc3, 0x3f, + 0x27, 0x40, 0xe5, 0x25, 0x43, 0x2a, 0xc8, 0x2f, 0x11, 0x05, 0x1d, 0x68, + 0xc1, 0x39, 0x85, 0x29, 0xfb, 0x66, 0xb5, 0x68, 0x8a, 0xb6, 0x90, 0x9e, + 0x92, 0xca, 0x11, 0x25, 0x80, 0x82, 0x0d, 0x4a, 0xda, 0xed, 0xb2, 0x65, + 0xdd, 0x33, 0xd5, 0x4c, 0xad, 0xde, 0x44, 0xcd, 0xf3, 0x06, 0xcc, 0x17, + 0x31, 0x9a, 0xec, 0x9f, 0x82, 0x3c, 0x23, 0xeb, 0x62, 0x6d, 0x39, 0xbe, + 0xef, 0x82, 0x91, 0xde, 0xea, 0x85, 0x3e, 0x51, 0x3c, 0x48, 0x81, 0x56, + 0x1c, 0x6d, 0xba, 0x1e, 0x7a, 0xa8, 0xe6, 0x10, 0xc9, 0x37, 0x8d, 0x4f, + 0x8c, 0x20, 0x0b, 0x4c, 0x48, 0x55, 0xb1, 0x38, 0x18, 0xa6, 0xb0, 0x47, + 0xca, 0x7c, 0x62, 0xeb, 0x91, 0x1e, 0x1b, 0x49, 0x60, 0xd8, 0x14, 0x7f, + 0x34, 0x43, 0xe9, 0x03, 0x0e, 0x82, 0x72, 0xa2, 0x55, 0xb2, 0x98, 0x48, + 0x32, 0x2b, 0x97, 0xb0, 0xab, 0xeb, 0xad, 0x03, 0xb6, 0x62, 0xa8, 0xf6, + 0x0c, 0xc5, 0x60, 0x16, 0x3b, 0x59, 0x9a, 0xc1, 0x05, 0xe9, 0x86, 0x1e, + 0x4f, 0x04, 0x69, 0x18, 0x7e, 0xe8, 0xce, 0xd7, 0x9c, 0x5b, 0x6a, 0x2f, + 0x5a, 0xb4, 0x09, 0x89, 0xe7, 0xdf, 0xe6, 0x86, 0x0f, 0xd2, 0xd9, 0x99, + 0x92, 0xc7, 0x0a, 0xa5, 0xb3, 0xd5, 0x2c, 0x7c, 0xfd, 0x31, 0x0b, 0xd5, + 0x37, 0x74, 0x57, 0xcf, 0x1c, 0xc2, 0xe0, 0x78, 0x47, 0x1d, 0x35, 0x68, + 0x0a, 0xdc, 0xa6, 0x89, 0xd3, 0x5f, 0x9f, 0x24, 0xef, 0x61, 0x4f, 0xc4, + 0x76, 0x66, 0x0d, 0xdc, 0xc6, 0x74, 0x39, 0x5d, 0xb4, 0xa8, 0xcf, 0x60, + 0x91, 0x9f, 0xed, 0x1d, 0xc9, 0x13, 0x6c, 0x17, 0xf3, 0xd2, 0x0c, 0xfe, + 0xb2, 0xc2, 0x7f, 0x17, 0xe3, 0x01, 0xd6, 0xf8, 0x06, 0xda, 0x85, 0x26, + 0x52, 0x21, 0x6a, 0x54, 0x71, 0xfd, 0x45, 0xde, 0xb7, 0x14, 0x65, 0xcd, + 0xb0, 0x33, 0xed, 0xff, 0x8d, 0x36, 0x4d, 0xed, 0xe4, 0x39, 0x05, 0x9a, + 0xdc, 0x9b, 0x73, 0xb2, 0x40, 0xd3, 0x24, 0x89, 0x01, 0xeb, 0x85, 0x7f, + 0xf6, 0x9d, 0xdc, 0x5d, 0x6c, 0x46, 0x8f, 0xe9, 0x6c, 0x82, 0x24, 0x01, + 0x2c, 0xf4, 0x37, 0x57, 0xff, 0xa4, 0x43, 0x29, 0xd3, 0xc6, 0xe2, 0x16, + 0xd6, 0x33, 0xf9, 0xb5, 0xf7, 0x92, 0x10, 0x0e, 0xd5, 0x02, 0x07, 0x51, + 0x0c, 0xb7, 0x69, 0x0e, 0x1f, 0xdc, 0x36, 0xde, 0x80, 0xc2, 0x07, 0x61, + 0xb2, 0xcd, 0x02, 0x55, 0x14, 0x91, 0x9d, 0x1c, 0x1f, 0xe9, 0x0b, 0x49, + 0x02, 0x44, 0x6b, 0x2f, 0x1a, 0x51, 0xa2, 0xf2, 0x93, 0x4c, 0x51, 0xca, + 0xe6, 0x65, 0x68, 0x45, 0x59, 0x52, 0xcf, 0x92, 0x48, 0xd3, 0x78, 0x4b, + 0x1f, 0xda, 0xcb, 0x22, 0x19, 0xef, 0x13, 0x86, 0x6c, 0x58, 0x5e, 0x35, + 0x63, 0x65, 0xfb, 0x38, 0x13, 0x24, 0x0e, 0x3c, 0xed, 0x94, 0xb5, 0xcf, + 0xf8, 0x28, 0x3f, 0x25, 0x88, 0xcc, 0x9b, 0x7c, 0x4d, 0x99, 0x42, 0x9f, + 0x82, 0xb4, 0x42, 0xdd, 0xd3, 0x5c, 0xfb, 0x41, 0x77, 0xdc, 0x1d, 0x92, + 0x85, 0xca, 0x62, 0x63, 0x58, 0x36, 0x55, 0x4a, 0xc3, 0x12, 0x27, 0xcd, + 0x69, 0xd6, 0xe6, 0xe2, 0x41, 0x51, 0xc5, 0x97, 0x43, 0x3d, 0x7a, 0xc5, + 0x19, 0xcc, 0xf6, 0x62, 0x08, 0x67, 0x36, 0xdd, 0xda, 0x79, 0xc4, 0xa2, + 0x3c, 0x9c, 0x8c, 0x4d, 0xf9, 0x01, 0x6b, 0xf9, 0x2a, 0x9e, 0x62, 0xa3, + 0x27, 0xe5, 0x2b, 0xc2, 0x38, 0xe4, 0xc0, 0x10, 0x92, 0x39, 0x4a, 0xa1, + 0x65, 0x6b, 0x6f, 0x86, 0x6b, 0xd2, 0x6c, 0xf2, 0x96, 0xde, 0x4b, 0xf9, + 0x41, 0x18, 0xbf, 0x7c, 0xc6, 0x5f, 0x69, 0xfd, 0x20, 0x04, 0xc5, 0xc2, + 0x53, 0xf1, 0xe0, 0xf6, 0x0d, 0xab, 0x1e, 0x6a, 0x6c, 0xbf, 0x28, 0xd0, + 0x44, 0x86, 0x4e, 0x7d, 0xa7, 0x7c, 0xad, 0x05, 0x37, 0x75, 0x9c, 0xd4, + 0x7d, 0xc0, 0xa8, 0x83, 0x57, 0xba, 0xa3, 0xf4, 0x29, 0x8c, 0xac, 0x7f, + 0xf9, 0x4d, 0x66, 0x1e, 0x8f, 0xb7, 0x24, 0x84, 0x28, 0xb8, 0xbe, 0x19, + 0x2b, 0x88, 0xa4, 0x59, 0xc1, 0xae, 0x6a, 0x69, 0x86, 0x59, 0x8c, 0xb8, + 0x2e, 0xf4, 0x91, 0x74, 0xf5, 0xca, 0xa6, 0x52, 0x6e, 0x05, 0xb8, 0x71, + 0x0c, 0xb2, 0x08, 0xde, 0xba, 0xd6, 0xae, 0xcd, 0x38, 0x2f, 0x45, 0x5e, + 0xe8, 0x8d, 0xec, 0x7c, 0xd8, 0x10, 0x18, 0xc9, 0x25, 0x10, 0x90, 0xdb, + 0xac, 0x8b, 0x90, 0x8e, 0x9c, 0xb0, 0x58, 0xfd, 0x55, 0x1b, 0x84, 0x2a, + 0x3a, 0x03, 0x7e, 0x24, 0xe0, 0x6c, 0x74, 0xae, 0x7a, 0x1e, 0x7c, 0xfc, + 0x03, 0x57, 0x56, 0x5e, 0x74, 0xa2, 0x59, 0xe2, 0xe8, 0x22, 0xe2, 0xf0, + 0x71, 0x49, 0x71, 0x48, 0xfd, 0x6d, 0x2a, 0xaf, 0xf0, 0x80, 0xad, 0xcf, + 0x37, 0x52, 0x3f, 0x68, 0x33, 0xf7, 0x07, 0xc2, 0xa2, 0x88, 0x4d, 0x2b, + 0x92, 0x62, 0xdf, 0x19, 0x17, 0x6a, 0x12, 0xca, 0xe4, 0x7c, 0x09, 0x1b, + 0x08, 0x75, 0xb2, 0x95, 0x75, 0xc7, 0x0f, 0x07, 0x2d, 0xc9, 0x78, 0xed, + 0x75, 0x44, 0x31, 0x7e, 0x53, 0x72, 0x89, 0x3f, 0x33, 0x5e, 0xdd, 0x93, + 0xea, 0x48, 0xa7, 0x73, 0x6f, 0x07, 0xae, 0x5b, 0xfc, 0x03, 0xcf, 0x9e, + 0x1c, 0x6e, 0xb3, 0x90, 0x68, 0x36, 0x29, 0x95, 0x26, 0xe9, 0x2f, 0x44, + 0xdc, 0x17, 0x68, 0xa2, 0xc2, 0xf2, 0xf7, 0x96, 0xf6, 0x96, 0x41, 0x0b, + 0x0f, 0xdc, 0xcf, 0xbb, 0x13, 0xd3, 0x54, 0x80, 0x3a, 0x95, 0xe3, 0xd0, + 0xc2, 0x10, 0xf5, 0x8f, 0x4c, 0xc0, 0xbe, 0x2f, 0xf2, 0x1e, 0x6d, 0x2d, + 0x87, 0xb3, 0x1d, 0x75, 0x3a, 0xb2, 0x4a, 0xec, 0x47, 0x0e, 0x13, 0x1a, + 0x97, 0x12, 0xf7, 0x02, 0x6c, 0xda, 0xe2, 0xd5, 0x6d, 0xdc, 0xb1, 0xbc, + 0x25, 0x30, 0xa1, 0xb0, 0x0d, 0xf7, 0x44, 0xb4, 0x67, 0x6d, 0xe0, 0x64, + 0x1d, 0x13, 0x0f, 0xd9, 0x95, 0xa5, 0x4f, 0x2b, 0x3e, 0xfa, 0x7f, 0xd5, + 0x4e, 0xaf, 0xe8, 0x19, 0xb5, 0x40, 0x49, 0xda, 0xc9, 0x69, 0xfa, 0x7a, + 0xe4, 0xa6, 0x00, 0x84, 0x0f, 0xf3, 0x75, 0x11, 0xb2, 0x6d, 0xe8, 0x1e, + 0xd3, 0x8a, 0xac, 0xec, 0x3c, 0xcc, 0x53, 0x65, 0x82, 0x6e, 0x59, 0x3a, + 0xfc, 0x51, 0x0b, 0x16, 0x24, 0xda, 0x26, 0x83, 0x76, 0x01, 0x6b, 0x90, + 0xe1, 0x81, 0xc7, 0xbd, 0x04, 0x9e, 0x97, 0x2c, 0x0e, 0x77, 0x86, 0xf0, + 0x36, 0x7b, 0x10, 0x24, 0x67, 0x09, 0x5d, 0x10, 0x23, 0xd1, 0x44, 0x20, + 0x57, 0x71, 0x85, 0x4a, 0x62, 0x5b, 0x70, 0x23, 0xe5, 0x68, 0x82, 0x5e, + 0x05, 0x9e, 0xd6, 0x82, 0xc2, 0x23, 0x7e, 0xd7, 0xd0, 0x63, 0x33, 0x7f, + 0xe6, 0x51, 0x26, 0x5d, 0xe8, 0xc5, 0x6d, 0xfc, 0xd4, 0xeb, 0x5c, 0xd9, + 0x22, 0x88, 0xaf, 0xea, 0xc8, 0xd4, 0x6e, 0xe3, 0x04, 0xb2, 0xe9, 0x89, + 0x92, 0x93, 0xfd, 0xf6, 0x5b, 0x55, 0xa9, 0x9f, 0xd1, 0xba, 0x85, 0xd6, + 0xce, 0x48, 0x3a, 0xfb, 0x68, 0x41, 0xdd, 0x68, 0x38, 0x6e, 0xd0, 0x6f, + 0x07, 0xc2, 0x3a, 0xaa, 0x68, 0x0b, 0xfe, 0xb8, 0x58, 0xcc, 0xe0, 0xe6, + 0x81, 0x67, 0x55, 0x32, 0x16, 0xed, 0xd2, 0x50, 0x33, 0x25, 0x11, 0xd3, + 0x6d, 0xb7, 0xeb, 0xa5, 0xe7, 0x81, 0xfc, 0x78, 0x31, 0x48, 0x19, 0x31, + 0xb9, 0x77, 0x85, 0x6f, 0x47, 0xd2, 0x64, 0x74, 0x15, 0x98, 0x81, 0xf4, + 0x21, 0x32, 0x8f, 0x39, 0xde, 0x8f, 0x70, 0xec, 0xe3, 0xf4, 0x29, 0x26, + 0x59, 0x7d, 0xc1, 0x66, 0xcf, 0x43, 0xd7, 0x7b, 0x39, 0xf4, 0xf6, 0xf9, + 0xf8, 0x81, 0xa6, 0x83, 0xf9, 0x24, 0x45, 0xab, 0x80, 0x5f, 0x48, 0xbe, + 0x8a, 0x48, 0x30, 0xc6, 0xbe, 0x26, 0xe8, 0x66, 0x01, 0x93, 0x16, 0x76, + 0x0b, 0x59, 0x81, 0x0d, 0xe3, 0xc5, 0x3e, 0xa0, 0xcf, 0x23, 0x74, 0xc3, + 0x06, 0x5d, 0xf3, 0x1d, 0x56, 0xda, 0x6d, 0x66, 0x44, 0x34, 0x42, 0xcc, + 0x87, 0xf9, 0x43, 0x71, 0xbb, 0x99, 0xba, 0x93, 0x96, 0x9c, 0x13, 0x21, + 0xf8, 0x76, 0x30, 0x9e, 0x45, 0xf2, 0xa1, 0x67, 0x6c, 0xe7, 0xcf, 0x1b, + 0x20, 0x80, 0xba, 0xe8, 0x2c, 0x6e, 0x66, 0xe7, 0xe5, 0xa1, 0xe6, 0x65, + 0xcf, 0x87, 0x5f, 0x8f, 0x74, 0xf6, 0xd7, 0xf4, 0x6e, 0x9d, 0xc7, 0x54, + 0xaf, 0x47, 0x6b, 0x9f, 0xf4, 0xd5, 0x7b, 0x45, 0x57, 0x02, 0xd3, 0x87, + 0xc6, 0xd6, 0x23, 0xd1, 0x15, 0x7b, 0x8c, 0x99, 0x8a, 0x9c, 0x57, 0xf2, + 0xf3, 0x3f, 0xc9, 0x56, 0xa4, 0x04, 0xc8, 0x2b, 0xb3, 0xcf, 0xf1, 0x9d, + 0x48, 0xf1, 0x44, 0x99, 0x02, 0x59, 0x0d, 0x8a, 0x91, 0x3b, 0x37, 0x9d, + 0xf1, 0xc9, 0x47, 0x01, 0x04, 0x0a, 0xb6, 0xc5, 0xd5, 0x60, 0xed, 0x6f, + 0xc8, 0x68, 0x35, 0x9e, 0x86, 0xea, 0x75, 0xc9, 0x21, 0x35, 0x6b, 0x02, + 0x45, 0x50, 0x2a, 0x6a, 0x96, 0xc4, 0x0b, 0x4a, 0x36, 0xda, 0x40, 0xb1, + 0x4d, 0xd1, 0x14, 0xca, 0x8b, 0x4c, 0x05, 0xb2, 0x00, 0xfe, 0x12, 0xdf, + 0x8d, 0x41, 0xb5, 0x97, 0xb0, 0x43, 0x08, 0x3c, 0x57, 0x34, 0xeb, 0xac, + 0xfd, 0xa2, 0xd6, 0x64, 0x64, 0xd0, 0x60, 0xe3, 0xf2, 0xfa, 0x43, 0x52, + 0xc7, 0xe4, 0x42, 0x47, 0x80, 0x1e, 0x20, 0x39, 0x24, 0x84, 0x5e, 0xa1, + 0x1c, 0xce, 0x28, 0x4f, 0x31, 0xec, 0xa6, 0x43, 0x71, 0x9e, 0xd2, 0xdf, + 0x9e, 0x66, 0x35, 0xc4, 0x62, 0xcc, 0xce, 0xd2, 0x49, 0xdf, 0x43, 0xb1, + 0x20, 0x27, 0x5e, 0x81, 0x30, 0x52, 0xd0, 0x44, 0x8d, 0xe1, 0x26, 0x9e, + 0xc7, 0x4a, 0x9c, 0xb7, 0x49, 0xe5, 0x29, 0xe8, 0x55, 0xfd, 0x1a, 0x23, + 0xf6, 0xc1, 0x0b, 0x8a, 0x97, 0xdd, 0x50, 0xa0, 0x6c, 0xef, 0x73, 0x40, + 0xe2, 0x3c, 0x10, 0xf5, 0x8a, 0x34, 0xf3, 0xae, 0x83, 0xf0, 0x21, 0x36, + 0x7c, 0x6c, 0xb2, 0xf1, 0x93, 0x43, 0x4d, 0x32, 0x22, 0xe7, 0x26, 0xec, + 0xc4, 0xcc, 0xbc, 0x6a, 0xe0, 0x20, 0x4c, 0xc6, 0x3d, 0x38, 0xa1, 0x0b, + 0x5b, 0x26, 0xb3, 0xbc, 0xe7, 0x41, 0x74, 0x90, 0x2c, 0x4f, 0x1e, 0x82, + 0xf8, 0xe6, 0x2d, 0xda, 0x15, 0x35, 0x32, 0x45, 0xe8, 0x1c, 0x73, 0xa3, + 0x0e, 0xd8, 0x1d, 0x37, 0x73, 0x2b, 0x23, 0x42, 0x18, 0xed, 0x00, 0x41, + 0x9f, 0xbb, 0xd3, 0x4b, 0x16, 0x0b, 0x28, 0x3a, 0x95, 0xab, 0xce, 0x0d, + 0x67, 0x71, 0xec, 0x46, 0x82, 0x1c, 0xc1, 0xd6, 0x27, 0x8c, 0x74, 0xa3, + 0x2f, 0xd4, 0x16, 0x90, 0x2b, 0x3d, 0xee, 0x09, 0x9f, 0xbf, 0x7e, 0x1a, + 0x80, 0x3c, 0x79, 0xdb, 0x13, 0x79, 0x14, 0x02, 0xe5, 0x46, 0xc5, 0x5d, + 0xaa, 0x90, 0x72, 0x00, 0x76, 0xfc, 0xcf, 0x53, 0xfd, 0x1c, 0xb6, 0x0a, + 0x2b, 0xa1, 0x83, 0xa6, 0x75, 0x25, 0x5e, 0x57, 0x8c, 0xfa, 0x7b, 0x17, + 0xc4, 0x19, 0x80, 0x1f, 0xf2, 0x13, 0x56, 0xf8, 0x93, 0xdd, 0x9b, 0x6c, + 0x66, 0xd8, 0x63, 0x40, 0x96, 0xcc, 0xcf, 0xa7, 0x1c, 0x0c, 0x19, 0x8e, + 0x45, 0x81, 0x7c, 0x30, 0xd0, 0x7b, 0xc1, 0x01, 0x63, 0x47, 0xa4, 0x01, + 0x25, 0xa7, 0x81, 0xdc, 0x68, 0x3c, 0xef, 0x7d, 0x6e, 0x73, 0x73, 0x19, + 0xb0, 0xc3, 0xb3, 0x62, 0x11, 0xbb, 0xea, 0x68, 0xb1, 0xc9, 0x63, 0x39, + 0x25, 0xa8, 0xdf, 0xc9, 0x89, 0xbe, 0x2a, 0xd9, 0xaf, 0x49, 0x33, 0xf4, + 0x4b, 0x1c, 0x5a, 0xb5, 0x2a, 0x0d, 0x3b, 0x09, 0x20, 0x43, 0x87, 0xc5, + 0x7b, 0x23, 0x10, 0x8c, 0x3b, 0x61, 0xb5, 0x86, 0xd7, 0xff, 0x4c, 0x18, + 0x23, 0x32, 0x37, 0x35, 0xab, 0xd7, 0x29, 0xf4, 0x7c, 0x75, 0xad, 0x74, + 0xb5, 0xd6, 0xaf, 0xe2, 0xca, 0x21, 0x85, 0x52, 0xff, 0xf6, 0x67, 0x14, + 0xc0, 0x0a, 0xb5, 0xf3, 0xfb, 0xfa, 0x46, 0x72, 0x3b, 0xad, 0xd4, 0xd9, + 0x5d, 0x1c, 0xb3, 0x56, 0x2d, 0x47, 0xf3, 0xb5, 0xa7, 0x47, 0xcc, 0x54, + 0x15, 0x8c, 0xb4, 0xef, 0x91, 0x01, 0xcb, 0xd5, 0x8f, 0x9e, 0xc3, 0x01, + 0x89, 0x36, 0x63, 0xa3, 0x21, 0x0f, 0xf0, 0xf9, 0xb0, 0xb6, 0xd3, 0x4d, + 0xf9, 0x11, 0x1d, 0x8f, 0xb1, 0x43, 0x98, 0x5e, 0x9d, 0xbc, 0x4f, 0x24, + 0xb6, 0xe2, 0x37, 0x9b, 0xc1, 0x6b, 0x2e, 0xe7, 0x46, 0xe6, 0x6f, 0x50, + 0x0c, 0xa9, 0x0e, 0x85, 0xe8, 0xf2, 0xd0, 0x9f, 0x0b, 0x2e, 0x08, 0x2d, + 0x41, 0xe9, 0xe0, 0xab, 0x7a, 0xe9, 0x92, 0xc3, 0x51, 0x27, 0x24, 0x5d, + 0x12, 0x5e, 0xbf, 0x58, 0xe5, 0xd2, 0x02, 0x8c, 0x5d, 0x01, 0x73, 0xfa, + 0x83, 0x41, 0x5c, 0x09, 0xb7, 0xe9, 0x8b, 0x76, 0xd3, 0xa6, 0x27, 0xec, + 0x13, 0x6f, 0xc2, 0x4b, 0xd6, 0xb5, 0x89, 0xeb, 0x44, 0xe5, 0xb0, 0x34, + 0x12, 0x13, 0x43, 0x88, 0xf5, 0xf3, 0x35, 0x21, 0xa8, 0x3d, 0x49, 0xdc, + 0x24, 0xac, 0x00, 0x46, 0xab, 0x6a, 0x78, 0x83, 0xac, 0xe2, 0xe9, 0x0e, + 0x37, 0x41, 0x62, 0x4f, 0x95, 0xea, 0xba, 0xc9, 0x4a, 0xe1, 0x75, 0x4c, + 0xd5, 0x98, 0xa4, 0xeb, 0xcf, 0x50, 0x3f, 0x2e, 0x98, 0xb4, 0x83, 0x63, + 0xb3, 0x87, 0xf9, 0xf4, 0x25, 0xef, 0xf4, 0xfd, 0xc3, 0xdf, 0x5b, 0x75, + 0xb1, 0x87, 0x8a, 0x06, 0xe3, 0xcb, 0xf9, 0xd0, 0x9e, 0x7b, 0x3c, 0x6d, + 0xbd, 0xaa, 0xe1, 0xce, 0x97, 0x92, 0x54, 0x0c, 0xa9, 0xc5, 0x80, 0x3b, + 0x63, 0x17, 0x7f, 0xff, 0xa7, 0xff, 0xf2, 0x7a, 0x2f, 0x55, 0xfd, 0x07, + 0x03, 0x8b, 0x1a, 0xc0, 0x68, 0xc4, 0xc7, 0x3b, 0xfe, 0xfd, 0x19, 0xad, + 0x4b, 0xc7, 0x99, 0x6d, 0x50, 0x2c, 0xb6, 0xb7, 0x34, 0x58, 0x56, 0x9e, + 0x23, 0xfc, 0x7d, 0x06, 0x18, 0xea, 0xd8, 0xf8, 0x32, 0x59, 0xf8, 0x4d, + 0x4d, 0x0f, 0x77, 0x85, 0xfe, 0x5d, 0xcb, 0xd1, 0x82, 0x0e, 0x62, 0xa5, + 0x04, 0xca, 0x90, 0x1c, 0x4b, 0x2e, 0xa8, 0xe1, 0xbf, 0x06, 0x9e, 0x4f, + 0x6e, 0xe4, 0xe3, 0x22, 0x48, 0xcf, 0x04, 0x69, 0x50, 0x95, 0xd8, 0x2e, + 0x17, 0x17, 0xce, 0x8a, 0x07, 0xc7, 0x14, 0x79, 0x49, 0x16, 0x9b, 0x31, + 0xc4, 0x94, 0xc7, 0x49, 0x2f, 0x6a, 0x93, 0x66, 0xab, 0x0b, 0x14, 0x5c, + 0x02, 0x0a, 0xb9, 0xd5, 0xaf, 0x2b, 0xed, 0xbf, 0xa0, 0xfe, 0x37, 0x44, + 0x12, 0x03, 0x7a, 0x7e, 0x8f, 0x8f, 0x77, 0x00, 0x51, 0x3a, 0xfc, 0x3a, + 0x92, 0x4d, 0xee, 0xcf, 0xdb, 0xfc, 0x0e, 0x7d, 0xec, 0xe2, 0x05, 0x25, + 0xff, 0x36, 0x15, 0x15, 0x0f, 0x11, 0x77, 0x84, 0x46, 0xcd, 0xc0, 0x13, + 0xfc, 0x72, 0x73, 0x26, 0x78, 0x4f, 0x1b, 0xa4, 0xc8, 0x82, 0x0a, 0x05, + 0xd0, 0x5d, 0x78, 0x56, 0x20, 0x32, 0xd6, 0xde, 0x68, 0xe6, 0x49, 0xb8, + 0x7b, 0x0b, 0xf6, 0xff, 0x04, 0xdf, 0x54, 0x82, 0x00, 0x8f, 0xde, 0x0a, + 0x3a, 0x56, 0x1a, 0xb8, 0x07, 0xbe, 0x0d, 0xff, 0x16, 0xa9, 0xcf, 0x45, + 0xae, 0xa9, 0x55, 0x0a, 0x03, 0x77, 0x23, 0x05, 0x1a, 0x01, 0xbf, 0x65, + 0x56, 0x45, 0x11, 0x9d, 0x63, 0xb9, 0x9b, 0x15, 0x0c, 0xdd, 0x77, 0x30, + 0x6d, 0x2e, 0x15, 0x27, 0xfd, 0x8b, 0xb5, 0xcd, 0x45, 0xfc, 0x5d, 0xbe, + 0x76, 0x91, 0xcc, 0x7f, 0x93, 0x31, 0x85, 0x83, 0x5a, 0x82, 0x0f, 0x44, + 0x62, 0x40, 0xb8, 0xf5, 0x01, 0x6d, 0xf7, 0x09, 0xa8, 0x97, 0xe9, 0x0b, + 0xa3, 0x7b, 0x46, 0xef, 0xd4, 0xe3, 0xc3, 0xb7, 0x92, 0x22, 0xde, 0x4e, + 0xda, 0x0b, 0x8a, 0x58, 0x2a, 0x8a, 0x42, 0xde, 0xf5, 0x65, 0xc0, 0xd7, + 0x32, 0xef, 0x3e, 0xad, 0xd1, 0xe7, 0xf5, 0xc4, 0x0f, 0x82, 0xbd, 0x13, + 0x67, 0x93, 0x42, 0x1b, 0x16, 0xa9, 0xa1, 0xf3, 0xc1, 0x4a, 0xe2, 0xc1, + 0x9e, 0x06, 0xab, 0x09, 0xd1, 0x43, 0x41, 0xd9, 0x5a, 0x35, 0x36, 0xe0, + 0xd6, 0x89, 0xb3, 0xa2, 0x63, 0xee, 0x97, 0x88, 0x07, 0xe8, 0x34, 0xaa, + 0x34, 0x17, 0x13, 0x90, 0x42, 0xb6, 0xae, 0xf8, 0xef, 0x06, 0xbd, 0xc1, + 0x25, 0x7f, 0x3e, 0x19, 0xa6, 0x60, 0x63, 0xfd, 0xf7, 0xfb, 0x06, 0x90, + 0x8e, 0xb9, 0x78, 0xe4, 0x2c, 0x61, 0x8f, 0x35, 0xe1, 0x9b, 0x88, 0x01, + 0xf6, 0xcc, 0xb4, 0x36, 0x3f, 0x56, 0xbb, 0xc3, 0xdc, 0x6d, 0x10, 0x09, + 0x63, 0xbc, 0x9a, 0xea, 0xa9, 0xa8, 0xc8, 0xc2, 0x01, 0x26, 0x94, 0xfb, + 0x7b, 0x50, 0x1b, 0x28, 0xe2, 0x21, 0xbb, 0x5d, 0x72, 0xbc, 0xb3, 0x74, + 0x61, 0x0a, 0x5a, 0x87, 0x88, 0x39, 0x29, 0xff, 0x3c, 0x9f, 0xf5, 0xbd, + 0x5b, 0x95, 0x03, 0xb8, 0x35, 0x47, 0xf6, 0xb0, 0x03, 0x6c, 0x72, 0x90, + 0xa1, 0x68, 0xde, 0x86, 0xa3, 0x33, 0xaa, 0x87, 0x82, 0x42, 0x40, 0x7d, + 0x35, 0x5c, 0xe3, 0x7b, 0x76, 0x5d, 0x95, 0x36, 0x99, 0xc5, 0x24, 0xe0, + 0xed, 0x92, 0xce, 0x48, 0x4b, 0xdc, 0x86, 0xe2, 0x64, 0x68, 0xee, 0x94, + 0xc8, 0x08, 0xb6, 0x7e, 0x5e, 0xf3, 0x97, 0x7c, 0x0b, 0xf0, 0x13, 0x92, + 0xf5, 0xc6, 0xc4, 0x81, 0xd0, 0x2e, 0xa6, 0x40, 0xf0, 0x08, 0xbe, 0x94, + 0x6b, 0x9c, 0x22, 0x51, 0xea, 0x5c, 0x41, 0x73, 0x3b, 0xd8, 0x18, 0x6d, + 0xae, 0x1c, 0x85, 0x2d, 0x58, 0xae, 0xe1, 0x06, 0xeb, 0x41, 0x34, 0x17, + 0x7c, 0x1c, 0x16, 0xe3, 0xe3, 0x36, 0xb5, 0x9f, 0x04, 0xff, 0xf0, 0x58, + 0xab, 0x4b, 0x8e, 0x0d, 0x6f, 0xe0, 0x61, 0x43, 0xee, 0x65, 0xde, 0x96, + 0xe8, 0xbb, 0x03, 0xa7, 0x10, 0xfc, 0x8b, 0x66, 0xfb, 0xd8, 0x2e, 0xba, + 0x4b, 0xf1, 0x56, 0xdf, 0xa9, 0xfe, 0x24, 0xbc, 0x2c, 0x02, 0xd0, 0x63, + 0xe3, 0xec, 0x74, 0xe2, 0xae, 0x55, 0x86, 0xc2, 0xa3, 0xde, 0x89, 0x75, + 0x6e, 0xcd, 0x1d, 0x3e, 0x5f, 0x21, 0xfa, 0xe9, 0x58, 0xfa, 0x5c, 0x78, + 0x40, 0x4c, 0x36, 0x5a, 0x6a, 0xc9, 0x60, 0xb7, 0xbe, 0xd6, 0x81, 0x4e, + 0xcf, 0xb6, 0xe8, 0x18, 0x9b, 0xbf, 0xa6, 0x7e, 0x6f, 0x12, 0x5f, 0x41, + 0x72, 0xfb, 0xee, 0xdb, 0x50, 0x45, 0x02, 0xe9, 0xd5, 0xbf, 0x7e, 0xd2, + 0x1d, 0x1b, 0xa9, 0x5a, 0x03, 0x8d, 0x21, 0x8f, 0xc7, 0x34, 0x01, 0x5a, + 0xa7, 0x12, 0xbb, 0xa7, 0xd0, 0xdd, 0x88, 0x14, 0x56, 0xec, 0x9e, 0xf0, + 0xec, 0x0a, 0xf8, 0x3f, 0x5d, 0x6d, 0x19, 0x6d, 0xad, 0xa8, 0xfa, 0xc8, + 0x11, 0xdc, 0x4e, 0x20, 0xd5, 0x05, 0xb6, 0x58, 0x87, 0x94, 0x05, 0x22, + 0x8d, 0x51, 0x5b, 0x0e, 0x62, 0xbd, 0xb9, 0x22, 0xa7, 0x83, 0xd7, 0x98, + 0x0c, 0xce, 0xb6, 0xed, 0x0a, 0xde, 0x23, 0xed, 0x79, 0x7a, 0x27, 0xe6, + 0x48, 0x4d, 0xe7, 0xc3, 0x47, 0xab, 0xa2, 0x25, 0x60, 0xea, 0x41, 0x8a, + 0xed, 0x5e, 0x89, 0xce, 0xf5, 0x7f, 0xeb, 0xb2, 0xaa, 0x30, 0x17, 0x91, + 0x72, 0x48, 0x48, 0xba, 0x80, 0xa1, 0x86, 0x26, 0x70, 0xbc, 0x3d, 0xa2, + 0x69, 0xeb, 0x2a, 0x7f, 0x53, 0x9c, 0xec, 0x48, 0x6c, 0x70, 0x60, 0x98, + 0x68, 0x14, 0x06, 0x0e, 0x41, 0x25, 0xba, 0x15, 0xd4, 0xd6, 0x32, 0x72, + 0x5e, 0xb1, 0xad, 0xf0, 0x31, 0xfd, 0xca, 0x1b, 0x52, 0x75, 0xb6, 0x73, + 0xc0, 0xf1, 0x06, 0x98, 0xbe, 0x7f, 0x39, 0xcb, 0x2c, 0x31, 0xd6, 0x1d, + 0xa7, 0x7f, 0x68, 0x43, 0x82, 0x9e, 0x84, 0x06, 0x9f, 0x54, 0xe1, 0xc9, + 0x0b, 0xc5, 0xf2, 0x53, 0x97, 0x89, 0x60, 0xba, 0xfe, 0xb2, 0xbb, 0x6c, + 0x6d, 0xbe, 0xa3, 0xce, 0xcb, 0xc6, 0xe7, 0x19, 0x82, 0xe5, 0x56, 0x33, + 0x46, 0xc6, 0x21, 0xbc, 0xe9, 0x80, 0xd2, 0x2f, 0x51, 0x1a, 0x12, 0x0d, + 0xf4, 0xd7, 0x11, 0x8b, 0xb3, 0x7e, 0xa5, 0xd7, 0x06, 0x01, 0xc9, 0xe0, + 0x48, 0x49, 0xa1, 0x53, 0x0c, 0xe1, 0x42, 0x1c, 0x06, 0x25, 0x5d, 0x83, + 0xec, 0x5d, 0x2d, 0x0f, 0x01, 0xd2, 0x4a, 0x20, 0xd9, 0x3a, 0x68, 0x26, + 0x03, 0xec, 0x4c, 0x4a, 0x9c, 0x78, 0xa6, 0xe8, 0x4d, 0xda, 0x4f, 0x4d, + 0x52, 0xeb, 0x58, 0x8d, 0x88, 0x8a, 0xd3, 0x7e, 0x6f, 0xfa, 0x9d, 0x6d, + 0xf4, 0x1a, 0xc1, 0xa4, 0x68, 0xba, 0x7c, 0x47, 0xd4, 0x86, 0x0d, 0x32, + 0x4b, 0x74, 0x73, 0x28, 0x93, 0x56, 0xa8, 0xd8, 0xd6, 0x5c, 0x40, 0x23, + 0x90, 0xc0, 0x73, 0xd5, 0x67, 0xec, 0x40, 0x5a, 0x72, 0xe3, 0x99, 0xc6, + 0xf1, 0xdc, 0xac, 0x62, 0x9e, 0x30, 0x6f, 0x60, 0x0e, 0xca, 0x47, 0x2e, + 0x14, 0xdc, 0xaa, 0x95, 0x13, 0xb4, 0x2a, 0xa4, 0x66, 0xcb, 0x7a, 0xdc, + 0x9c, 0x38, 0xa0, 0x32, 0x47, 0xc7, 0x16, 0x46, 0xe1, 0xc7, 0x4c, 0x72, + 0x43, 0x21, 0x2e, 0x92, 0x7e, 0x3a, 0x09, 0xb6, 0x6f, 0x56, 0x18, 0x91, + 0xba, 0x08, 0xa7, 0xab, 0x0f, 0x95, 0xfb, 0x3d, 0x69, 0xb9, 0x69, 0x91, + 0x3f, 0xb7, 0x87, 0x2a, 0x0e, 0xe5, 0x16, 0xa5, 0x3a, 0x7f, 0x1a, 0x9e, + 0x8d, 0x58, 0x8e, 0x00, 0xcd, 0x53, 0xf0, 0xfc, 0xd9, 0xa4, 0xa0, 0xf9, + 0xf7, 0x14, 0x82, 0xaf, 0x54, 0x41, 0xfa, 0x98, 0xf5, 0xbe, 0x51, 0x63, + 0xef, 0xd8, 0x04, 0x4e, 0x5d, 0x3a, 0x1c, 0x48, 0x61, 0xa6, 0x70, 0x38, + 0xae, 0x55, 0xdf, 0x5b, 0xc3, 0xa6, 0x9a, 0x30, 0x48, 0x68, 0xa7, 0x42, + 0xc1, 0xe2, 0x2c, 0x46, 0x33, 0x7f, 0x38, 0xf5, 0x66, 0xda, 0xa1, 0xb1, + 0x55, 0x16, 0x2c, 0xc3, 0x57, 0xdc, 0xc9, 0x11, 0x7c, 0x0c, 0x9d, 0xe1, + 0xfc, 0x23, 0x79, 0xa3, 0x5f, 0x14, 0x06, 0x03, 0x50, 0x95, 0x60, 0x0b, + 0xae, 0x9f, 0x20, 0x31, 0xb6, 0xc0, 0xec, 0xf8, 0x4c, 0x16, 0x50, 0x35, + 0x39, 0x48, 0x1b, 0xd0, 0xa9, 0xa1, 0x4c, 0x55, 0x52, 0x38, 0xd1, 0xc4, + 0xcc, 0x88, 0x81, 0xf9, 0xfa, 0xeb, 0x04, 0x53, 0xb5, 0x42, 0x5d, 0x8b, + 0x3d, 0x39, 0x1b, 0x27, 0x73, 0x50, 0x13, 0xc8, 0x9c, 0x54, 0x61, 0x0c, + 0x1a, 0x57, 0xc5, 0x99, 0x45, 0x85, 0xd8, 0x9a, 0x36, 0x07, 0xa3, 0xe1, + 0x25, 0x0e, 0xde, 0xa5, 0xdf, 0xd2, 0xf0, 0xcf, 0x14, 0x08, 0x65, 0x28, + 0x8f, 0xb6, 0x0c, 0x16, 0x64, 0x59, 0x56, 0x42, 0xf7, 0xb4, 0x66, 0x8f, + 0xd7, 0x1d, 0x86, 0xef, 0x9e, 0x8c, 0xb1, 0xdd, 0x7f, 0x5e, 0x6c, 0x31, + 0x1c, 0xe2, 0xd5, 0x97, 0xa8, 0x8b, 0x0f, 0x64, 0x93, 0x7c, 0x91, 0xcd, + 0xc9, 0x61, 0x95, 0xeb, 0xe1, 0x42, 0x9b, 0x54, 0x41, 0x35, 0x90, 0xcb, + 0x26, 0xee, 0xfb, 0x6b, 0x10, 0x42, 0x93, 0x32, 0xcb, 0x4b, 0xd0, 0x8a, + 0xb5, 0xe6, 0x5e, 0xa1, 0x83, 0x07, 0xe5, 0x89, 0x13, 0x4f, 0x18, 0x4c, + 0x69, 0xdc, 0xf9, 0xd3, 0x23, 0x27, 0xfe, 0xeb, 0xad, 0x96, 0x06, 0x12, + 0x42, 0x9e, 0x6d, 0x8c, 0xdf, 0x3d, 0x29, 0x23, 0x88, 0x6a, 0x64, 0x67, + 0xd8, 0x5f, 0x5e, 0xcf, 0x00, 0xfb, 0x7e, 0x6e, 0x07, 0xfb, 0x04, 0x1a, + 0xcd, 0x07, 0x15, 0x86, 0x36, 0x39, 0xa7, 0x2e, 0xa5, 0x6d, 0x49, 0x7b, + 0x8f, 0x5b, 0x84, 0xd4, 0xd4, 0x88, 0x63, 0xcd, 0x96, 0x30, 0x96, 0x88, + 0xba, 0x75, 0xed, 0xd6, 0x0e, 0x24, 0xd7, 0x44, 0xf3, 0xfc, 0xb6, 0x41, + 0x49, 0x5e, 0xf1, 0x84, 0x95, 0xfd, 0x04, 0x2f, 0x86, 0x46, 0x37, 0x53, + 0x9c, 0x70, 0x7f, 0xdc, 0x8f, 0x7b, 0xe8, 0x44, 0xa2, 0x2e, 0xbe, 0xcf, + 0xae, 0xdf, 0x90, 0xf2, 0x6d, 0xb6, 0x0b, 0xc0, 0xe8, 0xb6, 0x64, 0xdf, + 0x4b, 0x3a, 0x1d, 0xb6, 0xf1, 0xed, 0x05, 0x7f, 0x63, 0x1e, 0xa2, 0x66, + 0x24, 0x0a, 0x20, 0xba, 0xe5, 0xa4, 0xbe, 0x4e, 0x23, 0xda, 0xff, 0x54, + 0x3a, 0xe3, 0xd4, 0x28, 0x14, 0x00, 0x74, 0xad, 0x19, 0xa5, 0xe3, 0x85, + 0xb3, 0x45, 0x1d, 0x52, 0x3c, 0x0f, 0x3a, 0x46, 0x4c, 0x29, 0x41, 0xf5, + 0x5b, 0x94, 0xcb, 0x3c, 0x0e, 0x6d, 0x50, 0x95, 0xca, 0x4b, 0x23, 0x1b, + 0x17, 0x96, 0x93, 0x0d, 0xe5, 0x4c, 0xfd, 0x8f, 0x04, 0x91, 0xad, 0x9b, + 0x1e, 0xfe, 0xab, 0xbc, 0xe7, 0x2c, 0x80, 0xe5, 0xdc, 0xf2, 0xc8, 0x8c, + 0x03, 0x74, 0x95, 0xd7, 0xe7, 0x15, 0x90, 0xf4, 0x81, 0x86, 0xb9, 0x09, + 0x98, 0x67, 0xa2, 0xc5, 0x55, 0x08, 0xbf, 0xb2, 0xb8, 0x4a, 0x6b, 0x0b, + 0xd2, 0x35, 0xa1, 0x0a, 0xd2, 0x3d, 0x17, 0x8a, 0x78, 0xd7, 0x01, 0x1b, + 0x68, 0xea, 0x65, 0x07, 0x45, 0xa6, 0xe4, 0x42, 0x1e, 0x7e, 0x6a, 0x63, + 0xb6, 0x1d, 0x30, 0x99, 0x45, 0x28, 0x54, 0x22, 0x27, 0xba, 0x3e, 0x67, + 0xbb, 0x92, 0x1e, 0xb0, 0xba, 0xaa, 0x0b, 0xed, 0x57, 0x1d, 0xf8, 0x37, + 0x85, 0x03, 0x01, 0x81, 0x25, 0xfb, 0xe9, 0xfe, 0x94, 0xef, 0x55, 0x15, + 0xb7, 0xcc, 0x76, 0x21, 0x9e, 0xd2, 0x56, 0x40, 0xf9, 0x02, 0x05, 0x09, + 0x4f, 0x22, 0xea, 0x2d, 0xbf, 0x73, 0x10, 0x0c, 0x8a, 0x82, 0x34, 0x79, + 0x3a, 0x3a, 0xce, 0xe1, 0x97, 0xc4, 0x3b, 0xef, 0xa7, 0xe0, 0x60, 0x59, + 0x27, 0x4b, 0x56, 0xa7, 0x8d, 0xda, 0x36, 0x12, 0x0b, 0x77, 0x9f, 0x83, + 0xb3, 0x4f, 0xad, 0x11, 0x8e, 0x88, 0x9f, 0x64, 0xd7, 0x39, 0x37, 0xc9, + 0xf9, 0xad, 0xae, 0x91, 0xd2, 0x65, 0xf7, 0x40, 0x61, 0x07, 0x9a, 0x21, + 0x05, 0x12, 0x49, 0x53, 0x18, 0xb8, 0xa1, 0x59, 0x1e, 0x58, 0xd7, 0xb7, + 0xe4, 0x36, 0x58, 0x74, 0x95, 0x0f, 0x5a, 0xf8, 0x70, 0xaa, 0xa8, 0x2a, + 0xc5, 0x76, 0xf2, 0x43, 0x11, 0x48, 0x60, 0x7e, 0x66, 0xe0, 0xc1, 0x92, + 0x5f, 0xbf, 0x4c, 0x93, 0xd1, 0x49, 0xf3, 0x8d, 0x2e, 0xbe, 0x59, 0xb4, + 0xa7, 0xbf, 0x5c, 0x62, 0x54, 0xa6, 0x00, 0x64, 0xd8, 0x76, 0x76, 0x84, + 0x61, 0x4f, 0x9f, 0x44, 0xe8, 0xc5, 0xe6, 0xf0, 0x52, 0x58, 0xcc, 0x7c, + 0xb3, 0x54, 0x4d, 0xb5, 0x4c, 0xef, 0x46, 0xad, 0x64, 0x6d, 0x38, 0x4a, + 0x53, 0xde, 0xed, 0xf6, 0x2a, 0x35, 0x2d, 0x17, 0x38, 0x76, 0x34, 0x78, + 0xcf, 0x50, 0x8d, 0x3f, 0x7a, 0x5b, 0x83, 0x0c, 0xbd, 0x09, 0xb2, 0x6a, + 0x11, 0x8f, 0xbe, 0x09, 0x98, 0xdc, 0x6a, 0x66, 0x11, 0x32, 0x7a, 0x68, + 0x0e, 0xd5, 0x84, 0x94, 0x4c, 0xa5, 0x2b, 0x6b, 0x39, 0xe1, 0x84, 0x8d, + 0x79, 0xbd, 0x16, 0x60, 0x26, 0x39, 0x05, 0x59, 0x7b, 0x81, 0x90, 0x73, + 0xaa, 0xaa, 0x27, 0xb4, 0x0b, 0x19, 0x82, 0xb2, 0x16, 0xf1, 0x82, 0xf0, + 0xb3, 0x44, 0x88, 0xf6, 0x05, 0xff, 0x4a, 0x68, 0xa8, 0xf1, 0x0d, 0xd4, + 0xb7, 0x1b, 0xab, 0x8e, 0xf3, 0x5b, 0x5e, 0xde, 0x94, 0x34, 0x3a, 0x92, + 0x9e, 0x7f, 0xd7, 0x98, 0xd8, 0x99, 0x0b, 0x3b, 0x20, 0xab, 0xa9, 0x3e, + 0xaf, 0xd8, 0x15, 0xd3, 0x50, 0x4b, 0x1f, 0x32, 0xfc, 0x39, 0x63, 0xff, + 0x39, 0x01, 0x9c, 0x88, 0xf5, 0x61, 0xd0, 0x7d, 0xe2, 0xbf, 0xd4, 0xcd, + 0xd7, 0xc4, 0x93, 0x76, 0x42, 0x86, 0x8b, 0xe5, 0x98, 0xd7, 0x54, 0x87, + 0xc4, 0x1f, 0x96, 0xe6, 0x5a, 0x7e, 0x44, 0xb2, 0x32, 0x5d, 0x4a, 0xca, + 0x23, 0xe7, 0xe9, 0xfa, 0x5a, 0xa7, 0x9a, 0x06, 0x05, 0x18, 0xb9, 0x20, + 0x05, 0x8b, 0x41, 0x1d, 0x1c, 0x40, 0xd0, 0x6c, 0xe5, 0xe1, 0x0c, 0x1f, + 0xfa, 0xf6, 0xf8, 0xdb, 0x28, 0xa8, 0xf5, 0x51, 0x29, 0xe8, 0x45, 0x5a, + 0x4b, 0xdb, 0x49, 0x18, 0xe8, 0x54, 0xa2, 0x60, 0xb2, 0x95, 0xea, 0x27, + 0x0c, 0x2f, 0xcc, 0x05, 0x81, 0x56, 0x5f, 0x92, 0x6a, 0xba, 0x69, 0x27, + 0x55, 0x93, 0xb0, 0x90, 0x27, 0xab, 0xf3, 0x7f, 0x46, 0x46, 0x10, 0x39, + 0x57, 0x67, 0x5e, 0x7d, 0x52, 0xba, 0x9e, 0x6d, 0x44, 0x1b, 0x22, 0x1a, + 0x7e, 0xb0, 0x13, 0x50, 0x1c, 0x79, 0xa8, 0x6b, 0x34, 0xb1, 0xb0, 0x3c, + 0x03, 0x97, 0xbc, 0xad, 0x98, 0x15, 0x5b, 0x07, 0x94, 0x09, 0xc9, 0x17, + 0x82, 0xd1, 0x5f, 0xe7, 0xd1, 0xb0, 0x22, 0xa9, 0x6c, 0xc0, 0x4f, 0x13, + 0x0c, 0x15, 0x44, 0xda, 0xee, 0x49, 0x45, 0xaf, 0x1c, 0xb8, 0x5a, 0x6d, + 0x12, 0xd9, 0x75, 0x66, 0x03, 0xf0, 0x01, 0x8e, 0xda, 0x78, 0xb0, 0xe5, + 0x54, 0xb5, 0xb3, 0x3e, 0xc9, 0x5a, 0x20, 0x67, 0x44, 0x48, 0x2e, 0x21, + 0x4a, 0xc7, 0xb1, 0x83, 0xeb, 0xbb, 0x33, 0x1a, 0x9b, 0x68, 0x0f, 0x32, + 0x4b, 0x7c, 0x81, 0xe6, 0x09, 0xa3, 0xab, 0xf9, 0xa6, 0x85, 0x70, 0x13, + 0x88, 0xb1, 0x20, 0x0d, 0x6c, 0x2e, 0xbe, 0x42, 0xe6, 0x04, 0x05, 0x3a, + 0x5c, 0x10, 0x93, 0x07, 0x58, 0xe0, 0xb2, 0x0d, 0x5f, 0x9a, 0x9e, 0x77, + 0x7a, 0xaf, 0x53, 0x05, 0xe1, 0xfb, 0x14, 0xfe, 0xf8, 0x7d, 0x91, 0x18, + 0x6e, 0x85, 0xc1, 0x24, 0x9f, 0x67, 0xe7, 0x7f, 0x43, 0xe6, 0x06, 0x35, + 0x4b, 0x83, 0xf9, 0x65, 0xcf, 0x04, 0xdb, 0x4a, 0xc6, 0x71, 0x56, 0xca, + 0xd1, 0xa6, 0xeb, 0x83, 0xa6, 0x92, 0x8d, 0xff, 0x18, 0xa8, 0xda, 0xc9, + 0xf7, 0x95, 0x66, 0x48, 0x40, 0x2d, 0xc7, 0xf6, 0xda, 0x39, 0x0f, 0x2c, + 0xa4, 0x46, 0x36, 0xd7, 0x68, 0x5e, 0x96, 0xdd, 0x6e, 0x2f, 0xbd, 0x2c, + 0x8e, 0x81, 0xde, 0xac, 0xd2, 0x50, 0x5c, 0x15, 0xf1, 0x0e, 0x74, 0xe8, + 0xab, 0x02, 0xe5, 0x26, 0x47, 0x81, 0xa0, 0x14, 0x99, 0x60, 0x0d, 0xbc, + 0x82, 0x58, 0x4a, 0xe9, 0x4b, 0x43, 0xa7, 0x23, 0x81, 0x30, 0xcb, 0xe4, + 0x5d, 0x28, 0x9f, 0x45, 0x44, 0xad, 0x32, 0xf7, 0xac, 0x37, 0xd9, 0xcc, + 0x97, 0x64, 0xeb, 0x42, 0x43, 0xa8, 0xac, 0x36, 0xf8, 0x99, 0x58, 0x16, + 0xaa, 0x0c, 0xca, 0x4e, 0x17, 0x8e, 0x39, 0x20, 0x01, 0xbb, 0xcf, 0xb0, + 0xd7, 0x4c, 0x54, 0x45, 0x0f, 0xde, 0x94, 0xc3, 0x0f, 0x99, 0x98, 0x5c, + 0xe6, 0xf6, 0xdc, 0x73, 0xf9, 0xfe, 0xe3, 0xd9, 0x1c, 0xf8, 0xc6, 0x1b, + 0x11, 0x4a, 0xb9, 0x3d, 0xbf, 0xda, 0x5a, 0x30, 0x21, 0x40, 0xf8, 0x5b, + 0x01, 0x60, 0xe5, 0xe7, 0xbd, 0x91, 0x67, 0xcc, 0x02, 0x4f, 0x0e, 0x76, + 0x5e, 0xc9, 0x61, 0x95, 0x76, 0x3e, 0xb9, 0x8d, 0x87, 0xbd, 0x64, 0x45, + 0x7c, 0xbe, 0xe4, 0x6e, 0x4d, 0x9d, 0x8c, 0xcd, 0x64, 0x6b, 0xdc, 0x9d, + 0xda, 0xdc, 0xb5, 0x95, 0xe3, 0xc1, 0x7a, 0xe6, 0x80, 0x5a, 0xcd, 0x04, + 0xce, 0x8e, 0x65, 0xf1, 0x6a, 0xb2, 0x91, 0x1c, 0x6b, 0x56, 0xeb, 0x55, + 0x95, 0xc7, 0x19, 0x27, 0x57, 0xd5, 0xf2, 0x0f, 0xa5, 0x08, 0x4b, 0xdb, + 0x84, 0xe0, 0xa7, 0x63, 0x8c, 0x5a, 0x87, 0x8d, 0x13, 0x77, 0x54, 0x2d, + 0x37, 0xe5, 0x8a, 0xf3, 0x64, 0x1f, 0xb2, 0xed, 0xc5, 0x57, 0xe5, 0x1c, + 0xf0, 0xc6, 0xd4, 0xb1, 0x57, 0xc5, 0x9e, 0x50, 0x3b, 0xf1, 0xbf, 0x19, + 0x67, 0x77, 0xae, 0x37, 0xc4, 0x31, 0xff, 0xb7, 0x95, 0x49, 0xaf, 0xd0, + 0xdc, 0x95, 0x8b, 0xb8, 0xb2, 0x63, 0x9c, 0x3b, 0x61, 0x27, 0x4b, 0x4c, + 0xc0, 0x9b, 0x3a, 0x73, 0x6b, 0xbc, 0xb5, 0x8b, 0x29, 0x5c, 0xcb, 0xc7, + 0x32, 0x61, 0xb1, 0x25, 0xca, 0x20, 0x75, 0x33, 0xbe, 0x56, 0x27, 0x38, + 0x3d, 0x75, 0x1d, 0x6c, 0x44, 0x65, 0xf0, 0xe8, 0x26, 0xfa, 0x04, 0x5b, + 0x36, 0xe4, 0xc9, 0x19, 0x95, 0x68, 0x06, 0x89, 0x8f, 0xf4, 0x2f, 0x57, + 0x73, 0x8c, 0x7e, 0x77, 0x20, 0xdd, 0x89, 0x1e, 0xbf, 0x58, 0x2e, 0x46, + 0x22, 0xc0, 0x78, 0xcf, 0x1c, 0xd5, 0xb0, 0x49, 0xaf, 0x30, 0xc6, 0x17, + 0x1a, 0xc3, 0xb8, 0xe6, 0x67, 0x1e, 0x55, 0x50, 0xed, 0xce, 0x85, 0x0f, + 0x6b, 0xd9, 0x13, 0xc4, 0x33, 0x82, 0xe6, 0x0e, 0x1d, 0xe0, 0xe7, 0xb8, + 0x62, 0x21, 0x13, 0x8b, 0xcd, 0xb2, 0x98, 0x49, 0x29, 0xb3, 0x4a, 0xa1, + 0xca, 0xba, 0x23, 0xba, 0xf9, 0x6b, 0x0e, 0x7d, 0xed, 0x61, 0x65, 0xf1, + 0x3c, 0x67, 0x51, 0x86, 0x33, 0x39, 0xee, 0x1e, 0xb8, 0xdf, 0xbc, 0x21, + 0x08, 0x11, 0x87, 0x9e, 0xc5, 0xc7, 0xd6, 0x4b, 0xd4, 0x12, 0x91, 0x84, + 0x38, 0xb7, 0x20, 0xf3, 0x3e, 0x1d, 0x50, 0xa7, 0xb2, 0x05, 0xb8, 0xa5, + 0x32, 0xf1, 0xe8, 0x4d, 0x2f, 0x37, 0xf1, 0xa5, 0x44, 0xeb, 0x9d, 0xc6, + 0xa2, 0xb8, 0x7d, 0x26, 0x53, 0x8a, 0xfb, 0x19, 0x3a, 0xb5, 0x49, 0xb7, + 0x43, 0x45, 0x5c, 0xa5, 0xef, 0xe3, 0xed, 0x00, 0x18, 0x3a, 0xcf, 0xd6, + 0xa0, 0x51, 0x42, 0x60, 0x46, 0x7e, 0xa1, 0xf8, 0x94, 0x83, 0xa2, 0x16, + 0x5b, 0xb2, 0x50, 0xb5, 0x2f, 0x73, 0xf9, 0x0e, 0x32, 0xa0, 0x97, 0x01, + 0x04, 0x0d, 0x3b, 0xfb, 0xca, 0x78, 0x06, 0xfb, 0x73, 0x86, 0x4b, 0xf6, + 0xe0, 0x1a, 0x9f, 0x59, 0x61, 0xea, 0x19, 0x70, 0xd3, 0x43, 0xe7, 0xd1, + 0x81, 0xfc, 0x0e, 0x44, 0xbf, 0x7c, 0xb3, 0xcf, 0xff, 0xcc, 0x20, 0x41, + 0xab, 0x36, 0x90, 0xf6, 0x53, 0x2b, 0x38, 0x05, 0x04, 0xbc, 0x98, 0xec, + 0xbd, 0xa6, 0x8e, 0xd2, 0x62, 0xa3, 0xc4, 0x58, 0x96, 0x04, 0x6a, 0x5c, + 0xe5, 0x32, 0x1d, 0x23, 0xb5, 0x8d, 0xeb, 0x5e, 0xcc, 0x38, 0xf4, 0xa8, + 0x99, 0xe4, 0x6e, 0xc7, 0x5a, 0x58, 0xd6, 0xfe, 0xd9, 0x2c, 0x9e, 0x67, + 0x77, 0xfd, 0x9c, 0x2b, 0xa3, 0x42, 0xf6, 0x7d, 0x72, 0x16, 0x18, 0x6a, + 0x96, 0x74, 0x1e, 0x47, 0x84, 0x55, 0x8b, 0x6b, 0x36, 0xd7, 0x91, 0x9c, + 0x43, 0x30, 0xf9, 0x36, 0x98, 0x95, 0x38, 0xfb, 0xe0, 0xab, 0xa3, 0xa0, + 0xc1, 0x74, 0x43, 0x9b, 0xe3, 0x1b, 0x30, 0x53, 0x57, 0xd3, 0x2a, 0x92, + 0x19, 0x11, 0x70, 0x40, 0x7d, 0xba, 0x42, 0x69, 0xdb, 0x96, 0x88, 0x34, + 0x0d, 0xaf, 0xfe, 0xcc, 0xf3, 0xad, 0x55, 0xf2, 0xb1, 0x00, 0xaf, 0x35, + 0x91, 0xa1, 0x85, 0xfd, 0x9a, 0xae, 0xd5, 0x32, 0x5b, 0x8f, 0x00, 0x74, + 0x20, 0xd6, 0x1d, 0x2a, 0xc5, 0x1e, 0x50, 0x62, 0xe9, 0xca, 0xa0, 0xcc, + 0x74, 0x36, 0x4e, 0xd4, 0xb3, 0xf3, 0xb5, 0x41, 0xa3, 0xa9, 0xdd, 0x9e, + 0x89, 0x26, 0xff, 0x5b, 0x75, 0x1e, 0xb0, 0x20, 0x52, 0x7e, 0x65, 0x4c, + 0xc0, 0x4d, 0x58, 0xeb, 0xd6, 0x08, 0x59, 0x1b, 0xf8, 0xfe, 0x87, 0xd7, + 0xcf, 0x8c, 0x6e, 0x85, 0xb4, 0xe8, 0x5c, 0xc8, 0xdb, 0x4e, 0xf4, 0x78, + 0x7a, 0xc8, 0x6f, 0x9a, 0x8b, 0x60, 0xcc, 0xb3, 0x41, 0x4f, 0x47, 0xab, + 0xaf, 0xf2, 0x02, 0xaf, 0x0a, 0xb7, 0xea, 0x4b, 0x35, 0x77, 0xe2, 0x6f, + 0x4d, 0x58, 0x7e, 0xe9, 0x75, 0x02, 0xa3, 0x47, 0x5a, 0x55, 0xa8, 0x2c, + 0x74, 0x2b, 0x0a, 0x26, 0x5b, 0x5b, 0x0c, 0x89, 0xab, 0xa8, 0x88, 0xd0, + 0xbe, 0x42, 0xfd, 0x5c, 0xc1, 0xb0, 0x95, 0x2a, 0x12, 0x41, 0x3e, 0xa8, + 0x11, 0x7e, 0x76, 0x84, 0x46, 0xcb, 0xfe, 0x03, 0x21, 0x57, 0x13, 0x77, + 0x95, 0xa6, 0x7b, 0xe1, 0x75, 0x03, 0x8c, 0x9b, 0x7f, 0xf6, 0x8b, 0x48, + 0x7d, 0xbe, 0x65, 0xb5, 0x80, 0xca, 0x43, 0xae, 0x15, 0xd1, 0x5d, 0x90, + 0x51, 0xff, 0xc9, 0xd9, 0x0d, 0xa7, 0xac, 0xe8, 0x81, 0x89, 0xfa, 0xc5, + 0x96, 0x4b, 0xdf, 0x40, 0xd5, 0x70, 0xf8, 0xa2, 0x03, 0x25, 0x7a, 0x23, + 0x51, 0xdb, 0x02, 0x7e, 0x86, 0xd7, 0xea, 0xe6, 0x85, 0xd1, 0xc9, 0xb9, + 0xa0, 0x0d, 0x49, 0xe3, 0xcf, 0x88, 0xcc, 0xab, 0x01, 0xfb, 0xe1, 0x48, + 0xa0, 0x5e, 0x2c, 0xf3, 0x79, 0x09, 0x88, 0xb4, 0x42, 0xa5, 0x1d, 0xf7, + 0x2d, 0xb8, 0x29, 0x53, 0x34, 0xe0, 0x27, 0xd0, 0xac, 0x91, 0x8d, 0x46, + 0x4f, 0x59, 0x8b, 0x76, 0xe7, 0x57, 0x0c, 0xd3, 0x49, 0x4b, 0x66, 0xbc, + 0xa2, 0x02, 0x7c, 0x4a, 0xce, 0x33, 0xcf, 0x99, 0xf1, 0x48, 0xe4, 0xd0, + 0x12, 0xc0, 0x66, 0x9a, 0x34, 0xb1, 0xc6, 0xc9, 0x65, 0x84, 0x58, 0x20, + 0xda, 0xcb, 0x79, 0x73, 0x56, 0x25, 0x81, 0xef, 0x65, 0x37, 0xa2, 0x7c, + 0x5e, 0xab, 0xbe, 0xd1, 0xfe, 0x0a, 0x31, 0xe9, 0xf4, 0xc0, 0x6f, 0x62, + 0x75, 0xfe, 0x65, 0x08, 0xc6, 0x28, 0x73, 0x9f, 0x3b, 0x66, 0x5b, 0x45, + 0x75, 0x3d, 0x64, 0x4d, 0xe3, 0xd5, 0x5a, 0x85, 0xb8, 0x4d, 0x02, 0x5c, + 0x35, 0x3d, 0xe4, 0xa4, 0x9f, 0x60, 0x5c, 0xf2, 0x31, 0xfe, 0xeb, 0x6c, + 0xb7, 0x89, 0x9c, 0x49, 0x8a, 0xae, 0xbc, 0x63, 0x16, 0x2f, 0x98, 0xd0, + 0xe1, 0x78, 0xdf, 0x9b, 0x03, 0xe2, 0x34, 0x22, 0x34, 0xb2, 0x04, 0x32, + 0xf5, 0x69, 0xbf, 0xfc, 0x68, 0xd1, 0x40, 0xc4, 0x92, 0x15, 0x16, 0x32, + 0x48, 0xba, 0x53, 0x3d, 0xa0, 0xcb, 0x24, 0x7a, 0x12, 0x3c, 0xee, 0x3a, + 0x5f, 0xd2, 0x9e, 0xbc, 0xba, 0xa2, 0xc6, 0x0d, 0x33, 0x2b, 0x3c, 0x43, + 0x0b, 0x05, 0x04, 0x62, 0xd0, 0x3d, 0x9b, 0x14, 0xd9, 0x09, 0x85, 0x1c, + 0xd0, 0x6d, 0x4f, 0x23, 0x9e, 0xff, 0x13, 0x22, 0xb1, 0x84, 0x91, 0x1c, + 0x2d, 0xde, 0xc8, 0x24, 0xe6, 0x29, 0x13, 0x82, 0xa7, 0x64, 0xb7, 0xb6, + 0x3e, 0xf4, 0xfe, 0xa0, 0xed, 0x58, 0x10, 0x90, 0x48, 0x02, 0xba, 0x22, + 0x07, 0xcf, 0x56, 0x79, 0x71, 0xc9, 0x63, 0x9c, 0x67, 0xcd, 0x4a, 0xf7, + 0x58, 0x42, 0x62, 0x95, 0xef, 0xd1, 0x1c, 0x41, 0x6c, 0xdf, 0x84, 0x59, + 0x6f, 0x28, 0xa4, 0x49, 0x19, 0x23, 0x60, 0xf6, 0x89, 0xfd, 0x94, 0x3e, + 0xb2, 0xf3, 0x0f, 0x86, 0x9b, 0x76, 0x6e, 0xc6, 0x48, 0xeb, 0x99, 0x9f, + 0xd9, 0xbe, 0x91, 0x73, 0x14, 0x04, 0x3f, 0x9e, 0x2c, 0x1c, 0x4d, 0x9b, + 0x20, 0xbd, 0xa3, 0x91, 0x7b, 0x3e, 0xe1, 0xe8, 0x83, 0x2e, 0x64, 0xea, + 0xbe, 0xd8, 0xa6, 0xcb, 0xe9, 0x12, 0x19, 0xdd, 0x12, 0x15, 0x81, 0xf6, + 0x02, 0x72, 0x44, 0x39, 0xa4, 0xd3, 0xbb, 0x38, 0xcc, 0xe7, 0xfa, 0x06, + 0x0f, 0x1e, 0x17, 0x34, 0x97, 0x2a, 0x9c, 0xbd, 0x77, 0x6c, 0xc8, 0x35, + 0x9e, 0x85, 0xae, 0x75, 0xc0, 0xf3, 0x80, 0x29, 0x1f, 0xb4, 0x7a, 0x09, + 0xab, 0xcc, 0xfd, 0x1a, 0x65, 0xff, 0xee, 0x2c, 0x8f, 0x97, 0x5e, 0xf5, + 0x52, 0xfe, 0x90, 0xdf, 0x2d, 0x67, 0x59, 0xb2, 0x04, 0x9c, 0x94, 0xa8, + 0xfd, 0xf1, 0x89, 0x25, 0xdc, 0x6e, 0x04, 0x1c, 0xa1, 0xa0, 0x6a, 0xf9, + 0xac, 0x15, 0x3f, 0x83, 0x3d, 0x1f, 0x32, 0x8c, 0x0d, 0x06, 0xcf, 0x6a, + 0x21, 0xab, 0x7a, 0x38, 0xd7, 0xcd, 0x8f, 0x67, 0x89, 0x53, 0x10, 0xb3, + 0xdf, 0x39, 0xbc, 0x53, 0xa0, 0xa8, 0x48, 0xda, 0x2d, 0x61, 0x07, 0x17, + 0x4a, 0xd3, 0xb1, 0x98, 0x8a, 0xf1, 0x61, 0x99, 0x0e, 0x6e, 0x6c, 0x59, + 0x6b, 0xa3, 0xb3, 0x2a, 0x50, 0x12, 0x75, 0xf0, 0xd9, 0xd2, 0xe2, 0x7a, + 0xc1, 0x13, 0x27, 0x52, 0x48, 0x8b, 0xe3, 0x39, 0x06, 0x86, 0x39, 0xd7, + 0x00, 0x6e, 0x00, 0xc6, 0x15, 0x58, 0x3c, 0xb8, 0x3b, 0x14, 0x05, 0xa6, + 0xf1, 0x89, 0x5b, 0x89, 0x84, 0xd0, 0x1b, 0x3c, 0xea, 0x67, 0x98, 0x5b, + 0xa9, 0x7e, 0x90, 0xa2, 0xa2, 0x93, 0x8c, 0x2b, 0xea, 0x4c, 0xa9, 0x71, + 0x33, 0x9f, 0x9a, 0x30, 0xa7, 0xb1, 0x70, 0xf0, 0xc6, 0x23, 0x31, 0xdb, + 0xe4, 0x92, 0x66, 0xce, 0xad, 0x6b, 0xfb, 0x87, 0xb1, 0x6f, 0x89, 0xe8, + 0x64, 0x56, 0xf3, 0xc7, 0x63, 0x5c, 0xd8, 0x5a, 0xbe, 0x38, 0x5c, 0xaf, + 0xd3, 0x60, 0x6d, 0xc9, 0x74, 0xe9, 0xcf, 0x4d, 0x63, 0xbc, 0x88, 0x2a, + 0xce, 0x11, 0x05, 0x7d, 0xcd, 0x96, 0x54, 0x5d, 0xd5, 0x6f, 0x6d, 0x02, + 0xd2, 0x6f, 0xf4, 0x90, 0x9d, 0x37, 0xff, 0x86, 0x0a, 0x16, 0xc6, 0xc9, + 0xe7, 0x13, 0xf7, 0x8c, 0x06, 0x1a, 0x03, 0x89, 0x46, 0xef, 0xc0, 0x44, + 0x7d, 0xab, 0x84, 0x96, 0x02, 0x42, 0x62, 0x90, 0xef, 0x85, 0x9a, 0x55, + 0xba, 0x67, 0xcd, 0x73, 0xd7, 0x0b, 0x42, 0x93, 0xf9, 0xe2, 0xd6, 0xd6, + 0x3e, 0x3f, 0xeb, 0xa4, 0x9f, 0x92, 0xcf, 0xf9, 0xc9, 0xeb, 0x68, 0xd5, + 0x34, 0x35, 0xbe, 0x6b, 0xd1, 0x82, 0x21, 0xa6, 0x7d, 0x44, 0xa0, 0x3a, + 0x62, 0x5c, 0x99, 0xfb, 0xae, 0xe2, 0x34, 0x48, 0x23, 0x0b, 0x5a, 0x98, + 0xf6, 0x75, 0x70, 0xe9, 0x23, 0xdd, 0x56, 0x72, 0x6c, 0x83, 0x20, 0xf0, + 0xd5, 0x63, 0x29, 0x6b, 0xcc, 0xd1, 0x29, 0xc1, 0xa7, 0x2d, 0x4b, 0xf2, + 0xdc, 0xa8, 0x1b, 0x14, 0x85, 0xba, 0x6b, 0xca, 0x30, 0xae, 0xdc, 0xa5, + 0xbd, 0x91, 0x70, 0xd2, 0x1c, 0x3e, 0xff, 0x99, 0x52, 0xb3, 0xae, 0x47, + 0x20, 0xab, 0x06, 0xc9, 0x61, 0x1e, 0x52, 0xbf, 0x08, 0xb4, 0x50, 0x2b, + 0xd7, 0x96, 0x2d, 0x01, 0x89, 0x21, 0xd2, 0x74, 0x22, 0x22, 0xdd, 0x7a, + 0x18, 0x4d, 0x2d, 0x62, 0x07, 0xbd, 0x74, 0x8a, 0xa1, 0x9e, 0x7f, 0xbc, + 0x34, 0x61, 0x8b, 0xe9, 0xf3, 0x73, 0x6d, 0x46, 0xc5, 0x67, 0xea, 0x3b, + 0x5e, 0xbb, 0xb4, 0x6d, 0x19, 0x43, 0x29, 0xe2, 0x34, 0xfc, 0x02, 0x70, + 0x26, 0x17, 0x07, 0x2a, 0x01, 0x82, 0x3e, 0xdf, 0xf3, 0xec, 0xba, 0xd5, + 0x3a, 0x8b, 0x43, 0x59, 0xb7, 0x62, 0xab, 0x3e, 0xfa, 0xf3, 0xc2, 0xc0, + 0xba, 0x07, 0xef, 0x30, 0x93, 0x94, 0xf6, 0x3d, 0xbe, 0x32, 0xa0, 0xe2, + 0x20, 0xd3, 0x1d, 0x5f, 0x68, 0x5d, 0x1b, 0x85, 0x68, 0xee, 0x63, 0x4b, + 0x1f, 0xa8, 0x9e, 0xaa, 0xa2, 0xf1, 0x12, 0x2e, 0x8f, 0x58, 0xc6, 0x37, + 0xc6, 0x4b, 0x18, 0x8f, 0x04, 0x24, 0x1f, 0x08, 0xb0, 0xe5, 0x68, 0x02, + 0x6b, 0x99, 0xe2, 0xfb, 0x0d, 0xfc, 0x3f, 0x61, 0x05, 0xcb, 0x59, 0x99, + 0xc7, 0x67, 0xfe, 0x34, 0x13, 0x3d, 0x7e, 0x1b, 0xb9, 0x57, 0x14, 0x6a, + 0x34, 0x11, 0x05, 0xfa, 0xc7, 0x64, 0x5f, 0x96, 0xe3, 0x9e, 0x23, 0x68, + 0x1d, 0xdb, 0x2d, 0x39, 0x31, 0x56, 0x1a, 0xf9, 0xa2, 0xf2, 0xe8, 0xb9, + 0x30, 0x0e, 0xa9, 0x83, 0x3e, 0xb5, 0xab, 0x70, 0x0f, 0x18, 0x67, 0xe3, + 0x87, 0xda, 0xb9, 0x34, 0xda, 0x58, 0x84, 0x1f, 0xb6, 0x89, 0xe7, 0x81, + 0x44, 0x28, 0xf2, 0xe2, 0x71, 0x02, 0xbd, 0x9b, 0x70, 0x5e, 0x85, 0x3b, + 0x71, 0x5b, 0x6d, 0x80, 0x54, 0xaa, 0xd8, 0xc3, 0x21, 0x00, 0x1b, 0x68, + 0x3a, 0x31, 0xb3, 0x94, 0xae, 0x49, 0x71, 0x9c, 0x7c, 0xec, 0xe4, 0x26, + 0x24, 0xb1, 0xa7, 0x24, 0x4d, 0xcf, 0x18, 0xec, 0x96, 0x5a, 0x5d, 0x47, + 0xea, 0x3c, 0x9c, 0x62, 0x41, 0x5f, 0xe6, 0xd2, 0x92, 0x5b, 0xba, 0x81, + 0x1a, 0x96, 0x58, 0xd8, 0x62, 0x05, 0xa9, 0x9a, 0xbd, 0xc0, 0xd8, 0x6a, + 0xad, 0x23, 0x02, 0x5d, 0xa4, 0xf3, 0x7e, 0xa8, 0x2d, 0xf5, 0xd7, 0x37, + 0xf7, 0x44, 0x53, 0x82, 0x25, 0xb6, 0xc9, 0x10, 0x88, 0xe8, 0xe4, 0x75, + 0x68, 0x4e, 0xc1, 0x25, 0x40, 0x03, 0xab, 0x8c, 0x1a, 0x4a, 0xe7, 0xf3, + 0xe0, 0xac, 0x15, 0xd6, 0x24, 0xc2, 0xb9, 0x6c, 0x22, 0x62, 0xd4, 0xb5, + 0xec, 0xd7, 0x37, 0x33, 0x4a, 0x29, 0x6d, 0xe6, 0x6c, 0xaa, 0x24, 0x03, + 0x4d, 0x42, 0xb4, 0x15, 0x23, 0xb6, 0x0e, 0xeb, 0xe5, 0x4e, 0x79, 0x1c, + 0x9d, 0xc7, 0xd1, 0xad, 0xc0, 0xc6, 0xf5, 0x28, 0x36, 0xa2, 0xfd, 0xd7, + 0x9c, 0x6e, 0x53, 0xc8, 0x15, 0x8a, 0x60, 0x38, 0x8d, 0x97, 0xde, 0xfc, + 0xf9, 0x63, 0x58, 0x32, 0x89, 0x7c, 0xd0, 0x9f, 0x25, 0x51, 0x36, 0xa8, + 0x3f, 0x41, 0xf1, 0xf6, 0x32, 0x74, 0x05, 0x85, 0x60, 0x77, 0xe2, 0xac, + 0xf7, 0x30, 0x5f, 0x46, 0x8b, 0xa3, 0x74, 0x8a, 0x6b, 0x9f, 0xe9, 0x6f, + 0xfd, 0xcd, 0xb7, 0xb4, 0xf6, 0xf8, 0xab, 0x23, 0x64, 0x8b, 0xeb, 0x7c, + 0x95, 0xea, 0x58, 0xd5, 0x61, 0x3a, 0x76, 0x95, 0x74, 0x93, 0x38, 0x01, + 0xf0, 0x14, 0x28, 0x90, 0xd4, 0xeb, 0x30, 0x1a, 0x13, 0x4b, 0xb1, 0xd5, + 0x1b, 0x67, 0x36, 0xc7, 0x43, 0x0d, 0x74, 0x48, 0x30, 0x3f, 0x86, 0x39, + 0x77, 0xf6, 0x29, 0x0a, 0x12, 0x42, 0xc7, 0xb8, 0xa2, 0xd1, 0x73, 0x63, + 0x0f, 0x6f, 0xab, 0xed, 0x7d, 0xeb, 0x2c, 0xf8, 0x29, 0x56, 0x97, 0xe9, + 0x37, 0x89, 0x0b, 0xa7, 0xc9, 0x24, 0xea, 0x9f, 0x88, 0x3d, 0x5f, 0xeb, + 0xd3, 0x2e, 0x7f, 0xd3, 0x82, 0x0b, 0x6f, 0x12, 0xd4, 0x2e, 0x4c, 0xee, + 0xc7, 0xeb, 0xe5, 0x44, 0xef, 0xc7, 0x48, 0xe0, 0x65, 0xb3, 0xc8, 0xa1, + 0x88, 0xfb, 0x83, 0x0e, 0xa4, 0x52, 0x56, 0x24, 0x32, 0xc7, 0xe1, 0xf9, + 0xbd, 0x7f, 0x4c, 0x2c, 0x2d, 0x23, 0xff, 0xcd, 0x52, 0xba, 0xd5, 0x7f, + 0xf3, 0xb0, 0x22, 0x81, 0xb9, 0x65, 0xa1, 0x01, 0xe4, 0x30, 0xc3, 0x6a, + 0x9e, 0xc9, 0x49, 0x52, 0x80, 0x10, 0xee, 0xb7, 0x09, 0x3a, 0x54, 0x35, + 0x44, 0x89, 0x1e, 0x23, 0x38, 0xf6, 0xe7, 0x8e, 0x7a, 0xd0, 0x82, 0x0d, + 0x6d, 0x71, 0x6e, 0x57, 0x33, 0x45, 0x30, 0xb7, 0x59, 0xc0, 0xb8, 0xeb, + 0x80, 0x9c, 0x49, 0x0b, 0x5b, 0x7b, 0xf6, 0x65, 0x1b, 0x3c, 0x39, 0xdd, + 0x9c, 0xdc, 0x98, 0xb0, 0x2f, 0xf2, 0x92, 0x12, 0xe3, 0xc9, 0x2a, 0xa5, + 0x82, 0x4e, 0xe5, 0x27, 0x37, 0xec, 0xef, 0x1b, 0xef, 0x69, 0x0a, 0x50, + 0xcb, 0x5a, 0x94, 0x4a, 0x1e, 0xe9, 0x1b, 0x28, 0x36, 0xc1, 0x73, 0x5d, + 0x0d, 0x20, 0x10, 0x79, 0x30, 0x9b, 0xcf, 0x56, 0x0d, 0x74, 0x43, 0x10, + 0x0c, 0x1b, 0xa5, 0xe6, 0x98, 0x2a, 0x1d, 0x7a, 0xe7, 0xb3, 0x52, 0xd9, + 0xc6, 0xd6, 0xb2, 0x5b, 0x6f, 0x7c, 0xa6, 0x1d, 0x23, 0x1d, 0x1e, 0x00, + 0xb4, 0x42, 0x54, 0xc1, 0xa2, 0xd1, 0xb6, 0xf4, 0x91, 0x4a, 0x05, 0xf2, + 0x99, 0x5b, 0x97, 0xb2, 0x3c, 0xb4, 0xe2, 0xfd, 0x09, 0xd6, 0x92, 0x73, + 0x11, 0x3c, 0x3d, 0x73, 0x49, 0x84, 0x6c, 0x59, 0xd7, 0xb1, 0x91, 0xd4, + 0x68, 0x04, 0x3d, 0xdf, 0xa4, 0x0b, 0xb2, 0xce, 0xf0, 0xc8, 0xcb, 0xda, + 0x85, 0x92, 0x13, 0xa9, 0xee, 0x52, 0x4c, 0x33, 0x1c, 0x45, 0x8b, 0x60, + 0xed, 0x00, 0xcc, 0x4a, 0x5f, 0x45, 0xf5, 0x08, 0x3c, 0x45, 0x18, 0xe8, + 0x87, 0xf7, 0x89, 0x15, 0x7f, 0x02, 0xd9, 0xf8, 0x00, 0x25, 0xe2, 0x87, + 0xc1, 0xc7, 0x09, 0x8d, 0xc2, 0x72, 0x28, 0x07, 0x7a, 0x56, 0xdf, 0x9e, + 0x29, 0x04, 0x7a, 0xc8, 0x39, 0xb3, 0xb9, 0x5b, 0x2f, 0x58, 0xfb, 0xd1, + 0x43, 0xdb, 0x61, 0xa0, 0x4b, 0x1c, 0xfa, 0x16, 0x30, 0x09, 0xde, 0x71, + 0x86, 0x11, 0xb1, 0x08, 0x0f, 0x29, 0x24, 0x62, 0x38, 0x12, 0x92, 0xaf, + 0xdb, 0x2a, 0x2e, 0x96, 0xa7, 0x81, 0xa2, 0x37, 0x1e, 0xec, 0x32, 0xfb, + 0x52, 0x78, 0xd5, 0x46, 0x5e, 0xe8, 0x5f, 0x7b, 0x28, 0xcd, 0xb0, 0xa4, + 0x9f, 0x66, 0xdd, 0x52, 0xb8, 0x3d, 0xec, 0xa2, 0x8c, 0xaa, 0xf2, 0x08, + 0x52, 0x57, 0x89, 0x55, 0x4b, 0xd9, 0x71, 0x70, 0x50, 0xea, 0x71, 0x6f, + 0xfc, 0x61, 0xfe, 0x3c, 0x9e, 0x32, 0x7e, 0x7b, 0x24, 0x9b, 0x1f, 0xdc, + 0xd5, 0x3f, 0x91, 0x0c, 0x01, 0x4c, 0x43, 0xd2, 0x61, 0x34, 0x70, 0x1e, + 0x51, 0x4c, 0xa7, 0xa7, 0x46, 0x15, 0x34, 0x82, 0x7c, 0xa5, 0x82, 0xf6, + 0xed, 0x72, 0x41, 0xc9, 0x5f, 0xbd, 0x9c, 0x19, 0xea, 0x5a, 0x6f, 0x5a, + 0xba, 0xa1, 0x03, 0x52, 0x2d, 0x53, 0xb8, 0xe1, 0x54, 0x59, 0x7a, 0xb7, + 0xa0, 0xbb, 0x15, 0x75, 0xf9, 0xb9, 0xde, 0x93, 0xc7, 0x58, 0x2c, 0x00, + 0xfb, 0x64, 0xe2, 0x22, 0x26, 0x55, 0x46, 0x46, 0xf6, 0x46, 0xd5, 0x21, + 0x09, 0x68, 0x1d, 0x10, 0x21, 0x48, 0x8f, 0x98, 0x68, 0x6b, 0x69, 0xe8, + 0x74, 0xa8, 0x4c, 0xe4, 0x6f, 0x50, 0x40, 0xbe, 0xdb, 0xc1, 0xdc, 0xf5, + 0xfd, 0xd2, 0xf4, 0x54, 0x3c, 0x75, 0xe3, 0xc1, 0x2f, 0x93, 0x88, 0x05, + 0xc9, 0xc0, 0xc1, 0x79, 0xce, 0xf4, 0xab, 0xcc, 0x63, 0x02, 0x23, 0xda, + 0x9c, 0x3e, 0xc5, 0x8e, 0xe3, 0x3c, 0x6a, 0xec, 0x6b, 0xe4, 0x92, 0x01, + 0x23, 0x28, 0x10, 0x25, 0xd5, 0x20, 0xb8, 0xd9, 0xbd, 0xde, 0xa7, 0x7b, + 0x73, 0x38, 0xef, 0x69, 0xa8, 0xc7, 0x66, 0x5b, 0x95, 0xff, 0x52, 0xc8, + 0x14, 0xe4, 0x64, 0x9a, 0xd2, 0x09, 0x6f, 0x79, 0xf6, 0xb0, 0x81, 0x44, + 0x01, 0xf2, 0xd9, 0x7c, 0x56, 0x12, 0x02, 0x50, 0x85, 0xe5, 0x36, 0x95, + 0x8a, 0x39, 0xf5, 0x0e, 0x1b, 0x1e, 0xcb, 0x53, 0x56, 0x92, 0xc8, 0xfe, + 0x1b, 0x85, 0xdd, 0xfd, 0x03, 0x4b, 0x1e, 0x54, 0x95, 0xc8, 0x10, 0xd2, + 0x16, 0xbb, 0x8d, 0x55, 0x12, 0x7b, 0x52, 0x1d, 0xe1, 0xea, 0x75, 0xfd, + 0x22, 0x5b, 0xef, 0x61, 0x25, 0x9c, 0x40, 0xf2, 0x9c, 0x7e, 0x7e, 0x5d, + 0x1e, 0x4d, 0xcc, 0x99, 0x15, 0xba, 0xd8, 0x78, 0xb7, 0x38, 0xe5, 0x62, + 0x66, 0x1a, 0x25, 0xe8, 0xe5, 0x56, 0x81, 0xee, 0x21, 0xea, 0xd4, 0x8e, + 0xa5, 0x51, 0xcd, 0x4e, 0x54, 0x29, 0x09, 0x02, 0x2a, 0x47, 0x46, 0xf3, + 0xb4, 0xad, 0xba, 0x44, 0xb2, 0xfb, 0x3d, 0x33, 0xbb, 0x6f, 0x66, 0xb4, + 0xef, 0xc2, 0x5e, 0xe7, 0xf3, 0x6c, 0x72, 0xa9, 0xfd, 0xb4, 0x71, 0xe3, + 0xb7, 0xff, 0xed, 0x6d, 0xfc, 0xe4, 0x43, 0xb3, 0xb5, 0xc3, 0xf5, 0x79, + 0x97, 0x67, 0x11, 0xa4, 0x35, 0x43, 0x46, 0x64, 0x11, 0x14, 0xf5, 0xbf, + 0xaf, 0x7c, 0xbe, 0xb8, 0x9e, 0x06, 0x9e, 0xe1, 0x6c, 0xbb, 0x0f, 0xb9, + 0xa1, 0x16, 0xcc, 0xbb, 0x83, 0xa2, 0x3c, 0xb3, 0xad, 0x6d, 0x03, 0xad, + 0x43, 0xf6, 0xe4, 0x28, 0x87, 0xa8, 0x0a, 0x53, 0x79, 0x36, 0x91, 0xaf, + 0xb2, 0x67, 0x5a, 0xf1, 0xa2, 0x0d, 0x3c, 0xdd, 0x34, 0x6f, 0x62, 0xb9, + 0xb9, 0x1b, 0xb8, 0x54, 0x0e, 0x70, 0x41, 0x4e, 0x37, 0x5b, 0xbb, 0x51, + 0xc3, 0x72, 0xb0, 0x4a, 0x67, 0x92, 0x3d, 0x9a, 0xec, 0x70, 0x20, 0x21, + 0x7c, 0x5d, 0xb9, 0xd8, 0x67, 0x60, 0xae, 0x8f, 0x2b, 0x2c, 0x2d, 0xb0, + 0x57, 0x3b, 0x6f, 0xa6, 0x75, 0xf2, 0x6a, 0x14, 0x96, 0xbb, 0x6d, 0x81, + 0xad, 0xbe, 0x8d, 0x34, 0x80, 0xe0, 0x39, 0xe3, 0xa4, 0xb1, 0x46, 0xb3, + 0xbb, 0xef, 0x15, 0xf9, 0x16, 0xb2, 0x04, 0xb0, 0x92, 0x9f, 0xca, 0xfb, + 0x82, 0x31, 0x18, 0x2e, 0xac, 0xd1, 0x92, 0x2d, 0x62, 0x80, 0x1b, 0x2e, + 0x72, 0x77, 0xc7, 0xb7, 0xd3, 0xb3, 0xb8, 0x03, 0xc0, 0x04, 0x49, 0x56, + 0xf8, 0xd0, 0x2e, 0x1e, 0x06, 0x5a, 0x6c, 0xa3, 0x5a, 0xb3, 0xd3, 0xdd, + 0x6a, 0xca, 0x6d, 0x90, 0x55, 0xa7, 0x37, 0x1c, 0xe9, 0x53, 0x2b, 0x13, + 0x86, 0xf5, 0xf6, 0x5b, 0xe2, 0x61, 0xf9, 0x3b, 0x78, 0x31, 0x5e, 0xd0, + 0x7d, 0x8e, 0x2e, 0xba, 0x81, 0xca, 0xe2, 0x18, 0x9d, 0x77, 0x3e, 0x2d, + 0x4f, 0x32, 0x9c, 0x85, 0x50, 0xb3, 0x12, 0xf3, 0x92, 0xe0, 0x55, 0x60, + 0xdc, 0x2c, 0x92, 0xc8, 0x39, 0xa3, 0x7c, 0xb7, 0x67, 0x5c, 0x2b, 0xbd, + 0x55, 0x80, 0xcd, 0x24, 0x7d, 0xc5, 0x95, 0x0a, 0x29, 0x06, 0x3f, 0x7c, + 0xac, 0x6e, 0x6b, 0x4b, 0xfd, 0x04, 0x96, 0x22, 0x8a, 0x1e, 0xb0, 0x89, + 0x64, 0x98, 0x7a, 0xba, 0xce, 0x1a, 0x1a, 0xd2, 0x4a, 0x09, 0x14, 0x8f, + 0xe4, 0xd1, 0xeb, 0x6f, 0x36, 0x8a, 0x7e, 0xe5, 0x84, 0x8c, 0xe2, 0xc1, + 0xb4, 0x0c, 0x8b, 0xc9, 0xef, 0x61, 0x2d, 0x23, 0xff, 0xad, 0xfb, 0x40, + 0x02, 0xa0, 0x63, 0xe6, 0x0d, 0x1c, 0xfc, 0xac, 0xad, 0xbe, 0x29, 0x83, + 0x85, 0x68, 0xff, 0xa3, 0x0f, 0x42, 0xee, 0x06, 0x26, 0xd4, 0x78, 0xd6, + 0xeb, 0x1e, 0xe0, 0x35, 0xcc, 0xad, 0xfd, 0xa0, 0xb5, 0x3e, 0x87, 0x86, + 0xa8, 0x4e, 0x5b, 0x4f, 0xdd, 0x57, 0xd9, 0xc3, 0x95, 0x7e, 0xae, 0xcd, + 0x7a, 0xce, 0xf5, 0x37, 0x0f, 0xea, 0x71, 0x55, 0xa8, 0x8e, 0x62, 0xa5, + 0xfe, 0xa1, 0x40, 0xa7, 0xe7, 0x69, 0xaf, 0xe6, 0x24, 0xbd, 0xe3, 0xff, + 0x73, 0xa5, 0x11, 0x8a, 0x5a, 0x50, 0x6f, 0xa6, 0x71, 0x03, 0x13, 0xd8, + 0xaf, 0x8c, 0xfd, 0x1a, 0x38, 0x68, 0x49, 0x0b, 0x44, 0xc6, 0xec, 0xfb, + 0x3b, 0xcf, 0xf9, 0x13, 0xef, 0xed, 0x54, 0xd1, 0xc3, 0x34, 0x07, 0x2f, + 0x00, 0xac, 0x94, 0x25, 0x79, 0x5a, 0xa4, 0xc3, 0x39, 0x8d, 0xdf, 0xdd, + 0xfd, 0x99, 0xb3, 0x94, 0x94, 0xd0, 0x01, 0x92, 0x55, 0x0a, 0x7f, 0xd6, + 0x9d, 0x5c, 0x8a, 0x40, 0x37, 0x91, 0x4b, 0x8a, 0x0c, 0x8a, 0x95, 0x7c, + 0xb8, 0x52, 0x53, 0x3b, 0x19, 0x64, 0x9e, 0x1d, 0xac, 0xbd, 0x86, 0xff, + 0x15, 0x40, 0xb0, 0xaf, 0x0f, 0x90, 0x12, 0x42, 0x7c, 0xa3, 0xca, 0xff, + 0xf7, 0x58, 0x63, 0x5e, 0xa1, 0xcf, 0x97, 0x1c, 0xa2, 0xb0, 0x78, 0xca, + 0x41, 0xdd, 0x52, 0x0b, 0xeb, 0x14, 0xce, 0x49, 0x00, 0x6e, 0x7b, 0x62, + 0x91, 0x85, 0xb7, 0xa6, 0x03, 0xb0, 0xff, 0x41, 0xff, 0x26, 0xfa, 0x76, + 0xb0, 0x7d, 0xe8, 0xd2, 0x5d, 0x14, 0xc6, 0x89, 0xcf, 0x7b, 0xb3, 0xc8, + 0xba, 0xd5, 0x02, 0xcb, 0x2c, 0x7d, 0x02, 0x11, 0xa2, 0x69, 0x9c, 0xb2, + 0xe5, 0xcb, 0x82, 0xa9, 0x3d, 0xc5, 0xb1, 0x92, 0xd2, 0xdb, 0xb5, 0x90, + 0xaf, 0x3f, 0xcb, 0x5f, 0x57, 0x1d, 0xec, 0x89, 0x84, 0x65, 0x67, 0x57, + 0xf2, 0x22, 0xa9, 0x56, 0x4b, 0x12, 0x59, 0x12, 0x3d, 0x01, 0xa1, 0x90, + 0x9b, 0x3f, 0xfd, 0x79, 0x0a, 0x5c, 0x9c, 0x73, 0x01, 0xed, 0x26, 0xf1, + 0x37, 0x9b, 0x2a, 0x38, 0xc1, 0xbf, 0xf4, 0x1b, 0x0d, 0xb8, 0x0d, 0x5c, + 0x9e, 0x8e, 0x01, 0xc3, 0x22, 0xbe, 0x30, 0xa3, 0xe8, 0xbd, 0x7c, 0xc2, + 0xee, 0x3a, 0x12, 0xef, 0xcf, 0xa7, 0xaf, 0xae, 0xad, 0xf5, 0x56, 0x8b, + 0x2b, 0xb2, 0x9d, 0x09, 0x83, 0x38, 0x52, 0xd7, 0x7b, 0xda, 0xbf, 0x8b, + 0x12, 0xed, 0x54, 0x07, 0x2e, 0x81, 0xd5, 0x4f, 0xf3, 0x5a, 0x61, 0xd1, + 0xe8, 0xe5, 0x29, 0xa6, 0x9b, 0x82, 0x91, 0x0a, 0x0e, 0xf4, 0x3a, 0x94, + 0x48, 0x19, 0x50, 0xbf, 0xb5, 0x5c, 0xb5, 0x02, 0x29, 0xfd, 0xbf, 0xf7, + 0x9b, 0x50, 0x04, 0x62, 0x7b, 0x8d, 0x1f, 0xa3, 0x07, 0x54, 0x06, 0x3f, + 0xd6, 0x81, 0x00, 0x88, 0x87, 0x44, 0x16, 0xfb, 0xc1, 0xdd, 0xbc, 0xec, + 0xf6, 0xfe, 0x25, 0x42, 0x0b, 0x11, 0x0f, 0x67, 0x14, 0xee, 0x76, 0xb3, + 0xdf, 0x6e, 0x7a, 0x49, 0x9c, 0x37, 0x7b, 0x59, 0xe0, 0x9a, 0xbe, 0xf5, + 0x96, 0x94, 0x7c, 0xb0, 0x21, 0xde, 0x07, 0x5e, 0x6c, 0x0b, 0xd6, 0x0c, + 0xb4, 0x64, 0x73, 0xcd, 0xca, 0x1f, 0x73, 0x74, 0xb2, 0x49, 0x4c, 0x18, + 0xa2, 0xae, 0xdc, 0xac, 0x21, 0x95, 0xc3, 0x46, 0x82, 0x9c, 0xcf, 0xd4, + 0x89, 0x42, 0x5a, 0xe2, 0xa5, 0x47, 0x88, 0xbe, 0x11, 0x9a, 0x2b, 0xea, + 0x8f, 0x11, 0x8c, 0x81, 0x67, 0xc3, 0xf8, 0x25, 0xb7, 0xc5, 0x5e, 0x7b, + 0x54, 0xa4, 0x28, 0xd5, 0x0a, 0xa3, 0x07, 0x27, 0x8e, 0xb9, 0x35, 0x20, + 0x27, 0xe5, 0x05, 0xbf, 0x63, 0xc3, 0x13, 0x0c, 0x11, 0x2d, 0x5c, 0xb1, + 0x78, 0x9b, 0xc7, 0x6c, 0x00, 0x88, 0xdf, 0x25, 0xc6, 0xa2, 0x6a, 0x6e, + 0xc4, 0x6d, 0xd0, 0x6b, 0x35, 0xe9, 0x80, 0xd8, 0xe3, 0x8d, 0xaa, 0x22, + 0xc6, 0xd8, 0xac, 0x50, 0x0f, 0x43, 0x75, 0x93, 0xc8, 0xdb, 0xcc, 0xab, + 0x34, 0xb9, 0x07, 0x1c, 0x7e, 0xe3, 0xf8, 0x80, 0x5e, 0xf3, 0x38, 0x42, + 0xaf, 0xcc, 0x7e, 0xdc, 0x71, 0x90, 0x21, 0x76, 0xf1, 0xc0, 0xd2, 0x08, + 0x00, 0xbb, 0xcd, 0xcc, 0xe0, 0x5a, 0x90, 0xb1, 0x9d, 0x71, 0x8c, 0x35, + 0x16, 0x38, 0xd9, 0xda, 0x95, 0xe5, 0x31, 0x4d, 0x60, 0x44, 0xda, 0x72, + 0x41, 0xe3, 0xc1, 0xca, 0x6c, 0xe7, 0x11, 0x03, 0x79, 0x1e, 0x41, 0xe2, + 0x5a, 0x34, 0x39, 0x21, 0xd7, 0xbc, 0x33, 0x98, 0x96, 0x39, 0x16, 0xef, + 0x83, 0x6c, 0xa5, 0xce, 0x9f, 0x74, 0x2c, 0x31, 0xae, 0x15, 0xb3, 0x5b, + 0xd7, 0x1c, 0xf3, 0xc9, 0x3f, 0x49, 0xda, 0xb9, 0x0e, 0x19, 0x35, 0x66, + 0x93, 0x75, 0x86, 0x33, 0x9d, 0x60, 0x77, 0x2c, 0x5d, 0x5e, 0xb6, 0x86, + 0xef, 0x54, 0x77, 0xf7, 0x35, 0xe5, 0xea, 0xe6, 0x94, 0xe8, 0xec, 0x00, + 0x80, 0x14, 0x4c, 0xab, 0x74, 0x50, 0x9b, 0xfa, 0x11, 0x22, 0x5c, 0xd8, + 0x87, 0x92, 0xbf, 0xde, 0xbe, 0x63, 0xea, 0x6c, 0x48, 0x57, 0x20, 0xf4, + 0x6c, 0xc4, 0x30, 0xb2, 0x9d, 0x8f, 0x4b, 0x75, 0x98, 0xda, 0xde, 0xf9, + 0x27, 0x84, 0xb3, 0x30, 0x37, 0x95, 0x98, 0xf3, 0x2f, 0x85, 0xd6, 0xad, + 0x16, 0x15, 0x44, 0xb4, 0xfa, 0xaa, 0xf5, 0x90, 0x02, 0x4b, 0xb6, 0x4c, + 0x34, 0xb7, 0x0b, 0x2b, 0x5b, 0xae, 0xaf, 0x97, 0xa6, 0xa9, 0xa2, 0x92, + 0xaf, 0xa5, 0x43, 0x38, 0x29, 0x17, 0xf4, 0x40, 0x74, 0xc4, 0x44, 0xbd, + 0xc6, 0x06, 0x0b, 0xb9, 0x96, 0x01, 0x0f, 0xe8, 0x2b, 0xe5, 0x12, 0xea, + 0xaa, 0x66, 0x7a, 0x8e, 0x15, 0x45, 0xb8, 0x71, 0x72, 0x45, 0x90, 0x8c, + 0x38, 0xf2, 0xc7, 0x2f, 0x03, 0x2e, 0xa8, 0x3a, 0xd3, 0x20, 0xf8, 0x96, + 0x37, 0xc2, 0x8f, 0xc9, 0x7d, 0x71, 0x35, 0x78, 0x99, 0x22, 0xf5, 0xa9, + 0x38, 0x1c, 0x05, 0x9a, 0x5a, 0x97, 0xeb, 0x77, 0x43, 0xb7, 0xa7, 0x6a, + 0x52, 0xd8, 0x58, 0x53, 0x66, 0xf8, 0x9a, 0x24, 0x14, 0xd9, 0xfd, 0xc9, + 0x52, 0x12, 0x33, 0x17, 0x50, 0x5c, 0xd6, 0xb7, 0xd3, 0xff, 0xe4, 0x8c, + 0x01, 0xe7, 0x93, 0xe0, 0x35, 0x8f, 0xd5, 0x45, 0xe6, 0xe7, 0xbd, 0x46, + 0x20, 0x41, 0x90, 0x77, 0xf2, 0x3e, 0xba, 0x06, 0xef, 0x93, 0x59, 0x74, + 0xf5, 0xd1, 0x9e, 0x28, 0xd9, 0x2a, 0x4c, 0xb9, 0xfe, 0x88, 0xd8, 0x77, + 0xc9, 0x1e, 0x90, 0x8b, 0xcf, 0xbb, 0x1c, 0xae, 0x74, 0x53, 0x31, 0x20, + 0x79, 0x2e, 0x70, 0x19, 0x2d, 0x6c, 0x57, 0xbb, 0xba, 0xe5, 0x0c, 0xee, + 0xe2, 0xfd, 0x97, 0xf5, 0x59, 0x4d, 0x63, 0xd7, 0x32, 0x1f, 0x4c, 0x93, + 0xa5, 0xd7, 0x85, 0xd1, 0xf8, 0xb8, 0x5c, 0x06, 0xfe, 0x27, 0xc5, 0x3b, + 0x9f, 0x24, 0xc9, 0x5f, 0x43, 0x1c, 0x13, 0x97, 0xca, 0xfe, 0x23, 0xe7, + 0x3f, 0x7a, 0xb6, 0xd6, 0xdc, 0x56, 0xd6, 0x0a, 0xb0, 0x37, 0x06, 0xa3, + 0x02, 0x36, 0xeb, 0xc5, 0x00, 0x88, 0x42, 0x71, 0xc0, 0xde, 0x79, 0xcc, + 0xdc, 0x8c, 0xf8, 0x04, 0x61, 0x3f, 0x77, 0xbe, 0xcc, 0xdf, 0x4a, 0x7e, + 0x77, 0x47, 0x06, 0xf8, 0xad, 0x29, 0xee, 0xf8, 0x5e, 0xd9, 0xb5, 0xec, + 0xae, 0xf6, 0x02, 0x0a, 0xf2, 0x43, 0xdf, 0xa9, 0xbb, 0xec, 0x77, 0xee, + 0x5b, 0x6a, 0xa6, 0xaf, 0x2e, 0x90, 0x1f, 0xf1, 0xdd, 0x94, 0xb9, 0x56, + 0x17, 0x00, 0xb1, 0xb7, 0x48, 0x88, 0x57, 0xea, 0x5f, 0xbe, 0x99, 0xc0, + 0x1a, 0xfc, 0xb8, 0x59, 0xa2, 0x0b, 0x79, 0x6f, 0x41, 0xa6, 0x16, 0xd4, + 0x2d, 0xd1, 0x32, 0x20, 0x3d, 0x60, 0xb1, 0x6c, 0x1d, 0x78, 0xaa, 0x0e, + 0x35, 0xba, 0xbd, 0x6c, 0x48, 0x7c, 0xac, 0x0d, 0x36, 0x43, 0xfe, 0x3f, + 0x97, 0xaa, 0xb6, 0x6d, 0x07, 0xf4, 0x66, 0x0b, 0x42, 0x24, 0x49, 0xb2, + 0x92, 0x78, 0xea, 0x91, 0xa8, 0xfa, 0xa2, 0xf4, 0x99, 0x51, 0xc8, 0x92, + 0x29, 0x06, 0x8a, 0xce, 0xe6, 0x01, 0xac, 0x5e, 0x3b, 0xbe, 0xe3, 0xfc, + 0xf1, 0x4c, 0x53, 0x49, 0xa0, 0xdf, 0x68, 0xaa, 0xba, 0xf7, 0x51, 0xc2, + 0x31, 0x5b, 0xc7, 0xad, 0xd4, 0xd4, 0x29, 0xaf, 0x07, 0xa0, 0xf2, 0xdd, + 0x20, 0xeb, 0xe7, 0x23, 0x1f, 0x9d, 0xc9, 0xde, 0x56, 0xe9, 0x62, 0xba, + 0xf1, 0xca, 0xb7, 0x7c, 0xf8, 0x25, 0x77, 0x41, 0x48, 0xa7, 0x7e, 0x9b, + 0x8a, 0x81, 0x2f, 0x0a, 0xaa, 0x36, 0xb1, 0xbc, 0xa8, 0x7d, 0x75, 0x73, + 0xa9, 0x08, 0xb5, 0x4b, 0x11, 0x6f, 0xb6, 0xd0, 0x2a, 0x76, 0xc6, 0x1f, + 0x23, 0x71, 0xe9, 0xca, 0xb7, 0x85, 0x96, 0x0b, 0xff, 0x9b, 0x0d, 0x23, + 0x54, 0x53, 0x85, 0xc6, 0x02, 0x47, 0x08, 0x8c, 0x8e, 0xd8, 0xc2, 0xa4, + 0xb5, 0x0f, 0xe9, 0xd4, 0x93, 0x6e, 0x02, 0xb2, 0x8e, 0xda, 0xa2, 0x65, + 0x8a, 0x8d, 0x18, 0x03, 0x7f, 0xb5, 0x0d, 0xbc, 0x60, 0x37, 0xe6, 0x8a, + 0xf3, 0x6e, 0x77, 0x42, 0x7f, 0x35, 0x04, 0xf7, 0x42, 0x64, 0x30, 0xe5, + 0x3d, 0xd8, 0x80, 0xbe, 0x07, 0xcf, 0xf1, 0x68, 0x5b, 0xc9, 0x74, 0x0b, + 0x91, 0xdc, 0x5d, 0x14, 0x7f, 0x38, 0x1c, 0x19, 0x22, 0x69, 0x94, 0x14, + 0x56, 0x9b, 0x8d, 0x08, 0xff, 0x2e, 0xf9, 0xc8, 0xef, 0x49, 0x46, 0x69, + 0x58, 0x5e, 0x9c, 0x13, 0x98, 0x29, 0x9d, 0x48, 0xb0, 0x4c, 0x16, 0xb7, + 0xae, 0x3d, 0x45, 0xd6, 0x04, 0x72, 0xbf, 0xcf, 0x34, 0xff, 0xe4, 0x38, + 0x9e, 0x27, 0x97, 0x66, 0xf2, 0x42, 0x3d, 0xbf, 0x7f, 0xb2, 0xed, 0x68, + 0xf8, 0xb0, 0x98, 0xf1, 0x8e, 0x41, 0x5c, 0x7b, 0x7d, 0xd4, 0xce, 0x18, + 0xc9, 0x70, 0x33, 0x71, 0x35, 0x81, 0x0e, 0x2b, 0x40, 0x76, 0x4b, 0x45, + 0x55, 0xa3, 0x5c, 0xcf, 0xa6, 0x3f, 0x51, 0x09, 0xe8, 0xa4, 0xd7, 0xc2, + 0x46, 0x71, 0x54, 0x92, 0xd8, 0x5b, 0x38, 0x51, 0x63, 0xfb, 0xd7, 0x8e, + 0x2a, 0x01, 0xb0, 0x01, 0x08, 0x81, 0xec, 0xb7, 0xe7, 0x8f, 0x42, 0xc4, + 0x10, 0x2d, 0xe1, 0x9e, 0x88, 0x33, 0xa9, 0x9e, 0x26, 0x2e, 0x96, 0x68, + 0x3e, 0xc9, 0x09, 0x90, 0x38, 0xdd, 0xd3, 0x3c, 0xcb, 0x95, 0x05, 0xc7, + 0xea, 0x04, 0xa0, 0xa6, 0x20, 0x81, 0x0b, 0xb2, 0x16, 0xe0, 0xb8, 0x7a, + 0x22, 0xbb, 0x62, 0x58, 0x21, 0xc3, 0x25, 0x76, 0x2f, 0xa0, 0x39, 0x88, + 0xf3, 0xe6, 0x4c, 0xa9, 0xb5, 0x18, 0x27, 0xd4, 0x34, 0x40, 0xda, 0xcf, + 0x70, 0x6f, 0x9e, 0xcd, 0x19, 0x84, 0xb0, 0x68, 0x8b, 0x4e, 0x77, 0xc6, + 0x7e, 0xf8, 0xe6, 0xf6, 0x32, 0x7b, 0x11, 0xfc, 0x8a, 0x9d, 0x10, 0x2f, + 0x20, 0xb7, 0x13, 0x97, 0xb6, 0xa2, 0xad, 0xab, 0x10, 0xd7, 0x1f, 0xa4, + 0xfb, 0x17, 0x08, 0x33, 0x38, 0x05, 0xe6, 0x86, 0x89, 0xe9, 0x94, 0xb2, + 0x1e, 0x4c, 0xac, 0x12, 0x63, 0xb7, 0xab, 0x7a, 0x14, 0x5c, 0x91, 0xc6, + 0x15, 0xaa, 0x0d, 0x4f, 0x92, 0xa5, 0x46, 0xc6, 0x7e, 0x7a, 0xb8, 0xc6, + 0x34, 0x0f, 0x0d, 0xfb, 0xf8, 0xd0, 0x93, 0x4a, 0x7b, 0x23, 0xac, 0x03, + 0xe7, 0x1f, 0xb6, 0xb4, 0x5e, 0x5a, 0xa7, 0x91, 0x4b, 0xe6, 0x44, 0x5e, + 0x9d, 0xbe, 0x56, 0x58, 0x1a, 0x1f, 0x6d, 0x8c, 0xb9, 0xcc, 0x91, 0x37, + 0x11, 0xe8, 0xbc, 0x5c, 0x51, 0xb9, 0x6c, 0xe6, 0xcd, 0x81, 0x13, 0x0c, + 0xa1, 0x0a, 0xac, 0xc0, 0x83, 0x99, 0x51, 0x63, 0x24, 0x49, 0x21, 0x80, + 0x45, 0x2e, 0xd6, 0x41, 0xdb, 0x5c, 0x90, 0x47, 0xf9, 0x8c, 0x07, 0x36, + 0xaa, 0x50, 0xa8, 0x32, 0xf7, 0xc5, 0x68, 0x48, 0x5b, 0x10, 0xd6, 0x7c, + 0x30, 0x99, 0x8f, 0xb1, 0xe0, 0xf9, 0xc7, 0xd5, 0x8c, 0xd1, 0x0b, 0x1a, + 0x8d, 0x1d, 0xa1, 0x48, 0x09, 0x44, 0x01, 0x9a, 0x6b, 0x24, 0x61, 0x74, + 0x0c, 0x30, 0x7e, 0xe5, 0xe4, 0xba, 0x29, 0xc9, 0x1a, 0xe7, 0x7a, 0xb5, + 0xfc, 0xb3, 0xb4, 0xb6, 0x5e, 0x71, 0x54, 0xca, 0x6e, 0xe1, 0xcc, 0x2f, + 0x25, 0x1a, 0xa0, 0x91, 0xf1, 0x29, 0xfb, 0x23, 0x21, 0xc5, 0x76, 0x88, + 0x0c, 0x03, 0x36, 0xbe, 0x73, 0x23, 0xa9, 0x55, 0xa7, 0x04, 0x4f, 0x12, + 0x0c, 0xdc, 0x70, 0xb1, 0x71, 0xe1, 0xde, 0xf1, 0x1f, 0x10, 0xa1, 0x88, + 0x52, 0x27, 0xce, 0xbe, 0x14, 0x66, 0xca, 0x33, 0x28, 0xbc, 0x76, 0x40, + 0xf8, 0x33, 0x46, 0x25, 0xb1, 0x95, 0x76, 0x9b, 0x02, 0xfc, 0xd4, 0xbf, + 0x9f, 0x57, 0xf8, 0x0c, 0x42, 0x0e, 0x32, 0x67, 0x21, 0x92, 0xec, 0x47, + 0xfc, 0x85, 0xd2, 0x26, 0x98, 0x8c, 0x96, 0x2c, 0x5c, 0x11, 0x94, 0xb9, + 0xac, 0x76, 0x0f, 0x52, 0xf0, 0xed, 0xdd, 0xe2, 0xcc, 0x9d, 0x1a, 0xf7, + 0x42, 0xdd, 0x89, 0x86, 0x41, 0x54, 0xf4, 0xe7, 0xfd, 0x01, 0x79, 0xa0, + 0xc8, 0xee, 0x40, 0x26, 0x71, 0xea, 0x03, 0xe1, 0x00, 0x13, 0x55, 0x67, + 0x03, 0x22, 0xf3, 0xbb, 0x78, 0xdb, 0xc4, 0x2e, 0xd5, 0x55, 0xfc, 0x60, + 0xe5, 0x42, 0x8d, 0x97, 0xa5, 0x98, 0xe8, 0xbf, 0x88, 0x6f, 0x5d, 0xc0, + 0xaa, 0x7b, 0x4a, 0x38, 0x1c, 0x6f, 0xbc, 0xcc, 0x34, 0x5c, 0x33, 0x95, + 0x17, 0x74, 0x3b, 0x8a, 0x8b, 0xfb, 0xd8, 0xe9, 0xbf, 0x69, 0x77, 0xf7, + 0xed, 0x1d, 0x4c, 0x93, 0x3f, 0x60, 0x66, 0xf6, 0x41, 0x66, 0x08, 0x09, + 0xaf, 0x64, 0xa9, 0x85, 0x07, 0x49, 0x4b, 0x3f, 0xa0, 0x56, 0xd7, 0xde, + 0xcb, 0x40, 0x21, 0xa2, 0x81, 0x66, 0x38, 0x58, 0xc0, 0x3e, 0x11, 0x6b, + 0x59, 0xea, 0x0a, 0xdd, 0xa5, 0x72, 0x5e, 0xda, 0xaa, 0x68, 0x27, 0x15, + 0xc4, 0x29, 0xa4, 0x54, 0x48, 0x9f, 0x99, 0x99, 0xba, 0xfd, 0xc5, 0x19, + 0x63, 0xba, 0x6a, 0x72, 0x42, 0xc0, 0xa1, 0xec, 0x3e, 0x21, 0xd9, 0x48, + 0xf5, 0x36, 0x7e, 0x6e, 0x3c, 0x5e, 0x4f, 0x02, 0x9a, 0x5d, 0x97, 0x00, + 0x82, 0x6d, 0xf7, 0x18, 0xf1, 0x6f, 0x90, 0x0e, 0x7e, 0x7f, 0xd0, 0xa5, + 0x30, 0x88, 0xde, 0x82, 0x6a, 0xd5, 0x98, 0xd4, 0xbb, 0xc6, 0x0a, 0x5d, + 0x5f, 0xf8, 0x7c, 0xda, 0xbb, 0x07, 0x36, 0x5a, 0x0e, 0xc3, 0x0d, 0x7b, + 0xcd, 0x7b, 0xf7, 0x8d, 0xeb, 0x63, 0x7e, 0x32, 0xe7, 0x27, 0x40, 0x33, + 0xd6, 0x5d, 0xf9, 0x34, 0x47, 0xa2, 0x6e, 0x0c, 0x0d, 0xb6, 0x14, 0xc8, + 0x29, 0xb7, 0xc9, 0x6e, 0x78, 0x8e, 0xa3, 0xc6, 0x07, 0x49, 0xcc, 0x15, + 0x60, 0x73, 0xd6, 0x62, 0xe3, 0x45, 0x68, 0x73, 0x0c, 0x79, 0x60, 0x51, + 0xd8, 0x0d, 0xb4, 0x0e, 0x4e, 0xa7, 0xdf, 0x1c, 0x84, 0x2e, 0x1c, 0x57, + 0xd2, 0x99, 0x68, 0xcf, 0xaa, 0x2e, 0x8f, 0xed, 0x6b, 0x57, 0x94, 0xbb, + 0x24, 0x0b, 0xd7, 0x3e, 0xc7, 0xf5, 0xbd, 0x93, 0xf6, 0x31, 0x2f, 0xc8, + 0x56, 0x20, 0xb9, 0xeb, 0x95, 0xad, 0x3f, 0x9d, 0xf9, 0xee, 0xdd, 0x7e, + 0x61, 0xc9, 0xb7, 0x67, 0xfe, 0xce, 0xa3, 0x9f, 0xf4, 0x15, 0x6a, 0x9e, + 0x9a, 0xbb, 0xc2, 0x77, 0xc4, 0x86, 0xf0, 0x62, 0xb6, 0xba, 0xf2, 0xdd, + 0xab, 0x79, 0x4a, 0xef, 0x9f, 0x83, 0x68, 0xdb, 0x1c, 0x5b, 0x30, 0xe7, + 0x5b, 0x9b, 0x42, 0x12, 0xe7, 0xd5, 0xce, 0xe1, 0xe6, 0xfa, 0x87, 0x5a, + 0x81, 0x60, 0xbd, 0xb3, 0x69, 0xf9, 0x3d, 0xd3, 0x38, 0x3b, 0x75, 0x95, + 0xe1, 0x5c, 0xe7, 0x2c, 0xd0, 0x18, 0x68, 0xf8, 0x59, 0x05, 0x03, 0xf1, + 0xee, 0x2c, 0x29, 0xb3, 0x22, 0x5a, 0x5f, 0x52, 0x2a, 0xac, 0xde, 0x1e, + 0x1f, 0x91, 0x96, 0x2a, 0x82, 0x0d, 0x76, 0x8d, 0x33, 0x1b, 0x2a, 0x1a, + 0x01, 0xe6, 0x78, 0x3d, 0x13, 0x7c, 0x86, 0x8c, 0x84, 0xc1, 0xc2, 0x82, + 0xac, 0x99, 0x9c, 0xe6, 0x35, 0x8f, 0x93, 0x8d, 0xaf, 0x29, 0xe8, 0xf4, + 0x07, 0x77, 0xb1, 0x19, 0xbf, 0x08, 0x87, 0x67, 0x37, 0x33, 0xd9, 0x38, + 0xae, 0x58, 0xd7, 0x31, 0xc8, 0xc8, 0x53, 0x32, 0x9a, 0x00, 0x23, 0x23, + 0x11, 0x17, 0x5e, 0x56, 0x82, 0x60, 0xf7, 0x87, 0xc1, 0x79, 0x38, 0x0d, + 0x49, 0x73, 0x37, 0x68, 0xb8, 0x67, 0x9c, 0x36, 0x04, 0xf2, 0xf8, 0x93, + 0xe5, 0x28, 0xaa, 0xbb, 0x1f, 0x00, 0x2c, 0x80, 0xa1, 0x18, 0xbb, 0x94, + 0xdf, 0x95, 0x08, 0xb1, 0x27, 0x4c, 0x2b, 0x5b, 0x2e, 0x43, 0xcf, 0x3f, + 0xc6, 0xde, 0xf8, 0xac, 0x1d, 0xa5, 0x37, 0xff, 0x6c, 0xa8, 0xf5, 0xe3, + 0x88, 0xac, 0x37, 0x14, 0x11, 0xb9, 0x5d, 0x0f, 0xdc, 0x39, 0xb7, 0x70, + 0x12, 0x44, 0xa2, 0x7f, 0x0b, 0xbb, 0x19, 0xbd, 0x1b, 0xa1, 0x1f, 0x1b, + 0x6b, 0x68, 0xf1, 0x9c, 0x17, 0x51, 0xf1, 0xd0, 0x1c, 0x6a, 0xd8, 0xf9, + 0x69, 0x48, 0x7b, 0xbe, 0x34, 0x54, 0xc8, 0x6c, 0x8e, 0x5b, 0xb4, 0xb8, + 0xf1, 0x7d, 0xfd, 0xe3, 0xe4, 0xbc, 0xdb, 0x30, 0x47, 0x87, 0x6b, 0xfb, + 0xd6, 0xca, 0x0a, 0xcf, 0xdd, 0x13, 0x15, 0xd4, 0xd4, 0x27, 0xcf, 0xb2, + 0xef, 0xb0, 0x90, 0x5d, 0x7e, 0x32, 0x9d, 0x4d, 0xc1, 0x1d, 0xe0, 0xe1, + 0x25, 0x24, 0xc8, 0xdd, 0x39, 0x85, 0x35, 0xc0, 0xe0, 0xa3, 0x33, 0xd4, + 0x75, 0xeb, 0xef, 0xf2, 0x0a, 0x57, 0x14, 0xc7, 0x94, 0x92, 0x4f, 0x51, + 0x6a, 0xf9, 0xfd, 0x6b, 0x26, 0x95, 0xfe, 0xb6, 0x62, 0x56, 0xc8, 0xfb, + 0x55, 0xa9, 0x1f, 0x10, 0x85, 0xf0, 0xc8, 0x24, 0xf8, 0xa1, 0xd2, 0x15, + 0x2d, 0x5b, 0xa7, 0x96, 0x88, 0x14, 0x55, 0xf4, 0x5e, 0x88, 0xa4, 0xe9, + 0x08, 0x26, 0xb2, 0xdf, 0x8c, 0xaf, 0xc2, 0x8c, 0x81, 0x11, 0x98, 0xd9, + 0xae, 0x69, 0x17, 0x58, 0x8a, 0x50, 0xc4, 0x4d, 0x98, 0x6e, 0x4f, 0x7c, + 0x51, 0x7c, 0x44, 0x2d, 0xf2, 0xaf, 0xc8, 0x8f, 0x38, 0x5c, 0x98, 0x4d, + 0x09, 0x32, 0x6c, 0x37, 0x82, 0xe1, 0x62, 0x2e, 0x1a, 0x2f, 0x23, 0x86, + 0xf6, 0x5d, 0xb7, 0x9a, 0x76, 0x68, 0xaf, 0x56, 0x59, 0x9e, 0x5d, 0x17, + 0xcd, 0xec, 0x03, 0xcb, 0x19, 0x32, 0xe6, 0xce, 0x69, 0x8c, 0xdf, 0x4f, + 0x8e, 0x3b, 0xb0, 0x0f, 0x6a, 0x50, 0xda, 0x24, 0x68, 0x33, 0x40, 0xaa, + 0x14, 0xae, 0x32, 0x82, 0x40, 0xa7, 0x5a, 0x2e, 0xf3, 0xb3, 0xfb, 0x1d, + 0x5e, 0xcd, 0xd0, 0xd8, 0x19, 0xc5, 0x11, 0x47, 0x84, 0x20, 0x7c, 0x47, + 0x81, 0x54, 0x0d, 0xc3, 0xc5, 0x52, 0xb3, 0xc8, 0x68, 0x57, 0x53, 0xc6, + 0x94, 0xdc, 0xd2, 0x3c, 0x12, 0x19, 0x97, 0x13, 0xc7, 0x88, 0x34, 0xfe, + 0xde, 0xd7, 0xdf, 0x8f, 0xf1, 0xef, 0x29, 0xab, 0x9a, 0xe4, 0xb5, 0xb7, + 0x2e, 0xa4, 0x7f, 0x5d, 0xf2, 0x64, 0x70, 0x5c, 0x54, 0xb7, 0xb0, 0x79, + 0x8a, 0xbe, 0x3b, 0x50, 0xf6, 0x5a, 0x6c, 0x46, 0xd2, 0xec, 0x32, 0x2d, + 0x95, 0xc2, 0x10, 0x5c, 0x0c, 0x3c, 0xcd, 0xbd, 0x81, 0xe4, 0xc5, 0xb9, + 0xac, 0xaf, 0x0c, 0x1e, 0xea, 0x28, 0xfe, 0xfb, 0x28, 0xe4, 0x9e, 0x94, + 0x9a, 0xb4, 0x11, 0x22, 0x75, 0xbd, 0x74, 0x3e, 0xbe, 0x35, 0xa0, 0x16, + 0xa4, 0x72, 0x03, 0x80, 0x42, 0x02, 0xae, 0x5c, 0x18, 0x6c, 0xb4, 0x1f, + 0xb7, 0x30, 0x09, 0xda, 0x4e, 0x07, 0xb5, 0x34, 0xad, 0x53, 0x3a, 0xa2, + 0xfb, 0xb0, 0x37, 0xa9, 0x76, 0x6f, 0xd8, 0x5b, 0x26, 0xb0, 0x8a, 0xf3, + 0xc9, 0x45, 0x35, 0x42, 0xc5, 0x4b, 0x29, 0x04, 0xa6, 0x7b, 0x84, 0x32, + 0xc2, 0xd9, 0x5f, 0x86, 0x4d, 0xbd, 0x1f, 0x1a, 0xbf, 0x28, 0x7d, 0xba, + 0xc0, 0x41, 0x59, 0x61, 0x60, 0x51, 0xd5, 0x24, 0x45, 0xdd, 0xed, 0xaa, + 0xb4, 0xf7, 0x01, 0xa5, 0x01, 0x07, 0x1a, 0xe0, 0x8f, 0x04, 0xd0, 0xf0, + 0xd1, 0x7d, 0x39, 0xde, 0xaa, 0x2f, 0x87, 0x0d, 0xce, 0x69, 0x23, 0xcc, + 0x12, 0x4f, 0x01, 0x3b, 0x1d, 0xd2, 0xcd, 0x62, 0xee, 0xc5, 0xc6, 0x4f, + 0xd1, 0xae, 0x27, 0x16, 0x51, 0x2f, 0xc7, 0x50, 0x55, 0x3d, 0xb0, 0x30, + 0x6a, 0x35, 0x86, 0xd2, 0x97, 0x4b, 0xc2, 0x79, 0x50, 0xe1, 0x48, 0xa9, + 0xc2, 0x2b, 0x52, 0x8a, 0xa1, 0x0b, 0x4b, 0x77, 0x43, 0xf8, 0x25, 0xb0, + 0x3c, 0x31, 0x6c, 0x89, 0x62, 0x4a, 0xf9, 0x1f, 0xbc, 0x19, 0x55, 0xa4, + 0x9e, 0x57, 0x4f, 0x08, 0x99, 0x2b, 0x1f, 0xaa, 0x06, 0x17, 0xe4, 0x96, + 0x90, 0x55, 0x0d, 0xd9, 0xee, 0xee, 0xdb, 0x20, 0xe1, 0x9d, 0x6c, 0xbf, + 0xfa, 0x1b, 0xe8, 0xd0, 0x67, 0xb9, 0x56, 0xc5, 0x56, 0x78, 0x19, 0xd7, + 0x48, 0x95, 0x1a, 0x9c, 0x26, 0x2d, 0xe5, 0x4c, 0x01, 0x2f, 0x5e, 0x99, + 0x1a, 0xdd, 0x64, 0xe4, 0x0c, 0x62, 0x72, 0xda, 0x8c, 0x11, 0x54, 0xd5, + 0xdc, 0xc9, 0x3d, 0xaf, 0xf0, 0xb5, 0xb3, 0x7f, 0xb5, 0x36, 0xb8, 0x85, + 0x26, 0xf4, 0xd1, 0x76, 0xd4, 0xd4, 0x55, 0x6b, 0xa7, 0x2f, 0x27, 0xf8, + 0x2d, 0xe3, 0xdd, 0xae, 0xd9, 0x5d, 0x67, 0xeb, 0x95, 0xfb, 0x66, 0x87, + 0xf3, 0xe7, 0x05, 0x03, 0x6e, 0x7f, 0x7f, 0xde, 0x89, 0x0d, 0xcd, 0x32, + 0x04, 0xa3, 0xcc, 0xae, 0xc0, 0x46, 0x2f, 0x55, 0x78, 0x8c, 0x4c, 0x37, + 0x42, 0xc7, 0x6c, 0x65, 0xe2, 0x4c, 0xb5, 0x7e, 0x50, 0x2b, 0xc5, 0xf6, + 0x9c, 0x07, 0x42, 0xf3, 0x49, 0x03, 0x4b, 0x96, 0x21, 0xbe, 0xea, 0xa5, + 0x1a, 0x56, 0x12, 0xff, 0xb8, 0xcb, 0x09, 0x48, 0x4b, 0xad, 0x87, 0x82, + 0x13, 0x7d, 0x5d, 0xd0, 0x0f, 0xea, 0xb3, 0xc7, 0x2d, 0xbb, 0x4c, 0x05, + 0x04, 0xc5, 0x5b, 0x2b, 0x7c, 0x38, 0xeb, 0x44, 0x11, 0xd2, 0xcf, 0xb2, + 0xc7, 0x8a, 0x62, 0x02, 0x52, 0xc4, 0xb3, 0x1b, 0xcf, 0x58, 0xaa, 0xa0, + 0x26, 0xd6, 0x20, 0xba, 0xb0, 0xc4, 0xad, 0x9d, 0xa5, 0x5b, 0x03, 0x0e, + 0x7d, 0x82, 0xa3, 0x5c, 0x21, 0x34, 0x92, 0x60, 0xb3, 0xa7, 0x14, 0xe7, + 0x07, 0x52, 0xfd, 0x06, 0x53, 0x99, 0xa5, 0xdb, 0xba, 0xd0, 0x4d, 0x0c, + 0x8f, 0x1d, 0x47, 0xd9, 0xf8, 0x01, 0x0f, 0x76, 0xd9, 0xd4, 0x1a, 0x77, + 0xdc, 0x95, 0x53, 0x71, 0xbb, 0x82, 0x91, 0x06, 0xf3, 0xbe, 0x60, 0x60, + 0xb2, 0xb8, 0x84, 0x96, 0xc8, 0x2d, 0x44, 0xb1, 0x85, 0x19, 0xde, 0x27, + 0x77, 0xb0, 0xdb, 0x0e, 0x25, 0x47, 0xab, 0x0c, 0x72, 0x6d, 0x62, 0xff, + 0xdf, 0x77, 0xee, 0xab, 0x05, 0xca, 0x17, 0xe1, 0xe6, 0xe3, 0xb8, 0x47, + 0x19, 0x1b, 0x65, 0x2c, 0x0e, 0x66, 0x15, 0xac, 0x10, 0x53, 0xd4, 0xde, + 0x4c, 0x7f, 0x3f, 0xe0, 0x1d, 0x1c, 0x58, 0x3b, 0xfe, 0xf6, 0xdf, 0x38, + 0xb5, 0x57, 0x11, 0xab, 0x88, 0x81, 0x85, 0x29, 0x96, 0x99, 0xa9, 0x46, + 0x81, 0xf1, 0x33, 0xcb, 0x09, 0x01, 0x1b, 0xae, 0x4a, 0x78, 0x9b, 0xe0, + 0x9e, 0x95, 0x32, 0xcd, 0x5f, 0xda, 0xbe, 0x08, 0xcb, 0xba, 0xc6, 0x37, + 0xb8, 0xc6, 0x2d, 0x09, 0xee, 0x1b, 0x83, 0xa3, 0xad, 0xbd, 0xf1, 0x65, + 0xec, 0xd1, 0x8e, 0x69, 0xb7, 0x9a, 0x8f, 0x6a, 0xb6, 0xce, 0x91, 0x78, + 0x8c, 0x57, 0xb7, 0x7e, 0xae, 0x4d, 0x34, 0x52, 0xda, 0xdd, 0x80, 0xa4, + 0x26, 0x46, 0x4d, 0x3f, 0xb8, 0xab, 0x2b, 0xfc, 0x16, 0x96, 0x18, 0x9e, + 0x64, 0x5a, 0x5c, 0xc7, 0x3d, 0x3c, 0x65, 0x20, 0x57, 0x05, 0x86, 0xab, + 0x07, 0x14, 0x52, 0xcf, 0x6d, 0xe2, 0x47, 0x27, 0x93, 0x9b, 0xc8, 0x70, + 0x03, 0x0b, 0x1c, 0x71, 0x60, 0xb8, 0xe0, 0x97, 0x06, 0x69, 0x21, 0x1e, + 0x23, 0xe7, 0xb0, 0x8b, 0xf2, 0x16, 0x70, 0x72, 0xe1, 0x24, 0x53, 0x44, + 0x63, 0x22, 0x11, 0xa6, 0xe0, 0x78, 0x07, 0xb6, 0xa6, 0x20, 0x29, 0x0d, + 0xce, 0xc4, 0xf6, 0x59, 0x80, 0x27, 0xbe, 0x8b, 0x57, 0xe6, 0x15, 0xb6, + 0x86, 0x57, 0xb8, 0x53, 0xff, 0xda, 0xe5, 0xe6, 0x4f, 0x17, 0x3e, 0x4c, + 0xe6, 0xbd, 0x3c, 0xd3, 0x9d, 0xed, 0x2f, 0x32, 0x2c, 0x62, 0x83, 0x12, + 0x55, 0x2f, 0xd3, 0x5d, 0x43, 0xe9, 0xe6, 0x31, 0x32, 0xb3, 0x99, 0x6a, + 0xe4, 0x88, 0xc9, 0xaa, 0x62, 0x34, 0x8e, 0x0f, 0x47, 0xf9, 0xb8, 0x0e, + 0x10, 0x85, 0x92, 0x01, 0x61, 0xfa, 0xde, 0x71, 0x92, 0x65, 0x30, 0x3d, + 0x92, 0xe7, 0xa7, 0xfb, 0x4d, 0x2b, 0x5b, 0x18, 0x30, 0xc8, 0xe0, 0x4d, + 0x39, 0x2b, 0xec, 0xe8, 0x89, 0x60, 0xc6, 0xfa, 0xd5, 0xda, 0xa2, 0x85, + 0xd1, 0xe5, 0x54, 0x5a, 0x8d, 0xaa, 0x6b, 0xdc, 0xd3, 0xc4, 0xa1, 0xa4, + 0x39, 0x64, 0xa2, 0x56, 0x98, 0xdf, 0x97, 0xa5, 0x68, 0xe6, 0xa3, 0xdb, + 0x89, 0x12, 0xaa, 0x1e, 0xaa, 0x5e, 0x74, 0xe9, 0xf1, 0xa7, 0x4a, 0xe6, + 0x13, 0x51, 0x9f, 0xe5, 0x4b, 0x2f, 0xe9, 0xe0, 0x68, 0xbd, 0xdd, 0xa3, + 0x6c, 0x2d, 0x45, 0xf2, 0x46, 0xaa, 0xe0, 0x3e, 0x2c, 0xaa, 0xba, 0x3a, + 0xd9, 0x44, 0x0c, 0xd0, 0x12, 0x11, 0x7f, 0x9b, 0x9b, 0x5b, 0x50, 0xa1, + 0x0c, 0xaa, 0x46, 0x7a, 0xcc, 0x0d, 0x1b, 0xeb, 0xfe, 0xe6, 0xc5, 0x53, + 0x16, 0xa2, 0xa7, 0x47, 0x04, 0x04, 0x2e, 0x08, 0x33, 0x6b, 0xa7, 0xbc, + 0x04, 0x9e, 0xc4, 0xd0, 0x36, 0x67, 0x20, 0x48, 0xd1, 0x30, 0x5c, 0x53, + 0xe3, 0x55, 0xcf, 0x13, 0x79, 0x6d, 0xf2, 0xe9, 0x8c, 0xe5, 0x95, 0xa7, + 0x81, 0x50, 0x58, 0x28, 0x09, 0x97, 0xbd, 0xa8, 0x21, 0x66, 0x91, 0x69, + 0x01, 0xf0, 0x79, 0x81, 0x67, 0xde, 0x96, 0x0c, 0x54, 0x32, 0x32, 0xb1, + 0x9b, 0x77, 0xfa, 0x5b, 0xfd, 0x78, 0x10, 0x7e, 0xdc, 0xae, 0x4d, 0xc0, + 0x18, 0x6a, 0xda, 0xe2, 0xc8, 0x2a, 0xb4, 0x90, 0xc2, 0x71, 0x4e, 0xb7, + 0x8e, 0x49, 0xbb, 0x06, 0x7d, 0x4e, 0x3e, 0x9b, 0xe0, 0xb6, 0xc5, 0x42, + 0x48, 0xf0, 0xc4, 0xd4, 0x7f, 0xbc, 0x9e, 0xd6, 0x4e, 0x7f, 0x06, 0xa8, + 0x03, 0xec, 0xfd, 0xbb, 0x1a, 0x96, 0x44, 0xc5, 0xaf, 0xe9, 0x0e, 0x6c, + 0x29, 0xf5, 0xed, 0x89, 0x79, 0xd6, 0x69, 0x7b, 0xc7, 0x5d, 0xff, 0x3a, + 0x0b, 0x0c, 0x44, 0x72, 0xb6, 0x5a, 0xbc, 0xc0, 0x5b, 0xe8, 0x55, 0x7d, + 0xc9, 0x3d, 0x4c, 0x4a, 0xd1, 0x44, 0xe5, 0xd1, 0x97, 0x0d, 0x44, 0x08, + 0x8d, 0xb9, 0xc3, 0x8c, 0xaa, 0xb2, 0xb7, 0xda, 0x17, 0x56, 0xcc, 0x84, + 0x13, 0xe5, 0x4d, 0x02, 0x43, 0xfd, 0xf9, 0x63, 0x67, 0x3f, 0x63, 0x07, + 0x09, 0x95, 0xb7, 0x43, 0x00, 0x54, 0xd2, 0x9f, 0xb8, 0x98, 0xc0, 0x3c, + 0x13, 0xcb, 0xf6, 0x7f, 0xe5, 0x2d, 0xa3, 0x3c, 0x62, 0xac, 0xeb, 0xb4, + 0x70, 0x3b, 0x20, 0x44, 0xf8, 0x4d, 0x2f, 0x58, 0xe5, 0xd0, 0xb1, 0x01, + 0x53, 0x29, 0xba, 0x11, 0x30, 0x97, 0x2f, 0xd0, 0x44, 0x3c, 0x42, 0x71, + 0xbe, 0x10, 0x21, 0x8c, 0x9f, 0x21, 0x26, 0xcf, 0xf9, 0x24, 0x84, 0x4b, + 0x03, 0x2d, 0x67, 0x8a, 0xf3, 0xc9, 0x96, 0x29, 0x76, 0x3e, 0x8b, 0x46, + 0x93, 0x74, 0x8d, 0xd9, 0xa4, 0x47, 0x4b, 0x3a, 0x20, 0x00, 0x46, 0xd5, + 0x98, 0xa8, 0x01, 0x0a, 0x26, 0xe7, 0x15, 0x3d, 0x62, 0xee, 0xf9, 0xd3, + 0x86, 0x95, 0xd4, 0x72, 0x2f, 0x98, 0x11, 0x43, 0xac, 0xe7, 0xe9, 0x97, + 0x2a, 0x8b, 0xae, 0xac, 0xa8, 0xe9, 0xe7, 0x06, 0xf3, 0x6f, 0xaa, 0x12, + 0x67, 0xca, 0x5e, 0x6c, 0xb1, 0xbb, 0xfb, 0x8d, 0xa7, 0x8d, 0xbd, 0x9d, + 0x5a, 0x25, 0x27, 0x80, 0xe3, 0xd7, 0x7f, 0xec, 0x8e, 0x1d, 0x57, 0xa9, + 0x21, 0x0d, 0xd5, 0x9e, 0x10, 0xfe, 0xb3, 0x2b, 0x81, 0x3e, 0x4b, 0x36, + 0x8a, 0x19, 0x56, 0xb6, 0x8f, 0x34, 0xb7, 0xbe, 0x66, 0xf0, 0x99, 0x3e, + 0xeb, 0x20, 0x7d, 0x58, 0x2b, 0x56, 0x5b, 0xd3, 0x40, 0x32, 0x8a, 0x93, + 0xd3, 0x21, 0xc7, 0x14, 0x10, 0x5d, 0xfc, 0xa5, 0xab, 0xe0, 0xcb, 0xc5, + 0x9c, 0x29, 0xee, 0x01, 0x19, 0x90, 0x9d, 0x95, 0x60, 0xa9, 0x01, 0xac, + 0xa5, 0x47, 0x71, 0x7b, 0x2e, 0x62, 0x3b, 0x85, 0x60, 0x53, 0x11, 0x47, + 0xfb, 0x64, 0xfd, 0x77, 0xf1, 0xb9, 0xae, 0x10, 0x41, 0x7c, 0xf6, 0x5e, + 0x78, 0xb6, 0xaa, 0xec, 0xf1, 0xf4, 0xbe, 0x68, 0x1d, 0x1a, 0x8d, 0x1e, + 0x09, 0xc2, 0x35, 0x07, 0x2f, 0x43, 0x98, 0x96, 0xde, 0x53, 0xb1, 0xdf, + 0x09, 0xe8, 0x32, 0x35, 0x2e, 0x02, 0x47, 0xe0, 0x92, 0x1c, 0xe6, 0x0f, + 0xdb, 0xa9, 0x0a, 0x76, 0xab, 0x2d, 0x9b, 0x71, 0x48, 0x44, 0x7d, 0xed, + 0x76, 0x8c, 0x15, 0x60, 0xaa, 0xef, 0x1f, 0x0d, 0xb9, 0xd2, 0xfd, 0x1c, + 0xcc, 0x20, 0x59, 0xb7, 0x39, 0xe2, 0x77, 0x46, 0x4d, 0x78, 0x53, 0x15, + 0x4a, 0x44, 0x19, 0x7d, 0x71, 0x13, 0x54, 0xd4, 0x62, 0x69, 0x02, 0xcf, + 0xd9, 0x49, 0x8c, 0xbd, 0xe8, 0x4e, 0x0a, 0x29, 0xa7, 0x2e, 0x8f, 0xf7, + 0xf3, 0x18, 0x5c, 0x6b, 0x30, 0x9c, 0x29, 0x0a, 0x20, 0x87, 0x88, 0x8d, + 0x0a, 0x5e, 0x5c, 0x67, 0x5c, 0xd8, 0x0d, 0x49, 0xc6, 0xd7, 0x15, 0x78, + 0x10, 0x78, 0xa1, 0x3b, 0xbb, 0xa4, 0xb2, 0x39, 0x4d, 0xac, 0xf8, 0xb7, + 0x51, 0xc1, 0xd9, 0x7d, 0x4a, 0xb1, 0x2d, 0x95, 0x1b, 0xe2, 0xde, 0xbe, + 0xde, 0xb9, 0x11, 0x4e, 0x66, 0x98, 0x02, 0xcc, 0xd7, 0xfb, 0x0f, 0xab, + 0x61, 0x1b, 0x73, 0x61, 0xdc, 0x7c, 0x51, 0x9a, 0x80, 0x57, 0x1e, 0xab, + 0x04, 0x3c, 0x36, 0x15, 0x87, 0x21, 0x0c, 0xca, 0x1f, 0xa6, 0x70, 0x94, + 0x79, 0x30, 0x8d, 0x33, 0xb7, 0x3b, 0x4d, 0x10, 0x72, 0xa9, 0x50, 0x2b, + 0x7a, 0xaf, 0xca, 0x3f, 0xfd, 0xb8, 0x0b, 0x7c, 0x6c, 0x56, 0xd3, 0x21, + 0x5e, 0x71, 0xb1, 0x6b, 0x90, 0xe0, 0x4f, 0x4f, 0xd1, 0x5a, 0x6c, 0x95, + 0xbd, 0xaf, 0xc8, 0xa9, 0xa4, 0xe6, 0x3c, 0x19, 0x92, 0xea, 0xe0, 0xc1, + 0xf1, 0x53, 0xbe, 0x25, 0xa6, 0x05, 0xf6, 0x4a, 0x77, 0xc0, 0x11, 0x5e, + 0x82, 0x01, 0xb4, 0xac, 0xed, 0xb6, 0x8a, 0x78, 0x40, 0x63, 0x57, 0x49, + 0xad, 0x67, 0x60, 0x10, 0xe5, 0xe2, 0x57, 0xea, 0xd8, 0x99, 0x44, 0x41, + 0xd2, 0xef, 0x7c, 0xf7, 0xe6, 0x42, 0x21, 0x98, 0xc5, 0xe8, 0x0b, 0xf4, + 0x59, 0x4d, 0x2f, 0x05, 0x0e, 0x9d, 0x6c, 0x75, 0xb0, 0xea, 0xdb, 0x67, + 0x39, 0x2a, 0x5d, 0x30, 0xf9, 0x08, 0x47, 0x78, 0x9f, 0xea, 0x2f, 0xb2, + 0x67, 0x5d, 0xbc, 0xfb, 0x9b, 0xcb, 0xf1, 0x95, 0x03, 0xa6, 0x33, 0x85, + 0x0d, 0x44, 0xbd, 0x6c, 0x5c, 0x88, 0x17, 0x70, 0x53, 0x97, 0xe9, 0xc5, + 0x62, 0x45, 0x4f, 0xa7, 0x10, 0x18, 0xb3, 0xcc, 0x51, 0x02, 0x77, 0x36, + 0x4a, 0xd7, 0x55, 0x6d, 0x9b, 0x26, 0xbc, 0x1c, 0xda, 0x2a, 0x60, 0x88, + 0x59, 0xe6, 0x5d, 0x42, 0xea, 0xdc, 0x05, 0xde, 0x77, 0xe3, 0xd5, 0x4f, + 0xc8, 0x75, 0xf0, 0x11, 0xef, 0x97, 0x1e, 0xb6, 0x9b, 0x0c, 0x89, 0x9d, + 0x9a, 0x2b, 0x47, 0xa4, 0x4e, 0x4a, 0xe3, 0x9b, 0xec, 0xd3, 0x70, 0x84, + 0x73, 0x0d, 0x80, 0xb5, 0x06, 0x06, 0xb4, 0x45, 0x40, 0x9d, 0x38, 0x96, + 0xb8, 0xf1, 0x8a, 0x8b, 0xa6, 0xd3, 0x7a, 0x8f, 0x1d, 0x6a, 0xcf, 0xa6, + 0x6b, 0xc1, 0xe5, 0x7c, 0x01, 0x0f, 0x73, 0x99, 0x94, 0x24, 0xb1, 0x25, + 0x1f, 0x27, 0x22, 0xe0, 0xbd, 0xdc, 0x80, 0x9e, 0x1b, 0x0a, 0x07, 0x0e, + 0x51, 0x32, 0xaf, 0x39, 0x33, 0x2d, 0x10, 0xf5, 0xa4, 0x33, 0xda, 0x1f, + 0x2f, 0xa1, 0xd8, 0xea, 0x40, 0x32, 0x2d, 0x33, 0xde, 0xb5, 0x8d, 0x2a, + 0x29, 0x57, 0x6c, 0x4e, 0x47, 0x09, 0x06, 0x16, 0x83, 0x44, 0xcf, 0x37, + 0x7f, 0xe5, 0x47, 0x4f, 0x1c, 0x44, 0xea, 0x10, 0xc0, 0x99, 0x6d, 0x40, + 0xf5, 0x1d, 0x5a, 0x15, 0x80, 0x81, 0x7d, 0xcd, 0x94, 0x38, 0x56, 0xe1, + 0x29, 0x88, 0xc8, 0xc1, 0xf4, 0xd4, 0xa0, 0xa8, 0x01, 0xde, 0x84, 0x17, + 0x9b, 0x09, 0xc8, 0xb2, 0xe5, 0x93, 0x9a, 0x54, 0xc2, 0xe2, 0x52, 0x24, + 0x6f, 0x72, 0xd9, 0x31, 0xd8, 0x29, 0x3d, 0x2c, 0xd5, 0x23, 0xc2, 0xd6, + 0xb9, 0x82, 0xa8, 0x86, 0xa1, 0x3a, 0x60, 0x84, 0x31, 0x88, 0xca, 0x3f, + 0x22, 0x7c, 0x3d, 0x40, 0x05, 0xaa, 0x1e, 0xca, 0x66, 0x54, 0xdb, 0x62, + 0xcb, 0x7d, 0x77, 0x47, 0xb4, 0x53, 0x71, 0xbc, 0x11, 0x08, 0x2c, 0x15, + 0xc0, 0xaa, 0x27, 0xf1, 0x88, 0xc3, 0x25, 0x64, 0xc7, 0x2b, 0xf6, 0x25, + 0x50, 0x07, 0x9e, 0xe4, 0xf8, 0x31, 0xbd, 0xe5, 0xe8, 0x68, 0x30, 0xc6, + 0xc4, 0x65, 0xa0, 0x61, 0xac, 0x3c, 0x38, 0x5e, 0x0e, 0x94, 0xb8, 0x0b, + 0xd4, 0x38, 0xaa, 0xdd, 0xc8, 0xf4, 0x89, 0x08, 0xf7, 0xa7, 0x9f, 0x41, + 0x24, 0xa5, 0x09, 0x33, 0x9a, 0xbc, 0xac, 0xb3, 0xee, 0xfc, 0xbd, 0x0d, + 0x35, 0x91, 0xa2, 0xe0, 0xef, 0x4b, 0x09, 0x7a, 0x67, 0x2e, 0xd4, 0x1b, + 0x2e, 0x29, 0xdc, 0x46, 0xbb, 0xb7, 0xf7, 0x4a, 0xb6, 0x8e, 0x7e, 0xf3, + 0x46, 0x7f, 0xd8, 0x2d, 0x20, 0x75, 0xb7, 0xbf, 0xbd, 0x26, 0x58, 0xfb, + 0xa1, 0x92, 0xa8, 0x74, 0x3a, 0x1c, 0xe7, 0x19, 0x48, 0xf8, 0x9f, 0xde, + 0x46, 0x62, 0xdc, 0x22, 0x33, 0x66, 0xc5, 0x44, 0x93, 0x18, 0x3d, 0xed, + 0x92, 0x01, 0xa4, 0x9c, 0x62, 0xb9, 0xa3, 0xe4, 0x47, 0x78, 0xf6, 0x43, + 0x68, 0xf8, 0x57, 0x5d, 0x46, 0xcd, 0xb6, 0x76, 0x7d, 0x2f, 0x26, 0xf1, + 0x48, 0x84, 0x47, 0x4f, 0x8a, 0x9b, 0x9c, 0xef, 0x72, 0x88, 0xad, 0x7a, + 0x17, 0xc6, 0xeb, 0x31, 0x05, 0x92, 0x9e, 0x06, 0xe3, 0x63, 0x20, 0x01, + 0xcd, 0xe4, 0xb9, 0xb1, 0xc5, 0x82, 0x44, 0x73, 0x60, 0xa1, 0x91, 0xf5, + 0xed, 0xb1, 0x1f, 0x74, 0xae, 0x48, 0xe5, 0x9e, 0x1a, 0x7d, 0xd7, 0x90, + 0x77, 0x55, 0x41, 0x7e, 0xc1, 0x7e, 0xb5, 0x12, 0x35, 0xc4, 0xfb, 0x7b, + 0xa6, 0x09, 0x65, 0x57, 0x4c, 0x3e, 0xec, 0x4c, 0x5c, 0x12, 0x2a, 0x36, + 0xad, 0x23, 0x14, 0x4b, 0x13, 0x0d, 0xbb, 0x72, 0xed, 0x2c, 0x7b, 0xa0, + 0x1a, 0x9f, 0x94, 0xcc, 0xc9, 0x0b, 0x1d, 0x43, 0xfc, 0x20, 0x0a, 0xaa, + 0x79, 0x8a, 0x83, 0x52, 0x6f, 0x1e, 0xf4, 0xe5, 0xf1, 0x69, 0x4c, 0xc6, + 0x1b, 0x2c, 0xa2, 0xd6, 0xd6, 0xa0, 0x21, 0x22, 0x7e, 0x13, 0xa7, 0x26, + 0x7d, 0x7e, 0x88, 0x39, 0x0b, 0x42, 0xa5, 0x30, 0x58, 0xa4, 0xa2, 0xf4, + 0x4d, 0x29, 0x86, 0xda, 0xf6, 0xd0, 0x53, 0x3d, 0xf9, 0x37, 0x4e, 0x9f, + 0xa4, 0xa5, 0xdc, 0xa1, 0x82, 0x3d, 0x85, 0x2c, 0x36, 0x55, 0x0d, 0xd2, + 0x25, 0xac, 0x72, 0xa3, 0x38, 0x76, 0xa7, 0xfe, 0xb4, 0x66, 0xb6, 0x5c, + 0xeb, 0xc1, 0x8f, 0x4d, 0x2c, 0x2d, 0xc5, 0xf2, 0x9c, 0xa2, 0x8d, 0xd4, + 0x9a, 0x3b, 0x22, 0xcd, 0x0b, 0x62, 0x32, 0x02, 0xa8, 0x5c, 0xa6, 0x94, + 0x13, 0xcf, 0xcf, 0x18, 0x6c, 0xef, 0x4e, 0x83, 0x09, 0xad, 0xd6, 0x29, + 0x00, 0xfc, 0x0a, 0xfa, 0xe4, 0x4d, 0x46, 0x1e, 0x6b, 0xd0, 0x7e, 0x7b, + 0xf1, 0x86, 0x45, 0x58, 0x77, 0xd8, 0x51, 0xff, 0xa5, 0xf3, 0xe0, 0x5f, + 0xe2, 0x08, 0x8b, 0xe2, 0x2d, 0x60, 0xde, 0xb5, 0x78, 0xf0, 0x7d, 0x59, + 0x70, 0xaf, 0x95, 0x6f, 0x03, 0x79, 0xa0, 0x76, 0x65, 0x39, 0xb2, 0x16, + 0xad, 0x38, 0xe5, 0xef, 0xa7, 0x83, 0x5b, 0x58, 0xa8, 0x8b, 0x2b, 0x0d, + 0xaf, 0xae, 0x8d, 0xf6, 0x18, 0xe9, 0xc5, 0x0a, 0xf0, 0xfd, 0x6d, 0x0e, + 0x6b, 0xd8, 0x62, 0x09, 0x20, 0xda, 0xe5, 0x45, 0xbd, 0x51, 0x5e, 0x9e, + 0xad, 0xbc, 0x5a, 0xe9, 0x5b, 0x8f, 0xf0, 0x58, 0xb9, 0xb1, 0x86, 0xed, + 0x6e, 0xbf, 0xc2, 0x14, 0x55, 0xdc, 0x50, 0x3f, 0xa3, 0xcb, 0xb0, 0x09, + 0xd9, 0x85, 0x65, 0x0a, 0x68, 0x28, 0x41, 0xa4, 0xb3, 0xcb, 0x6c, 0x58, + 0xb4, 0x6d, 0x57, 0x11, 0xed, 0xda, 0x77, 0x68, 0x18, 0x27, 0x61, 0x7e, + 0x1c, 0x8b, 0xc5, 0x99, 0x77, 0xbe, 0x88, 0x40, 0x89, 0xbc, 0xd3, 0x0e, + 0xa0, 0xd0, 0x1c, 0xcb, 0x75, 0x03, 0x91, 0xaa, 0x47, 0xfd, 0x7f, 0x32, + 0x34, 0xb0, 0xe7, 0xc3, 0xda, 0x2b, 0x06, 0xa9, 0x05, 0x51, 0xa4, 0xfe, + 0xef, 0x42, 0xc1, 0xde, 0x05, 0xea, 0xe2, 0x45, 0x62, 0x36, 0xa3, 0x9e, + 0x36, 0xce, 0xbb, 0xab, 0x48, 0x9c, 0x40, 0x60, 0xee, 0x35, 0x45, 0x7e, + 0x24, 0xe5, 0x16, 0xac, 0xfe, 0x21, 0xe5, 0x45, 0x7d, 0xc0, 0xbd, 0x2d, + 0x1c, 0xf2, 0x29, 0xd8, 0x7d, 0xfa, 0x25, 0x32, 0xe0, 0x63, 0xa2, 0xc9, + 0x02, 0xba, 0x22, 0x18, 0x6e, 0x63, 0xa9, 0xa3, 0x84, 0xc5, 0xab, 0xbb, + 0xf9, 0xa3, 0x8a, 0xef, 0x7e, 0x86, 0x1e, 0x3f, 0x0a, 0x9e, 0x25, 0x20, + 0x14, 0x77, 0xad, 0xae, 0xb1, 0xf9, 0x38, 0x18, 0x99, 0x81, 0xb5, 0x73, + 0xf8, 0x2a, 0x7e, 0x23, 0x76, 0x2b, 0x70, 0xc2, 0x8c, 0x2a, 0x4b, 0x5b, + 0x7f, 0x18, 0x1a, 0xd6, 0xf1, 0x10, 0xa5, 0xf7, 0xf2, 0x8e, 0x84, 0x83, + 0x1b, 0x56, 0x24, 0x5f, 0x11, 0xe9, 0x9a, 0xa8, 0xfc, 0x04, 0xb4, 0x41, + 0xe6, 0x18, 0xfd, 0xf9, 0xd4, 0x14, 0x93, 0x46, 0xe5, 0x43, 0xb1, 0xad, + 0xb6, 0x17, 0x32, 0x95, 0xe5, 0x31, 0xbc, 0xf7, 0xe9, 0x1f, 0x80, 0x52, + 0x6c, 0x49, 0x31, 0xa4, 0x75, 0x0f, 0xca, 0xa8, 0xd0, 0x1c, 0xce, 0xba, + 0x6b, 0x27, 0x82, 0x28, 0x4a, 0x19, 0x4c, 0x9a, 0xcc, 0x97, 0x51, 0xd5, + 0x2b, 0xab, 0xe4, 0x61, 0xf9, 0x32, 0xc6, 0xe0, 0xfc, 0xd1, 0xc8, 0x1f, + 0x5c, 0x16, 0x1f, 0x05, 0x2f, 0xb9, 0x05, 0x4a, 0x34, 0x05, 0x20, 0x8e, + 0x95, 0x0f, 0xd5, 0x07, 0x5b, 0x38, 0x35, 0xc0, 0x12, 0xc2, 0x95, 0x10, + 0xe6, 0xeb, 0x4e, 0x4b, 0x3a, 0x11, 0x38, 0x10, 0x08, 0xac, 0x4e, 0xd9, + 0x7f, 0xf7, 0x41, 0xa2, 0x0f, 0x98, 0x78, 0x2c, 0xf7, 0x0b, 0xdc, 0x5f, + 0x80, 0xb3, 0xf9, 0xd9, 0xf4, 0x1d, 0x78, 0xf6, 0x4f, 0x50, 0xfb, 0x5f, + 0x4a, 0xf5, 0xbb, 0xb0, 0xa5, 0x56, 0xbb, 0x6f, 0xfc, 0x3d, 0x50, 0x53, + 0x2e, 0x60, 0xd0, 0x42, 0x47, 0xd5, 0xb6, 0x4f, 0x39, 0x5d, 0xcd, 0x6b, + 0x3e, 0x1e, 0x36, 0x13, 0xf2, 0xea, 0x35, 0xe9, 0x83, 0xa9, 0xe2, 0x43, + 0x7d, 0xd5, 0x9f, 0xa0, 0x0f, 0x92, 0xb4, 0xe6, 0x70, 0x35, 0x39, 0xe7, + 0x07, 0x9c, 0x0e, 0xa4, 0x5b, 0xc3, 0x33, 0xe9, 0x45, 0x4d, 0x19, 0xa8, + 0x9b, 0xda, 0xa6, 0xdf, 0xf3, 0x91, 0x4f, 0x0f, 0x07, 0x92, 0x04, 0x10, + 0x96, 0xaf, 0x0a, 0x77, 0x56, 0x75, 0xa3, 0x0b, 0x73, 0x4e, 0x1d, 0xc7, + 0x22, 0x48, 0xec, 0x2e, 0xde, 0x36, 0x3b, 0x57, 0x2c, 0x68, 0xa6, 0xc0, + 0x98, 0xa0, 0xa5, 0x1d, 0x39, 0x39, 0x07, 0x50, 0x14, 0x93, 0x04, 0x29, + 0x33, 0xc7, 0x39, 0x4a, 0x6f, 0x13, 0x0d, 0xfb, 0x11, 0x51, 0x0e, 0x0a, + 0xa0, 0xc8, 0xa6, 0x75, 0x9a, 0x2c, 0x0c, 0xbd, 0x66, 0x75, 0xf3, 0x94, + 0xbe, 0xdd, 0xc7, 0xa5, 0xcf, 0xda, 0xaa, 0x65, 0xa8, 0x77, 0xf3, 0xb6, + 0xb8, 0x9d, 0x01, 0x25, 0xc4, 0x87, 0xf6, 0xca, 0x8b, 0x1d, 0x15, 0xb9, + 0x8b, 0xcd, 0x16, 0x80, 0x5f, 0xdd, 0xdc, 0xad, 0x3c, 0x0a, 0x45, 0xb6, + 0xac, 0xdb, 0xc4, 0xba, 0x9f, 0x59, 0x47, 0x55, 0xa1, 0xe4, 0x55, 0x7b, + 0xbf, 0x5a, 0x6a, 0xef, 0x01, 0x1b, 0x69, 0xb8, 0x19, 0x45, 0xda, 0xe9, + 0x95, 0x4a, 0x2e, 0x62, 0x82, 0x7a, 0x63, 0xea, 0x0b, 0x14, 0xc6, 0x0b, + 0xc2, 0x78, 0xce, 0x73, 0x50, 0x4b, 0xd8, 0xee, 0xcc, 0xef, 0xfc, 0x28, + 0xb4, 0x24, 0x7f, 0x36, 0xf5, 0x9c, 0x8c, 0x41, 0x23, 0x7d, 0x10, 0x87, + 0xdd, 0xf1, 0x75, 0xe8, 0x3e, 0x7d, 0x58, 0x23, 0x8e, 0xb6, 0x42, 0x46, + 0xc7, 0x23, 0xfb, 0xee, 0x85, 0xaf, 0x72, 0xbd, 0x62, 0xb3, 0xaa, 0x8e, + 0x44, 0x0e, 0x4a, 0xab, 0x5a, 0x9b, 0x41, 0xc8, 0x67, 0x90, 0x4b, 0xf8, + 0xcf, 0x47, 0x3c, 0x3f, 0x76, 0x32, 0xf9, 0xc5, 0x47, 0x66, 0x7a, 0x93, + 0x1a, 0xeb, 0xe1, 0xe8, 0xf8, 0xb9, 0x9a, 0xa6, 0xa4, 0x11, 0x5d, 0xe5, + 0xec, 0x27, 0xa0, 0x5a, 0x52, 0x96, 0x70, 0xf1, 0x41, 0x11, 0xd1, 0x9f, + 0xe6, 0xbc, 0x3f, 0x2f, 0xfd, 0xa6, 0x5f, 0xd8, 0xd1, 0x36, 0x10, 0x80, + 0x98, 0x6f, 0x1f, 0x00, 0x7e, 0xf2, 0xb3, 0xd8, 0x65, 0x8e, 0xc8, 0x5d, + 0x81, 0xfb, 0xeb, 0x3d, 0xef, 0xea, 0xc1, 0x87, 0x0d, 0x68, 0x29, 0x79, + 0xdd, 0x4a, 0x73, 0x02, 0xcc, 0xe3, 0xed, 0xfe, 0x27, 0xfc, 0x3d, 0x9b, + 0x43, 0x18, 0x13, 0x35, 0xea, 0x18, 0xfe, 0x11, 0xde, 0x8b, 0xf6, 0x4a, + 0x08, 0xc3, 0xe4, 0x39, 0x7e, 0xee, 0x6e, 0xce, 0x41, 0x8b, 0x08, 0x57, + 0xdf, 0x6f, 0x7e, 0x7b, 0x4f, 0xee, 0x48, 0x95, 0x9d, 0x61, 0x95, 0xb0, + 0x8c, 0x2a, 0x55, 0xa3, 0x9e, 0xda, 0xdf, 0x60, 0x67, 0x8a, 0x87, 0xc0, + 0xa3, 0x05, 0xde, 0x79, 0xad, 0xeb, 0x19, 0x74, 0x1b, 0x77, 0x4c, 0x7d, + 0x94, 0x4d, 0xbc, 0x8f, 0x0e, 0x25, 0xe6, 0x2e, 0x9b, 0x14, 0x24, 0x52, + 0x4b, 0x62, 0xbb, 0xbc, 0x69, 0xd2, 0x65, 0x4e, 0x66, 0xdd, 0x3e, 0xe7, + 0x5e, 0xfd, 0xc6, 0xd6, 0x48, 0x56, 0xe8, 0x56, 0x32, 0x2d, 0x63, 0x8f, + 0xab, 0x87, 0x6f, 0x4c, 0x9a, 0x3a, 0xcc, 0x9d, 0xd6, 0x42, 0xec, 0x90, + 0x94, 0x69, 0x05, 0xa5, 0x98, 0x22, 0x4f, 0xb0, 0x14, 0xcf, 0xe2, 0x1e, + 0xe6, 0x24, 0x9f, 0x9d, 0x31, 0x36, 0x6d, 0xce, 0xfb, 0xab, 0xa4, 0x28, + 0xd9, 0x3a, 0x29, 0xad, 0x55, 0xdc, 0x60, 0xc3, 0x76, 0x0b, 0x9d, 0x38, + 0xfe, 0x45, 0xc0, 0xba, 0xb5, 0x1f, 0x78, 0xd1, 0x3b, 0xbf, 0x55, 0xbd, + 0x98, 0xd4, 0xf3, 0xdb, 0x63, 0x20, 0x19, 0x12, 0x8e, 0x11, 0xbb, 0xc6, + 0x63, 0x78, 0x00, 0x1b, 0x83, 0x2c, 0xaf, 0x86, 0xe1, 0x4d, 0x41, 0xb4, + 0x20, 0xf2, 0xc5, 0x44, 0x12, 0xbc, 0xa1, 0x69, 0x69, 0xe6, 0x60, 0x7b, + 0xf1, 0x23, 0xe2, 0x3b, 0x01, 0x61, 0xfe, 0x46, 0x36, 0x07, 0xcd, 0xd5, + 0x54, 0xb8, 0x62, 0xc8, 0x46, 0x81, 0x8c, 0xb7, 0x09, 0xaf, 0x45, 0xfb, + 0x78, 0x2a, 0xab, 0x96, 0x6d, 0xb3, 0x9f, 0x44, 0x4e, 0x92, 0x08, 0xca, + 0x48, 0x0b, 0xf0, 0xb5, 0x8a, 0xd9, 0xbf, 0xad, 0xa6, 0x8d, 0xef, 0x64, + 0x8a, 0xb2, 0x51, 0xb9, 0x18, 0x84, 0xa1, 0xb0, 0x40, 0x01, 0xf2, 0x93, + 0x9e, 0xaa, 0x37, 0xfc, 0xb0, 0x4a, 0x30, 0xe9, 0x3d, 0xbe, 0x92, 0xeb, + 0xf0, 0xc7, 0x95, 0x61, 0x30, 0x63, 0xb9, 0xa2, 0x7e, 0xdf, 0xa2, 0x64, + 0x20, 0xd6, 0xab, 0x3f, 0x68, 0x84, 0x0d, 0x8a, 0xbd, 0x20, 0x68, 0x17, + 0x96, 0x61, 0xd6, 0xdd, 0xa8, 0x4b, 0x33, 0x0c, 0x6b, 0x9a, 0xb7, 0x17, + 0x00, 0x30, 0x5f, 0xd1, 0xcb, 0xad, 0xe7, 0x90, 0x56, 0x63, 0x2e, 0xa6, + 0xd9, 0xf0, 0x4e, 0xbc, 0xd3, 0xe4, 0x53, 0xf1, 0x5c, 0xd1, 0x32, 0xb3, + 0x9a, 0x1c, 0xa7, 0xf7, 0xf9, 0xfb, 0xb5, 0xe5, 0x8b, 0xaf, 0x72, 0xca, + 0xb8, 0x05, 0x45, 0x63, 0xf7, 0x7c, 0x43, 0xd4, 0x26, 0x65, 0x54, 0x54, + 0x5e, 0x9e, 0x8b, 0x9d, 0x84, 0xb8, 0x13, 0xb1, 0xb7, 0xbf, 0xeb, 0xde, + 0x0c, 0x39, 0x79, 0x20, 0x7d, 0x2a, 0xbf, 0x97, 0xf0, 0x3a, 0x96, 0xb7, + 0xf3, 0x43, 0xb6, 0xf9, 0x14, 0xcb, 0x18, 0x96, 0xad, 0xae, 0x0f, 0x8b, + 0x14, 0x03, 0xad, 0x42, 0x0e, 0xee, 0x16, 0x24, 0x61, 0x20, 0xa2, 0x9d, + 0x8f, 0x0d, 0x11, 0x63, 0xb8, 0x5b, 0x10, 0x8f, 0x33, 0x51, 0x80, 0x02, + 0x93, 0x42, 0x30, 0x82, 0x40, 0xce, 0xc0, 0x03, 0x86, 0x42, 0x77, 0x32, + 0xa8, 0xf5, 0xb5, 0x8f, 0x4d, 0xbe, 0x2d, 0x1f, 0x4c, 0xc9, 0xbe, 0xa5, + 0xf0, 0xcc, 0x4e, 0x49, 0x9d, 0x27, 0xc3, 0xa9, 0x09, 0xfe, 0xff, 0xf9, + 0x51, 0xf4, 0xb4, 0x17, 0x71, 0xce, 0xee, 0xc3, 0x77, 0xf7, 0x72, 0x01, + 0x0f, 0xc8, 0xec, 0x5b, 0xd7, 0x31, 0xc7, 0xfb, 0xd7, 0xfb, 0xb5, 0x3b, + 0x06, 0xee, 0x33, 0xc6, 0x81, 0x63, 0x67, 0xed, 0x96, 0x47, 0x43, 0x9d, + 0xb3, 0x76, 0x23, 0x18, 0xfa, 0xc1, 0xbc, 0x99, 0xd4, 0xed, 0x97, 0xcd, + 0xbd, 0x0a, 0x2c, 0xd1, 0x99, 0xb6, 0xc2, 0x5d, 0x3d, 0x1d, 0xf2, 0xe6, + 0xe7, 0xc2, 0x31, 0xd7, 0xad, 0xdc, 0x5b, 0x7f, 0xbf, 0x50, 0xec, 0x70, + 0xb8, 0xb0, 0x51, 0xa2, 0xe9, 0x84, 0x8b, 0x2d, 0x1a, 0xa7, 0xdb, 0x05, + 0xf4, 0x2f, 0x5f, 0x83, 0x7a, 0x28, 0xbb, 0xbe, 0xdb, 0x6f, 0xf0, 0xea, + 0x75, 0x05, 0x50, 0x28, 0x5d, 0x24, 0x07, 0xcb, 0xa7, 0xe1, 0xeb, 0x6e, + 0x06, 0xe8, 0xb7, 0x42, 0x20, 0x8e, 0x23, 0xfe, 0x7b, 0x83, 0x1a, 0xb5, + 0x05, 0x09, 0x76, 0x7d, 0x91, 0x2b, 0x2f, 0x39, 0xdb, 0x47, 0x1d, 0xae, + 0xc9, 0xad, 0x2c, 0x89, 0xc8, 0x18, 0x47, 0x87, 0x95, 0xfb, 0x5d, 0xf5, + 0x5c, 0x95, 0x17, 0xc4, 0x87, 0x38, 0x1c, 0x8c, 0x85, 0x6e, 0xc2, 0x11, + 0xc6, 0x38, 0x97, 0xd3, 0x50, 0x32, 0x10, 0x0d, 0x1d, 0x9c, 0x40, 0x0a, + 0x85, 0xb1, 0xc0, 0xa8, 0x72, 0xd4, 0xfe, 0xe0, 0x6f, 0xc1, 0xcb, 0xe2, + 0x83, 0xfe, 0xe5, 0x57, 0x65, 0x41, 0xd9, 0x41, 0x1a, 0x97, 0x86, 0x24, + 0xaf, 0x46, 0x37, 0x78, 0x64, 0x4d, 0x7c, 0x6b, 0x13, 0xd6, 0xda, 0x3b, + 0xb9, 0xe7, 0x7d, 0x3a, 0x03, 0x77, 0x21, 0x7b, 0xa2, 0x0b, 0xb6, 0x86, + 0x57, 0x14, 0xc0, 0xf5, 0xf9, 0xa2, 0xee, 0xe1, 0x46, 0xb6, 0x46, 0x36, + 0xd4, 0x0b, 0x2c, 0x5e, 0x96, 0xc3, 0x73, 0x65, 0x64, 0x2e, 0x56, 0x22, + 0x76, 0xec, 0xd2, 0x1b, 0xbe, 0x62, 0x62, 0x38, 0xfd, 0x77, 0x6e, 0xa8, + 0x2c, 0x0e, 0xc8, 0x8a, 0xf2, 0x27, 0x09, 0xde, 0x2b, 0x4a, 0x3a, 0x4b, + 0xd8, 0x89, 0xfa, 0x04, 0xbc, 0xaf, 0x0c, 0xe3, 0xdf, 0x59, 0x41, 0xf1, + 0x63, 0xdd, 0xfe, 0x52, 0xe0, 0x84, 0x86, 0x03, 0xf1, 0xbb, 0xb1, 0x16, + 0xa4, 0xee, 0x4c, 0xc0, 0x26, 0x19, 0xd1, 0xe5, 0x76, 0x8d, 0xaf, 0x9d, + 0xb3, 0xd7, 0xcd, 0xf4, 0xde, 0xca, 0x47, 0x43, 0x5e, 0xe3, 0xa2, 0x61, + 0x07, 0x62, 0x8a, 0x0a, 0xba, 0xab, 0x0d, 0xd0, 0x2a, 0xd0, 0x47, 0xf6, + 0x97, 0x46, 0xc5, 0xf1, 0x39, 0xdc, 0x3f, 0x63, 0x6e, 0xc5, 0xee, 0x0d, + 0x39, 0xed, 0x03, 0xe1, 0x29, 0x97, 0x25, 0x4f, 0x22, 0x51, 0xe9, 0xe9, + 0xca, 0xa6, 0xb9, 0x31, 0xef, 0xe4, 0xa6, 0x2a, 0xac, 0x04, 0x85, 0xc9, + 0x1e, 0xb2, 0x9b, 0x35, 0x51, 0xd3, 0xfa, 0x1f, 0x0e, 0x0e, 0xa6, 0xf7, + 0x02, 0xdd, 0xcf, 0xeb, 0x27, 0xad, 0x05, 0x4a, 0x71, 0x9b, 0xcf, 0x2a, + 0x75, 0x40, 0x0a, 0x6d, 0x16, 0x8e, 0x4e, 0x3d, 0x4e, 0x36, 0xb4, 0x3f, + 0xc8, 0x91, 0x86, 0xfc, 0xf8, 0x8d, 0x5b, 0xf5, 0x88, 0x4c, 0x84, 0xe5, + 0xd5, 0xec, 0xce, 0x1a, 0x24, 0x0d, 0xc2, 0x03, 0x89, 0x78, 0xb4, 0x8a, + 0x9c, 0x83, 0xfb, 0x2c, 0x6c, 0x64, 0xd9, 0x3f, 0x45, 0x12, 0x30, 0xbe, + 0x95, 0x5c, 0x59, 0x35, 0x24, 0x6b, 0x56, 0x1c, 0x16, 0xe1, 0xf9, 0x4c, + 0xb9, 0x6c, 0x0c, 0x13, 0x22, 0xf6, 0x33, 0xc6, 0xe9, 0x08, 0x4e, 0xf3, + 0x4a, 0xb2, 0xac, 0xdf, 0xbe, 0x3f, 0x76, 0x2f, 0x9f, 0xef, 0x8a, 0xa9, + 0xc4, 0xe4, 0x30, 0xab, 0x4a, 0xb7, 0xdc, 0xa2, 0x62, 0xf4, 0x66, 0x4d, + 0xa2, 0x11, 0xc6, 0x60, 0x3a, 0x83, 0xa1, 0x09, 0xe2, 0xdc, 0xf6, 0xf9, + 0x19, 0x7a, 0x25, 0xd7, 0x64, 0x96, 0xaf, 0xbe, 0xc4, 0x61, 0x3f, 0x8a, + 0x10, 0x80, 0xae, 0x6e, 0x22, 0x1c, 0xa0, 0x1b, 0x57, 0x93, 0x4f, 0x80, + 0xa1, 0x39, 0x9a, 0xaa, 0x58, 0xe9, 0x04, 0x8f, 0x21, 0xf2, 0x12, 0x0a, + 0xbd, 0x36, 0x63, 0x22, 0x51, 0xe9, 0xfa, 0x0d, 0xb9, 0x59, 0xc2, 0x0f, + 0xa1, 0x9d, 0xcb, 0x7e, 0x58, 0x73, 0x9b, 0x90, 0xd6, 0xe0, 0xa6, 0x2b, + 0x0b, 0x13, 0x7a, 0x10, 0x36, 0x13, 0x09, 0xaa, 0xd8, 0xec, 0x69, 0xdd, + 0x87, 0x7e, 0x7b, 0xeb, 0x51, 0x46, 0x20, 0x87, 0x21, 0xbd, 0x1b, 0x91, + 0x17, 0x12, 0x50, 0x5f, 0xa1, 0xf3, 0x06, 0xa0, 0xc7, 0x65, 0x08, 0x9c, + 0x21, 0x14, 0x20, 0x22, 0xdf, 0xc5, 0xd7, 0x1a, 0xc9, 0xb8, 0xd5, 0xc0, + 0xbb, 0x14, 0x3c, 0x4e, 0xe1, 0xf9, 0x45, 0xe0, 0xc1, 0x8a, 0xcb, 0x28, + 0x37, 0xbb, 0x7c, 0xc2, 0x73, 0x48, 0x7d, 0x42, 0x03, 0xa5, 0x6f, 0x9f, + 0x07, 0x0f, 0xb7, 0x35, 0x01, 0x39, 0xf2, 0x8b, 0x4d, 0x50, 0xbf, 0x6a, + 0xc2, 0x44, 0x27, 0xc2, 0xd1, 0x68, 0x7e, 0x32, 0x3c, 0x48, 0x24, 0x9b, + 0xba, 0x51, 0x43, 0x38, 0xf3, 0x3d, 0x93, 0x3c, 0xda, 0xb5, 0xa9, 0xef, + 0x23, 0xbe, 0x92, 0x7c, 0xc7, 0xf1, 0x37, 0xa7, 0x3c, 0xfe, 0x89, 0xc3, + 0x0b, 0xfa, 0x10, 0xfe, 0xc8, 0x53, 0xcb, 0xfb, 0xe9, 0xeb, 0x05, 0xe6, + 0xd0, 0xe9, 0x96, 0x29, 0x56, 0x61, 0x12, 0x75, 0x91, 0x65, 0x2d, 0x81, + 0xc5, 0x32, 0x50, 0xe4, 0xe2, 0xe8, 0x76, 0xcb, 0xbb, 0x0a, 0x55, 0xdc, + 0x25, 0xa4, 0x42, 0x1d, 0x8f, 0xd4, 0x7f, 0x8c, 0x84, 0xb4, 0xb3, 0xb2, + 0x87, 0xf1, 0x9d, 0x6b, 0x3b, 0x0a, 0xae, 0x20, 0x04, 0x38, 0xb6, 0xea, + 0x43, 0x95, 0x2d, 0xad, 0x60, 0x64, 0x1a, 0x33, 0xd0, 0xfd, 0x04, 0x88, + 0x75, 0xec, 0x0d, 0x45, 0x7f, 0x5e, 0x23, 0x2a, 0x64, 0xb0, 0x26, 0xcc, + 0xb1, 0x46, 0x04, 0xbf, 0x20, 0x59, 0x0c, 0xd5, 0x8f, 0x2c, 0xa9, 0xce, + 0x01, 0x2f, 0x4d, 0x9a, 0xd1, 0xd6, 0xb4, 0xb5, 0xf4, 0xea, 0x5d, 0x11, + 0xfd, 0xe4, 0x7d, 0x51, 0x11, 0x7e, 0xd5, 0x4c, 0x36, 0x12, 0xfc, 0x24, + 0x0a, 0x37, 0x20, 0x3e, 0x5f, 0x0e, 0xdd, 0x83, 0xa8, 0x85, 0xce, 0x2f, + 0xa7, 0xc0, 0x7e, 0xd6, 0x56, 0x21, 0x8f, 0x65, 0x48, 0x0c, 0x25, 0x35, + 0x08, 0xf3, 0x04, 0x84, 0xe2, 0x25, 0x71, 0xc0, 0x13, 0xa9, 0xe6, 0x77, + 0x3f, 0x9b, 0x3a, 0x10, 0xa0, 0x2c, 0x28, 0xdb, 0x70, 0xfe, 0x3f, 0x40, + 0x68, 0x24, 0xfb, 0x3f, 0x3a, 0x76, 0x60, 0x88, 0x6b, 0x4a, 0xda, 0xcf, + 0xdd, 0x97, 0x6b, 0x6a, 0xef, 0x49, 0x16, 0x53, 0xe7, 0x0e, 0xf5, 0x3c, + 0x15, 0x94, 0xe2, 0xce, 0xdd, 0x21, 0x11, 0xbf, 0xd7, 0xf9, 0x9b, 0xa0, + 0x2b, 0xc2, 0x51, 0x7b, 0x79, 0x00, 0x21, 0x5b, 0x48, 0x96, 0xe7, 0x59, + 0x15, 0x40, 0x95, 0x05, 0x93, 0x3a, 0x24, 0x2e, 0x2c, 0x19, 0x6e, 0x42, + 0x9a, 0x1e, 0x7b, 0x8e, 0x96, 0x4e, 0xbb, 0xf1, 0xfb, 0xea, 0x3b, 0xc1, + 0xda, 0x9a, 0x85, 0x97, 0x32, 0xab, 0xd4, 0x06, 0x0a, 0xe8, 0xb7, 0x95, + 0x82, 0x20, 0x18, 0xfb, 0x2c, 0x93, 0x30, 0x42, 0x21, 0xbc, 0xe8, 0xe0, + 0x74, 0x7d, 0xc0, 0xbf, 0xcc, 0xad, 0xad, 0x43, 0xfe, 0x46, 0xec, 0x35, + 0xcd, 0x74, 0xed, 0x23, 0xd8, 0x7a, 0xb8, 0x53, 0xcd, 0x60, 0xef, 0xed, + 0xfe, 0x9a, 0x6b, 0x33, 0x07, 0x8f, 0x6d, 0xde, 0x35, 0xcd, 0xb3, 0xd1, + 0x92, 0x9f, 0x19, 0xaf, 0xfd, 0x0e, 0xab, 0x65, 0x64, 0x7f, 0x0e, 0x16, + 0xd5, 0x3d, 0x4f, 0x72, 0x38, 0x08, 0x47, 0xe5, 0xea, 0xdd, 0xe4, 0xc4, + 0xed, 0x10, 0x4a, 0x83, 0x6d, 0xad, 0x7f, 0xf5, 0x96, 0xd3, 0xc8, 0xee, + 0x1b, 0x30, 0xa4, 0xf7, 0x76, 0x27, 0xae, 0x39, 0x57, 0x62, 0x39, 0x7f, + 0x75, 0x9c, 0xf7, 0x60, 0x4d, 0xa3, 0xdb, 0x70, 0xd0, 0xbc, 0x92, 0x90, + 0x41, 0x54, 0xaa, 0x40, 0x6b, 0xaf, 0x71, 0x1a, 0xc8, 0x58, 0x81, 0xd2, + 0x10, 0xd6, 0x75, 0x2f, 0x22, 0x92, 0xd7, 0x72, 0xda, 0x9b, 0x19, 0x18, + 0x57, 0x1b, 0x0d, 0x69, 0x5e, 0x22, 0x8a, 0x85, 0x00, 0x0b, 0xd7, 0x5c, + 0x16, 0xb4, 0xe4, 0xb7, 0x4f, 0xdc, 0xec, 0x5b, 0xbd, 0x3d, 0x19, 0xfd, + 0x9e, 0x6e, 0x6e, 0x46, 0xf4, 0x86, 0x8c, 0xf7, 0x0f, 0x23, 0x94, 0x5d, + 0xec, 0x2f, 0x67, 0x41, 0xc2, 0x5d, 0x80, 0xaa, 0x2c, 0x36, 0xa9, 0xf2, + 0x46, 0x76, 0x49, 0x54, 0xfe, 0xe0, 0xe6, 0xaf, 0x0b, 0x80, 0xbc, 0xca, + 0xbf, 0x25, 0x6d, 0x45, 0x48, 0xf8, 0xd2, 0xf4, 0xb0, 0x9a, 0xd5, 0xea, + 0x10, 0x06, 0xf5, 0xdd, 0x1a, 0xfa, 0xa2, 0x6a, 0x7c, 0x46, 0x22, 0x58, + 0x26, 0xc8, 0x31, 0x36, 0x73, 0x11, 0x80, 0x88, 0x60, 0xea, 0x67, 0x8f, + 0xb5, 0xd0, 0xef, 0x8c, 0xc1, 0xe8, 0xaa, 0xce, 0xf0, 0xe7, 0x27, 0xf7, + 0xb5, 0xe4, 0xc9, 0xe4, 0x9e, 0x94, 0x90, 0x92, 0xdc, 0x65, 0xcf, 0x7a, + 0x1e, 0xf8, 0x2a, 0x8c, 0x66, 0xa4, 0x17, 0xa9, 0xaa, 0x05, 0xca, 0x90, + 0x00, 0x01, 0xf1, 0xa3, 0x8a, 0xc9, 0x1a, 0x18, 0xc9, 0xc2, 0x2f, 0xf8, + 0x47, 0x21, 0x57, 0x6e, 0xe9, 0x12, 0x9a, 0x9d, 0x92, 0x9b, 0x68, 0xac, + 0x76, 0x88, 0x5e, 0x65, 0x4e, 0x8a, 0xfd, 0x2a, 0x30, 0xe8, 0x9e, 0x86, + 0x9e, 0xf4, 0x7c, 0xc2, 0x1c, 0x6f, 0x6b, 0x06, 0xc7, 0x90, 0x25, 0x35, + 0xd0, 0x6d, 0x32, 0xd7, 0x88, 0x8b, 0x3b, 0x48, 0x49, 0xe9, 0x42, 0x97, + 0x78, 0x1d, 0x6e, 0xbe, 0xe2, 0xf6, 0xca, 0x22, 0x38, 0x19, 0xd5, 0x23, + 0x5c, 0x9e, 0x68, 0x18, 0xbf, 0x3e, 0xcd, 0x9d, 0x58, 0x12, 0xd4, 0xa0, + 0x53, 0x0a, 0x0a, 0x8b, 0x30, 0xd3, 0x8a, 0xf9, 0xc9, 0x4e, 0x57, 0xf7, + 0x31, 0x04, 0xdd, 0x0b, 0xc9, 0xb4, 0xc2, 0x67, 0x64, 0x85, 0x5e, 0xe6, + 0x14, 0xcd, 0x5e, 0xa5, 0x49, 0x3e, 0x83, 0xa9, 0xf1, 0xd3, 0xce, 0x7f, + 0x22, 0x73, 0x93, 0x32, 0x65, 0x81, 0x08, 0xfe, 0x90, 0x02, 0xd8, 0x7f, + 0xe4, 0x9e, 0x51, 0xad, 0x3a, 0x2c, 0x77, 0x07, 0xa3, 0x16, 0xd9, 0x5f, + 0xb6, 0x58, 0x7b, 0xd7, 0x75, 0x91, 0x73, 0xb5, 0x04, 0x73, 0xea, 0x1a, + 0x0f, 0xbf, 0x00, 0x75, 0x43, 0xa1, 0x7d, 0x2c, 0xe8, 0xaa, 0xd0, 0x6d, + 0x49, 0x17, 0x45, 0x5e, 0x63, 0x23, 0x3f, 0x38, 0xcc, 0x27, 0x37, 0x32, + 0xd7, 0x10, 0x71, 0x54, 0x6e, 0xd5, 0xbb, 0x38, 0x61, 0x28, 0xd2, 0x52, + 0xce, 0xd7, 0x4a, 0x4c, 0x69, 0xdf, 0x3d, 0x4f, 0xc9, 0x27, 0x6c, 0x1c, + 0x90, 0x79, 0x5a, 0x5f, 0x0d, 0x2e, 0x30, 0x4e, 0xd4, 0x50, 0x7e, 0x62, + 0x33, 0x14, 0x21, 0xc8, 0xf9, 0xa9, 0x8b, 0xea, 0x2b, 0x0e, 0xee, 0xe4, + 0x3f, 0x22, 0xc0, 0xfc, 0x6b, 0x22, 0x6d, 0x4a, 0x50, 0x53, 0x18, 0xd8, + 0x23, 0x82, 0x91, 0x09, 0x0a, 0x9c, 0x8b, 0x0f, 0xbc, 0x14, 0xa2, 0xad, + 0xa5, 0x38, 0xb7, 0x3b, 0x43, 0xe3, 0xfc, 0x88, 0x87, 0x7a, 0x45, 0x6a, + 0x18, 0xd6, 0x0b, 0xdf, 0x23, 0x55, 0x2f, 0xdb, 0xba, 0x1f, 0x5a, 0xe7, + 0x4b, 0x92, 0xa1, 0x28, 0xd2, 0xcc, 0xa7, 0xaa, 0x3b, 0x67, 0x07, 0x15, + 0x25, 0xb1, 0xef, 0xbf, 0x1f, 0x17, 0x8e, 0xc2, 0xdb, 0x5d, 0x29, 0xb2, + 0xad, 0x4b, 0xc3, 0x03, 0x61, 0x4e, 0x50, 0x36, 0x72, 0xd8, 0x7d, 0xc2, + 0xa2, 0x32, 0x74, 0x2a, 0xe1, 0x79, 0x9c, 0x30, 0x47, 0xde, 0x21, 0x64, + 0x35, 0x0e, 0xcc, 0x1c, 0x6d, 0x0e, 0x1f, 0x49, 0xef, 0x33, 0xd8, 0x01, + 0x5a, 0xec, 0x9d, 0x6f, 0x23, 0x64, 0xdf, 0xbb, 0x02, 0x26, 0x39, 0x8f, + 0xdb, 0xb4, 0xfa, 0x2f, 0x56, 0x7e, 0x30, 0x0f, 0x90, 0x8c, 0x3c, 0xc8, + 0xb0, 0x72, 0x27, 0x81, 0x89, 0x9a, 0xc5, 0x93, 0x77, 0xdc, 0xe3, 0xb9, + 0x16, 0xa8, 0x9c, 0xf5, 0xf4, 0x34, 0x0f, 0x7c, 0x34, 0x37, 0x54, 0x48, + 0x4e, 0x64, 0x55, 0xc0, 0x1f, 0x01, 0xe1, 0x59, 0x70, 0x73, 0x83, 0x07, + 0x19, 0xda, 0xe2, 0x00, 0x82, 0xc0, 0x2d, 0xa6, 0x6d, 0xdf, 0x43, 0x57, + 0x8a, 0x96, 0x5f, 0x71, 0x03, 0xba, 0xf1, 0x9c, 0xfa, 0x26, 0xaa, 0x5e, + 0x40, 0x77, 0xf0, 0x13, 0x40, 0x37, 0x6f, 0xc7, 0xeb, 0x15, 0xb2, 0x80, + 0x53, 0x42, 0x07, 0x04, 0xb5, 0xda, 0xdf, 0x5c, 0xd5, 0x0f, 0x87, 0xd9, + 0x7b, 0xfe, 0x74, 0xfc, 0x37, 0x44, 0x55, 0xe2, 0x81, 0x58, 0x8d, 0x6b, + 0xea, 0x59, 0x9a, 0x5b, 0x84, 0x8e, 0x47, 0x58, 0xda, 0x55, 0x06, 0x14, + 0x69, 0x1c, 0x8c, 0xb9, 0x32, 0x04, 0xc1, 0x92, 0x72, 0x77, 0xb2, 0xc2, + 0x8c, 0x34, 0x87, 0x87, 0x23, 0x2e, 0x30, 0xc1, 0x48, 0xa6, 0x79, 0x87, + 0xdb, 0xe7, 0x20, 0x93, 0x63, 0x48, 0x12, 0xfd, 0xcd, 0xca, 0x09, 0x47, + 0x0e, 0xe2, 0x46, 0x40, 0xa5, 0x2b, 0xda, 0x8e, 0x4a, 0xf0, 0xd6, 0x0a, + 0xa6, 0xf2, 0x67, 0xba, 0xa7, 0xd8, 0xa5, 0x1e, 0x42, 0x3d, 0xea, 0x9c, + 0xd9, 0x59, 0xa1, 0x5d, 0x7b, 0xfb, 0x1a, 0x7d, 0xe7, 0xd6, 0xdd, 0x2a, + 0xbd, 0xf6, 0x84, 0x0f, 0xb9, 0x30, 0xa7, 0xb7, 0x54, 0x1c, 0x39, 0xae, + 0xca, 0x74, 0x99, 0xb1, 0x51, 0xe6, 0x6a, 0x8c, 0x0a, 0xab, 0x6b, 0x31, + 0xd9, 0x26, 0xe8, 0x16, 0xdc, 0xd8, 0x85, 0x1e, 0xf7, 0xe1, 0xbd, 0x23, + 0xe2, 0x3b, 0xa0, 0x0f, 0x4e, 0xc7, 0x18, 0xf1, 0x3f, 0x26, 0xdb, 0x0a, + 0xc6, 0x25, 0x0c, 0x29, 0xca, 0x32, 0xe9, 0x66, 0x26, 0x8d, 0x46, 0xf7, + 0x95, 0x7f, 0x2e, 0x81, 0x8c, 0x4c, 0xf1, 0xa6, 0x48, 0x6a, 0xd2, 0xd3, + 0x84, 0x91, 0x14, 0x27, 0x88, 0x2a, 0x6f, 0x50, 0x1d, 0xe2, 0x1c, 0x3c, + 0x55, 0x6b, 0x24, 0x5c, 0x0d, 0x09, 0x61, 0xa1, 0x7d, 0xe3, 0x8d, 0x6e, + 0xd1, 0x16, 0x36, 0x82, 0xd4, 0x0d, 0xf1, 0xfa, 0xb8, 0x13, 0x27, 0x47, + 0xad, 0x8f, 0xad, 0xe6, 0xff, 0x71, 0x2f, 0x17, 0x58, 0xf6, 0x3a, 0xc4, + 0x21, 0x3b, 0xf9, 0xfe, 0x76, 0x43, 0x0a, 0x8f, 0xb8, 0x3d, 0xa0, 0x81, + 0x48, 0x3a, 0x91, 0x41, 0x82, 0x31, 0xf3, 0x9e, 0x89, 0x26, 0xfa, 0xda, + 0xd0, 0x79, 0xed, 0x52, 0x7b, 0xd1, 0xf1, 0x50, 0xf8, 0xd4, 0x52, 0x13, + 0x75, 0x16, 0x83, 0xe5, 0x03, 0x27, 0xcb, 0x8e, 0x5c, 0x40, 0x1d, 0x91, + 0x75, 0x9e, 0x08, 0x50, 0x07, 0x30, 0x15, 0x12, 0x1a, 0x79, 0x51, 0x38, + 0x03, 0x9f, 0x2b, 0xc5, 0xaf, 0xb3, 0x32, 0xd3, 0xd3, 0xa1, 0xb1, 0x68, + 0x74, 0xe5, 0x85, 0x76, 0x0b, 0x27, 0x3c, 0x02, 0xcf, 0x50, 0xb5, 0x17, + 0xb0, 0x45, 0x15, 0x74, 0x9d, 0x68, 0x63, 0xb2, 0xb8, 0x2d, 0x5d, 0x3d, + 0x90, 0x85, 0xad, 0x7e, 0x3a, 0x7b, 0xdf, 0xfb, 0x0a, 0x38, 0x8f, 0xc3, + 0x06, 0x4e, 0x2a, 0x23, 0x91, 0xff, 0x75, 0x3c, 0xca, 0x1d, 0x23, 0xd6, + 0x9b, 0xcc, 0xd0, 0xae, 0xe6, 0xd1, 0xa6, 0x61, 0xd8, 0x70, 0xbf, 0xdc, + 0xb3, 0x8e, 0xe2, 0x97, 0x39, 0xd2, 0x82, 0xc2, 0xf8, 0x34, 0x8a, 0x90, + 0xec, 0x08, 0x86, 0x88, 0xe5, 0xb4, 0x80, 0x35, 0x27, 0x2d, 0xbb, 0x28, + 0x64, 0x5d, 0xee, 0x15, 0x13, 0x01, 0x6c, 0x78, 0x0c, 0x3b, 0x40, 0x8f, + 0x47, 0x2d, 0xe6, 0xe5, 0x62, 0x07, 0xe6, 0x9f, 0x12, 0x13, 0xde, 0xb0, + 0x94, 0x3c, 0x69, 0x93, 0x52, 0x6b, 0x49, 0x44, 0x6b, 0x50, 0x34, 0xe1, + 0x37, 0x77, 0xa5, 0x48, 0x14, 0xd3, 0x66, 0x63, 0x81, 0xce, 0xb6, 0x0b, + 0x54, 0x3f, 0xf8, 0xe0, 0x42, 0x28, 0x29, 0xe9, 0x01, 0x02, 0xa1, 0x37, + 0x25, 0xef, 0xc3, 0xa1, 0x88, 0xd9, 0x91, 0x64, 0x7f, 0xb7, 0x62, 0xd7, + 0x12, 0x6e, 0x19, 0x51, 0x1a, 0x0d, 0xda, 0x48, 0xfd, 0x57, 0xb4, 0x47, + 0xd6, 0x66, 0xd0, 0xd4, 0x6f, 0x71, 0x62, 0x24, 0x09, 0xb3, 0x98, 0x62, + 0x71, 0x0c, 0x03, 0x33, 0x07, 0x3e, 0x5d, 0x3f, 0x21, 0x5f, 0x22, 0xca, + 0xc2, 0x85, 0xd2, 0xbf, 0x3e, 0x44, 0xdf, 0x98, 0xc8, 0x9c, 0x49, 0xa0, + 0x29, 0x42, 0x1a, 0x00, 0xab, 0x02, 0x0d, 0x69, 0xcb, 0xf9, 0x68, 0x04, + 0x79, 0x50, 0x6a, 0x8a, 0x92, 0xc7, 0x1f, 0xa2, 0x9b, 0x65, 0xe7, 0xc8, + 0xb5, 0x0d, 0xc0, 0xfc, 0x24, 0x05, 0x6f, 0x33, 0xe9, 0xc8, 0x15, 0x73, + 0xe8, 0xcc, 0x94, 0x4d, 0x39, 0x3f, 0xcb, 0xf8, 0xbf, 0xbd, 0x3d, 0xbc, + 0x9a, 0x05, 0x66, 0x48, 0x6f, 0xb8, 0xd9, 0xe2, 0x6a, 0x4a, 0x3d, 0xc8, + 0x2e, 0x0a, 0xdf, 0x90, 0xae, 0x97, 0x6d, 0x9b, 0xbb, 0xe4, 0x3f, 0xf7, + 0xcc, 0xe4, 0xb2, 0x71, 0x70, 0x5d, 0xa9, 0xa0, 0xfc, 0x0e, 0xe5, 0x26, + 0x96, 0x9e, 0xcf, 0xde, 0xc2, 0x04, 0xba, 0x2f, 0xaf, 0x45, 0x3a, 0xe4, + 0x62, 0x04, 0x24, 0x70, 0xcc, 0x6b, 0x1f, 0x0f, 0x09, 0xf4, 0x6f, 0x9d, + 0xf6, 0x6d, 0xbf, 0xcc, 0x6c, 0xc2, 0xa2, 0x8e, 0xd3, 0xb0, 0xcf, 0x4e, + 0x0d, 0x23, 0x8d, 0x86, 0xcd, 0x2b, 0x0b, 0xa6, 0xcf, 0x75, 0x89, 0x4b, + 0xc8, 0x40, 0x16, 0x81, 0xae, 0xf4, 0x2d, 0x37, 0x48, 0x72, 0xc9, 0x69, + 0x57, 0x64, 0x62, 0x13, 0x80, 0x7b, 0x81, 0x9d, 0x54, 0x0d, 0xac, 0x6d, + 0x3d, 0x9d, 0x44, 0x23, 0x1a, 0xb9, 0xd3, 0x2f, 0xd2, 0x99, 0x09, 0x2c, + 0x2d, 0x80, 0x23, 0xf1, 0x65, 0xd9, 0xf7, 0x29, 0x32, 0x52, 0x71, 0x40, + 0x0f, 0xf8, 0xc2, 0x38, 0xac, 0x6e, 0x8d, 0xfd, 0x8d, 0xe4, 0x57, 0xd0, + 0xee, 0xac, 0x95, 0xac, 0x9e, 0x01, 0xc5, 0x49, 0x79, 0x93, 0x87, 0x8c, + 0xcd, 0x02, 0xf0, 0xe4, 0x67, 0x33, 0x87, 0x7a, 0xdd, 0xae, 0x66, 0x5f, + 0x82, 0x14, 0x8a, 0x83, 0x77, 0xa7, 0x76, 0xea, 0x85, 0xda, 0xf3, 0xe6, + 0xca, 0x19, 0xc3, 0xd7, 0xff, 0x07, 0xfa, 0x87, 0x16, 0x51, 0x3d, 0x2e, + 0xe0, 0xd5, 0xeb, 0xba, 0x2c, 0x41, 0xaf, 0x31, 0x5a, 0x98, 0x7d, 0x49, + 0x9a, 0x82, 0x54, 0xbe, 0x2e, 0xfb, 0xfd, 0xe4, 0x82, 0xd5, 0x15, 0x91, + 0x2f, 0xcb, 0x68, 0x39, 0xa1, 0x8d, 0x2f, 0x23, 0xb4, 0x4c, 0x86, 0x73, + 0xf2, 0x57, 0x4b, 0xfb, 0x41, 0x26, 0x5f, 0x2e, 0x53, 0x2b, 0x6e, 0x15, + 0x08, 0xb3, 0x46, 0x9b, 0xd5, 0xb3, 0x88, 0x87, 0x65, 0xe7, 0x2c, 0x54, + 0xa3, 0xc7, 0x05, 0x39, 0xdf, 0x3a, 0x95, 0x8c, 0x6b, 0x5c, 0xaa, 0x25, + 0xc0, 0x3e, 0xc5, 0xbe, 0x1e, 0xe5, 0xe9, 0xdf, 0xf6, 0x81, 0xe3, 0x11, + 0x5e, 0xe1, 0xb5, 0x8d, 0x0a, 0x6f, 0xfd, 0xd2, 0x90, 0x15, 0xc1, 0x1c, + 0x19, 0x53, 0x3a, 0x12, 0xfd, 0xfb, 0xb5, 0xcc, 0x52, 0xdd, 0x74, 0x3e, + 0x5c, 0xc3, 0xf2, 0x12, 0x4f, 0x02, 0x1c, 0xb9, 0x6b, 0x40, 0xd7, 0x52, + 0x0e, 0x5a, 0x47, 0x9b, 0x04, 0xeb, 0x15, 0xa5, 0x9c, 0x68, 0x88, 0x81, + 0x27, 0xc6, 0x2a, 0x3e, 0x6b, 0x4d, 0xfd, 0xb1, 0x76, 0x14, 0x86, 0x92, + 0x37, 0xe2, 0x26, 0x46, 0x58, 0x9b, 0xd9, 0x39, 0xbb, 0xdf, 0x05, 0x18, + 0xc8, 0xaf, 0x8b, 0x5d, 0x96, 0xc4, 0xf8, 0xcd, 0x84, 0x53, 0x82, 0xa9, + 0xbf, 0xe8, 0x73, 0x92, 0x78, 0x1e, 0x7e, 0x68, 0xd0, 0x87, 0x33, 0x6a, + 0x14, 0xbf, 0x79, 0xf2, 0x45, 0x39, 0x8e, 0xd0, 0xce, 0x71, 0xc4, 0x23, + 0x7d, 0x54, 0x90, 0xd3, 0x71, 0xd4, 0x58, 0x8d, 0xbe, 0xf9, 0x91, 0xd1, + 0xa6, 0x22, 0xec, 0xba, 0x56, 0xaf, 0xf6, 0xb7, 0x9d, 0xa0, 0x26, 0x52, + 0xe7, 0xce, 0x3f, 0xe0, 0x18, 0x23, 0xe4, 0x6b, 0xef, 0x23, 0x2d, 0x5d, + 0xbf, 0xc0, 0xe7, 0x0b, 0x9f, 0x8f, 0xde, 0xbe, 0x42, 0x0d, 0x27, 0x07, + 0x02, 0x6b, 0xd7, 0x8b, 0xa4, 0x83, 0x2a, 0x68, 0x4c, 0x9c, 0x68, 0x3d, + 0xdc, 0x88, 0x7e, 0x6c, 0xd5, 0xec, 0x4b, 0x8a, 0xeb, 0x18, 0x94, 0x55, + 0x6d, 0x21, 0x72, 0xff, 0xf0, 0xe8, 0xb3, 0x71, 0xde, 0x5f, 0xf0, 0x55, + 0x9d, 0xd3, 0x50, 0x79, 0xa1, 0xb3, 0x80, 0x8e, 0xf1, 0xfb, 0xce, 0x02, + 0x6d, 0xed, 0x50, 0xa7, 0x6e, 0xac, 0xb1, 0x4a, 0x3e, 0xa9, 0x72, 0xf6, + 0xf4, 0xbe, 0xe5, 0xb6, 0x8d, 0x3f, 0x2a, 0xd6, 0x74, 0x3b, 0xe9, 0x32, + 0x3e, 0x22, 0x6e, 0x87, 0xcf, 0xd2, 0x61, 0x34, 0xbe, 0x5c, 0x3d, 0xac, + 0xf4, 0x49, 0x4c, 0xd1, 0x91, 0xf2, 0x13, 0x5f, 0xb2, 0xea, 0xd3, 0x23, + 0x88, 0xec, 0x88, 0xde, 0x82, 0xef, 0x52, 0x2f, 0xda, 0xb7, 0x34, 0x15, + 0xdf, 0xe9, 0x24, 0x58, 0x2c, 0x00, 0x45, 0x90, 0x5a, 0x92, 0x0b, 0xdf, + 0x0d, 0x82, 0xb0, 0xa0, 0xa0, 0xdf, 0xc9, 0x8e, 0x86, 0xb2, 0x54, 0x32, + 0xc8, 0xf5, 0xff, 0x13, 0x63, 0xf6, 0x77, 0xf6, 0xd0, 0x8e, 0x87, 0x76, + 0x90, 0x7e, 0x2d, 0xf9, 0xa9, 0x22, 0xd9, 0x74, 0xcf, 0x48, 0x00, 0x77, + 0xeb, 0x8e, 0xad, 0x87, 0x8c, 0xdc, 0x0c, 0xcc, 0xc5, 0x9a, 0x7b, 0x4e, + 0xf2, 0x8b, 0xc5, 0xe9, 0xa3, 0xf9, 0x33, 0x08, 0x0b, 0xa0, 0x60, 0x0a, + 0xab, 0xb5, 0x57, 0x48, 0xcd, 0x4a, 0x1e, 0x78, 0x2e, 0x0c, 0xf5, 0x6d, + 0x64, 0x69, 0x8e, 0x78, 0xbf, 0xb9, 0xd6, 0xad, 0x42, 0x2d, 0x0e, 0x97, + 0xc9, 0x1d, 0xc5, 0x03, 0x91, 0x90, 0xec, 0xef, 0x5e, 0x94, 0xdf, 0x77, + 0x01, 0xa3, 0xbe, 0xcd, 0x63, 0xe1, 0x3a, 0x65, 0xee, 0x92, 0x50, 0x17, + 0x7f, 0xed, 0xec, 0xaf, 0x23, 0xd7, 0x2f, 0x8f, 0x73, 0x63, 0x63, 0xb1, + 0x21, 0xd4, 0x9a, 0xf0, 0x3b, 0x1d, 0x1c, 0xd2, 0x3e, 0xa0, 0xd2, 0x4f, + 0x0a, 0x76, 0xef, 0x54, 0x1e, 0x9d, 0x99, 0xc6, 0xe0, 0x53, 0x54, 0xa1, + 0xea, 0x19, 0x3a, 0xe5, 0xb6, 0xc6, 0x53, 0xdf, 0xaa, 0x7e, 0xbb, 0x3a, + 0xd2, 0x60, 0x84, 0x1a, 0x8c, 0xe4, 0xb3, 0x90, 0x4f, 0xe0, 0x50, 0x1c, + 0x17, 0x3a, 0x81, 0x8e, 0x4a, 0x40, 0xdd, 0x8e, 0x57, 0xae, 0xf0, 0xe2, + 0x3f, 0x28, 0x62, 0x9c, 0xf2, 0x7a, 0xf4, 0xa6, 0x3e, 0xa1, 0x9c, 0xf7, + 0x2c, 0x5b, 0x97, 0xd2, 0x5c, 0x6e, 0x73, 0xde, 0x76, 0xf0, 0x66, 0x38, + 0xc3, 0x5b, 0x86, 0x40, 0x7f, 0x14, 0x38, 0x0f, 0x17, 0x37, 0x06, 0x47, + 0x63, 0x4e, 0xf0, 0x43, 0xf5, 0x98, 0xbb, 0xe1, 0x2c, 0x56, 0x48, 0x8d, + 0xf6, 0xe2, 0xa0, 0xd0, 0xa6, 0x54, 0xd1, 0x47, 0x90, 0xd7, 0x23, 0xd1, + 0xd1, 0x67, 0xf5, 0xad, 0x89, 0xf3, 0x56, 0x87, 0x81, 0x80, 0x5e, 0x1a, + 0xaf, 0x80, 0xf9, 0xbe, 0xb5, 0x12, 0xa9, 0x4a, 0xc7, 0xdd, 0x90, 0x1b, + 0xe6, 0xf6, 0xa7, 0x92, 0x1b, 0x28, 0x88, 0xad, 0x05, 0x7b, 0x90, 0x8b, + 0x16, 0x7c, 0xbe, 0xa0, 0x28, 0x1e, 0x50, 0xc8, 0x48, 0xdd, 0x9a, 0xa6, + 0xf5, 0xfc, 0xb3, 0x34, 0x58, 0xfa, 0x2e, 0x97, 0xa7, 0x82, 0x24, 0x43, + 0xf9, 0x00, 0x76, 0x32, 0x08, 0x08, 0xb3, 0xc8, 0x18, 0x6e, 0x61, 0x77, + 0xbf, 0xa0, 0xf9, 0xd5, 0x33, 0x5c, 0x0c, 0xf7, 0xfb, 0x90, 0x23, 0x8a, + 0x37, 0x52, 0x9c, 0x5b, 0x7b, 0x9a, 0x99, 0xf2, 0xcb, 0xdb, 0x89, 0xda, + 0x7f, 0xa0, 0x8d, 0x48, 0x95, 0x8e, 0xd5, 0xe4, 0x44, 0x20, 0xe4, 0x3f, + 0xef, 0xe3, 0x86, 0x4b, 0xdf, 0x62, 0x7b, 0x7b, 0x5f, 0x76, 0x41, 0x7d, + 0xef, 0xab, 0x54, 0x96, 0xae, 0x15, 0x4b, 0x2d, 0x90, 0x13, 0x39, 0x48, + 0x9a, 0x9b, 0xa7, 0x4d, 0x03, 0xd6, 0xf4, 0x73, 0xf9, 0xd1, 0xcf, 0x32, + 0x26, 0xa7, 0x42, 0xad, 0x8d, 0x40, 0x60, 0x19, 0x23, 0x81, 0x05, 0x57, + 0x0b, 0x7a, 0x11, 0x82, 0xea, 0x3c, 0x75, 0xe6, 0xdb, 0x38, 0x8d, 0x8b, + 0x74, 0x50, 0xfe, 0xf6, 0x4e, 0x40, 0x62, 0xb8, 0x0c, 0xff, 0x18, 0x2b, + 0x4b, 0xef, 0x08, 0x71, 0x92, 0xbc, 0x0c, 0x1e, 0x1c, 0xab, 0x36, 0xcb, + 0xf8, 0xea, 0x85, 0xd1, 0x62, 0x6b, 0xe7, 0x7c, 0x92, 0x0a, 0x3d, 0x2a, + 0x78, 0x0f, 0x97, 0xd0, 0x83, 0x92, 0xdb, 0xed, 0xd5, 0xb8, 0xaf, 0x7c, + 0x52, 0x78, 0x5f, 0x64, 0x81, 0x1e, 0x18, 0xf9, 0x5f, 0xe6, 0xbb, 0x22, + 0xd6, 0x00, 0x18, 0x29, 0x09, 0x55, 0x96, 0x27, 0x53, 0x48, 0x5a, 0x11, + 0x85, 0x78, 0xc4, 0x5f, 0x1d, 0x0e, 0x5b, 0x4d, 0x0c, 0x66, 0xcf, 0x14, + 0x26, 0x48, 0x3e, 0x3a, 0xd1, 0x1b, 0xe4, 0xce, 0x8b, 0x08, 0xdd, 0x04, + 0xc6, 0x19, 0x4c, 0x18, 0x92, 0xf3, 0xf4, 0xc8, 0xf0, 0x69, 0x38, 0x22, + 0x97, 0x64, 0x6b, 0x16, 0xcf, 0xea, 0x55, 0x7e, 0x36, 0x18, 0xcd, 0x8d, + 0x95, 0x26, 0x5f, 0x12, 0x42, 0x84, 0xb6, 0x4c, 0x6b, 0xd8, 0x42, 0x97, + 0x4f, 0x5e, 0x84, 0xcb, 0xd6, 0x82, 0x5c, 0x32, 0xd3, 0xf7, 0xb1, 0x71, + 0xae, 0xce, 0x17, 0x2a, 0xc3, 0x89, 0x18, 0x50, 0x1d, 0x82, 0x65, 0x50, + 0x83, 0x7d, 0x92, 0x89, 0x48, 0xd8, 0x2f, 0xe3, 0xa9, 0x4c, 0x02, 0xb0, + 0x32, 0x8d, 0x36, 0x0a, 0x09, 0x4a, 0x3e, 0x86, 0x47, 0xfd, 0x06, 0xf9, + 0xf1, 0xb7, 0x77, 0x52, 0xa5, 0x37, 0x0f, 0x11, 0x93, 0xaf, 0x11, 0x4e, + 0x8d, 0xc7, 0x3c, 0x97, 0x96, 0xfa, 0x73, 0x40, 0xb3, 0xd4, 0x91, 0x4a, + 0x01, 0xfb, 0x29, 0x7d, 0xfe, 0xca, 0xec, 0xa0, 0x1d, 0xbc, 0xff, 0x34, + 0x0d, 0x09, 0x0f, 0xa6, 0xbb, 0x85, 0x17, 0x77, 0xfc, 0x63, 0x8e, 0xb8, + 0x2b, 0x3e, 0x21, 0xd9, 0xd7, 0x4a, 0x15, 0x29, 0x37, 0xf1, 0xfa, 0x29, + 0x1e, 0x6a, 0xca, 0x73, 0xce, 0xd4, 0xb4, 0xd7, 0xae, 0xcd, 0xc4, 0xf2, + 0xa5, 0x28, 0x67, 0xfe, 0x20, 0xab, 0x4d, 0xbc, 0xfa, 0xed, 0xd5, 0x7c, + 0x1a, 0x6d, 0x6f, 0xad, 0x63, 0xc8, 0xbc, 0xe4, 0x77, 0xb3, 0x6b, 0x1a, + 0xef, 0x5e, 0x17, 0xe3, 0xf7, 0x89, 0xfa, 0x02, 0x03, 0x60, 0x8c, 0xd2, + 0x09, 0x2f, 0xeb, 0xe7, 0x0b, 0x63, 0xed, 0x51, 0x6a, 0x6c, 0x58, 0xef, + 0x29, 0xb8, 0x41, 0x4f, 0x89, 0x34, 0x73, 0x64, 0xaf, 0x8a, 0xaa, 0x2f, + 0x0e, 0x67, 0xd4, 0x48, 0x15, 0x97, 0x9a, 0xad, 0xe5, 0x03, 0xa0, 0xfe, + 0xcc, 0xdd, 0xfb, 0xac, 0x34, 0x36, 0x15, 0x60, 0x68, 0x3b, 0x07, 0x0f, + 0x8a, 0x78, 0xa1, 0x33, 0xbe, 0x98, 0x97, 0x5f, 0x4f, 0x29, 0x95, 0x15, + 0x8f, 0xbc, 0xa5, 0x16, 0x2a, 0xc4, 0xe2, 0xd1, 0x6c, 0x08, 0xe2, 0x4e, + 0xf7, 0x63, 0xe2, 0x56, 0x1f, 0x40, 0xd9, 0xe4, 0xbc, 0xf2, 0x85, 0xeb, + 0xa1, 0x1a, 0xbf, 0xdf, 0x3b, 0x5c, 0xbb, 0x0b, 0x31, 0xa4, 0xeb, 0x52, + 0x2e, 0x0f, 0x38, 0x6b, 0xe7, 0xbb, 0x06, 0x66, 0x0e, 0x41, 0xb2, 0xa3, + 0x40, 0x8a, 0xf9, 0x39, 0x9f, 0x1d, 0x75, 0xa9, 0x4c, 0x20, 0x5c, 0x30, + 0x25, 0x0a, 0xac, 0x09, 0xe8, 0x2b, 0x9f, 0x5b, 0x9a, 0x21, 0x3d, 0x5d, + 0xa4, 0x5c, 0x3c, 0x20, 0x1f, 0x19, 0xc7, 0xfe, 0x15, 0x15, 0x49, 0x90, + 0xb3, 0x60, 0x4a, 0xd4, 0xc1, 0x54, 0xcc, 0x75, 0xb7, 0x14, 0x61, 0xc2, + 0x17, 0xcd, 0x0a, 0xc7, 0x70, 0x5f, 0xdf, 0x1c, 0x37, 0x92, 0xea, 0xff, + 0x11, 0x0f, 0x4a, 0x2d, 0x54, 0x4c, 0xd4, 0x83, 0xca, 0x52, 0x3c, 0x1b, + 0xa6, 0x7c, 0xfa, 0x41, 0x77, 0x74, 0x24, 0x1e, 0x9e, 0xfb, 0x04, 0x9f, + 0x71, 0xa3, 0x4f, 0x79, 0x1f, 0x22, 0x0b, 0x7b, 0x2d, 0xdf, 0x5a, 0xc9, + 0x7c, 0x72, 0xa8, 0x10, 0xe3, 0x06, 0xdb, 0x68, 0xa3, 0x90, 0x85, 0x43, + 0x65, 0xdb, 0xb6, 0x3f, 0x80, 0x40, 0x63, 0x87, 0x3e, 0xb2, 0x1c, 0x8e, + 0x55, 0xe5, 0xa9, 0x9f, 0x16, 0x3d, 0x35, 0x1d, 0xd0, 0x8f, 0xd9, 0x9a, + 0xdc, 0x19, 0x25, 0x2e, 0x1f, 0x12, 0xcf, 0xe7, 0x59, 0x9d, 0x8a, 0x39, + 0x8e, 0xf0, 0xba, 0xde, 0x54, 0x61, 0x97, 0x3c, 0x39, 0x6f, 0xb4, 0x0d, + 0x7a, 0x44, 0x6b, 0xa1, 0x59, 0xfd, 0x35, 0x1f, 0xb9, 0xe6, 0xb5, 0x90, + 0x81, 0x34, 0x6b, 0x6c, 0xb4, 0x04, 0xba, 0x78, 0x6d, 0xa9, 0x67, 0xec, + 0xc1, 0x40, 0x52, 0x50, 0x27, 0x7f, 0x57, 0xfb, 0x4d, 0xc8, 0x82, 0x7b, + 0x82, 0xbc, 0x26, 0x8e, 0x3f, 0xc6, 0xae, 0xf3, 0xe7, 0xc8, 0x43, 0x0c, + 0x5a, 0x71, 0xd8, 0x4d, 0x71, 0x5c, 0x43, 0x6f, 0x96, 0x47, 0x59, 0xbd, + 0xba, 0x6a, 0xea, 0x7e, 0x69, 0x41, 0x16, 0x6d, 0x6c, 0x2b, 0x16, 0x3a, + 0x03, 0xc6, 0x85, 0x7f, 0xfd, 0x62, 0xf6, 0x23, 0x5c, 0x09, 0x84, 0x25, + 0x5a, 0x91, 0x68, 0x73, 0x2c, 0x69, 0xed, 0x8a, 0x2f, 0x05, 0x0c, 0x84, + 0xa7, 0x37, 0xea, 0xe6, 0x61, 0xc0, 0xef, 0x96, 0xc7, 0x1c, 0xd9, 0x7a, + 0x1e, 0x37, 0x8c, 0x23, 0x76, 0x5f, 0x06, 0xd9, 0x64, 0xb1, 0x80, 0xce, + 0x0f, 0xa6, 0x44, 0x19, 0xab, 0x23, 0xe6, 0x28, 0x3e, 0x9e, 0x2e, 0xbb, + 0xff, 0x5b, 0xd1, 0x93, 0x42, 0x47, 0xab, 0xad, 0xee, 0x22, 0x93, 0x4d, + 0x37, 0x39, 0x83, 0x95, 0xf5, 0x26, 0xd7, 0x39, 0x93, 0x18, 0xc3, 0xa3, + 0xa5, 0x02, 0x7a, 0xb8, 0x04, 0xd6, 0x36, 0xca, 0x13, 0x1a, 0x3f, 0x47, + 0x07, 0x83, 0x9d, 0x79, 0x69, 0x82, 0x3f, 0x87, 0xc0, 0x3b, 0x22, 0xdd, + 0x9e, 0x36, 0x9b, 0xae, 0xc1, 0xc3, 0xc8, 0xc2, 0xe5, 0x72, 0x73, 0xb5, + 0x2d, 0x64, 0xa9, 0x49, 0x00, 0x4d, 0x24, 0x2f, 0x4b, 0x7f, 0xb2, 0xd8, + 0x17, 0xcd, 0xf4, 0xc9, 0xf0, 0x3b, 0x7e, 0xf8, 0x49, 0x93, 0xb7, 0x9c, + 0xc3, 0xa2, 0x3c, 0x60, 0x62, 0xba, 0xcc, 0x5d, 0xfe, 0x1d, 0x4b, 0xcb, + 0x28, 0x80, 0xf8, 0x3c, 0x8b, 0xb1, 0xbd, 0x9d, 0x73, 0xfb, 0x67, 0x76, + 0xc7, 0x3a, 0xaa, 0x1d, 0x09, 0xca, 0xbd, 0x3b, 0xb9, 0xce, 0xf0, 0x4f, + 0xb0, 0xe5, 0xfd, 0xab, 0x0d, 0xf0, 0x0a, 0x8f, 0x07, 0x55, 0x6b, 0x6f, + 0xd9, 0xc8, 0x4b, 0x32, 0x05, 0x21, 0x21, 0x1e, 0x80, 0x1d, 0x69, 0x2a, + 0xf9, 0x5e, 0x58, 0xcb, 0xc5, 0xc6, 0x27, 0x55, 0x67, 0x16, 0x2f, 0x96, + 0x01, 0xb7, 0x6e, 0x20, 0x7d, 0x9a, 0x02, 0x74, 0xd6, 0xe5, 0x2f, 0xd4, + 0x4f, 0xbd, 0xee, 0x1a, 0xbb, 0x48, 0xc2, 0xbe, 0x79, 0xae, 0x43, 0xd7, + 0x42, 0xae, 0xbf, 0xc8, 0xd4, 0xef, 0x51, 0x37, 0xa8, 0xa2, 0x0b, 0x63, + 0xf2, 0x74, 0xac, 0x32, 0x0b, 0x26, 0x95, 0x9c, 0xe0, 0xf9, 0x64, 0x54, + 0x04, 0x06, 0x99, 0x4b, 0xb3, 0xc9, 0xfe, 0x31, 0xdc, 0x0c, 0xb0, 0x06, + 0x69, 0xaa, 0x7c, 0x00, 0x6b, 0xbf, 0x92, 0x89, 0x4c, 0x6b, 0x9b, 0x4d, + 0xe7, 0x81, 0xfa, 0xbf, 0xd3, 0xc6, 0x09, 0x98, 0xc6, 0x96, 0xa3, 0xf3, + 0x1e, 0x1e, 0x6d, 0x04, 0x0f, 0x6d, 0x7b, 0x98, 0x4a, 0x80, 0x5c, 0x99, + 0x44, 0x43, 0xb6, 0x26, 0x50, 0x0c, 0xc5, 0x26, 0xc4, 0x68, 0x50, 0xca, + 0xdf, 0x0c, 0xcf, 0x46, 0x9a, 0xaa, 0x93, 0x13, 0x80, 0x00, 0x43, 0x34, + 0x2f, 0x8a, 0xf0, 0x86, 0x0c, 0xa2, 0xfc, 0x9a, 0x4f, 0x54, 0x58, 0xb5, + 0xb9, 0x5e, 0xb1, 0x2b, 0xf8, 0xc8, 0x68, 0x0e, 0x05, 0xdf, 0xbb, 0x79, + 0xc8, 0x72, 0xb8, 0x5b, 0x72, 0x47, 0x05, 0xd2, 0x97, 0x9e, 0xa3, 0x39, + 0xeb, 0x8b, 0xfd, 0x99, 0x52, 0xba, 0x67, 0x5b, 0x40, 0x45, 0xde, 0x09, + 0xf4, 0x9f, 0x33, 0xc6, 0x53, 0x68, 0x93, 0xe4, 0xeb, 0x09, 0x97, 0x9d, + 0xec, 0xd0, 0x9a, 0xbb, 0xe6, 0x2b, 0xaa, 0xfe, 0x79, 0xa6, 0x71, 0xe2, + 0xea, 0x2d, 0xdd, 0x66, 0x41, 0xc4, 0xbe, 0xca, 0x62, 0x10, 0xa4, 0x23, + 0xa5, 0xcd, 0x68, 0x70, 0x51, 0xae, 0x8f, 0x4f, 0xe1, 0xa1, 0xa8, 0x35, + 0x4c, 0x37, 0xa4, 0xd6, 0x27, 0x32, 0x52, 0x1e, 0xfc, 0xbc, 0x48, 0x14, + 0x14, 0x1b, 0x05, 0x0d, 0xa1, 0xcd, 0x0b, 0xcd, 0x16, 0xbf, 0x88, 0xe2, + 0x20, 0x3a, 0x93, 0x8c, 0xcb, 0x9e, 0x97, 0x59, 0x36, 0x6b, 0x9c, 0x3f, + 0x71, 0x51, 0xdc, 0xf5, 0x05, 0x29, 0x23, 0x9b, 0x78, 0x23, 0x29, 0x63, + 0x35, 0x82, 0xda, 0x8e, 0xf2, 0xba, 0x2b, 0x61, 0xc7, 0xa4, 0x86, 0xc8, + 0xa2, 0xa3, 0xf4, 0x98, 0x49, 0x1c, 0x18, 0x20, 0x69, 0x98, 0xe4, 0xcf, + 0xc1, 0x98, 0x80, 0x48, 0x4e, 0x2f, 0x29, 0x0e, 0xdb, 0x0c, 0xf2, 0x3e, + 0x2a, 0xb5, 0x8d, 0xfe, 0x9e, 0x92, 0xe8, 0x82, 0xc8, 0x97, 0x16, 0xef, + 0xd1, 0x9a, 0x26, 0x8e, 0xb2, 0xd8, 0x58, 0x07, 0x94, 0x02, 0x66, 0x1c, + 0x7b, 0xec, 0xdb, 0x1b, 0xc7, 0x49, 0xfd, 0xe5, 0x40, 0x36, 0xe3, 0x4d, + 0x04, 0x80, 0x1b, 0xf5, 0xa5, 0x3a, 0x86, 0x66, 0x7f, 0xd5, 0xcf, 0xeb, + 0xc6, 0xf5, 0xa0, 0x6d, 0xe9, 0xef, 0xd4, 0x03, 0xce, 0x90, 0xd0, 0xbb, + 0x9e, 0xa8, 0xb0, 0x30, 0x23, 0xca, 0x4e, 0x59, 0x0d, 0x36, 0xf8, 0xd9, + 0xed, 0x47, 0x86, 0x53, 0x45, 0xa6, 0xb3, 0x27, 0x54, 0x81, 0x37, 0x75, + 0x85, 0xd3, 0x2a, 0x37, 0x60, 0xbe, 0xae, 0x7d, 0x57, 0xf1, 0xed, 0x71, + 0xe3, 0x8b, 0x3d, 0xab, 0x7c, 0x8c, 0xb9, 0xa4, 0xd3, 0x44, 0x14, 0x0c, + 0x4f, 0x9c, 0xf4, 0xfe, 0x62, 0x7f, 0x76, 0x02, 0x4f, 0x88, 0x53, 0xdd, + 0x70, 0x92, 0x00, 0x7f, 0xe4, 0x10, 0x48, 0x45, 0x8d, 0x58, 0xdf, 0x45, + 0x7a, 0x0a, 0x65, 0x80, 0x05, 0xb8, 0x2e, 0x64, 0x46, 0x1a, 0x69, 0x9a, + 0x99, 0xcf, 0x1c, 0x19, 0x87, 0xf2, 0x8e, 0x82, 0x48, 0x39, 0x0a, 0x32, + 0x2a, 0x1f, 0x4d, 0x2f, 0x5e, 0x75, 0x6b, 0x9b, 0x85, 0x7c, 0xf7, 0xc5, + 0x89, 0x5d, 0x1d, 0xc3, 0x0d, 0x99, 0x9c, 0x71, 0x15, 0x33, 0x26, 0x6c, + 0xe5, 0x56, 0xb8, 0xbe, 0x92, 0x6c, 0x4a, 0xcb, 0xe7, 0xbe, 0xad, 0x21, + 0x3b, 0xfc, 0x10, 0xb3, 0x51, 0x71, 0xf2, 0xe3, 0xbb, 0x33, 0x97, 0x2c, + 0x4a, 0x93, 0xbf, 0x8d, 0x31, 0x40, 0x25, 0x92, 0xa5, 0x17, 0x5a, 0xee, + 0xd0, 0xc9, 0xfc, 0xfc, 0x24, 0xc8, 0x54, 0x4b, 0x12, 0x3f, 0xb3, 0x9e, + 0x30, 0x1e, 0xcd, 0xaa, 0xb6, 0xef, 0x50, 0x8a, 0x38, 0x90, 0x73, 0xba, + 0xc1, 0x89, 0x1a, 0xae, 0xc8, 0x3a, 0xda, 0xc0, 0xa0, 0x18, 0xa8, 0x02, + 0x78, 0xc3, 0x15, 0xa2, 0x3c, 0x47, 0xf5, 0x35, 0x1b, 0xc7, 0x4a, 0x4b, + 0x2f, 0xc9, 0xb8, 0xe6, 0xab, 0x21, 0x5f, 0x6c, 0x58, 0x1a, 0xe6, 0xa2, + 0x3a, 0xeb, 0x88, 0xfc, 0x24, 0x82, 0xd4, 0xcf, 0x96, 0x1b, 0x37, 0xd4, + 0x03, 0xba, 0x8c, 0x63, 0x4e, 0x1d, 0xf5, 0x82, 0xbf, 0x07, 0xd2, 0xbb, + 0x30, 0x84, 0x6e, 0x3b, 0x72, 0x29, 0x88, 0xf7, 0x9c, 0x6c, 0xaf, 0x20, + 0x6a, 0x9b, 0x63, 0x85, 0x3e, 0x89, 0x5b, 0x49, 0x07, 0x7e, 0xef, 0xc1, + 0x76, 0xe6, 0x9f, 0x74, 0x60, 0x35, 0xa2, 0x40, 0x41, 0x9d, 0xb9, 0xf8, + 0x01, 0x48, 0xc6, 0x02, 0xab, 0x89, 0xa7, 0xda, 0xff, 0xe0, 0xd9, 0x7b, + 0x1b, 0x68, 0x71, 0x20, 0x67, 0xa5, 0xc5, 0x4c, 0x3b, 0xc6, 0x39, 0x13, + 0xd1, 0x02, 0x77, 0xa7, 0xd9, 0xf6, 0x8b, 0x2f, 0x1b, 0x68, 0xf8, 0xab, + 0x51, 0x9c, 0x7a, 0xa8, 0xd6, 0x54, 0x14, 0xe7, 0xc5, 0x8e, 0xf9, 0x30, + 0x63, 0xa6, 0x70, 0x90, 0xc2, 0x10, 0x07, 0x01, 0x97, 0x1b, 0x9b, 0x3b, + 0xe1, 0x76, 0xdd, 0x53, 0x33, 0xd3, 0xbc, 0x84, 0x95, 0x14, 0xab, 0xe5, + 0xa3, 0xab, 0xe2, 0x4d, 0xb9, 0xc5, 0xfa, 0xcf, 0x4b, 0x19, 0x31, 0xb5, + 0x81, 0x49, 0x81, 0xca, 0x5e, 0x25, 0x00, 0xf2, 0x91, 0x6e, 0x11, 0x01, + 0xb0, 0x83, 0x9e, 0xb6, 0x80, 0xfd, 0x2f, 0x2d, 0xda, 0xf1, 0xa3, 0xa3, + 0xd9, 0x10, 0x2d, 0x22, 0x70, 0x43, 0xc3, 0x8d, 0xee, 0x0b, 0x60, 0x09, + 0x26, 0xef, 0x87, 0x08, 0xad, 0x5d, 0xab, 0xd6, 0xf7, 0xa2, 0xa0, 0x08, + 0xbf, 0xc4, 0xef, 0xb5, 0xb6, 0x69, 0x7c, 0x5d, 0x74, 0x7a, 0xc5, 0x55, + 0xc2, 0xd3, 0x02, 0xfc, 0x4f, 0x85, 0xf0, 0x7b, 0x97, 0x7e, 0xe6, 0x83, + 0xd8, 0x62, 0x14, 0x3a, 0x9d, 0xc2, 0x92, 0x32, 0x7e, 0x1a, 0x7e, 0xf2, + 0x9e, 0xff, 0xe6, 0xe0, 0x31, 0xc8, 0x15, 0x23, 0xea, 0x2f, 0x44, 0x45, + 0x36, 0xe6, 0xf8, 0xe7, 0x06, 0x48, 0xa1, 0xce, 0xfb, 0x2b, 0x70, 0x57, + 0x6a, 0xe7, 0xec, 0x00, 0xae, 0xeb, 0x55, 0x23, 0xef, 0xa2, 0xec, 0xa4, + 0x41, 0xff, 0xf8, 0x17, 0xd3, 0x09, 0x37, 0xc9, 0x29, 0x59, 0xca, 0x54, + 0x14, 0xe7, 0x67, 0x9e, 0x0f, 0xd6, 0x62, 0x86, 0x3a, 0x35, 0x89, 0x01, + 0x72, 0x55, 0x2a, 0xa1, 0xd2, 0xa8, 0xa0, 0x85, 0x06, 0x6a, 0x01, 0xa6, + 0x9e, 0x44, 0xec, 0x3a, 0x29, 0xec, 0xff, 0xb3, 0x5a, 0xf2, 0x7d, 0x61, + 0x65, 0x72, 0xe8, 0x42, 0xe3, 0x96, 0x33, 0x31, 0x4b, 0xa1, 0xa1, 0x40, + 0x41, 0xb1, 0xe4, 0x35, 0xee, 0xe0, 0xcf, 0xe6, 0x30, 0xeb, 0xc2, 0xc8, + 0x53, 0x25, 0x79, 0xf9, 0xd7, 0x62, 0xe0, 0x33, 0x3a, 0x39, 0x8f, 0x71, + 0xd9, 0x13, 0x87, 0xb0, 0xe6, 0xb1, 0x11, 0x91, 0x49, 0xf2, 0x41, 0xd7, + 0xcc, 0xcd, 0x72, 0xae, 0x3b, 0x69, 0xba, 0x16, 0x38, 0x24, 0x40, 0x37, + 0xb5, 0x63, 0xca, 0x1c, 0x62, 0xf3, 0x63, 0x6c, 0xd6, 0xe3, 0x28, 0xb2, + 0x01, 0x70, 0xbc, 0x48, 0x08, 0x80, 0xf9, 0x34, 0xaa, 0xb0, 0xfd, 0xb8, + 0x2d, 0x68, 0x28, 0xb7, 0xad, 0x96, 0x7f, 0x4d, 0xdc, 0x63, 0x1a, 0xfb, + 0x77, 0xcf, 0xcf, 0xc4, 0x28, 0x3a, 0x02, 0x51, 0x61, 0xa8, 0x85, 0x93, + 0xa8, 0x48, 0xf7, 0x24, 0x87, 0x0e, 0xe5, 0x2c, 0x1d, 0x9f, 0x04, 0xee, + 0x13, 0x58, 0x2d, 0x20, 0x70, 0xa0, 0x2b, 0x4a, 0xce, 0xfc, 0xc6, 0x4d, + 0x80, 0x82, 0xd0, 0x24, 0x4c, 0x87, 0x0c, 0x6b, 0x27, 0x1b, 0xc0, 0xe9, + 0x4c, 0x00, 0x37, 0xf5, 0x75, 0x3c, 0xcd, 0x83, 0xba, 0xeb, 0xe7, 0xce, + 0xea, 0x30, 0xa8, 0x80, 0xd6, 0x82, 0x07, 0xc7, 0x25, 0xcc, 0x6d, 0x50, + 0x45, 0x7f, 0x31, 0xed, 0x8d, 0x50, 0x41, 0x3a, 0x83, 0x83, 0x14, 0x8b, + 0xe9, 0x7e, 0x07, 0x75, 0x41, 0x04, 0x2d, 0xe7, 0x70, 0xc8, 0x1f, 0xdd, + 0x89, 0xe3, 0xe3, 0xbe, 0x29, 0xc0, 0x0d, 0x7d, 0xf1, 0x54, 0x44, 0xdd, + 0x60, 0x2b, 0xd9, 0x79, 0x44, 0xf4, 0x4c, 0xaf, 0x5e, 0x2f, 0x0c, 0xd5, + 0x39, 0xe0, 0xf0, 0xa5, 0xf0, 0x25, 0xc9, 0x39, 0x9d, 0x6e, 0x5a, 0x43, + 0x7b, 0xe7, 0xa9, 0x63, 0x74, 0xd1, 0x74, 0x2e, 0x1f, 0x01, 0x2a, 0x5b, + 0x7d, 0xa4, 0xe4, 0x90, 0xb5, 0xc2, 0x55, 0x01, 0x6f, 0x9a, 0xeb, 0x30, + 0x3e, 0xac, 0x34, 0x63, 0x1b, 0x1e, 0x86, 0x9f, 0x0e, 0x14, 0xe4, 0x76, + 0x3c, 0x33, 0x63, 0x4a, 0xe6, 0xf7, 0x1e, 0xec, 0xaa, 0xad, 0x4b, 0x2c, + 0xc4, 0x6c, 0x09, 0x24, 0x94, 0xdb, 0x30, 0x6c, 0xf6, 0x8d, 0x98, 0xb2, + 0xe2, 0x69, 0x40, 0x0a, 0xcf, 0x0e, 0xac, 0x76, 0xbe, 0x71, 0xa7, 0x22, + 0x43, 0xd9, 0xbf, 0xcf, 0x62, 0x34, 0x64, 0x99, 0xc0, 0xcb, 0x10, 0xe4, + 0x4e, 0x37, 0xab, 0x3d, 0x60, 0x18, 0x6e, 0xfd, 0xd2, 0xcb, 0xc6, 0x7e, + 0x7d, 0x3b, 0x4f, 0xcd, 0xd1, 0xc6, 0xf7, 0x22, 0x17, 0xea, 0x09, 0x1d, + 0x51, 0x64, 0x83, 0x0b, 0xa3, 0x6d, 0x16, 0x10, 0xa2, 0x16, 0x8e, 0x2a, + 0x6f, 0xdd, 0xb8, 0xb4, 0xa1, 0x0e, 0xd9, 0x9c, 0x7d, 0x0b, 0x5f, 0xe7, + 0xac, 0x93, 0x4e, 0x9d, 0xe2, 0xab, 0x14, 0x43, 0xdc, 0x3d, 0xe5, 0x5b, + 0xd6, 0x06, 0x64, 0xf4, 0x02, 0x1a, 0x7f, 0x54, 0x3f, 0x36, 0x16, 0xbf, + 0x9a, 0x78, 0x74, 0xa2, 0x02, 0x0b, 0xbe, 0x01, 0xf1, 0x70, 0xa0, 0xd1, + 0x6d, 0x94, 0xec, 0xba, 0x17, 0x6f, 0x56, 0xf7, 0x9e, 0x3e, 0x27, 0x0d, + 0x64, 0xcd, 0x65, 0x6f, 0x2d, 0x26, 0xf8, 0x2b, 0x2b, 0xf3, 0xf0, 0x02, + 0x55, 0xd9, 0xef, 0x06, 0xe2, 0xcb, 0xff, 0x5b, 0xd5, 0x48, 0xa2, 0x5e, + 0xb5, 0x0a, 0x86, 0x72, 0x4c, 0xc4, 0xb7, 0x35, 0xc5, 0xd3, 0xc8, 0x18, + 0xf6, 0x5f, 0x7b, 0x82, 0x77, 0x0f, 0xab, 0x3d, 0xc9, 0xce, 0x9c, 0xd4, + 0xff, 0x38, 0x6c, 0xfb, 0xc3, 0x70, 0xe5, 0xc2, 0xc1, 0xa4, 0x81, 0x6b, + 0x81, 0x98, 0x95, 0xa6, 0xd7, 0x0f, 0x1f, 0x84, 0x58, 0xd8, 0x4a, 0xb5, + 0xc2, 0xcd, 0xa2, 0xe7, 0xb7, 0xbf, 0xd2, 0xc4, 0x71, 0xc7, 0xba, 0x76, + 0x0c, 0xcb, 0xaf, 0x92, 0xde, 0x75, 0x99, 0xec, 0xee, 0xe7, 0x38, 0x7a, + 0x18, 0x2a, 0x39, 0x4f, 0x94, 0x7d, 0xc2, 0xd6, 0xd1, 0x90, 0xcd, 0x4c, + 0xc5, 0x59, 0x78, 0x21, 0x4f, 0xe3, 0x1e, 0xde, 0x06, 0x71, 0x94, 0xdc, + 0xb5, 0x41, 0xd2, 0xe4, 0x7e, 0x24, 0x14, 0xe0, 0xdf, 0x01, 0x9d, 0xb0, + 0xa7, 0x69, 0xdb, 0xb4, 0x74, 0x85, 0xcf, 0x8d, 0xbd, 0x27, 0x09, 0x40, + 0x82, 0xaa, 0x86, 0x68, 0x84, 0xbc, 0x82, 0x44, 0x8f, 0x57, 0x4a, 0x8e, + 0x5d, 0x88, 0x60, 0x8d, 0x14, 0xfb, 0xc3, 0x23, 0x9f, 0x09, 0x5a, 0xa2, + 0x91, 0x32, 0xf4, 0xd1, 0x99, 0xf4, 0x12, 0x6b, 0x00, 0x4d, 0x0b, 0xf9, + 0x14, 0xa0, 0x45, 0x6a, 0x96, 0x72, 0x27, 0xe9, 0x88, 0xd8, 0x88, 0x4a, + 0x71, 0xb2, 0x32, 0x85, 0xf3, 0x55, 0x79, 0xae, 0x07, 0x17, 0x2c, 0x33, + 0x1f, 0x5f, 0xf4, 0xac, 0xb5, 0xaf, 0x80, 0x44, 0x86, 0x61, 0xc2, 0xf9, + 0xb4, 0x03, 0xae, 0x3a, 0x4d, 0xed, 0xa7, 0xcd, 0x45, 0x5b, 0x2b, 0xba, + 0x90, 0x7e, 0x07, 0xd9, 0x30, 0xb0, 0xd3, 0xd7, 0x3a, 0xa3, 0xff, 0xd1, + 0x9a, 0x1e, 0x5b, 0xf0, 0xb2, 0x70, 0x59, 0x37, 0x38, 0xc2, 0x31, 0x1b, + 0x0f, 0xe0, 0xec, 0x80, 0x4f, 0x3b, 0x04, 0x99, 0x2b, 0x88, 0x6b, 0xea, + 0x38, 0xb5, 0x3f, 0xab, 0x26, 0x0a, 0x21, 0x17, 0x96, 0x90, 0xe0, 0x85, + 0x1f, 0xb7, 0x42, 0x98, 0xbb, 0x5b, 0xc1, 0x7d, 0xa1, 0xaf, 0x95, 0x1f, + 0x52, 0x94, 0x77, 0x6f, 0x12, 0xac, 0xd4, 0x22, 0xf5, 0x9f, 0x06, 0x08, + 0x13, 0x44, 0x63, 0x1c, 0x5f, 0x16, 0x64, 0x94, 0xf0, 0xd8, 0x83, 0x6f, + 0x82, 0x6a, 0xe8, 0x93, 0x8e, 0x95, 0xf5, 0x0e, 0x44, 0x46, 0xfd, 0x5b, + 0xf4, 0x4f, 0xed, 0x38, 0xff, 0xef, 0xde, 0x7c, 0x27, 0xa4, 0xf9, 0xfa, + 0x8d, 0xbe, 0x63, 0xdb, 0x61, 0x7d, 0x47, 0xb5, 0x5c, 0x63, 0x32, 0x1a, + 0x41, 0x87, 0x70, 0x7f, 0x54, 0x80, 0x4e, 0x1c, 0x87, 0xa7, 0x14, 0x42, + 0xfb, 0x77, 0x37, 0xce, 0xf4, 0x74, 0xb8, 0x56, 0xcc, 0x73, 0x62, 0xc6, + 0xdd, 0x56, 0x4e, 0x78, 0x30, 0x93, 0x24, 0x31, 0x34, 0x17, 0xe4, 0x9b, + 0xef, 0xec, 0x9f, 0x50, 0x8d, 0x36, 0xa7, 0x6d, 0x20, 0xe6, 0x3a, 0x67, + 0x95, 0x22, 0x3a, 0xf2, 0xdc, 0x16, 0x16, 0x00, 0x3f, 0x31, 0xa8, 0xfa, + 0xd2, 0x79, 0x03, 0xbe, 0x85, 0x1c, 0xe7, 0x1f, 0x11, 0x6f, 0x22, 0x0c, + 0x25, 0xc0, 0xb0, 0x12, 0x02, 0x30, 0xad, 0x89, 0xce, 0xce, 0xe8, 0x0d, + 0xe7, 0x33, 0x42, 0x46, 0x00, 0xea, 0x7c, 0x57, 0x79, 0xf4, 0xe2, 0xe6, + 0xed, 0x4d, 0x76, 0x58, 0x50, 0x5c, 0x44, 0x24, 0x80, 0x48, 0x1e, 0xd8, + 0x64, 0x3a, 0x73, 0xd8, 0x10, 0x4a, 0x0c, 0x2a, 0xbc, 0xd5, 0x75, 0xc8, + 0x92, 0x43, 0xbf, 0x36, 0x22, 0x8e, 0xa2, 0x33, 0xf5, 0x77, 0x1c, 0x93, + 0x4e, 0xc7, 0x48, 0x6c, 0x35, 0x8a, 0x6f, 0xb1, 0xca, 0xcb, 0x89, 0xc7, + 0x5a, 0xa9, 0x5e, 0x45, 0x75, 0x46, 0x35, 0x26, 0x29, 0x51, 0x5f, 0xde, + 0xf6, 0xfc, 0x8e, 0x39, 0x64, 0x1e, 0xd3, 0x66, 0x65, 0xdb, 0x6f, 0x43, + 0xe3, 0xb0, 0xf6, 0xb9, 0x17, 0x5e, 0x46, 0x5e, 0xb6, 0xc6, 0xd4, 0xa6, + 0xc2, 0x00, 0x55, 0x3a, 0x44, 0xe3, 0x32, 0x0b, 0x81, 0x69, 0x75, 0xa2, + 0x74, 0xcd, 0x15, 0x8e, 0x40, 0x46, 0x38, 0x2c, 0xb2, 0x95, 0x4a, 0x67, + 0x0d, 0x73, 0xe1, 0xe2, 0x49, 0xaa, 0xd0, 0x1a, 0xba, 0x9d, 0x3c, 0x43, + 0x4e, 0xb8, 0xf0, 0xcb, 0x20, 0xc5, 0xaa, 0x34, 0xdb, 0x96, 0x8e, 0xf0, + 0xda, 0xc8, 0x1a, 0x93, 0xac, 0x82, 0xb4, 0xf5, 0x22, 0x12, 0xea, 0xf1, + 0x4d, 0x74, 0x27, 0x77, 0x9c, 0x84, 0xae, 0xae, 0x2d, 0x6b, 0x4a, 0xbc, + 0xd9, 0x0d, 0x81, 0xfa, 0x93, 0xea, 0x09, 0x45, 0xa7, 0xb0, 0x06, 0xde, + 0xb0, 0x30, 0xf7, 0xaf, 0x44, 0xef, 0x5a, 0x89, 0x9e, 0x04, 0x23, 0x25, + 0x7e, 0xf1, 0x4d, 0xf1, 0x8f, 0xda, 0x25, 0xd7, 0xed, 0xd8, 0x08, 0x83, + 0x4e, 0xbf, 0x36, 0x98, 0x9f, 0xf7, 0x30, 0x64, 0x48, 0x25, 0xb6, 0xa2, + 0xe0, 0x03, 0x06, 0xeb, 0xb4, 0x00, 0x08, 0x57, 0xef, 0x5f, 0xdf, 0xd0, + 0x02, 0x7d, 0xbe, 0x98, 0x2b, 0x90, 0x16, 0xd4, 0x8f, 0x48, 0x05, 0xe1, + 0x03, 0xd2, 0xba, 0x0e, 0xaf, 0x59, 0xcb, 0xd9, 0x78, 0xae, 0x77, 0xd5, + 0x70, 0x2b, 0x1d, 0x9f, 0x51, 0xcf, 0xbc, 0xb3, 0xce, 0x90, 0x8e, 0xcb, + 0x22, 0x36, 0x43, 0x05, 0x7c, 0x0f, 0x97, 0x67, 0xf4, 0x18, 0x87, 0x32, + 0x55, 0x8a, 0xa7, 0xf0, 0x41, 0x37, 0x3b, 0x02, 0x64, 0x5d, 0x27, 0x27, + 0x25, 0x93, 0x38, 0x94, 0xac, 0xc3, 0x5a, 0x4c, 0x96, 0xec, 0xbe, 0x9c, + 0x9b, 0x18, 0xec, 0xc5, 0x64, 0x4c, 0xc2, 0x3c, 0x5c, 0x04, 0x3f, 0x16, + 0x5e, 0xcd, 0x55, 0x10, 0x3f, 0xbd, 0xd0, 0x7f, 0x74, 0xf0, 0xce, 0xd3, + 0x4a, 0x11, 0xbf, 0xb5, 0xad, 0xf0, 0x93, 0x2b, 0x20, 0x12, 0x45, 0x48, + 0x07, 0x6b, 0x86, 0x92, 0x33, 0x71, 0xbe, 0xcc, 0x5f, 0xbf, 0x4e, 0xe6, + 0xf3, 0xc2, 0x5d, 0x5a, 0xd8, 0x36, 0xe3, 0x98, 0xea, 0xd5, 0x9a, 0xdc, + 0x83, 0x2f, 0x47, 0x90, 0xcb, 0xeb, 0x05, 0x06, 0x56, 0x51, 0x25, 0x48, + 0x01, 0xd7, 0xdb, 0xa0, 0x28, 0x6e, 0x1c, 0xad, 0xd5, 0x3d, 0x5c, 0xc7, + 0x22, 0x13, 0x77, 0xc8, 0xdf, 0xaf, 0x8a, 0x82, 0x04, 0x4d, 0xd2, 0x5a, + 0x48, 0xe4, 0x40, 0xb5, 0xe8, 0x04, 0x87, 0xf2, 0xb0, 0x8d, 0xc1, 0x20, + 0x45, 0xc6, 0x66, 0x07, 0x7c, 0xec, 0x38, 0x21, 0x0a, 0xad, 0x22, 0xb5, + 0xb1, 0xff, 0x92, 0x6d, 0xb1, 0x32, 0x56, 0x2c, 0xa7, 0x7b, 0xc2, 0x77, + 0x6e, 0xfb, 0x9e, 0x6b, 0xb9, 0xfe, 0x42, 0x52, 0xa1, 0x3b, 0x1e, 0xc7, + 0x6d, 0x39, 0x8e, 0x5a, 0x22, 0x16, 0xdc, 0xc2, 0x52, 0x5e, 0x85, 0xb3, + 0x63, 0x33, 0x26, 0xc9, 0x88, 0xee, 0x69, 0x0c, 0x80, 0x37, 0xee, 0x1f, + 0x82, 0x07, 0x78, 0x65, 0x01, 0x4e, 0x59, 0x3d, 0x00, 0x85, 0xb4, 0x77, + 0x39, 0x7a, 0x20, 0x58, 0xd7, 0xd0, 0xee, 0xfb, 0x29, 0xd6, 0x53, 0xef, + 0x01, 0x81, 0x43, 0x53, 0x2d, 0xe4, 0xb5, 0x55, 0xc6, 0x4c, 0x62, 0x75, + 0x67, 0x48, 0x0f, 0x96, 0x1e, 0xd1, 0x51, 0xac, 0x62, 0xff, 0x70, 0x0d, + 0xd4, 0x89, 0x6c, 0x02, 0x5b, 0xc5, 0xb3, 0x42, 0xca, 0x19, 0x50, 0x55, + 0xb1, 0x2e, 0xd4, 0x8e, 0x9b, 0x4d, 0x27, 0x2d, 0x68, 0x55, 0x9a, 0xdb, + 0x8d, 0xfc, 0xbd, 0xcd, 0xf5, 0xd9, 0xcf, 0x08, 0x18, 0xf8, 0x0b, 0x5e, + 0xc8, 0x48, 0xa0, 0xa0, 0xef, 0x66, 0x34, 0xf2, 0x89, 0xb6, 0x8f, 0xbb, + 0xa1, 0x69, 0x77, 0x09, 0xe3, 0xd8, 0x3f, 0x11, 0x1f, 0x9b, 0xad, 0xae, + 0x5c, 0xc8, 0xf0, 0xfa, 0x8f, 0x92, 0x12, 0x7a, 0x78, 0x53, 0xa7, 0xce, + 0x63, 0x2b, 0xf9, 0x7b, 0x15, 0x3c, 0x3e, 0xe6, 0xae, 0x39, 0x09, 0x31, + 0xb7, 0xca, 0x56, 0x4e, 0x0d, 0x7d, 0x76, 0x7a, 0x44, 0x81, 0x5d, 0x04, + 0x1d, 0x3a, 0xfc, 0xeb, 0x7b, 0x89, 0x83, 0x86, 0x82, 0x50, 0xf1, 0x33, + 0xb2, 0xff, 0x8f, 0x00, 0x29, 0x60, 0x95, 0x57, 0xf2, 0x47, 0x07, 0xd7, + 0x56, 0x58, 0x2b, 0x4a, 0x43, 0x84, 0xf8, 0xd2, 0xd1, 0x0a, 0x6d, 0x70, + 0xbe, 0xb2, 0x4e, 0x3a, 0xaa, 0x30, 0xab, 0xad, 0x13, 0xfb, 0x80, 0x89, + 0x13, 0xdd, 0xe4, 0x01, 0x28, 0x04, 0x84, 0x4b, 0xe5, 0x17, 0x16, 0xfc, + 0xe6, 0xb4, 0x9a, 0x4d, 0x35, 0xe2, 0xdd, 0x1c, 0x8e, 0xdc, 0x97, 0xab, + 0xc9, 0x1d, 0x45, 0x32, 0x0f, 0xa5, 0x08, 0xda, 0xf7, 0xe0, 0x64, 0x55, + 0xe5, 0x78, 0xab, 0xe7, 0x6c, 0x6f, 0x5e, 0x56, 0x09, 0x3b, 0x32, 0x75, + 0x17, 0x4b, 0x39, 0x06, 0x9f, 0xfc, 0x40, 0x71, 0xaa, 0x57, 0xb3, 0x36, + 0xca, 0x70, 0xcc, 0xdf, 0xd5, 0xd2, 0xa1, 0xcd, 0x4b, 0xae, 0x58, 0xfe, + 0x12, 0xbf, 0x5f, 0x8f, 0x44, 0x06, 0x08, 0x73, 0xc5, 0x66, 0xed, 0x4a, + 0x70, 0xe6, 0x66, 0x1e, 0x55, 0x2f, 0x0e, 0x65, 0x81, 0xd0, 0xcb, 0x9f, + 0xd4, 0x48, 0xfe, 0x72, 0xc1, 0xd4, 0xa1, 0xc1, 0x05, 0x68, 0x28, 0x0c, + 0xdc, 0xa1, 0x85, 0x8c, 0xac, 0xe4, 0x2e, 0x9d, 0x95, 0xcc, 0x0e, 0xc6, + 0xec, 0xae, 0x7d, 0xe4, 0x0f, 0x02, 0x81, 0x6b, 0x84, 0x02, 0xf9, 0xbb, + 0xb2, 0xb6, 0x30, 0x4d, 0x29, 0xaf, 0x52, 0x31, 0xb7, 0xca, 0xf7, 0x10, + 0xdf, 0xb3, 0x7d, 0xf9, 0x3e, 0x45, 0xc9, 0x97, 0x15, 0xaf, 0xf4, 0x49, + 0x46, 0x52, 0xbe, 0x7e, 0x68, 0x8c, 0xf7, 0x3a, 0x00, 0x9e, 0x56, 0xad, + 0xd0, 0x9b, 0x87, 0x74, 0xf0, 0x25, 0x8e, 0xae, 0x7a, 0xa4, 0x71, 0xfc, + 0x91, 0xec, 0xbc, 0x82, 0x56, 0xdc, 0xb8, 0xb2, 0x20, 0x57, 0x60, 0x8f, + 0x1e, 0xf2, 0x84, 0x94, 0x15, 0xb3, 0x19, 0x47, 0x55, 0x8a, 0xd0, 0xa4, + 0x2b, 0x3d, 0x47, 0x7e, 0x4c, 0xab, 0x36, 0xf0, 0x90, 0x5a, 0x27, 0xca, + 0x0b, 0x23, 0xcc, 0x37, 0x77, 0x21, 0x1e, 0x57, 0xdc, 0xaa, 0x89, 0x73, + 0xf7, 0x26, 0x79, 0x38, 0x37, 0xaa, 0x11, 0x28, 0x8c, 0xa0, 0x33, 0xae, + 0x3d, 0x65, 0x8b, 0x53, 0x47, 0x02, 0x65, 0xa0, 0x8b, 0xd0, 0xad, 0xd1, + 0xa7, 0xe0, 0x46, 0xb3, 0x4d, 0x5b, 0x80, 0x3e, 0xe6, 0x7c, 0x47, 0xe1, + 0x14, 0x74, 0xd2, 0x7d, 0xe6, 0xfe, 0x00, 0x90, 0x57, 0x3f, 0xd6, 0xd1, + 0x05, 0x5d, 0x72, 0xa6, 0x97, 0xf3, 0x41, 0xbe, 0x9a, 0x8d, 0x93, 0x83, + 0x88, 0xdc, 0xf7, 0x68, 0x1a, 0x85, 0x15, 0xf1, 0x0e, 0xca, 0xaa, 0x05, + 0x24, 0xbd, 0xd8, 0x2f, 0xbd, 0xb2, 0x25, 0x26, 0x0c, 0x54, 0xd0, 0x6c, + 0xad, 0xef, 0x46, 0x3c, 0xc7, 0xd2, 0x9a, 0xfa, 0x95, 0x1c, 0x12, 0xd3, + 0x72, 0x3f, 0x3c, 0x23, 0xf3, 0xf6, 0x8a, 0x30, 0x58, 0xa5, 0xd4, 0xfd, + 0x31, 0x2f, 0xaf, 0x2d, 0x83, 0x8b, 0xe0, 0xab, 0x4f, 0x85, 0xe4, 0xfa, + 0xde, 0xa3, 0x52, 0x99, 0x4e, 0x3e, 0xa8, 0x0f, 0x29, 0x9f, 0x65, 0x4d, + 0x96, 0x62, 0x75, 0x53, 0xbd, 0x44, 0x96, 0xa9, 0xe3, 0x6b, 0xb1, 0xba, + 0x70, 0xed, 0x85, 0x35, 0xee, 0x70, 0x5f, 0xab, 0x8a, 0xb0, 0xdf, 0xf9, + 0x2e, 0x34, 0x56, 0xa7, 0x31, 0xd1, 0x10, 0x1b, 0x90, 0x61, 0x96, 0xc4, + 0x62, 0x6a, 0xe8, 0x79, 0x11, 0xf1, 0x99, 0x09, 0xa2, 0x0b, 0xb7, 0x92, + 0xb2, 0xae, 0xdc, 0x4a, 0xe0, 0x2a, 0x63, 0xf0, 0xc8, 0x2b, 0x6f, 0x0b, + 0x2b, 0xc4, 0x37, 0x46, 0x8b, 0xa6, 0xbf, 0x1f, 0x26, 0xd3, 0xe7, 0x25, + 0x95, 0xbd, 0xb8, 0x3f, 0x15, 0x3e, 0x7e, 0xd9, 0x0c, 0x97, 0x08, 0x4d, + 0xcf, 0x14, 0x79, 0x17, 0xfc, 0x3b, 0x9d, 0x80, 0x48, 0x49, 0x28, 0x87, + 0x53, 0xca, 0xad, 0xc3, 0x11, 0xf6, 0x60, 0x0c, 0xf1, 0xd2, 0x35, 0x40, + 0x5c, 0xc2, 0x89, 0xff, 0xf8, 0xb8, 0x2e, 0x29, 0xbf, 0xe6, 0x0f, 0x31, + 0xac, 0x1a, 0xe0, 0x66, 0x12, 0x90, 0x08, 0x8d, 0x28, 0x16, 0xf6, 0x6f, + 0x36, 0x3a, 0xfa, 0xad, 0xe8, 0x6f, 0x4f, 0x3d, 0xc1, 0xda, 0xf9, 0xfa, + 0xa6, 0x85, 0xcf, 0x62, 0x13, 0x23, 0x17, 0xfb, 0x2f, 0xd7, 0x83, 0xbd, + 0x42, 0xbc, 0x66, 0x4e, 0xe8, 0xe1, 0x79, 0x8b, 0x05, 0x47, 0xc3, 0x77, + 0x4d, 0x1b, 0xc1, 0x47, 0xa7, 0xd1, 0x66, 0x82, 0x16, 0x58, 0x8e, 0xb3, + 0x8c, 0xa4, 0x97, 0x31, 0x1a, 0x08, 0x44, 0xdc, 0xc4, 0xa8, 0xd3, 0x57, + 0x4a, 0x33, 0x0e, 0x8a, 0xb6, 0x40, 0x3d, 0x53, 0xdc, 0x6e, 0xa3, 0x8e, + 0x00, 0x01, 0x30, 0xb8, 0x10, 0x90, 0xbb, 0x08, 0xb5, 0xc9, 0x81, 0x41, + 0x21, 0xaf, 0xda, 0x56, 0x3c, 0x05, 0x89, 0x76, 0xde, 0x1d, 0x58, 0xab, + 0xa4, 0x33, 0x29, 0xe6, 0xe3, 0x51, 0xbd, 0x69, 0xe5, 0xbd, 0xd2, 0x29, + 0xb5, 0xad, 0x9b, 0x03, 0xbd, 0x1c, 0x2c, 0x01, 0xc2, 0xf5, 0x05, 0x1a, + 0x30, 0xaa, 0xd5, 0x9e, 0xb9, 0x69, 0x04, 0x05, 0x80, 0x81, 0x7c, 0xba, + 0x6b, 0x5f, 0xa3, 0x7d, 0x95, 0x68, 0x13, 0xcf, 0x4a, 0xdd, 0x55, 0xe1, + 0x2a, 0x03, 0x61, 0x9b, 0x1f, 0xdc, 0x28, 0x92, 0x3b, 0x46, 0x2a, 0x99, + 0x17, 0xfd, 0x45, 0x74, 0x6a, 0x45, 0x13, 0x0d, 0xa5, 0xf1, 0x28, 0xef, + 0x5f, 0x8a, 0x25, 0x60, 0x4c, 0x55, 0xb6, 0xc2, 0xd5, 0x50, 0xa4, 0x1e, + 0x68, 0xcb, 0xcd, 0x4c, 0xcf, 0xf1, 0x1f, 0xd1, 0xcd, 0x16, 0xc0, 0x6f, + 0x5e, 0xab, 0xd2, 0x72, 0xc0, 0xae, 0xe9, 0xb1, 0xfa, 0xb5, 0xae, 0xcd, + 0x9a, 0xca, 0x5b, 0x9c, 0x39, 0x11, 0xf0, 0x46, 0x44, 0xcf, 0x42, 0xa1, + 0x2d, 0x91, 0xcc, 0x4d, 0xb5, 0x98, 0xae, 0x5a, 0x40, 0x96, 0x8b, 0x53, + 0x36, 0x56, 0x21, 0x2d, 0xd8, 0x5d, 0xa9, 0x01, 0x6e, 0xdd, 0x82, 0x19, + 0x0a, 0x1a, 0x81, 0x0b, 0x0c, 0x5f, 0x9b, 0x69, 0x57, 0x77, 0xad, 0x39, + 0x26, 0x16, 0xc5, 0x5c, 0x19, 0x2b, 0x6f, 0x4c, 0x05, 0x83, 0x78, 0xcd, + 0xbc, 0x3c, 0x51, 0x6b, 0x19, 0xe9, 0xb5, 0x5f, 0xf2, 0x07, 0x8d, 0x3d, + 0x53, 0x8e, 0xb6, 0x2f, 0x6a, 0xe9, 0xde, 0x8b, 0xf8, 0x25, 0xc3, 0xbb, + 0x5a, 0x43, 0x93, 0x93, 0x77, 0x3f, 0xa3, 0x1a, 0x5e, 0x07, 0xc8, 0x3b, + 0x74, 0x32, 0x66, 0x03, 0x3f, 0xe4, 0x27, 0x3c, 0x38, 0x44, 0xb4, 0x57, + 0x4d, 0x7f, 0x0b, 0xcd, 0x4a, 0xed, 0x4d, 0x7d, 0x46, 0x59, 0xb7, 0xf1, + 0x6f, 0xa8, 0x4d, 0x22, 0x8a, 0x27, 0x78, 0xda, 0x14, 0x1c, 0xce, 0x48, + 0x6e, 0x7e, 0x95, 0x8d, 0xe1, 0x30, 0x28, 0xd6, 0x4d, 0x77, 0xe7, 0x3d, + 0x71, 0x25, 0x6f, 0xd5, 0xcc, 0xfe, 0x38, 0x1a, 0xa8, 0xfb, 0xbf, 0xaa, + 0x9d, 0x24, 0xba, 0xa7, 0x9f, 0x49, 0x3b, 0xe3, 0x2d, 0x96, 0x22, 0x45, + 0xa9, 0x8e, 0x22, 0x35, 0x89, 0x70, 0xea, 0x81, 0x69, 0x3d, 0x6b, 0xb4, + 0x2e, 0x5a, 0x59, 0xc0, 0x2f, 0xf2, 0x26, 0x49, 0xcc, 0x6c, 0x0c, 0xcb, + 0x7e, 0x76, 0xfb, 0xbf, 0x01, 0x97, 0xa1, 0xfe, 0x7c, 0x41, 0xd8, 0x3c, + 0x5e, 0x30, 0x1a, 0x57, 0x2c, 0xf4, 0x67, 0x94, 0x55, 0x29, 0x16, 0x77, + 0xfa, 0x19, 0xb2, 0x81, 0x10, 0x2f, 0xe6, 0x5b, 0x1e, 0x72, 0x63, 0xa9, + 0x16, 0x01, 0xdb, 0x3d, 0x52, 0x78, 0x88, 0x90, 0x9d, 0x05, 0x6b, 0xd6, + 0xd2, 0x53, 0xdd, 0xbc, 0x38, 0x3c, 0x7a, 0x2d, 0x74, 0x23, 0xb2, 0xfe, + 0x51, 0x51, 0x33, 0x3b, 0x84, 0xb9, 0xce, 0x7e, 0x26, 0x55, 0x49, 0x73, + 0x37, 0x42, 0x05, 0xd4, 0x00, 0x36, 0xb8, 0x2b, 0xab, 0xf4, 0xff, 0x1c, + 0xa7, 0x6b, 0x22, 0x7c, 0x92, 0xf9, 0x18, 0xbf, 0xaf, 0xf5, 0x60, 0x01, + 0x50, 0x4e, 0xe9, 0x97, 0x62, 0x15, 0x3a, 0x7d, 0xec, 0xfd, 0x57, 0x84, + 0x4d, 0x17, 0x92, 0x8e, 0x56, 0x00, 0xaa, 0x4a, 0x57, 0x1c, 0x43, 0x1c, + 0x3a, 0x10, 0xc9, 0xf2, 0x83, 0xac, 0xe3, 0xd1, 0xcd, 0x04, 0x43, 0x98, + 0x07, 0x82, 0x92, 0x10, 0xc1, 0xd8, 0x37, 0x49, 0x4a, 0xce, 0x93, 0xcc, + 0x91, 0x72, 0x8f, 0xd7, 0x18, 0x86, 0x11, 0x8b, 0xa4, 0xd1, 0x58, 0x81, + 0x2c, 0x60, 0x5b, 0x35, 0xf1, 0x0c, 0x75, 0xbb, 0xed, 0xa8, 0x4c, 0xec, + 0xce, 0xd8, 0xc5, 0xee, 0x3a, 0x10, 0xec, 0xc8, 0x1d, 0x88, 0xc1, 0x00, + 0x1f, 0xc8, 0x78, 0xf0, 0xc9, 0x9b, 0x44, 0x36, 0x40, 0x33, 0x0e, 0xcd, + 0x42, 0x25, 0x0b, 0xe4, 0x4a, 0x31, 0x7e, 0x05, 0xf8, 0xd6, 0xe8, 0xdd, + 0x66, 0x55, 0xf8, 0x24, 0x19, 0x07, 0x9b, 0xb5, 0x95, 0x91, 0x99, 0x5a, + 0x36, 0x78, 0xb3, 0x4f, 0x06, 0xe0, 0x39, 0x03, 0x0b, 0x84, 0x3b, 0x4a, + 0xa3, 0x0b, 0x82, 0x0f, 0x55, 0x32, 0x8b, 0xef, 0x7a, 0x03, 0xf7, 0x42, + 0x0a, 0xc2, 0x4a, 0xb6, 0x13, 0x4f, 0xb7, 0x51, 0x36, 0x2a, 0xea, 0x9a, + 0x7f, 0x8a, 0xaf, 0xcc, 0x34, 0xab, 0xf5, 0xc3, 0x4b, 0xb6, 0xc3, 0xe7, + 0x23, 0x6c, 0x33, 0xe1, 0xff, 0x9f, 0xef, 0xf4, 0xe9, 0xac, 0x8e, 0xa9, + 0x72, 0x42, 0x07, 0x63, 0xe8, 0xd2, 0x6b, 0xc4, 0xfb, 0x72, 0x05, 0xc3, + 0xd5, 0x88, 0x82, 0x98, 0x29, 0xf6, 0xfa, 0xe6, 0x91, 0x0e, 0x91, 0xbd, + 0x7b, 0xaa, 0x2c, 0x15, 0xd3, 0xa3, 0xe6, 0x3e, 0x87, 0x8d, 0xbc, 0xf8, + 0x34, 0x2f, 0x5f, 0x63, 0xab, 0x97, 0x2e, 0x34, 0x84, 0x75, 0x29, 0x3c, + 0x66, 0x17, 0x15, 0x66, 0x86, 0xc7, 0x56, 0xa5, 0xba, 0x90, 0x9c, 0x2c, + 0xf7, 0x5f, 0x35, 0x3d, 0x3a, 0xa0, 0x9e, 0xda, 0x6c, 0x22, 0xe2, 0x32, + 0x64, 0x2d, 0xb3, 0xaf, 0xe3, 0xc7, 0x83, 0x24, 0x42, 0x7e, 0x0e, 0xc2, + 0x83, 0x49, 0xef, 0xa6, 0x54, 0xf7, 0xd9, 0x92, 0xb5, 0xf0, 0xa0, 0xf3, + 0x87, 0x82, 0x89, 0xd3, 0xbd, 0xdb, 0x80, 0x43, 0xf7, 0x60, 0xef, 0xca, + 0x92, 0x37, 0x83, 0xc2, 0x9d, 0x20, 0x4f, 0xc3, 0x0c, 0xc1, 0xab, 0xf0, + 0xc8, 0x5b, 0x34, 0x05, 0x46, 0xff, 0xb3, 0x8b, 0x27, 0x00, 0x72, 0x68, + 0xa5, 0x0c, 0xab, 0xb1, 0xef, 0x20, 0xde, 0x3d, 0x68, 0x22, 0xd0, 0x8b, + 0xa7, 0x43, 0x6f, 0x1a, 0xfa, 0x58, 0x7c, 0x0b, 0xc8, 0x59, 0x87, 0x25, + 0xa4, 0x4a, 0x9f, 0x6e, 0xb3, 0x03, 0x55, 0xf4, 0xb0, 0x2a, 0x3e, 0xfc, + 0xd7, 0xeb, 0xcd, 0xc9, 0x3e, 0x43, 0xeb, 0x72, 0x42, 0x19, 0x22, 0x88, + 0xb9, 0xdc, 0x77, 0xe6, 0xc5, 0x63, 0x50, 0x8e, 0xcb, 0x0c, 0xe8, 0x62, + 0x4b, 0x1e, 0x13, 0x17, 0x54, 0x23, 0x95, 0x79, 0xaa, 0x7e, 0x04, 0xdd, + 0x1a, 0x09, 0x73, 0x4b, 0x47, 0x9f, 0x71, 0x11, 0xe7, 0x53, 0x95, 0x01, + 0x9c, 0xc9, 0x7a, 0x6a, 0x2d, 0xa4, 0x0b, 0x64, 0xfb, 0x4f, 0x78, 0x6c, + 0x37, 0xf2, 0xb5, 0x80, 0x62, 0xe0, 0x48, 0x0e, 0x57, 0xfe, 0x6d, 0x49, + 0xd9, 0xdd, 0x64, 0xe2, 0xc9, 0x80, 0xc1, 0x27, 0xed, 0x6b, 0xd6, 0x7c, + 0x52, 0x43, 0xfe, 0x0f, 0xa1, 0x60, 0x3c, 0x74, 0x59, 0x44, 0x22, 0xb4, + 0x7e, 0xde, 0xd9, 0xd6, 0x82, 0x20, 0x18, 0x00, 0x9f, 0x04, 0xab, 0x71, + 0x21, 0x58, 0xd8, 0xfe, 0x2b, 0xfc, 0x82, 0x0e, 0x19, 0x9f, 0x96, 0x03, + 0xc3, 0xf1, 0x0a, 0xeb, 0x1e, 0xa9, 0xf7, 0xfb, 0x1b, 0x9a, 0x8f, 0x94, + 0x8b, 0x6a, 0x92, 0xff, 0xa9, 0xfa, 0x63, 0x32, 0x33, 0x1c, 0xf9, 0xbc, + 0xf2, 0xb0, 0xd6, 0xd7, 0x7e, 0xdb, 0x3c, 0xd5, 0xb8, 0xf7, 0xea, 0x71, + 0xd4, 0x83, 0x2d, 0x8a, 0xc1, 0x86, 0x49, 0x48, 0x6e, 0x92, 0x4d, 0x70, + 0xc6, 0xc7, 0xb5, 0x3b, 0x55, 0xc7, 0xd6, 0xdb, 0x3b, 0x40, 0xc9, 0x0c, + 0xc7, 0x49, 0x41, 0x91, 0xdd, 0xfe, 0x12, 0x25, 0xc9, 0xee, 0x18, 0x2b, + 0x38, 0x72, 0x89, 0x3d, 0x4a, 0xcf, 0xe8, 0xac, 0xcf, 0xfe, 0x7d, 0x1b, + 0x9a, 0xb8, 0xd3, 0xa8, 0x86, 0x7c, 0x38, 0x63, 0x71, 0xc4, 0xdf, 0xe6, + 0x3e, 0xe8, 0x8d, 0xb3, 0xed, 0xec, 0x6d, 0x6d, 0x2d, 0x4a, 0x0d, 0x55, + 0xe8, 0xb7, 0x9a, 0xd7, 0xfb, 0x64, 0xb5, 0x76, 0x03, 0x97, 0xb0, 0x77, + 0x0f, 0xcb, 0x8e, 0xb3, 0x72, 0x33, 0x2c, 0x64, 0xcb, 0x4a, 0x4d, 0x90, + 0xe6, 0xb8, 0xc7, 0xbd, 0x9d, 0x72, 0xca, 0x5c, 0x8f, 0x3b, 0x6d, 0x9e, + 0x37, 0x66, 0xf5, 0x6c, 0x2b, 0x2b, 0xac, 0xb4, 0xef, 0x3d, 0x63, 0x6f, + 0xc3, 0x14, 0x3c, 0xaf, 0xe8, 0xb3, 0x75, 0x0c, 0x68, 0xa1, 0xa4, 0xe8, + 0x7d, 0x1c, 0x3e, 0xbb, 0x8f, 0x81, 0xb3, 0x4e, 0x19, 0xc9, 0xaf, 0xdd, + 0x71, 0x2d, 0xf9, 0x98, 0xe1, 0x70, 0xb8, 0xa6, 0x2a, 0x76, 0xf0, 0xde, + 0xc0, 0xb3, 0xac, 0x92, 0xce, 0x09, 0x66, 0x53, 0xf2, 0x5b, 0x9e, 0x32, + 0x70, 0x51, 0x3b, 0xac, 0x5c, 0x69, 0xf0, 0xdf, 0x14, 0x5c, 0x12, 0x41, + 0x18, 0xe8, 0xd4, 0xa2, 0x50, 0x20, 0x1c, 0xfe, 0x13, 0x70, 0xe7, 0x94, + 0x61, 0xf9, 0x9f, 0x01, 0x4a, 0xb9, 0x16, 0x13, 0x9a, 0x57, 0x9d, 0x86, + 0xed, 0x0c, 0x69, 0x6e, 0x4d, 0x5b, 0x60, 0x71, 0x67, 0xd7, 0x07, 0xb7, + 0x09, 0x05, 0xe5, 0xc5, 0x55, 0x88, 0x39, 0x1c, 0xed, 0xb8, 0xb6, 0x7f, + 0x20, 0x9f, 0x17, 0x47, 0x71, 0xf1, 0x68, 0xb1, 0x5d, 0xe0, 0xeb, 0x18, + 0x90, 0xbf, 0x1c, 0xe2, 0x8b, 0x9f, 0xb8, 0x05, 0x06, 0x52, 0x60, 0x2f, + 0x4e, 0x33, 0x7a, 0x49, 0x84, 0x0c, 0x71, 0x71, 0xb1, 0xe8, 0x77, 0xaa, + 0xf4, 0x7e, 0xb1, 0xf9, 0xd2, 0x29, 0x4d, 0x79, 0x24, 0x12, 0x52, 0xbc, + 0x4f, 0x6b, 0xa8, 0xcd, 0xd0, 0xcf, 0x41, 0xf8, 0x6e, 0xcb, 0x5f, 0xcd, + 0xe8, 0x32, 0x56, 0xb0, 0xcc, 0x3d, 0xf6, 0xd4, 0x4a, 0x68, 0x52, 0x65, + 0x37, 0x68, 0xdf, 0x62, 0x96, 0x83, 0x4b, 0x8c, 0xa4, 0x12, 0x21, 0x3f, + 0x0f, 0xd9, 0x35, 0xb3, 0x11, 0x6e, 0xca, 0xea, 0x90, 0xb6, 0x20, 0x59, + 0x1a, 0x4e, 0xa5, 0xdf, 0x39, 0x26, 0x01, 0x7c, 0x20, 0xc1, 0xde, 0x04, + 0xfb, 0x21, 0xb8, 0xea, 0x6f, 0x56, 0xfc, 0x07, 0xc5, 0x0d, 0x48, 0x28, + 0xa6, 0x06, 0x49, 0x1f, 0x95, 0x7a, 0x69, 0xfb, 0x71, 0x96, 0xe2, 0x65, + 0x15, 0xf8, 0x7b, 0xc1, 0xe0, 0x71, 0xdf, 0x82, 0x35, 0xb8, 0xaa, 0xaa, + 0xa9, 0x78, 0x69, 0xae, 0xe0, 0xc8, 0xb1, 0xf3, 0x9f, 0x8f, 0xab, 0xfe, + 0x04, 0xbf, 0x90, 0x78, 0x7e, 0xc4, 0x22, 0xe3, 0x05, 0xa5, 0x05, 0x0e, + 0xcb, 0x18, 0xd9, 0xde, 0x52, 0x5e, 0xce, 0xb8, 0x40, 0x35, 0xa3, 0x90, + 0xfc, 0x3e, 0x50, 0xe4, 0x78, 0xa3, 0xc1, 0x28, 0x9a, 0x37, 0xa4, 0x75, + 0x86, 0x95, 0x39, 0x9b, 0x5a, 0x46, 0x1c, 0x2f, 0x26, 0x4a, 0x55, 0xa1, + 0x1b, 0x3c, 0xfd, 0x1b, 0x7f, 0x1c, 0x60, 0xc4, 0x5f, 0xe4, 0x1f, 0xb9, + 0x61, 0xa6, 0x5a, 0xbc, 0xd2, 0x5c, 0x92, 0x56, 0xdd, 0xcf, 0xd6, 0x4b, + 0x22, 0x05, 0xc3, 0xf7, 0xfa, 0x9c, 0x95, 0x7e, 0xec, 0x86, 0xa3, 0x8b, + 0x5f, 0x9b, 0xf8, 0xbd, 0xeb, 0x6a, 0xa1, 0xf2, 0x0d, 0x5e, 0x4e, 0x0c, + 0x95, 0x99, 0xa9, 0x69, 0x1a, 0xb4, 0x44, 0x3b, 0x15, 0x38, 0xdb, 0xd9, + 0x7e, 0xb7, 0x59, 0x3c, 0x6d, 0x0b, 0xf5, 0x15, 0x8d, 0x3b, 0x0e, 0x1c, + 0x60, 0x3c, 0x5d, 0xd7, 0xb1, 0x6d, 0xf3, 0xa7, 0x75, 0x5c, 0xf2, 0x3e, + 0xdf, 0x8c, 0xda, 0x7f, 0x15, 0xc1, 0x9e, 0xd6, 0xff, 0xa6, 0x66, 0x33, + 0xd7, 0x6c, 0x5c, 0x4a, 0xbc, 0xc5, 0xc6, 0x6e, 0x88, 0xb7, 0x9b, 0xe3, + 0xcc, 0x9d, 0x89, 0x75, 0x29, 0x1c, 0xac, 0xb3, 0x9e, 0x95, 0x2c, 0x93, + 0xd4, 0xa0, 0x0d, 0xa2, 0xaf, 0xd6, 0x6d, 0x62, 0x84, 0xb5, 0xfe, 0xa4, + 0x2b, 0xb7, 0x9f, 0xc2, 0xe1, 0x32, 0x35, 0x26, 0xce, 0x40, 0xb8, 0x90, + 0x6b, 0xed, 0xa8, 0xea, 0xa1, 0x76, 0xc3, 0xa1, 0x76, 0x5c, 0xb5, 0x96, + 0xc7, 0xcc, 0x61, 0x58, 0x60, 0x5e, 0x70, 0x06, 0x74, 0x85, 0xed, 0x86, + 0xef, 0x50, 0x62, 0xfb, 0x9b, 0xea, 0x0e, 0x66, 0x13, 0xd7, 0x13, 0x24, + 0x9e, 0xce, 0x98, 0x41, 0xa6, 0xbb, 0xed, 0x66, 0x3b, 0x40, 0x68, 0x52, + 0xf1, 0x04, 0x65, 0x4c, 0xac, 0x68, 0x4f, 0x31, 0xe8, 0x08, 0x05, 0x94, + 0x88, 0x88, 0x05, 0x7b, 0xb4, 0xcb, 0x14, 0x48, 0x50, 0xc9, 0x8f, 0x38, + 0x39, 0xea, 0xf2, 0xbd, 0x54, 0x46, 0x98, 0xd5, 0xe0, 0xcf, 0x3a, 0x67, + 0x71, 0xab, 0x34, 0x62, 0x1e, 0x0e, 0xcc, 0x0e, 0xb5, 0xbf, 0xca, 0xd9, + 0x00, 0x5b, 0x1d, 0xb8, 0x9b, 0x46, 0x37, 0xc1, 0xa8, 0x40, 0x36, 0x90, + 0x8e, 0xd5, 0xb7, 0x66, 0x73, 0x7b, 0x3f, 0xf8, 0x72, 0x20, 0xcf, 0x93, + 0x32, 0x62, 0x12, 0xe4, 0xd5, 0x84, 0xa8, 0xcd, 0xe5, 0xc0, 0x8b, 0x57, + 0x34, 0xb0, 0x36, 0x3b, 0x70, 0x66, 0x9f, 0xe1, 0x7a, 0xb9, 0x15, 0xc6, + 0x6a, 0xf1, 0x39, 0xd7, 0xbf, 0xb3, 0x61, 0x10, 0xf7, 0xb4, 0x48, 0x95, + 0xf7, 0xb4, 0x47, 0xac, 0x06, 0xd0, 0xaf, 0x13, 0x37, 0x56, 0x94, 0xa9, + 0x91, 0x3c, 0xb2, 0x50, 0x00, 0x2f, 0xad, 0x40, 0xe9, 0x57, 0xd2, 0xaf, + 0x48, 0xb8, 0xe6, 0x36, 0x05, 0xa0, 0xb8, 0x92, 0x60, 0x46, 0xae, 0xb5, + 0xdc, 0x0d, 0xd7, 0xbf, 0xe2, 0xa5, 0x79, 0x36, 0x5c, 0x5a, 0xe5, 0xf9, + 0xfb, 0x2d, 0x35, 0x42, 0x6b, 0x63, 0x98, 0x69, 0x6f, 0x1d, 0x97, 0xb6, + 0xce, 0xea, 0x45, 0x51, 0x29, 0xd8, 0xb6, 0x8f, 0xae, 0x1f, 0xce, 0x86, + 0x43, 0x18, 0x29, 0xb9, 0x55, 0x38, 0x9c, 0x01, 0xd3, 0xf3, 0x8a, 0x6f, + 0x3f, 0xb7, 0x9c, 0x05, 0xd2, 0x76, 0x65, 0xda, 0x1f, 0x34, 0xc1, 0xda, + 0xb7, 0x85, 0x04, 0x66, 0xe0, 0x28, 0xaa, 0xb0, 0x07, 0x2a, 0x9b, 0x13, + 0x9d, 0x92, 0x29, 0x0d, 0x8b, 0x97, 0x05, 0x40, 0x91, 0x6f, 0xcb, 0x74, + 0xe9, 0x87, 0x89, 0xa4, 0x62, 0x0d, 0x20, 0xa1, 0xb0, 0x4d, 0xa3, 0x87, + 0xf1, 0x4e, 0x05, 0xc1, 0x56, 0x2d, 0xd1, 0xff, 0xbe, 0x04, 0x79, 0x4a, + 0x43, 0xcd, 0x72, 0x84, 0xb6, 0xb6, 0xda, 0x76, 0x98, 0xac, 0x59, 0x12, + 0x91, 0xcb, 0x63, 0xac, 0xd6, 0xcd, 0xd0, 0xf6, 0xb2, 0xc0, 0x23, 0xc7, + 0x2d, 0xf1, 0x32, 0xeb, 0x8f, 0x50, 0x23, 0x0f, 0x8b, 0x69, 0x0e, 0x62, + 0xfa, 0x82, 0xbf, 0x72, 0x24, 0x3e, 0x08, 0xb4, 0x01, 0x66, 0x5f, 0xf8, + 0x9c, 0x33, 0x44, 0xac, 0x3a, 0xaf, 0x1e, 0xdd, 0x1a, 0x58, 0xc5, 0xa5, + 0xa5, 0x58, 0x6e, 0x0a, 0x53, 0xc9, 0xba, 0x60, 0x11, 0x40, 0xf4, 0x6d, + 0x4c, 0x2c, 0xcc, 0xf8, 0xf0, 0xc7, 0x9d, 0xfa, 0x48, 0x92, 0x4c, 0x58, + 0xd4, 0x1c, 0x24, 0x45, 0x00, 0x16, 0xa0, 0xa5, 0x98, 0x58, 0xc3, 0xb6, + 0x86, 0xc6, 0x49, 0xfd, 0xf1, 0x8b, 0xd3, 0x91, 0xa2, 0x73, 0x34, 0x33, + 0x8e, 0xa5, 0xb4, 0xda, 0xc7, 0x9f, 0x5d, 0x6e, 0xb7, 0xe1, 0x77, 0x08, + 0x73, 0xb1, 0xc6, 0x2c, 0x9a, 0x13, 0x72, 0xb5, 0xb0, 0xbb, 0x04, 0xc7, + 0xfc, 0x70, 0x8a, 0x20, 0x31, 0xbd, 0xfb, 0xa7, 0xbe, 0x9e, 0xe4, 0x34, + 0x18, 0x11, 0x0f, 0x92, 0x74, 0xa0, 0x65, 0xaa, 0xe1, 0xfa, 0x8a, 0xa9, + 0x0f, 0xdb, 0x2b, 0x13, 0xb0, 0xc2, 0x08, 0xad, 0x85, 0xb5, 0x66, 0xc4, + 0xad, 0x64, 0xff, 0xc7, 0xf2, 0x00, 0xec, 0xdb, 0x3d, 0xdd, 0xa0, 0x56, + 0xe1, 0xb6, 0xa8, 0xfa, 0x52, 0xfd, 0x08, 0x10, 0xe1, 0x39, 0xb2, 0x7e, + 0xa0, 0xc8, 0x7c, 0xcf, 0x7b, 0xab, 0xa3, 0xa2, 0xda, 0xca, 0x6e, 0x27, + 0x57, 0x1f, 0xfc, 0x46, 0xd2, 0xb2, 0x1d, 0xd8, 0xe6, 0x7a, 0x6d, 0xcc, + 0x87, 0xc2, 0x7f, 0x35, 0xab, 0xbb, 0x05, 0xd8, 0xcc, 0x51, 0xa8, 0x9a, + 0x59, 0x7c, 0x68, 0xbe, 0x28, 0xb6, 0x5d, 0x00, 0x12, 0x4b, 0x30, 0xf5, + 0xab, 0x79, 0x64, 0x51, 0x81, 0xc6, 0x47, 0xe1, 0xa3, 0xbf, 0x31, 0xe4, + 0x06, 0x4c, 0xa8, 0x82, 0x25, 0x40, 0xe4, 0x5e, 0x8c, 0xe0, 0x5c, 0xc3, + 0x10, 0x42, 0x60, 0x0e, 0xe9, 0xa3, 0x17, 0x2f, 0xbf, 0x1b, 0xd8, 0xdd, + 0xf2, 0x99, 0xcc, 0xe1, 0x91, 0x0a, 0xa3, 0x48, 0x5b, 0x60, 0xa8, 0xb0, + 0x70, 0x49, 0xda, 0x61, 0x68, 0x1b, 0xcd, 0xcb, 0xe5, 0xc3, 0xbc, 0x08, + 0x42, 0xaf, 0xfe, 0xb5, 0x30, 0x73, 0xc8, 0x54, 0x1a, 0xdf, 0x0d, 0xb1, + 0x8a, 0x77, 0xe0, 0x3d, 0x76, 0xbb, 0xd6, 0x55, 0xf2, 0x6c, 0x1a, 0xa6, + 0x75, 0xe1, 0xd3, 0x5f, 0x0f, 0xdd, 0x0d, 0x69, 0x59, 0x8b, 0x3b, 0x59, + 0xb5, 0x6f, 0x30, 0x8f, 0xb3, 0xa6, 0x0c, 0x91, 0xb4, 0x98, 0x49, 0x19, + 0x8e, 0xd2, 0xd9, 0x7f, 0xd0, 0x79, 0x32, 0x7e, 0x0c, 0x3f, 0x65, 0x0e, + 0x94, 0xbc, 0x9f, 0x98, 0xba, 0x02, 0x2c, 0xf1, 0xe0, 0xf4, 0xe8, 0x05, + 0x85, 0x9f, 0x8b, 0xfc, 0x79, 0xb6, 0x2b, 0xf4, 0x26, 0x7e, 0xf3, 0x74, + 0x85, 0xa9, 0x18, 0xa1, 0x9e, 0x0e, 0x5e, 0xc4, 0x95, 0x45, 0x1f, 0xa8, + 0xf2, 0xf9, 0x28, 0x33, 0x06, 0x42, 0x5d, 0x86, 0xb8, 0x81, 0x82, 0xbd, + 0x00, 0xf3, 0xfb, 0x9d, 0x8f, 0x0e, 0xf9, 0x0e, 0x55, 0xf8, 0x65, 0xcb, + 0xdf, 0x8c, 0xec, 0xe3, 0x09, 0xa4, 0x67, 0xa2, 0xea, 0x1e, 0xa5, 0xb8, + 0x3e, 0x4d, 0xd1, 0xeb, 0x24, 0xd4, 0x67, 0x52, 0x44, 0xbe, 0x56, 0xbf, + 0xac, 0x76, 0x3c, 0x47, 0x01, 0x86, 0x35, 0x27, 0x95, 0x5d, 0x2e, 0x9d, + 0x0b, 0xa0, 0xe4, 0xc2, 0x69, 0xfc, 0x6c, 0x58, 0x20, 0x92, 0x94, 0x39, + 0xa7, 0x2b, 0x9c, 0x09, 0x1c, 0xed, 0x3c, 0x3a, 0x59, 0xcf, 0x39, 0x85, + 0x43, 0x55, 0xf0, 0x90, 0xe4, 0xae, 0x40, 0x32, 0x1d, 0x73, 0x90, 0x28, + 0xf4, 0xa4, 0xc5, 0x08, 0xfb, 0x62, 0x92, 0xed, 0xcb, 0x41, 0x3f, 0xe5, + 0x7f, 0x07, 0x4d, 0xbd, 0x14, 0x0a, 0xc4, 0xbf, 0xe2, 0x45, 0xfb, 0x9b, + 0x62, 0x78, 0xb3, 0xfc, 0xd1, 0xcf, 0xeb, 0x6a, 0x4e, 0x12, 0xb9, 0x67, + 0x40, 0xdd, 0x6c, 0x84, 0x29, 0xde, 0xcc, 0x18, 0xfb, 0x9c, 0x25, 0x8d, + 0x14, 0xd7, 0x51, 0x32, 0xe7, 0x08, 0x7c, 0x32, 0xc6, 0x66, 0xd0, 0x8a, + 0xad, 0x0e, 0x3b, 0x9f, 0x5a, 0xce, 0x38, 0x2c, 0xcc, 0x47, 0x38, 0x1f, + 0xb1, 0x18, 0xc8, 0x99, 0xdb, 0x6a, 0xae, 0x5d, 0x65, 0xa3, 0x6c, 0x1a, + 0x02, 0x11, 0x4e, 0x5f, 0xb2, 0xa0, 0xeb, 0x3e, 0xc2, 0xef, 0x05, 0x87, + 0xb3, 0x11, 0x23, 0x08, 0x32, 0x5d, 0x41, 0xd4, 0x14, 0xb5, 0x76, 0xea, + 0x06, 0x37, 0xce, 0x23, 0x1c, 0xb0, 0x42, 0xba, 0x63, 0xdb, 0x10, 0xff, + 0xff, 0x93, 0xc3, 0x82, 0x63, 0x12, 0x4d, 0xa1, 0x98, 0xf6, 0xf8, 0xc8, + 0x97, 0x09, 0x58, 0x2a, 0x06, 0x5d, 0xa5, 0xc5, 0x09, 0xfc, 0x45, 0x65, + 0x14, 0x1f, 0x63, 0xa4, 0x7d, 0x98, 0x1b, 0x46, 0xfb, 0x54, 0x42, 0x9f, + 0x35, 0x9e, 0xc4, 0x1f, 0x72, 0xa0, 0x8f, 0x69, 0x14, 0x2c, 0x2d, 0x32, + 0xc3, 0x79, 0xca, 0x16, 0x55, 0x60, 0x0f, 0x8b, 0x05, 0x63, 0x4e, 0x23, + 0x77, 0x23, 0x63, 0xb6, 0x2a, 0x88, 0xfa, 0xd3, 0xcd, 0xd4, 0x85, 0xe2, + 0x34, 0xf7, 0xd8, 0x69, 0xe4, 0x34, 0x5e, 0x37, 0x66, 0xdd, 0x6a, 0x86, + 0x2f, 0xc0, 0x42, 0x0e, 0xba, 0x2f, 0xfa, 0x53, 0xda, 0x4b, 0xce, 0x2a, + 0x0c, 0x58, 0x86, 0xb8, 0xab, 0x5b, 0x15, 0x43, 0xcb, 0xea, 0x71, 0xff, + 0x1f, 0x88, 0x8f, 0x4a, 0x0f, 0xe5, 0x53, 0x8f, 0xc4, 0x63, 0xfc, 0x5b, + 0xca, 0xcb, 0x97, 0x4a, 0x09, 0x37, 0x35, 0xd7, 0xdc, 0x9d, 0xfd, 0xd6, + 0x0d, 0xa8, 0x49, 0xc0, 0x7d, 0x16, 0xff, 0x7f, 0x23, 0xa8, 0xf2, 0x4a, + 0xaf, 0xf9, 0x31, 0x9b, 0x3e, 0x07, 0x3b, 0xf2, 0x2a, 0x42, 0x5a, 0x61, + 0xa2, 0xab, 0xfa, 0xfd, 0xcd, 0x26, 0x54, 0xd6, 0xdc, 0x98, 0x8b, 0xc8, + 0xf4, 0x65, 0x5a, 0x68, 0x59, 0x11, 0x73, 0x3e, 0xbf, 0xbb, 0x57, 0x1a, + 0x64, 0xf5, 0x9e, 0xc8, 0x18, 0xb1, 0x21, 0x73, 0xb5, 0xe1, 0xcc, 0xeb, + 0xdd, 0xb2, 0x63, 0x22, 0x47, 0x0a, 0x10, 0x00, 0x64, 0x36, 0xab, 0x08, + 0x2b, 0x40, 0x23, 0x1d, 0xb0, 0xd5, 0xe0, 0xee, 0xb2, 0x73, 0x7d, 0x6a, + 0x2e, 0x76, 0x45, 0xaa, 0x1b, 0xd9, 0x0e, 0xb2, 0xca, 0xa5, 0x03, 0x0d, + 0x92, 0x57, 0xe8, 0xb6, 0x25, 0x7c, 0x75, 0x16, 0xd1, 0x2b, 0x80, 0xa8, + 0xfb, 0xca, 0x29, 0xbe, 0x31, 0x17, 0x2d, 0x2e, 0xe3, 0xb4, 0xac, 0xb7, + 0xba, 0x29, 0x71, 0x92, 0xaf, 0x45, 0x9f, 0xb4, 0xc6, 0x79, 0xe3, 0xa7, + 0x84, 0x54, 0x3a, 0x56, 0x9e, 0xe7, 0xec, 0x43, 0xb9, 0xd8, 0xa6, 0x75, + 0x5a, 0x90, 0x47, 0x05, 0x90, 0xc8, 0x5b, 0x60, 0x5b, 0x27, 0x58, 0x06, + 0x19, 0x28, 0xc5, 0x48, 0x0a, 0x1a, 0x0a, 0xc5, 0xe6, 0x2d, 0xd1, 0x60, + 0x71, 0xf6, 0xad, 0x5e, 0x21, 0x42, 0x2f, 0xfb, 0x7c, 0x68, 0x5b, 0xd8, + 0x6e, 0x67, 0x60, 0x5e, 0x58, 0xe7, 0x6c, 0x61, 0xd2, 0x87, 0xf7, 0x0e, + 0x61, 0x78, 0xc3, 0xb8, 0x7b, 0xc1, 0x21, 0x8c, 0x77, 0x3e, 0xac, 0x75, + 0x70, 0x21, 0x6c, 0x36, 0x80, 0xa1, 0x03, 0xcc, 0x7b, 0x45, 0xe4, 0x21, + 0x2d, 0x21, 0x34, 0x86, 0xa2, 0xce, 0x71, 0x6e, 0x52, 0x36, 0x54, 0x10, + 0xb5, 0xe0, 0xc0, 0xf6, 0x7e, 0x2b, 0x93, 0x88, 0x99, 0x51, 0x61, 0xa8, + 0xa8, 0x8a, 0xd0, 0xd5, 0x5c, 0x9f, 0x1a, 0x8a, 0xde, 0x69, 0x9e, 0xd2, + 0xe6, 0x58, 0x07, 0x35, 0x01, 0x66, 0x05, 0xe1, 0x69, 0xee, 0x4d, 0x96, + 0xdf, 0x86, 0xb3, 0x15, 0xb6, 0x40, 0x84, 0xc6, 0x1f, 0xd1, 0x8b, 0x94, + 0xf5, 0x43, 0x8e, 0x4c, 0x56, 0x67, 0x6d, 0xad, 0x9b, 0x3d, 0x24, 0x3c, + 0x6e, 0x52, 0x6f, 0x07, 0x7f, 0xc5, 0xb2, 0x86, 0x23, 0xcf, 0x48, 0x41, + 0x4a, 0x86, 0x5a, 0x89, 0xfb, 0x86, 0xc6, 0x6a, 0x00, 0x9a, 0x09, 0xc3, + 0x4d, 0x91, 0x3c, 0xfc, 0x95, 0xba, 0x27, 0x34, 0x61, 0xaf, 0x4d, 0x49, + 0xa8, 0x58, 0xa2, 0x1a, 0xc7, 0xc7, 0x89, 0x7c, 0x2b, 0x13, 0xc7, 0xb0, + 0x60, 0xa9, 0xc5, 0x1f, 0x3a, 0x7d, 0xf5, 0x74, 0xe7, 0xbe, 0x9f, 0x37, + 0x77, 0x53, 0xd3, 0x96, 0x40, 0x56, 0x5d, 0x13, 0x4c, 0xd1, 0x9b, 0x6c, + 0xcd, 0x5e, 0xfd, 0x9b, 0xc2, 0x60, 0x0d, 0x05, 0xa7, 0xc2, 0xea, 0x5a, + 0x64, 0xb4, 0xbc, 0xa3, 0x12, 0xb5, 0x6a, 0x5f, 0x23, 0x83, 0xd9, 0x20, + 0xda, 0x23, 0x08, 0x1f, 0x14, 0x4c, 0x66, 0xb9, 0x33, 0x1f, 0xfa, 0xd7, + 0x8a, 0xde, 0x4e, 0x5d, 0xc1, 0xc8, 0x5c, 0x93, 0x0b, 0xc7, 0x95, 0xa2, + 0x8f, 0x0f, 0xf7, 0xcd, 0xc5, 0xec, 0x1f, 0x6e, 0x4c, 0xc9, 0xb0, 0x6a, + 0xf0, 0x93, 0x0d, 0xa6, 0xeb, 0x82, 0xe0, 0xc5, 0xf2, 0x87, 0xac, 0x40, + 0xe1, 0x04, 0x6c, 0xe9, 0x55, 0xea, 0xe3, 0x33, 0xa9, 0x73, 0x06, 0x78, + 0x36, 0xd7, 0x65, 0xcf, 0xd3, 0x1d, 0xb1, 0x45, 0x4b, 0xce, 0x72, 0x21, + 0x07, 0x41, 0x3a, 0xd6, 0x9c, 0x16, 0x0e, 0xc4, 0x30, 0x84, 0x74, 0x18, + 0x52, 0xeb, 0x63, 0xa2, 0x63, 0xb2, 0x9a, 0x39, 0x73, 0x30, 0x14, 0x36, + 0x6a, 0x8a, 0xae, 0x3e, 0x95, 0x03, 0x31, 0x54, 0xac, 0x4c, 0xeb, 0xbe, + 0x91, 0x19, 0x61, 0x7d, 0xd6, 0xb6, 0xfc, 0xc1, 0x92, 0x26, 0xdb, 0x78, + 0x59, 0xfc, 0x3b, 0x0f, 0x40, 0xda, 0x89, 0x17, 0x58, 0x5e, 0x25, 0x41, + 0x20, 0xfc, 0x3c, 0x72, 0xd8, 0xbd, 0x52, 0x50, 0xe5, 0x51, 0xf8, 0x2d, + 0x12, 0x77, 0xe9, 0xd2, 0xd4, 0xd2, 0x91, 0xdf, 0xac, 0xd4, 0x68, 0xba, + 0x7b, 0x95, 0x33, 0xff, 0x31, 0x4d, 0x58, 0xf0, 0xf3, 0x8e, 0x8f, 0xe0, + 0x20, 0xad, 0x26, 0x8f, 0xa6, 0x23, 0x2f, 0xbb, 0xe8, 0xc9, 0x5b, 0xc0, + 0xcc, 0xe1, 0x89, 0x8a, 0xfc, 0x95, 0x24, 0x1d, 0xe4, 0xe6, 0x2c, 0xc4, + 0xdd, 0xce, 0xe6, 0x6f, 0x00, 0xbf, 0xdc, 0x4c, 0x38, 0x2b, 0xc8, 0x37, + 0xb0, 0xa7, 0x53, 0x56, 0x37, 0x14, 0x91, 0x67, 0xde, 0x09, 0x1b, 0x52, + 0x64, 0xce, 0xc0, 0x93, 0x04, 0x89, 0x19, 0xb5, 0x98, 0xbc, 0x85, 0x45, + 0x5c, 0x63, 0xff, 0x98, 0xb8, 0xd4, 0x37, 0x4a, 0x5c, 0xa1, 0x69, 0x6b, + 0xd8, 0xd6, 0xae, 0xc6, 0x95, 0x86, 0x6d, 0x0d, 0xbe, 0x7b, 0xb0, 0x21, + 0x2d, 0x77, 0x94, 0x10, 0x14, 0x33, 0xc4, 0x9d, 0x1c, 0xfd, 0x9b, 0x21, + 0x1e, 0x56, 0xa1, 0xca, 0x16, 0xd0, 0x01, 0x4a, 0xeb, 0x6a, 0x84, 0x3b, + 0x88, 0x55, 0x56, 0x9b, 0xb8, 0x0b, 0xb5, 0x60, 0xce, 0xe0, 0xc1, 0xea, + 0x7f, 0x78, 0x7c, 0xa5, 0x3d, 0x35, 0xaa, 0xe6, 0xc5, 0x38, 0x74, 0x8d, + 0xcc, 0xa8, 0x9d, 0x80, 0xe7, 0x4b, 0xb7, 0x11, 0xd0, 0x20, 0x36, 0x34, + 0x2d, 0x7c, 0x1b, 0x9f, 0x90, 0xc8, 0x11, 0x5d, 0x35, 0xca, 0x41, 0x19, + 0x67, 0x1c, 0x1d, 0x37, 0xaf, 0x26, 0x87, 0x4c, 0xd2, 0x13, 0x66, 0xe0, + 0x55, 0xed, 0x94, 0xff, 0x4e, 0x8c, 0x54, 0x17, 0xe8, 0xa7, 0x8e, 0x34, + 0xa1, 0xed, 0xf4, 0xca, 0x38, 0x53, 0xce, 0xbe, 0xd4, 0x54, 0xfe, 0xae, + 0x83, 0xb5, 0x32, 0x44, 0x49, 0xfa, 0xd5, 0x1f, 0xc5, 0x05, 0x6c, 0xeb, + 0x9f, 0x47, 0x47, 0xc0, 0x56, 0x1e, 0xf7, 0x89, 0xe5, 0x5f, 0xe0, 0x1a, + 0xae, 0x4d, 0xd6, 0x89, 0xac, 0xdc, 0x1a, 0x07, 0xe1, 0x74, 0xf6, 0x47, + 0x51, 0xa0, 0x0f, 0xe7, 0x61, 0xf4, 0x41, 0xde, 0xf8, 0x7e, 0x42, 0xbe, + 0x5f, 0xfd, 0x1b, 0x74, 0x02, 0x6a, 0xe2, 0xf1, 0x81, 0x94, 0x37, 0x1e, + 0xa7, 0x93, 0x0b, 0xf1, 0xa8, 0xd5, 0x59, 0xd3, 0x69, 0x65, 0x7d, 0xec, + 0x28, 0xb4, 0x4e, 0x7b, 0xd6, 0x08, 0x2d, 0xce, 0x2f, 0xcd, 0xca, 0x89, + 0xe9, 0x72, 0x65, 0x18, 0x4a, 0x88, 0x1f, 0x8a, 0x2d, 0xfd, 0x9e, 0x5c, + 0xe0, 0xc0, 0x74, 0x85, 0x27, 0xb4, 0xee, 0x09, 0xd5, 0xb3, 0xd5, 0xd0, + 0x3b, 0xd8, 0x16, 0xe1, 0x04, 0xb3, 0x31, 0x01, 0x7c, 0x16, 0xc0, 0x28, + 0xd1, 0x45, 0xda, 0xfb, 0xf4, 0xdf, 0xb0, 0x90, 0x24, 0x0f, 0xfc, 0xba, + 0x04, 0xf3, 0x51, 0x27, 0xec, 0xbd, 0x42, 0x2a, 0xb6, 0x0d, 0x45, 0xfe, + 0xab, 0x16, 0x36, 0x59, 0xa4, 0xe1, 0x28, 0xbe, 0xa2, 0xf5, 0xb7, 0xc2, + 0xaa, 0x66, 0x96, 0x07, 0xeb, 0x43, 0xf9, 0x60, 0xd9, 0xdb, 0x4d, 0x1d, + 0x6a, 0x6f, 0xe4, 0x3a, 0x02, 0x5e, 0x68, 0x40, 0xb2, 0x18, 0x35, 0xd8, + 0x15, 0xf2, 0x91, 0x02, 0x2c, 0xbb, 0x9a, 0x5b, 0xff, 0xc8, 0xd9, 0x4f, + 0x88, 0xd6, 0x0d, 0x2d, 0x6b, 0x8f, 0xc7, 0xe9, 0x18, 0x8b, 0x86, 0x5c, + 0xf4, 0xcb, 0xcc, 0xbf, 0xe2, 0xcb, 0x79, 0xf6, 0x8f, 0x56, 0x1d, 0x83, + 0x66, 0x5d, 0xb8, 0x02, 0xd7, 0x9f, 0x48, 0x31, 0x38, 0xb2, 0x27, 0x35, + 0xb1, 0xf4, 0x43, 0x1d, 0x88, 0x22, 0xab, 0x1f, 0x08, 0x20, 0xb3, 0xd2, + 0xbd, 0xf3, 0x14, 0x22, 0x42, 0x58, 0xe1, 0xdc, 0x3d, 0x53, 0x82, 0xbd, + 0x07, 0xb1, 0xbd, 0xb5, 0x76, 0x07, 0xa2, 0xac, 0xf6, 0xda, 0xf5, 0x31, + 0xbd, 0x8b, 0x40, 0x4b, 0xdf, 0xee, 0xb2, 0x0c, 0xfc, 0x2b, 0xd8, 0x4e, + 0xd5, 0x5a, 0x02, 0xa8, 0xaf, 0xe0, 0xf6, 0x34, 0x48, 0x6d, 0xaf, 0x8d, + 0x54, 0x51, 0x5b, 0xf9, 0x6d, 0x40, 0x30, 0xa7, 0xc5, 0x64, 0x5a, 0xb5, + 0xa3, 0xc6, 0x20, 0x3f, 0x94, 0xca, 0x8b, 0xa0, 0xa0, 0xb7, 0x33, 0x0b, + 0x80, 0x67, 0x8f, 0xca, 0x6d, 0xd5, 0xa0, 0x1c, 0xf1, 0xdb, 0x60, 0x2a, + 0xa0, 0x28, 0x2d, 0xa3, 0x77, 0xa1, 0xca, 0xf4, 0x30, 0x8e, 0xde, 0x16, + 0x79, 0xdd, 0xa1, 0x81, 0x99, 0xd2, 0x78, 0x2d, 0xa4, 0x8a, 0x89, 0xa3, + 0xe7, 0x7f, 0x8b, 0x94, 0xe9, 0xe4, 0x6a, 0x0c, 0x1e, 0x18, 0x94, 0x9e, + 0x31, 0xdd, 0x75, 0xf7, 0x92, 0x86, 0x6d, 0x77, 0x96, 0x35, 0x2e, 0x1f, + 0x58, 0xa6, 0xef, 0x38, 0xbe, 0x1c, 0x4c, 0x95, 0xfd, 0x7e, 0x91, 0x90, + 0x97, 0xbb, 0xee, 0xa6, 0xab, 0x3f, 0x9c, 0x96, 0x1b, 0x6e, 0x48, 0x90, + 0xeb, 0x90, 0xac, 0xb8, 0x49, 0xe4, 0x08, 0x88, 0x91, 0x2a, 0x60, 0xf9, + 0xd4, 0x36, 0x05, 0x1e, 0xe3, 0x50, 0x2c, 0x3d, 0x5e, 0xe0, 0xbc, 0xa4, + 0xad, 0xf9, 0xdc, 0x4f, 0x8a, 0xd8, 0x87, 0x6e, 0x43, 0x32, 0x05, 0xac, + 0x95, 0x54, 0x98, 0x00, 0x30, 0x03, 0xbe, 0xff, 0xa9, 0x59, 0x30, 0x14, + 0xfd, 0x73, 0xa6, 0x65, 0x40, 0xd1, 0x0e, 0xfc, 0x5b, 0xb0, 0x24, 0xcf, + 0x37, 0x76, 0x4c, 0xb6, 0xad, 0xd3, 0xd5, 0xbf, 0x70, 0x37, 0xfb, 0x4a, + 0x09, 0xe3, 0xc0, 0x7e, 0xf8, 0xb2, 0xc5, 0xbd, 0x4c, 0x10, 0x8e, 0x4a, + 0xc2, 0xa7, 0x6d, 0x7c, 0x22, 0x96, 0xb1, 0xc1, 0xe3, 0xf1, 0x9e, 0x7e, + 0x4b, 0xd7, 0xb2, 0x07, 0xb2, 0xb8, 0xac, 0x93, 0x59, 0x37, 0xf7, 0x6d, + 0x33, 0xa2, 0x9d, 0xb7, 0xb9, 0x07, 0x7b, 0x97, 0x0a, 0xd6, 0x97, 0xaa, + 0xec, 0x58, 0xb3, 0x93, 0x10, 0x99, 0x7e, 0x45, 0x4a, 0xf3, 0x0b, 0x23, + 0x23, 0xd1, 0x40, 0x1e, 0xac, 0xdc, 0x45, 0x08, 0x16, 0x0b, 0xfc, 0x94, + 0xf8, 0x11, 0x3d, 0xd7, 0xf3, 0xbf, 0x32, 0x38, 0x73, 0x3e, 0x2d, 0xc1, + 0xbe, 0xe6, 0xfa, 0x10, 0xee, 0xc8, 0xf9, 0xaf, 0x5e, 0xcb, 0x8c, 0x62, + 0x0c, 0xf7, 0x5d, 0x2d, 0xf7, 0x31, 0xca, 0xcb, 0x57, 0xd2, 0xc8, 0xd9, + 0xbf, 0x82, 0xb7, 0x32, 0x16, 0xb9, 0x48, 0x80, 0x17, 0x93, 0xc9, 0x16, + 0xe6, 0x10, 0x1d, 0x26, 0x86, 0xc1, 0x2f, 0xb9, 0x6d, 0xb4, 0x6a, 0x7c, + 0x24, 0xc5, 0x07, 0xf7, 0x40, 0x5e, 0xe4, 0xc1, 0xda, 0xb1, 0xcd, 0xcb, + 0xe5, 0x6f, 0x57, 0x0a, 0x11, 0x9c, 0xfb, 0x62, 0xaa, 0x2a, 0xb3, 0xee, + 0x22, 0x15, 0x10, 0x9a, 0xea, 0x9b, 0x54, 0x25, 0xa0, 0x60, 0xdf, 0x4a, + 0x2e, 0x35, 0x15, 0x02, 0x5d, 0x1c, 0x61, 0x4f, 0xaa, 0xd4, 0x47, 0x7c, + 0x0a, 0xcc, 0xb4, 0x6f, 0x5d, 0xd9, 0x86, 0x75, 0x29, 0x4b, 0xfe, 0x1e, + 0xa9, 0x14, 0xfd, 0x55, 0x9e, 0x2c, 0xa2, 0x77, 0xb7, 0xef, 0x58, 0x6d, + 0x7c, 0xce, 0x89, 0xe8, 0xd2, 0x28, 0xdb, 0xef, 0x2e, 0x8c, 0x3c, 0xca, + 0xe6, 0x66, 0x17, 0xf8, 0x3f, 0x14, 0x41, 0x99, 0x9b, 0x00, 0x84, 0x1f, + 0x8f, 0x02, 0x75, 0x5d, 0x90, 0x41, 0x3e, 0x81, 0xbf, 0xa0, 0x0c, 0x66, + 0xdb, 0x44, 0xc6, 0x4e, 0x62, 0x29, 0x9c, 0x70, 0x3c, 0xa9, 0xda, 0x1b, + 0xee, 0xd1, 0xab, 0x5d, 0xb1, 0x5f, 0x84, 0xc5, 0x19, 0x4a, 0xdf, 0x74, + 0xbc, 0xc1, 0xf4, 0x9d, 0x54, 0x5a, 0x34, 0x41, 0xc8, 0x93, 0xe5, 0xc7, + 0x05, 0xe3, 0x83, 0xb7, 0x24, 0x26, 0x6b, 0xaf, 0x77, 0x03, 0xcb, 0x1b, + 0xe0, 0x2d, 0xdc, 0x1c, 0xd9, 0xac, 0xe1, 0xe8, 0x55, 0x3a, 0x53, 0x23, + 0x13, 0xc7, 0x1f, 0xd2, 0x4d, 0xc2, 0x9c, 0xc9, 0x9a, 0xc8, 0x3e, 0xfa, + 0x29, 0xdc, 0xb1, 0xba, 0x9e, 0x7c, 0xef, 0x7d, 0x3c, 0x83, 0x67, 0xff, + 0xf0, 0x73, 0xa5, 0x36, 0xb2, 0xe5, 0x24, 0x8b, 0xe2, 0xe4, 0x1b, 0xf4, + 0xb9, 0xee, 0x11, 0xe7, 0xe6, 0xf3, 0x04, 0x7e, 0x5b, 0x28, 0xdd, 0x19, + 0x4b, 0x8b, 0x03, 0x88, 0xe6, 0x02, 0x42, 0x00, 0xdd, 0xa8, 0xfd, 0x45, + 0xbe, 0x3f, 0x9c, 0x21, 0x60, 0x84, 0x55, 0x61, 0x29, 0x85, 0xca, 0x6b, + 0x3a, 0x09, 0x47, 0x11, 0xbe, 0xa4, 0x31, 0x91, 0xb7, 0x46, 0xc2, 0xc9, + 0xdb, 0x0d, 0x3e, 0x7a, 0x5c, 0xeb, 0xc7, 0x0f, 0xc0, 0x9f, 0xf2, 0xef, + 0xb2, 0x16, 0x09, 0x80, 0xf8, 0xef, 0xbd, 0x10, 0x4c, 0x89, 0x46, 0x8b, + 0x5b, 0x12, 0xcc, 0x77, 0x5d, 0xd8, 0x29, 0xc9, 0xea, 0x99, 0xf6, 0x35, + 0x39, 0x20, 0x0a, 0xe9, 0x94, 0x8c, 0x68, 0xb6, 0x69, 0x32, 0x64, 0xe2, + 0x39, 0xbe, 0x57, 0xb8, 0xf7, 0xe0, 0xb0, 0xa6, 0xef, 0xf7, 0xac, 0x00, + 0xc8, 0xbc, 0xc4, 0x7d, 0x71, 0x42, 0x8f, 0x40, 0xca, 0xf9, 0xea, 0x50, + 0xd7, 0xee, 0x90, 0xaa, 0x2d, 0x20, 0x2a, 0x11, 0x5c, 0x74, 0xa4, 0x09, + 0xde, 0x09, 0xee, 0xb8, 0x66, 0x01, 0x86, 0x32, 0xf4, 0x51, 0x36, 0x28, + 0x69, 0x0a, 0x2f, 0x9d, 0x10, 0xcc, 0x42, 0x33, 0x56, 0xc3, 0x8c, 0x91, + 0x12, 0x3d, 0x09, 0x25, 0x4d, 0x25, 0xc5, 0x68, 0x51, 0x7d, 0x6e, 0x95, + 0x43, 0xbc, 0x19, 0x10, 0x14, 0x23, 0x26, 0x5e, 0x1e, 0x01, 0x6e, 0xb2, + 0x03, 0x5e, 0x52, 0x69, 0xa6, 0xc1, 0xa1, 0xaf, 0x7c, 0x92, 0x44, 0x26, + 0x1c, 0x70, 0x84, 0x64, 0x74, 0x47, 0xd2, 0x29, 0x12, 0x06, 0xb2, 0x43, + 0x95, 0x60, 0x29, 0x74, 0x3a, 0x93, 0x7a, 0xa0, 0x03, 0xf0, 0x37, 0x2f, + 0x93, 0x01, 0xd4, 0xf0, 0xa7, 0xb9, 0xa6, 0x8b, 0x6e, 0x8c, 0x55, 0x79, + 0xd2, 0x0a, 0xba, 0xb4, 0x2d, 0x2b, 0x63, 0xfe, 0xf4, 0xeb, 0x79, 0x0e, + 0xac, 0x7f, 0xbe, 0x17, 0x62, 0xc9, 0xd0, 0x7a, 0x4b, 0x9b, 0x1f, 0x44, + 0x10, 0x17, 0x9d, 0x2a, 0xc6, 0x95, 0xf0, 0x8d, 0x88, 0x25, 0x8a, 0x24, + 0xdf, 0xb7, 0x8c, 0xb1, 0x68, 0x62, 0x5c, 0xf0, 0x25, 0xc3, 0x01, 0x3d, + 0xa7, 0xfe, 0x83, 0x62, 0x8c, 0xf3, 0xbf, 0x59, 0x50, 0x2f, 0x64, 0xbc, + 0x5f, 0xe0, 0x4d, 0xc0, 0xf1, 0x06, 0x13, 0x63, 0xbc, 0x1d, 0x7d, 0x57, + 0x92, 0x5a, 0x0d, 0x7a, 0xc6, 0xa7, 0x25, 0x3e, 0x79, 0x98, 0x2e, 0x68, + 0xfa, 0xbc, 0xf2, 0xa0, 0x6f, 0xe4, 0x4e, 0xbc, 0xae, 0x3f, 0xef, 0x34, + 0x00, 0x3c, 0x87, 0xb5, 0x13, 0x12, 0x22, 0xef, 0xad, 0x0e, 0x6e, 0xb2, + 0x42, 0x81, 0xd1, 0x8f, 0x04, 0x92, 0xc3, 0x73, 0xc2, 0x3e, 0x7d, 0xc3, + 0x9e, 0x28, 0x27, 0xe6, 0x9e, 0xc9, 0x61, 0x16, 0x1b, 0x42, 0xe2, 0xdc, + 0x01, 0x18, 0xf8, 0x20, 0x08, 0xb0, 0xf7, 0x65, 0x6e, 0xdd, 0x9f, 0x5b, + 0xd0, 0x63, 0x98, 0x9b, 0x0c, 0x41, 0xe9, 0x33, 0xf7, 0xd7, 0xef, 0x5b, + 0x3a, 0xfb, 0x8a, 0x6b, 0x6c, 0x15, 0xf0, 0xe0, 0x07, 0x75, 0x58, 0xb9, + 0xf5, 0x30, 0xb5, 0xa3, 0x45, 0x3d, 0x0a, 0x60, 0x22, 0x97, 0x4b, 0x3c, + 0xa4, 0x04, 0xae, 0xe5, 0x0b, 0xa8, 0x56, 0xbf, 0x66, 0x62, 0xdb, 0x9b, + 0x59, 0xfa, 0xe1, 0xc0, 0x89, 0x02, 0xa3, 0x65, 0xd7, 0x83, 0x54, 0xbf, + 0x65, 0x85, 0xea, 0xcb, 0xb0, 0xc7, 0x2d, 0x36, 0xac, 0x06, 0x65, 0xe5, + 0xcd, 0x49, 0xc6, 0x0f, 0xa6, 0x35, 0x8f, 0xa7, 0x46, 0x6b, 0x60, 0x39, + 0xc5, 0x21, 0xbc, 0x22, 0xcd, 0x16, 0x2d, 0xc8, 0x55, 0x7c, 0xa8, 0x36, + 0xaf, 0x63, 0xb1, 0xec, 0xad, 0x44, 0xaa, 0x2f, 0x5c, 0xb0, 0x78, 0x7f, + 0xdf, 0xec, 0x23, 0x23, 0x9e, 0x65, 0x48, 0x90, 0x28, 0x3e, 0x26, 0x78, + 0x0d, 0x08, 0xd8, 0x21, 0x2c, 0x9c, 0x58, 0x94, 0xd3, 0xfc, 0x2b, 0x24, + 0x6e, 0xb4, 0x39, 0x3a, 0x60, 0xc9, 0x51, 0xdb, 0x10, 0x3f, 0x06, 0xff, + 0xd7, 0x07, 0x13, 0xad, 0x18, 0xf5, 0x83, 0x03, 0x49, 0x5d, 0x07, 0x98, + 0xde, 0x78, 0xc8, 0x28, 0x18, 0x5c, 0xa5, 0x3f, 0x43, 0x4e, 0x99, 0xfb, + 0x30, 0x82, 0xba, 0x95, 0x58, 0xd7, 0xfc, 0x79, 0x90, 0x9a, 0x6d, 0xf0, + 0x3d, 0xc7, 0xa4, 0x57, 0x74, 0x29, 0xfe, 0x6e, 0xb1, 0x0e, 0x51, 0x4f, + 0x3e, 0xfd, 0xb1, 0x48, 0xa6, 0x24, 0xb8, 0xbd, 0x45, 0xcf, 0xe9, 0xbc, + 0xa9, 0x99, 0x12, 0x7f, 0xc7, 0xf7, 0xd4, 0xf3, 0x91, 0x36, 0x1f, 0x24, + 0x29, 0x32, 0xf8, 0x8a, 0x2e, 0xf5, 0xe6, 0x71, 0x42, 0x64, 0x3a, 0xf3, + 0x8d, 0xd5, 0xb2, 0x4f, 0x55, 0xc4, 0xe8, 0xa4, 0x70, 0x5e, 0x0f, 0x58, + 0xb9, 0x12, 0xa1, 0x2f, 0xe1, 0x0c, 0x99, 0xac, 0x99, 0x5c, 0x0c, 0xa3, + 0x5c, 0x25, 0x3e, 0xa5, 0x09, 0x76, 0x04, 0x26, 0xcc, 0x1c, 0x68, 0xf6, + 0x32, 0xaf, 0xbc, 0x23, 0x82, 0x0b, 0xd1, 0x84, 0xa9, 0xc6, 0x35, 0xe8, + 0x63, 0xbf, 0x11, 0x06, 0xb2, 0xf1, 0x8e, 0x57, 0x30, 0xd4, 0xd6, 0x9f, + 0x9d, 0xe9, 0xf5, 0x50, 0xf8, 0xc3, 0x5e, 0xec, 0xa6, 0x0a, 0xa9, 0x32, + 0x10, 0xbb, 0x16, 0xe0, 0x65, 0x58, 0x9d, 0xee, 0xea, 0x1d, 0xd8, 0x38, + 0x65, 0xa3, 0xe5, 0x03, 0x9e, 0x9e, 0xb2, 0xaf, 0x22, 0x27, 0xfe, 0x67, + 0x3a, 0xde, 0xea, 0xf2, 0x18, 0x8e, 0x7c, 0x7a, 0xdd, 0x40, 0x95, 0x7e, + 0x77, 0x16, 0x5b, 0xb4, 0x51, 0x6e, 0x7a, 0x0c, 0xa2, 0x17, 0xba, 0x58, + 0x6f, 0xa3, 0xb5, 0xc0, 0x0b, 0x58, 0x0f, 0x11, 0x76, 0xe5, 0x97, 0xb2, + 0xdc, 0x35, 0xe1, 0x36, 0xc0, 0xf2, 0x88, 0xff, 0xc7, 0xcb, 0x9f, 0x83, + 0xa1, 0x8e, 0xdc, 0x9a, 0xf0, 0x70, 0x51, 0x66, 0x63, 0xe1, 0x73, 0x88, + 0x08, 0x84, 0x12, 0xef, 0x6c, 0xef, 0x8d, 0x9a, 0xc3, 0xf3, 0xd9, 0x4a, + 0x5a, 0x94, 0x53, 0xfd, 0x56, 0xba, 0xe4, 0xe9, 0x5d, 0x73, 0x7f, 0x0d, + 0x96, 0xf0, 0xe1, 0x4a, 0x8c, 0x0f, 0xea, 0x8f, 0xdb, 0xc6, 0x59, 0xe7, + 0xc5, 0x62, 0xe9, 0xfc, 0x84, 0xe4, 0x3b, 0x99, 0x0f, 0xbd, 0xd4, 0xe4, + 0xec, 0x3a, 0xa2, 0x81, 0x50, 0xe9, 0xa7, 0xf3, 0x9f, 0xe9, 0xf9, 0x8a, + 0xd1, 0x5a, 0xb8, 0xd5, 0x47, 0x18, 0xdf, 0x3b, 0xaa, 0x9d, 0x33, 0xe0, + 0x00, 0x5c, 0x95, 0x91, 0x88, 0xca, 0xa7, 0xb3, 0x7b, 0xba, 0x8b, 0xd0, + 0x76, 0x59, 0x89, 0xfb, 0x7a, 0x34, 0x08, 0x9a, 0xaf, 0x9a, 0xfc, 0xdb, + 0x1f, 0xe5, 0xcf, 0xf5, 0xc5, 0xcd, 0xcc, 0x4b, 0x78, 0x39, 0xf0, 0x2f, + 0x1e, 0x5e, 0x0e, 0xa0, 0x7d, 0xc6, 0x15, 0xa6, 0xf4, 0xed, 0xb2, 0x34, + 0x42, 0x7f, 0xff, 0x83, 0x33, 0x33, 0x7e, 0xbe, 0x7f, 0x57, 0x3f, 0x3d, + 0x8b, 0x38, 0xbb, 0xfe, 0x7f, 0x2d, 0x92, 0x7b, 0x5b, 0x3e, 0x12, 0x4e, + 0xe7, 0xf0, 0x55, 0x92, 0x2d, 0x2a, 0x39, 0xb9, 0x71, 0x9d, 0xca, 0xa9, + 0x05, 0xc1, 0xb7, 0x32, 0x3a, 0x83, 0xff, 0x89, 0xdf, 0xd0, 0x13, 0x95, + 0xa1, 0x9a, 0xb3, 0x42, 0xe8, 0x8b, 0xaa, 0xcc, 0xa3, 0x47, 0xc1, 0x1c, + 0xb2, 0xed, 0xde, 0x7b, 0x67, 0xab, 0x4c, 0xa6, 0x90, 0x61, 0xeb, 0xd3, + 0x24, 0xef, 0xc8, 0x89, 0xc7, 0xd3, 0x9a, 0x97, 0x27, 0x2d, 0x86, 0xb2, + 0x7e, 0xb9, 0x1f, 0x41, 0xd6, 0x25, 0x7e, 0x39, 0xf2, 0x90, 0xaa, 0x41, + 0x9c, 0x3e, 0xb0, 0xdb, 0x75, 0xcb, 0x94, 0x0a, 0x1b, 0x2b, 0xcd, 0xc7, + 0x11, 0x47, 0xb8, 0xf7, 0x87, 0xf5, 0xb1, 0x16, 0xe9, 0xba, 0x50, 0x67, + 0x52, 0x12, 0x29, 0x27, 0x9f, 0x4d, 0x1c, 0xbb, 0xa3, 0x2c, 0xab, 0x63, + 0x08, 0xd1, 0x38, 0xdc, 0x89, 0x7f, 0x4a, 0x68, 0x9b, 0x70, 0xa4, 0x98, + 0x0a, 0x3a, 0xf8, 0xfe, 0xad, 0x4c, 0x8d, 0x3b, 0x77, 0xa7, 0x9a, 0x43, + 0x1f, 0x72, 0xc0, 0xb0, 0xa8, 0x7c, 0x3d, 0x5f, 0xec, 0xac, 0x19, 0x04, + 0x42, 0x6a, 0xdf, 0x62, 0x6a, 0x33, 0x02, 0x44, 0x45, 0xf5, 0xdf, 0x84, + 0x89, 0x9f, 0x94, 0x1e, 0xc1, 0x87, 0x20, 0x01, 0xfb, 0xc0, 0x53, 0xb5, + 0x3c, 0x90, 0x91, 0xb7, 0xf6, 0xff, 0x47, 0x6d, 0x10, 0xcd, 0xba, 0x94, + 0x66, 0x84, 0xbd, 0xbc, 0x78, 0x27, 0x81, 0xd4, 0x99, 0xbd, 0xf5, 0xea, + 0xf7, 0x68, 0x08, 0x06, 0xfe, 0xde, 0xf0, 0xa6, 0x82, 0x75, 0x8b, 0x79, + 0x9c, 0x10, 0xb2, 0xf2, 0xa5, 0xa1, 0x9f, 0xef, 0x26, 0x0f, 0x0d, 0x15, + 0x12, 0x96, 0xa0, 0x0a, 0x36, 0xc9, 0x97, 0xfa, 0x09, 0x08, 0x3f, 0x9e, + 0x85, 0x21, 0x89, 0xb7, 0xec, 0xc5, 0x28, 0xce, 0x72, 0xfb, 0x0f, 0xeb, + 0xca, 0xfc, 0x88, 0xa1, 0x0f, 0xfd, 0xb0, 0xd2, 0x6f, 0x32, 0xbc, 0x06, + 0x27, 0xc3, 0xce, 0x0d, 0x55, 0xea, 0xc4, 0x56, 0xbc, 0xac, 0x2d, 0x2a, + 0xda, 0x97, 0x76, 0x73, 0x68, 0x7f, 0xd2, 0xb0, 0xbc, 0xa6, 0xc4, 0xad, + 0xb0, 0xad, 0x66, 0xb6, 0xc4, 0x21, 0x71, 0x9b, 0xa2, 0xde, 0xa8, 0x0e, + 0x8d, 0xa6, 0xc5, 0x4a, 0xa5, 0xce, 0xd0, 0x3b, 0xa3, 0xdf, 0x0f, 0xb8, + 0x0b, 0x50, 0x53, 0x53, 0xba, 0x27, 0xd9, 0xc2, 0xd6, 0x1f, 0xd5, 0xd3, + 0x4f, 0x0d, 0x72, 0x5f, 0x9f, 0xdf, 0x8f, 0x98, 0x1a, 0xfc, 0xdf, 0x89, + 0xd6, 0x25, 0xff, 0x56, 0x5b, 0xe0, 0xe4, 0x8e, 0xa4, 0x16, 0x77, 0x7a, + 0xe5, 0x01, 0x94, 0x23, 0xb5, 0xb5, 0xf0, 0x05, 0xf5, 0x0d, 0x87, 0x0f, + 0x9b, 0x4e, 0x5e, 0x3a, 0x1b, 0x83, 0xd2, 0x85, 0xb5, 0xbe, 0x17, 0xf9, + 0xd9, 0x67, 0x00, 0x72, 0x1c, 0x2b, 0xf7, 0xa9, 0x27, 0x2b, 0xf3, 0x18, + 0x70, 0x9a, 0x7d, 0x5a, 0x36, 0x42, 0x6d, 0x16, 0x59, 0x58, 0x3f, 0x1a, + 0x65, 0xeb, 0x16, 0x59, 0xfb, 0xc0, 0xb7, 0x9c, 0xb6, 0x83, 0xd2, 0x2e, + 0xe2, 0x99, 0x8d, 0xd3, 0xd1, 0xfd, 0x55, 0x9f, 0x0c, 0x42, 0x02, 0x9c, + 0x6c, 0x28, 0x51, 0xcf, 0xe2, 0x0a, 0x19, 0x18, 0x3d, 0xf1, 0x32, 0x41, + 0x9d, 0xf1, 0x9e, 0x25, 0xa3, 0x63, 0xc0, 0x50, 0xb0, 0x62, 0x86, 0x1e, + 0x6b, 0xe4, 0x33, 0xe0, 0x16, 0xbd, 0x59, 0xea, 0x32, 0x47, 0x56, 0xf3, + 0x33, 0xd3, 0x4b, 0xb9, 0xf0, 0x1c, 0xbf, 0xdc, 0x47, 0x5c, 0xfa, 0x20, + 0x8a, 0xda, 0x6b, 0x2b, 0x45, 0xdb, 0x36, 0xfa, 0x25, 0x7e, 0xa4, 0x17, + 0x55, 0x7b, 0x60, 0x36, 0x43, 0x57, 0x99, 0x18, 0x1e, 0xfe, 0xdd, 0x98, + 0xcc, 0xf1, 0xf9, 0x53, 0xbb, 0xf8, 0x81, 0x94, 0xcc, 0xa5, 0x1b, 0xd2, + 0x95, 0x6c, 0x25, 0x1d, 0x76, 0x96, 0x83, 0x6a, 0xc8, 0x58, 0x1f, 0x5f, + 0x70, 0x12, 0xaa, 0x4c, 0x09, 0x19, 0xb3, 0xbc, 0x93, 0xcc, 0x9e, 0x8b, + 0x53, 0xab, 0xcd, 0x1f, 0x8d, 0x22, 0xfa, 0x75, 0xb3, 0x77, 0x1b, 0x0c, + 0x37, 0xa8, 0x8d, 0xc5, 0x22, 0x17, 0x49, 0x5c, 0xdf, 0xf2, 0x94, 0x34, + 0x7f, 0x64, 0x94, 0x05, 0x91, 0x5a, 0x16, 0x2c, 0x57, 0xeb, 0x1c, 0x1d, + 0xf8, 0xe4, 0x3a, 0x37, 0x91, 0x5e, 0x84, 0x40, 0x99, 0x1e, 0x22, 0x53, + 0x44, 0xfb, 0x08, 0xce, 0x1c, 0x53, 0xbc, 0xaf, 0x95, 0xef, 0xa8, 0x4a, + 0x40, 0x52, 0xd6, 0xdc, 0xa5, 0xb6, 0x8c, 0xe6, 0xa5, 0x51, 0xcd, 0xa8, + 0xd4, 0x9f, 0x1a, 0x69, 0x2c, 0x38, 0x8f, 0xdf, 0xb0, 0x1e, 0xc9, 0x33, + 0x6a, 0xc3, 0x33, 0x22, 0x0c, 0x75, 0x47, 0xe3, 0x75, 0xba, 0x63, 0xde, + 0x3e, 0x65, 0xf0, 0x4d, 0x6c, 0x00, 0xe7, 0x03, 0x0d, 0x3d, 0x14, 0xcf, + 0xd9, 0x8a, 0xd2, 0x80, 0xcb, 0x01, 0xc9, 0x0a, 0x71, 0x0c, 0xe5, 0xbe, + 0x76, 0xe2, 0xe1, 0xac, 0x2c, 0x1f, 0xf5, 0x92, 0x12, 0xbb, 0xb3, 0x18, + 0x93, 0xdc, 0x63, 0xf0, 0xbc, 0xd5, 0x10, 0x03, 0xc7, 0x39, 0x45, 0x3b, + 0xa1, 0xfb, 0xe8, 0xcc, 0x46, 0xbf, 0x82, 0xa8, 0x77, 0x98, 0xd9, 0x1a, + 0x73, 0xc0, 0x61, 0x80, 0xf1, 0x88, 0x9e, 0xa9, 0x39, 0xa9, 0xd9, 0x85, + 0x61, 0x7e, 0x50, 0xb8, 0x57, 0x2e, 0xb1, 0x73, 0x58, 0xd4, 0xa8, 0x82, + 0x29, 0xc8, 0x5f, 0x8d, 0x97, 0xd5, 0xc2, 0xd7, 0x71, 0xe0, 0xd3, 0x7a, + 0x05, 0x07, 0x22, 0xb1, 0xeb, 0x2c, 0xea, 0x47, 0xdb, 0x55, 0x3e, 0x1b, + 0x4a, 0x42, 0x26, 0xd3, 0x22, 0xea, 0x48, 0x65, 0xaa, 0x53, 0xb9, 0xfa, + 0xd1, 0xa3, 0x4f, 0xb8, 0x0a, 0x23, 0x66, 0x7b, 0x71, 0xfc, 0x1a, 0x5a, + 0x31, 0xfb, 0xc7, 0x35, 0xd4, 0x3f, 0xd7, 0x8a, 0x54, 0xab, 0xee, 0x89, + 0x4b, 0xb8, 0xc8, 0x1c, 0xaa, 0x09, 0x46, 0x2c, 0xad, 0xc3, 0x9e, 0xde, + 0x8f, 0x40, 0xe4, 0xa4, 0x96, 0xe3, 0x51, 0x97, 0xe8, 0x68, 0x5d, 0x13, + 0x49, 0x7f, 0x6f, 0x55, 0x37, 0x65, 0xba, 0xb8, 0x39, 0xbb, 0x3c, 0x2f, + 0x7e, 0x06, 0x9d, 0x18, 0x46, 0xba, 0x1c, 0x09, 0x15, 0x55, 0x36, 0x45, + 0x95, 0xc4, 0x91, 0xcc, 0x86, 0xeb, 0xda, 0x14, 0xc0, 0x99, 0x96, 0xd0, + 0x99, 0xb2, 0x4c, 0xb0, 0x90, 0x82, 0x18, 0x5f, 0xe1, 0x49, 0x77, 0xe4, + 0x24, 0xcf, 0x1e, 0xcd, 0xd3, 0xa8, 0x54, 0x52, 0x6f, 0xe0, 0x58, 0x67, + 0xf3, 0xaf, 0xe1, 0x23, 0x7a, 0xb4, 0xa6, 0x60, 0x3e, 0xb5, 0xe2, 0x42, + 0x54, 0x36, 0xae, 0x1e, 0xc7, 0x01, 0x3e, 0x24, 0xa8, 0x94, 0x92, 0x9a, + 0x71, 0x49, 0x53, 0xb8, 0xa2, 0x8a, 0x03, 0x5e, 0x86, 0x67, 0xa1, 0x9a, + 0x73, 0x87, 0x0c, 0xb0, 0x04, 0xdb, 0x79, 0x46, 0x5d, 0xcd, 0xe0, 0x3f, + 0x7e, 0xd1, 0x8f, 0x6c, 0x3a, 0xd6, 0xc2, 0x07, 0x60, 0x2d, 0x32, 0xe2, + 0xda, 0x58, 0x50, 0xee, 0x3d, 0xce, 0x70, 0xfe, 0xaa, 0x04, 0xcb, 0xde, + 0x46, 0xd9, 0x9c, 0xb2, 0xc7, 0x31, 0xb3, 0xdb, 0x67, 0x1d, 0x23, 0x69, + 0x81, 0x4e, 0x14, 0x65, 0x82, 0xe9, 0x65, 0xd2, 0xaf, 0xa3, 0x57, 0xfe, + 0xa4, 0xb3, 0x81, 0xac, 0xc3, 0x30, 0x3b, 0x95, 0xd8, 0x66, 0x48, 0xae, + 0xd4, 0xff, 0xe8, 0x46, 0x4b, 0x54, 0x03, 0x8c, 0x7b, 0xb5, 0xa6, 0xa3, + 0x1b, 0x3b, 0xc1, 0x0c, 0x91, 0x1b, 0xbf, 0x17, 0x42, 0xfa, 0xb1, 0x3e, + 0xa9, 0x16, 0xe8, 0x72, 0x07, 0x72, 0x58, 0x86, 0x3c, 0x60, 0xea, 0xb4, + 0x27, 0xfe, 0xaa, 0x3d, 0x0d, 0xb5, 0xb9, 0x1f, 0x71, 0x63, 0x48, 0xde, + 0xd9, 0x11, 0xa0, 0x08, 0x71, 0xa8, 0xaa, 0x0c, 0x58, 0x7c, 0x45, 0x08, + 0x28, 0x0b, 0xd5, 0xd6, 0x89, 0xeb, 0xd4, 0xcd, 0xee, 0x7f, 0xb8, 0x0c, + 0x11, 0xc6, 0xcc, 0x26, 0x9e, 0x61, 0x80, 0xcc, 0x8d, 0xd0, 0x62, 0x7a, + 0x23, 0xdf, 0x8c, 0x93, 0xf7, 0x7b, 0x7f, 0x4f, 0x17, 0x27, 0x94, 0x0e, + 0xaa, 0x40, 0x0d, 0x80, 0xf4, 0x41, 0x14, 0xcc, 0x69, 0xa9, 0xd5, 0xe4, + 0x78, 0x99, 0xc8, 0x73, 0x7b, 0x2c, 0x45, 0x7c, 0x35, 0x39, 0xe1, 0xf1, + 0x97, 0x9e, 0x63, 0xc3, 0x86, 0x57, 0xd1, 0xac, 0xec, 0xbc, 0xc9, 0x58, + 0xd4, 0xd6, 0xa0, 0xd3, 0x01, 0xee, 0x60, 0x01, 0x6a, 0xa9, 0x5b, 0xe9, + 0x08, 0x18, 0x5c, 0x2e, 0x72, 0x61, 0x80, 0xf7, 0x33, 0x0b, 0x35, 0x7a, + 0xcc, 0x98, 0xdc, 0x84, 0xf8, 0x78, 0x2b, 0x52, 0x4e, 0x4c, 0xcc, 0xfa, + 0xf1, 0x4e, 0x5b, 0x8b, 0xdd, 0x7c, 0x1e, 0x1d, 0x3b, 0xf3, 0xce, 0x8d, + 0xa5, 0x36, 0x77, 0x8d, 0x24, 0xe9, 0x24, 0x33, 0x21, 0x20, 0x57, 0xbf, + 0xcf, 0xf3, 0x0d, 0x69, 0x52, 0xe3, 0x88, 0x65, 0x9d, 0x66, 0xae, 0x7a, + 0x43, 0x90, 0x37, 0x8b, 0xe1, 0xe7, 0x39, 0x3d, 0x0a, 0x23, 0x1c, 0xee, + 0x3f, 0x66, 0xe5, 0xaf, 0x51, 0x83, 0x2f, 0xbd, 0x37, 0x69, 0xed, 0x15, + 0xc9, 0x07, 0x00, 0x2f, 0x9c, 0xcf, 0x99, 0xfa, 0xfe, 0x4e, 0x41, 0x58, + 0x0c, 0xfd, 0x7b, 0x80, 0xd6, 0x00, 0x7b, 0x5e, 0x1d, 0x87, 0x87, 0x54, + 0xa3, 0x61, 0xb1, 0xb0, 0x23, 0x91, 0x2b, 0x3a, 0x07, 0x41, 0x4d, 0x4b, + 0x7f, 0xbd, 0x8e, 0x6c, 0xa6, 0x46, 0x46, 0x1c, 0xbf, 0xc1, 0x87, 0x74, + 0x9d, 0x1c, 0xca, 0x32, 0x3e, 0x6a, 0x0f, 0xa6, 0xc4, 0x63, 0x38, 0x0d, + 0x30, 0xde, 0x82, 0x77, 0xc7, 0xbc, 0xc1, 0x08, 0xb7, 0x95, 0xf7, 0xfb, + 0x21, 0x00, 0xb2, 0xb9, 0xa1, 0x8c, 0x19, 0xa7, 0x9f, 0x83, 0xba, 0x1c, + 0xe0, 0x2e, 0x09, 0x2d, 0x75, 0xbe, 0x5d, 0x2d, 0xe6, 0x63, 0xd2, 0x7f, + 0xa8, 0xf6, 0x50, 0xa2, 0xe8, 0x74, 0x10, 0xd9, 0x3a, 0x53, 0xf7, 0xce, + 0xf4, 0xaf, 0xab, 0x11, 0xd6, 0x71, 0xd6, 0x61, 0x17, 0xf8, 0xc4, 0x45, + 0x34, 0xf7, 0x00, 0x9a, 0x73, 0xa6, 0x3f, 0x7a, 0x05, 0x06, 0x80, 0x3f, + 0xa5, 0x63, 0x7c, 0xec, 0x12, 0x8b, 0x1e, 0xc3, 0xa2, 0xa1, 0x87, 0x5c, + 0x7d, 0xed, 0x99, 0x19, 0x84, 0xd8, 0xf8, 0x20, 0x55, 0xf6, 0x72, 0x70, + 0x55, 0xb8, 0x2d, 0xdb, 0x8f, 0x15, 0x80, 0x5b, 0x03, 0x2e, 0x0b, 0xac, + 0x7e, 0xdc, 0x4f, 0xdd, 0xb2, 0x48, 0xb5, 0xc3, 0x11, 0x40, 0x8d, 0x19, + 0x1a, 0xfe, 0x76, 0x10, 0xb8, 0xd5, 0xf8, 0x99, 0x2a, 0x54, 0x9d, 0x33, + 0x68, 0xe5, 0xf2, 0x74, 0x2d, 0x09, 0xe1, 0x43, 0x5b, 0x21, 0xfc, 0x8f, + 0x33, 0x4b, 0xfa, 0x40, 0xf8, 0x62, 0xbf, 0x5a, 0xa3, 0x64, 0xf3, 0x48, + 0x4e, 0x79, 0x96, 0x38, 0xf6, 0x74, 0x44, 0xb0, 0x1b, 0x4a, 0xed, 0x6a, + 0xec, 0xb4, 0x2c, 0xde, 0x20, 0x22, 0x28, 0xd4, 0x1e, 0x96, 0x63, 0x86, + 0x23, 0xe6, 0x66, 0x13, 0xbd, 0x0f, 0xfe, 0x36, 0xe8, 0x3a, 0x01, 0x88, + 0x50, 0x30, 0x46, 0x45, 0xa6, 0xed, 0x95, 0xbf, 0x47, 0xe8, 0x7c, 0xbe, + 0x39, 0x2e, 0x95, 0x91, 0x30, 0x1b, 0xc8, 0x9b, 0xce, 0x4c, 0xb0, 0x08, + 0x7b, 0xf4, 0x5e, 0xa1, 0xad, 0x8e, 0x25, 0x96, 0x3e, 0x52, 0x29, 0x5c, + 0xe6, 0xaa, 0x24, 0xf9, 0xef, 0x0a, 0xf3, 0xa6, 0xb1, 0xcf, 0xe4, 0x7a, + 0x27, 0x92, 0x11, 0xd3, 0x40, 0xd9, 0x4c, 0x40, 0xe9, 0x1b, 0x3e, 0x8b, + 0x74, 0xab, 0x6b, 0x7a, 0x86, 0x29, 0x5c, 0x4f, 0xc2, 0xd4, 0x8e, 0xee, + 0xd0, 0x83, 0x6b, 0x71, 0x5c, 0x0d, 0x95, 0x27, 0x09, 0x4c, 0xaa, 0x69, + 0x38, 0x63, 0xd9, 0x14, 0x39, 0x13, 0x1b, 0x4b, 0x56, 0x91, 0x00, 0xd7, + 0xe4, 0x7d, 0x4a, 0xf2, 0x40, 0x7a, 0x46, 0xd2, 0x5a, 0xde, 0x62, 0xc9, + 0x79, 0x24, 0x97, 0x91, 0xc1, 0x51, 0xac, 0x03, 0xe5, 0x5f, 0xd5, 0xd6, + 0xb1, 0xf2, 0x2e, 0xc9, 0x9e, 0xb1, 0x16, 0x18, 0xa5, 0x72, 0xb5, 0xa2, + 0xf1, 0x9f, 0xbf, 0xc0, 0x2b, 0x00, 0x9d, 0x1c, 0x3e, 0x2d, 0x3e, 0x7c, + 0x1c, 0xb0, 0xeb, 0x6f, 0x1b, 0x91, 0x5c, 0x52, 0x56, 0x4a, 0x5f, 0xee, + 0x98, 0x8b, 0x11, 0xfb, 0x6d, 0x7a, 0xa9, 0x84, 0x2d, 0xcb, 0x7f, 0x74, + 0xc3, 0xe7, 0xce, 0xc4, 0x6b, 0x17, 0xd2, 0xb8, 0x9e, 0x4b, 0xdb, 0xee, + 0x94, 0x58, 0x74, 0x90, 0xbc, 0x62, 0xb1, 0xe4, 0xa4, 0xa7, 0x65, 0x80, + 0xcd, 0x30, 0xb6, 0xc3, 0x2c, 0xc5, 0x30, 0xb0, 0x97, 0x39, 0x3a, 0x07, + 0x53, 0x1f, 0x7b, 0xac, 0x0a, 0x8e, 0x5b, 0x4f, 0x31, 0x38, 0xde, 0x6d, + 0x9d, 0xbe, 0x9a, 0x8d, 0x37, 0x2d, 0xcb, 0x99, 0x9d, 0xac, 0x6e, 0xeb, + 0xd7, 0x7d, 0x33, 0x09, 0xc1, 0xd8, 0xe5, 0x01, 0x4c, 0xb1, 0x18, 0x68, + 0x54, 0xbe, 0x95, 0x78, 0x76, 0x7c, 0xf3, 0xc7, 0x40, 0x6d, 0xf3, 0x73, + 0x5b, 0xbb, 0x14, 0xde, 0xf8, 0xd7, 0x7f, 0x41, 0xea, 0xc2, 0x61, 0x54, + 0x63, 0xec, 0x2c, 0x4b, 0x9a, 0x47, 0xd4, 0xea, 0xe4, 0xcf, 0xf8, 0xf9, + 0xe3, 0x6e, 0x0f, 0x54, 0x0a, 0x9c, 0x3f, 0xe7, 0xb0, 0xa7, 0x99, 0xf6, + 0x67, 0xaf, 0x47, 0x1e, 0x49, 0xf0, 0x9c, 0x2d, 0x90, 0xa6, 0x5f, 0xf7, + 0xbe, 0x02, 0x51, 0xab, 0x07, 0x43, 0x48, 0xd1, 0xdc, 0xf9, 0x80, 0x9d, + 0xfa, 0xdf, 0x90, 0x5e, 0xf3, 0xc9, 0xff, 0x77, 0x9a, 0x3d, 0xf9, 0x11, + 0x0f, 0x52, 0x0d, 0x6a, 0x18, 0x17, 0x25, 0x5c, 0x61, 0x34, 0x30, 0xfd, + 0x2e, 0xac, 0xfc, 0xdd, 0xd0, 0x17, 0x8a, 0x44, 0x0a, 0xf7, 0x4e, 0x2e, + 0x3b, 0xdd, 0xaf, 0x66, 0x05, 0x37, 0x90, 0x0c, 0xa1, 0xd8, 0x06, 0xfb, + 0x8e, 0x2d, 0x94, 0x2d, 0x4c, 0xdd, 0x31, 0x72, 0x5c, 0xe4, 0x22, 0xe5, + 0x14, 0x6f, 0x93, 0xa3, 0x21, 0x37, 0xa2, 0x9d, 0xef, 0x92, 0xac, 0x6f, + 0x82, 0x52, 0x3d, 0x0d, 0x2b, 0x3f, 0x37, 0xf6, 0x7f, 0x41, 0x9e, 0xdd, + 0x93, 0xde, 0x89, 0x4c, 0x7d, 0x70, 0x6a, 0xde, 0xe1, 0x78, 0xa1, 0x9a, + 0x69, 0x28, 0x1c, 0xd3, 0xca, 0x13, 0x6a, 0x1f, 0x8b, 0x7a, 0x3e, 0x83, + 0xb8, 0xd0, 0xbd, 0x0b, 0x2d, 0x9e, 0xf8, 0x7e, 0x8a, 0x97, 0x79, 0x0e, + 0x28, 0x7d, 0xd6, 0x9d, 0xdd, 0x16, 0x6a, 0xa8, 0xc5, 0x68, 0x02, 0xd6, + 0x39, 0xd7, 0x48, 0xa8, 0x1a, 0x81, 0xf4, 0xcb, 0x3b, 0x32, 0xc4, 0x96, + 0xc6, 0x4d, 0x1a, 0x8d, 0x04, 0xea, 0xc2, 0xc5, 0xb8, 0x23, 0x32, 0xf0, + 0x59, 0x50, 0x99, 0x76, 0x48, 0x92, 0xeb, 0x30, 0x63, 0xa0, 0xa6, 0xe9, + 0xcb, 0x4a, 0x85, 0x49, 0x52, 0xf2, 0x09, 0x4a, 0x99, 0x31, 0x89, 0x7d, + 0x16, 0xb6, 0xeb, 0xb7, 0xb1, 0x96, 0xaa, 0x36, 0x42, 0xb9, 0x2c, 0x82, + 0x2d, 0x7a, 0xa0, 0x75, 0x46, 0x10, 0x5d, 0xd3, 0x5a, 0x32, 0x7d, 0x07, + 0xcd, 0x6f, 0xe7, 0xef, 0xc8, 0x74, 0x96, 0xc5, 0x84, 0xe4, 0x3b, 0x06, + 0xc2, 0xfd, 0xbc, 0x8e, 0x66, 0x37, 0x28, 0xfe, 0xf1, 0xdc, 0x07, 0x37, + 0x57, 0xd3, 0x08, 0x7c, 0xd2, 0x9f, 0x3c, 0x49, 0x7f, 0xfa, 0xf7, 0xce, + 0xe7, 0x2a, 0x88, 0xb6, 0xc9, 0x1a, 0x9c, 0x62, 0x42, 0x37, 0x93, 0x55, + 0xa0, 0x08, 0x65, 0x28, 0xad, 0xdd, 0x10, 0xa4, 0x23, 0x2b, 0xc8, 0x85, + 0xea, 0x32, 0x37, 0x67, 0x82, 0x37, 0x3c, 0x57, 0x13, 0xa2, 0xd3, 0x26, + 0xf9, 0x2c, 0xc7, 0xe0, 0x28, 0x14, 0x9e, 0x5d, 0x02, 0x24, 0x6a, 0x9c, + 0x6b, 0x89, 0xa6, 0x7c, 0x38, 0xb4, 0x3d, 0x6f, 0x27, 0xd3, 0x25, 0xfd, + 0xf9, 0x30, 0xa9, 0xe8, 0x46, 0xc6, 0xf0, 0xf7, 0x6f, 0x53, 0x57, 0x46, + 0x1d, 0x57, 0x1d, 0x96, 0xa9, 0xdc, 0xfd, 0x2e, 0x39, 0x0a, 0xda, 0x71, + 0xf3, 0x6e, 0x4c, 0x18, 0xc5, 0x75, 0xcb, 0x61, 0x66, 0xc6, 0xde, 0xa3, + 0x6d, 0x99, 0x44, 0xf6, 0xf3, 0x14, 0x6e, 0x90, 0x85, 0xef, 0x32, 0x81, + 0x39, 0x0e, 0xdb, 0xd3, 0xff, 0xc3, 0xbf, 0x2b, 0x53, 0xa5, 0xfa, 0xa3, + 0x77, 0xc3, 0x44, 0x96, 0xdb, 0xf5, 0x06, 0x37, 0xe4, 0x62, 0x3f, 0xaa, + 0xe1, 0x59, 0x75, 0x82, 0xef, 0x0d, 0x72, 0x47, 0x28, 0x73, 0x21, 0x15, + 0x07, 0xe6, 0x14, 0x8f, 0xdb, 0x84, 0x96, 0x9a, 0x54, 0x7a, 0x1d, 0xee, + 0x75, 0x14, 0x22, 0xab, 0xad, 0x37, 0xaf, 0x36, 0x2e, 0x24, 0xf6, 0xd8, + 0x29, 0x94, 0xb8, 0x5b, 0x9a, 0xce, 0xe8, 0xa7, 0xbf, 0xd8, 0x85, 0x8c, + 0x9f, 0x75, 0x99, 0x99, 0x10, 0xde, 0xa2, 0x31, 0x4c, 0x90, 0x16, 0xde, + 0xbc, 0x9b, 0x93, 0xf5, 0x85, 0xd2, 0xfa, 0xba, 0x0a, 0x28, 0xf2, 0x3b, + 0xca, 0x12, 0xc2, 0x9e, 0x2e, 0xa9, 0x81, 0x81, 0xd0, 0x2a, 0xf1, 0xf7, + 0x2b, 0xa0, 0xbb, 0xe3, 0x1f, 0x31, 0x5d, 0xe3, 0xc1, 0x4b, 0xc9, 0x5c, + 0x59, 0x47, 0x38, 0x1d, 0xda, 0x6a, 0x44, 0xc3, 0x08, 0x89, 0x35, 0xda, + 0xd1, 0x88, 0x42, 0xa4, 0x0f, 0xbd, 0x80, 0xd1, 0x95, 0x42, 0x65, 0xfa, + 0xd7, 0x81, 0x08, 0x9e, 0xd1, 0xd5, 0x15, 0x52, 0x57, 0x5b, 0x3b, 0x9e, + 0xa6, 0x20, 0x14, 0x1a, 0x81, 0x0c, 0x66, 0x22, 0x1c, 0x7e, 0x44, 0x9a, + 0xb7, 0xb2, 0xc2, 0x55, 0x75, 0x97, 0xe8, 0x40, 0x98, 0xfb, 0x50, 0x6c, + 0x8d, 0x24, 0xd3, 0x3c, 0x75, 0xce, 0x8a, 0x03, 0xb6, 0x68, 0x20, 0x9d, + 0xe5, 0x91, 0xe0, 0xaf, 0xc9, 0xc8, 0x7d, 0x6f, 0x3f, 0x69, 0xb2, 0x88, + 0x83, 0xcc, 0xfb, 0x5c, 0x62, 0xcd, 0x9d, 0x12, 0x63, 0xa6, 0xb4, 0x43, + 0x2f, 0xd7, 0x27, 0x28, 0x4e, 0x6b, 0xde, 0x45, 0xe6, 0xe1, 0xae, 0x83, + 0x59, 0x8c, 0x68, 0x56, 0x4c, 0x91, 0xa6, 0x44, 0xe7, 0xeb, 0xf1, 0x9e, + 0xbe, 0xd3, 0x89, 0x50, 0x67, 0x35, 0x3c, 0x3e, 0x3d, 0x94, 0xb9, 0xd1, + 0xb2, 0x84, 0x03, 0xb2, 0x7b, 0xa7, 0x83, 0x5e, 0x96, 0x96, 0xdf, 0x90, + 0x5b, 0x6a, 0x4a, 0x75, 0x05, 0xab, 0x31, 0xcf, 0x45, 0xfa, 0x88, 0xc7, + 0x6c, 0x02, 0x96, 0xbe, 0xdc, 0x59, 0xdc, 0xf5, 0x05, 0x25, 0x4f, 0x8b, + 0x3a, 0x79, 0xfd, 0x8f, 0xbf, 0x9b, 0xdd, 0x4a, 0x2a, 0x39, 0x87, 0x54, + 0xc8, 0x03, 0x40, 0xea, 0xb9, 0x38, 0x56, 0x8d, 0x5f, 0x4f, 0xe5, 0x29, + 0x35, 0xb1, 0xc0, 0x80, 0x77, 0x4e, 0xe9, 0x6a, 0xba, 0x86, 0xdd, 0xb5, + 0x7a, 0x6f, 0x64, 0xd5, 0xf0, 0x66, 0x65, 0xbf, 0xbf, 0xdf, 0x22, 0x49, + 0xb7, 0xa7, 0xd3, 0xab, 0xb6, 0x76, 0xec, 0xe9, 0xc9, 0xb9, 0xca, 0x9f, + 0x6a, 0xb4, 0xd7, 0xdf, 0xb3, 0x84, 0x21, 0x31, 0x17, 0xe9, 0x40, 0x79, + 0x1a, 0x65, 0x76, 0x64, 0xb6, 0x73, 0x3c, 0x76, 0x47, 0x00, 0xf5, 0x21, + 0x76, 0x6e, 0x20, 0xc4, 0x52, 0x9d, 0x2e, 0x03, 0x60, 0xe2, 0xb5, 0x16, + 0xa3, 0xd8, 0x9b, 0x1f, 0x4f, 0xfa, 0x24, 0x0b, 0xdc, 0xe7, 0x89, 0x24, + 0x2c, 0xb0, 0xe9, 0x30, 0x45, 0x5a, 0x27, 0x7b, 0x6d, 0x5b, 0x0f, 0xf5, + 0xa2, 0xa6, 0xe5, 0x7c, 0x49, 0x1b, 0xa6, 0xcb, 0xdd, 0x15, 0x95, 0xa1, + 0xb9, 0x7b, 0x1d, 0x70, 0xef, 0xd9, 0xe3, 0x26, 0x51, 0x03, 0xc0, 0xda, + 0x6c, 0x6c, 0xbf, 0x29, 0xc7, 0x7f, 0xc4, 0x58, 0xa8, 0x26, 0x9c, 0x67, + 0x80, 0x1c, 0xc2, 0x06, 0xab, 0x78, 0x41, 0xbb, 0x0a, 0x51, 0x49, 0x54, + 0x75, 0xf3, 0xd6, 0x22, 0xb1, 0x22, 0xd4, 0x2b, 0x0d, 0xf3, 0x19, 0x0c, + 0xfc, 0x54, 0x4d, 0x75, 0xe5, 0xfa, 0x3b, 0x97, 0x61, 0x65, 0x46, 0x07, + 0x27, 0x4e, 0x29, 0x76, 0x3d, 0x97, 0x33, 0x2d, 0x12, 0xee, 0x03, 0x90, + 0x52, 0x73, 0x67, 0x96, 0x9d, 0x5f, 0x1f, 0x95, 0x07, 0x04, 0xc2, 0xb0, + 0x7a, 0x30, 0x2e, 0x63, 0x7e, 0x57, 0x92, 0xfc, 0xc0, 0xbb, 0x4e, 0x33, + 0x7d, 0x49, 0xfb, 0x15, 0x7a, 0x3f, 0x8a, 0x56, 0xac, 0x55, 0xbe, 0x39, + 0xf2, 0xd6, 0x68, 0xe7, 0xf2, 0xff, 0x70, 0xf5, 0x86, 0xb7, 0x32, 0x1e, + 0x55, 0x87, 0x1c, 0xd6, 0x8f, 0x77, 0x8b, 0x99, 0xb9, 0x46, 0xbd, 0xde, + 0xfe, 0x81, 0x28, 0xde, 0x68, 0x7c, 0x46, 0xb1, 0x1f, 0xd4, 0xfb, 0x24, + 0xc6, 0x5c, 0xc0, 0x7f, 0x1d, 0x0e, 0xf0, 0x09, 0x4e, 0xd8, 0x9b, 0x0d, + 0x27, 0xec, 0xe8, 0x97, 0x3a, 0xe2, 0xaa, 0x77, 0x44, 0xa9, 0xd0, 0xad, + 0x4f, 0x0f, 0x09, 0xff, 0x83, 0x1c, 0x87, 0x86, 0xbe, 0x9e, 0x95, 0x8d, + 0x60, 0xf1, 0x9e, 0xf6, 0xea, 0x8c, 0x23, 0x16, 0x20, 0x86, 0x46, 0xd0, + 0x7c, 0xbe, 0xa5, 0xea, 0xe6, 0x63, 0x75, 0x4a, 0x32, 0x5f, 0x94, 0x9f, + 0x8f, 0x42, 0xb0, 0xd9, 0xb9, 0xce, 0x92, 0x9b, 0x59, 0x8d, 0x41, 0x89, + 0x51, 0x5c, 0x0d, 0x9d, 0xac, 0x81, 0x49, 0x60, 0xdf, 0xd5, 0x0d, 0x69, + 0x69, 0x5c, 0x62, 0x3f, 0xd0, 0xce, 0x5a, 0xbb, 0x70, 0x5f, 0x7d, 0x9d, + 0xd9, 0xad, 0x26, 0x1c, 0xce, 0x0d, 0x7d, 0x42, 0x6d, 0x1f, 0x36, 0x10, + 0xc7, 0x5a, 0x24, 0xf1, 0xf4, 0x00, 0xea, 0xfb, 0x09, 0x69, 0x26, 0x76, + 0x55, 0x3b, 0x39, 0x00, 0x90, 0x2c, 0x45, 0x83, 0xb7, 0xd0, 0x50, 0xc2, + 0x6a, 0x6d, 0x7a, 0x1b, 0x9b, 0xfc, 0x62, 0x06, 0x26, 0x39, 0x35, 0x93, + 0x12, 0xf2, 0x36, 0xbe, 0x64, 0xd9, 0x90, 0x3b, 0x01, 0xfa, 0xa6, 0x26, + 0x00, 0x85, 0xa1, 0xac, 0xeb, 0xec, 0x43, 0x14, 0x90, 0xd5, 0x71, 0xf4, + 0x09, 0xee, 0x84, 0x0e, 0x03, 0x8e, 0x44, 0x96, 0x49, 0xd3, 0x00, 0x80, + 0xb1, 0x8d, 0x16, 0x02, 0xb8, 0xce, 0x56, 0x76, 0x66, 0xe8, 0x06, 0xec, + 0x14, 0x1b, 0x12, 0x11, 0x61, 0x0f, 0xf9, 0x13, 0x6f, 0x10, 0x6d, 0xe9, + 0x5f, 0x0d, 0xe0, 0x39, 0x88, 0xe5, 0xf7, 0x0c, 0x5f, 0xa3, 0x19, 0x6a, + 0x1d, 0xb6, 0x4b, 0x5f, 0xc8, 0x74, 0x0e, 0x20, 0x70, 0x4f, 0x41, 0xd6, + 0x03, 0xe2, 0x3a, 0xa0, 0x58, 0x4b, 0x91, 0x6c, 0xb4, 0x56, 0xcd, 0xe5, + 0x7c, 0x8b, 0x72, 0x60, 0x76, 0x99, 0xbe, 0x26, 0x34, 0x70, 0x4f, 0x85, + 0x1b, 0x24, 0xd8, 0x93, 0x56, 0x4d, 0xe2, 0x05, 0xa6, 0x31, 0x0f, 0x40, + 0x23, 0x4f, 0x1f, 0xa3, 0xca, 0xde, 0xb0, 0x18, 0xbd, 0x01, 0xcf, 0x19, + 0x5a, 0x76, 0x9d, 0x55, 0x32, 0x18, 0x08, 0x0c, 0x0a, 0x9b, 0xda, 0xac, + 0x32, 0x80, 0x17, 0x7e, 0x18, 0xb4, 0xca, 0xd7, 0x80, 0xb1, 0x7f, 0x67, + 0x86, 0xb0, 0xba, 0x7b, 0xe2, 0xe8, 0xee, 0x93, 0x4b, 0x2b, 0xb1, 0xe5, + 0x82, 0x95, 0x16, 0xe6, 0x27, 0x7f, 0xa3, 0xca, 0x91, 0x57, 0xb4, 0x5b, + 0x4f, 0x33, 0x14, 0xba, 0x8e, 0x7b, 0xb4, 0x02, 0x92, 0x0a, 0x0b, 0x6c, + 0xd3, 0x54, 0xcc, 0x27, 0x7c, 0xd9, 0x53, 0x1a, 0xac, 0xa1, 0xef, 0x00, + 0x8f, 0xa3, 0x01, 0x54, 0xf3, 0xc3, 0x9c, 0xa0, 0x1f, 0x7a, 0x61, 0xf1, + 0x44, 0xad, 0x86, 0xb6, 0x31, 0xfe, 0xc7, 0x10, 0x6c, 0x93, 0x74, 0xe7, + 0x60, 0xc0, 0x5a, 0x0d, 0x00, 0xe5, 0x1c, 0xd7, 0x01, 0xd0, 0x65, 0x66, + 0xce, 0xc9, 0xa0, 0x48, 0xa6, 0x45, 0x85, 0xe3, 0x42, 0xf3, 0x40, 0x85, + 0x0b, 0x86, 0x4f, 0x2f, 0x64, 0xac, 0x1a, 0x3c, 0x05, 0xc8, 0x58, 0x4a, + 0x2e, 0xe2, 0x15, 0xc2, 0xe0, 0x6e, 0x00, 0xa9, 0x73, 0x63, 0xba, 0x0a, + 0x17, 0xa2, 0xfe, 0x3c, 0x50, 0x84, 0x44, 0x9b, 0x7e, 0xa4, 0xa9, 0x08, + 0xcc, 0x24, 0xd4, 0x57, 0x27, 0x67, 0x2d, 0xbd, 0x64, 0x94, 0x28, 0x27, + 0x1c, 0xa9, 0xa9, 0xab, 0x0c, 0xbd, 0xb3, 0x48, 0x9e, 0xd0, 0x6f, 0xa6, + 0x37, 0x25, 0xb5, 0x85, 0x26, 0xc6, 0xf3, 0xf1, 0x3c, 0xe5, 0xf1, 0xa3, + 0x6e, 0xac, 0x91, 0x7f, 0xba, 0xac, 0xef, 0x97, 0x40, 0x5a, 0x5f, 0xcb, + 0x2c, 0x07, 0x4b, 0x35, 0x82, 0xf5, 0xea, 0x9b, 0x6b, 0x6d, 0x8a, 0x33, + 0x3a, 0xb2, 0x37, 0xe1, 0x57, 0x09, 0x27, 0xc2, 0x94, 0x14, 0xa4, 0x8d, + 0xde, 0x2b, 0xcb, 0xd7, 0x9b, 0x8e, 0x3d, 0xb2, 0x88, 0x3e, 0xa7, 0x93, + 0xba, 0x7f, 0x7f, 0x18, 0x28, 0x72, 0x82, 0x48, 0x5f, 0x88, 0x41, 0x8e, + 0x24, 0x60, 0x97, 0xbd, 0x0d, 0xba, 0xe9, 0x5d, 0xb6, 0x61, 0x50, 0xb2, + 0x62, 0x56, 0xea, 0x2e, 0x2d, 0xc6, 0xa5, 0xda, 0x94, 0xd8, 0x8d, 0x5a, + 0xbd, 0x3e, 0x0a, 0x3d, 0x65, 0x95, 0x85, 0x85, 0xb3, 0xd9, 0x12, 0x91, + 0x1a, 0xce, 0xdb, 0x39, 0xbd, 0xaa, 0x6c, 0xc8, 0x08, 0xb3, 0xa6, 0x95, + 0x51, 0x17, 0x91, 0xa7, 0xe7, 0xd1, 0xb6, 0xec, 0x61, 0x08, 0xc5, 0x4e, + 0x96, 0x41, 0x93, 0xed, 0xf9, 0x51, 0xb3, 0xde, 0x87, 0x58, 0x11, 0xb1, + 0x3e, 0x5a, 0x61, 0x81, 0x00, 0x05, 0x0a, 0xd9, 0x1b, 0xbe, 0xaa, 0x1d, + 0x69, 0x78, 0x7d, 0x47, 0x98, 0xca, 0x60, 0x57, 0x7c, 0x41, 0x54, 0x49, + 0xfb, 0xad, 0x6b, 0x94, 0x7b, 0x09, 0xf6, 0x8d, 0x3f, 0x24, 0xa3, 0x8b, + 0x1c, 0xd5, 0x85, 0xbc, 0x43, 0x50, 0xeb, 0xe4, 0x84, 0x72, 0x50, 0x85, + 0x1a, 0xf3, 0x56, 0x46, 0x01, 0xd1, 0xf0, 0xe2, 0xfc, 0x66, 0x2c, 0x55, + 0x1b, 0x7c, 0x97, 0xe0, 0x49, 0xbe, 0xb2, 0x45, 0xce, 0xfe, 0xf6, 0xf0, + 0x41, 0xbe, 0xdd, 0x63, 0x9c, 0x6d, 0xe4, 0x0a, 0x40, 0x04, 0xcb, 0xb6, + 0x85, 0xcd, 0x97, 0x4e, 0xe2, 0x98, 0x4c, 0x4c, 0xe5, 0x2c, 0x29, 0x41, + 0xb2, 0x79, 0x86, 0xa1, 0x75, 0x94, 0xe5, 0x0a, 0x2b, 0x17, 0xb7, 0xa8, + 0xe5, 0x7c, 0xac, 0x57, 0xd6, 0x12, 0xde, 0x0c, 0x41, 0x4f, 0x35, 0x63, + 0x49, 0xa8, 0xd5, 0x0e, 0xa7, 0x86, 0x5e, 0x85, 0x96, 0x7b, 0x34, 0x07, + 0x3c, 0xd0, 0x44, 0xa3, 0x85, 0x85, 0xe5, 0x7f, 0x0b, 0x56, 0x5b, 0x2a, + 0x28, 0xd3, 0x00, 0x33, 0xe4, 0xef, 0xb9, 0x37, 0xac, 0x68, 0xea, 0x95, + 0xe0, 0x8c, 0x97, 0xe7, 0x17, 0xf2, 0x4f, 0x9f, 0x6d, 0x8a, 0x0c, 0xfd, + 0x84, 0x17, 0xc8, 0x46, 0x86, 0xe0, 0x0d, 0x89, 0x7a, 0xa7, 0xca, 0x68, + 0x53, 0x77, 0xb3, 0x75, 0xb0, 0xc6, 0x9b, 0x8f, 0xf7, 0x3a, 0xdc, 0xee, + 0x17, 0x14, 0x58, 0x87, 0xc0, 0x87, 0xba, 0x95, 0x6c, 0xef, 0x81, 0xb2, + 0x11, 0xe8, 0xf7, 0x1b, 0x4f, 0x77, 0x21, 0xfe, 0x10, 0x69, 0x2a, 0x3c, + 0x9b, 0xc1, 0x9b, 0xa0, 0x6b, 0xe6, 0x47, 0x83, 0x28, 0xe7, 0x5d, 0xff, + 0xa3, 0x56, 0xac, 0x14, 0x47, 0x42, 0x43, 0x3f, 0x89, 0xa6, 0x5a, 0xde, + 0x0e, 0x2b, 0x7c, 0x89, 0xcf, 0x72, 0x25, 0x6b, 0x1b, 0xfc, 0x02, 0x91, + 0x55, 0xc8, 0x84, 0x68, 0xd7, 0x93, 0xec, 0xc5, 0xc0, 0x5d, 0x92, 0x2c, + 0xc0, 0xbd, 0xcd, 0xcf, 0x48, 0x0a, 0x09, 0x41, 0x60, 0x25, 0x4c, 0x8e, + 0x23, 0xc1, 0xbf, 0x68, 0x37, 0xe4, 0x3b, 0xcd, 0x0d, 0x78, 0xef, 0x77, + 0xba, 0x2c, 0xc9, 0xe7, 0xfb, 0xc6, 0x15, 0x5d, 0x83, 0x62, 0x75, 0xc1, + 0xca, 0x1e, 0xf4, 0x96, 0x02, 0xb6, 0x24, 0xc0, 0x97, 0xb3, 0x00, 0x0e, + 0x8d, 0x8f, 0x5d, 0xc4, 0x3f, 0x29, 0xec, 0x5e, 0x55, 0x39, 0xf2, 0x07, + 0x9d, 0xea, 0x57, 0x2b, 0xa4, 0x27, 0x68, 0x7f, 0x42, 0x47, 0x13, 0xbf, + 0x64, 0x5c, 0xfb, 0x84, 0xb6, 0x6d, 0xb9, 0xa0, 0x59, 0x9d, 0x03, 0x19, + 0xe9, 0xaa, 0x5f, 0x12, 0x6f, 0xca, 0x45, 0x56, 0x38, 0x6b, 0x9d, 0x6b, + 0xda, 0xcf, 0x33, 0xbb, 0x49, 0xad, 0x94, 0x9c, 0xab, 0x8e, 0x45, 0x97, + 0x3d, 0x0b, 0x51, 0x37, 0xd2, 0xed, 0x98, 0xc0, 0xbb, 0xaf, 0x22, 0x19, + 0x0f, 0x80, 0xb5, 0xf5, 0x93, 0xa2, 0xd0, 0x6b, 0xb3, 0x32, 0xd0, 0xd6, + 0xea, 0x2c, 0xe3, 0x36, 0x4c, 0xc5, 0x3f, 0x12, 0x66, 0x6c, 0x72, 0x57, + 0x98, 0x20, 0xc9, 0x23, 0x83, 0xfa, 0xe9, 0xea, 0xf6, 0xc4, 0xfc, 0x07, + 0x53, 0x01, 0x6f, 0x01, 0x26, 0xb3, 0xa4, 0x86, 0x3f, 0x14, 0xab, 0x2b, + 0xd5, 0xd9, 0x0e, 0x8f, 0xff, 0xf0, 0x8a, 0xdb, 0xd4, 0x6c, 0x47, 0x54, + 0x76, 0x59, 0x73, 0x1e, 0xc8, 0xd0, 0x8a, 0xa3, 0xbf, 0x4d, 0x92, 0x85, + 0x15, 0x50, 0x69, 0xeb, 0x22, 0xd1, 0x98, 0x3d, 0x05, 0xb2, 0x38, 0x87, + 0x9e, 0x75, 0xfd, 0x3b, 0xce, 0x66, 0x09, 0x49, 0x6a, 0xb0, 0xdf, 0xe3, + 0x4d, 0x41, 0x97, 0xe4, 0xe8, 0x02, 0x68, 0x29, 0x45, 0x9e, 0x4a, 0x7b, + 0x21, 0x5f, 0xbc, 0x3c, 0xdd, 0x59, 0x4f, 0x7c, 0xdc, 0xff, 0x91, 0x85, + 0x14, 0x37, 0xf9, 0xd4, 0xf8, 0x0e, 0xfc, 0x61, 0x93, 0x69, 0xa6, 0xfa, + 0x12, 0x8a, 0x97, 0x1d, 0x48, 0x52, 0xbf, 0xf0, 0x85, 0xe9, 0xe1, 0x52, + 0x69, 0xed, 0xf7, 0x2c, 0x76, 0x01, 0xfe, 0xdf, 0x37, 0x50, 0x82, 0x40, + 0xe8, 0xa4, 0x5d, 0xfb, 0xb6, 0x8f, 0x70, 0xdb, 0x27, 0xce, 0x51, 0x9c, + 0x24, 0xde, 0x84, 0x47, 0x67, 0xa8, 0x17, 0x4e, 0x28, 0x17, 0x42, 0x4d, + 0xb0, 0x5d, 0x13, 0x32, 0x18, 0x94, 0x16, 0x99, 0x76, 0x29, 0x34, 0xe0, + 0x1a, 0x77, 0xbf, 0x1a, 0xef, 0x8c, 0x2a, 0xcb, 0xbf, 0x36, 0xb8, 0x62, + 0x8e, 0x9c, 0xf5, 0x59, 0xcb, 0xf9, 0x1d, 0x26, 0x59, 0x88, 0xc9, 0x08, + 0xf6, 0xf0, 0x08, 0xab, 0xab, 0x9d, 0xb3, 0xfd, 0x37, 0xff, 0xe5, 0x59, + 0x55, 0xa4, 0xaa, 0x28, 0x6b, 0x8a, 0x45, 0x41, 0x47, 0x5a, 0x68, 0xd3, + 0xff, 0x93, 0x9a, 0x00, 0x71, 0xe9, 0x5a, 0x7c, 0x04, 0x4a, 0xed, 0x54, + 0x88, 0x04, 0xd9, 0x95, 0xa3, 0x65, 0xcd, 0x6a, 0xf4, 0x48, 0x1e, 0xed, + 0xef, 0xc6, 0x62, 0x26, 0x00, 0xa6, 0x1f, 0xae, 0x97, 0xa2, 0x50, 0xf5, + 0xb1, 0x21, 0x12, 0x43, 0x1f, 0x6a, 0x09, 0x74, 0xb8, 0xb6, 0xe3, 0x37, + 0x60, 0x88, 0xcf, 0x49, 0x50, 0x92, 0xac, 0x66, 0x9a, 0xe0, 0x5c, 0x66, + 0x3b, 0x0c, 0xfe, 0x75, 0x34, 0x72, 0xc6, 0xd6, 0x28, 0xe6, 0x05, 0xee, + 0x31, 0x37, 0x86, 0xae, 0x3c, 0xb4, 0x18, 0x7d, 0x6a, 0x9b, 0x32, 0x3f, + 0x39, 0x37, 0xb9, 0xe0, 0xa9, 0xb5, 0x75, 0x9a, 0x3a, 0xb9, 0x69, 0x1a, + 0x51, 0xe4, 0x08, 0x28, 0x88, 0x08, 0xd0, 0xf5, 0xdf, 0xb2, 0x3b, 0x44, + 0x72, 0x04, 0xde, 0xcd, 0xd8, 0xa2, 0x9e, 0xd2, 0x2d, 0x85, 0x47, 0x2f, + 0x5b, 0x2d, 0xb8, 0xa9, 0x3d, 0x6b, 0xee, 0xbb, 0x2b, 0x9a, 0xb8, 0xcd, + 0x6c, 0xec, 0x96, 0xf2, 0x54, 0xa2, 0x55, 0xc8, 0x04, 0x48, 0x7f, 0x41, + 0xbe, 0x47, 0xd0, 0x3d, 0xee, 0x5f, 0x62, 0x76, 0x57, 0x4e, 0x03, 0xb9, + 0xbf, 0xc1, 0x73, 0x33, 0x90, 0x16, 0xf8, 0x95, 0xe2, 0xca, 0xad, 0xe4, + 0xb5, 0xf0, 0x41, 0x44, 0x07, 0x39, 0xa0, 0x11, 0x39, 0x37, 0x62, 0x81, + 0x4f, 0x7b, 0x46, 0x73, 0x76, 0x3e, 0x6e, 0x22, 0x4b, 0x09, 0xa0, 0x2a, + 0xfd, 0xd2, 0x83, 0xc0, 0x82, 0x91, 0x19, 0x99, 0xda, 0x7e, 0x93, 0x62, + 0x21, 0x83, 0xe1, 0x2c, 0xb1, 0xb2, 0xe3, 0x9a, 0xd4, 0xb7, 0xcb, 0xb2, + 0xa0, 0x49, 0xbe, 0x57, 0x52, 0xd1, 0x30, 0x4a, 0x2f, 0x66, 0x7e, 0x2d, + 0x33, 0x52, 0xa2, 0x2a, 0xcf, 0x6b, 0x1b, 0x35, 0x63, 0x43, 0x0e, 0xc8, + 0x2a, 0x95, 0xec, 0x7e, 0xb2, 0x14, 0xf5, 0x1d, 0x89, 0x84, 0xf1, 0x03, + 0x1c, 0x1b, 0x66, 0xfc, 0x7c, 0xa2, 0x2d, 0x8d, 0x2f, 0x05, 0x3e, 0x37, + 0xbe, 0x61, 0xcb, 0x83, 0xe0, 0x8c, 0x35, 0x63, 0x57, 0xd6, 0x84, 0x57, + 0x34, 0x4b, 0xd8, 0x2e, 0xe1, 0x4f, 0x37, 0x91, 0x3d, 0xf3, 0x93, 0xee, + 0x29, 0x78, 0x6d, 0x1e, 0x6b, 0x72, 0x81, 0x21, 0x2d, 0x55, 0xa6, 0xaa, + 0xb5, 0x56, 0x9f, 0xbd, 0x93, 0x5c, 0x88, 0x74, 0xb5, 0xf9, 0xf6, 0xa7, + 0x9d, 0xa7, 0x56, 0x02, 0xa8, 0x5b, 0x68, 0x1a, 0x87, 0xcb, 0x3a, 0x2c, + 0xf7, 0x94, 0x8e, 0xf5, 0xbe, 0xb6, 0x06, 0x3d, 0xfc, 0x88, 0x9d, 0x50, + 0xd5, 0xdb, 0x79, 0xfb, 0x85, 0xbb, 0x25, 0xa1, 0x22, 0xf5, 0xc7, 0x2d, + 0x33, 0xe7, 0xe1, 0x3f, 0x41, 0x28, 0x56, 0x57, 0x4c, 0x21, 0x7c, 0x9c, + 0xaf, 0x73, 0x13, 0x37, 0x56, 0xde, 0x98, 0x6a, 0x86, 0xbf, 0x86, 0x8c, + 0xa8, 0xfe, 0xb7, 0x84, 0x68, 0x03, 0x26, 0xa0, 0xc8, 0x8d, 0x65, 0x3e, + 0xa8, 0x67, 0x3b, 0xd8, 0xb4, 0x53, 0xc4, 0xe5, 0xee, 0x17, 0x3c, 0xdf, + 0xae, 0xa1, 0xea, 0xa5, 0x52, 0x40, 0xad, 0x95, 0x40, 0xcc, 0xf8, 0xba, + 0xab, 0x88, 0xc5, 0x8f, 0xd1, 0x45, 0x47, 0x15, 0x68, 0x9c, 0x85, 0x38, + 0x86, 0x96, 0x51, 0x62, 0x97, 0xdf, 0x2e, 0xda, 0x5c, 0x89, 0xf7, 0xb7, + 0x5b, 0x75, 0x5f, 0xac, 0xe7, 0xf7, 0x57, 0xbd, 0x36, 0x68, 0xcc, 0xdb, + 0xab, 0x58, 0x5c, 0xd2, 0xc8, 0x80, 0x93, 0x6c, 0xd6, 0x80, 0xdf, 0x23, + 0xd1, 0x2e, 0xd0, 0xea, 0x6b, 0x17, 0x3d, 0x9d, 0x08, 0x3c, 0x37, 0xff, + 0xef, 0x9c, 0xd2, 0xc8, 0x88, 0xbb, 0x80, 0xdd, 0x4c, 0x11, 0x63, 0x03, + 0x41, 0xeb, 0x77, 0x80, 0x69, 0x56, 0x8b, 0x5a, 0xda, 0xcd, 0x8e, 0x61, + 0x15, 0x63, 0x09, 0x75, 0x06, 0x93, 0xe9, 0x2c, 0xd4, 0x42, 0x1f, 0x4c, + 0x0f, 0x77, 0xfe, 0xc7, 0x59, 0xf8, 0x25, 0x3b, 0x84, 0xd1, 0x9d, 0x3b, + 0xc1, 0xca, 0x00, 0x70, 0xfc, 0xec, 0xf8, 0xb4, 0x6b, 0xac, 0xaa, 0x6c, + 0x8f, 0x37, 0xfd, 0x1a, 0x80, 0x8c, 0x66, 0x23, 0x4b, 0x11, 0x39, 0xca, + 0x71, 0x62, 0x7c, 0x05, 0xdc, 0x69, 0x5a, 0xf7, 0x7d, 0x07, 0x07, 0x83, + 0xb9, 0xc0, 0x56, 0xfa, 0xea, 0x26, 0x4a, 0x70, 0xe6, 0x77, 0xdd, 0x70, + 0xca, 0x2d, 0x64, 0x50, 0xe7, 0x8c, 0xdc, 0xe7, 0x45, 0x23, 0xe3, 0x08, + 0x24, 0xb8, 0xae, 0x2b, 0x0c, 0x18, 0x34, 0x03, 0x95, 0xbb, 0x4c, 0x24, + 0xaa, 0xe5, 0x15, 0xca, 0xe0, 0xcb, 0x16, 0xaf, 0xdf, 0x7b, 0x3e, 0x0c, + 0x8f, 0x56, 0x1d, 0x8c, 0x7a, 0xae, 0x18, 0xdd, 0x16, 0xfa, 0xb1, 0x97, + 0xac, 0x59, 0xac, 0x8d, 0x57, 0x9b, 0x61, 0x78, 0x89, 0x53, 0x73, 0x8a, + 0x56, 0x8d, 0xd4, 0x65, 0x20, 0x55, 0xc2, 0x50, 0xdd, 0x76, 0x6e, 0x47, + 0x5e, 0x22, 0x78, 0x66, 0xa5, 0xf9, 0x49, 0xe6, 0xf9, 0xbd, 0x4f, 0x6e, + 0xad, 0x66, 0x84, 0x73, 0x7e, 0xef, 0x27, 0xeb, 0x7c, 0x0e, 0x5a, 0x0a, + 0x91, 0x2f, 0x34, 0xe0, 0xbf, 0xb1, 0x97, 0x1a, 0x4d, 0xf1, 0xe1, 0xef, + 0xee, 0xfb, 0x53, 0x4a, 0x41, 0xd9, 0x0f, 0xf8, 0xa5, 0xd6, 0x68, 0x7a, + 0xdd, 0xd2, 0x09, 0x0e, 0xd9, 0xc2, 0x7d, 0xa4, 0xc9, 0x8e, 0x9d, 0x50, + 0x45, 0xc7, 0xc5, 0xf4, 0x96, 0x79, 0x77, 0xf8, 0x80, 0x3b, 0xb0, 0x37, + 0x32, 0x89, 0xe2, 0xc2, 0x7c, 0x95, 0x01, 0x34, 0x5b, 0x43, 0xed, 0x38, + 0x53, 0x04, 0xc3, 0xa2, 0x52, 0xfe, 0x54, 0xc6, 0x52, 0x90, 0x3a, 0x23, + 0xfb, 0x10, 0x1d, 0x71, 0x2e, 0xc4, 0x8b, 0xe9, 0xdb, 0x15, 0x00, 0x7f, + 0xea, 0x37, 0x1a, 0x0e, 0xfc, 0xb4, 0x35, 0xb7, 0x90, 0xbf, 0x12, 0x68, + 0x93, 0x7a, 0x07, 0x07, 0x67, 0x1b, 0xc5, 0x6f, 0x91, 0xea, 0x19, 0xdc, + 0x59, 0x68, 0x8b, 0xf8, 0x42, 0xb5, 0xd7, 0xa0, 0x22, 0x7a, 0xab, 0x04, + 0x67, 0x9a, 0x76, 0x49, 0xab, 0x00, 0x9d, 0x37, 0x11, 0xfb, 0xf9, 0x1f, + 0xd0, 0x59, 0xec, 0x27, 0xd2, 0xc3, 0x62, 0x95, 0xaa, 0x78, 0xbe, 0xbb, + 0xf1, 0xe6, 0x01, 0xa1, 0xf5, 0x42, 0xcd, 0xd9, 0x78, 0x25, 0xa2, 0xf8, + 0x31, 0x57, 0x0c, 0xed, 0xe3, 0x22, 0xf0, 0xa9, 0xd7, 0xee, 0xb4, 0x1f, + 0xc5, 0x1c, 0xb8, 0xd9, 0xd3, 0xae, 0x49, 0xed, 0x49, 0xf7, 0x54, 0xe5, + 0x00, 0x6b, 0x78, 0x84, 0x49, 0xa7, 0x10, 0xe4, 0x2f, 0xca, 0xb1, 0xb4, + 0x90, 0x6e, 0xd6, 0x81, 0xa4, 0x01, 0x7d, 0x38, 0x9d, 0x38, 0x4d, 0x52, + 0x43, 0x81, 0x47, 0x4f, 0x6c, 0xc1, 0x14, 0x0e, 0x14, 0xf7, 0x43, 0xf7, + 0xfe, 0xdb, 0x72, 0x97, 0x39, 0x46, 0xca, 0x9e, 0xa8, 0xca, 0x7e, 0x69, + 0xac, 0x84, 0xaa, 0x87, 0xc4, 0xd4, 0xc3, 0xa4, 0x1c, 0x57, 0xd3, 0xbe, + 0xf8, 0xde, 0xf4, 0xf3, 0x08, 0x54, 0x07, 0xf3, 0xfa, 0x0d, 0x98, 0xa4, + 0x88, 0xde, 0x7b, 0xd9, 0x85, 0x6e, 0x60, 0x62, 0x17, 0x9a, 0x3a, 0x9b, + 0x82, 0x59, 0x97, 0xb7, 0x52, 0xb6, 0x7e, 0x87, 0x3c, 0x5d, 0x3f, 0x64, + 0xda, 0xee, 0xac, 0x46, 0xb6, 0x04, 0x4e, 0x27, 0x21, 0xf0, 0x88, 0xb9, + 0xbd, 0xab, 0xd5, 0x98, 0x7a, 0xf9, 0x0f, 0xdc, 0x5c, 0xa3, 0xc7, 0x8c, + 0x47, 0xa1, 0x70, 0xef, 0x2c, 0x72, 0x88, 0xde, 0xde, 0x38, 0x46, 0xdc, + 0x88, 0xbc, 0x82, 0xce, 0x9c, 0x19, 0x73, 0xab, 0xa5, 0x9a, 0xa7, 0x4f, + 0xe9, 0xc6, 0xc1, 0x49, 0x0f, 0xac, 0x89, 0x77, 0x0f, 0x3a, 0xe9, 0xae, + 0x47, 0xf4, 0x28, 0x09, 0xec, 0xff, 0xe6, 0xce, 0x2f, 0x8b, 0x73, 0x53, + 0x19, 0x12, 0xde, 0x1f, 0xf2, 0xeb, 0xc5, 0x03, 0x3f, 0xee, 0xf1, 0x1e, + 0xb8, 0x56, 0x96, 0x28, 0x2e, 0xee, 0x99, 0x15, 0x5d, 0x40, 0x19, 0x93, + 0xfb, 0xba, 0xdc, 0xe4, 0xc2, 0x6e, 0x30, 0x23, 0xaa, 0xcb, 0xdd, 0x68, + 0x84, 0xce, 0x91, 0x7a, 0x41, 0xa3, 0xa8, 0x63, 0xe4, 0x2b, 0x1e, 0x53, + 0xb5, 0x22, 0xa7, 0x98, 0x65, 0x91, 0x62, 0x87, 0x77, 0x64, 0x78, 0xb2, + 0xe2, 0x1e, 0x4a, 0xdb, 0xbd, 0x9c, 0x3b, 0xae, 0x7a, 0x15, 0x1c, 0xc3, + 0x24, 0x23, 0x4d, 0x2d, 0x6e, 0xc5, 0x5e, 0xf7, 0x0b, 0x56, 0xd9, 0xe7, + 0xc3, 0x7d, 0x26, 0xc0, 0x53, 0x5a, 0x6e, 0xb4, 0x2a, 0x82, 0x4e, 0xf7, + 0x23, 0x2f, 0xa8, 0x62, 0x59, 0x94, 0x89, 0xec, 0x1f, 0x4e, 0x3c, 0x35, + 0x41, 0x6d, 0x03, 0xd8, 0x10, 0x9f, 0xb9, 0xd7, 0x42, 0x5d, 0x23, 0x85, + 0xb0, 0xfe, 0x98, 0x4f, 0x70, 0xba, 0xe2, 0x99, 0x6b, 0x6b, 0x47, 0x25, + 0x72, 0x87, 0x81, 0xc8, 0x01, 0x90, 0x44, 0xaf, 0xbf, 0x14, 0x7d, 0x99, + 0x93, 0xb1, 0x74, 0x35, 0x56, 0x55, 0xa7, 0xc0, 0x46, 0x0d, 0x6a, 0xd0, + 0x06, 0x4f, 0x8f, 0xab, 0xc3, 0xe1, 0x79, 0x90, 0x61, 0x7b, 0x2c, 0x6c, + 0x28, 0xd5, 0xc2, 0x5f, 0x8c, 0x30, 0x43, 0xbe, 0x34, 0x12, 0x8e, 0x85, + 0x41, 0x96, 0xf5, 0x4f, 0xc3, 0x6e, 0x76, 0x61, 0x54, 0xdb, 0xc5, 0x1b, + 0x65, 0x3b, 0xaf, 0xd5, 0xa2, 0x25, 0x4a, 0xe2, 0x76, 0xeb, 0x0e, 0x04, + 0x28, 0xed, 0x21, 0xa5, 0xa0, 0x4e, 0x0d, 0xd0, 0xc7, 0x0f, 0x19, 0xb9, + 0xa7, 0x97, 0x2e, 0x85, 0x03, 0x94, 0xae, 0x75, 0x60, 0xef, 0x68, 0x5d, + 0xed, 0x39, 0x2e, 0xed, 0xe4, 0x8e, 0x26, 0x46, 0x50, 0x4a, 0xc1, 0xeb, + 0x8f, 0x91, 0x49, 0x1d, 0x1e, 0x80, 0x0f, 0x19, 0x27, 0xbf, 0xc0, 0xb6, + 0x1e, 0x2d, 0x42, 0x96, 0xdc, 0xdd, 0x4a, 0x05, 0x53, 0xab, 0x22, 0x20, + 0x54, 0xc5, 0xe2, 0x5c, 0x86, 0xcd, 0x53, 0xf9, 0x55, 0x4d, 0x1c, 0x44, + 0x7c, 0x7c, 0xc9, 0xbb, 0xe7, 0x5c, 0x8e, 0x59, 0xc1, 0x04, 0xe1, 0x53, + 0xe8, 0x7c, 0x04, 0x6d, 0xa9, 0x23, 0x58, 0x6d, 0x41, 0x11, 0xd4, 0xb7, + 0x74, 0x33, 0xde, 0x01, 0xbc, 0x40, 0x11, 0x49, 0x61, 0xf9, 0x37, 0x89, + 0x7c, 0x30, 0xff, 0xee, 0x9d, 0x4f, 0x57, 0xd6, 0x8d, 0x58, 0xbc, 0xc9, + 0x81, 0x72, 0x4d, 0xac, 0xac, 0x82, 0x92, 0xca, 0xed, 0x7a, 0x8d, 0x46, + 0x40, 0x92, 0x4f, 0x5d, 0xcf, 0x6f, 0xe5, 0x84, 0x03, 0x85, 0xf5, 0x93, + 0xb4, 0xd1, 0xd6, 0x8d, 0x28, 0x3a, 0x8b, 0xdf, 0xcd, 0x5e, 0xbf, 0x0e, + 0xd8, 0xdb, 0x1c, 0x28, 0x59, 0x62, 0x4f, 0x87, 0x10, 0x80, 0xee, 0x52, + 0xc9, 0x3a, 0x17, 0xa6, 0xeb, 0x43, 0xda, 0x01, 0xda, 0x68, 0xf2, 0x79, + 0x1a, 0x51, 0xee, 0x32, 0x0b, 0x93, 0x39, 0x8f, 0x45, 0x79, 0xfc, 0xf1, + 0x41, 0x43, 0x43, 0xa2, 0x09, 0xbd, 0x18, 0xc9, 0x09, 0x0e, 0x71, 0xea, + 0xaf, 0xb0, 0xcf, 0x1d, 0xf5, 0x7a, 0x78, 0xa3, 0x95, 0x51, 0x59, 0x8d, + 0x9a, 0xce, 0xeb, 0x2e, 0x95, 0xad, 0x11, 0xd6, 0x69, 0x42, 0xcd, 0x64, + 0x19, 0x7e, 0x88, 0x87, 0x5e, 0xd2, 0x3f, 0x65, 0x22, 0x47, 0xc9, 0x3a, + 0x4c, 0x8e, 0xa8, 0x53, 0xed, 0xaf, 0x71, 0x71, 0x40, 0x6a, 0x04, 0xaf, + 0x8f, 0x36, 0xeb, 0x62, 0xc2, 0x9e, 0x1d, 0x87, 0xb0, 0x83, 0x37, 0x0b, + 0xfc, 0xcb, 0xc2, 0xed, 0xb3, 0x86, 0xce, 0x79, 0xd2, 0x1b, 0x74, 0x2a, + 0x31, 0x95, 0x23, 0xf9, 0xdb, 0x13, 0x4e, 0x04, 0x8e, 0xeb, 0x23, 0xff, + 0x9f, 0xe4, 0xf6, 0x08, 0xa5, 0x92, 0xa5, 0xfc, 0xdb, 0x7d, 0x8b, 0x59, + 0xd0, 0xb5, 0x76, 0x5f, 0x23, 0x98, 0xf2, 0x5e, 0xc2, 0xf1, 0x3c, 0x0d, + 0x0b, 0x5a, 0x3b, 0xd8, 0xe6, 0x76, 0xa6, 0xba, 0xfd, 0x09, 0xe8, 0x1c, + 0xa6, 0x10, 0x07, 0x8c, 0x8a, 0xdb, 0x6d, 0x31, 0xd6, 0x29, 0x84, 0x80, + 0x69, 0x12, 0x20, 0xa7, 0x61, 0x5b, 0x8c, 0x24, 0x51, 0xd3, 0x6b, 0xd3, + 0xcf, 0x06, 0xce, 0x2e, 0xca, 0xb1, 0xfb, 0xd5, 0xe2, 0xb0, 0xbc, 0xf5, + 0x8d, 0xde, 0xdf, 0xb1, 0x13, 0x77, 0xbc, 0x22, 0xca, 0x12, 0x99, 0x71, + 0x0d, 0x56, 0x79, 0x1a, 0x71, 0x0f, 0x69, 0xed, 0xd1, 0x15, 0x94, 0xe9, + 0xf2, 0x9a, 0xc2, 0x2a, 0xac, 0xaa, 0x7c, 0x2b, 0xce, 0x91, 0x5b, 0x9e, + 0xde, 0x7a, 0x6b, 0x2d, 0xbd, 0x2c, 0x5f, 0x2d, 0xba, 0xc8, 0xbd, 0x07, + 0xa6, 0xd3, 0xa0, 0xf2, 0x0e, 0xa2, 0x92, 0x59, 0xb6, 0xc8, 0xfc, 0x40, + 0x2d, 0x1c, 0xef, 0x1f, 0xdc, 0x96, 0x2f, 0x95, 0x2d, 0x32, 0x86, 0x36, + 0x08, 0x9b, 0xd7, 0x6f, 0xe0, 0x75, 0x52, 0x9a, 0x29, 0xa7, 0x29, 0x96, + 0x5d, 0x01, 0x26, 0x23, 0xac, 0x80, 0x95, 0x58, 0x9c, 0x5d, 0x8c, 0xbe, + 0xfd, 0x66, 0x56, 0xfb, 0xb4, 0x0a, 0xd0, 0x89, 0xea, 0x4a, 0x07, 0xd1, + 0x58, 0x99, 0x75, 0x07, 0x4d, 0x64, 0xd2, 0x7d, 0x0d, 0xfc, 0x92, 0xc8, + 0x7a, 0x09, 0xcf, 0x7e, 0xb7, 0x5e, 0x6b, 0x1c, 0x1c, 0x3b, 0x9e, 0x84, + 0x87, 0x94, 0x57, 0x06, 0xd1, 0x9f, 0x9c, 0x0c, 0x0b, 0xae, 0xea, 0x56, + 0x9f, 0x89, 0x8a, 0xa2, 0xfa, 0xdc, 0xb3, 0x1f, 0x0c, 0x44, 0x60, 0x78, + 0xad, 0x38, 0xc0, 0xc6, 0x7d, 0xb3, 0x09, 0x48, 0x6e, 0x7e, 0x90, 0x9f, + 0xd7, 0x26, 0xd3, 0x95, 0x15, 0xc9, 0x3f, 0x7c, 0x2f, 0x4b, 0x9d, 0x55, + 0x6b, 0x24, 0xad, 0x02, 0x8e, 0x28, 0x19, 0x43, 0x9d, 0x75, 0xdc, 0xac, + 0x01, 0x76, 0x52, 0x93, 0x58, 0x9b, 0x49, 0x3b, 0xb8, 0xfa, 0x5b, 0x4e, + 0xb5, 0xdc, 0xc5, 0xb8, 0x00, 0x7a, 0x83, 0xf1, 0x71, 0x05, 0xac, 0xa6, + 0xa5, 0x4c, 0x8a, 0x0d, 0xbb, 0xc0, 0x1b, 0x1d, 0x4d, 0xd2, 0x1b, 0xfa, + 0xf9, 0x4b, 0x36, 0xa4, 0x67, 0xf4, 0xc6, 0x37, 0xee, 0x90, 0x40, 0xcc, + 0x4c, 0x2d, 0x03, 0xd2, 0x1f, 0xff, 0x77, 0x31, 0x84, 0x2a, 0xae, 0x21, + 0x99, 0x42, 0x28, 0x36, 0x0d, 0xba, 0x8c, 0xd5, 0x1b, 0x37, 0x9f, 0xfb, + 0x11, 0x6b, 0x34, 0xa1, 0xf5, 0x94, 0x8b, 0xeb, 0xb4, 0x3d, 0x21, 0x16, + 0xde, 0x70, 0x0e, 0x0c, 0x5b, 0x70, 0x83, 0x97, 0x36, 0xd3, 0x0b, 0x29, + 0x7e, 0x6b, 0x7c, 0xfd, 0x74, 0xdc, 0x24, 0xb3, 0x9e, 0x59, 0x76, 0x10, + 0x25, 0x73, 0x38, 0xe8, 0x28, 0xff, 0x74, 0x7d, 0xf0, 0xfc, 0xb0, 0x33, + 0x92, 0xbb, 0x67, 0xcc, 0x8b, 0x66, 0x06, 0x0c, 0x0c, 0xa3, 0x27, 0x53, + 0xf3, 0x05, 0x6f, 0x54, 0xe4, 0x20, 0x9b, 0x39, 0xaa, 0x2f, 0x8a, 0x56, + 0xd8, 0xec, 0x4b, 0x05, 0xb1, 0xbb, 0x91, 0xa3, 0x4f, 0x43, 0xc1, 0x41, + 0x93, 0x38, 0x2f, 0x1e, 0x17, 0x3f, 0x89, 0xc3, 0xd0, 0xd7, 0xf7, 0x1b, + 0x0c, 0xc4, 0x65, 0xb6, 0x8f, 0x9e, 0x5f, 0xba, 0x52, 0x19, 0xaf, 0x84, + 0xda, 0xd4, 0xab, 0xf1, 0x38, 0xe5, 0xb9, 0x2b, 0x53, 0x08, 0x67, 0xb2, + 0xb9, 0x75, 0x12, 0xe9, 0x39, 0x18, 0xd9, 0x13, 0x31, 0xf4, 0x35, 0xf2, + 0x21, 0xb4, 0xa5, 0xe9, 0x47, 0x0e, 0x83, 0x04, 0x29, 0x20, 0xb0, 0xde, + 0xd6, 0x74, 0x2b, 0x9d, 0x8e, 0x0e, 0x43, 0x16, 0x5d, 0xa9, 0x37, 0x43, + 0xf7, 0xa3, 0x37, 0xd0, 0x34, 0xa3, 0x85, 0x2f, 0x37, 0x7d, 0x35, 0x26, + 0x98, 0xa4, 0x65, 0xa5, 0x2f, 0xb6, 0xe6, 0x9c, 0xc6, 0xf8, 0x6d, 0x55, + 0x84, 0xe7, 0x3e, 0x5e, 0xc6, 0x1e, 0x56, 0x72, 0xf2, 0x85, 0x28, 0x3e, + 0x5f, 0x87, 0x0c, 0x1d, 0xea, 0xb6, 0xed, 0x1c, 0x32, 0xa3, 0x96, 0xa8, + 0x5c, 0x17, 0x09, 0xa0, 0x1d, 0x42, 0xf4, 0x5e, 0x8e, 0x5a, 0x49, 0x13, + 0xb4, 0x66, 0x0a, 0xe0, 0x2d, 0x51, 0xcb, 0x83, 0xb4, 0x64, 0xc9, 0x8e, + 0xe9, 0xf5, 0xb0, 0x49, 0x57, 0xd0, 0x4a, 0xd1, 0xc0, 0x23, 0xc0, 0x30, + 0x7a, 0x3e, 0xf5, 0x32, 0x3b, 0xd6, 0xa3, 0x80, 0x40, 0x3a, 0xa1, 0x96, + 0x0e, 0xbb, 0xbb, 0xfd, 0xa2, 0x91, 0x04, 0x6f, 0xbf, 0xff, 0x62, 0x81, + 0x74, 0x51, 0xff, 0x5f, 0xba, 0x38, 0x4b, 0x0f, 0x58, 0xa1, 0x15, 0xc6, + 0xd2, 0x20, 0x60, 0x01, 0xf7, 0x47, 0x1c, 0xdc, 0x77, 0x90, 0xe2, 0xa2, + 0xe3, 0xeb, 0x75, 0x19, 0x59, 0x1b, 0x7c, 0x89, 0x42, 0x42, 0xaf, 0x86, + 0xb2, 0x19, 0xce, 0xfa, 0x3f, 0x69, 0x1c, 0xa6, 0xe7, 0xa5, 0xf3, 0x6e, + 0x6a, 0xa0, 0xed, 0x16, 0xac, 0x45, 0xaf, 0x32, 0xc2, 0x76, 0x82, 0xaa, + 0xc0, 0x42, 0xed, 0x53, 0x4e, 0x53, 0x2a, 0x8e, 0xb4, 0x55, 0x65, 0x77, + 0x20, 0xd7, 0xd9, 0x0e, 0xc9, 0x22, 0x9c, 0x3a, 0x4a, 0xa3, 0xb4, 0x49, + 0x72, 0x46, 0x26, 0xd1, 0xeb, 0x2e, 0x05, 0xfe, 0x1e, 0x2c, 0xe0, 0x35, + 0x44, 0x1f, 0x51, 0xbf, 0xbb, 0x2c, 0x78, 0xb5, 0x27, 0x65, 0x7d, 0xe2, + 0xe1, 0x1c, 0x11, 0xd0, 0xb6, 0x04, 0x8b, 0xbf, 0x93, 0xd5, 0xa9, 0x25, + 0xdc, 0x7f, 0xff, 0x9c, 0x29, 0x62, 0x43, 0x32, 0x1e, 0x4a, 0xea, 0x30, + 0x6b, 0xe6, 0x12, 0x69, 0xf4, 0xc7, 0x95, 0x95, 0xd4, 0x4d, 0x83, 0xb3, + 0xfb, 0x53, 0xd8, 0x01, 0x1e, 0x8f, 0x2c, 0x8c, 0xde, 0x9d, 0xfc, 0x60, + 0xe0, 0x7b, 0xa0, 0x65, 0xc4, 0xd2, 0x75, 0x24, 0x78, 0x4f, 0xb4, 0xe3, + 0xb3, 0xb2, 0x7d, 0xbc, 0xad, 0x49, 0x97, 0x12, 0xf0, 0x8d, 0xc4, 0x6b, + 0x54, 0xc1, 0x82, 0x62, 0x02, 0x27, 0x1f, 0xe3, 0xd6, 0x4a, 0xcd, 0x38, + 0x97, 0x97, 0xd9, 0xeb, 0x65, 0xf9, 0xa7, 0xcf, 0xcb, 0x02, 0xb9, 0xea, + 0x8a, 0x84, 0xfe, 0x4d, 0xef, 0x21, 0x29, 0xff, 0xba, 0xd1, 0x5d, 0xec, + 0xea, 0x1d, 0x08, 0x57, 0x6f, 0x1d, 0x77, 0xa7, 0x18, 0xcc, 0xff, 0x2a, + 0x70, 0x2f, 0xfe, 0xd2, 0xfc, 0xaa, 0x9b, 0xff, 0x69, 0x3d, 0xf9, 0xe0, + 0x95, 0x51, 0x22, 0x9b, 0x1f, 0xc7, 0x98, 0xb2, 0xeb, 0x7b, 0xd9, 0x72, + 0x6e, 0x2e, 0x19, 0xcb, 0xa4, 0x55, 0x99, 0x3a, 0x98, 0xe8, 0x13, 0x71, + 0x3a, 0x0e, 0xf5, 0xd5, 0x71, 0x41, 0xcc, 0xf9, 0xe4, 0x1a, 0x5a, 0x46, + 0x03, 0xa8, 0x8d, 0x12, 0x95, 0x60, 0xee, 0x12, 0x89, 0xf6, 0xe7, 0xb4, + 0xf7, 0x6c, 0xaa, 0x93, 0x93, 0xc9, 0x7d, 0x13, 0x46, 0x64, 0xed, 0x88, + 0x31, 0x04, 0x6f, 0x4f, 0xc3, 0xe3, 0x3c, 0x3b, 0x95, 0x54, 0x29, 0xc9, + 0x15, 0xf1, 0x05, 0xdc, 0x8c, 0x08, 0xbc, 0x5c, 0x9c, 0xf1, 0x7f, 0x5d, + 0xbd, 0x7c, 0xf0, 0xc0, 0x30, 0xaf, 0xae, 0x23, 0xdc, 0x4f, 0x46, 0x6c, + 0x1c, 0x20, 0xb1, 0xc7, 0x87, 0xd9, 0x2c, 0xe2, 0x05, 0x43, 0xf3, 0x2e, + 0xc5, 0x4b, 0x51, 0x04, 0xbb, 0x93, 0xeb, 0x30, 0x06, 0xfd, 0x85, 0x1e, + 0x59, 0x3c, 0x80, 0xb7, 0x0a, 0x8a, 0x57, 0x76, 0x1a, 0x85, 0xff, 0xc2, + 0x5d, 0xb5, 0xc4, 0xa1, 0xf7, 0x35, 0x86, 0xfe, 0xe8, 0x4b, 0xef, 0xb4, + 0x24, 0x29, 0x76, 0xeb, 0x85, 0x2d, 0x59, 0xa9, 0xdd, 0xab, 0xd2, 0x01, + 0x2b, 0x73, 0x23, 0x6c, 0x2f, 0x19, 0x40, 0x27, 0x35, 0xba, 0x56, 0x44, + 0x38, 0x7c, 0x0d, 0x63, 0x76, 0x97, 0xe6, 0xce, 0x12, 0x52, 0x42, 0x26, + 0x59, 0x9e, 0xf9, 0xa6, 0x28, 0xdf, 0x19, 0x08, 0xee, 0x04, 0x5e, 0x25, + 0xbd, 0x58, 0x10, 0x7f, 0xa8, 0x54, 0xa6, 0x25, 0x1a, 0x0c, 0xde, 0xe2, + 0xa4, 0x1f, 0x78, 0x3a, 0xd7, 0xfb, 0x5f, 0xdc, 0x92, 0x07, 0x26, 0x43, + 0x9a, 0x0f, 0xfe, 0xda, 0x11, 0x4e, 0x15, 0x2a, 0x13, 0xcd, 0x2f, 0x9f, + 0x0c, 0x27, 0xa6, 0xca, 0xc1, 0xa0, 0xce, 0xb6, 0x49, 0x01, 0x25, 0xc4, + 0x15, 0x81, 0xa8, 0xc3, 0x68, 0xef, 0x41, 0xe2, 0x9f, 0xd8, 0xe3, 0xa6, + 0x7a, 0xd6, 0xa6, 0x67, 0x2d, 0x6b, 0xec, 0x02, 0x5c, 0x5f, 0x9c, 0xa5, + 0x2d, 0xf6, 0x45, 0xd8, 0x60, 0xe9, 0x7b, 0x51, 0x63, 0x1b, 0xf0, 0xfb, + 0x97, 0xc5, 0x1e, 0x6b, 0x7c, 0xca, 0x11, 0xa8, 0x6f, 0xd2, 0x3c, 0x34, + 0xc2, 0x83, 0x4d, 0x18, 0x30, 0x7d, 0x59, 0xcc, 0x0d, 0xad, 0xa4, 0x64, + 0xcd, 0x63, 0x89, 0xe9, 0xeb, 0x1d, 0x47, 0x79, 0x68, 0x86, 0xd0, 0x7f, + 0x01, 0x90, 0x02, 0x7f, 0xb4, 0x24, 0x78, 0x95, 0x68, 0xc2, 0xe0, 0xa3, + 0x07, 0x44, 0xfe, 0x20, 0x52, 0x37, 0x3d, 0xb6, 0x1e, 0xd6, 0x6b, 0x70, + 0x85, 0x3b, 0x1b, 0x6d, 0x89, 0x39, 0x70, 0xbd, 0x3d, 0xe4, 0xcb, 0xb6, + 0xc5, 0xe5, 0xdd, 0x6f, 0x94, 0xe4, 0x5d, 0x45, 0x4f, 0xbb, 0x56, 0xb1, + 0xff, 0x8a, 0x05, 0x16, 0xbd, 0x5f, 0x9b, 0xab, 0xe6, 0x2f, 0x34, 0x10, + 0x8b, 0x3b, 0xda, 0x98, 0x61, 0x41, 0x5e, 0xf3, 0x3a, 0x92, 0xa4, 0xe4, + 0xbb, 0xd9, 0xd6, 0xfc, 0x1d, 0xf9, 0xda, 0xdb, 0x20, 0x4a, 0xe4, 0x0a, + 0x88, 0x5b, 0x15, 0x93, 0x12, 0x56, 0x65, 0x5f, 0xcc, 0xac, 0x18, 0x9f, + 0xef, 0xdd, 0xf4, 0xaf, 0xa0, 0xc4, 0xf3, 0xee, 0x4d, 0xe5, 0x8d, 0x91, + 0x80, 0x62, 0x19, 0x0c, 0x17, 0x8e, 0xd3, 0xb9, 0xd2, 0xea, 0xf8, 0xcf, + 0x9d, 0x96, 0x0a, 0x6d, 0xcc, 0x87, 0xfc, 0x28, 0xbe, 0xf4, 0xea, 0x98, + 0x6f, 0x12, 0x11, 0xee, 0xbe, 0x6a, 0x4e, 0x7a, 0x33, 0xc9, 0x60, 0x37, + 0x7f, 0xf3, 0x2c, 0xdf, 0x77, 0x4f, 0xbc, 0x38, 0x9f, 0xda, 0xa6, 0x92, + 0xf3, 0xa1, 0x44, 0x08, 0x23, 0x0a, 0x29, 0xf5, 0x2d, 0x27, 0xeb, 0x66, + 0x1b, 0x9a, 0xd1, 0x8a, 0xf9, 0x19, 0x33, 0xd4, 0x58, 0xa5, 0x86, 0x4e, + 0xd3, 0xa8, 0x59, 0x07, 0x7d, 0x0d, 0xaa, 0xb3, 0x17, 0x13, 0x80, 0x57, + 0x8a, 0x17, 0x20, 0x34, 0xc3, 0x81, 0x94, 0xd6, 0x6e, 0xa7, 0x9e, 0x3c, + 0xbc, 0xc0, 0xfb, 0x6a, 0x57, 0x9f, 0xa2, 0x44, 0x04, 0x4b, 0xff, 0x27, + 0xdb, 0xa0, 0xab, 0x40, 0xf6, 0x05, 0xed, 0x2d, 0x7d, 0x6b, 0x60, 0xba, + 0x36, 0x53, 0xb8, 0x70, 0x4e, 0x8c, 0x51, 0x3b, 0xf3, 0x2e, 0x5a, 0x20, + 0x53, 0x2d, 0x11, 0xd1, 0x3c, 0xba, 0x6e, 0xf5, 0xce, 0x52, 0x0d, 0x34, + 0xa9, 0x75, 0x7c, 0xbe, 0x1f, 0xfe, 0xf6, 0xdc, 0xf8, 0x01, 0x64, 0xad, + 0x4c, 0xc7, 0x40, 0x6b, 0x88, 0x79, 0xc9, 0x1b, 0x4d, 0x98, 0x75, 0xa4, + 0xa7, 0x2a, 0xfc, 0x39, 0x7e, 0xca, 0x37, 0xf7, 0x83, 0x68, 0x71, 0xda, + 0xc9, 0xb1, 0xaf, 0xbe, 0xb7, 0x2f, 0xdb, 0x7f, 0x52, 0x95, 0x37, 0x36, + 0xd5, 0x01, 0x9c, 0x46, 0x5d, 0x20, 0xee, 0x40, 0xdd, 0x2f, 0x2d, 0x96, + 0x2d, 0xd5, 0x69, 0x39, 0xe0, 0xd5, 0x3d, 0x3b, 0x9a, 0xae, 0xe1, 0x6a, + 0xc7, 0x3d, 0x41, 0x49, 0xcc, 0x18, 0xf5, 0x49, 0x67, 0x08, 0xc0, 0xab, + 0x4c, 0x9d, 0x57, 0xb0, 0xe2, 0x66, 0xbd, 0xcb, 0x03, 0x0c, 0xb1, 0x98, + 0xb0, 0x35, 0x33, 0xab, 0xc3, 0x47, 0xbe, 0x75, 0xa9, 0xcf, 0x5e, 0x6a, + 0xb8, 0x9c, 0x93, 0x77, 0x4c, 0xb0, 0x55, 0xab, 0xe4, 0xa2, 0x83, 0xc7, + 0xf0, 0x0a, 0x87, 0x8e, 0xe3, 0x38, 0xa2, 0xe4, 0x72, 0x1c, 0x86, 0xaf, + 0x51, 0x1e, 0x18, 0x8c, 0x5f, 0xd1, 0x04, 0x06, 0x4b, 0xbe, 0xb4, 0x74, + 0x59, 0x2c, 0x35, 0x15, 0x92, 0xa7, 0x9d, 0x3d, 0x34, 0x8c, 0x19, 0xea, + 0xbc, 0x06, 0x47, 0xd6, 0xf1, 0x8e, 0x72, 0x69, 0x44, 0x3c, 0xe1, 0x34, + 0x4b, 0x97, 0xae, 0x03, 0x79, 0x70, 0x90, 0xe5, 0x11, 0x95, 0xa7, 0xe0, + 0x17, 0x3f, 0x50, 0xc8, 0xbb, 0x83, 0x13, 0x80, 0xb4, 0x1a, 0x43, 0x2d, + 0x75, 0x09, 0x5d, 0x25, 0xeb, 0x53, 0x1b, 0xd6, 0x21, 0x9a, 0x36, 0xa2, + 0x7a, 0xa5, 0x3b, 0xd6, 0x9b, 0x5e, 0xf3, 0x30, 0xa7, 0xf4, 0x10, 0x7d, + 0xdc, 0x29, 0x7a, 0x53, 0x6e, 0xdd, 0x01, 0xd1, 0x2b, 0xf9, 0xe6, 0x17, + 0x91, 0x40, 0xc9, 0x81, 0xb8, 0x7d, 0xa0, 0x23, 0x1f, 0x8b, 0x6a, 0x2d, + 0x90, 0xbe, 0x47, 0x9c, 0x40, 0x7b, 0x0b, 0xdf, 0x11, 0xaf, 0x0f, 0x1d, + 0xac, 0xb2, 0xbf, 0x09, 0x94, 0x23, 0x87, 0xd0, 0x3c, 0xd1, 0xf1, 0x24, + 0xa5, 0x16, 0xf7, 0xb6, 0x51, 0x11, 0x7f, 0xa1, 0xba, 0xdf, 0xbc, 0x3f, + 0x09, 0xa7, 0xff, 0x36, 0x21, 0xe8, 0x34, 0x66, 0xda, 0xde, 0xfa, 0x44, + 0x17, 0xf4, 0x2c, 0xb2, 0xee, 0xf6, 0x8f, 0x87, 0x66, 0x41, 0x15, 0x20, + 0x7c, 0xf4, 0xe2, 0xda, 0x40, 0x38, 0x9d, 0xcf, 0x9f, 0x66, 0xb9, 0x5b, + 0x92, 0xaa, 0x64, 0x79, 0x23, 0x04, 0xe0, 0x2a, 0x26, 0x06, 0x37, 0x9e, + 0xab, 0x41, 0x06, 0x15, 0x79, 0x7b, 0x70, 0x5c, 0x2d, 0xd7, 0x28, 0x2e, + 0x79, 0xd6, 0x23, 0x2c, 0x78, 0xf0, 0xe0, 0xc1, 0x25, 0xba, 0x74, 0xba, + 0x61, 0x2b, 0x76, 0x74, 0xf7, 0x7d, 0xaa, 0x85, 0xb7, 0x1f, 0x7c, 0x05, + 0x39, 0x36, 0x48, 0xc0, 0x66, 0xca, 0x05, 0xb2, 0x73, 0x2c, 0x34, 0x41, + 0x91, 0x0d, 0x24, 0x5d, 0x5d, 0xb0, 0xde, 0xba, 0xb2, 0x44, 0xd5, 0xaf, + 0x86, 0x72, 0x06, 0x2a, 0xae, 0x35, 0xca, 0xaa, 0xac, 0xa5, 0x92, 0x80, + 0x93, 0xf5, 0x2d, 0x6d, 0xde, 0x0c, 0x19, 0x63, 0x19, 0x6e, 0x56, 0x3d, + 0xf4, 0x42, 0x36, 0x89, 0x8f, 0xe8, 0xd9, 0x57, 0x51, 0x77, 0xcb, 0xc5, + 0x82, 0x01, 0xb5, 0x1a, 0x81, 0x77, 0x5f, 0xc4, 0x9c, 0xc2, 0xbf, 0xd3, + 0xbc, 0x39, 0xca, 0x48, 0x4f, 0xc8, 0xb7, 0x84, 0xb4, 0xa9, 0xfc, 0x67, + 0x67, 0x3e, 0xdd, 0x1d, 0xa4, 0xac, 0xbe, 0x71, 0x75, 0x43, 0x52, 0xec, + 0x8a, 0x52, 0x74, 0x5d, 0x1d, 0x40, 0x3e, 0x2c, 0xda, 0xad, 0xb9, 0x8f, + 0x78, 0x55, 0xbf, 0xef, 0x73, 0x25, 0xc6, 0x09, 0xd5, 0x0b, 0xb7, 0xba, + 0x75, 0x85, 0xea, 0x05, 0x01, 0xbd, 0x5a, 0x61, 0x91, 0xd9, 0xbb, 0x82, + 0xbf, 0xd4, 0x1b, 0xf7, 0xcd, 0xd2, 0x44, 0x14, 0x15, 0xc8, 0xa3, 0x5d, + 0x4d, 0xca, 0x79, 0x28, 0x57, 0x09, 0xe0, 0xe3, 0x36, 0xc6, 0x16, 0xee, + 0x76, 0x6c, 0x36, 0xda, 0xcc, 0x0f, 0x68, 0x4e, 0x0e, 0x95, 0x6f, 0xb4, + 0xe3, 0x18, 0xe5, 0x8c, 0xf2, 0x24, 0xeb, 0x6c, 0x21, 0x46, 0x1e, 0xcf, + 0x0b, 0x35, 0x1b, 0xc6, 0x30, 0xee, 0x36, 0xc2, 0x6c, 0xcc, 0x7f, 0xf8, + 0xd5, 0xfa, 0x23, 0x07, 0x85, 0x65, 0x3f, 0x58, 0x7a, 0x70, 0x5d, 0x70, + 0x88, 0xbf, 0xf1, 0xdd, 0xb8, 0xec, 0x7c, 0x50, 0xa2, 0xac, 0x80, 0x9b, + 0x5c, 0xa5, 0xa9, 0xf2, 0x81, 0x16, 0x64, 0xd7, 0x7a, 0xef, 0xac, 0xff, + 0x29, 0x9e, 0xac, 0x81, 0x57, 0x4d, 0x43, 0x0d, 0x5d, 0x55, 0x1a, 0xf4, + 0x0c, 0xca, 0x4c, 0xdf, 0x7e, 0x67, 0x76, 0x0b, 0xd5, 0x82, 0x6f, 0x2d, + 0x46, 0xf7, 0x70, 0x7b, 0x9d, 0x96, 0xba, 0x63, 0xc8, 0xae, 0x04, 0xba, + 0xd9, 0x5e, 0xcc, 0x82, 0xf7, 0xc3, 0x64, 0x7a, 0x8a, 0x07, 0x7f, 0x43, + 0x76, 0xb3, 0xd0, 0xf9, 0xbf, 0x1a, 0x34, 0x0e, 0xba, 0x4e, 0x32, 0xa7, + 0xb6, 0x6c, 0x61, 0xa1, 0xfb, 0x96, 0xfe, 0x9a, 0xd6, 0xe8, 0x76, 0xa6, + 0xf7, 0x68, 0x2f, 0xe2, 0x9a, 0x56, 0x13, 0xf6, 0xf4, 0x89, 0xa1, 0xf4, + 0x19, 0x49, 0x01, 0x95, 0xbe, 0x51, 0x2e, 0x01, 0xab, 0x25, 0xd0, 0xad, + 0xa2, 0xdf, 0xf5, 0x99, 0x29, 0xdc, 0xc4, 0x73, 0xa7, 0x31, 0x49, 0xbf, + 0x37, 0x34, 0x42, 0xf5, 0x38, 0xe3, 0xe6, 0x52, 0xa0, 0x87, 0x95, 0x14, + 0xf3, 0xdc, 0xa7, 0x64, 0x5f, 0x73, 0x21, 0xfa, 0x6b, 0x7b, 0x75, 0xa5, + 0xe6, 0xec, 0x40, 0x04, 0x8a, 0x3e, 0xf3, 0xf0, 0x6b, 0x72, 0xea, 0xab, + 0x56, 0xd0, 0x30, 0xbf, 0x7a, 0x9f, 0xce, 0xdc, 0xbb, 0x1e, 0x0e, 0x79, + 0xd6, 0x77, 0x75, 0x82, 0x1c, 0x37, 0x5a, 0x11, 0x8e, 0x15, 0x0b, 0x53, + 0xbd, 0xdb, 0x49, 0x73, 0x14, 0x9c, 0x18, 0x24, 0x39, 0x2a, 0x73, 0x2e, + 0x15, 0x8a, 0x88, 0x21, 0x23, 0x09, 0x00, 0x01, 0x4e, 0xd3, 0xf9, 0xd0, + 0x2d, 0x57, 0x36, 0xed, 0x3e, 0x48, 0xd7, 0x08, 0x2d, 0xd4, 0xa5, 0xd8, + 0xdc, 0xef, 0xe9, 0x05, 0x31, 0x8b, 0x2b, 0xe7, 0x0d, 0x14, 0x1f, 0x7c, + 0x22, 0xf1, 0xdf, 0xd9, 0x66, 0x63, 0xba, 0xc3, 0x0f, 0x21, 0x15, 0xbd, + 0x16, 0x04, 0x4f, 0xd3, 0x6f, 0xb5, 0xb2, 0xe7, 0x24, 0xd3, 0x21, 0x94, + 0xd9, 0x9c, 0x21, 0x1a, 0xc3, 0x37, 0xb4, 0x35, 0xdf, 0x34, 0x54, 0x5d, + 0xbf, 0x14, 0xb2, 0xae, 0x87, 0x1b, 0x5a, 0x4e, 0x7f, 0x5d, 0xa6, 0xb3, + 0x7b, 0x6e, 0xd0, 0xfa, 0x62, 0xfe, 0xf2, 0xb3, 0x86, 0xed, 0x0c, 0xd9, + 0xb2, 0x4d, 0xd7, 0xc5, 0xe4, 0x2a, 0x00, 0x8e, 0x5b, 0x42, 0x34, 0x0b, + 0xf3, 0xbb, 0xeb, 0x13, 0xaf, 0x1f, 0xb1, 0xb4, 0xfc, 0x18, 0xd3, 0x47, + 0x75, 0x86, 0x2c, 0x73, 0x9c, 0x7d, 0xad, 0x41, 0xa1, 0x0e, 0x40, 0x12, + 0xa8, 0x6b, 0x0b, 0x0a, 0x93, 0x98, 0xab, 0xee, 0x5f, 0xbb, 0xa3, 0x73, + 0x7a, 0x45, 0xea, 0xe5, 0x2f, 0x7b, 0x73, 0xda, 0x9d, 0x13, 0x56, 0x59, + 0x7a, 0xb9, 0x3f, 0xf4, 0x58, 0x81, 0x37, 0x34, 0xcf, 0xd2, 0xb2, 0x63, + 0x4b, 0x52, 0x8c, 0xe5, 0x6c, 0x35, 0x9c, 0xe5, 0x32, 0x7f, 0xa7, 0x47, + 0xe3, 0x0b, 0x2f, 0x98, 0xf9, 0xa1, 0x1d, 0xfc, 0x7d, 0xe4, 0x02, 0x0e, + 0x7a, 0x2b, 0x5c, 0xa9, 0x8e, 0xe3, 0x5c, 0x2c, 0x82, 0xae, 0x69, 0xef, + 0x42, 0xe5, 0x06, 0xc2, 0xe7, 0xfd, 0xb8, 0x73, 0x60, 0xba, 0x76, 0xd6, + 0x5b, 0xf6, 0x5f, 0x7b, 0x5f, 0x6f, 0xa0, 0x54, 0xd8, 0x98, 0xb9, 0x6a, + 0xf7, 0x3d, 0x10, 0xda, 0x29, 0x2d, 0x76, 0xc3, 0x59, 0x71, 0x33, 0x2c, + 0xc2, 0xdd, 0x2f, 0x7f, 0xfd, 0xb6, 0x41, 0xb3, 0x5a, 0xf1, 0x9b, 0x50, + 0x4e, 0x1e, 0x54, 0x84, 0xdd, 0xbf, 0x26, 0x01, 0x54, 0x29, 0xc1, 0xe1, + 0x26, 0xc8, 0xfd, 0x20, 0x0e, 0x6d, 0x44, 0xea, 0x97, 0x62, 0x32, 0x20, + 0x31, 0xe7, 0x75, 0x37, 0x28, 0x27, 0x74, 0x01, 0xd0, 0x29, 0x33, 0xb1, + 0x71, 0x3a, 0x46, 0xbb, 0x28, 0x9b, 0xea, 0x66, 0x76, 0x00, 0x89, 0xfb, + 0x8c, 0xf5, 0x79, 0x57, 0x44, 0xfd, 0x52, 0x68, 0x48, 0x60, 0xd0, 0x2d, + 0xa2, 0x0f, 0xd7, 0x69, 0x2b, 0x76, 0xc7, 0xdc, 0x7c, 0x84, 0xa9, 0xd3, + 0xa3, 0xde, 0x3d, 0xfc, 0xbc, 0x1a, 0xcf, 0x00, 0xf0, 0x60, 0x05, 0xa1, + 0x16, 0xbd, 0xad, 0x51, 0x4b, 0x0c, 0x35, 0x66, 0x24, 0xee, 0x20, 0xdf, + 0x36, 0xed, 0xf1, 0xf4, 0xf8, 0x76, 0xfc, 0x84, 0x33, 0xff, 0xad, 0x1d, + 0x92, 0xe0, 0x94, 0xd7, 0x69, 0xe0, 0xef, 0x72, 0x3a, 0x6e, 0x42, 0xce, + 0xa0, 0xa0, 0x33, 0x06, 0xf5, 0x88, 0x76, 0x35, 0xf6, 0x24, 0x6d, 0x96, + 0xe8, 0x27, 0xb4, 0xc4, 0x79, 0x48, 0xf5, 0xa8, 0xe1, 0x64, 0x40, 0x2f, + 0xe2, 0x4d, 0x2f, 0x11, 0xba, 0xd8, 0xa2, 0x5a, 0x0f, 0x03, 0x74, 0x59, + 0x49, 0x81, 0xde, 0x8e, 0x1a, 0x7a, 0x7b, 0x12, 0x3b, 0x20, 0x38, 0x87, + 0xc2, 0xb8, 0x3d, 0xcc, 0x8f, 0x68, 0x5d, 0x38, 0x7d, 0x23, 0x12, 0xbe, + 0x66, 0xed, 0xc5, 0x8e, 0x06, 0x30, 0xc6, 0x66, 0xeb, 0x95, 0x8f, 0xed, + 0x33, 0x93, 0x31, 0x93, 0xb5, 0xe4, 0x6c, 0x55, 0x25, 0x65, 0xd2, 0x1b, + 0xcf, 0x8c, 0x90, 0x44, 0x40, 0xf8, 0x5c, 0x5e, 0x89, 0x99, 0x1f, 0xe3, + 0x4b, 0x30, 0xf7, 0x75, 0x1b, 0x2a, 0x8f, 0x0d, 0x12, 0xe3, 0x46, 0xce, + 0xe0, 0xac, 0xf4, 0x9a, 0xe5, 0xaf, 0xfb, 0x5b, 0x64, 0x35, 0x7a, 0x88, + 0xf1, 0x6f, 0x47, 0x6b, 0x38, 0xd5, 0x70, 0xb4, 0x07, 0xeb, 0x2d, 0x99, + 0x83, 0xb7, 0x39, 0xdd, 0x22, 0xc9, 0xc3, 0xee, 0x3e, 0x93, 0x82, 0x62, + 0x52, 0xe9, 0x99, 0x42, 0x18, 0x6d, 0xd8, 0xe2, 0x99, 0x2a, 0x3f, 0xc5, + 0xc4, 0x09, 0xfd, 0x33, 0xd4, 0xd7, 0x06, 0x60, 0xb1, 0x44, 0x9f, 0x08, + 0x0a, 0xfa, 0x5b, 0x17, 0xa9, 0x07, 0x51, 0xd8, 0xa3, 0x92, 0x7f, 0x98, + 0x32, 0x73, 0xdc, 0x00, 0xf8, 0x64, 0x42, 0x88, 0x2e, 0xc1, 0xe3, 0x73, + 0x6b, 0x33, 0xd6, 0xde, 0x8e, 0x66, 0xb6, 0x66, 0x3b, 0x25, 0x77, 0xae, + 0xcc, 0x21, 0x95, 0x0a, 0x1b, 0x5e, 0x86, 0x1e, 0x2e, 0x7a, 0xd1, 0x2f, + 0xf9, 0x29, 0xd8, 0x07, 0x80, 0x74, 0x18, 0x20, 0x28, 0x64, 0x32, 0x38, + 0xc7, 0x02, 0x81, 0x63, 0x2f, 0x15, 0x5a, 0xce, 0x15, 0xa2, 0x0e, 0xd5, + 0xc1, 0x49, 0x7f, 0x4d, 0xf6, 0xa0, 0xb1, 0x47, 0xd3, 0x66, 0x97, 0x61, + 0x7e, 0xa7, 0x38, 0xc5, 0x46, 0x0e, 0x67, 0x1e, 0x71, 0xea, 0xed, 0x07, + 0x84, 0xd8, 0x63, 0x5d, 0x64, 0xa3, 0x33, 0x0a, 0x7b, 0xe2, 0x69, 0x8d, + 0x46, 0x49, 0x07, 0x14, 0x3e, 0x24, 0x34, 0x0d, 0x19, 0x20, 0xfc, 0xaa, + 0x45, 0xe1, 0x6c, 0x46, 0x6d, 0x48, 0x29, 0xdc, 0x37, 0xd0, 0xb4, 0x25, + 0xf4, 0x71, 0x31, 0x1b, 0x5e, 0x28, 0x74, 0x8a, 0xc7, 0xef, 0x22, 0xe7, + 0xa5, 0x6c, 0x70, 0x1a, 0xe5, 0xd8, 0x13, 0x0a, 0x53, 0x89, 0x6f, 0xe1, + 0x56, 0x4e, 0x83, 0x9c, 0xf2, 0xa4, 0x1d, 0xd8, 0xf2, 0xb3, 0x27, 0xc5, + 0x3f, 0x40, 0x7d, 0xa1, 0x8d, 0x47, 0x54, 0x31, 0xfa, 0x8c, 0x72, 0x5c, + 0x71, 0xa1, 0x3d, 0x46, 0x14, 0xb4, 0xb6, 0x63, 0x55, 0x3b, 0xfd, 0x8d, + 0x37, 0x32, 0xc9, 0xcb, 0x8e, 0xfb, 0x99, 0x08, 0x58, 0x27, 0x86, 0xd2, + 0xcb, 0x14, 0x4a, 0xb1, 0x61, 0xef, 0x7d, 0x34, 0x2a, 0xf6, 0x11, 0xbc, + 0x9a, 0xee, 0xee, 0x6f, 0xf6, 0x70, 0x58, 0xa4, 0x23, 0x7a, 0xd9, 0xac, + 0xdf, 0x3d, 0x52, 0x7c, 0x57, 0x64, 0x1a, 0x13, 0x1c, 0x78, 0x52, 0x69, + 0x70, 0x3d, 0x67, 0x91, 0x00, 0x92, 0xc6, 0xf4, 0x55, 0xb4, 0x4e, 0xb0, + 0x74, 0x8a, 0x8f, 0x46, 0x43, 0x4e, 0xf3, 0x66, 0x2f, 0x42, 0x7c, 0xba, + 0x99, 0xd2, 0xd3, 0x66, 0x94, 0x44, 0x57, 0x71, 0x23, 0x6c, 0xd4, 0x80, + 0xb2, 0x0d, 0x15, 0x0b, 0x40, 0xed, 0xcc, 0xd6, 0x48, 0x0d, 0xb6, 0x25, + 0x41, 0x4e, 0x22, 0xc9, 0x28, 0x33, 0xf5, 0x5e, 0x6c, 0x0d, 0x9f, 0xfb, + 0x5e, 0xd0, 0x19, 0x18, 0xf7, 0x62, 0xfb, 0x99, 0x6f, 0x9b, 0xe0, 0x3e, + 0x95, 0xa7, 0x15, 0xd8, 0x2c, 0x47, 0x44, 0x0e, 0xab, 0x49, 0x45, 0xb8, + 0x2b, 0x05, 0xb4, 0xf1, 0xe9, 0xcd, 0xe0, 0x23, 0x00, 0x11, 0xdf, 0xe6, + 0x0b, 0xab, 0x7b, 0xcd, 0xa8, 0xec, 0xbf, 0x82, 0x2b, 0x88, 0x07, 0xad, + 0xa2, 0x1e, 0xda, 0xab, 0xf6, 0xe1, 0xfb, 0xc4, 0x57, 0xd1, 0xe1, 0x50, + 0xa1, 0xda, 0xde, 0xb2, 0x5f, 0xfa, 0xbe, 0xa6, 0xff, 0x68, 0x57, 0x85, + 0x3d, 0xd6, 0x41, 0x22, 0x3e, 0x18, 0x9b, 0xf7, 0x45, 0xa3, 0xd2, 0x1b, + 0x5a, 0x0f, 0x75, 0x01, 0x25, 0xbf, 0xb3, 0x7d, 0x61, 0xa9, 0x74, 0x6f, + 0x8d, 0xd2, 0x52, 0xb4, 0x84, 0x4a, 0x64, 0x22, 0x27, 0xcb, 0xdd, 0xfe, + 0x5d, 0xfc, 0x4a, 0xd1, 0xe9, 0x22, 0xa3, 0x83, 0x6c, 0x6f, 0x19, 0x82, + 0xb7, 0xbf, 0x3f, 0xeb, 0xad, 0x8c, 0x98, 0x74, 0x3d, 0xb5, 0x92, 0xb2, + 0x60, 0xff, 0x81, 0xf8, 0x9f, 0x75, 0x31, 0xe8, 0xda, 0x67, 0xfc, 0xf5, + 0x2c, 0x46, 0x86, 0xbb, 0xf8, 0x40, 0x6c, 0x9b, 0x66, 0x0d, 0xde, 0x55, + 0xf4, 0x3a, 0x41, 0x56, 0xbc, 0x26, 0x64, 0xf6, 0xa5, 0xd4, 0x4d, 0x6b, + 0x5d, 0x84, 0x2e, 0x87, 0xc3, 0x44, 0x30, 0xbc, 0xf6, 0x4b, 0x2b, 0x19, + 0x5e, 0x53, 0xc6, 0x25, 0x58, 0x21, 0x00, 0x56, 0xcb, 0x49, 0x3d, 0x0c, + 0x00, 0xbe, 0x1c, 0xf2, 0x1d, 0xfe, 0x60, 0x95, 0x76, 0x63, 0x13, 0xc8, + 0x25, 0x1f, 0x1f, 0x61, 0xe4, 0xde, 0x84, 0xb7, 0xb4, 0x11, 0xbb, 0xc0, + 0x1d, 0xca, 0x1f, 0x1b, 0x80, 0xd8, 0xfe, 0xb9, 0xa3, 0xa8, 0x19, 0x0b, + 0x95, 0x53, 0x8c, 0x53, 0x9e, 0xbc, 0x81, 0xc2, 0xc7, 0x0f, 0x2b, 0xe0, + 0x56, 0xf6, 0x51, 0x34, 0xcc, 0x0b, 0x07, 0x29, 0x0b, 0x55, 0x69, 0x81, + 0x7d, 0x19, 0x4f, 0x46, 0x57, 0x2c, 0x28, 0xa0, 0x6e, 0x09, 0x1f, 0xf7, + 0x85, 0xc0, 0x75, 0x27, 0xa1, 0x7c, 0x6a, 0x3c, 0xe1, 0x5c, 0xa1, 0x2a, + 0x66, 0x5b, 0xb9, 0x7d, 0xc8, 0xe3, 0xee, 0x59, 0x94, 0xb0, 0x11, 0x23, + 0x75, 0xbc, 0xe4, 0x5f, 0x9d, 0x25, 0x02, 0x35, 0x70, 0xee, 0x3f, 0xe3, + 0x3d, 0x21, 0x66, 0xcd, 0x6a, 0x7d, 0x7b, 0xde, 0xa3, 0xea, 0x27, 0x5b, + 0x51, 0x9d, 0x94, 0x78, 0xcb, 0xe7, 0x3a, 0xa1, 0x6c, 0x5b, 0x97, 0xc7, + 0x98, 0x5c, 0x3a, 0x29, 0x2a, 0x2a, 0x61, 0x30, 0x6a, 0xdf, 0xf2, 0x82, + 0xa5, 0x76, 0x5c, 0xa3, 0x40, 0x87, 0x44, 0x77, 0xf1, 0x9c, 0x2b, 0x91, + 0x53, 0x48, 0x67, 0x29, 0xf4, 0xb7, 0x47, 0xef, 0xa7, 0x31, 0x26, 0xb6, + 0xc5, 0xc8, 0xd1, 0xac, 0xf9, 0x6a, 0x88, 0x39, 0x9a, 0x37, 0xd5, 0x1e, + 0xeb, 0x11, 0xe2, 0xd4, 0x55, 0x7f, 0x6c, 0x08, 0x77, 0xcf, 0xa2, 0x93, + 0x15, 0x9b, 0xf8, 0xf9, 0x46, 0x2b, 0x0e, 0xc2, 0xa9, 0xcf, 0xc3, 0x31, + 0x73, 0x71, 0x57, 0x1a, 0x66, 0x95, 0xde, 0x51, 0x64, 0x7d, 0x14, 0x9b, + 0x8d, 0xe6, 0x11, 0xce, 0xd5, 0xa2, 0x49, 0x5f, 0x0b, 0xa6, 0xf9, 0xda, + 0x48, 0xa2, 0x50, 0x2e, 0x39, 0x89, 0x04, 0xcf, 0x20, 0x63, 0xf4, 0xc8, + 0x7a, 0xa4, 0x48, 0x0d, 0x58, 0x5d, 0x30, 0xe4, 0x4d, 0x06, 0x02, 0x87, + 0xe4, 0x5e, 0x66, 0xf2, 0xe8, 0xeb, 0x38, 0x00, 0x7a, 0xdf, 0xc1, 0xc5, + 0x97, 0x63, 0xbf, 0xe0, 0xdf, 0x0c, 0xcc, 0xbb, 0x47, 0xa5, 0x24, 0x14, + 0x29, 0xc8, 0x28, 0x8b, 0xe3, 0x57, 0x45, 0xd5, 0x8d, 0xe4, 0xd9, 0x7b, + 0x4a, 0x7f, 0x3a, 0x5c, 0xf1, 0x25, 0xe2, 0x27, 0x5a, 0xe2, 0xed, 0x7a, + 0x5b, 0x5a, 0x26, 0x2c, 0xa4, 0xdc, 0x8d, 0x0b, 0xc0, 0x89, 0x98, 0xff, + 0x55, 0x1d, 0x4b, 0xa1, 0x5d, 0x2a, 0x6b, 0x4f, 0x10, 0x91, 0x7d, 0xd5, + 0x9f, 0x33, 0xa5, 0x08, 0x9d, 0x7f, 0x0e, 0x6f, 0x36, 0xa6, 0x13, 0x4f, + 0x05, 0x7f, 0x47, 0xf3, 0x23, 0x27, 0x70, 0xe6, 0x3a, 0xd1, 0x9e, 0x25, + 0x17, 0xf8, 0x77, 0x68, 0xec, 0x91, 0x43, 0x30, 0x75, 0xdd, 0x4b, 0xdc, + 0x49, 0x34, 0x7b, 0x49, 0x67, 0x0c, 0x56, 0xc5, 0x49, 0x5d, 0x31, 0xad, + 0xfd, 0x6b, 0x94, 0xe9, 0x42, 0x33, 0x9d, 0xd7, 0x38, 0xec, 0xc5, 0x6b, + 0xff, 0x64, 0x77, 0xa2, 0xd4, 0xfb, 0x89, 0x97, 0x99, 0x7c, 0x38, 0x96, + 0xc4, 0xa0, 0xc5, 0x79, 0x37, 0x4c, 0xfa, 0xb7, 0xf5, 0x18, 0x20, 0x99, + 0x8f, 0x8d, 0x33, 0x54, 0xe9, 0xed, 0x45, 0x6e, 0x49, 0xad, 0x74, 0xe6, + 0x0f, 0x7d, 0xbe, 0x79, 0x8f, 0xce, 0xac, 0xea, 0xf6, 0x20, 0xf0, 0x39, + 0x8b, 0x1d, 0x63, 0x13, 0xad, 0x2a, 0x16, 0xc0, 0xab, 0x4f, 0x59, 0x00, + 0x3e, 0xe2, 0x5a, 0xd1, 0xd1, 0xd5, 0xbd, 0xc9, 0x0f, 0x23, 0x3c, 0xe5, + 0xc4, 0x22, 0xb6, 0xbd, 0x66, 0x6d, 0x0c, 0x2d, 0x37, 0x6f, 0x77, 0x96, + 0xf7, 0x89, 0xe8, 0x95, 0x82, 0x4c, 0x4c, 0x7f, 0x5d, 0x1b, 0x2a, 0x5e, + 0x6d, 0x4a, 0x85, 0xb7, 0x0b, 0x70, 0x50, 0x10, 0xd5, 0xa3, 0xb5, 0x10, + 0x43, 0x1a, 0x34, 0x32, 0x71, 0x55, 0xca, 0x41, 0x7d, 0x05, 0x46, 0x92, + 0x83, 0x78, 0x84, 0x45, 0x69, 0xb0, 0x9c, 0x75, 0x75, 0xe5, 0x18, 0x22, + 0x52, 0xf9, 0x0b, 0x44, 0x6f, 0x78, 0x79, 0x0a, 0xf8, 0x1c, 0x06, 0x73, + 0xe1, 0x97, 0x5f, 0xca, 0xf5, 0x33, 0x4a, 0x76, 0x25, 0x5f, 0x91, 0xb0, + 0x44, 0x67, 0x0b, 0xb0, 0xc4, 0x4c, 0x46, 0xde, 0xe0, 0x20, 0x93, 0xb8, + 0xe3, 0x99, 0x7a, 0x84, 0xeb, 0x3e, 0x5a, 0xdb, 0xa6, 0x88, 0x0a, 0x9b, + 0xf8, 0xf5, 0xa4, 0xdd, 0x95, 0xb5, 0x74, 0xbd, 0x4d, 0x83, 0x2f, 0xe9, + 0x86, 0x16, 0xd8, 0x37, 0xdb, 0x26, 0x87, 0x1e, 0x63, 0xd3, 0xd1, 0x30, + 0x5b, 0xea, 0xc0, 0xa1, 0x5b, 0xa6, 0xb2, 0x8c, 0xf0, 0xf8, 0xd5, 0x21, + 0xda, 0xde, 0xd8, 0x59, 0x4c, 0x80, 0x57, 0x27, 0xf2, 0xb5, 0x44, 0xe4, + 0xe5, 0xca, 0x6d, 0x30, 0x9d, 0x9a, 0x79, 0xda, 0xa6, 0x9e, 0xa1, 0x6b, + 0x40, 0x22, 0x93, 0x0a, 0x11, 0xe6, 0x68, 0x13, 0x36, 0xf2, 0x09, 0xc5, + 0x31, 0x44, 0x7d, 0x6b, 0x41, 0xbf, 0x94, 0x9e, 0xd5, 0x2f, 0x5a, 0xbd, + 0x89, 0x82, 0x20, 0x5a, 0x67, 0x84, 0x0c, 0x57, 0xa5, 0x33, 0xf3, 0x30, + 0x09, 0x69, 0xcd, 0xe9, 0x29, 0x15, 0xca, 0x73, 0x78, 0xea, 0xa2, 0xbd, + 0x6d, 0xcc, 0xb4, 0x3c, 0xe6, 0x33, 0x90, 0xd8, 0x9e, 0xfa, 0x8c, 0x49, + 0x78, 0x09, 0x71, 0x0c, 0x3d, 0xda, 0xb8, 0xf3, 0xbf, 0x77, 0x6a, 0xa8, + 0x4a, 0x99, 0x40, 0xb3, 0x11, 0xf4, 0x50, 0xfd, 0x43, 0xae, 0x95, 0xf4, + 0xfd, 0x9b, 0x2a, 0xd0, 0x18, 0x06, 0xc6, 0xc2, 0xa8, 0x59, 0x79, 0x66, + 0x55, 0xdc, 0xbc, 0x6b, 0x2c, 0x89, 0x95, 0xaa, 0x6d, 0x96, 0x9e, 0x73, + 0x0e, 0xbe, 0xc4, 0xe4, 0x54, 0xc8, 0xc1, 0x3b, 0x7b, 0x0f, 0xc8, 0x3a, + 0x19, 0x48, 0xce, 0xf2, 0x1b, 0xd4, 0xb5, 0x78, 0xa5, 0xfb, 0x5a, 0xe7, + 0xc1, 0xd5, 0xbd, 0x9c, 0x1d, 0x7d, 0x9c, 0xb9, 0xfb, 0x94, 0x12, 0xe4, + 0x4d, 0xbf, 0x9e, 0xee, 0x53, 0x32, 0x97, 0x0e, 0x35, 0xf7, 0x88, 0x3c, + 0x05, 0xa9, 0x67, 0xc3, 0x7d, 0x0e, 0x30, 0xb7, 0x1e, 0xa8, 0x50, 0x94, + 0x31, 0x51, 0x49, 0x97, 0x8f, 0x7b, 0x2f, 0x52, 0x70, 0x7f, 0x7c, 0xaf, + 0xf8, 0xa6, 0x20, 0x8b, 0x3e, 0xf1, 0x3c, 0xf4, 0xab, 0xe4, 0x18, 0x1d, + 0x45, 0xeb, 0xb8, 0x88, 0x2f, 0x8a, 0x90, 0xd1, 0xb8, 0xc5, 0xab, 0xe6, + 0x9e, 0x53, 0x74, 0x66, 0x7e, 0xc2, 0x28, 0x78, 0x1b, 0x13, 0x0b, 0x36, + 0x73, 0xe9, 0x98, 0xc9, 0xdd, 0xe6, 0x71, 0x84, 0x77, 0xeb, 0x8c, 0xeb, + 0x65, 0x76, 0x55, 0xf6, 0xf2, 0xaf, 0xb0, 0x05, 0xd2, 0xc3, 0x2a, 0xc9, + 0x37, 0xd9, 0x34, 0x51, 0x16, 0xa1, 0x1b, 0x85, 0x14, 0x73, 0x74, 0x2a, + 0x97, 0x2f, 0x5b, 0x14, 0x53, 0x51, 0xad, 0x74, 0x1f, 0xf7, 0x3b, 0x81, + 0xad, 0x46, 0xec, 0x43, 0x91, 0x0c, 0x40, 0x93, 0xe3, 0x61, 0x0a, 0xd1, + 0x6b, 0x0d, 0xb2, 0x66, 0x0e, 0x25, 0xd9, 0x54, 0xa4, 0xed, 0xd5, 0x29, + 0xde, 0x3e, 0xfb, 0xee, 0x25, 0x28, 0x7b, 0xb2, 0xf6, 0x2e, 0xfc, 0x65, + 0xbf, 0x06, 0xf0, 0xae, 0xa5, 0x0e, 0xf1, 0xea, 0xcc, 0x5d, 0x9c, 0x8f, + 0xd2, 0x44, 0x1f, 0x7f, 0xe4, 0x50, 0x27, 0x03, 0x17, 0x54, 0xf3, 0x37, + 0xcf, 0x7b, 0x2d, 0xda, 0xe5, 0xb5, 0xdd, 0x3d, 0xae, 0xa2, 0x95, 0x9c, + 0x58, 0xbc, 0x5f, 0x24, 0x39, 0x96, 0x1b, 0xf6, 0xdc, 0x3a, 0xf7, 0xf8, + 0x6d, 0xef, 0x6b, 0xd6, 0x68, 0x05, 0x2f, 0x01, 0xfa, 0x0d, 0x4f, 0x35, + 0x3f, 0x17, 0x49, 0x03, 0xb2, 0x25, 0x71, 0xbf, 0xa3, 0xa4, 0xa2, 0xe2, + 0x16, 0x8c, 0x02, 0xe0, 0x99, 0x68, 0x87, 0x16, 0x22, 0x61, 0xce, 0xd2, + 0xc0, 0xc9, 0x67, 0xb9, 0x7c, 0xb6, 0xc4, 0x58, 0xc5, 0x0a, 0xae, 0xcc, + 0x38, 0x52, 0xae, 0x0b, 0xc0, 0xc8, 0xad, 0x14, 0xaa, 0x58, 0xb4, 0x5f, + 0x1e, 0xc0, 0xef, 0xfa, 0x08, 0x5a, 0x28, 0x2b, 0xae, 0xb3, 0x77, 0x2b, + 0xf3, 0x9d, 0xfd, 0xb4, 0xdf, 0x09, 0x46, 0xba, 0x59, 0xa6, 0xc4, 0x80, + 0xb1, 0x80, 0x91, 0xac, 0x5e, 0x47, 0x2f, 0xc8, 0xa8, 0x96, 0x26, 0x54, + 0xb6, 0xe9, 0x97, 0x3c, 0x50, 0x69, 0x5d, 0x7f, 0x15, 0xfb, 0x78, 0x5b, + 0x33, 0xcd, 0x39, 0x38, 0x6e, 0xb4, 0x08, 0xac, 0x2a, 0xcf, 0xc5, 0x8a, + 0x69, 0x73, 0xe7, 0xab, 0x5a, 0xb4, 0xca, 0x63, 0xf8, 0xe5, 0x77, 0xf8, + 0xeb, 0x06, 0xff, 0xf1, 0x6a, 0x1d, 0xdc, 0x02, 0xb9, 0x98, 0x66, 0x05, + 0xb1, 0xc6, 0x35, 0x7f, 0xce, 0xcb, 0x13, 0x4f, 0x99, 0x39, 0x76, 0x9b, + 0xe5, 0x8f, 0xed, 0x8a, 0x44, 0x82, 0xd4, 0x79, 0xee, 0x75, 0x0e, 0x11, + 0x3b, 0x35, 0xab, 0xf1, 0xa1, 0x6a, 0x4b, 0xa4, 0x89, 0x86, 0x2f, 0x14, + 0xcc, 0xd0, 0x68, 0x91, 0xb3, 0x41, 0xc4, 0x5a, 0xc0, 0x4e, 0x67, 0x55, + 0x65, 0x8f, 0xc4, 0x56, 0xf9, 0xb8, 0x63, 0x75, 0x17, 0x0f, 0x9c, 0xd8, + 0xeb, 0xe3, 0xda, 0x0a, 0xd6, 0x8c, 0xcb, 0x4f, 0x62, 0x9a, 0x07, 0xad, + 0x71, 0x2b, 0xc3, 0x08, 0xaf, 0x26, 0x05, 0x8f, 0x8f, 0x67, 0xaf, 0x68, + 0x8e, 0xb1, 0xca, 0x19, 0x83, 0x96, 0xc5, 0xed, 0x4a, 0xe1, 0xc6, 0x22, + 0x7c, 0xf4, 0xe2, 0x13, 0xc8, 0x13, 0xcc, 0x35, 0xbe, 0x40, 0xc4, 0xfb, + 0xfa, 0xdc, 0xf8, 0x26, 0xe7, 0x6d, 0xa3, 0x7a, 0x9f, 0x39, 0x84, 0x2c, + 0x4a, 0x74, 0x34, 0x4d, 0x2c, 0x43, 0x62, 0xb9, 0xb4, 0x32, 0x9a, 0x38, + 0x29, 0xdb, 0xa6, 0xc6, 0xee, 0x1f, 0x46, 0xff, 0x81, 0xcf, 0x9d, 0x8d, + 0xb4, 0xb1, 0xf9, 0x5b, 0xb1, 0x83, 0xc0, 0xc6, 0x50, 0xb2, 0x17, 0xd6, + 0x5a, 0x9d, 0xcf, 0x16, 0xee, 0x03, 0xd0, 0x28, 0xde, 0xb9, 0x2e, 0x1f, + 0x6b, 0x90, 0xf0, 0x03, 0x5a, 0x11, 0xcf, 0x1c, 0x9a, 0x1f, 0x9f, 0x1f, + 0xce, 0x49, 0x7d, 0x67, 0xec, 0x30, 0x8d, 0x13, 0x5e, 0x3f, 0x19, 0x14, + 0x17, 0xf9, 0xb9, 0x6d, 0xf2, 0x2c, 0xfc, 0xe1, 0x91, 0x61, 0x51, 0x6b, + 0x2d, 0x42, 0x8a, 0x00, 0xad, 0xc9, 0xd2, 0x90, 0x29, 0x00, 0xe1, 0xf2, + 0x4a, 0x51, 0x60, 0x69, 0x31, 0x0a, 0x45, 0x14, 0xd5, 0x15, 0xc9, 0x74, + 0x64, 0x42, 0xaf, 0x2a, 0x6c, 0x5e, 0xaf, 0xc6, 0x85, 0x41, 0x2b, 0xdb, + 0x93, 0x7a, 0xbd, 0xa0, 0x06, 0x32, 0xcc, 0x73, 0xd8, 0xcc, 0x25, 0x44, + 0x0b, 0xae, 0x67, 0xa4, 0x72, 0x64, 0x3d, 0x65, 0x5e, 0xa3, 0xa4, 0x90, + 0x3d, 0x91, 0x9e, 0xf9, 0x8e, 0xcc, 0x67, 0x60, 0xda, 0x06, 0xc6, 0x41, + 0xb9, 0x7b, 0x0c, 0x09, 0x42, 0xef, 0x66, 0x39, 0x77, 0x11, 0xfd, 0xef, + 0x7f, 0xa8, 0xed, 0xc9, 0x21, 0x86, 0x97, 0xaf, 0xcb, 0x4f, 0x4c, 0xfb, + 0xd6, 0x8f, 0xc5, 0x70, 0xa5, 0x5e, 0xdb, 0xad, 0xd2, 0xef, 0xaf, 0x9d, + 0xef, 0xeb, 0x21, 0xe2, 0x9c, 0x28, 0x4d, 0xfd, 0x8d, 0xef, 0x74, 0x5e, + 0x2e, 0xec, 0x39, 0x59, 0x87, 0x87, 0xcd, 0x86, 0x22, 0x94, 0xc6, 0x0a, + 0xcb, 0x67, 0x29, 0x8a, 0xf9, 0xac, 0x89, 0x6f, 0x86, 0x15, 0x29, 0x9b, + 0xfa, 0xf5, 0x32, 0xac, 0x7f, 0x70, 0xe8, 0x64, 0xbe, 0x1c, 0xba, 0xdb, + 0xc9, 0x72, 0xac, 0xaa, 0x2b, 0x6e, 0x72, 0x67, 0x31, 0xf3, 0xa9, 0x6d, + 0x1d, 0xd6, 0xbb, 0x48, 0xd4, 0x84, 0x6e, 0x37, 0xb5, 0xa5, 0x51, 0x92, + 0xb0, 0x09, 0xff, 0x1e, 0x36, 0xcc, 0xa2, 0x00, 0xc9, 0x74, 0x0f, 0x3b, + 0x65, 0x73, 0x5c, 0x29, 0xf7, 0x7a, 0xae, 0x8c, 0x18, 0xf9, 0x32, 0xf9, + 0xd9, 0x86, 0x50, 0x66, 0xa9, 0x2b, 0xa4, 0x16, 0xde, 0x40, 0x1a, 0xdd, + 0x57, 0xd7, 0x3f, 0x2b, 0xed, 0x4d, 0x67, 0x3c, 0x9d, 0xee, 0x96, 0x27, + 0xf8, 0xa2, 0x05, 0xe6, 0xaf, 0xdb, 0x21, 0x12, 0x1e, 0x8b, 0x9f, 0xba, + 0x48, 0x1a, 0xe5, 0x25, 0xf6, 0xd8, 0x1c, 0x80, 0x49, 0xd0, 0xb5, 0x96, + 0x21, 0x18, 0x30, 0x9c, 0x3a, 0x1f, 0x0c, 0xec, 0x50, 0x1f, 0x3d, 0x02, + 0xf9, 0x82, 0x87, 0xaa, 0xac, 0xbc, 0xe6, 0x95, 0x20, 0x17, 0x99, 0x01, + 0x91, 0x9b, 0x15, 0x4a, 0x76, 0x74, 0x8f, 0xa2, 0xe3, 0xf5, 0x33, 0xd8, + 0xb2, 0x45, 0xaf, 0x7a, 0x0e, 0x66, 0x30, 0x1e, 0xe7, 0x21, 0xa9, 0x5a, + 0xd6, 0x3e, 0x40, 0x20, 0x38, 0xc0, 0x12, 0x48, 0xba, 0x17, 0xcc, 0x16, + 0x08, 0x30, 0xe5, 0x1c, 0xbc, 0x10, 0x2e, 0xbc, 0x56, 0xfb, 0x2a, 0x95, + 0xc8, 0x94, 0xb9, 0x65, 0x3d, 0x9c, 0x68, 0x59, 0x97, 0x53, 0xc1, 0xc5, + 0x30, 0x3f, 0x60, 0xfc, 0x07, 0x79, 0xe8, 0x3e, 0xb6, 0x6d, 0x5a, 0x64, + 0x2d, 0x9e, 0x8f, 0xf3, 0x11, 0xc1, 0x2d, 0xa1, 0x38, 0xfa, 0x46, 0x37, + 0xf8, 0x4f, 0x5b, 0x29, 0xc9, 0xb9, 0xe2, 0x0a, 0xf8, 0xea, 0xd8, 0xe7, + 0xc9, 0xe8, 0x32, 0xdf, 0xfe, 0x2a, 0x9b, 0xa4, 0x39, 0x7a, 0x91, 0x84, + 0x5e, 0xd3, 0x3d, 0xa3, 0x2a, 0x82, 0x3c, 0x1f, 0xe7, 0x96, 0x87, 0x7a, + 0x27, 0xa7, 0xa1, 0x34, 0x1c, 0x48, 0x76, 0xa6, 0xb2, 0x8c, 0x04, 0xd5, + 0xb9, 0x05, 0x3a, 0xa6, 0x91, 0xd3, 0xce, 0xf0, 0xcc, 0xb9, 0x2d, 0x14, + 0xb3, 0x90, 0x4b, 0xaf, 0x44, 0x18, 0x6c, 0x4a, 0x94, 0xd2, 0x33, 0x90, + 0x62, 0xe0, 0xe0, 0xf7, 0x4c, 0x66, 0x84, 0x95, 0x09, 0x00, 0xc4, 0xbf, + 0xdd, 0x7a, 0x3b, 0xf1, 0xec, 0xde, 0x0c, 0x9b, 0x41, 0x8c, 0xbf, 0x9a, + 0x2d, 0x16, 0x1f, 0x0f, 0xb3, 0x32, 0x55, 0x37, 0x3f, 0xbb, 0x4d, 0x9e, + 0x1c, 0x9f, 0x7f, 0xf4, 0xcd, 0xf0, 0xfd, 0xa9, 0xa1, 0x1d, 0x0c, 0xa8, + 0xee, 0x03, 0x0e, 0x86, 0x8e, 0x62, 0xba, 0xcf, 0x58, 0x96, 0x66, 0xa1, + 0xf9, 0xd3, 0x61, 0xce, 0xd6, 0xe1, 0x40, 0x3f, 0x88, 0x66, 0x33, 0xed, + 0x5a, 0x90, 0x10, 0xac, 0x7d, 0x9b, 0x04, 0xcd, 0x03, 0x42, 0x25, 0xa2, + 0xc2, 0xf2, 0xf2, 0x40, 0xf0, 0xb9, 0x82, 0x66, 0xa1, 0xa8, 0x6e, 0xe5, + 0xa2, 0x68, 0xd7, 0x92, 0xc9, 0x74, 0xa1, 0x43, 0x3d, 0x88, 0x6b, 0x89, + 0xf6, 0x96, 0x3a, 0x8d, 0x4f, 0xb7, 0xbe, 0x10, 0x4c, 0x7f, 0x0f, 0xc9, + 0x87, 0x0c, 0x63, 0x11, 0x31, 0xb0, 0xe4, 0x07, 0x84, 0x56, 0x35, 0xed, + 0x9d, 0xf1, 0xf4, 0xcd, 0x5a, 0x8c, 0xb7, 0xac, 0xaa, 0x79, 0x80, 0x7e, + 0x35, 0x25, 0xb8, 0x79, 0xbb, 0x26, 0xce, 0x16, 0x12, 0x21, 0x90, 0xd9, + 0xde, 0x19, 0x12, 0x98, 0xff, 0x8d, 0x8e, 0xd7, 0x41, 0x06, 0x42, 0xa2, + 0x58, 0x6d, 0x32, 0xa5, 0x86, 0x0c, 0x58, 0x81, 0x00, 0xac, 0x86, 0xc7, + 0x65, 0xef, 0xb8, 0x00, 0x8b, 0x90, 0xbe, 0xd9, 0x20, 0x83, 0x27, 0xd1, + 0xc1, 0xb6, 0x18, 0x4f, 0x11, 0x8c, 0x04, 0x0a, 0x5b, 0x7e, 0x72, 0xa7, + 0x50, 0x08, 0x51, 0x12, 0x7a, 0x45, 0x87, 0x01, 0xb1, 0x2d, 0x8b, 0xf2, + 0x12, 0x13, 0xfa, 0x05, 0x90, 0xd4, 0x03, 0x9e, 0xca, 0x4f, 0xc7, 0x8e, + 0x82, 0x4f, 0x40, 0x83, 0x8c, 0x27, 0x23, 0x0c, 0xab, 0x7f, 0x0d, 0x24, + 0x9a, 0xad, 0x91, 0xcc, 0x58, 0xbd, 0x37, 0x23, 0x0a, 0xa9, 0xab, 0x25, + 0x72, 0x0a, 0x16, 0x82, 0x9e, 0x38, 0xd7, 0x75, 0x69, 0xab, 0xc3, 0x42, + 0xc4, 0x4c, 0xd6, 0xd9, 0xe3, 0xfe, 0x57, 0xed, 0xa0, 0xcc, 0x59, 0xd9, + 0x0e, 0x82, 0x7a, 0x29, 0x7e, 0xad, 0x6b, 0x7c, 0xf9, 0x74, 0x40, 0x74, + 0xdd, 0xe4, 0x85, 0x31, 0x65, 0x31, 0x6c, 0x0c, 0x0a, 0xe0, 0x62, 0x1a, + 0x19, 0x40, 0xcc, 0x27, 0xd0, 0x22, 0x4d, 0xfe, 0x68, 0x3e, 0xc0, 0xb2, + 0x64, 0xbf, 0xb2, 0x4b, 0x13, 0x94, 0x10, 0x51, 0xa6, 0x97, 0x68, 0x03, + 0x53, 0xa3, 0x9f, 0x45, 0x42, 0x5e, 0x57, 0x6b, 0x8c, 0xdb, 0x2f, 0x81, + 0xcd, 0xa4, 0x89, 0x32, 0x08, 0x0e, 0x3c, 0x97, 0x52, 0xeb, 0xf0, 0xa2, + 0x9a, 0xeb, 0x30, 0xae, 0xc8, 0x18, 0xa0, 0xc1, 0xbb, 0x78, 0x10, 0x89, + 0xff, 0xeb, 0x20, 0xa0, 0xdb, 0x3d, 0xa8, 0xd8, 0xf2, 0x03, 0xda, 0x9d, + 0x8f, 0x31, 0xfe, 0x51, 0x53, 0x95, 0x78, 0x09, 0xbf, 0x75, 0x53, 0xb1, + 0xc2, 0xa3, 0x86, 0x4e, 0xca, 0x06, 0x24, 0x85, 0xcb, 0xd0, 0xe5, 0xe5, + 0xaa, 0x13, 0xde, 0xec, 0x9a, 0xb1, 0x9b, 0x10, 0x27, 0xd3, 0x72, 0xaf, + 0x6a, 0xa1, 0x68, 0x6f, 0x0e, 0x0b, 0x14, 0x24, 0x94, 0x57, 0x01, 0x77, + 0x5b, 0x8d, 0xdb, 0x6d, 0xba, 0xb8, 0x4a, 0xc8, 0xaa, 0x6d, 0x6c, 0xb9, + 0x72, 0x43, 0x23, 0xb3, 0xb0, 0x42, 0x87, 0x91, 0x56, 0x0e, 0x7b, 0x26, + 0xf7, 0x92, 0x97, 0x5c, 0x1e, 0x0d, 0xd9, 0x1a, 0x12, 0xe7, 0x8d, 0xce, + 0x0e, 0xf2, 0xd0, 0x13, 0xac, 0xf6, 0x5f, 0x60, 0xc4, 0x7e, 0x26, 0x10, + 0x19, 0x4b, 0xdd, 0x7f, 0x42, 0x1b, 0xfa, 0xee, 0x72, 0xbe, 0x44, 0xa6, + 0xe3, 0x4d, 0x65, 0xcc, 0xb1, 0xd8, 0xa1, 0x44, 0x04, 0xff, 0xaf, 0xe8, + 0x5e, 0xdc, 0xa8, 0x4a, 0x47, 0x8d, 0x00, 0x43, 0x6e, 0x91, 0xff, 0xed, + 0x22, 0xe5, 0x6e, 0x85, 0x15, 0x3f, 0x01, 0x41, 0xda, 0x2f, 0xaa, 0x1c, + 0xbe, 0x48, 0x4c, 0x0c, 0x48, 0xf3, 0xb2, 0x6d, 0xce, 0xdd, 0xa3, 0x41, + 0x8f, 0x4d, 0x67, 0x20, 0x16, 0xb6, 0x86, 0xcd, 0x05, 0x68, 0xf4, 0x74, + 0x93, 0x3f, 0xdb, 0xc9, 0x15, 0x04, 0x85, 0x23, 0xc7, 0x34, 0xa5, 0x05, + 0xa3, 0x27, 0x08, 0x5e, 0xf1, 0x87, 0xc7, 0xcb, 0x07, 0xec, 0x5e, 0xc4, + 0x0e, 0xef, 0x23, 0x0f, 0xcf, 0x51, 0xd2, 0x9a, 0xe1, 0xb2, 0x58, 0x31, + 0x29, 0xa9, 0xe3, 0x33, 0x11, 0xd2, 0x31, 0x6c, 0x7c, 0x0c, 0x2f, 0xcb, + 0x58, 0x30, 0xc8, 0x25, 0xed, 0x03, 0x7a, 0x70, 0xe3, 0x6a, 0x91, 0x4b, + 0xd4, 0x13, 0x4d, 0x6a, 0x2b, 0xfb, 0x2c, 0xc7, 0x0e, 0x0c, 0xd6, 0x92, + 0x8e, 0x83, 0x6a, 0x9e, 0xe1, 0x79, 0x85, 0xf1, 0x6a, 0x91, 0xcd, 0xc7, + 0x4d, 0x20, 0x22, 0x39, 0x40, 0xcb, 0x11, 0x36, 0x4c, 0xd0, 0xbe, 0x5f, + 0xaf, 0xc0, 0xb6, 0x64, 0x94, 0xec, 0x90, 0xe3, 0x23, 0x77, 0xdf, 0x46, + 0x54, 0x84, 0xc8, 0xd3, 0xfc, 0xe8, 0x87, 0xd4, 0xf4, 0xd1, 0xa8, 0x54, + 0xa3, 0x5c, 0x06, 0xa5, 0xea, 0x05, 0xb3, 0x62, 0x6d, 0x82, 0x30, 0xe8, + 0xd4, 0x3f, 0xca, 0xaa, 0x54, 0xc5, 0x36, 0xa3, 0x8e, 0xe8, 0xa0, 0x17, + 0x38, 0x72, 0x4f, 0xc7, 0xaa, 0x85, 0x33, 0x08, 0x75, 0x7e, 0x1a, 0xc2, + 0x41, 0x8b, 0x3f, 0xa5, 0x97, 0xb1, 0x3b, 0x52, 0x64, 0x46, 0x66, 0x6b, + 0xf4, 0x8d, 0xf3, 0xe3, 0x16, 0x26, 0xf0, 0xc2, 0x8a, 0xee, 0x35, 0x1d, + 0x68, 0x2a, 0x24, 0x58, 0xcf, 0x92, 0x6a, 0xd3, 0x7e, 0x26, 0xc6, 0xf6, + 0xec, 0x47, 0x08, 0x69, 0xf6, 0x1e, 0x9b, 0x7b, 0x59, 0x2c, 0xac, 0x98, + 0x24, 0x7b, 0x68, 0x98, 0x95, 0x6e, 0xd9, 0xe2, 0x8d, 0xc6, 0x40, 0x6b, + 0xd5, 0x1e, 0xcd, 0x07, 0x16, 0xa7, 0x4f, 0x3c, 0x48, 0xb0, 0xc3, 0x2d, + 0xbb, 0xe0, 0x39, 0x11, 0xe3, 0x88, 0xd5, 0x7b, 0x8c, 0x25, 0xc2, 0x56, + 0x01, 0xb5, 0x45, 0x2f, 0xa9, 0x41, 0x1c, 0xc7, 0x4d, 0x94, 0x65, 0x62, + 0x3f, 0x07, 0xf0, 0x2e, 0x9a, 0x09, 0xf0, 0xe1, 0x3a, 0xf7, 0x7e, 0x16, + 0x23, 0x0f, 0xf6, 0xd1, 0x6b, 0xce, 0xde, 0x0b, 0x8b, 0xf8, 0xa9, 0x2f, + 0x7b, 0x94, 0x30, 0x10, 0x13, 0x3d, 0xa0, 0x39, 0x61, 0xcf, 0xba, 0xbc, + 0x4c, 0x22, 0x4f, 0x0d, 0xaa, 0x83, 0x24, 0x7c, 0x7c, 0x65, 0xcc, 0x92, + 0x84, 0xb8, 0xa8, 0x21, 0xb6, 0x0f, 0xd2, 0x5a, 0x63, 0xa5, 0x82, 0xcd, + 0x2d, 0x0f, 0x1e, 0x44, 0x97, 0xea, 0x0a, 0x0c, 0x76, 0x8b, 0x9b, 0x53, + 0xb2, 0x7d, 0xcb, 0xe4, 0x44, 0xf8, 0x95, 0x72, 0x09, 0x0b, 0xfd, 0x1f, + 0x44, 0x3d, 0xa5, 0xaf, 0xa8, 0xd5, 0x48, 0xe8, 0x38, 0x3e, 0x36, 0x91, + 0x8c, 0x99, 0x3c, 0x02, 0x35, 0xd8, 0x6b, 0xa8, 0x9e, 0xeb, 0x4a, 0x57, + 0x08, 0x40, 0x30, 0x69, 0xa4, 0x01, 0x5a, 0xf3, 0xeb, 0xa7, 0x08, 0xf1, + 0x40, 0xcb, 0x18, 0xe2, 0x7a, 0xfc, 0x60, 0x22, 0x16, 0x22, 0xfc, 0x2d, + 0x65, 0x98, 0x1e, 0xdb, 0xe7, 0xd9, 0xfe, 0x49, 0x40, 0x6e, 0xc4, 0x38, + 0x43, 0x36, 0x16, 0x74, 0x66, 0x9b, 0x03, 0xe1, 0xb8, 0x9e, 0x0d, 0x09, + 0x84, 0x8d, 0x72, 0x92, 0x22, 0xb8, 0xb1, 0x6b, 0xc1, 0x20, 0x66, 0xeb, + 0x83, 0x70, 0xc8, 0x49, 0x08, 0xa8, 0x20, 0x28, 0x05, 0x6c, 0xd0, 0xe3, + 0xf3, 0xed, 0xc1, 0x3e, 0x7e, 0xf0, 0x20, 0x4e, 0x65, 0x1e, 0xd6, 0x75, + 0x28, 0x50, 0x3e, 0xfc, 0x52, 0x12, 0x78, 0xdc, 0x40, 0x43, 0x6d, 0xb6, + 0xfa, 0x2f, 0x80, 0x86, 0x24, 0x5a, 0x57, 0x67, 0x2b, 0xef, 0xda, 0x77, + 0x52, 0x1d, 0x46, 0xf1, 0x55, 0x57, 0xd0, 0xb9, 0x56, 0xce, 0x63, 0x59, + 0xe0, 0x22, 0xa2, 0xe7, 0xa9, 0x50, 0xec, 0x10, 0x6c, 0x14, 0x1c, 0x08, + 0x28, 0x6a, 0x3c, 0x50, 0x45, 0xb0, 0x5d, 0xc7, 0x2a, 0x00, 0xcd, 0x91, + 0x7f, 0x62, 0xbc, 0x65, 0x9e, 0x22, 0xbb, 0xcf, 0x02, 0x6a, 0xe4, 0xda, + 0xa6, 0x51, 0xc1, 0x99, 0xa5, 0xa7, 0x23, 0x80, 0xde, 0x4d, 0xc5, 0xdf, + 0x6d, 0x31, 0xf9, 0x84, 0x2b, 0xff, 0x06, 0x00, 0x9b, 0x9f, 0xd0, 0x32, + 0x0b, 0x03, 0xdc, 0xce, 0x37, 0x11, 0x84, 0xc5, 0x56, 0xd1, 0x87, 0x9c, + 0x80, 0xf2, 0xe1, 0x76, 0x86, 0x55, 0x60, 0xf0, 0x03, 0x06, 0xe9, 0x13, + 0x8f, 0x6c, 0xdd, 0xf3, 0x63, 0x25, 0x19, 0x65, 0x03, 0xdb, 0x69, 0x2e, + 0x33, 0x1b, 0xf3, 0xe6, 0xa8, 0x4d, 0x20, 0xb7, 0x61, 0x4b, 0x89, 0xe3, + 0x14, 0xa7, 0x6c, 0xa0, 0x74, 0x30, 0x5d, 0x77, 0x10, 0xb6, 0x13, 0xd8, + 0xfd, 0x71, 0x5b, 0x71, 0x98, 0xc8, 0xac, 0xd2, 0x1c, 0xb9, 0xc5, 0x5d, + 0xf0, 0x5d, 0x83, 0xe4, 0xde, 0x4f, 0x83, 0xe5, 0x9a, 0xca, 0x2f, 0xb4, + 0xb5, 0xbb, 0x72, 0x0f, 0x5d, 0xf7, 0xe2, 0x6d, 0xd7, 0x54, 0xd5, 0xa0, + 0x6f, 0x07, 0xab, 0x41, 0x86, 0x7b, 0x7e, 0xd1, 0x5a, 0x38, 0xee, 0x54, + 0x13, 0x5e, 0x0d, 0x68, 0x10, 0xe0, 0x7f, 0x35, 0xe9, 0x52, 0x36, 0xb3, + 0x84, 0x38, 0xa1, 0xa5, 0x65, 0xed, 0x2f, 0x21, 0xfc, 0x08, 0x62, 0x21, + 0xc8, 0x17, 0xc6, 0xd4, 0x9e, 0xe7, 0x48, 0x12, 0x41, 0xc2, 0xbf, 0x6c, + 0xce, 0xb5, 0xe8, 0x8e, 0x61, 0x0d, 0x6c, 0xdd, 0x52, 0x2c, 0x47, 0xa5, + 0x2e, 0x0b, 0x64, 0x8c, 0x1f, 0xde, 0x21, 0xd7, 0x9f, 0x8c, 0xa7, 0x4d, + 0x4c, 0x74, 0x09, 0xb3, 0x6c, 0x93, 0xe7, 0x51, 0xbe, 0xb2, 0x61, 0x35, + 0xbc, 0x13, 0xee, 0x71, 0x7b, 0x6b, 0xbf, 0xf9, 0x6e, 0x8c, 0x87, 0x04, + 0xdb, 0x03, 0xa4, 0xb5, 0x00, 0x8a, 0x4e, 0xb1, 0x7f, 0x19, 0x35, 0xc0, + 0xf3, 0x25, 0xe1, 0xcb, 0x7f, 0xf9, 0x83, 0xcf, 0xba, 0xf8, 0x51, 0x83, + 0x56, 0x06, 0x02, 0x8d, 0x24, 0xc7, 0x44, 0xc1, 0xaf, 0x07, 0xe2, 0x65, + 0xee, 0x54, 0x3e, 0xc7, 0x88, 0xce, 0x57, 0xaf, 0x8e, 0xf8, 0x01, 0x17, + 0x35, 0x69, 0x70, 0xd4, 0xc2, 0x35, 0x5b, 0x74, 0x77, 0xaf, 0x8e, 0xb1, + 0xe3, 0x60, 0x91, 0x6b, 0x23, 0xe3, 0x04, 0x1f, 0x02, 0xf7, 0x89, 0xee, + 0x48, 0x1a, 0x94, 0x1f, 0x32, 0x86, 0xb7, 0xaf, 0xc4, 0xc9, 0xac, 0x90, + 0x8c, 0xf7, 0x7d, 0x76, 0xb1, 0x8a, 0x44, 0xf2, 0x6f, 0xc9, 0xfc, 0x46, + 0x6f, 0xad, 0x2c, 0x56, 0x2a, 0x62, 0x5e, 0x6f, 0x51, 0x9e, 0xe9, 0xf8, + 0xb4, 0xdc, 0x33, 0xa0, 0xea, 0x70, 0xbe, 0x40, 0x7b, 0x6c, 0x07, 0x55, + 0x02, 0x08, 0x9a, 0x61, 0x10, 0x23, 0x8d, 0xea, 0xe9, 0x4f, 0xf3, 0xf9, + 0x83, 0xf3, 0x8c, 0xdd, 0x75, 0xfa, 0x74, 0x3f, 0xc5, 0xb9, 0x0a, 0x59, + 0x6d, 0xe8, 0x59, 0x13, 0xac, 0x75, 0x76, 0x6e, 0x77, 0x07, 0x92, 0x78, + 0x79, 0x73, 0x12, 0x41, 0x48, 0x64, 0x11, 0x70, 0x65, 0xd2, 0x87, 0x90, + 0xa3, 0xf8, 0x20, 0x79, 0xbb, 0x61, 0x4b, 0xa9, 0x8a, 0xd1, 0x38, 0x15, + 0xb5, 0x6d, 0x16, 0x3d, 0xc9, 0x6d, 0x42, 0x81, 0x26, 0x8b, 0x46, 0x65, + 0xd6, 0xf2, 0x81, 0x9b, 0xb0, 0x3d, 0x49, 0x4e, 0x10, 0x8a, 0xb7, 0x32, + 0x4b, 0x14, 0xd3, 0x43, 0xf5, 0xf1, 0xf1, 0xac, 0x04, 0x7d, 0x3a, 0xd8, + 0x44, 0xeb, 0x3c, 0xb4, 0x28, 0xf5, 0x7e, 0x00, 0xad, 0x85, 0x17, 0x20, + 0x8d, 0xb8, 0xac, 0x51, 0x0e, 0xe7, 0x08, 0xb0, 0x1d, 0xc3, 0x1c, 0x23, + 0x89, 0x72, 0x2b, 0x91, 0xed, 0x58, 0x39, 0xbf, 0x3c, 0x49, 0x38, 0xbc, + 0xff, 0x7d, 0xbf, 0x74, 0x2a, 0x86, 0x81, 0x71, 0x4b, 0xea, 0x03, 0x76, + 0xfa, 0x0b, 0xa4, 0x40, 0xee, 0xc0, 0x9c, 0x8b, 0x7b, 0x7b, 0x4a, 0x6e, + 0xf2, 0xbf, 0xbb, 0x40, 0x11, 0x20, 0x0b, 0x71, 0x30, 0x2f, 0xed, 0x52, + 0x78, 0xf3, 0xbb, 0x3c, 0xb0, 0x40, 0xe5, 0xb1, 0xde, 0x45, 0x33, 0x3f, + 0xef, 0x40, 0x0f, 0xe0, 0x57, 0x44, 0xae, 0x32, 0x21, 0xce, 0x0c, 0xf7, + 0xa7, 0x66, 0x0c, 0xb0, 0x1e, 0x6f, 0x91, 0xbb, 0x8c, 0x1f, 0x1c, 0x55, + 0x11, 0x84, 0x26, 0x3c, 0xe5, 0x94, 0xd9, 0x93, 0x95, 0xfd, 0x65, 0x04, + 0xd9, 0xaa, 0xeb, 0xf0, 0x12, 0x3e, 0x73, 0x9c, 0x02, 0x4c, 0x1e, 0xc9, + 0x6f, 0x29, 0x84, 0xe0, 0xe8, 0x8b, 0xc1, 0xa7, 0xea, 0x05, 0x70, 0xc5, + 0x59, 0x8d, 0xa5, 0x22, 0xf0, 0x9d, 0xbc, 0x68, 0x84, 0xe2, 0xe9, 0x99, + 0xa2, 0x80, 0xce, 0xeb, 0x47, 0x70, 0x2e, 0x4c, 0x57, 0x86, 0xbd, 0xbf, + 0x1e, 0xb6, 0x17, 0xca, 0xe4, 0xf4, 0x30, 0x19, 0x27, 0x9b, 0xab, 0x3f, + 0xd1, 0xa6, 0x4a, 0x9e, 0x49, 0xd8, 0x91, 0xcf, 0xd4, 0x6d, 0x28, 0x1c, + 0xfe, 0xdb, 0xd3, 0x4b, 0xbb, 0xa4, 0xb0, 0xb9, 0x19, 0x20, 0x2a, 0x29, + 0x69, 0x7e, 0xda, 0x7e, 0x78, 0xc9, 0xe6, 0x12, 0xcb, 0x00, 0xd3, 0x51, + 0xe9, 0x65, 0x75, 0x9f, 0xfb, 0xfe, 0xcb, 0x81, 0xe0, 0x32, 0x4d, 0x52, + 0x44, 0xd2, 0xd0, 0x4c, 0x30, 0xe3, 0xa4, 0x1c, 0x6b, 0xd2, 0x98, 0x78, + 0x34, 0x63, 0x11, 0x1b, 0x06, 0x3a, 0xa1, 0x69, 0x76, 0x5a, 0x58, 0x9e, + 0x30, 0x61, 0x07, 0x68, 0x84, 0x7f, 0x8f, 0x9e, 0xdb, 0x75, 0xac, 0xc8, + 0x4f, 0xba, 0xbb, 0x8d, 0xd8, 0xc5, 0xaf, 0xa3, 0xc3, 0x3f, 0x4e, 0x85, + 0xc4, 0x9a, 0x74, 0x70, 0xfc, 0xca, 0x61, 0xf2, 0x5b, 0xa7, 0x79, 0x1e, + 0x4e, 0x27, 0x21, 0x6e, 0x1d, 0xf0, 0xbd, 0x95, 0xf7, 0x1f, 0x7c, 0x28, + 0xa3, 0xf4, 0x18, 0x68, 0x8b, 0x81, 0x29, 0x98, 0x10, 0xb6, 0xb9, 0x29, + 0xc2, 0xa4, 0x0d, 0x8a, 0xb7, 0x91, 0xbd, 0xa9, 0xa7, 0x52, 0xb5, 0xea, + 0x59, 0xf7, 0x7b, 0x17, 0x75, 0xa9, 0x8a, 0x16, 0x72, 0xa5, 0x58, 0x8b, + 0xcd, 0xdf, 0xe0, 0x15, 0xe4, 0x85, 0xcc, 0xc5, 0xca, 0x44, 0x36, 0x45, + 0x99, 0x4b, 0xd0, 0xf5, 0xd0, 0xa6, 0x9b, 0xeb, 0x79, 0x33, 0x08, 0x47, + 0x64, 0x7e, 0x7a, 0x9c, 0xf1, 0x66, 0x5f, 0x6a, 0x07, 0x78, 0xf8, 0x22, + 0x60, 0x7c, 0x32, 0x72, 0xe7, 0x1c, 0xfe, 0x9e, 0xd4, 0x90, 0x36, 0x63, + 0x06, 0x67, 0x81, 0xc2, 0x82, 0xbd, 0x22, 0x75, 0x23, 0x90, 0x0a, 0x13, + 0x20, 0xf9, 0x2e, 0x16, 0xa8, 0x8a, 0xc5, 0xab, 0x2b, 0x87, 0x64, 0x0d, + 0x91, 0xa4, 0x44, 0xc2, 0xb3, 0xe6, 0x1e, 0x27, 0x44, 0x46, 0xa3, 0x09, + 0x9f, 0xaa, 0x55, 0x79, 0x4e, 0x27, 0xaf, 0x3f, 0xee, 0xd0, 0xc3, 0xef, + 0xc0, 0x69, 0x4c, 0xb9, 0xd7, 0xbc, 0x45, 0x1f, 0x39, 0xd7, 0xa6, 0x0b, + 0x97, 0x0c, 0x87, 0x8e, 0xce, 0x68, 0x50, 0x2c, 0x45, 0x76, 0x10, 0x38, + 0x4b, 0x39, 0x3d, 0x7b, 0x32, 0x01, 0x69, 0xda, 0xf6, 0x6f, 0x60, 0x8f, + 0xfa, 0x65, 0x07, 0x22, 0xd8, 0xd0, 0x36, 0x4c, 0xb0, 0x6b, 0x09, 0x4b, + 0xb7, 0xe1, 0x9f, 0x51, 0x03, 0x35, 0x14, 0x70, 0x7a, 0xa8, 0x83, 0x33, + 0x10, 0x95, 0x89, 0x15, 0x54, 0x9f, 0x72, 0x46, 0x4f, 0xca, 0xef, 0xa4, + 0xb7, 0x1a, 0x07, 0xe5, 0x73, 0x2e, 0x33, 0x3a, 0xe1, 0x40, 0xfc, 0x69, + 0x72, 0xc2, 0xff, 0xb6, 0xd5, 0x38, 0x11, 0x55, 0xa9, 0x44, 0x8c, 0xe4, + 0xe0, 0x99, 0x35, 0xc5, 0x83, 0xbf, 0xef, 0xf5, 0x42, 0x17, 0x2f, 0x1f, + 0x27, 0xcd, 0x7d, 0x8e, 0xac, 0xdc, 0x0d, 0x50, 0x89, 0x32, 0x95, 0x25, + 0x1d, 0x06, 0xab, 0xb4, 0x78, 0x5b, 0xed, 0x0e, 0x2b, 0xd0, 0x62, 0xe5, + 0x61, 0x39, 0x4c, 0x95, 0x84, 0x94, 0xad, 0x73, 0xa8, 0x5b, 0xed, 0xa5, + 0x97, 0xf0, 0xdc, 0xb0, 0xb8, 0x7c, 0x8b, 0x36, 0x8d, 0x32, 0x81, 0xd7, + 0xf4, 0x13, 0x03, 0xd3, 0xa0, 0xff, 0x6c, 0xad, 0x53, 0x6d, 0x64, 0x9c, + 0x5f, 0xb0, 0xb9, 0x72, 0x73, 0xba, 0x0e, 0x81, 0xd8, 0x89, 0x94, 0x90, + 0xac, 0x45, 0xc7, 0xb4, 0x78, 0x53, 0xf9, 0x59, 0xc7, 0x13, 0x42, 0xd4, + 0xc6, 0xd8, 0x06, 0x6a, 0x2c, 0x8b, 0xa1, 0xc0, 0x7b, 0x33, 0x6f, 0x5b, + 0x28, 0x56, 0x3a, 0xf0, 0xa6, 0xf8, 0x77, 0x6a, 0xfc, 0xe1, 0xa9, 0x3b, + 0x2d, 0x2d, 0x43, 0x1d, 0xdb, 0x9d, 0xd4, 0xb4, 0xe4, 0x0e, 0xf9, 0xd8, + 0x4a, 0xca, 0x03, 0x79, 0x64, 0x6a, 0x44, 0x7d, 0x63, 0x46, 0x38, 0x9b, + 0x5d, 0xdd, 0xb0, 0x77, 0xd6, 0xad, 0xa3, 0xf0, 0xc0, 0x07, 0x1e, 0x0f, + 0x8e, 0xca, 0xd6, 0x81, 0x95, 0x53, 0x09, 0x5c, 0x7c, 0xac, 0xaf, 0x61, + 0x21, 0x6c, 0xf2, 0xad, 0x1b, 0x87, 0xd1, 0x4e, 0x3a, 0x55, 0x77, 0xed, + 0x3c, 0x2f, 0xa9, 0xe1, 0x0c, 0xdf, 0x48, 0xfe, 0x72, 0x91, 0xf2, 0x69, + 0x4e, 0xeb, 0x92, 0x7f, 0x94, 0xe6, 0x33, 0x16, 0x2f, 0x97, 0xcf, 0x08, + 0x95, 0x24, 0x08, 0xd0, 0x1b, 0x38, 0x9a, 0x8c, 0xff, 0x6e, 0x58, 0x16, + 0xca, 0x88, 0xfc, 0x01, 0x6a, 0xf1, 0xd9, 0xf3, 0x55, 0xd5, 0x60, 0x23, + 0x03, 0xa5, 0x66, 0xb8, 0x41, 0xa5, 0x02, 0xab, 0xa4, 0xcc, 0xfe, 0x64, + 0x48, 0x49, 0x4d, 0xec, 0x13, 0x41, 0x35, 0x51, 0x04, 0x15, 0x59, 0xd2, + 0xf9, 0x91, 0x62, 0x1f, 0x86, 0x9d, 0x35, 0x03, 0x1e, 0x45, 0xfd, 0xb4, + 0xa4, 0x46, 0x98, 0x30, 0xc4, 0xa0, 0xa6, 0x06, 0x56, 0x83, 0x98, 0xbc, + 0x08, 0xbe, 0x3e, 0x40, 0xf2, 0x64, 0x41, 0xa2, 0xdc, 0xec, 0x90, 0x20, + 0xfa, 0x45, 0x44, 0x72, 0x21, 0xc9, 0xa4, 0x3b, 0xb2, 0x97, 0xab, 0x42, + 0xb2, 0x96, 0xbe, 0x75, 0x50, 0x1d, 0x75, 0x94, 0xec, 0xf8, 0xcc, 0x02, + 0x99, 0x2d, 0xcb, 0x10, 0x4a, 0x6e, 0x6c, 0x4c, 0x3d, 0x36, 0xc5, 0x55, + 0xca, 0x82, 0x35, 0x21, 0x94, 0x92, 0x7d, 0x7d, 0xcd, 0xa7, 0x63, 0xc2, + 0x35, 0x23, 0x54, 0xb9, 0x79, 0xe3, 0xf3, 0xd7, 0xaf, 0x9f, 0x3f, 0x21, + 0x4e, 0xb7, 0xc2, 0x89, 0x61, 0x62, 0xd8, 0xe6, 0xd7, 0x37, 0x6e, 0xcb, + 0xc7, 0x87, 0x1c, 0xd7, 0xbd, 0xa5, 0x50, 0x83, 0xaa, 0xa3, 0x2d, 0xf5, + 0x93, 0x6c, 0xa0, 0x91, 0xd5, 0x70, 0x56, 0xdf, 0x6c, 0x13, 0x07, 0xfa, + 0xa6, 0x38, 0x08, 0x00, 0x4c, 0x5b, 0xf5, 0x88, 0xa5, 0x46, 0x37, 0x9e, + 0xdf, 0x8e, 0x67, 0xac, 0x29, 0x4d, 0x94, 0xcb, 0xe9, 0x15, 0xd1, 0x03, + 0x45, 0x45, 0xd6, 0x5e, 0x3c, 0x0b, 0x4d, 0xe8, 0xc1, 0x31, 0xe8, 0x15, + 0xee, 0x16, 0x29, 0x0c, 0x82, 0x62, 0x3f, 0x13, 0x52, 0xa9, 0x6d, 0xd8, + 0x1b, 0x06, 0xbb, 0x3c, 0xd7, 0x8d, 0x57, 0x08, 0xbe, 0x76, 0xe0, 0x42, + 0xa1, 0xd5, 0x61, 0x6c, 0xd0, 0x38, 0x52, 0x1f, 0xa4, 0xfb, 0xf5, 0xac, + 0x0a, 0xcd, 0x1f, 0xa3, 0xb6, 0x3f, 0xe4, 0xdf, 0x73, 0x42, 0x43, 0x8f, + 0x76, 0x11, 0x29, 0xb8, 0xc1, 0x83, 0x11, 0x90, 0x5c, 0x81, 0x30, 0xd7, + 0xc2, 0x59, 0xb7, 0x73, 0x4a, 0x2a, 0x81, 0x67, 0x8e, 0xf4, 0x22, 0x8f, + 0x85, 0x18, 0x96, 0x58, 0xd6, 0x83, 0x5d, 0xf2, 0x8a, 0x9a, 0x23, 0xd8, + 0xa0, 0x00, 0xc0, 0xc7, 0xc0, 0x52, 0xc8, 0x6e, 0xbe, 0x06, 0xff, 0x63, + 0x39, 0xff, 0x1f, 0xa5, 0x03, 0x64, 0xaa, 0xfd, 0x71, 0x96, 0x11, 0x1a, + 0x87, 0xd3, 0x4c, 0xd2, 0x2d, 0xd4, 0x7c, 0xa6, 0x98, 0x91, 0x5b, 0x8c, + 0x40, 0x98, 0x26, 0xf2, 0x5c, 0x48, 0x73, 0x91, 0xad, 0x19, 0xc2, 0x42, + 0xe8, 0x29, 0x9b, 0x97, 0xe6, 0x00, 0xf0, 0xb8, 0x1d, 0x1c, 0x33, 0x7d, + 0x60, 0x05, 0x8b, 0xeb, 0xac, 0x2e, 0x92, 0xc4, 0x97, 0x0a, 0x94, 0x12, + 0xd9, 0x63, 0xf4, 0xff, 0x1c, 0xc3, 0x7b, 0x5b, 0xca, 0x54, 0x8e, 0x7b, + 0x65, 0x44, 0xa3, 0x49, 0x85, 0x7d, 0x17, 0x68, 0xf7, 0x22, 0x76, 0x65, + 0xf1, 0xf9, 0x8e, 0x8c, 0x05, 0xb4, 0xfe, 0x01, 0x6a, 0x97, 0x86, 0xcd, + 0x80, 0xea, 0x69, 0x26, 0x41, 0x31, 0x12, 0x2e, 0x6c, 0x3d, 0xa7, 0x11, + 0xc6, 0xe5, 0xda, 0x13, 0x57, 0x2d, 0xe7, 0x51, 0xf4, 0xf9, 0x02, 0x20, + 0x6a, 0x54, 0x2a, 0xa2, 0x01, 0x03, 0x15, 0x3f, 0xb3, 0xdb, 0xe5, 0x8d, + 0x1c, 0xac, 0x67, 0xfb, 0x43, 0xfd, 0x11, 0xb9, 0xdd, 0xde, 0x40, 0x43, + 0x9e, 0x6c, 0x0a, 0x8e, 0x10, 0xcb, 0xff, 0xd2, 0xf9, 0x32, 0x61, 0x6f, + 0xcc, 0x87, 0xcc, 0x65, 0xce, 0xfa, 0xce, 0xb0, 0xc0, 0xc4, 0xc6, 0xdf, + 0xaa, 0x18, 0x5d, 0x26, 0xc7, 0x18, 0xbc, 0xbd, 0x2f, 0x92, 0xff, 0xfb, + 0xe8, 0x88, 0xc3, 0x89, 0x67, 0xec, 0x07, 0x8f, 0x02, 0x31, 0x7c, 0xa2, + 0xbc, 0xe2, 0xf8, 0x9d, 0xad, 0xc5, 0xd7, 0x9b, 0x1a, 0x37, 0xf8, 0x0f, + 0xbb, 0x8b, 0xbe, 0x2e, 0x8e, 0x7e, 0x03, 0x06, 0x95, 0x03, 0x83, 0x7a, + 0xb9, 0x63, 0x6d, 0x65, 0x61, 0xfb, 0x0b, 0xcb, 0xb4, 0x47, 0x65, 0x5e, + 0xdb, 0xff, 0xc3, 0xf4, 0x6a, 0x52, 0xc0, 0xc2, 0xb2, 0x7f, 0x66, 0x79, + 0x2a, 0xbe, 0xd1, 0xe9, 0x80, 0x82, 0xfe, 0x6f, 0x00, 0x7f, 0x2f, 0xf4, + 0x78, 0x52, 0xb6, 0x7c, 0xf7, 0x84, 0x0b, 0x96, 0x53, 0x78, 0xf8, 0x50, + 0x1a, 0x89, 0x1b, 0x71, 0x89, 0xd5, 0xbf, 0xd1, 0x81, 0x14, 0x6f, 0x75, + 0x1c, 0xf9, 0x19, 0x8e, 0xb7, 0x45, 0x97, 0xbc, 0x45, 0xa1, 0x3d, 0xa2, + 0xe6, 0x50, 0xc1, 0x19, 0x09, 0x44, 0x31, 0xce, 0x10, 0xfa, 0xd9, 0x7a, + 0x17, 0xf4, 0x3a, 0xe7, 0x9f, 0x9e, 0xc6, 0xbd, 0xe4, 0x23, 0x90, 0x8a, + 0xb6, 0x72, 0xa0, 0xe3, 0x7d, 0xca, 0xc9, 0xaf, 0x04, 0xde, 0x76, 0x40, + 0xe5, 0xb4, 0xda, 0xb4, 0xf9, 0x98, 0xc6, 0x14, 0xe0, 0x61, 0xa4, 0xbe, + 0xa8, 0x1d, 0x11, 0xb8, 0x6e, 0x7b, 0x71, 0xec, 0xf4, 0xe8, 0x1a, 0x75, + 0x24, 0x67, 0x0f, 0xb5, 0x24, 0xb6, 0x04, 0x96, 0xa4, 0xa4, 0x0c, 0x7e, + 0xce, 0xba, 0x8e, 0xbc, 0xde, 0xf1, 0xfd, 0x39, 0x2a, 0x99, 0x35, 0x2e, + 0x04, 0x96, 0xf6, 0x7d, 0x10, 0x52, 0xeb, 0xaa, 0x85, 0xdf, 0xdb, 0x0d, + 0x5b, 0x0f, 0xe0, 0xe4, 0x2b, 0x3b, 0xc7, 0xb5, 0x76, 0x1c, 0x64, 0xc8, + 0x33, 0x3b, 0xc3, 0x6e, 0xe7, 0xd3, 0x76, 0x9b, 0xe4, 0x4b, 0x29, 0x3a, + 0x04, 0xfa, 0x15, 0x9b, 0x33, 0x65, 0xfd, 0xb2, 0x80, 0x4e, 0x74, 0x38, + 0x9e, 0x64, 0x82, 0x23, 0x1a, 0xb5, 0x3e, 0x5a, 0xaa, 0xd8, 0x22, 0x83, + 0x6b, 0x1f, 0x62, 0x73, 0x2d, 0x74, 0x7b, 0xda, 0x64, 0xbd, 0xac, 0xcd, + 0x34, 0xe8, 0x33, 0xad, 0x5b, 0x99, 0x1d, 0xd8, 0xe4, 0x6e, 0x68, 0x40, + 0x8c, 0x99, 0xdf, 0xa2, 0xd7, 0x0a, 0x66, 0xbb, 0xd8, 0x0d, 0xa3, 0x2d, + 0xe3, 0x2e, 0x6d, 0x45, 0x39, 0x77, 0xca, 0xf4, 0x7f, 0x89, 0x83, 0x2c, + 0x15, 0xe6, 0x40, 0xb9, 0x0d, 0x7b, 0x1b, 0x6e, 0xa3, 0xc7, 0xfe, 0x5b, + 0x90, 0x1b, 0x3c, 0x91, 0xa2, 0x53, 0x0f, 0x1b, 0x3a, 0xde, 0x72, 0x72, + 0xa3, 0x26, 0x27, 0xa5, 0xa7, 0x12, 0x6b, 0x0e, 0x2e, 0xe3, 0x36, 0xcc, + 0xeb, 0x00, 0x56, 0xe9, 0x44, 0x48, 0x4a, 0x89, 0x43, 0x65, 0x45, 0x60, + 0x74, 0x1e, 0x18, 0x08, 0x68, 0xe4, 0x79, 0xc1, 0x89, 0xad, 0xaa, 0xaa, + 0x27, 0xe6, 0x3f, 0xfe, 0x5e, 0x86, 0xa7, 0x5e, 0x9f, 0x58, 0xc8, 0xe6, + 0x85, 0xd8, 0x43, 0xb0, 0x3f, 0x06, 0xd0, 0x3e, 0x9d, 0x85, 0x27, 0xe1, + 0xe3, 0xb6, 0x81, 0x89, 0xbd, 0x38, 0xec, 0xfd, 0x96, 0xcd, 0x9d, 0x3a, + 0xe1, 0x33, 0x65, 0xb2, 0x86, 0xee, 0xa5, 0x50, 0xe6, 0x15, 0x85, 0x70, + 0xa3, 0xf9, 0xd3, 0xd3, 0x49, 0x85, 0xa7, 0x41, 0x22, 0xda, 0x07, 0x27, + 0x23, 0xd5, 0xf4, 0xc0, 0x07, 0x86, 0x65, 0x45, 0xa0, 0xf7, 0x35, 0x46, + 0x12, 0x25, 0xd6, 0x95, 0x5a, 0x72, 0x59, 0x0a, 0xb6, 0xae, 0x21, 0xaa, + 0xf0, 0x9b, 0x40, 0x9a, 0xbe, 0x78, 0x82, 0x19, 0xbb, 0x11, 0x1a, 0xb6, + 0x18, 0x86, 0x74, 0xa4, 0x20, 0xd4, 0x40, 0xe7, 0x72, 0x72, 0xe6, 0x27, + 0x6e, 0x31, 0x20, 0x64, 0x50, 0x14, 0xe8, 0x55, 0x16, 0xc6, 0x10, 0xa1, + 0x8f, 0x00, 0xdc, 0x3c, 0x28, 0x21, 0xdb, 0xe5, 0x9c, 0xba, 0xe1, 0x0a, + 0x13, 0x8c, 0x02, 0x0c, 0xea, 0xe6, 0x88, 0x0a, 0xf9, 0x40, 0xb3, 0xda, + 0x63, 0x22, 0x82, 0x8a, 0x67, 0xfd, 0x65, 0x9a, 0xd1, 0x7e, 0x1d, 0xa1, + 0x94, 0xc7, 0x9b, 0x1f, 0x0a, 0x2d, 0xda, 0x1f, 0x43, 0x74, 0xc2, 0x25, + 0x29, 0x18, 0xba, 0x1a, 0x6e, 0x2d, 0x04, 0x7c, 0x60, 0x7e, 0x05, 0xd3, + 0x3e, 0x9c, 0x27, 0x5b, 0x17, 0x90, 0x42, 0x61, 0xeb, 0xd9, 0xb8, 0x4e, + 0xbd, 0x38, 0xd0, 0x23, 0x2f, 0x4e, 0xaf, 0x6b, 0x61, 0x37, 0x05, 0x7f, + 0x1d, 0xc8, 0x32, 0xc5, 0x01, 0x09, 0xf9, 0x9c, 0x73, 0xd6, 0xf1, 0x4a, + 0xf9, 0x6b, 0xc8, 0xfb, 0x6a, 0x4a, 0xb3, 0xab, 0x26, 0x98, 0xd4, 0xe2, + 0x9b, 0x1c, 0xae, 0xdb, 0x86, 0x54, 0xa5, 0xc6, 0xba, 0x01, 0xa6, 0x14, + 0x6b, 0x25, 0x81, 0x59, 0xc3, 0xee, 0x86, 0xb6, 0xa7, 0xa7, 0xb0, 0xe3, + 0xba, 0x3d, 0x92, 0x2a, 0x25, 0x4e, 0x17, 0x72, 0xa8, 0x01, 0x44, 0xc8, + 0x44, 0xc2, 0x7e, 0x45, 0xa2, 0xd3, 0xfb, 0x53, 0x59, 0xb5, 0xaa, 0x45, + 0xfc, 0xc0, 0x51, 0x9b, 0xe2, 0x33, 0x83, 0x71, 0x6e, 0x88, 0xbb, 0xab, + 0x38, 0xac, 0xd6, 0xd8, 0xf3, 0xa4, 0xbd, 0x17, 0xcd, 0x15, 0x2e, 0xdb, + 0x39, 0xb3, 0x9d, 0x3f, 0x85, 0xfd, 0xea, 0x1c, 0xab, 0xd0, 0x39, 0x45, + 0x3d, 0x0e, 0x15, 0x8f, 0xef, 0xd5, 0x15, 0x2b, 0xaf, 0x29, 0xb4, 0x9d, + 0x0d, 0xcd, 0x24, 0x88, 0x65, 0xd6, 0xcf, 0xd9, 0x51, 0x5a, 0x70, 0x87, + 0xc8, 0x03, 0x63, 0xb5, 0xca, 0xc4, 0x86, 0x6d, 0x08, 0x12, 0xda, 0xcc, + 0x29, 0x74, 0xe2, 0xec, 0x9e, 0xcd, 0x84, 0x33, 0x12, 0xd7, 0x1d, 0x08, + 0x54, 0xa4, 0x16, 0xb5, 0x32, 0x88, 0x14, 0x0e, 0x3a, 0x3c, 0x37, 0xa2, + 0x68, 0x83, 0x4a, 0xe6, 0xf2, 0xbe, 0x54, 0xa9, 0x9a, 0x5c, 0xe1, 0x93, + 0x33, 0xc3, 0x52, 0xae, 0x08, 0xf8, 0x87, 0xae, 0x23, 0x83, 0xe1, 0xb2, + 0x8d, 0xf2, 0x8a, 0x7a, 0x53, 0x35, 0x2c, 0xc1, 0x80, 0xad, 0x31, 0x7e, + 0x7c, 0xbf, 0xf8, 0x24, 0x31, 0x39, 0x36, 0x3f, 0x14, 0xce, 0x56, 0xc7, + 0x25, 0x97, 0xa5, 0x4a, 0x2e, 0x12, 0xdb, 0x3c, 0xd3, 0x7f, 0x44, 0x91, + 0x73, 0x1c, 0xe9, 0xe8, 0xa8, 0xb2, 0x46, 0xb5, 0x3a, 0x62, 0xaa, 0xa1, + 0x45, 0x1c, 0x10, 0xbc, 0xec, 0xe6, 0x11, 0x68, 0x46, 0x64, 0x96, 0x77, + 0x34, 0x00, 0x8e, 0x3a, 0x5c, 0xef, 0xba, 0x26, 0xff, 0x8e, 0x7a, 0xa0, + 0x0d, 0xe5, 0xee, 0x08, 0x12, 0x56, 0x68, 0xcd, 0xe4, 0x58, 0x71, 0x28, + 0x74, 0x91, 0xe6, 0x43, 0xb8, 0x3a, 0x44, 0x1b, 0xda, 0x66, 0x40, 0x45, + 0xb1, 0xf6, 0x75, 0xbf, 0xa1, 0x6f, 0xfa, 0x13, 0xd5, 0x8a, 0x8f, 0x88, + 0xfa, 0x46, 0x27, 0x02, 0xc6, 0x50, 0xf2, 0x51, 0xf9, 0x6a, 0x75, 0x76, + 0x21, 0x55, 0x55, 0xc7, 0x04, 0xe9, 0x68, 0x41, 0xc7, 0xb8, 0x6b, 0x32, + 0xa0, 0xba, 0x83, 0x2a, 0x1a, 0xf1, 0x31, 0xc7, 0x7f, 0xa5, 0xb5, 0xe6, + 0x3a, 0x08, 0xdc, 0x4d, 0x88, 0x72, 0x1f, 0x11, 0x9b, 0x78, 0xa3, 0x16, + 0x5e, 0xe1, 0xf3, 0xbc, 0x32, 0x5d, 0xfb, 0xb4, 0x63, 0x03, 0xa5, 0xc6, + 0xd7, 0x88, 0xd2, 0x42, 0xa8, 0xd2, 0x9d, 0x95, 0x76, 0xb2, 0x49, 0x67, + 0x01, 0x7f, 0xd7, 0x2c, 0x49, 0x13, 0xdc, 0x98, 0x36, 0x0e, 0xa4, 0xe1, + 0x58, 0x23, 0x7a, 0xd8, 0xc1, 0x43, 0xb9, 0x8f, 0xef, 0x18, 0x5e, 0x05, + 0x86, 0x55, 0x9b, 0x98, 0xbb, 0x30, 0x5c, 0x4c, 0x41, 0xf0, 0xe5, 0x15, + 0x41, 0xf0, 0x14, 0x4d, 0xa3, 0x40, 0xed, 0xc9, 0x18, 0x14, 0x1c, 0xc9, + 0x33, 0xdc, 0x59, 0x1d, 0x4c, 0x65, 0x99, 0xc3, 0x36, 0x9d, 0x8e, 0x58, + 0x1d, 0x0d, 0xc8, 0x1f, 0x89, 0xf0, 0x95, 0xaf, 0x6d, 0x5f, 0xfb, 0x2b, + 0x39, 0x04, 0x9f, 0x20, 0x32, 0xd5, 0x2f, 0x28, 0x4b, 0xdb, 0x49, 0x80, + 0xff, 0x78, 0x11, 0x72, 0xbe, 0x9c, 0x84, 0xcb, 0x12, 0x12, 0x7d, 0x95, + 0x68, 0x1d, 0xad, 0xa6, 0xfa, 0x24, 0xd5, 0x84, 0xe9, 0x9b, 0x97, 0xf1, + 0xfe, 0x0a, 0x82, 0xde, 0xf7, 0x6a, 0xf4, 0x86, 0x2c, 0x73, 0x41, 0x4d, + 0x63, 0xdc, 0xd2, 0x6e, 0xc3, 0x0b, 0x09, 0x63, 0x18, 0x8f, 0x62, 0x63, + 0x41, 0xb0, 0x6d, 0x20, 0xa9, 0x00, 0xb1, 0x78, 0x60, 0xf4, 0xbb, 0xa0, + 0x16, 0x8d, 0x3d, 0xb0, 0xbe, 0x44, 0x2f, 0xc5, 0xa6, 0x08, 0x93, 0xcb, + 0x86, 0xa6, 0x19, 0xff, 0x51, 0x7e, 0xf5, 0xea, 0x5f, 0x36, 0xa6, 0x7c, + 0x3f, 0x33, 0x3e, 0x08, 0xb0, 0x22, 0x10, 0xea, 0xfd, 0x09, 0x94, 0xc3, + 0xb7, 0xd7, 0xb7, 0x0e, 0x16, 0xe0, 0xb0, 0x7c, 0xe0, 0x7a, 0x87, 0x4c, + 0x27, 0x70, 0x2a, 0x56, 0x46, 0xb7, 0x76, 0x90, 0xb3, 0x1e, 0x74, 0x71, + 0x9b, 0x95, 0x7b, 0x7b, 0x9e, 0x9c, 0x8b, 0xcc, 0x8b, 0x14, 0x68, 0x46, + 0x3b, 0x83, 0x30, 0xb6, 0xc4, 0x49, 0xb2, 0x25, 0xd3, 0xae, 0x29, 0x7f, + 0x6d, 0x93, 0x1d, 0x8a, 0x68, 0x63, 0x52, 0x9f, 0x40, 0x16, 0xec, 0xeb, + 0x92, 0x85, 0xc9, 0xd4, 0x3c, 0xbe, 0x58, 0x15, 0x3f, 0x6b, 0xc5, 0xba, + 0xc0, 0x4e, 0xb6, 0x51, 0xa3, 0x52, 0xb6, 0x2a, 0x8f, 0x2a, 0xa6, 0x53, + 0xfe, 0x04, 0x0d, 0x81, 0x9c, 0x88, 0xe8, 0xf1, 0xab, 0xab, 0x0d, 0xc5, + 0x9a, 0x58, 0xdd, 0xbc, 0xc3, 0xae, 0x42, 0x90, 0x84, 0xc3, 0x30, 0x28, + 0x65, 0x52, 0xa9, 0xf4, 0xcc, 0xd7, 0xb9, 0x7d, 0xed, 0x87, 0x76, 0x4c, + 0xf9, 0x66, 0x6f, 0x7e, 0xac, 0x48, 0x7d, 0x70, 0x3b, 0x5c, 0x40, 0x65, + 0xa0, 0x85, 0xf5, 0x1f, 0x2e, 0x7b, 0x1a, 0xd1, 0xd9, 0x22, 0x64, 0xf3, + 0x68, 0xa0, 0x2f, 0xbb, 0x5e, 0x3c, 0xe4, 0xdd, 0x7a, 0xd4, 0x99, 0x98, + 0xc9, 0x41, 0x13, 0x6f, 0x40, 0x47, 0x05, 0x9f, 0x89, 0x01, 0x80, 0xb5, + 0xeb, 0x79, 0x85, 0xa6, 0xb6, 0xf6, 0xc2, 0xbc, 0x83, 0xe8, 0x47, 0x19, + 0x9e, 0x46, 0x97, 0x4c, 0x62, 0x04, 0x44, 0x59, 0xb5, 0xef, 0x19, 0x05, + 0xeb, 0xea, 0x48, 0x69, 0xaf, 0x31, 0x69, 0x25, 0xa2, 0x8d, 0x82, 0x47, + 0xd2, 0x09, 0x1c, 0x71, 0x32, 0x9d, 0xc7, 0xf2, 0x2b, 0x29, 0xb4, 0x7c, + 0x13, 0x05, 0x1c, 0x49, 0x6d, 0xb8, 0x2c, 0xff, 0x37, 0x18, 0x45, 0x2e, + 0x83, 0xbc, 0x66, 0x85, 0xc8, 0x03, 0xe1, 0x60, 0x6a, 0x18, 0x87, 0xa7, + 0x61, 0xc5, 0xac, 0x7d, 0xfe, 0x9f, 0xec, 0x78, 0x04, 0x44, 0xdb, 0xef, + 0x77, 0xeb, 0x3a, 0x6f, 0xe9, 0x11, 0x0b, 0x69, 0x94, 0x3c, 0x9c, 0x6c, + 0x81, 0x9d, 0x4f, 0xc1, 0x44, 0xf4, 0xdd, 0xef, 0x68, 0x43, 0x16, 0x1e, + 0x27, 0xf3, 0x87, 0x62, 0x30, 0x17, 0x5e, 0x4d, 0x31, 0x0d, 0xd4, 0x14, + 0x13, 0x19, 0x61, 0x01, 0xea, 0x2b, 0x81, 0x2a, 0xeb, 0xd6, 0x55, 0x8b, + 0xa7, 0x94, 0x7b, 0x78, 0x37, 0x68, 0xe3, 0x6e, 0x76, 0x8d, 0x89, 0xb1, + 0xa0, 0x10, 0xc2, 0x3e, 0xb9, 0x0a, 0xe7, 0xfb, 0x2b, 0xf5, 0xe3, 0xa9, + 0x84, 0x5a, 0x69, 0x02, 0x33, 0x38, 0x07, 0x23, 0x3c, 0x37, 0xc2, 0x83, + 0xde, 0x26, 0x67, 0x10, 0x16, 0x14, 0xa4, 0x39, 0xbf, 0x76, 0x52, 0x99, + 0x0d, 0x80, 0x11, 0x22, 0xb3, 0xaa, 0x08, 0xfd, 0x04, 0x90, 0x94, 0x85, + 0x58, 0x85, 0xd8, 0xdd, 0xd9, 0x23, 0x56, 0x1b, 0x69, 0xb7, 0xfc, 0xf4, + 0x50, 0xdf, 0xab, 0xd3, 0xad, 0x10, 0xd6, 0xbd, 0x3d, 0x1b, 0x18, 0xab, + 0x4d, 0x06, 0x81, 0x6e, 0x0d, 0xce, 0x85, 0x7c, 0x85, 0x54, 0x08, 0xf4, + 0xfd, 0x1e, 0xb6, 0x6e, 0x96, 0x4d, 0x86, 0x6e, 0xf2, 0x28, 0xd7, 0x10, + 0xe6, 0x59, 0xb2, 0xb8, 0x90, 0xf0, 0xbc, 0x49, 0x84, 0xe0, 0xe5, 0x60, + 0x62, 0x61, 0xaf, 0x44, 0x6d, 0x4f, 0xdd, 0xd2, 0xaa, 0xc8, 0x2f, 0x4a, + 0x59, 0xdc, 0xbd, 0xb5, 0xe4, 0xba, 0x82, 0x6e, 0x1d, 0x95, 0x05, 0xfe, + 0xaa, 0xa6, 0x67, 0x38, 0xe6, 0x5c, 0xe6, 0x50, 0x49, 0x3e, 0xc0, 0xa6, + 0x40, 0xd4, 0xca, 0x29, 0x55, 0x0c, 0xbf, 0x76, 0xcb, 0x8e, 0x90, 0x6b, + 0x37, 0x32, 0xe0, 0x43, 0x7e, 0x19, 0x7f, 0x93, 0x5f, 0x46, 0x4f, 0x22, + 0x1a, 0xac, 0x56, 0xf1, 0x96, 0x63, 0x4b, 0x75, 0xbc, 0x5c, 0x9f, 0x32, + 0xd9, 0x6a, 0xdc, 0xfe, 0x88, 0x82, 0x5f, 0x0b, 0xc4, 0xb3, 0xb3, 0x8d, + 0x6e, 0x09, 0x94, 0x84, 0xf8, 0x91, 0xfb, 0x39, 0x90, 0xe5, 0x78, 0xd6, + 0xa8, 0xbf, 0x0a, 0x54, 0x5c, 0xdd, 0x1c, 0xa5, 0xa2, 0xf8, 0x91, 0x13, + 0x6d, 0xac, 0xa9, 0xfd, 0xdb, 0xf3, 0x0d, 0x64, 0xe4, 0x25, 0xc9, 0x8f, + 0x71, 0xfe, 0x88, 0x0a, 0xde, 0x7e, 0x9d, 0xc8, 0x0d, 0x75, 0xc9, 0x1b, + 0x8f, 0xd0, 0x70, 0xf8, 0xf8, 0xdb, 0x27, 0x36, 0x2a, 0x4e, 0xcf, 0x02, + 0xd1, 0xb1, 0xd0, 0x56, 0x57, 0x87, 0x90, 0x47, 0xb0, 0x42, 0x82, 0x3d, + 0xa5, 0x2b, 0xf0, 0x25, 0x14, 0x15, 0x4d, 0xd3, 0x72, 0xa4, 0x1e, 0xda, + 0x59, 0x8a, 0xed, 0xf1, 0x03, 0x87, 0x7b, 0x9e, 0x66, 0x77, 0xe9, 0xec, + 0x42, 0x0f, 0xd4, 0x3c, 0xf8, 0x0c, 0xd4, 0x93, 0x00, 0xcd, 0x57, 0xe4, + 0xce, 0xc7, 0x08, 0xe0, 0x70, 0x9c, 0x70, 0x6b, 0x2d, 0x0c, 0xed, 0x89, + 0x41, 0x7f, 0x02, 0xe3, 0x80, 0xf2, 0x9e, 0xa8, 0x07, 0xd6, 0x7f, 0xd5, + 0x3b, 0x2e, 0x19, 0xd3, 0xad, 0x16, 0x1b, 0xad, 0x0e, 0x39, 0x53, 0xe7, + 0x50, 0x53, 0x39, 0xe2, 0xc6, 0xea, 0x62, 0xd4, 0x22, 0xf0, 0xb8, 0x0d, + 0x98, 0xac, 0x15, 0xda, 0x2b, 0x00, 0x78, 0x99, 0x77, 0x65, 0xe0, 0x7e, + 0x2e, 0x63, 0xd7, 0x1c, 0x42, 0x16, 0xa6, 0x57, 0xfd, 0xe8, 0xe9, 0x0d, + 0xef, 0xa5, 0x08, 0x38, 0xf9, 0xb8, 0x86, 0x0a, 0x5d, 0xbb, 0xbf, 0xed, + 0x62, 0xcd, 0xce, 0xbc, 0x4d, 0x8f, 0xd8, 0x3d, 0x11, 0xb9, 0xf4, 0xe4, + 0xaf, 0x10, 0x78, 0xe1, 0x6c, 0x2a, 0x76, 0x73, 0x5f, 0x40, 0xad, 0xeb, + 0xb2, 0x0a, 0x52, 0x62, 0x81, 0x7f, 0x47, 0x09, 0x70, 0x2c, 0x8f, 0xc3, + 0x10, 0x5a, 0xe9, 0x00, 0x9c, 0xb9, 0x70, 0xe8, 0x19, 0xda, 0x25, 0xe0, + 0x80, 0xed, 0xb5, 0xf7, 0x53, 0x35, 0xea, 0xf7, 0x50, 0x67, 0xfa, 0xfa, + 0xd2, 0xe8, 0x0b, 0xd0, 0x9f, 0xa9, 0x08, 0x57, 0x5a, 0xd9, 0xfc, 0xfb, + 0x6c, 0x8c, 0x6e, 0xfd, 0x19, 0x8f, 0x9d, 0x74, 0x02, 0x82, 0xb4, 0x19, + 0xdf, 0x6e, 0x25, 0x8f, 0xe8, 0xaf, 0x3b, 0x62, 0x3f, 0x0b, 0x63, 0x11, + 0xf2, 0xcf, 0xeb, 0x95, 0xa3, 0x05, 0xd4, 0x58, 0xc9, 0xd3, 0x10, 0xa0, + 0x2c, 0x8c, 0x4b, 0x54, 0x91, 0x42, 0x16, 0x95, 0xbb, 0x0d, 0xd7, 0x53, + 0x94, 0xe6, 0x9f, 0x9c, 0xc2, 0x57, 0xeb, 0xc0, 0xef, 0xd2, 0x6e, 0x97, + 0x5d, 0xab, 0xf0, 0x9c, 0x5a, 0x3d, 0xb4, 0xec, 0x8a, 0x4f, 0x06, 0x49, + 0x67, 0x2d, 0xd3, 0x79, 0x5c, 0x28, 0xdb, 0xc1, 0x12, 0xa3, 0x36, 0xb3, + 0x28, 0xaa, 0x14, 0xbc, 0x12, 0xbd, 0xfc, 0xa5, 0xd7, 0x68, 0xd8, 0x76, + 0xa1, 0xb8, 0x24, 0x20, 0xd7, 0x7a, 0x3d, 0xb0, 0x67, 0x4e, 0x51, 0x48, + 0xb1, 0x80, 0x69, 0x7b, 0xec, 0x42, 0x32, 0x99, 0xef, 0xf8, 0x91, 0x9f, + 0xce, 0x99, 0xd2, 0xf7, 0xd1, 0x06, 0x84, 0x4f, 0xce, 0x88, 0xa0, 0x04, + 0x36, 0xcf, 0x2b, 0x15, 0xe7, 0x57, 0x80, 0x5a, 0xeb, 0xc4, 0xe6, 0x7c, + 0x43, 0x35, 0xbb, 0xae, 0xf9, 0xb2, 0x93, 0x7a, 0xc1, 0xbd, 0x2c, 0xae, + 0xe6, 0xa2, 0x46, 0x32, 0x12, 0x66, 0xcf, 0xcb, 0x19, 0x9c, 0x53, 0x81, + 0xa2, 0xa2, 0x98, 0xe8, 0x97, 0x44, 0xfe, 0x00, 0x3a, 0xcb, 0xf2, 0x9b, + 0x93, 0x9c, 0x9d, 0xb7, 0xaf, 0xce, 0x7a, 0x42, 0x6c, 0xae, 0xac, 0xd0, + 0xe0, 0x0a, 0x02, 0xb4, 0xa5, 0xa0, 0x89, 0x1f, 0x47, 0xc4, 0x9c, 0x12, + 0x84, 0x6a, 0xa3, 0x86, 0x2d, 0x0a, 0x22, 0x8a, 0xa7, 0x20, 0x3a, 0x89, + 0xb9, 0x3c, 0x05, 0xac, 0x0d, 0x87, 0xbe, 0x73, 0xbf, 0x14, 0xd6, 0xf0, + 0xf4, 0x88, 0x78, 0xde, 0xdb, 0x3c, 0x20, 0x1a, 0x87, 0xd5, 0x75, 0xe3, + 0xa2, 0x35, 0x71, 0x3b, 0x8b, 0xf9, 0x8e, 0x57, 0xcb, 0x0e, 0x9d, 0x75, + 0xc3, 0x49, 0x09, 0xa0, 0x4f, 0x97, 0x9c, 0x2c, 0x20, 0x1b, 0x4d, 0x28, + 0x32, 0x44, 0xdc, 0xeb, 0x5d, 0x5f, 0x9d, 0x18, 0x91, 0xfb, 0xc8, 0x82, + 0xe7, 0xc2, 0x31, 0x9c, 0xf4, 0x01, 0xe6, 0xb1, 0xe8, 0x36, 0xfc, 0x16, + 0xc3, 0x3e, 0x53, 0x1d, 0xba, 0x54, 0xa5, 0x6a, 0xfc, 0x2a, 0x07, 0xff, + 0xdd, 0xee, 0x5b, 0x2c, 0x35, 0x3d, 0x8f, 0x16, 0xd4, 0xc2, 0x94, 0x8c, + 0x92, 0x1c, 0x9a, 0x27, 0xc9, 0xab, 0x09, 0x88, 0x60, 0xbe, 0x1d, 0xa0, + 0x3f, 0x3c, 0xe4, 0xdc, 0xbc, 0x4c, 0xb8, 0x77, 0xf4, 0x59, 0x35, 0x04, + 0x3b, 0x4d, 0xe1, 0xb3, 0x80, 0x74, 0xf5, 0x65, 0xc2, 0xbe, 0x43, 0xe1, + 0x9a, 0x3a, 0x59, 0x2e, 0xb3, 0x9a, 0x56, 0x89, 0x69, 0x77, 0x04, 0xfe, + 0xd2, 0x0c, 0x21, 0x79, 0xa2, 0x60, 0x1e, 0x9b, 0xd7, 0xa1, 0x19, 0x31, + 0xc2, 0x9a, 0xc6, 0xf0, 0x1d, 0xb4, 0x80, 0x76, 0x56, 0xe4, 0xbb, 0x5a, + 0xce, 0xdb, 0x75, 0xe3, 0x65, 0xf4, 0x9b, 0xb8, 0x4a, 0x10, 0xe9, 0xa1, + 0x91, 0x79, 0x34, 0x3f, 0x52, 0x47, 0x63, 0x4c, 0x0d, 0xbd, 0x46, 0xf1, + 0xf2, 0xe1, 0x15, 0x65, 0xac, 0x63, 0x2d, 0x45, 0x30, 0x3b, 0xf7, 0xbd, + 0x94, 0x1c, 0x63, 0xff, 0x73, 0x9c, 0xd6, 0x4f, 0x39, 0x34, 0xc0, 0x35, + 0xa6, 0xd3, 0x5c, 0x2d, 0x1b, 0xd7, 0xe8, 0x9d, 0x52, 0x68, 0x7d, 0x6b, + 0x59, 0x24, 0x70, 0xe7, 0x8a, 0x37, 0x60, 0x8a, 0x69, 0x95, 0x18, 0x1b, + 0x00, 0x9c, 0x5a, 0x07, 0x2b, 0xb1, 0xbf, 0xc0, 0x21, 0x4b, 0x45, 0xab, + 0x3e, 0xe1, 0x73, 0xf7, 0x4a, 0xc1, 0x45, 0x8a, 0x6e, 0x28, 0x45, 0x4d, + 0x6a, 0xad, 0xfb, 0x82, 0xf8, 0x4a, 0xfd, 0xed, 0x9d, 0x21, 0xbd, 0x0c, + 0xe3, 0x5f, 0xd6, 0xd7, 0x21, 0x12, 0xeb, 0x88, 0xfe, 0x87, 0x2b, 0x80, + 0xbf, 0xbc, 0x62, 0x89, 0xbe, 0x90, 0xec, 0x4b, 0xb7, 0x2e, 0x95, 0xb0, + 0x61, 0x92, 0x5b, 0x63, 0x93, 0x7c, 0x22, 0x59, 0x3d, 0x6a, 0x78, 0x50, + 0x98, 0xd0, 0x65, 0x3a, 0x19, 0xc2, 0x04, 0xeb, 0xd8, 0x3f, 0xb4, 0xe7, + 0x26, 0x4e, 0x54, 0x4c, 0x00, 0x0c, 0x86, 0xd5, 0x6e, 0x95, 0x2f, 0x2a, + 0x13, 0x8c, 0x81, 0x60, 0xb4, 0x15, 0x81, 0x7f, 0xe0, 0x3f, 0xe7, 0x52, + 0xe0, 0x34, 0xc2, 0x47, 0x4b, 0x54, 0xd7, 0x33, 0x20, 0x7f, 0x87, 0x8a, + 0xce, 0xb2, 0x31, 0x02, 0x9c, 0xf4, 0xbb, 0x79, 0x81, 0x19, 0x0d, 0xb9, + 0xc9, 0xee, 0x6e, 0x49, 0x5d, 0x24, 0x2d, 0xb4, 0x5a, 0xca, 0x96, 0xb2, + 0x7d, 0xd0, 0x23, 0xb0, 0x85, 0x30, 0x96, 0x1e, 0x2a, 0x25, 0x97, 0x04, + 0x96, 0x9a, 0x76, 0x42, 0x63, 0x02, 0x19, 0x21, 0x0d, 0x3b, 0xc0, 0x23, + 0x56, 0x79, 0x46, 0x6f, 0x0e, 0x2a, 0xe5, 0xef, 0x6b, 0xda, 0x61, 0x27, + 0xfb, 0x02, 0xf6, 0x4f, 0xec, 0x41, 0xef, 0x7c, 0xe0, 0xa2, 0xa4, 0x84, + 0x24, 0x9d, 0x5b, 0x83, 0x25, 0x6e, 0x0e, 0x00, 0xc1, 0x7d, 0x13, 0x34, + 0xf5, 0x37, 0x6c, 0x7b, 0x93, 0xb4, 0x38, 0x20, 0xae, 0x32, 0xd1, 0xf6, + 0x6b, 0xec, 0x51, 0x32, 0xc8, 0x14, 0x26, 0x03, 0x31, 0x68, 0xd8, 0x0e, + 0xf9, 0xcc, 0x0e, 0xea, 0x63, 0xe5, 0xf5, 0x62, 0x4e, 0x3b, 0x41, 0x03, + 0xab, 0x1e, 0x15, 0x02, 0x44, 0xef, 0xc7, 0x26, 0xd7, 0x1f, 0x18, 0x03, + 0x21, 0xb7, 0x42, 0xfe, 0x49, 0x5d, 0x53, 0x7a, 0x45, 0x9d, 0xfe, 0x85, + 0x60, 0xc0, 0xd9, 0x42, 0x59, 0xd9, 0xa9, 0xfb, 0xe6, 0x84, 0x1d, 0x33, + 0xe5, 0x41, 0xcd, 0x08, 0xf9, 0x35, 0xdd, 0xd5, 0xaa, 0x5a, 0x6d, 0xa7, + 0xe3, 0x09, 0x3e, 0x7f, 0x54, 0xc7, 0x6c, 0x26, 0x45, 0xa3, 0x23, 0x05, + 0x37, 0x04, 0x50, 0x3c, 0x23, 0xc9, 0xaa, 0x50, 0xde, 0x0e, 0xb0, 0xbc, + 0xce, 0xc9, 0x87, 0xf4, 0xaf, 0x0d, 0x93, 0x9e, 0x28, 0x5e, 0x6d, 0x11, + 0x74, 0x87, 0x7b, 0x06, 0xe7, 0xc0, 0xdf, 0x38, 0xd9, 0x8e, 0xd1, 0x15, + 0xdf, 0xd6, 0x7f, 0x2d, 0x11, 0x21, 0x60, 0xcd, 0xa8, 0x9c, 0xaf, 0x60, + 0x34, 0xd1, 0x51, 0x9a, 0x2f, 0x8a, 0xa9, 0x24, 0xa1, 0x74, 0x9b, 0xf7, + 0x88, 0xce, 0x4c, 0xe3, 0x64, 0x0a, 0xe5, 0xf3, 0xc3, 0x99, 0xfa, 0x79, + 0x88, 0xdd, 0x60, 0x47, 0x8c, 0x5e, 0x6d, 0x9f, 0x20, 0x13, 0x2a, 0x29, + 0x25, 0xfb, 0x32, 0xdb, 0x1c, 0xeb, 0x1b, 0xbf, 0x46, 0xbc, 0x29, 0xb4, + 0x41, 0x83, 0xf6, 0x53, 0x4c, 0x01, 0xa4, 0x66, 0xbe, 0x31, 0x93, 0xdc, + 0x06, 0xb6, 0xcf, 0x45, 0x9f, 0xb4, 0xf1, 0x7a, 0x40, 0x30, 0xb1, 0x44, + 0x8c, 0x1f, 0xe2, 0x52, 0x07, 0x57, 0xf4, 0x30, 0x08, 0xbf, 0xec, 0x52, + 0x54, 0x16, 0x77, 0xd4, 0x15, 0xce, 0x66, 0xea, 0x6b, 0x54, 0xec, 0xd3, + 0x7d, 0xa7, 0xdc, 0xef, 0xee, 0x61, 0x72, 0x59, 0xc0, 0x82, 0x7f, 0xe2, + 0xb2, 0x8c, 0x12, 0x9b, 0x16, 0xe0, 0xe5, 0x21, 0x45, 0x0e, 0x23, 0xb1, + 0xc4, 0x8f, 0x4a, 0x53, 0x6d, 0x00, 0x6f, 0xea, 0x74, 0xda, 0xe1, 0x79, + 0xc7, 0x21, 0x98, 0x7e, 0x9b, 0xe3, 0xf6, 0x8f, 0xf5, 0x1d, 0x7f, 0x6d, + 0x7a, 0x49, 0x67, 0x13, 0x20, 0x8a, 0x61, 0x2a, 0xb3, 0xbd, 0xd2, 0xdb, + 0xb1, 0x9f, 0x25, 0x07, 0x4f, 0xac, 0x32, 0x6a, 0x70, 0xe0, 0x3f, 0xcd, + 0x9e, 0x8e, 0xcb, 0xa7, 0x59, 0xee, 0x54, 0xec, 0xc4, 0x00, 0x87, 0xb6, + 0x0d, 0x50, 0x2e, 0x45, 0xba, 0xbc, 0x29, 0x5f, 0x57, 0x79, 0x3d, 0xec, + 0x55, 0x25, 0xc0, 0x0b, 0x3f, 0xe9, 0x51, 0x27, 0x95, 0x17, 0x0e, 0xa1, + 0xdc, 0xb1, 0x79, 0x6a, 0xf4, 0x87, 0xd9, 0x7d, 0x31, 0x1f, 0xd6, 0x58, + 0x25, 0xb8, 0x4f, 0x2a, 0x3a, 0x0b, 0xc4, 0x49, 0xd9, 0xcd, 0x0f, 0xff, + 0xa8, 0x74, 0x60, 0x59, 0x1d, 0xea, 0x38, 0x6b, 0xd5, 0x41, 0x08, 0x75, + 0xa1, 0x14, 0xe5, 0x9c, 0xd5, 0x72, 0xff, 0x61, 0xea, 0x6c, 0xe5, 0x91, + 0x0a, 0x0e, 0x81, 0xb5, 0x38, 0x57, 0x21, 0x9f, 0x37, 0x09, 0x86, 0x1f, + 0xd0, 0x0d, 0xce, 0xa6, 0xab, 0xfd, 0xb2, 0xdf, 0x21, 0x7a, 0x06, 0x8f, + 0x76, 0xc1, 0xe0, 0x88, 0x8e, 0x95, 0xca, 0x04, 0x33, 0x05, 0x18, 0xb9, + 0x82, 0x58, 0x85, 0x99, 0x6a, 0x42, 0x8e, 0x06, 0xc8, 0x50, 0x1b, 0xe3, + 0x02, 0x73, 0xf3, 0x82, 0x8e, 0x22, 0x71, 0x9c, 0x13, 0xaa, 0xd1, 0x80, + 0x0d, 0x94, 0xb0, 0xb0, 0x8e, 0x78, 0x7b, 0xfb, 0x70, 0x64, 0x78, 0x72, + 0xbf, 0x18, 0x7e, 0x37, 0x29, 0xa1, 0x43, 0x86, 0x75, 0x79, 0xda, 0xa6, + 0xbe, 0x09, 0xd7, 0xd1, 0xca, 0x92, 0xbe, 0x07, 0xe6, 0x8a, 0x16, 0xf4, + 0x2b, 0x6c, 0x11, 0x4b, 0xa2, 0x25, 0x47, 0x86, 0x98, 0x2b, 0x78, 0xdd, + 0xaf, 0xef, 0xab, 0x9c, 0x43, 0xe7, 0x5c, 0x94, 0x3b, 0x41, 0xf2, 0xa4, + 0x21, 0x46, 0xc1, 0x4f, 0xa9, 0x64, 0x09, 0x55, 0x5d, 0xb7, 0xf2, 0x10, + 0xca, 0xdc, 0xfa, 0xd9, 0xf6, 0xd8, 0x47, 0xf9, 0xda, 0x1d, 0xd4, 0x3b, + 0xa0, 0xbf, 0x7c, 0x1c, 0x0a, 0x01, 0x3d, 0x3f, 0xb2, 0x71, 0x98, 0xe3, + 0x25, 0x38, 0x09, 0x2a, 0x30, 0xcf, 0xac, 0xc5, 0x3d, 0x20, 0xcf, 0x5c, + 0x0c, 0xfb, 0x0c, 0xe2, 0xe6, 0x0b, 0x53, 0x34, 0xf5, 0x1d, 0xb8, 0x08, + 0x69, 0xcf, 0xac, 0x47, 0x2a, 0xd5, 0xe8, 0x83, 0xa1, 0x69, 0xf9, 0xb5, + 0xe2, 0xc1, 0x4e, 0x68, 0xae, 0x7d, 0x20, 0x9b, 0xbc, 0x6d, 0xf2, 0x65, + 0x70, 0x3a, 0x09, 0x17, 0xfa, 0xa3, 0x24, 0x47, 0x4a, 0xaf, 0x74, 0x0c, + 0xd1, 0x1f, 0xd3, 0xd6, 0xb0, 0x21, 0x67, 0xb9, 0x58, 0xdb, 0xc3, 0x5f, + 0x95, 0xc1, 0x4c, 0x80, 0x2a, 0xc2, 0xa9, 0x2a, 0x69, 0x0d, 0x9d, 0xd6, + 0xff, 0xc2, 0x32, 0xcd, 0x4c, 0x99, 0x48, 0x50, 0x8a, 0xe8, 0x7e, 0x88, + 0xb8, 0x69, 0x63, 0xea, 0xbc, 0x04, 0xf8, 0xc6, 0x8e, 0x16, 0xe9, 0x9a, + 0x5f, 0x6c, 0x95, 0xf4, 0xb1, 0x0a, 0x4b, 0x0a, 0x16, 0x18, 0x3a, 0xbb, + 0xf1, 0x0c, 0xd7, 0x64, 0xb8, 0xb0, 0xcb, 0xc5, 0xd0, 0x57, 0x0b, 0x92, + 0xec, 0x3c, 0x97, 0xa1, 0x57, 0x54, 0x66, 0xce, 0xc5, 0x4f, 0xcc, 0x28, + 0xbe, 0xe8, 0xfb, 0x12, 0x52, 0x61, 0x4f, 0xa2, 0x0f, 0x56, 0xc4, 0x16, + 0xb8, 0xb6, 0xa0, 0xaa, 0x4a, 0x43, 0x0e, 0xb5, 0x32, 0x97, 0x03, 0xcf, + 0x6a, 0xe7, 0xb7, 0xc2, 0x51, 0x08, 0x7c, 0xb1, 0xdb, 0x05, 0x70, 0xdc, + 0xa1, 0x92, 0x0b, 0x06, 0x3e, 0xeb, 0x02, 0x20, 0x43, 0x73, 0xa9, 0x2f, + 0x62, 0x85, 0x30, 0x15, 0x9f, 0x13, 0x43, 0x00, 0xf3, 0xb4, 0xb0, 0x8b, + 0x53, 0x20, 0x32, 0x7b, 0x04, 0x4c, 0xa8, 0x36, 0x82, 0xbc, 0x5c, 0x7d, + 0xde, 0xd2, 0x81, 0x2c, 0x9a, 0x26, 0x94, 0x45, 0xfd, 0x6d, 0xce, 0xe9, + 0xb9, 0xeb, 0xae, 0xbc, 0xd7, 0xfc, 0xdb, 0x1b, 0xdc, 0x11, 0xcd, 0xb4, + 0x1c, 0xff, 0xed, 0xdf, 0xbb, 0x31, 0x21, 0x15, 0xa3, 0x7a, 0x87, 0x9e, + 0x5c, 0x34, 0x5e, 0x2e, 0x36, 0x9f, 0xce, 0x63, 0xfe, 0x53, 0xbd, 0x4b, + 0x9f, 0xe9, 0xf1, 0x51, 0x5d, 0xe3, 0xb7, 0x7f, 0xc3, 0xe6, 0xfd, 0xff, + 0x3e, 0x7d, 0x69, 0xed, 0xfe, 0x25, 0x2d, 0x27, 0xec, 0x72, 0x90, 0x15, + 0x6e, 0x91, 0xfa, 0x7f, 0x99, 0x97, 0x09, 0xf2, 0x13, 0x80, 0xe3, 0x88, + 0x94, 0x2a, 0x33, 0x84, 0xe7, 0x96, 0xa0, 0x06, 0xc7, 0x11, 0x18, 0x8f, + 0xc7, 0xb3, 0xfc, 0x25, 0xaf, 0x8d, 0x60, 0xc5, 0x09, 0x6b, 0x9a, 0x7f, + 0xa5, 0xeb, 0x06, 0xf7, 0x09, 0x52, 0x7d, 0x33, 0x8d, 0x13, 0x5a, 0x73, + 0x7c, 0xe6, 0x41, 0x41, 0x25, 0xc9, 0xd3, 0x0b, 0x02, 0x45, 0xe8, 0xbd, + 0xc7, 0x04, 0xd8, 0x85, 0xb4, 0x49, 0x27, 0xcb, 0x81, 0x59, 0xc2, 0xb5, + 0x5f, 0x6f, 0xff, 0xe6, 0xed, 0xb1, 0x9f, 0x23, 0x3b, 0xf3, 0xeb, 0x34, + 0x0e, 0x90, 0xc8, 0x53, 0x5c, 0x67, 0xe6, 0x40, 0xce, 0x48, 0x54, 0xd5, + 0x76, 0x9e, 0x07, 0x2d, 0x8c, 0xfa, 0xf0, 0x7e, 0x46, 0x7d, 0xf7, 0xa6, + 0x15, 0xf0, 0xe5, 0x42, 0x4b, 0xcd, 0x08, 0x93, 0x4a, 0x8c, 0xdb, 0x64, + 0xd4, 0x47, 0x65, 0x0b, 0xc2, 0x36, 0xe6, 0x85, 0x8b, 0x6e, 0xd6, 0xc3, + 0x48, 0x77, 0x09, 0x1f, 0xc2, 0xe2, 0x4a, 0x71, 0x4b, 0xf7, 0xf4, 0xbe, + 0xe7, 0xc5, 0x69, 0xf8, 0x7e, 0xd4, 0x68, 0x5a, 0xa2, 0xa1, 0xe5, 0x84, + 0x8e, 0x18, 0xa7, 0x36, 0x4d, 0xc5, 0x35, 0x05, 0x4a, 0x13, 0x8c, 0x53, + 0xd8, 0x70, 0x8c, 0x37, 0x0a, 0x19, 0xec, 0x5f, 0xd8, 0x0f, 0x68, 0xcb, + 0xd9, 0xa6, 0x8f, 0xbb, 0x49, 0x56, 0x40, 0x3b, 0xf2, 0xf1, 0x69, 0x46, + 0xab, 0x71, 0xf9, 0x00, 0x0e, 0x01, 0x44, 0xdc, 0x29, 0x0c, 0xee, 0x09, + 0xb8, 0x99, 0xe5, 0xf2, 0x4b, 0x57, 0x22, 0x9d, 0xfa, 0x1d, 0x0f, 0x5b, + 0x05, 0x6c, 0xe0, 0x19, 0xb9, 0x0f, 0x80, 0xb5, 0x33, 0x5f, 0xf8, 0x55, + 0x84, 0x98, 0xb9, 0x5e, 0x3b, 0x70, 0x04, 0xfb, 0xfc, 0x7a, 0x5d, 0x02, + 0x02, 0x3b, 0x0c, 0xec, 0xa4, 0x0c, 0x70, 0xba, 0x53, 0x2c, 0xeb, 0x2b, + 0x7d, 0xda, 0xb5, 0xb1, 0xc1, 0xf9, 0x4f, 0xf4, 0xa6, 0xf2, 0xf7, 0xe2, + 0x19, 0xd5, 0x38, 0xb8, 0xbe, 0x07, 0x72, 0xb4, 0x2d, 0x7f, 0x32, 0xac, + 0x3c, 0x7a, 0x18, 0x64, 0xde, 0x99, 0x13, 0xf2, 0xa0, 0xde, 0x70, 0xc5, + 0xcb, 0x72, 0x7a, 0x2c, 0xf7, 0xb7, 0x3b, 0xdb, 0x8a, 0x2d, 0x98, 0xbf, + 0xc9, 0x77, 0xd8, 0x39, 0x33, 0x9f, 0x6f, 0xc3, 0x16, 0xe7, 0x09, 0x59, + 0xc4, 0x32, 0xfa, 0x89, 0xdc, 0xf2, 0x47, 0xb1, 0x92, 0x78, 0xb9, 0x61, + 0x7f, 0x11, 0xb9, 0xbb, 0xb7, 0xce, 0x40, 0xeb, 0x50, 0x1b, 0x36, 0xab, + 0xc4, 0xd3, 0x96, 0x50, 0xc4, 0x22, 0x5c, 0xc5, 0x4f, 0x93, 0x83, 0x8c, + 0xab, 0xa1, 0xe4, 0x02, 0x7b, 0x6d, 0xe7, 0x86, 0x94, 0x7f, 0x18, 0x22, + 0x1f, 0x46, 0xfd, 0x89, 0x27, 0xa4, 0xd9, 0x15, 0x9a, 0x99, 0xc3, 0x82, + 0x96, 0x95, 0x23, 0x23, 0x7f, 0xf6, 0x0a, 0x42, 0x16, 0x70, 0x9c, 0x09, + 0xde, 0x92, 0x5c, 0x9f, 0x17, 0xf4, 0xb1, 0x91, 0x11, 0x8f, 0x00, 0xb4, + 0x03, 0xba, 0xd1, 0xb9, 0x7b, 0xe5, 0x22, 0x41, 0x32, 0xc9, 0x6f, 0xe1, + 0xb5, 0xb3, 0x6a, 0xed, 0x12, 0x07, 0xe6, 0x6e, 0x4c, 0x02, 0xd3, 0x16, + 0xd5, 0x0f, 0x01, 0x15, 0x02, 0x58, 0x75, 0x0c, 0xab, 0x84, 0xb2, 0x33, + 0x4b, 0x62, 0x1d, 0x00, 0x71, 0xf7, 0x37, 0xef, 0xeb, 0x1d, 0xab, 0xa9, + 0x92, 0x1a, 0x8c, 0xd7, 0x44, 0x37, 0x75, 0x24, 0x28, 0xd3, 0x5c, 0x5f, + 0xd7, 0xd5, 0x71, 0xad, 0x93, 0x97, 0x31, 0x2d, 0x3e, 0x21, 0x72, 0xd9, + 0xa7, 0xb0, 0xfc, 0x8b, 0xee, 0x5d, 0x68, 0x82, 0xa4, 0x7b, 0xa7, 0x29, + 0x5d, 0x2e, 0x60, 0x13, 0xfd, 0xc3, 0x8c, 0xa5, 0x7e, 0xe3, 0x27, 0xfa, + 0x3d, 0x98, 0x3a, 0x80, 0x14, 0x1c, 0xfd, 0x3e, 0x9f, 0xf2, 0x7f, 0x37, + 0x89, 0xeb, 0xde, 0x14, 0xce, 0xdf, 0xdc, 0xf4, 0x6f, 0x11, 0x0c, 0xf9, + 0x4f, 0xd9, 0xdd, 0x08, 0xd6, 0xdd, 0x64, 0x1e, 0xea, 0x4a, 0x0f, 0x33, + 0x08, 0xb2, 0x0f, 0x0f, 0xaf, 0xba, 0x74, 0xa6, 0x7d, 0xb5, 0x85, 0x81, + 0x69, 0x67, 0x6e, 0x5f, 0xec, 0x9f, 0xdd, 0x21, 0xd3, 0xc4, 0x94, 0x81, + 0xd4, 0xbb, 0x89, 0xa9, 0xa1, 0x84, 0xc7, 0x73, 0x2c, 0x14, 0x74, 0xc2, + 0xf5, 0xfd, 0x5c, 0x1f, 0xd7, 0xc2, 0xa8, 0x2a, 0xa7, 0x31, 0xd1, 0xaa, + 0x74, 0x9d, 0x37, 0x07, 0xdb, 0x9d, 0x1c, 0xec, 0x26, 0x7d, 0x11, 0x6c, + 0xa1, 0xf9, 0x22, 0xaa, 0xbe, 0xc5, 0xa4, 0xb4, 0x9c, 0xcf, 0x65, 0x77, + 0xe7, 0x03, 0xf9, 0x34, 0x5c, 0x7f, 0x40, 0x22, 0x0e, 0x67, 0x36, 0x54, + 0x16, 0xd8, 0x34, 0xca, 0xa6, 0xa3, 0x62, 0x64, 0xc5, 0x3b, 0x1c, 0x79, + 0xab, 0xb0, 0x92, 0xd9, 0xc3, 0x07, 0xaa, 0xfb, 0x0d, 0x41, 0x22, 0x88, + 0xfd, 0x06, 0x97, 0x92, 0xb9, 0x2c, 0x1c, 0x3b, 0x9d, 0xf2, 0x17, 0xc3, + 0x85, 0xb3, 0xf8, 0x10, 0x41, 0x1e, 0x5f, 0xdd, 0xaa, 0x23, 0x1c, 0xc1, + 0xd5, 0x9f, 0x54, 0xfb, 0x48, 0xf2, 0x41, 0xd5, 0x78, 0x09, 0xb5, 0xf9, + 0xc7, 0x90, 0x6d, 0xcb, 0x75, 0x85, 0x20, 0x67, 0xda, 0xa0, 0x6c, 0xf9, + 0x57, 0x76, 0x39, 0x26, 0xd1, 0x35, 0x60, 0xe0, 0xd1, 0x19, 0xee, 0xb4, + 0xf4, 0xe6, 0x83, 0xbf, 0xfd, 0x54, 0x7e, 0x42, 0x40, 0x0f, 0xd3, 0x59, + 0xa7, 0x0f, 0x39, 0x23, 0x9f, 0xda, 0xd5, 0x5c, 0x7a, 0xb5, 0xac, 0x1e, + 0xde, 0xf8, 0xff, 0xcd, 0xe3, 0x5c, 0x6e, 0x03, 0xb2, 0x14, 0x2a, 0x9e, + 0x78, 0x4d, 0x3e, 0xe4, 0xd0, 0x9b, 0x68, 0x3b, 0xb1, 0x72, 0xef, 0xf7, + 0xf8, 0x8e, 0xc8, 0x6c, 0x62, 0x42, 0x5f, 0xb4, 0xdb, 0x0e, 0xab, 0x68, + 0xc9, 0x9d, 0xb4, 0xd1, 0xa8, 0x29, 0xce, 0x9b, 0x2a, 0xb8, 0x54, 0x03, + 0x42, 0x22, 0x9a, 0xb2, 0x78, 0xe8, 0x57, 0x4e, 0x6d, 0x8a, 0xe8, 0x08, + 0xef, 0x04, 0x1d, 0x3a, 0xb1, 0x67, 0x51, 0x67, 0xbf, 0xe6, 0x0c, 0xcc, + 0xfb, 0x28, 0x8e, 0x59, 0x69, 0x54, 0x0e, 0x5e, 0x3e, 0x99, 0x15, 0x67, + 0x1c, 0x16, 0x33, 0x33, 0x32, 0x84, 0xe4, 0xf8, 0x70, 0x5d, 0xcb, 0xd7, + 0x0f, 0xa4, 0xf2, 0x86, 0xcb, 0xc6, 0x25, 0xd9, 0x03, 0x87, 0xae, 0x9d, + 0x23, 0xd5, 0xc7, 0x26, 0xca, 0xc6, 0xa3, 0xba, 0xca, 0x6b, 0xcf, 0x04, + 0xa8, 0xbb, 0x07, 0x0c, 0xe6, 0xb5, 0x87, 0x44, 0x94, 0x6b, 0xb5, 0x2e, + 0x16, 0x66, 0x19, 0x25, 0xb9, 0x42, 0x79, 0xce, 0x46, 0xc8, 0x3a, 0xdd, + 0xec, 0x11, 0xa7, 0xe1, 0xae, 0x4f, 0x91, 0x67, 0xf7, 0xee, 0x67, 0xc1, + 0xa4, 0x52, 0xe3, 0x59, 0xb8, 0xff, 0xd3, 0x4c, 0xaf, 0x6b, 0x31, 0x01, + 0x96, 0x30, 0x14, 0x74, 0x7e, 0xf4, 0x21, 0x31, 0x84, 0x68, 0x87, 0x32, + 0x84, 0x10, 0xad, 0x4c, 0x41, 0x80, 0xa3, 0xfb, 0x55, 0x1c, 0x49, 0x2e, + 0x9a, 0x59, 0x84, 0x9d, 0x36, 0x3a, 0xea, 0x55, 0x0a, 0xd8, 0x00, 0xb1, + 0xf9, 0x7e, 0xb3, 0xeb, 0x7e, 0x87, 0xd5, 0x38, 0x59, 0x97, 0x40, 0xec, + 0xcd, 0x5c, 0xb3, 0x6a, 0x76, 0x0d, 0xad, 0x1f, 0x6e, 0x60, 0x5a, 0x2d, + 0x21, 0xf4, 0x36, 0x96, 0x32, 0x46, 0xc9, 0x84, 0x9f, 0x24, 0xe4, 0x86, + 0x6c, 0xab, 0x37, 0x15, 0x45, 0x77, 0x0d, 0x1e, 0x89, 0xe1, 0xac, 0xda, + 0x5b, 0xf7, 0x83, 0xec, 0xea, 0xa5, 0x9c, 0xa3, 0xe5, 0x15, 0x7c, 0xf8, + 0x39, 0xf9, 0xd5, 0x27, 0x9a, 0xce, 0xb2, 0xc2, 0x6c, 0xd3, 0xf1, 0xf6, + 0x52, 0xe8, 0x50, 0xcb, 0xf6, 0x06, 0x10, 0x3e, 0xd2, 0x8c, 0xcc, 0x31, + 0x24, 0x9c, 0x96, 0x81, 0x5e, 0x28, 0xc7, 0x70, 0xd5, 0xa8, 0xb7, 0xfe, + 0x40, 0x83, 0xdb, 0xfd, 0x92, 0x95, 0xf4, 0x2d, 0x4c, 0xff, 0x49, 0x2e, + 0x76, 0x4d, 0x6b, 0x78, 0x4f, 0x74, 0x48, 0x5a, 0xc5, 0x14, 0x18, 0x64, + 0x25, 0x74, 0x7a, 0x9a, 0xaf, 0x9b, 0x24, 0xfa, 0x3f, 0xfc, 0x0e, 0x6e, + 0x87, 0x60, 0x4b, 0xa2, 0x0a, 0x74, 0x36, 0xfd, 0xfa, 0x9e, 0x68, 0x4a, + 0x0e, 0x81, 0xc1, 0xad, 0xe0, 0x11, 0x8d, 0x9a, 0xb2, 0xdd, 0xf3, 0x4b, + 0x54, 0x68, 0x92, 0x4d, 0x3e, 0x8b, 0xcc, 0x64, 0x61, 0x99, 0x11, 0x3a, + 0x7a, 0x3e, 0x53, 0x7d, 0x93, 0x9e, 0xea, 0x35, 0x6a, 0xc2, 0x67, 0x0b, + 0xa8, 0x82, 0x57, 0x94, 0x59, 0xac, 0x37, 0x64, 0xed, 0x9b, 0x1d, 0x8f, + 0x41, 0xca, 0x57, 0xb5, 0x56, 0x6e, 0x7c, 0x81, 0x22, 0x86, 0xa3, 0xe6, + 0xf6, 0x5f, 0x66, 0xba, 0xba, 0xd1, 0xf5, 0x74, 0x9b, 0x75, 0xe6, 0x6d, + 0x52, 0x9a, 0x44, 0x46, 0xea, 0x35, 0xf7, 0xb2, 0x35, 0x52, 0xc3, 0x88, + 0x2b, 0xa5, 0xd9, 0x95, 0xc1, 0x8c, 0xb2, 0x88, 0xbf, 0xf0, 0x8d, 0xdb, + 0xee, 0x85, 0xbc, 0x89, 0x4e, 0xb8, 0xde, 0xda, 0x60, 0x16, 0xdd, 0xb1, + 0x77, 0xf8, 0x76, 0xa0, 0x75, 0xb6, 0x0b, 0xea, 0x1e, 0x69, 0x71, 0x52, + 0x23, 0xf4, 0x6e, 0x4d, 0xcc, 0xc9, 0x3a, 0x3b, 0xea, 0xc4, 0xdb, 0xc2, + 0xb2, 0x5d, 0x58, 0xc1, 0x82, 0xf8, 0x6f, 0x11, 0x67, 0x89, 0x7d, 0x46, + 0x27, 0x00, 0x92, 0x0e, 0x35, 0xec, 0x15, 0xc8, 0x0c, 0x4a, 0x7d, 0xf0, + 0x56, 0x87, 0xd7, 0x72, 0x13, 0x83, 0x29, 0x21, 0xed, 0x5d, 0x50, 0xcb, + 0x35, 0x5d, 0xe4, 0x8e, 0xf6, 0xbf, 0x8a, 0x71, 0xaa, 0x80, 0xd7, 0x3a, + 0x55, 0xae, 0x54, 0x9e, 0x94, 0xa2, 0xe9, 0xba, 0xfd, 0x80, 0x65, 0x2a, + 0x89, 0x72, 0x39, 0x0a, 0xa1, 0xdf, 0xe3, 0xa3, 0xcc, 0x50, 0x49, 0x48, + 0x07, 0xea, 0xaa, 0xd4, 0x08, 0xc6, 0xae, 0xcd, 0x44, 0xec, 0x56, 0x8d, + 0x71, 0x60, 0xd8, 0x9f, 0x17, 0x95, 0xfb, 0xf4, 0x46, 0x50, 0x44, 0x7c, + 0xa4, 0xe3, 0xe3, 0xe4, 0xf7, 0x0f, 0xb3, 0x79, 0x0e, 0xfd, 0xb9, 0x2e, + 0x08, 0x41, 0xce, 0xe2, 0x8e, 0x2e, 0x0f, 0x1f, 0xbc, 0x15, 0x22, 0x61, + 0x88, 0xe5, 0xc1, 0x4b, 0x75, 0x43, 0xeb, 0x9c, 0xab, 0x3e, 0xdb, 0x8a, + 0xdc, 0x8d, 0x07, 0x78, 0xb4, 0xbc, 0xaf, 0xe3, 0xb3, 0x3d, 0x52, 0xde, + 0x1f, 0x66, 0x32, 0xe7, 0x6c, 0x85, 0x3b, 0x7a, 0x9a, 0xdb, 0x33, 0xce, + 0xbd, 0x8d, 0xbf, 0xb6, 0xa8, 0xbd, 0xe1, 0xa0, 0xd5, 0xe7, 0xdb, 0x38, + 0xfa, 0x39, 0x96, 0xae, 0x99, 0x42, 0x4a, 0xc1, 0x2a, 0x2d, 0x37, 0xca, + 0x03, 0xd1, 0x75, 0x31, 0x5e, 0x9c, 0x77, 0x0f, 0xf0, 0xf7, 0x6c, 0x33, + 0xf9, 0xf9, 0xa6, 0x76, 0x49, 0x5b, 0xff, 0xcf, 0xa9, 0x46, 0x82, 0x60, + 0x3b, 0x7f, 0xaa, 0x8f, 0x11, 0xd2, 0x04, 0xd2, 0x68, 0xe9, 0xe1, 0xa3, + 0x69, 0x19, 0xe6, 0xb0, 0x60, 0xf1, 0xd0, 0x02, 0xd1, 0x08, 0xf5, 0x1c, + 0x13, 0xfa, 0xe0, 0x13, 0x51, 0x14, 0x24, 0x05, 0x20, 0x16, 0xd0, 0x42, + 0x3f, 0x06, 0x5a, 0x66, 0x48, 0x16, 0x91, 0x8b, 0xb6, 0x58, 0xc5, 0x01, + 0x4d, 0xcb, 0x09, 0x53, 0x32, 0x3b, 0x2e, 0x60, 0x5f, 0x96, 0x4a, 0x93, + 0xed, 0x7b, 0xba, 0x48, 0xf6, 0xcf, 0xce, 0xe4, 0xc2, 0x1f, 0x57, 0x5b, + 0xd0, 0xfb, 0x4c, 0x98, 0x44, 0x23, 0x33, 0xc0, 0x98, 0xa8, 0x06, 0x17, + 0x08, 0x3d, 0xd4, 0xd3, 0xb4, 0xf0, 0x21, 0xd9, 0x8c, 0x21, 0x66, 0x5e, + 0xc1, 0xcb, 0x19, 0xe3, 0x83, 0xe0, 0x87, 0xcc, 0x9f, 0x41, 0xa9, 0xeb, + 0x60, 0x32, 0xce, 0xd8, 0xb7, 0x03, 0x23, 0x2e, 0x4d, 0x74, 0x31, 0x82, + 0xee, 0xc2, 0xee, 0x20, 0x8b, 0x36, 0x10, 0x60, 0xc8, 0x03, 0xb0, 0x06, + 0x55, 0x9c, 0x8b, 0x8b, 0xdd, 0x67, 0x76, 0x56, 0x86, 0xb8, 0xe2, 0x4f, + 0x6c, 0xf0, 0x5a, 0xf1, 0x7c, 0x8d, 0x00, 0x18, 0xff, 0x61, 0x5e, 0x42, + 0x2a, 0x60, 0xa3, 0x41, 0x35, 0x74, 0x3b, 0x8d, 0x07, 0x42, 0x9d, 0x5b, + 0x59, 0x14, 0x6e, 0x92, 0x32, 0x68, 0xe4, 0xfa, 0x63, 0x8b, 0x68, 0xe9, + 0x7c, 0x92, 0xa5, 0x85, 0xa2, 0xb9, 0x5d, 0xa5, 0x82, 0x50, 0x18, 0x3a, + 0xc3, 0xdc, 0xb2, 0x47, 0x7e, 0x75, 0xbd, 0x87, 0x73, 0xce, 0x09, 0x22, + 0xa6, 0x61, 0x61, 0xe5, 0x6f, 0xd6, 0xaa, 0x60, 0xc2, 0x6b, 0xf0, 0xe3, + 0x02, 0xd6, 0xce, 0x8f, 0x57, 0xc8, 0xdc, 0x3f, 0x84, 0x56, 0xb8, 0xbb, + 0x47, 0x50, 0x84, 0x51, 0xa6, 0xfa, 0x6f, 0x8e, 0xed, 0xe1, 0x20, 0x18, + 0xbb, 0x45, 0x32, 0x3a, 0x14, 0xeb, 0x0b, 0x44, 0x3a, 0xc6, 0x30, 0xfb, + 0xa8, 0x1c, 0x46, 0x7c, 0xa8, 0x1f, 0x2d, 0xee, 0x6b, 0x11, 0x07, 0x41, + 0xfa, 0xf3, 0xa2, 0x54, 0xaf, 0x04, 0x0f, 0x22, 0x2e, 0x4e, 0x8e, 0xeb, + 0x58, 0xf1, 0x90, 0xf5, 0xe4, 0xa0, 0x3f, 0x3b, 0xe6, 0xe6, 0xc9, 0xfd, + 0x9f, 0x09, 0xc5, 0x04, 0xa8, 0x53, 0xaf, 0xdc, 0x5f, 0xb2, 0x1e, 0xbb, + 0x45, 0x52, 0xb4, 0xb8, 0x12, 0xa6, 0xec, 0x27, 0x13, 0x9d, 0xb7, 0x09, + 0xd5, 0xec, 0x35, 0xf2, 0xbc, 0x47, 0x02, 0x5b, 0xac, 0x0d, 0x7d, 0x38, + 0xa3, 0xf7, 0xa8, 0x1d, 0xd4, 0xb8, 0xb7, 0x1f, 0xac, 0x8b, 0xbb, 0x61, + 0x1b, 0xe7, 0x01, 0x37, 0x4e, 0x50, 0x79, 0x33, 0xdc, 0xbd, 0x8d, 0x48, + 0x1b, 0xbb, 0x4c, 0xed, 0x9c, 0xc9, 0x07, 0x8c, 0x89, 0x0c, 0x44, 0x68, + 0xbb, 0xbe, 0x97, 0xe4, 0x79, 0xb6, 0x99, 0x5f, 0xe4, 0xeb, 0xb2, 0xc5, + 0xf2, 0xca, 0x8a, 0xf5, 0xa0, 0x44, 0x2c, 0x65, 0xff, 0xf7, 0x26, 0x56, + 0xdc, 0x81, 0x9c, 0x77, 0xee, 0xfb, 0x7a, 0x10, 0xbe, 0x96, 0x08, 0x51, + 0x4d, 0x5a, 0x2e, 0xab, 0xed, 0x21, 0x3a, 0x62, 0xa8, 0x82, 0x60, 0x48, + 0x25, 0x83, 0xbc, 0x8a, 0x6f, 0xe2, 0xa4, 0x89, 0x4a, 0x3a, 0xbd, 0xbd, + 0x47, 0x98, 0x9f, 0x1f, 0x93, 0xfc, 0x75, 0x7c, 0x43, 0x80, 0x1f, 0x7a, + 0xed, 0x2b, 0x63, 0x27, 0x5f, 0x94, 0x5b, 0xab, 0xba, 0xbb, 0xef, 0x6f, + 0x73, 0xb7, 0xa1, 0x1b, 0x70, 0xd8, 0xbe, 0xc9, 0x1f, 0xf4, 0x1e, 0x88, + 0x66, 0xe3, 0xa7, 0x28, 0x5e, 0x04, 0x18, 0x83, 0x6b, 0xe9, 0xc2, 0x42, + 0x44, 0x7d, 0x80, 0xaa, 0xf1, 0x1a, 0x41, 0x13, 0xa6, 0x59, 0x7e, 0x84, + 0xf9, 0x34, 0x0f, 0x20, 0x0c, 0x9f, 0x5c, 0x5a, 0xf7, 0xd0, 0xb4, 0x2e, + 0x1e, 0xf4, 0xef, 0xc8, 0x02, 0xcd, 0x53, 0xeb, 0x48, 0xc0, 0xb2, 0x3b, + 0x44, 0xa2, 0x48, 0xd4, 0x45, 0x89, 0xc6, 0x1d, 0x0a, 0x76, 0x7d, 0x8a, + 0x42, 0xe4, 0xcd, 0xcb, 0x74, 0x98, 0x10, 0x59, 0xea, 0x68, 0x5b, 0xca, + 0x5f, 0x66, 0x23, 0x90, 0xd3, 0xbc, 0x30, 0x70, 0x0e, 0x21, 0x23, 0x67, + 0xe6, 0x4a, 0x1e, 0xeb, 0xda, 0xbc, 0xb9, 0x44, 0x3f, 0xd3, 0xc1, 0xaa, + 0xa0, 0x4d, 0x40, 0xc9, 0xdb, 0x05, 0x68, 0x0b, 0x2a, 0x34, 0x0e, 0xd5, + 0x6d, 0xe8, 0xb1, 0xfe, 0x31, 0x42, 0xfb, 0xfb, 0xb4, 0x87, 0x45, 0x24, + 0xc1, 0x80, 0xd0, 0x3b, 0x4e, 0x0d, 0xa9, 0x34, 0x2b, 0xc9, 0xe3, 0x2f, + 0xf9, 0x73, 0xd3, 0x2f, 0x27, 0xa2, 0xd1, 0xa4, 0x00, 0x73, 0xd3, 0xe6, + 0x2d, 0xdf, 0x7f, 0xcf, 0x32, 0xc9, 0x9a, 0xfa, 0xf0, 0x48, 0x52, 0x32, + 0x03, 0xbe, 0xee, 0x7b, 0xd5, 0x3a, 0x7c, 0x23, 0xcf, 0xca, 0x93, 0xb9, + 0xca, 0x4d, 0xdc, 0x3a, 0x51, 0xae, 0x7e, 0xd7, 0x1c, 0xf0, 0xc8, 0x2f, + 0xb6, 0x59, 0xe6, 0xdd, 0xb1, 0x74, 0x0c, 0xf0, 0xf1, 0xbe, 0x2d, 0xc9, + 0xb4, 0xbd, 0x56, 0x89, 0x64, 0x83, 0xad, 0x6d, 0xa9, 0x14, 0x7c, 0x74, + 0xb8, 0x7d, 0x5d, 0x71, 0x98, 0x0c, 0x72, 0x84, 0xac, 0x32, 0x8f, 0x8f, + 0x49, 0x1a, 0x23, 0x9a, 0xdd, 0xae, 0xc8, 0x1c, 0xdd, 0xcd, 0x45, 0x1a, + 0x20, 0xca, 0x7d, 0xd1, 0x04, 0xd3, 0x03, 0x5c, 0x1c, 0xdd, 0x29, 0x02, + 0xfd, 0x2b, 0x4e, 0x33, 0x89, 0x89, 0xe4, 0x68, 0x83, 0x88, 0x7c, 0xd0, + 0xc4, 0x85, 0x44, 0xa3, 0x97, 0xe9, 0x2b, 0x27, 0x77, 0x0c, 0x46, 0xdc, + 0x0e, 0x7d, 0xf5, 0x4c, 0x23, 0x26, 0x71, 0xbe, 0x67, 0x68, 0x22, 0xc9, + 0xff, 0x24, 0x80, 0xa0, 0xf3, 0x17, 0xc5, 0x15, 0xaf, 0x58, 0x24, 0x68, + 0xa7, 0x19, 0x5d, 0xf0, 0x3a, 0xb8, 0xdb, 0x9a, 0x05, 0xc9, 0xb4, 0x32, + 0x66, 0x91, 0x4e, 0x13, 0x00, 0xee, 0x17, 0xd4, 0x08, 0x15, 0x57, 0x73, + 0x73, 0xa7, 0x55, 0xff, 0x08, 0x13, 0xc4, 0xdb, 0x48, 0x8f, 0xed, 0xa6, + 0xd3, 0x0e, 0x7a, 0x64, 0xa1, 0xb7, 0xba, 0xdb, 0xd1, 0x80, 0x40, 0x86, + 0x81, 0x9d, 0xe6, 0x9c, 0x54, 0xdc, 0x35, 0x00, 0x10, 0xb0, 0x0f, 0x0e, + 0x68, 0x41, 0x5f, 0x32, 0x8f, 0xae, 0x75, 0x5b, 0xb5, 0x6c, 0x79, 0xb4, + 0x73, 0x88, 0xde, 0xdf, 0x73, 0xa5, 0xf8, 0xa9, 0xba, 0xcf, 0xe1, 0x9e, + 0x37, 0x67, 0xcc, 0x52, 0x42, 0x12, 0x7e, 0xce, 0x47, 0xd0, 0x6c, 0x26, + 0x49, 0x24, 0xff, 0xb0, 0x4b, 0x79, 0x10, 0x25, 0x8e, 0x91, 0x5a, 0x5d, + 0xd1, 0xad, 0x32, 0x9c, 0x38, 0xfd, 0xd4, 0x6c, 0xe8, 0x07, 0xda, 0x7d, + 0x42, 0xc8, 0x28, 0x46, 0xb2, 0xfc, 0x80, 0x83, 0x1b, 0x3e, 0xf7, 0xe2, + 0xc2, 0xfd, 0xa0, 0xc0, 0x35, 0x9e, 0xec, 0x3f, 0xfd, 0x00, 0x3e, 0xf3, + 0xd7, 0x34, 0x86, 0x9c, 0x55, 0x92, 0x20, 0x88, 0x7a, 0x65, 0x74, 0x9c, + 0x36, 0x74, 0x76, 0x88, 0x6d, 0xc7, 0xba, 0x4b, 0xe7, 0x05, 0xa8, 0xc5, + 0xf1, 0xe4, 0x50, 0x97, 0x11, 0xf4, 0xf2, 0x51, 0x73, 0x40, 0xb5, 0x18, + 0x89, 0xe1, 0x1c, 0x08, 0xf6, 0x7e, 0x41, 0x43, 0x16, 0x4a, 0x55, 0x35, + 0xfc, 0x48, 0xee, 0x04, 0x05, 0x0a, 0xcd, 0xdc, 0xbf, 0xe9, 0xd7, 0xbf, + 0x68, 0x63, 0xc8, 0x28, 0xee, 0xaa, 0xbc, 0xc3, 0x38, 0x00, 0x64, 0xff, + 0xc6, 0x17, 0x49, 0xc6, 0xbb, 0xc0, 0x06, 0x76, 0x9e, 0xdb, 0x87, 0xa0, + 0x4e, 0x15, 0xa3, 0x53, 0x13, 0x55, 0xb9, 0xf0, 0x5c, 0xa8, 0x6b, 0xcd, + 0x4e, 0x95, 0x19, 0xb0, 0x0e, 0x27, 0xda, 0xb9, 0xa5, 0x05, 0x5f, 0x58, + 0x59, 0xf6, 0x0f, 0x29, 0xc1, 0x3d, 0x89, 0x3f, 0x45, 0x47, 0x58, 0x3b, + 0x9a, 0x9a, 0x52, 0xff, 0x0d, 0x95, 0xe8, 0xc5, 0xc1, 0xfe, 0xd8, 0x66, + 0xca, 0xc7, 0xb7, 0xa4, 0x1e, 0x86, 0x96, 0xdb, 0x9e, 0x6a, 0x1b, 0x90, + 0x17, 0x68, 0x2e, 0x5f, 0x39, 0x44, 0x07, 0x97, 0x5b, 0x8a, 0x0c, 0x11, + 0x06, 0x85, 0x08, 0x60, 0xb4, 0x28, 0x8d, 0xb5, 0x67, 0xda, 0x92, 0x4a, + 0x30, 0xc8, 0xf1, 0x75, 0xc7, 0xe9, 0xf7, 0xab, 0x66, 0x41, 0x79, 0x72, + 0xe7, 0xf4, 0xfa, 0x0f, 0x5c, 0xaa, 0xc0, 0xe4, 0xb9, 0x4c, 0x0c, 0x6d, + 0x63, 0x4d, 0x6f, 0xf6, 0x82, 0xf2, 0x47, 0x70, 0x8b, 0x55, 0x83, 0x3b, + 0x60, 0x3d, 0x94, 0x09, 0x76, 0xf0, 0x13, 0xf4, 0x15, 0xf3, 0x42, 0x13, + 0xfc, 0x9b, 0x62, 0x11, 0x5a, 0xf7, 0x85, 0xdf, 0xcc, 0xd2, 0x21, 0xee, + 0xbd, 0xc2, 0x32, 0x33, 0x7a, 0x49, 0x5e, 0x90, 0xff, 0xfd, 0xfa, 0x03, + 0xb0, 0x03, 0x3d, 0x1b, 0x6a, 0xad, 0x17, 0xe3, 0xb4, 0x76, 0xce, 0x96, + 0x90, 0x17, 0x44, 0x1a, 0x6f, 0xb4, 0x04, 0xdc, 0x05, 0x11, 0x54, 0x8f, + 0xf0, 0x0f, 0xef, 0xcd, 0xae, 0x77, 0xa5, 0xa2, 0x10, 0x45, 0x3f, 0x89, + 0x05, 0x93, 0xfe, 0x7b, 0xe3, 0x06, 0x2f, 0x0b, 0x34, 0x6d, 0x97, 0x85, + 0xad, 0x7e, 0xd5, 0xb5, 0x6a, 0xd7, 0x72, 0x8b, 0x3e, 0x70, 0x09, 0xb7, + 0x44, 0x44, 0x76, 0xd7, 0xc5, 0x7d, 0x43, 0xc0, 0x4d, 0xd9, 0x84, 0xe8, + 0xb2, 0x71, 0xf6, 0xd6, 0x37, 0xc0, 0x50, 0x26, 0xb9, 0xa3, 0x76, 0xc1, + 0x87, 0x6c, 0x39, 0x7c, 0x61, 0xa9, 0xaa, 0x0d, 0xd9, 0x08, 0xcb, 0x01, + 0xf1, 0x4f, 0x31, 0x39, 0x4b, 0x45, 0xd7, 0xd6, 0xb0, 0x3d, 0x84, 0x7b, + 0xe4, 0x9c, 0xb8, 0xee, 0x4a, 0x60, 0x3b, 0x4e, 0xda, 0x32, 0xca, 0xdd, + 0x81, 0x83, 0xce, 0x2e, 0xcd, 0x77, 0x37, 0x7c, 0x95, 0x81, 0x38, 0x86, + 0x20, 0x4c, 0x78, 0xce, 0x66, 0x70, 0xd9, 0x22, 0xc4, 0x57, 0x50, 0x3d, + 0x49, 0x1d, 0xe0, 0x81, 0x6e, 0x63, 0x01, 0xba, 0xc5, 0xfe, 0x75, 0x5e, + 0x20, 0x74, 0xf0, 0x09, 0xa6, 0x60, 0x4b, 0xfc, 0xb0, 0xde, 0x26, 0x34, + 0xa5, 0xf2, 0xd1, 0xd1, 0x46, 0x21, 0x9a, 0x1a, 0xeb, 0x5b, 0x62, 0xb8, + 0x8b, 0x3c, 0x29, 0xe2, 0xe1, 0x4a, 0xaa, 0x67, 0xce, 0x82, 0x8f, 0x8f, + 0x63, 0x56, 0x38, 0x7e, 0x61, 0x83, 0x20, 0x91, 0x1b, 0x44, 0x2b, 0xac, + 0x57, 0xaf, 0x7f, 0xed, 0x9a, 0xbf, 0xd0, 0x09, 0x53, 0xa3, 0xb4, 0x02, + 0xb2, 0xcc, 0x8d, 0x61, 0xf4, 0x6a, 0xd8, 0xea, 0xc7, 0x6d, 0x30, 0x7a, + 0x90, 0x31, 0xe9, 0xf4, 0xee, 0x7d, 0x98, 0xab, 0x6a, 0x19, 0x19, 0x67, + 0x10, 0x57, 0xf0, 0x74, 0x43, 0x27, 0xa1, 0xc0, 0xb2, 0xad, 0x74, 0xf3, + 0xaa, 0x4a, 0xf4, 0xa2, 0x55, 0x30, 0x5e, 0x6b, 0xeb, 0x4c, 0x05, 0x72, + 0xab, 0xf6, 0x95, 0x19, 0x96, 0x3c, 0xf3, 0xb2, 0x17, 0xff, 0x7d, 0x91, + 0xdf, 0x0c, 0xdd, 0xd0, 0x51, 0xe7, 0x67, 0x5b, 0x77, 0x07, 0x2c, 0xff, + 0x3b, 0x74, 0xcd, 0x27, 0xef, 0xb6, 0x2d, 0x26, 0x65, 0x63, 0xd4, 0xb6, + 0x01, 0xa5, 0x3e, 0x1c, 0x82, 0xa0, 0x63, 0x10, 0xf0, 0x2e, 0xbf, 0xd5, + 0x18, 0xf1, 0xb9, 0x44, 0x41, 0x76, 0xbc, 0xb1, 0xab, 0x15, 0x21, 0xbb, + 0x99, 0x81, 0x76, 0x69, 0xd1, 0x37, 0xa3, 0x79, 0xe2, 0x2b, 0x98, 0x5d, + 0x55, 0x11, 0x0c, 0xfb, 0xa1, 0xc9, 0xfe, 0x8a, 0x4b, 0x7a, 0x02, 0x96, + 0x1f, 0xc2, 0x71, 0xc9, 0xf6, 0x7a, 0x65, 0x97, 0xf7, 0x8b, 0xc7, 0x15, + 0xab, 0xca, 0x2a, 0xc7, 0x8d, 0x2b, 0x15, 0x63, 0x8f, 0xb8, 0xb6, 0x4d, + 0xe5, 0xe6, 0xcc, 0x26, 0x8c, 0x4e, 0x17, 0x67, 0x95, 0x99, 0x91, 0xec, + 0xfd, 0x55, 0x9f, 0x64, 0x34, 0x8c, 0x3c, 0x21, 0x08, 0xf4, 0x97, 0x42, + 0xda, 0x11, 0x5f, 0xa4, 0x46, 0xd7, 0x88, 0x59, 0x44, 0x25, 0x1f, 0x7b, + 0x71, 0x59, 0x5b, 0x59, 0x06, 0xb1, 0x14, 0xc1, 0x92, 0x38, 0xb8, 0xe5, + 0x3e, 0x0c, 0xf8, 0x9b, 0x8f, 0x64, 0x56, 0xf0, 0x95, 0xee, 0x50, 0x76, + 0xd1, 0x31, 0x7c, 0x0d, 0x72, 0x24, 0xcd, 0x0c, 0x96, 0xfc, 0x96, 0x50, + 0xcf, 0xce, 0xdb, 0xfa, 0x98, 0xaf, 0x6b, 0x85, 0x51, 0x35, 0x94, 0x6f, + 0x5d, 0x96, 0x1d, 0xc8, 0xc1, 0xf5, 0x66, 0x8b, 0x46, 0x58, 0x24, 0xad, + 0x8b, 0x0d, 0x61, 0x05, 0x57, 0x29, 0x03, 0x40, 0x96, 0xa2, 0x69, 0x6c, + 0x41, 0xa4, 0x4e, 0x21, 0xb9, 0x36, 0x99, 0xd7, 0x86, 0x4e, 0x8f, 0x4a, + 0xca, 0x4b, 0xb4, 0x10, 0xf5, 0x9d, 0x71, 0x96, 0x9e, 0x7b, 0x08, 0x34, + 0x36, 0x5c, 0xaf, 0x89, 0xb7, 0x7b, 0x93, 0x69, 0x57, 0x2b, 0x0b, 0xa1, + 0x7d, 0x75, 0xce, 0x93, 0x20, 0x89, 0x4a, 0xa7, 0xe8, 0x16, 0xc8, 0x53, + 0xf9, 0xa9, 0xd4, 0xbe, 0x0b, 0x49, 0x5a, 0xe4, 0x65, 0x17, 0xb9, 0x9a, + 0xe0, 0xdd, 0x57, 0x83, 0x1e, 0xce, 0xda, 0x1b, 0x4b, 0xf7, 0x62, 0xd3, + 0x61, 0xec, 0x3a, 0x4c, 0x20, 0x63, 0xd1, 0x86, 0x29, 0x77, 0x2d, 0x78, + 0x23, 0xb3, 0x2e, 0x9f, 0xab, 0x5c, 0x7f, 0x91, 0x26, 0x48, 0x48, 0x1c, + 0x7a, 0x36, 0xd2, 0xe1, 0x99, 0x89, 0xc8, 0xf9, 0xaf, 0xbb, 0x11, 0xbd, + 0xbd, 0x65, 0xc3, 0xbf, 0xda, 0x8b, 0xbf, 0xd7, 0x14, 0xf7, 0x2d, 0x9e, + 0xde, 0xdf, 0x98, 0x98, 0xe3, 0x5b, 0xb4, 0x39, 0xf5, 0xe9, 0xaa, 0xd0, + 0x0c, 0xfe, 0x70, 0x7b, 0x76, 0x60, 0x51, 0x21, 0xda, 0xe9, 0xe4, 0x91, + 0xca, 0x99, 0xbe, 0x31, 0xc7, 0xda, 0x90, 0xb8, 0x91, 0x3a, 0xfd, 0xfd, + 0x50, 0x0f, 0x30, 0x43, 0xd2, 0xa9, 0xac, 0xb4, 0x1b, 0x48, 0xb7, 0x7b, + 0x24, 0xfb, 0xa7, 0x5a, 0x86, 0x2c, 0x0a, 0xee, 0xb4, 0x8b, 0x07, 0x8d, + 0xbd, 0x01, 0x65, 0xf8, 0x65, 0xaf, 0xdf, 0x6d, 0x53, 0xff, 0x0c, 0xda, + 0x7f, 0x3f, 0xa4, 0x39, 0x92, 0xe4, 0x3c, 0xc7, 0xab, 0x54, 0xba, 0x5e, + 0x6c, 0x66, 0x3b, 0xd6, 0xdd, 0xdc, 0xef, 0x50, 0x2d, 0xbb, 0x66, 0xb4, + 0xa9, 0x64, 0x7d, 0x29, 0x90, 0x1f, 0xc9, 0x07, 0xbc, 0xd4, 0x48, 0x35, + 0x3e, 0x0d, 0xf0, 0xf6, 0x78, 0x81, 0x67, 0xeb, 0x71, 0xfe, 0xd5, 0x8c, + 0x22, 0xa9, 0x1a, 0x35, 0x1b, 0x7c, 0x0d, 0x15, 0x84, 0xa4, 0x5e, 0x91, + 0xdf, 0xe8, 0x06, 0x0f, 0xae, 0x17, 0xdd, 0x33, 0x39, 0x4a, 0x7b, 0xe8, + 0x07, 0xcb, 0xd3, 0xbf, 0xf0, 0x11, 0x98, 0x11, 0xf3, 0x74, 0x2a, 0xb7, + 0x81, 0xf8, 0x51, 0xc5, 0xcb, 0xa6, 0xe5, 0x9e, 0x74, 0x2e, 0x10, 0xe7, + 0x82, 0xaf, 0x67, 0x76, 0x18, 0x2a, 0x06, 0x1e, 0xc7, 0xed, 0x02, 0x9e, + 0x73, 0x65, 0x25, 0x77, 0x57, 0x3a, 0x7e, 0x4f, 0xa2, 0xd2, 0x33, 0xe7, + 0x22, 0x54, 0xf0, 0x60, 0x6e, 0x63, 0xd7, 0x32, 0x4f, 0x19, 0xa1, 0x20, + 0x72, 0x43, 0x34, 0xbb, 0x02, 0x83, 0xd5, 0xc0, 0xc1, 0x59, 0xcd, 0x3a, + 0x39, 0x8a, 0x7b, 0x5e, 0xba, 0xf0, 0x00, 0xd7, 0x0a, 0x16, 0xe0, 0xdc, + 0xcb, 0x0d, 0x93, 0xf4, 0xdc, 0xa7, 0xfb, 0x1a, 0x4d, 0x90, 0xe8, 0xbc, + 0x45, 0x03, 0x1f, 0x07, 0x97, 0x02, 0x8e, 0xac, 0x3e, 0x6e, 0xe0, 0x80, + 0xaf, 0xf1, 0x06, 0xac, 0x4a, 0x73, 0x75, 0x8c, 0x04, 0x74, 0x75, 0x52, + 0x07, 0x54, 0x34, 0x72, 0x0d, 0xe4, 0xbb, 0x1f, 0xdf, 0x60, 0x6a, 0x39, + 0x74, 0x4e, 0x95, 0x96, 0x26, 0xe9, 0xe5, 0x18, 0xda, 0x3c, 0x25, 0x7d, + 0xd0, 0xd8, 0x4a, 0x13, 0x5f, 0x32, 0x18, 0x2a, 0xc5, 0x57, 0x52, 0x36, + 0xd6, 0x9d, 0x25, 0x3c, 0x5f, 0x45, 0x45, 0xa1, 0x19, 0x11, 0x3d, 0xc6, + 0x3f, 0xa7, 0x5a, 0x0e, 0x0f, 0x4f, 0x42, 0x5a, 0x03, 0xb7, 0x3f, 0xbf, + 0x6b, 0x7e, 0x4a, 0x43, 0x1c, 0xf7, 0x2b, 0x44, 0x27, 0x3d, 0xbc, 0x52, + 0x04, 0x49, 0xb0, 0x16, 0x29, 0xc8, 0x68, 0xff, 0x50, 0xa6, 0x00, 0x3a, + 0x53, 0x19, 0x71, 0x3e, 0xc4, 0xb6, 0x36, 0x9d, 0xc3, 0x1e, 0x7a, 0xde, + 0x45, 0xbc, 0x2b, 0xc4, 0x25, 0xe2, 0x04, 0x12, 0xff, 0x00, 0xd0, 0xaf, + 0x3d, 0xa1, 0x49, 0x12, 0x3a, 0x92, 0x42, 0x71, 0xfc, 0xcd, 0x6a, 0x8d, + 0xbb, 0x0f, 0xd6, 0xe2, 0x09, 0x81, 0x51, 0x61, 0x87, 0xb6, 0xc8, 0xc4, + 0xfe, 0xe1, 0xd3, 0xef, 0xd3, 0x25, 0xd4, 0xf9, 0x00, 0xa8, 0xdc, 0x58, + 0x84, 0xe5, 0x3c, 0x68, 0xb0, 0x27, 0xd5, 0x21, 0x48, 0xad, 0x2c, 0x39, + 0x73, 0x3d, 0x24, 0xb6, 0xfa, 0xe6, 0x0e, 0xa3, 0x47, 0x5a, 0x5b, 0xc5, + 0x83, 0x03, 0xc3, 0x02, 0x7c, 0x5a, 0x30, 0xe2, 0x13, 0xef, 0x05, 0x5b, + 0xc0, 0x17, 0x3c, 0x95, 0xe6, 0x63, 0x8d, 0x00, 0x1d, 0x60, 0xf2, 0xb3, + 0x19, 0xb5, 0xc4, 0x6f, 0xe9, 0x8b, 0x9d, 0xab, 0x55, 0x52, 0x17, 0x7b, + 0xa7, 0x45, 0x5d, 0x7f, 0xe7, 0x58, 0xc9, 0x1c, 0xd7, 0x4e, 0x68, 0x1c, + 0xaa, 0x25, 0x30, 0x60, 0x27, 0x38, 0xc5, 0xfb, 0x5e, 0x67, 0x95, 0x77, + 0xd8, 0xe3, 0x24, 0x0d, 0xf3, 0x58, 0x49, 0x72, 0x5c, 0x0b, 0x9e, 0x41, + 0x7d, 0x5c, 0xbd, 0xad, 0xdb, 0x5d, 0x86, 0xbd, 0xe9, 0xd7, 0x13, 0xb3, + 0x34, 0x14, 0xd4, 0x03, 0xc0, 0x5f, 0xa8, 0x5d, 0xfc, 0x63, 0x2c, 0x4d, + 0xaa, 0x9c, 0x42, 0xaa, 0xde, 0x4e, 0xd6, 0x16, 0x8c, 0xc8, 0x85, 0x23, + 0xbe, 0x65, 0x90, 0x5d, 0xf0, 0x68, 0x47, 0x18, 0x65, 0xc2, 0x1a, 0x2b, + 0x7d, 0x8e, 0xea, 0xe2, 0x21, 0xf9, 0x66, 0xfd, 0x37, 0x16, 0xec, 0x9b, + 0x60, 0xf5, 0xcb, 0xb3, 0xe5, 0x13, 0xe1, 0x86, 0xc0, 0x40, 0x0d, 0xfd, + 0x2a, 0x6d, 0xc2, 0xd6, 0xef, 0xa0, 0x1d, 0xcf, 0xd8, 0x50, 0xd1, 0x09, + 0x59, 0x46, 0x3d, 0x92, 0x39, 0x0a, 0x3b, 0x0f, 0x2c, 0xa0, 0x28, 0x4f, + 0xf6, 0x62, 0x2b, 0x45, 0x18, 0x8b, 0xff, 0x68, 0x91, 0xec, 0xa7, 0x59, + 0x19, 0x2a, 0xeb, 0x3c, 0xe3, 0x03, 0xe7, 0x6d, 0x60, 0xc2, 0xd9, 0xf2, + 0x4a, 0x0a, 0x1f, 0xb6, 0x5b, 0x74, 0xba, 0x0b, 0x85, 0x1e, 0xe7, 0x3e, + 0x19, 0x3c, 0x69, 0x3a, 0xc6, 0x3c, 0xfc, 0x50, 0x6f, 0x13, 0x7d, 0x92, + 0xe7, 0x98, 0x73, 0xb4, 0x86, 0x41, 0x04, 0x93, 0x70, 0x81, 0x3f, 0x62, + 0x55, 0x04, 0xbd, 0x74, 0x81, 0x02, 0x60, 0xa5, 0x62, 0x8a, 0x8d, 0xd0, + 0x04, 0x03, 0x6d, 0x3e, 0x7a, 0xd8, 0xf3, 0x2f, 0x56, 0x0d, 0xab, 0x5f, + 0x57, 0x0e, 0xd1, 0x3c, 0x43, 0x93, 0x7f, 0xe6, 0x38, 0x1d, 0x6d, 0xe4, + 0x19, 0xc4, 0x11, 0xe4, 0x37, 0xeb, 0xcb, 0x06, 0xfe, 0x6e, 0x86, 0x8f, + 0x67, 0x80, 0x5f, 0x7b, 0x71, 0x69, 0xc6, 0x12, 0x22, 0xb7, 0x14, 0x4f, + 0x44, 0x54, 0xe0, 0x0a, 0xc1, 0x62, 0x6b, 0x54, 0xcf, 0xf1, 0xaf, 0x76, + 0x98, 0x59, 0x95, 0xfd, 0xc6, 0xf3, 0xfe, 0x77, 0x9b, 0x6a, 0x8c, 0x29, + 0x31, 0x82, 0xd9, 0x70, 0xee, 0xab, 0x1c, 0x91, 0x1c, 0x94, 0xa5, 0x5b, + 0x81, 0x8e, 0x62, 0x8a, 0x2c, 0xbb, 0x73, 0xbd, 0x85, 0xf8, 0xef, 0xaf, + 0xff, 0x84, 0xfd, 0x7a, 0xd1, 0x73, 0x58, 0xa9, 0x3c, 0xb1, 0x28, 0x47, + 0xcc, 0xc8, 0xf9, 0xcf, 0x29, 0xb5, 0xc8, 0x02, 0xcc, 0xda, 0x4b, 0x56, + 0x09, 0xf3, 0xfc, 0x6e, 0x5f, 0xd1, 0xe1, 0x5e, 0x7b, 0xc6, 0xc7, 0xa1, + 0x03, 0xe8, 0xdb, 0xcf, 0xfe, 0x82, 0xf2, 0x27, 0x72, 0x78, 0x21, 0x21, + 0xe6, 0xcb, 0x76, 0x04, 0x6f, 0x5f, 0x74, 0x77, 0x8a, 0x9d, 0x40, 0xbc, + 0x8c, 0x97, 0x91, 0x64, 0x20, 0x99, 0xf5, 0xee, 0xb1, 0xc4, 0xcd, 0x12, + 0x07, 0x61, 0x7f, 0xe7, 0x7d, 0x62, 0x25, 0xb8, 0x7d, 0xb3, 0xbd, 0x12, + 0x1e, 0x78, 0xc6, 0xfa, 0xbe, 0xd0, 0xa6, 0x0c, 0x88, 0xe7, 0x40, 0x33, + 0xd8, 0x19, 0xfa, 0x10, 0x75, 0x97, 0xd0, 0xa3, 0x11, 0xf3, 0xca, 0x4f, + 0x33, 0xfe, 0x1f, 0x6c, 0x06, 0x1d, 0x1c, 0x5a, 0x7f, 0x5d, 0x14, 0x71, + 0x2a, 0xc2, 0x19, 0xcc, 0x19, 0x85, 0x33, 0x00, 0x5b, 0x66, 0x35, 0xdf, + 0x3e, 0x0f, 0x28, 0xdd, 0xd8, 0x05, 0x3a, 0xc5, 0x1e, 0x4f, 0xc0, 0xb7, + 0x57, 0x16, 0x88, 0xcb, 0xdd, 0xaa, 0xcc, 0x95, 0x9f, 0xde, 0xb1, 0x05, + 0x03, 0x65, 0xf4, 0xad, 0x3d, 0x17, 0x80, 0x08, 0x33, 0x47, 0x79, 0x8c, + 0x74, 0x9b, 0x44, 0xd0, 0xe1, 0x6b, 0xd8, 0x77, 0xc6, 0xed, 0x29, 0x62, + 0xc1, 0x39, 0xdc, 0xd7, 0x8d, 0x5c, 0xb8, 0xbd, 0x3d, 0x83, 0xea, 0x4e, + 0x7d, 0x6f, 0xe5, 0x93, 0xc1, 0x6d, 0x68, 0xa0, 0x36, 0x4d, 0xca, 0xcb, + 0xd1, 0xba, 0xc9, 0x4c, 0x03, 0x52, 0xbc, 0x33, 0xed, 0xd1, 0x81, 0x28, + 0x55, 0xa6, 0xbf, 0xd5, 0x7b, 0xd4, 0x8a, 0x86, 0x59, 0x84, 0x1f, 0x0a, + 0x2b, 0x8d, 0xb7, 0x14, 0x67, 0x49, 0x4e, 0xd9, 0xc4, 0xa6, 0xee, 0xdc, + 0x6e, 0x48, 0x2d, 0xe0, 0x17, 0xc8, 0x77, 0x75, 0x28, 0x0f, 0xb6, 0x72, + 0x22, 0x61, 0x59, 0x25, 0x43, 0xcd, 0xcb, 0x74, 0x6a, 0xdd, 0x5c, 0x0d, + 0x37, 0xd9, 0x1d, 0xd6, 0xd1, 0x38, 0x7f, 0x27, 0xe4, 0xc9, 0x2f, 0x35, + 0x5f, 0x05, 0x45, 0x4b, 0x7b, 0x2d, 0x15, 0x89, 0xb2, 0xdf, 0x3d, 0x29, + 0xea, 0x40, 0xe4, 0x7f, 0x10, 0x58, 0xc0, 0x4f, 0x98, 0x78, 0x01, 0x47, + 0x2f, 0xf1, 0x5f, 0xe0, 0x8a, 0x54, 0x0a, 0x86, 0x89, 0xbb, 0x6e, 0xd8, + 0x4a, 0x11, 0x0a, 0xce, 0x8c, 0x70, 0x10, 0x0f, 0x76, 0xb1, 0xf4, 0xad, + 0x4e, 0x0f, 0xf4, 0xf7, 0x02, 0xe2, 0x37, 0xb9, 0x9d, 0x85, 0x19, 0x0a, + 0x83, 0xf4, 0x0a, 0xd6, 0xbf, 0x2c, 0x3c, 0xd5, 0x85, 0x3a, 0x31, 0x48, + 0xa5, 0x8d, 0x3f, 0x1f, 0x14, 0xc1, 0xbc, 0x6c, 0x98, 0xdf, 0x60, 0xed, + 0xf8, 0x79, 0x5f, 0xba, 0xa8, 0x05, 0x2f, 0x38, 0xf9, 0x1f, 0x0a, 0x47, + 0x26, 0x93, 0xf1, 0x90, 0xcb, 0x87, 0x95, 0x79, 0x0f, 0x7e, 0x54, 0x95, + 0x93, 0x8e, 0xdf, 0xa9, 0x43, 0xdf, 0x9c, 0x83, 0x97, 0xac, 0x48, 0xa5, + 0x79, 0xb7, 0x6d, 0xaf, 0xb8, 0x4c, 0xa5, 0xe6, 0x57, 0x9a, 0xb8, 0xd3, + 0x41, 0x0f, 0xac, 0x1b, 0x36, 0x18, 0xbd, 0xaa, 0xd2, 0x1b, 0x07, 0x96, + 0x7e, 0xfe, 0xf9, 0xf8, 0x8a, 0x16, 0xba, 0x1d, 0xdc, 0xcc, 0x89, 0x4d, + 0x78, 0x74, 0xd1, 0x7c, 0x68, 0xde, 0xfc, 0x55, 0x7f, 0xa9, 0xd8, 0xf8, + 0x8e, 0xeb, 0xa8, 0x87, 0x49, 0x29, 0x23, 0xae, 0xfb, 0xa8, 0x74, 0x5e, + 0xce, 0x8a, 0xe8, 0x2e, 0xc1, 0x9b, 0x17, 0xeb, 0xda, 0x1a, 0xe4, 0xca, + 0xe9, 0x0f, 0x89, 0xc9, 0xfb, 0xf4, 0x64, 0x61, 0x84, 0x59, 0x91, 0x9e, + 0x47, 0xb2, 0x83, 0x62, 0xea, 0x65, 0xee, 0x79, 0xd6, 0x12, 0xd1, 0xdd, + 0x29, 0xfa, 0x93, 0xfb, 0x34, 0x7a, 0x7c, 0x04, 0xfa, 0x63, 0xa8, 0x6c, + 0xcb, 0x0b, 0x9d, 0xfc, 0xc5, 0x1f, 0xe6, 0x37, 0xd3, 0x01, 0x37, 0xc1, + 0xd7, 0xac, 0xd2, 0x15, 0xc9, 0xd5, 0x8d, 0xf8, 0x33, 0xa0, 0xff, 0x6a, + 0x78, 0xc2, 0xa3, 0xcf, 0xc1, 0x9f, 0xed, 0x2a, 0x5c, 0x8f, 0x56, 0xd7, + 0xc3, 0xe6, 0xcb, 0x19, 0x60, 0x64, 0xb4, 0x0e, 0x0e, 0xd2, 0x9c, 0xab, + 0xcd, 0x79, 0xa6, 0x68, 0xd8, 0x21, 0x2f, 0xb4, 0x1c, 0x4d, 0xc6, 0xdf, + 0xb2, 0xd7, 0xae, 0xc6, 0x2a, 0xf7, 0x2d, 0x59, 0x0d, 0xe8, 0x90, 0x06, + 0x4e, 0xfc, 0x30, 0x4b, 0x44, 0x7c, 0x59, 0xfa, 0xfd, 0x8e, 0x04, 0x54, + 0x93, 0x48, 0x0c, 0x30, 0xa9, 0x1c, 0xf5, 0xe6, 0x36, 0x66, 0x45, 0xdb, + 0xc2, 0x55, 0x24, 0x3e, 0xc5, 0x73, 0x83, 0x19, 0xcb, 0x7d, 0x54, 0x55, + 0x05, 0x44, 0xb3, 0x51, 0x58, 0xde, 0x5c, 0xca, 0xda, 0x37, 0xe8, 0x77, + 0xcb, 0xf4, 0xe7, 0xf7, 0x5c, 0xfc, 0x05, 0x97, 0x9d, 0xc0, 0x37, 0xb3, + 0x38, 0x2b, 0x66, 0x05, 0xe3, 0x3f, 0xb2, 0x59, 0x07, 0x60, 0x43, 0xcd, + 0x71, 0xdb, 0x83, 0x90, 0x19, 0x7e, 0xf8, 0x2e, 0x31, 0xdf, 0x74, 0x73, + 0xda, 0x5a, 0x89, 0x90, 0xb5, 0x94, 0x7e, 0x84, 0x73, 0x62, 0xc0, 0xb1, + 0x53, 0x07, 0xc6, 0x9e, 0xbe, 0xc6, 0x2d, 0x16, 0xf6, 0xf0, 0xf4, 0x3e, + 0x3e, 0xa5, 0x85, 0xe5, 0x40, 0x74, 0x41, 0xf2, 0x33, 0xf6, 0x1d, 0xf8, + 0x30, 0xc1, 0x60, 0x17, 0xda, 0xc4, 0x05, 0x02, 0xe7, 0x72, 0x77, 0xfc, + 0xad, 0x5d, 0x01, 0x45, 0x7c, 0xfc, 0xdc, 0x0d, 0x58, 0x53, 0x14, 0x34, + 0x80, 0x78, 0x93, 0xd6, 0x5b, 0x53, 0xee, 0xc2, 0xce, 0x09, 0x13, 0xb3, + 0x61, 0xa7, 0xe8, 0xd4, 0xf6, 0x61, 0x88, 0x42, 0x30, 0xd3, 0xb4, 0x76, + 0xab, 0x17, 0x8e, 0xcd, 0xbf, 0x39, 0xff, 0xe4, 0x4a, 0x36, 0xf6, 0xfd, + 0xa8, 0xa3, 0x91, 0x48, 0xa2, 0x32, 0xd0, 0xbb, 0x4e, 0xea, 0x58, 0x18, + 0xeb, 0xa6, 0x13, 0x74, 0xe0, 0xb2, 0x27, 0xfc, 0x65, 0xd4, 0xf2, 0x0f, + 0xdf, 0x6a, 0x6c, 0xde, 0xd0, 0x38, 0xc4, 0xdf, 0x44, 0xc9, 0xcd, 0x62, + 0x43, 0x47, 0x6b, 0x2e, 0x2b, 0x4b, 0xc7, 0x05, 0xad, 0xcb, 0x26, 0xac, + 0x3a, 0xa7, 0x9d, 0xf4, 0xd6, 0xc7, 0x00, 0x05, 0x4c, 0xe4, 0xb4, 0xa1, + 0xb2, 0xbd, 0x50, 0x9a, 0xd2, 0xcb, 0xfa, 0x77, 0x52, 0x5b, 0x9f, 0xd9, + 0x90, 0x9f, 0x6e, 0x27, 0x2f, 0xc7, 0x06, 0x48, 0xac, 0x89, 0x31, 0x48, + 0xa0, 0x66, 0x25, 0x03, 0x26, 0xc5, 0x64, 0xbc, 0x8a, 0xd2, 0x78, 0x9f, + 0xb0, 0x8e, 0x7d, 0x41, 0x30, 0xc5, 0xa5, 0xca, 0x40, 0x48, 0x97, 0x3d, + 0x5a, 0x18, 0x73, 0x03, 0x73, 0xf7, 0xa3, 0x50, 0xde, 0x6d, 0xbb, 0x46, + 0x89, 0x5e, 0xae, 0xc6, 0x4b, 0xc3, 0xc4, 0x08, 0x6e, 0x54, 0xc9, 0x9d, + 0x83, 0x68, 0xbf, 0x08, 0x66, 0x0d, 0xff, 0x56, 0xf5, 0x02, 0xcf, 0x80, + 0xe5, 0xf7, 0xaf, 0xfc, 0x7f, 0x7f, 0xbc, 0x58, 0x08, 0x6b, 0xf8, 0x27, + 0x0f, 0x2c, 0xde, 0xc5, 0xb8, 0xf3, 0x0a, 0x01, 0xba, 0xe2, 0x7c, 0x5b, + 0xdd, 0xaa, 0x20, 0xc1, 0xe4, 0x8c, 0xfd, 0x77, 0xc3, 0xa6, 0x97, 0x0f, + 0xb0, 0x19, 0x53, 0x0c, 0xd0, 0xac, 0xc9, 0xba, 0x74, 0x93, 0x82, 0x6e, + 0x3a, 0x94, 0x11, 0x02, 0x74, 0x03, 0xeb, 0x09, 0x0e, 0xe5, 0x41, 0xdf, + 0xa0, 0x93, 0x49, 0xae, 0x8a, 0x30, 0xa3, 0x59, 0x3f, 0x81, 0x40, 0xe2, + 0xca, 0xac, 0xda, 0x38, 0x13, 0x37, 0xca, 0x95, 0xf0, 0xe7, 0x39, 0xa2, + 0x7f, 0x77, 0xb9, 0x9d, 0x4d, 0xe0, 0x9b, 0x2c, 0x7c, 0xc5, 0x73, 0xd0, + 0xe2, 0x98, 0x87, 0x3f, 0x9b, 0x97, 0x0d, 0x26, 0x6b, 0x06, 0x38, 0x61, + 0x25, 0xaa, 0x8d, 0x2e, 0x88, 0x7c, 0x14, 0xbe, 0x80, 0xad, 0x57, 0xbe, + 0xc6, 0xc5, 0x3e, 0xfa, 0x73, 0x0f, 0x0a, 0x68, 0x70, 0xe5, 0xed, 0x2c, + 0x08, 0x15, 0xcb, 0xbb, 0x04, 0xed, 0x9b, 0xb5, 0x87, 0x01, 0xcc, 0x07, + 0x37, 0xd6, 0x15, 0xea, 0xe0, 0x3f, 0x8d, 0x78, 0xd8, 0xbd, 0xb8, 0x94, + 0x44, 0xab, 0xdc, 0x4a, 0x03, 0xc1, 0x85, 0xe7, 0xea, 0x12, 0x0f, 0x42, + 0x2a, 0x2e, 0x32, 0xbd, 0xef, 0xce, 0x25, 0xea, 0xa8, 0xcd, 0x02, 0x33, + 0x8f, 0xf9, 0xf2, 0x87, 0x69, 0xc6, 0x76, 0xe9, 0x4b, 0x8c, 0x72, 0x94, + 0xf7, 0x35, 0xb8, 0x55, 0x88, 0xe4, 0x23, 0x5f, 0xe4, 0xa2, 0x87, 0x07, + 0x12, 0x29, 0x00, 0x8d, 0x34, 0xab, 0x38, 0xa6, 0xf4, 0x74, 0xf3, 0xb3, + 0x4a, 0x8b, 0x77, 0x40, 0xc2, 0xfd, 0x80, 0xcf, 0xad, 0x8f, 0xdb, 0x92, + 0x26, 0xc3, 0xa0, 0xaa, 0x44, 0x04, 0x59, 0xf4, 0xc8, 0x77, 0x6a, 0x80, + 0x25, 0xcf, 0xae, 0x73, 0xb9, 0x19, 0xbe, 0xeb, 0xac, 0x5a, 0xef, 0xcf, + 0x83, 0x30, 0x3d, 0x74, 0xc7, 0x53, 0x24, 0x75, 0xab, 0x8b, 0x85, 0x5b, + 0xfd, 0xa4, 0xf4, 0x5e, 0xfe, 0x2a, 0xf4, 0x9f, 0xeb, 0x82, 0x51, 0x2b, + 0x77, 0x9e, 0x8d, 0x16, 0xff, 0x3f, 0xf1, 0x27, 0xf0, 0x34, 0xd3, 0x0e, + 0x1b, 0x2a, 0xec, 0xe1, 0x88, 0x72, 0x76, 0x80, 0xfb, 0xf5, 0x9f, 0xae, + 0xc4, 0xb1, 0xa2, 0x60, 0xe3, 0xfc, 0xb3, 0x76, 0x3c, 0xec, 0x5b, 0xe2, + 0xfb, 0x1c, 0x1b, 0x33, 0x34, 0x8f, 0x31, 0x6a, 0xc3, 0x6f, 0x5d, 0xa1, + 0x1d, 0x1e, 0x56, 0x03, 0xe2, 0xbb, 0xf6, 0xaf, 0x91, 0x5a, 0x58, 0xf2, + 0x89, 0x7e, 0xcb, 0xee, 0x46, 0x5d, 0x6e, 0xa7, 0xcb, 0xf1, 0xf2, 0x12, + 0x6c, 0x65, 0x2c, 0x2e, 0xc6, 0xb2, 0x03, 0x93, 0x1f, 0x7e, 0xb7, 0x88, + 0xd9, 0x07, 0xe0, 0x7f, 0xb4, 0xfd, 0xf4, 0x63, 0x3c, 0xd7, 0xd5, 0xec, + 0x3b, 0xc7, 0x9a, 0xd4, 0xb8, 0x58, 0x38, 0xa7, 0xe0, 0x28, 0x01, 0x96, + 0xb2, 0xa5, 0x5b, 0xe2, 0x7d, 0x07, 0xa3, 0x38, 0xa7, 0xb3, 0xda, 0xfe, + 0xed, 0xfc, 0xd8, 0x54, 0x71, 0x44, 0x19, 0x49, 0xe3, 0x88, 0xe4, 0x34, + 0x65, 0x46, 0x28, 0x06, 0x1e, 0x27, 0x56, 0x74, 0xe6, 0x7d, 0x7d, 0xa7, + 0x8f, 0x4d, 0x39, 0x82, 0xa2, 0x5a, 0x85, 0xc6, 0xb0, 0xb7, 0x43, 0x24, + 0x5a, 0x21, 0x26, 0xfb, 0xd7, 0x3e, 0x22, 0x0e, 0x96, 0x57, 0xf3, 0x9d, + 0x2f, 0x70, 0xcb, 0x60, 0x70, 0xda, 0x4b, 0xf6, 0xcc, 0x96, 0x3e, 0x36, + 0x33, 0xd9, 0xde, 0xf9, 0xca, 0xb1, 0x8a, 0xd1, 0x04, 0xc2, 0x76, 0x27, + 0x12, 0x6a, 0x72, 0xf3, 0x69, 0x92, 0x26, 0x2e, 0x05, 0x05, 0x33, 0xa4, + 0x40, 0xca, 0xbe, 0x3f, 0xd4, 0x42, 0x27, 0x4c, 0xd8, 0xbb, 0xfe, 0x1a, + 0xde, 0xa3, 0xf9, 0xdf, 0x76, 0x31, 0x9f, 0x7d, 0xf9, 0x44, 0x98, 0x5e, + 0xd2, 0x3a, 0xba, 0x2c, 0x13, 0xbb, 0x90, 0x20, 0x86, 0x01, 0x18, 0xb5, + 0x8f, 0x84, 0x5a, 0xa5, 0xc7, 0x30, 0xdf, 0x69, 0xc6, 0xec, 0x82, 0xc4, + 0xa8, 0xc2, 0x08, 0x3a, 0xe4, 0x53, 0x83, 0x7b, 0x6d, 0x0d, 0xb0, 0xfd, + 0xca, 0x2b, 0x59, 0x8d, 0xcd, 0xc1, 0xab, 0xa7, 0xf1, 0x00, 0x14, 0x27, + 0xa4, 0x72, 0xa2, 0xa8, 0xfb, 0x89, 0x5f, 0xce, 0xc1, 0x79, 0x6d, 0x06, + 0x5f, 0xc6, 0x85, 0xc6, 0xa4, 0x05, 0x04, 0x66, 0xaa, 0xf5, 0x6f, 0x51, + 0xf0, 0x9b, 0xe4, 0xaf, 0x2f, 0xf5, 0xdb, 0xf2, 0xed, 0x4e, 0xfc, 0x70, + 0x04, 0x30, 0x44, 0x2f, 0x42, 0xb8, 0xa7, 0x07, 0x20, 0x19, 0xae, 0x61, + 0xeb, 0xc7, 0x0d, 0x90, 0x04, 0xa2, 0x97, 0x84, 0x0a, 0xe5, 0x44, 0x0f, + 0x9d, 0x05, 0x4e, 0x1d, 0xa3, 0x8d, 0x8e, 0x2c, 0x60, 0x47, 0xa1, 0x91, + 0x8c, 0x69, 0x0b, 0x96, 0x5d, 0x98, 0xa6, 0x99, 0xae, 0x5e, 0xb1, 0xa6, + 0xc0, 0x58, 0x38, 0x48, 0x39, 0x4f, 0x9d, 0x5f, 0x62, 0x2f, 0xa9, 0x2d, + 0xbd, 0x22, 0x17, 0x27, 0xf9, 0xa3, 0xa3, 0x9d, 0x4b, 0xc9, 0xeb, 0x31, + 0xd5, 0xd8, 0x10, 0x62, 0xf6, 0xc1, 0xbf, 0xa4, 0x1d, 0x02, 0x8b, 0xb1, + 0xb8, 0x57, 0xc2, 0x39, 0x73, 0xc7, 0x01, 0xe0, 0x66, 0xd0, 0xed, 0x86, + 0xb1, 0x31, 0x67, 0xa7, 0x57, 0x15, 0xd9, 0x21, 0xdb, 0x89, 0x8d, 0x31, + 0x4d, 0x25, 0x10, 0x04, 0x82, 0x0d, 0xd1, 0x23, 0x29, 0x95, 0x2f, 0xea, + 0xbb, 0xfa, 0x45, 0x41, 0xd5, 0x87, 0xd3, 0xf8, 0x4a, 0x74, 0x7c, 0xa9, + 0xbd, 0xe6, 0xff, 0x59, 0x60, 0x8a, 0xf5, 0x48, 0xed, 0x05, 0x26, 0xe7, + 0x93, 0xb2, 0x76, 0x11, 0x1d, 0x60, 0x87, 0x96, 0x4a, 0xc0, 0xa1, 0x98, + 0x32, 0x78, 0x1c, 0xfa, 0x9a, 0x66, 0x94, 0x9f, 0x7e, 0xc5, 0x6d, 0xad, + 0x48, 0xa2, 0x72, 0x9d, 0x81, 0x06, 0x20, 0x0b, 0x62, 0x06, 0x74, 0x96, + 0x68, 0x26, 0x57, 0x5d, 0x84, 0xa2, 0xd6, 0xb1, 0xb4, 0x69, 0xa8, 0x77, + 0x21, 0x73, 0x7b, 0x71, 0xdf, 0xbb, 0x8e, 0xed, 0xca, 0xaf, 0x26, 0xec, + 0xd7, 0xb9, 0xd5, 0x88, 0x15, 0x5e, 0x1e, 0xa3, 0x19, 0xfb, 0x59, 0xcb, + 0xa7, 0xae, 0x74, 0x71, 0x7b, 0x51, 0x21, 0xe5, 0xcc, 0xda, 0xde, 0xb8, + 0xfd, 0x85, 0xcd, 0xf6, 0xed, 0x3f, 0xa1, 0xb5, 0xfb, 0x66, 0xb5, 0xfc, + 0x86, 0x38, 0xfc, 0xa3, 0x60, 0x8c, 0xe3, 0xa6, 0xe2, 0x3d, 0xd1, 0x84, + 0x82, 0xa1, 0xc7, 0x94, 0xaa, 0xd6, 0x2b, 0x90, 0xf7, 0xf5, 0x63, 0x3f, + 0xfd, 0xe5, 0x60, 0x36, 0xfe, 0xad, 0xbf, 0xf1, 0x7c, 0x68, 0x61, 0xb3, + 0x82, 0x9b, 0xe2, 0xcc, 0x31, 0xd9, 0x8b, 0xe7, 0x73, 0xa3, 0x71, 0x0f, + 0x93, 0x61, 0xd1, 0xfd, 0x16, 0xb2, 0x8c, 0xb2, 0x7c, 0x60, 0x61, 0x32, + 0x1b, 0x07, 0xdd, 0xf1, 0xd7, 0xb9, 0x22, 0x86, 0x54, 0x25, 0x69, 0xc6, + 0x5e, 0x1b, 0x9d, 0x55, 0x25, 0x1b, 0xc7, 0x2c, 0xdf, 0x77, 0xf9, 0xb7, + 0x67, 0xf0, 0xa2, 0xa6, 0xb6, 0xb9, 0xdc, 0x8c, 0x6c, 0x7e, 0xe3, 0x75, + 0x09, 0x54, 0x0f, 0xec, 0xc0, 0xa6, 0xe5, 0x1e, 0x0b, 0x50, 0xc2, 0x6e, + 0xe4, 0xd4, 0x0e, 0x89, 0x83, 0xc2, 0xed, 0xfa, 0x59, 0x40, 0x2c, 0xdc, + 0xe1, 0xc0, 0x05, 0x12, 0xc5, 0x0c, 0x91, 0xb1, 0x23, 0x97, 0x9e, 0xc8, + 0x3d, 0xb4, 0xbd, 0x6f, 0x13, 0x07, 0x1b, 0xed, 0xd4, 0x32, 0x40, 0xb9, + 0x35, 0xb8, 0x1b, 0x05, 0x50, 0x1b, 0xcb, 0xd8, 0x9a, 0xf7, 0x70, 0x08, + 0xd2, 0x46, 0xff, 0xa4, 0xad, 0xe2, 0x46, 0xc7, 0x65, 0x1e, 0x1f, 0x55, + 0x2e, 0xab, 0x95, 0xcf, 0xa6, 0xe7, 0x2e, 0x04, 0x5b, 0x55, 0xe2, 0xa9, + 0xef, 0xba, 0xbb, 0xc9, 0xf8, 0x05, 0x14, 0x74, 0x9c, 0x69, 0x89, 0x36, + 0x48, 0xaf, 0xba, 0x0f, 0x63, 0x35, 0xe7, 0x19, 0x47, 0xa7, 0x4e, 0xbd, + 0xf2, 0x4d, 0xd3, 0x16, 0xe1, 0xa8, 0x56, 0x82, 0x84, 0xe3, 0x60, 0x06, + 0x6a, 0x5d, 0x29, 0x4c, 0xb2, 0xaa, 0x71, 0xda, 0xe2, 0x3f, 0x7a, 0xbf, + 0x31, 0xd6, 0x11, 0xaf, 0x84, 0x2d, 0xf6, 0xab, 0x0a, 0xa6, 0xc4, 0xf9, + 0x10, 0x59, 0x93, 0xa8, 0xc7, 0x51, 0x63, 0xd1, 0xc0, 0xac, 0xf5, 0x19, + 0x6c, 0xf4, 0x7b, 0x30, 0xf5, 0xb4, 0x36, 0x35, 0xb6, 0xda, 0x8b, 0x1c, + 0xcd, 0x39, 0xca, 0xad, 0xb0, 0x79, 0x9c, 0xdd, 0x3c, 0x5c, 0x17, 0x07, + 0x32, 0x42, 0x1a, 0x5a, 0x14, 0xcf, 0x66, 0x76, 0x7a, 0x6a, 0xf2, 0xd9, + 0x72, 0x61, 0x05, 0x9f, 0x38, 0xa7, 0x82, 0xc6, 0x64, 0xaf, 0x84, 0x0d, + 0x21, 0xf9, 0x4b, 0x2b, 0x08, 0x83, 0x40, 0xba, 0x4c, 0x72, 0x64, 0x43, + 0xce, 0xd6, 0xfb, 0x51, 0x48, 0x92, 0x70, 0x1e, 0xaa, 0x01, 0x40, 0xcf, + 0x56, 0xd9, 0x13, 0x30, 0xf2, 0x6e, 0xb0, 0x8f, 0x7d, 0x46, 0x0c, 0xdc, + 0x4c, 0xd7, 0xed, 0x25, 0x76, 0x52, 0x57, 0x42, 0x81, 0xc8, 0x80, 0xea, + 0x34, 0x61, 0x00, 0x13, 0xd6, 0xe0, 0x0a, 0x7f, 0x92, 0xdd, 0xe7, 0x19, + 0x69, 0x1e, 0x1b, 0x46, 0x5b, 0x3e, 0x3e, 0xdb, 0xbf, 0x07, 0x95, 0xd6, + 0x54, 0x2c, 0xcc, 0xc7, 0x69, 0x9f, 0xde, 0xda, 0x4d, 0x78, 0xd9, 0x6e, + 0xa6, 0xdd, 0x9f, 0x9a, 0x40, 0xa7, 0x38, 0x94, 0x7f, 0x66, 0x37, 0x7d, + 0x6b, 0x75, 0x14, 0x30, 0x28, 0x42, 0x0d, 0xe2, 0xc9, 0x6e, 0x2f, 0xc2, + 0xd5, 0xef, 0xe5, 0x93, 0x00, 0xdb, 0x90, 0x8b, 0xf4, 0xa1, 0x86, 0x97, + 0xc7, 0xba, 0x9d, 0xe6, 0x0a, 0x3c, 0x7e, 0x29, 0x57, 0x15, 0xf9, 0x7a, + 0xa0, 0x97, 0xcf, 0x0a, 0x9f, 0x20, 0xe8, 0xdf, 0xc9, 0xff, 0x5a, 0xca, + 0xc9, 0xa9, 0xad, 0xe9, 0x00, 0xf9, 0xca, 0x48, 0xc6, 0x88, 0x64, 0x6f, + 0x53, 0x08, 0x53, 0xf4, 0xe2, 0x16, 0x28, 0xb8, 0x31, 0x29, 0x8e, 0x19, + 0xe7, 0x48, 0x26, 0xe4, 0x01, 0xe5, 0xc0, 0xa7, 0x47, 0x21, 0xba, 0x1a, + 0x54, 0x65, 0x89, 0x3e, 0x2a, 0x97, 0x36, 0xd1, 0x69, 0x08, 0x42, 0xd5, + 0x3a, 0x1a, 0xe4, 0x59, 0xa9, 0xdb, 0x77, 0x06, 0x87, 0xed, 0xcd, 0x87, + 0xfe, 0xcf, 0xc5, 0x55, 0xf3, 0x69, 0x07, 0x35, 0x46, 0x02, 0x0b, 0x0e, + 0x99, 0xfb, 0x39, 0x58, 0xe2, 0x53, 0x6b, 0xbe, 0xde, 0xc7, 0x64, 0x30, + 0x33, 0xbe, 0xd9, 0xdf, 0x74, 0xe9, 0x75, 0x31, 0x24, 0xb3, 0x09, 0x35, + 0xf0, 0xa9, 0x30, 0x32, 0x53, 0x0c, 0x35, 0x72, 0xa4, 0x46, 0x95, 0x15, + 0xae, 0xce, 0x76, 0x19, 0x88, 0x1f, 0x38, 0xa5, 0xb4, 0x48, 0x9b, 0x04, + 0xd8, 0xe4, 0x07, 0xe3, 0xaa, 0xab, 0xed, 0x6b, 0x7a, 0x08, 0x1b, 0x38, + 0x3d, 0x78, 0x16, 0x02, 0x2e, 0x7c, 0x27, 0xa8, 0xea, 0xa9, 0xb6, 0x89, + 0x75, 0x67, 0xa9, 0x4c, 0x5c, 0x24, 0xf7, 0x1b, 0x9c, 0x26, 0x16, 0xd0, + 0x91, 0x46, 0x84, 0x18, 0x35, 0xb0, 0xe4, 0xd7, 0x11, 0x6e, 0x59, 0x5e, + 0x00, 0x2f, 0xe2, 0xd6, 0xf9, 0x36, 0x12, 0xeb, 0xab, 0xcd, 0x75, 0x71, + 0xa8, 0x0a, 0x22, 0xc3, 0x4b, 0x58, 0xde, 0xf0, 0xba, 0xd4, 0x80, 0x28, + 0x69, 0x83, 0xbd, 0x7b, 0xc7, 0x87, 0x52, 0xe5, 0x48, 0xf7, 0x76, 0xb8, + 0x89, 0xda, 0x83, 0xe5, 0x8c, 0x58, 0x39, 0xa4, 0x6d, 0xc6, 0xff, 0xd2, + 0xff, 0xf5, 0x9e, 0xd6, 0x6e, 0x1a, 0xda, 0x98, 0x1a, 0x2f, 0xf1, 0x6d, + 0x77, 0x0d, 0xe9, 0xf7, 0xd0, 0x70, 0x20, 0x1a, 0x67, 0x79, 0xba, 0x60, + 0x6b, 0xff, 0x2c, 0x3c, 0x1e, 0x39, 0xc4, 0xf6, 0x2f, 0x77, 0xde, 0x82, + 0xa2, 0x39, 0x72, 0x4e, 0xb9, 0x07, 0xa6, 0x91, 0x2b, 0x5a, 0xab, 0x46, + 0xaa, 0xed, 0x0a, 0xc3, 0x39, 0x23, 0x5a, 0x08, 0xa0, 0xa1, 0xad, 0x2f, + 0x73, 0xb7, 0x36, 0x47, 0x0b, 0x3f, 0x6c, 0xf2, 0xf0, 0xd2, 0x9b, 0x17, + 0x5c, 0x88, 0x87, 0xd6, 0x5c, 0x83, 0xb0, 0x77, 0xe6, 0xa4, 0x88, 0xc7, + 0xbe, 0x9f, 0xb9, 0xcf, 0x05, 0x37, 0xce, 0x69, 0xd8, 0x8d, 0xcc, 0xf8, + 0xf0, 0x04, 0x53, 0x15, 0x06, 0x30, 0xa2, 0xb5, 0x2d, 0x96, 0xd0, 0x57, + 0xe5, 0xa0, 0x3e, 0xde, 0xfc, 0x36, 0x9e, 0xf4, 0x14, 0xfd, 0x6c, 0xaa, + 0x57, 0xdd, 0x18, 0xf1, 0xca, 0xef, 0x79, 0x27, 0xca, 0x5a, 0xef, 0x8f, + 0xc5, 0x50, 0x3f, 0x18, 0xc2, 0x66, 0x0a, 0xe8, 0xb8, 0x3e, 0xfe, 0xab, + 0xf4, 0x16, 0xdb, 0xbf, 0xdd, 0x5c, 0x49, 0xb3, 0xbb, 0x18, 0xbc, 0xd0, + 0xdd, 0x00, 0xd0, 0xee, 0xed, 0x4f, 0x58, 0xf6, 0x10, 0x50, 0x6e, 0x27, + 0x08, 0x8d, 0x08, 0x1d, 0x98, 0x74, 0xad, 0x59, 0x3a, 0x1f, 0x65, 0xc8, + 0xc6, 0x2d, 0xd7, 0xf9, 0x43, 0x3a, 0xd3, 0x5d, 0x54, 0x2a, 0x38, 0xc4, + 0x98, 0xe7, 0xc9, 0x22, 0xdf, 0x4b, 0x2a, 0x08, 0x62, 0xea, 0x91, 0xed, + 0x4f, 0x99, 0x90, 0xa8, 0x0a, 0x01, 0xcd, 0x1a, 0xd3, 0xbf, 0x83, 0x9c, + 0x08, 0xd7, 0x20, 0x0a, 0x43, 0x1c, 0x3a, 0x6a, 0x08, 0x7f, 0xa0, 0x4a, + 0xbe, 0x73, 0x2f, 0xcb, 0x91, 0x46, 0xa1, 0xf3, 0x50, 0xe0, 0xff, 0xd6, + 0xd4, 0x4a, 0xe3, 0x06, 0xd3, 0x01, 0xaf, 0x4b, 0xaa, 0xc1, 0xdf, 0xbe, + 0x90, 0xaf, 0x39, 0xe0, 0x20, 0x67, 0x55, 0x34, 0xb6, 0x85, 0x23, 0x7d, + 0x71, 0x5a, 0x86, 0xbd, 0xe8, 0xec, 0x66, 0x08, 0x08, 0x87, 0xbd, 0xd1, + 0x18, 0x4e, 0xb0, 0x19, 0x23, 0x6a, 0x05, 0xa5, 0x6c, 0x35, 0x08, 0x97, + 0x03, 0x1a, 0x5e, 0x40, 0x8d, 0x04, 0x55, 0xd3, 0xe7, 0x61, 0x15, 0xc8, + 0xab, 0x4f, 0x7d, 0xf6, 0xb1, 0xa5, 0x47, 0xe9, 0xa7, 0xb1, 0xd3, 0x03, + 0x03, 0xe2, 0x01, 0x7f, 0x23, 0xea, 0xc1, 0x41, 0xa4, 0x62, 0x79, 0x01, + 0xcb, 0x76, 0xbc, 0x26, 0x64, 0x02, 0x06, 0xdf, 0xfc, 0x54, 0x04, 0x84, + 0x73, 0x68, 0x1f, 0x83, 0xab, 0x84, 0x8e, 0x24, 0x77, 0xd7, 0x7d, 0xfc, + 0x53, 0xe5, 0x25, 0x0d, 0x31, 0x04, 0x16, 0xba, 0x63, 0x2e, 0xa9, 0x8b, + 0xf6, 0x6d, 0x31, 0xd6, 0x6a, 0x47, 0xa9, 0x4e, 0xd9, 0x09, 0x36, 0x50, + 0xdc, 0x22, 0xc7, 0xd2, 0xda, 0xf7, 0xe8, 0x2a, 0x6a, 0x04, 0x71, 0xb2, + 0x5d, 0xf4, 0xe7, 0x3c, 0x97, 0x42, 0x35, 0x22, 0x15, 0x05, 0x0e, 0x93, + 0x94, 0xb5, 0xad, 0xac, 0x01, 0x44, 0x91, 0xe4, 0xaf, 0xc0, 0x76, 0xdf, + 0xb1, 0xb4, 0x12, 0xaf, 0xa7, 0x86, 0xcd, 0xac, 0x2d, 0x9e, 0xfa, 0x3d, + 0x11, 0x57, 0x73, 0x97, 0x03, 0x2c, 0x76, 0x82, 0x15, 0x85, 0xca, 0x47, + 0x91, 0x8b, 0xcf, 0x77, 0x13, 0xf1, 0xc6, 0xe6, 0x0f, 0xc6, 0xa6, 0xd0, + 0x29, 0xc3, 0x1a, 0x75, 0xfc, 0x61, 0x32, 0x83, 0xc6, 0x47, 0x45, 0x8e, + 0xb6, 0xf1, 0x78, 0x9b, 0x26, 0x15, 0xa3, 0x5f, 0x08, 0x7a, 0x3d, 0xd3, + 0x4f, 0xf6, 0xd3, 0x55, 0xfa, 0x9a, 0x2a, 0x3c, 0xe3, 0xfb, 0xa4, 0x87, + 0xd9, 0x1f, 0x98, 0xfb, 0x0e, 0xc5, 0xa3, 0x01, 0x04, 0x57, 0xc1, 0x1d, + 0x7f, 0x58, 0x28, 0x34, 0x05, 0x6c, 0x96, 0x92, 0x44, 0x3d, 0xe0, 0xe2, + 0x88, 0x28, 0x61, 0x53, 0xff, 0x7e, 0xe0, 0x9f, 0x30, 0x9d, 0xcd, 0xf4, + 0xa6, 0x99, 0x22, 0x3c, 0x1f, 0xef, 0x18, 0x21, 0x87, 0xd3, 0x41, 0x81, + 0xfa, 0xb2, 0xdc, 0x49, 0x42, 0xce, 0xd0, 0x64, 0x8b, 0x75, 0x1d, 0x7f, + 0xdc, 0x42, 0xc1, 0x98, 0x65, 0x31, 0xeb, 0x5e, 0x24, 0x65, 0xc2, 0x4c, + 0xcb, 0x28, 0x29, 0x4b, 0xdb, 0x51, 0x30, 0x8c, 0xde, 0x82, 0xd6, 0x94, + 0x50, 0xce, 0x4e, 0x9c, 0xa7, 0xa9, 0xab, 0xc1, 0x0e, 0x76, 0x7b, 0x25, + 0x05, 0x43, 0x5d, 0x33, 0xe6, 0x0f, 0x92, 0x20, 0xc0, 0x46, 0x65, 0x11, + 0x0b, 0xf2, 0x97, 0x7b, 0xcb, 0x61, 0x7e, 0x50, 0x13, 0x55, 0x27, 0xae, + 0x21, 0xd9, 0xcb, 0xe2, 0x99, 0x49, 0x97, 0x14, 0xc8, 0xd3, 0xef, 0x05, + 0x56, 0x3f, 0x1c, 0x54, 0xb0, 0xa5, 0xcf, 0xf6, 0x2a, 0xc0, 0x60, 0x51, + 0xfb, 0xee, 0x91, 0x49, 0x17, 0xdb, 0x1c, 0x66, 0xac, 0x8d, 0xa6, 0x46, + 0xc8, 0xad, 0xe0, 0xb4, 0x14, 0xad, 0x24, 0xe0, 0x43, 0x7c, 0xce, 0xd3, + 0x14, 0xd9, 0x47, 0xb0, 0xca, 0xed, 0x30, 0x7c, 0xe0, 0xe8, 0xe2, 0x91, + 0xa5, 0xf0, 0x31, 0x3e, 0x48, 0xb7, 0x87, 0x99, 0x8e, 0x6c, 0x4b, 0x48, + 0x91, 0x5a, 0x41, 0x05, 0x3e, 0x8f, 0x1d, 0x4b, 0x73, 0x50, 0x0a, 0xfc, + 0xcf, 0x79, 0x7c, 0xab, 0x2b, 0x60, 0xa4, 0x5b, 0x66, 0x0c, 0x8a, 0xc4, + 0x04, 0xdf, 0x44, 0x41, 0xe7, 0x59, 0xc2, 0x78, 0x3a, 0xfa, 0x05, 0x9b, + 0x49, 0xf7, 0xbf, 0x7c, 0x61, 0xa6, 0xf0, 0xcc, 0x88, 0x45, 0x4b, 0xab, + 0x81, 0x8e, 0xdb, 0xa1, 0x12, 0xa2, 0xac, 0xba, 0xeb, 0xbf, 0xa9, 0xbd, + 0xa0, 0xcd, 0x64, 0x72, 0x0b, 0x94, 0x48, 0xe4, 0x6e, 0x50, 0x3b, 0xaf, + 0x00, 0x03, 0xbf, 0x89, 0xcd, 0xdd, 0xbb, 0x9f, 0xfc, 0xee, 0xa0, 0x79, + 0x22, 0x11, 0x62, 0x75, 0x4c, 0x53, 0x50, 0x29, 0x61, 0x03, 0xc7, 0x94, + 0xa3, 0xfc, 0x61, 0xda, 0x2b, 0x86, 0x35, 0xaa, 0xd3, 0x33, 0x9a, 0xeb, + 0xcb, 0x8d, 0x3a, 0x85, 0x44, 0xd8, 0xc7, 0x3c, 0x0f, 0xfb, 0x3b, 0x61, + 0x0c, 0x7a, 0x28, 0xd7, 0xea, 0x2c, 0xf5, 0xef, 0x20, 0xff, 0xed, 0x57, + 0xdb, 0xee, 0x13, 0x2a, 0x29, 0xef, 0xce, 0xbc, 0xfd, 0x0b, 0x73, 0x07, + 0x93, 0xfb, 0xad, 0x02, 0xd1, 0xd5, 0xcc, 0x48, 0x6c, 0x2c, 0x2f, 0x02, + 0xea, 0x1f, 0xf1, 0xb0, 0x56, 0x4b, 0x50, 0xcf, 0x15, 0xe1, 0xf6, 0xfb, + 0x1f, 0x1f, 0x81, 0xc3, 0x65, 0x36, 0xe2, 0x3a, 0x7b, 0xaf, 0xe3, 0x33, + 0xbe, 0x88, 0x8f, 0x35, 0x56, 0x54, 0xf6, 0x6b, 0x7a, 0xc5, 0xd7, 0x65, + 0x7a, 0x2e, 0xd9, 0xcf, 0x17, 0xba, 0xf4, 0xb0, 0xd1, 0x6e, 0x90, 0x39, + 0xd1, 0x20, 0xdc, 0x06, 0xcd, 0x01, 0xf5, 0xe0, 0x8a, 0x3a, 0x79, 0xb0, + 0xb8, 0x42, 0xf9, 0x80, 0x91, 0xc2, 0x71, 0x9b, 0x34, 0x76, 0x16, 0x15, + 0xcb, 0xb0, 0x47, 0xab, 0x67, 0x03, 0x66, 0x7a, 0x64, 0xf2, 0xea, 0x26, + 0x86, 0xbe, 0x79, 0xb1, 0x5b, 0xd3, 0x86, 0x6d, 0x67, 0xa9, 0x3b, 0xfa, + 0x52, 0xad, 0x20, 0xea, 0x91, 0xf9, 0xea, 0x1d, 0xe1, 0x23, 0xab, 0xff, + 0x9d, 0x75, 0x86, 0xf2, 0xfc, 0x85, 0x95, 0x32, 0xec, 0x61, 0xaa, 0xab, + 0x04, 0x1e, 0xc3, 0x43, 0xb7, 0xc4, 0x8a, 0x10, 0x99, 0xf2, 0x14, 0xae, + 0xab, 0x1c, 0x36, 0x88, 0x67, 0x6f, 0xb9, 0x32, 0x18, 0x8a, 0x02, 0xa7, + 0x18, 0xc2, 0x48, 0xb9, 0x99, 0xcf, 0x5a, 0xea, 0x55, 0xcc, 0x86, 0x70, + 0x9b, 0xfc, 0x43, 0x39, 0x8d, 0xa7, 0xd4, 0xd8, 0xf6, 0x07, 0x35, 0x96, + 0xc0, 0x30, 0xf4, 0xf2, 0x15, 0xc7, 0x49, 0xc6, 0x94, 0x5a, 0x0c, 0xfc, + 0x8f, 0x6c, 0xd7, 0x95, 0xd5, 0xcf, 0xd3, 0x11, 0xa4, 0x37, 0x47, 0xae, + 0xfb, 0x44, 0x64, 0xba, 0x6d, 0xb5, 0xd1, 0x2c, 0x5c, 0x8d, 0x56, 0x07, + 0xc5, 0xd8, 0x99, 0x3f, 0xd4, 0x04, 0x01, 0x1e, 0xfd, 0xcf, 0x0d, 0xbf, + 0xe2, 0x32, 0x99, 0x47, 0x16, 0xc9, 0x46, 0x74, 0xfb, 0x0c, 0x44, 0xdc, + 0x20, 0xb7, 0xe8, 0x63, 0x73, 0x26, 0xe2, 0xa9, 0x55, 0x0b, 0xc0, 0x4c, + 0x1f, 0x46, 0x69, 0xe1, 0x06, 0xd3, 0xf1, 0x8e, 0x5b, 0xb5, 0x0d, 0x08, + 0xc0, 0x20, 0x7f, 0xec, 0xd8, 0xa6, 0xd9, 0x4c, 0x32, 0x77, 0x64, 0xc1, + 0xf9, 0x79, 0xcf, 0x65, 0xca, 0xcf, 0xcd, 0x4e, 0xbd, 0xa6, 0x97, 0xfa, + 0x19, 0x71, 0x9e, 0xfa, 0x1b, 0x94, 0x63, 0x01, 0xce, 0x50, 0xec, 0x5f, + 0x23, 0x13, 0x10, 0xdd, 0x4e, 0xd0, 0x41, 0x3a, 0xbd, 0xbb, 0x13, 0xe3, + 0xba, 0x2d, 0x11, 0xaa, 0x64, 0x7a, 0x17, 0xf7, 0x51, 0x91, 0x36, 0xa3, + 0x84, 0xeb, 0x30, 0x04, 0x9a, 0xd0, 0xb1, 0xe1, 0xe3, 0x11, 0x13, 0x67, + 0xf7, 0x31, 0x21, 0xf0, 0xa0, 0x9d, 0x03, 0x9b, 0xc2, 0x3a, 0xdb, 0xff, + 0xa3, 0xf0, 0xcb, 0x48, 0x01, 0x5f, 0xa9, 0xdf, 0xa2, 0xd3, 0xb5, 0xfc, + 0x6f, 0x61, 0xca, 0x1b, 0x5a, 0xf1, 0x25, 0xc6, 0x6a, 0x3f, 0x0c, 0x20, + 0xee, 0xb7, 0x84, 0x17, 0xdf, 0x4b, 0x39, 0x33, 0xcd, 0xde, 0x0a, 0xaf, + 0xd7, 0x65, 0x57, 0x3d, 0x67, 0x26, 0x99, 0x62, 0xc8, 0x14, 0xbf, 0xce, + 0x5f, 0xa0, 0xc2, 0x1f, 0x1c, 0x72, 0x33, 0xbd, 0x10, 0x78, 0xe9, 0x75, + 0x66, 0x1a, 0x13, 0x51, 0x6b, 0xd4, 0x81, 0xce, 0x04, 0xbf, 0x08, 0x68, + 0xd3, 0x11, 0xb3, 0x8d, 0xd5, 0x8f, 0x5b, 0x84, 0x37, 0xf9, 0x84, 0xe5, + 0x50, 0x9c, 0xee, 0x86, 0xcb, 0xc7, 0xa6, 0x6a, 0x88, 0x8a, 0x22, 0x37, + 0x34, 0xfd, 0xf5, 0x36, 0x17, 0xd9, 0xc1, 0x01, 0xad, 0xd5, 0x97, 0x7e, + 0xc6, 0xb2, 0x58, 0x3c, 0xc6, 0x6e, 0xe0, 0x60, 0xb5, 0xa2, 0x6f, 0x96, + 0xbc, 0x0f, 0x9f, 0x73, 0x71, 0xc8, 0x5f, 0x1c, 0x5e, 0x5f, 0xfa, 0x99, + 0x5e, 0x2b, 0x40, 0xc6, 0x89, 0x73, 0xd1, 0xb6, 0x3a, 0xd5, 0xd9, 0x3d, + 0x03, 0x26, 0x05, 0x29, 0xf0, 0x91, 0x30, 0xd3, 0xfc, 0xf3, 0xd8, 0xf0, + 0x76, 0x3a, 0x72, 0xa8, 0x17, 0xe3, 0x97, 0x61, 0x99, 0xbf, 0xb8, 0xc3, + 0x86, 0x81, 0x64, 0x91, 0x39, 0xbb, 0xe3, 0xd2, 0x62, 0x1a, 0x9f, 0x9f, + 0xf1, 0x78, 0x6d, 0x2b, 0xd6, 0xe3, 0x4f, 0x47, 0x73, 0x47, 0x60, 0xbf, + 0x8a, 0xcc, 0xb5, 0x12, 0x17, 0x8c, 0x49, 0x71, 0xf1, 0x4b, 0x92, 0xdc, + 0x00, 0x64, 0x2b, 0x73, 0x6b, 0x0a, 0x3d, 0x3e, 0x15, 0x5c, 0x7c, 0xe7, + 0x8e, 0x50, 0x39, 0xf2, 0x5b, 0xd4, 0x5a, 0xe8, 0x90, 0x8d, 0xb9, 0x9a, + 0x5f, 0xad, 0xb4, 0x68, 0xb1, 0x19, 0xb3, 0x74, 0x58, 0xc7, 0x03, 0xb0, + 0xb0, 0x16, 0x9f, 0x26, 0x89, 0x8a, 0xdb, 0xf8, 0x3d, 0x3f, 0xf5, 0xdc, + 0xd4, 0x28, 0x0a, 0x1a, 0x65, 0x16, 0x66, 0x72, 0xec, 0x02, 0x14, 0x9b, + 0x34, 0x43, 0xe3, 0xae, 0xb0, 0xec, 0xc9, 0xae, 0xae, 0x7e, 0x8b, 0x8e, + 0x19, 0xae, 0xed, 0x20, 0x22, 0xe2, 0x08, 0x95, 0xd1, 0xc1, 0xf8, 0xd6, + 0xaf, 0xf4, 0xf5, 0xa1, 0xf6, 0x7a, 0x39, 0x2c, 0x20, 0x5b, 0xb0, 0xf3, + 0xde, 0x9c, 0x9b, 0x73, 0x47, 0x1a, 0x59, 0x69, 0x5f, 0x42, 0x4a, 0xcc, + 0x21, 0x54, 0xce, 0x69, 0x8c, 0xe1, 0x6b, 0x5a, 0xea, 0xee, 0xdd, 0x57, + 0xae, 0x1f, 0x28, 0x1d, 0x8e, 0x74, 0xcc, 0xc9, 0x2e, 0x3e, 0x7b, 0xa3, + 0xff, 0x93, 0xaa, 0xb0, 0x5b, 0xa4, 0xad, 0x86, 0x45, 0xf5, 0x17, 0x64, + 0x0f, 0xdf, 0x49, 0xa8, 0x98, 0xc2, 0x4f, 0xdf, 0xf4, 0x4d, 0x18, 0x24, + 0x12, 0x60, 0x68, 0x86, 0x4f, 0x96, 0x9d, 0xa6, 0xfb, 0xe8, 0x9d, 0x99, + 0xd2, 0x2c, 0x30, 0x6f, 0x59, 0x2d, 0x76, 0x56, 0x3b, 0x78, 0x99, 0x0b, + 0x6f, 0xdf, 0x8e, 0x04, 0x6a, 0x34, 0xea, 0x3f, 0x3a, 0x56, 0x67, 0x71, + 0xe1, 0x19, 0xbf, 0x98, 0x55, 0x9a, 0x9d, 0xf3, 0x6e, 0xd6, 0x71, 0x2b, + 0xde, 0xd5, 0xb2, 0xe9, 0xce, 0x43, 0x11, 0x4c, 0x8e, 0x5a, 0x6a, 0x5b, + 0x3f, 0xb9, 0xec, 0x1c, 0x15, 0xbb, 0xda, 0x49, 0x51, 0x36, 0x47, 0x94, + 0x40, 0x8c, 0x3d, 0xb0, 0xff, 0xed, 0xd0, 0x32, 0x00, 0xa6, 0x73, 0x7b, + 0x2b, 0x00, 0x02, 0x89, 0xb0, 0x05, 0xfe, 0x31, 0x17, 0xa7, 0xf8, 0xea, + 0x16, 0xe0, 0x50, 0x62, 0x34, 0xae, 0x6a, 0xa2, 0x2c, 0x80, 0xe9, 0x05, + 0xcd, 0x2f, 0x32, 0x15, 0x32, 0xe2, 0xe1, 0x07, 0xae, 0x48, 0x67, 0x75, + 0x68, 0x42, 0xaf, 0x17, 0x1e, 0x6c, 0xe1, 0x93, 0x61, 0x38, 0x7d, 0x52, + 0x05, 0x0d, 0x9e, 0x03, 0x99, 0x94, 0xe4, 0x57, 0x33, 0x2a, 0x3c, 0x04, + 0x58, 0x3d, 0x34, 0xda, 0x42, 0xd8, 0x05, 0x47, 0x72, 0x75, 0x97, 0x38, + 0xbd, 0xb2, 0xe9, 0x5d, 0xd6, 0xa9, 0x82, 0x1a, 0x2a, 0x0b, 0xf9, 0xdf, + 0x06, 0xc3, 0x24, 0x56, 0x6f, 0xaf, 0x31, 0x0f, 0x6f, 0x1e, 0x15, 0xe4, + 0x91, 0xed, 0xca, 0x70, 0x57, 0x1e, 0xbf, 0x74, 0x2c, 0x7b, 0xc7, 0x8a, + 0x72, 0xa8, 0xad, 0x71, 0xf9, 0xa7, 0x4a, 0x59, 0xba, 0x9f, 0x8d, 0x8c, + 0x4a, 0xee, 0x3f, 0xe4, 0xe3, 0x84, 0xc1, 0x45, 0xb7, 0xe8, 0xbe, 0x92, + 0x93, 0xf3, 0x05, 0x6f, 0x00, 0x06, 0xa0, 0x4c, 0x4b, 0x7e, 0x3e, 0xe9, + 0x3d, 0x40, 0xaa, 0x08, 0x9a, 0xf9, 0x59, 0x10, 0xb8, 0x49, 0xce, 0x9c, + 0xae, 0x2d, 0x48, 0x5b, 0x40, 0x75, 0xb3, 0x8d, 0xd5, 0x01, 0x3e, 0xd9, + 0xb7, 0xef, 0x3e, 0x4d, 0x23, 0x6d, 0x01, 0x43, 0x74, 0xbb, 0x07, 0x7f, + 0x25, 0x35, 0xb5, 0x7f, 0x04, 0xe9, 0xe8, 0xe3, 0xa2, 0x41, 0x5f, 0x81, + 0x9a, 0x5c, 0x47, 0xd8, 0x5e, 0x56, 0xdd, 0xae, 0xc6, 0x0d, 0x3c, 0x9f, + 0x19, 0xbb, 0x1b, 0x5e, 0x08, 0xdc, 0xb3, 0x13, 0x96, 0x25, 0x40, 0x5c, + 0xe3, 0x3d, 0x50, 0xc7, 0xd1, 0xbb, 0x85, 0x52, 0x11, 0x3d, 0xe6, 0xfd, + 0x96, 0x2b, 0xf3, 0xd9, 0xff, 0x08, 0x1a, 0xc9, 0xb2, 0x56, 0x77, 0xe1, + 0x57, 0x3e, 0x20, 0x21, 0x2f, 0xfd, 0x1e, 0xff, 0x43, 0x2d, 0xaa, 0x10, + 0x8d, 0xa2, 0x51, 0x1b, 0x1a, 0x3d, 0x70, 0x4d, 0x15, 0xbf, 0x26, 0xb8, + 0xdb, 0x08, 0xa8, 0xb0, 0x74, 0x11, 0x87, 0xc8, 0x91, 0xf0, 0x9e, 0x41, + 0xdd, 0xb4, 0xc5, 0x1a, 0x3b, 0x74, 0x9a, 0xbe, 0xe5, 0xfa, 0xc3, 0xf0, + 0x46, 0x5e, 0x0e, 0xb7, 0x11, 0xa2, 0xc9, 0x50, 0x5b, 0x89, 0x80, 0xb6, + 0x2a, 0xeb, 0x46, 0x18, 0x15, 0x85, 0x3e, 0x10, 0xcc, 0xeb, 0x84, 0x78, + 0xed, 0xed, 0x0f, 0x76, 0xfe, 0xe8, 0xe4, 0x52, 0x47, 0xef, 0x7a, 0x5f, + 0x20, 0x77, 0x7f, 0x7b, 0xad, 0x8e, 0x6e, 0x5b, 0x02, 0x80, 0x31, 0xe0, + 0xc1, 0x65, 0xbe, 0x46, 0x6f, 0x0e, 0xa0, 0x6d, 0xfb, 0xb0, 0x33, 0xd4, + 0xf8, 0x8b, 0x59, 0xba, 0xeb, 0xd9, 0x12, 0xa3, 0xa4, 0x43, 0xfb, 0x4d, + 0x19, 0x03, 0x71, 0xd5, 0x08, 0x10, 0x95, 0x75, 0xeb, 0x47, 0x2e, 0x4a, + 0x4e, 0x20, 0x79, 0x7c, 0x49, 0x8b, 0xde, 0xc8, 0xe2, 0x6b, 0x83, 0xa8, + 0x1b, 0xb4, 0x62, 0xae, 0xef, 0xef, 0xa4, 0x33, 0x5b, 0x7e, 0x44, 0xa2, + 0xbd, 0xe5, 0x08, 0x0d, 0x88, 0x28, 0x65, 0x93, 0x88, 0xc1, 0xe5, 0x3e, + 0x1e, 0x94, 0xf0, 0x57, 0x29, 0xf8, 0x45, 0xfb, 0x90, 0xa5, 0x41, 0x7b, + 0xa1, 0x94, 0xda, 0x37, 0xee, 0xfb, 0x93, 0xd3, 0xcc, 0x71, 0x46, 0x60, + 0xf2, 0x21, 0xfd, 0xd4, 0xfc, 0xec, 0xf7, 0xe9, 0x68, 0xc1, 0x3a, 0x3e, + 0x66, 0x38, 0x49, 0x65, 0x26, 0x2c, 0x33, 0x48, 0x6c, 0x3b, 0x5e, 0x4d, + 0xa8, 0x8d, 0xcd, 0xa6, 0xc7, 0xbe, 0x56, 0x4c, 0xfc, 0xd3, 0xb2, 0x63, + 0x98, 0xa3, 0x2d, 0x44, 0x12, 0xb6, 0x85, 0xfa, 0x18, 0xec, 0x52, 0x2c, + 0x43, 0xe5, 0x82, 0xaf, 0xd1, 0x0e, 0x5a, 0x30, 0x09, 0x2f, 0xc1, 0x1c, + 0x3f, 0xc6, 0xf8, 0xec, 0xb8, 0xbb, 0x19, 0x0d, 0x16, 0x8e, 0xcf, 0xa8, + 0xb5, 0x15, 0xfd, 0xcc, 0x34, 0xa1, 0x66, 0x1a, 0xb4, 0x87, 0x7c, 0x5f, + 0x7f, 0xb4, 0x18, 0x44, 0x7c, 0xe3, 0x57, 0x2a, 0x36, 0xd7, 0x37, 0x18, + 0x6e, 0xe5, 0xf7, 0xf6, 0xa1, 0x48, 0xbc, 0xf6, 0xd3, 0x64, 0x75, 0x6f, + 0xee, 0xf6, 0x79, 0x46, 0x9a, 0xd5, 0x53, 0xe4, 0x15, 0x7e, 0x05, 0x93, + 0x60, 0xa6, 0x8c, 0x21, 0x77, 0xca, 0x3c, 0xdc, 0x22, 0x19, 0x02, 0x89, + 0xb7, 0x9d, 0x5a, 0xde, 0xef, 0xdc, 0xf5, 0xd6, 0x4c, 0xf9, 0x0b, 0xf4, + 0x41, 0xb5, 0x30, 0x2a, 0x8f, 0x72, 0xa7, 0x9b, 0xf5, 0x37, 0x58, 0xb1, + 0x18, 0x35, 0xf1, 0x1d, 0x25, 0x35, 0xfa, 0xb7, 0x01, 0x44, 0x80, 0x3f, + 0x3d, 0x1f, 0xe1, 0x93, 0x96, 0x89, 0x7f, 0xd7, 0xf7, 0x75, 0x69, 0x03, + 0x6f, 0x4c, 0xca, 0x81, 0x71, 0xd0, 0xbd, 0x6c, 0x78, 0x0b, 0xf8, 0xca, + 0xe1, 0x4c, 0x8b, 0xf8, 0xc7, 0xb5, 0x26, 0x1a, 0x05, 0x6f, 0x77, 0x55, + 0xc3, 0xfa, 0x76, 0x6b, 0x36, 0x6d, 0xea, 0x36, 0xdf, 0xcf, 0x1f, 0x8b, + 0x9e, 0x9f, 0x58, 0x0d, 0x87, 0x85, 0x7b, 0xe4, 0xe1, 0x0e, 0x65, 0x8b, + 0x51, 0x20, 0xe6, 0x2c, 0x77, 0xcb, 0xed, 0x54, 0xb4, 0x2a, 0x73, 0x7e, + 0xe3, 0xb0, 0xfe, 0x64, 0x25, 0x98, 0xe8, 0xb8, 0xcf, 0xae, 0x46, 0x06, + 0x7c, 0x50, 0xc2, 0x69, 0x4a, 0x0e, 0xf0, 0x66, 0xdf, 0x7b, 0x81, 0x5f, + 0x32, 0xbc, 0x1e, 0x84, 0x0d, 0x0d, 0x5c, 0xd2, 0x9a, 0x2e, 0xc6, 0x42, + 0xfe, 0x31, 0x28, 0x7e, 0xc6, 0x92, 0x1c, 0xee, 0x0b, 0xeb, 0x5e, 0x41, + 0x76, 0x80, 0xaf, 0x81, 0x41, 0x03, 0x0b, 0x14, 0x84, 0x37, 0x1c, 0x1f, + 0x0d, 0xb8, 0xb3, 0xa3, 0x40, 0xc7, 0x25, 0x94, 0xf1, 0xf6, 0x7a, 0x1e, + 0x8f, 0x74, 0x0c, 0x74, 0x6a, 0x9a, 0x7f, 0x89, 0xa3, 0x28, 0x6b, 0xbb, + 0x6d, 0xa0, 0x82, 0xa9, 0x0e, 0x33, 0x41, 0x41, 0x21, 0x9e, 0x1e, 0xe2, + 0x4f, 0x0a, 0x7b, 0xcc, 0xa8, 0xd5, 0x2d, 0x28, 0x43, 0x3a, 0x2d, 0x96, + 0xea, 0xb0, 0xd1, 0x21, 0xf5, 0x25, 0x77, 0x4b, 0x77, 0xae, 0x9d, 0x13, + 0x18, 0x62, 0x52, 0xc4, 0xd1, 0xe1, 0xe0, 0xb0, 0x32, 0x2a, 0x5a, 0x20, + 0xba, 0x25, 0x6b, 0xfa, 0x5f, 0xa3, 0x42, 0xde, 0x93, 0x26, 0x9f, 0x0e, + 0x2c, 0xee, 0x95, 0xfb, 0x2d, 0xa8, 0xd5, 0x3f, 0x7b, 0xf9, 0xd8, 0x81, + 0x96, 0x59, 0xd3, 0x05, 0xb0, 0x31, 0xfb, 0xf3, 0x2c, 0xf3, 0xb5, 0x16, + 0x17, 0x35, 0x1e, 0x80, 0xb0, 0x0e, 0xeb, 0xe4, 0x30, 0xe1, 0xce, 0xd5, + 0xc3, 0x01, 0xae, 0x11, 0xf8, 0x7b, 0x9d, 0x33, 0x8e, 0xed, 0x22, 0xfa, + 0x4e, 0x2a, 0xf9, 0x1c, 0x34, 0xa3, 0xd5, 0x39, 0x2e, 0xb9, 0x47, 0xdf, + 0x02, 0xcc, 0xd6, 0xd7, 0xf9, 0x1e, 0x33, 0x74, 0x04, 0xcc, 0x83, 0x63, + 0x17, 0x7a, 0x96, 0x3a, 0xf3, 0x87, 0xaa, 0xcd, 0xe0, 0x5f, 0x3a, 0xbc, + 0x99, 0x6f, 0x6c, 0x48, 0xb0, 0x8c, 0x28, 0x3a, 0xd1, 0xc4, 0x88, 0x86, + 0x86, 0x36, 0x93, 0x1c, 0x35, 0x4a, 0x18, 0xb3, 0xd1, 0x00, 0x0a, 0x44, + 0xb5, 0x1f, 0xc9, 0xf7, 0x56, 0xe1, 0x6e, 0xb8, 0xb1, 0xb2, 0xdd, 0x54, + 0x99, 0xdd, 0x90, 0xa8, 0xd4, 0xde, 0x8c, 0x78, 0x93, 0xfd, 0x4b, 0x4a, + 0x04, 0x51, 0xdc, 0xc6, 0x82, 0xe7, 0xba, 0x2c, 0x5d, 0x4f, 0xd6, 0x44, + 0x01, 0xfd, 0x99, 0xc6, 0x37, 0xe3, 0x23, 0x9c, 0x41, 0x2d, 0x17, 0x74, + 0xc6, 0x63, 0x3a, 0xa8, 0xb5, 0xf1, 0x48, 0x70, 0x2a, 0x7e, 0xea, 0xbe, + 0x8b, 0x34, 0x4b, 0x75, 0x1d, 0xa2, 0x7c, 0x7d, 0xe1, 0x2f, 0xe0, 0xac, + 0x56, 0xb5, 0x4c, 0x65, 0x25, 0x0a, 0xa9, 0x11, 0x7c, 0x48, 0x58, 0x6a, + 0x74, 0x4b, 0x61, 0x66, 0xd6, 0x9e, 0x0f, 0x30, 0xa4, 0x38, 0xbb, 0x4b, + 0x26, 0xce, 0xbf, 0x43, 0x3c, 0x66, 0xa6, 0xdc, 0xaf, 0x89, 0x8b, 0x5d, + 0xab, 0x86, 0xb8, 0xfa, 0xe9, 0x69, 0x38, 0xc4, 0xd7, 0x64, 0xdd, 0x37, + 0x99, 0x10, 0x8b, 0xcb, 0xa8, 0xc5, 0x3d, 0x46, 0x0c, 0xd4, 0x4c, 0x07, + 0xb6, 0x32, 0x8c, 0x64, 0xb8, 0xe1, 0x1b, 0x15, 0x63, 0x9b, 0x2f, 0x5c, + 0xe8, 0x85, 0xd7, 0xe9, 0xde, 0x87, 0xa0, 0x49, 0xf2, 0x14, 0x86, 0x7a, + 0x86, 0x47, 0x5a, 0x53, 0x0a, 0xd0, 0x88, 0x39, 0xbb, 0x28, 0x4a, 0xaa, + 0xcf, 0x77, 0xc2, 0x26, 0x14, 0x4c, 0xc1, 0x30, 0x06, 0x33, 0x36, 0x7d, + 0xdd, 0x29, 0xff, 0x53, 0x32, 0x6b, 0x54, 0x2f, 0xc9, 0x52, 0x02, 0x8f, + 0x5c, 0x13, 0x49, 0x73, 0x63, 0xcf, 0x1b, 0x65, 0xbb, 0x2c, 0x78, 0x20, + 0xd8, 0xbf, 0xcf, 0x51, 0xdb, 0xc0, 0x5b, 0xb2, 0x50, 0x25, 0xb1, 0x4e, + 0x69, 0x37, 0xec, 0xfb, 0x88, 0x53, 0x35, 0x6c, 0x1f, 0xb7, 0x7b, 0x23, + 0xcb, 0x13, 0x6f, 0x6a, 0xcf, 0x0a, 0x10, 0xc5, 0xce, 0x2c, 0xb5, 0x65, + 0xf7, 0x07, 0x3f, 0x81, 0x41, 0xee, 0xb7, 0x9d, 0xf6, 0xd8, 0x31, 0xab, + 0x44, 0x28, 0xbb, 0xf1, 0xf7, 0x52, 0x12, 0xee, 0x2a, 0x51, 0x33, 0xb5, + 0xe3, 0x8b, 0x05, 0x50, 0x58, 0x34, 0x6e, 0xc5, 0x9f, 0x69, 0x23, 0x68, + 0x05, 0x2b, 0xce, 0x98, 0x70, 0x18, 0x04, 0x87, 0xf7, 0x36, 0xb4, 0xfc, + 0x8f, 0xd7, 0xd6, 0x86, 0xf3, 0x32, 0x72, 0x07, 0x16, 0x5f, 0x54, 0x1d, + 0x51, 0xee, 0x15, 0x76, 0x01, 0xb4, 0x0c, 0x09, 0x73, 0x7a, 0xa1, 0xf6, + 0xe8, 0x12, 0xe0, 0xaf, 0xb0, 0xfc, 0xbb, 0x3e, 0x67, 0xd5, 0x51, 0xc7, + 0x68, 0x75, 0x78, 0x2b, 0x81, 0x3b, 0xb4, 0x1d, 0xa0, 0x20, 0xaf, 0x23, + 0x3e, 0x3f, 0x33, 0x62, 0xff, 0x45, 0x09, 0xeb, 0x1e, 0xcd, 0x28, 0x76, + 0xde, 0xf6, 0xea, 0x51, 0x10, 0x8d, 0xe6, 0x6f, 0x39, 0x7c, 0xe5, 0x7e, + 0x71, 0x2c, 0x2b, 0xb3, 0x04, 0xd1, 0x6c, 0x0f, 0x48, 0xf4, 0x68, 0x0f, + 0x38, 0x07, 0x59, 0xc4, 0xd9, 0x40, 0xb8, 0x8d, 0x48, 0xad, 0x2f, 0x27, + 0x23, 0xd0, 0xc0, 0x2f, 0x00, 0x6b, 0x79, 0xd0, 0xe4, 0x21, 0x59, 0x95, + 0x97, 0x54, 0x71, 0x38, 0x6c, 0x52, 0x3d, 0x08, 0xdf, 0xaa, 0x3c, 0x2c, + 0xcd, 0xac, 0xbb, 0x13, 0x7f, 0x23, 0xcb, 0x16, 0x50, 0xda, 0xba, 0x99, + 0x2f, 0xea, 0x01, 0xb2, 0xe2, 0x3c, 0x87, 0x09, 0x67, 0x6a, 0xef, 0x3d, + 0x3b, 0x1b, 0x80, 0x1e, 0xe9, 0xae, 0x6c, 0x6d, 0xdc, 0x1d, 0x56, 0x8a, + 0x3b, 0x59, 0xee, 0xb7, 0xaa, 0x09, 0x05, 0xa8, 0x3f, 0xce, 0x4e, 0xbb, + 0x71, 0xa8, 0xff, 0xe2, 0xf6, 0xb8, 0x5f, 0xd9, 0x1b, 0x73, 0xf8, 0x51, + 0x18, 0x75, 0xbc, 0x37, 0x1c, 0x9f, 0x06, 0xf4, 0xa7, 0x31, 0x00, 0xa4, + 0x8d, 0xfa, 0x4a, 0x41, 0xa7, 0x9e, 0x22, 0x72, 0xa9, 0x23, 0x0b, 0xc8, + 0xa6, 0xcb, 0xa5, 0x32, 0x36, 0x22, 0x53, 0x21, 0x67, 0x3e, 0x70, 0x10, + 0x58, 0xc1, 0x33, 0x04, 0xb3, 0x85, 0xe0, 0x42, 0xfd, 0x81, 0x7b, 0xbb, + 0xd4, 0xea, 0x23, 0xbb, 0xe2, 0x3e, 0xe0, 0x8b, 0xaa, 0x54, 0x5d, 0x6b, + 0x4c, 0xff, 0xdf, 0xc6, 0x19, 0x44, 0x7c, 0xe5, 0x04, 0x83, 0xbf, 0x0e, + 0x10, 0x72, 0x2e, 0x4c, 0x4d, 0x16, 0x37, 0x73, 0x25, 0x9c, 0xf7, 0x3a, + 0x3b, 0xc1, 0x73, 0x33, 0x40, 0x98, 0xb1, 0x0b, 0x5a, 0x50, 0x9f, 0xa5, + 0x9b, 0xc3, 0x1b, 0xbf, 0xba, 0x8d, 0x3b, 0xf7, 0xd9, 0xec, 0xcc, 0x45, + 0xf6, 0xef, 0xbd, 0x2e, 0xf0, 0xac, 0xf9, 0x41, 0x4f, 0x6d, 0xd4, 0xa1, + 0x89, 0xc5, 0x2b, 0xc7, 0x3a, 0x2f, 0xc7, 0x01, 0xc8, 0xe0, 0xe5, 0xe1, + 0xc1, 0x4e, 0x4e, 0xc0, 0x06, 0x80, 0x92, 0x74, 0x12, 0xcb, 0xf6, 0x3a, + 0xf8, 0x93, 0x0b, 0x1f, 0x4f, 0xac, 0xe5, 0xe7, 0xb8, 0x44, 0xce, 0x82, + 0x42, 0x9b, 0x93, 0xda, 0xb3, 0x1c, 0x2c, 0x49, 0x7b, 0x81, 0x39, 0x26, + 0x79, 0xfa, 0x5f, 0x84, 0xd3, 0xcd, 0x35, 0xbc, 0x92, 0x63, 0xab, 0xf5, + 0xc7, 0x56, 0x45, 0x8f, 0x33, 0xc5, 0x41, 0x63, 0x5c, 0xba, 0x75, 0x22, + 0xdb, 0xa4, 0xdd, 0x19, 0x95, 0xbc, 0x67, 0x54, 0x1f, 0x1e, 0x3b, 0xfa, + 0x5a, 0xa0, 0x2d, 0x50, 0xf7, 0x8e, 0xe5, 0xb5, 0xfe, 0xa8, 0x67, 0xf7, + 0x11, 0x6d, 0xb0, 0x85, 0x9f, 0x7a, 0x67, 0xad, 0x1c, 0x6f, 0x73, 0x1c, + 0x7c, 0x2b, 0x6b, 0x5e, 0xc4, 0xb8, 0x68, 0x6e, 0x65, 0x20, 0x21, 0x6a, + 0xd2, 0x4a, 0xc7, 0x51, 0xd4, 0x25, 0x3e, 0x1f, 0x26, 0xdc, 0x2c, 0x4d, + 0x62, 0x01, 0xf8, 0x98, 0x1d, 0xa5, 0x4c, 0x82, 0xa4, 0x3f, 0xf7, 0xf0, + 0xa2, 0x9a, 0x02, 0x56, 0xcd, 0x87, 0xbe, 0xa3, 0xe8, 0xf2, 0xd1, 0x00, + 0x74, 0x7b, 0x8e, 0xe4, 0xbf, 0x2b, 0x0d, 0x19, 0xee, 0xf8, 0xdc, 0x88, + 0x3b, 0xd9, 0x88, 0x9d, 0x80, 0x03, 0x45, 0x66, 0x99, 0x34, 0x4d, 0x3c, + 0x96, 0xda, 0x8b, 0x6b, 0x7a, 0x6e, 0x0d, 0x8e, 0x75, 0x2d, 0xe8, 0x73, + 0xa9, 0xd1, 0xaf, 0x34, 0x6f, 0x1a, 0xed, 0x1d, 0xfb, 0xc7, 0x36, 0xd0, + 0xef, 0xbf, 0x9a, 0x7b, 0x2e, 0x45, 0xfc, 0xca, 0x1f, 0x79, 0x86, 0x5c, + 0x18, 0xd5, 0xa0, 0x50, 0x65, 0xba, 0xb3, 0xbc, 0xee, 0x23, 0x60, 0x21, + 0x8a, 0x00, 0x88, 0xfe, 0x21, 0x7f, 0x8a, 0xbc, 0xa1, 0x2b, 0x45, 0x2f, + 0xda, 0xf8, 0xfb, 0x04, 0x63, 0x40, 0x98, 0xf0, 0xa0, 0xd8, 0xd4, 0xed, + 0x2f, 0xf5, 0x89, 0x1b, 0x01, 0xbb, 0x7a, 0x2c, 0x30, 0xd1, 0xbc, 0xb3, + 0x01, 0x42, 0x74, 0xc1, 0x50, 0x08, 0xfe, 0x74, 0xe9, 0x70, 0xc9, 0xe8, + 0xff, 0xb5, 0x15, 0xc0, 0xf9, 0x68, 0xf1, 0x6a, 0xa9, 0xd0, 0xaa, 0x13, + 0xea, 0xcd, 0xae, 0xfe, 0xc4, 0xb2, 0x68, 0x0b, 0x09, 0xe3, 0xb8, 0xf0, + 0x39, 0xad, 0x92, 0x67, 0x82, 0x20, 0x95, 0x98, 0x7c, 0xf0, 0x73, 0x81, + 0xb8, 0xcf, 0x6c, 0xab, 0x03, 0x9c, 0x6d, 0x08, 0x35, 0xd9, 0x05, 0x05, + 0x96, 0x7d, 0x63, 0x13, 0x81, 0x42, 0x6f, 0x28, 0xb7, 0xbe, 0x9f, 0x52, + 0xb2, 0xe7, 0x44, 0xf0, 0x1a, 0x94, 0xfa, 0x86, 0xeb, 0x07, 0xfd, 0x8d, + 0x30, 0x97, 0x79, 0x71, 0xc2, 0x8d, 0x4c, 0x9a, 0x4d, 0x21, 0xf2, 0x1d, + 0xb4, 0xc6, 0x9c, 0x4a, 0x57, 0x6e, 0x4e, 0xe9, 0xc1, 0x86, 0x8d, 0xc9, + 0x86, 0xfb, 0x66, 0x27, 0x02, 0xf7, 0x26, 0xc7, 0x01, 0xaa, 0x1f, 0x99, + 0x9c, 0x20, 0x32, 0xfa, 0xc6, 0x1b, 0xcf, 0xbd, 0x49, 0x5e, 0x47, 0x7d, + 0x5e, 0x19, 0x8e, 0x66, 0x16, 0x3b, 0xca, 0x99, 0xb2, 0xcd, 0x23, 0xd7, + 0xaa, 0xef, 0x15, 0x6f, 0x94, 0x55, 0xdb, 0xd5, 0xaa, 0x22, 0x42, 0xea, + 0x0f, 0x1b, 0x79, 0x54, 0x32, 0x23, 0x81, 0xcb, 0x54, 0x66, 0x3a, 0x53, + 0x2a, 0xb7, 0xfa, 0x3a, 0xf2, 0x60, 0xc9, 0xcf, 0x59, 0x20, 0xc2, 0x6a, + 0xef, 0x8e, 0x5e, 0x41, 0x6a, 0x1b, 0xbe, 0xc6, 0x38, 0x1c, 0x47, 0x76, + 0xc4, 0x55, 0x89, 0xe7, 0xb7, 0x6a, 0xbc, 0xc1, 0xda, 0xa4, 0x6a, 0x6b, + 0xa0, 0x5b, 0x7b, 0xdb, 0x64, 0x25, 0x81, 0x1b, 0xc4, 0xb2, 0xb3, 0xba, + 0x36, 0x61, 0x90, 0x40, 0x6d, 0x9d, 0xc6, 0x2b, 0x50, 0x44, 0x22, 0x76, + 0x03, 0x51, 0x4f, 0xb1, 0x34, 0x12, 0xf6, 0xef, 0x5a, 0x77, 0x33, 0x22, + 0x1b, 0x70, 0xa6, 0x30, 0x54, 0xab, 0x07, 0xa5, 0x6a, 0x62, 0x2a, 0x24, + 0xc9, 0x5b, 0xad, 0xf5, 0x10, 0x41, 0x8d, 0xbc, 0x91, 0x06, 0x04, 0x31, + 0x76, 0xba, 0xd2, 0x81, 0x41, 0x28, 0x98, 0x94, 0x9f, 0x48, 0x73, 0xf9, + 0xf8, 0x2e, 0xb2, 0x44, 0x48, 0xa6, 0x20, 0x22, 0xc4, 0x40, 0x44, 0xb6, + 0xe6, 0x43, 0x1e, 0x3c, 0x4d, 0xaa, 0x30, 0x57, 0x34, 0xba, 0xb6, 0x34, + 0xd4, 0xa2, 0xf2, 0x30, 0x3f, 0xcb, 0x4e, 0x7b, 0x3f, 0x80, 0x33, 0x05, + 0xe9, 0x4c, 0xa1, 0x49, 0x99, 0x4a, 0xe1, 0x2d, 0x1b, 0xa2, 0x48, 0x5e, + 0x7b, 0xe5, 0x4d, 0xd0, 0x30, 0xf2, 0x1b, 0x74, 0xf6, 0xd9, 0xc1, 0x17, + 0xf3, 0xc3, 0xd3, 0x36, 0x40, 0x9d, 0xf9, 0x90, 0x34, 0xc5, 0x32, 0x2e, + 0x9a, 0x5e, 0x19, 0xbb, 0x5b, 0x9a, 0x4d, 0x95, 0xef, 0x06, 0x4a, 0x83, + 0xda, 0x9f, 0x4d, 0xfa, 0xb6, 0xe3, 0x04, 0x88, 0x8c, 0x04, 0x31, 0x54, + 0x00, 0x28, 0x38, 0xdc, 0xeb, 0x9b, 0xc5, 0xe2, 0x28, 0x79, 0x9f, 0x97, + 0xb0, 0xf7, 0x58, 0x0e, 0xc6, 0x3f, 0xd3, 0xbc, 0xf8, 0x3b, 0x29, 0xde, + 0xce, 0x7f, 0xfc, 0xef, 0x5f, 0x13, 0x1d, 0xdd, 0xcc, 0x8d, 0x04, 0xe7, + 0x18, 0x24, 0x59, 0x8f, 0xf2, 0x68, 0x36, 0x49, 0x19, 0x72, 0x02, 0xfc, + 0xc9, 0x71, 0x5b, 0xed, 0xe4, 0xd2, 0x69, 0xf9, 0xb6, 0xa4, 0xec, 0xca, + 0x47, 0x7a, 0x17, 0xf7, 0x59, 0xe5, 0x07, 0x8a, 0xed, 0xe5, 0xba, 0x30, + 0xf8, 0xad, 0xcb, 0xca, 0xf9, 0x57, 0x29, 0x03, 0xac, 0x2a, 0x62, 0x44, + 0x68, 0x7d, 0xcd, 0x9c, 0x3d, 0x21, 0x09, 0xc4, 0xc0, 0xa8, 0x79, 0x6b, + 0x77, 0xf7, 0x31, 0xe8, 0x68, 0xbb, 0x3b, 0x2e, 0x80, 0x94, 0x71, 0x32, + 0xad, 0x1c, 0x5f, 0x31, 0x72, 0xa4, 0x23, 0x7a, 0x34, 0x11, 0xe4, 0xd7, + 0xfb, 0x8c, 0xaf, 0x3b, 0x39, 0xfa, 0xdf, 0xdb, 0x1d, 0x17, 0xea, 0x68, + 0x14, 0xff, 0xcf, 0x10, 0xa3, 0x5b, 0x41, 0xbb, 0x00, 0x6b, 0xd5, 0x15, + 0x39, 0xca, 0xef, 0x07, 0xf7, 0x4d, 0x7a, 0x2e, 0x61, 0xa9, 0x2b, 0xce, + 0x3a, 0xb1, 0xfb, 0x90, 0xfb, 0x5c, 0xcb, 0x85, 0x2d, 0x0c, 0x83, 0xd5, + 0x38, 0xd3, 0x03, 0xab, 0xc4, 0x83, 0xf1, 0x85, 0xf0, 0x78, 0x80, 0x26, + 0xd1, 0x17, 0x65, 0x5f, 0xd6, 0x3a, 0x2b, 0x9d, 0x21, 0x44, 0x6d, 0xcd, + 0x57, 0x49, 0xbc, 0xed, 0xf0, 0x8e, 0xde, 0x28, 0xc5, 0x96, 0x5a, 0xd2, + 0x1e, 0x7e, 0x35, 0xe1, 0xa9, 0xdf, 0xb5, 0x1b, 0x49, 0x5c, 0x19, 0x4c, + 0x87, 0x8b, 0x1a, 0x7b, 0x81, 0x0c, 0x9c, 0xb1, 0x1f, 0x69, 0x20, 0xf1, + 0x99, 0x51, 0xb5, 0x52, 0x9f, 0x2d, 0xc4, 0x5a, 0x7e, 0x40, 0xa2, 0x71, + 0x65, 0x45, 0xb7, 0xca, 0xc1, 0xb5, 0x97, 0x51, 0xc9, 0xb2, 0xc4, 0x43, + 0x9f, 0x5c, 0x62, 0xa3, 0x78, 0xb1, 0x17, 0x25, 0xba, 0x6f, 0x11, 0x95, + 0x78, 0x0a, 0xec, 0x6f, 0xc6, 0x9f, 0xf8, 0xad, 0xa7, 0xe7, 0x24, 0xf5, + 0x34, 0xb5, 0x55, 0x37, 0x8d, 0xb5, 0x91, 0x4a, 0x69, 0xa3, 0xaf, 0xc3, + 0xe0, 0xeb, 0x41, 0xff, 0xa1, 0xd5, 0x81, 0x41, 0x5e, 0x24, 0x97, 0xc7, + 0x60, 0xde, 0x01, 0x9f, 0x61, 0x78, 0x0a, 0xcf, 0x4f, 0xe2, 0xd0, 0x3a, + 0x3a, 0xb8, 0x82, 0x33, 0xff, 0x93, 0x95, 0xea, 0xaa, 0x71, 0x41, 0xaf, + 0x06, 0xee, 0xb8, 0xe5, 0xd6, 0x1d, 0x76, 0x68, 0x96, 0x0d, 0x8b, 0xf8, + 0x46, 0xe0, 0x4c, 0x46, 0xfe, 0xcb, 0x62, 0xa3, 0xf2, 0x5e, 0xe5, 0x9e, + 0xf7, 0x95, 0xff, 0x4f, 0x64, 0xb9, 0x80, 0x88, 0x84, 0x2c, 0xfc, 0x1e, + 0x77, 0x05, 0xee, 0x63, 0x14, 0xb6, 0x17, 0x7f, 0x6a, 0x72, 0x28, 0x50, + 0x62, 0x67, 0xb8, 0x37, 0x85, 0x61, 0x9d, 0xbb, 0xeb, 0x02, 0x10, 0x16, + 0x0b, 0x98, 0xec, 0xa9, 0x12, 0x52, 0xeb, 0x56, 0xa7, 0xc0, 0xc0, 0xe3, + 0x0f, 0xbf, 0xc7, 0x59, 0xd4, 0x0c, 0x35, 0xed, 0x72, 0x9e, 0x5a, 0xce, + 0x47, 0x7c, 0xcf, 0x6d, 0x49, 0x95, 0x1a, 0x68, 0x3a, 0x83, 0x16, 0x54, + 0xf9, 0x88, 0x8b, 0x51, 0x44, 0x89, 0x31, 0x1f, 0x51, 0x67, 0xa9, 0xfc, + 0xf8, 0x3f, 0x98, 0xc0, 0xbf, 0xd7, 0x3c, 0xf2, 0xda, 0x0d, 0x37, 0x1c, + 0xa7, 0xd1, 0x3f, 0xbf, 0xa3, 0x3a, 0x5d, 0xf5, 0x0a, 0x01, 0xef, 0x74, + 0x69, 0x53, 0xa9, 0x29, 0x90, 0x1e, 0x57, 0xbb, 0xbf, 0x97, 0x02, 0x45, + 0xee, 0x6f, 0x59, 0x2e, 0xa2, 0x9c, 0x6a, 0x4f, 0xac, 0x65, 0xb0, 0xee, + 0xc5, 0x52, 0x8d, 0xc5, 0xbe, 0xe5, 0x81, 0x21, 0x04, 0x87, 0x00, 0xfe, + 0x78, 0xe3, 0x40, 0x53, 0x6f, 0xa1, 0x6b, 0x48, 0x9f, 0xb0, 0x1e, 0x93, + 0xa5, 0xb4, 0xfe, 0x84, 0x8b, 0x9c, 0xde, 0xf2, 0xb7, 0xbe, 0x17, 0x28, + 0x43, 0x52, 0x0e, 0x35, 0xc3, 0x0a, 0xd0, 0x6f, 0x37, 0xdf, 0xda, 0xf9, + 0x9e, 0x4d, 0xc7, 0x15, 0xce, 0x7c, 0xd4, 0x63, 0x0e, 0xfd, 0xe7, 0xfa, + 0xea, 0x05, 0x9e, 0x46, 0x39, 0x57, 0x4d, 0x16, 0x4a, 0xaf, 0x20, 0xe2, + 0x3e, 0x17, 0xe2, 0xea, 0x72, 0xbf, 0x3a, 0xe8, 0xcf, 0x78, 0xe7, 0xcc, + 0xee, 0x88, 0xf9, 0x24, 0x2e, 0x88, 0xef, 0x4c, 0x2c, 0x60, 0x32, 0xa8, + 0x03, 0x72, 0x03, 0xa5, 0x0e, 0x66, 0xe2, 0x25, 0x89, 0x0d, 0x8a, 0xc0, + 0xcd, 0xbb, 0xa4, 0x8d, 0x2e, 0x39, 0xa7, 0x16, 0xd9, 0xa5, 0xe5, 0x54, + 0x19, 0x11, 0x0b, 0x30, 0xb4, 0x34, 0x54, 0x40, 0x13, 0x93, 0x07, 0xff, + 0xf1, 0x05, 0xb9, 0x2c, 0x48, 0x27, 0xd1, 0xc5, 0xaf, 0x43, 0xe3, 0xab, + 0xcc, 0x12, 0xca, 0x34, 0x74, 0x63, 0xac, 0x8e, 0xf9, 0x16, 0x8d, 0xa0, + 0xdb, 0xfd, 0xbe, 0x6d, 0xb2, 0xd7, 0xdb, 0x48, 0xc5, 0xbe, 0x86, 0x1e, + 0x07, 0x01, 0xd8, 0x3c, 0x6d, 0xf3, 0xc3, 0xd7, 0x3d, 0x68, 0xcf, 0xe8, + 0x0d, 0x2d, 0x25, 0xca, 0x3c, 0x45, 0x34, 0x9a, 0x3f, 0x56, 0xa1, 0x96, + 0x6e, 0x3b, 0xee, 0xa7, 0xba, 0x4f, 0xe3, 0xa4, 0x79, 0x55, 0xdd, 0xce, + 0x9c, 0x58, 0x08, 0xc7, 0x35, 0x36, 0x7a, 0xde, 0x3e, 0xbe, 0x9e, 0x4b, + 0xda, 0x29, 0xad, 0x22, 0x6c, 0x2a, 0x60, 0x71, 0xb3, 0x5b, 0xec, 0x57, + 0x6f, 0x8e, 0x49, 0x67, 0xd5, 0x06, 0x1f, 0xce, 0xa0, 0xe5, 0xa7, 0x35, + 0x9e, 0x9b, 0x58, 0xbb, 0x0f, 0xe1, 0xe9, 0x75, 0xee, 0x92, 0xa4, 0xfa, + 0x9f, 0xa2, 0x0d, 0x1e, 0x1a, 0x49, 0x5f, 0x4c, 0xc6, 0xc9, 0xe5, 0x1e, + 0x66, 0x0a, 0xcc, 0x33, 0x75, 0xec, 0xe6, 0x48, 0x3d, 0xd6, 0x52, 0xe2, + 0x54, 0x70, 0x9d, 0x15, 0xf3, 0x93, 0x29, 0x64, 0x39, 0x98, 0x47, 0x08, + 0x38, 0x02, 0x0a, 0xd6, 0xfd, 0x39, 0xf6, 0x07, 0x3d, 0x1d, 0xcd, 0x51, + 0x98, 0x6f, 0xbf, 0x74, 0x97, 0xd5, 0xd8, 0x95, 0x66, 0x27, 0x59, 0x32, + 0x4b, 0xcd, 0x1b, 0x95, 0xfa, 0x48, 0x69, 0x57, 0x91, 0xbe, 0x8b, 0xeb, + 0xc9, 0x5a, 0x48, 0x9b, 0xed, 0xbb, 0xdd, 0x86, 0xca, 0x1b, 0x55, 0x32, + 0xd4, 0x7a, 0x90, 0xd0, 0x63, 0xb1, 0xca, 0x61, 0x1d, 0x68, 0x5b, 0xf1, + 0xf0, 0xf7, 0xd8, 0xf8, 0x2c, 0x21, 0xc0, 0x61, 0x90, 0x8d, 0x10, 0x92, + 0x15, 0x65, 0xe2, 0xeb, 0xd8, 0x90, 0x2c, 0xa7, 0x40, 0x17, 0xed, 0x9f, + 0x69, 0x89, 0x38, 0xb3, 0xf2, 0xe4, 0x12, 0x60, 0xd3, 0xfb, 0x57, 0xfc, + 0xdd, 0xac, 0x15, 0x27, 0xe7, 0xfd, 0x13, 0xb1, 0xeb, 0x10, 0x21, 0xeb, + 0x21, 0x3a, 0xc4, 0x89, 0x0e, 0x4d, 0x43, 0xfa, 0xec, 0x6a, 0xd7, 0xa9, + 0x81, 0x74, 0x65, 0x90, 0xfe, 0xef, 0x7f, 0xef, 0x6c, 0xe0, 0xec, 0x8d, + 0x00, 0x57, 0x75, 0x20, 0x9e, 0x8b, 0xd4, 0xbc, 0x7d, 0x87, 0xad, 0x5c, + 0xba, 0x1b, 0x8d, 0xce, 0x9e, 0xf0, 0x77, 0xb4, 0xa5, 0x81, 0xe9, 0x0d, + 0x06, 0xf5, 0xab, 0x53, 0x39, 0xf0, 0x96, 0x4c, 0xd2, 0x3d, 0x2d, 0xff, + 0xc9, 0xa2, 0x48, 0x02, 0x28, 0x98, 0xc6, 0xa9, 0xfc, 0xc0, 0x1d, 0xbc, + 0x98, 0x6d, 0xb1, 0xe3, 0x63, 0x29, 0x5a, 0x95, 0x5a, 0x2d, 0x4a, 0x01, + 0x37, 0x23, 0x97, 0x47, 0x32, 0xb3, 0xf9, 0x14, 0x01, 0x06, 0xcd, 0xf0, + 0x6f, 0x31, 0xdf, 0x52, 0x9d, 0x3b, 0xaf, 0x6d, 0x6f, 0x3f, 0xd4, 0x57, + 0xd4, 0x76, 0x64, 0xc9, 0x21, 0x17, 0x63, 0xb4, 0xb2, 0xcb, 0x76, 0xfc, + 0x8a, 0x9f, 0xec, 0x76, 0x99, 0x24, 0x56, 0x55, 0xdf, 0x9a, 0xbe, 0x20, + 0xdb, 0x31, 0x96, 0xbf, 0x24, 0xec, 0xb0, 0x91, 0xa8, 0x15, 0x03, 0xe4, + 0x4e, 0x59, 0x9b, 0x1b, 0x9b, 0x32, 0xe7, 0x92, 0xbb, 0xd2, 0x24, 0x27, + 0x8c, 0x85, 0x70, 0x96, 0x49, 0x17, 0x89, 0x5b, 0xac, 0xf4, 0xa8, 0x32, + 0x74, 0xa6, 0x90, 0x56, 0x3b, 0xea, 0x63, 0x66, 0x04, 0x14, 0xad, 0x35, + 0xee, 0x44, 0x3b, 0xa9, 0x7e, 0xa5, 0x6d, 0xdb, 0xb3, 0xa3, 0x2e, 0xe2, + 0x41, 0xde, 0x7f, 0x16, 0xb2, 0xa1, 0x62, 0x15, 0xe8, 0x14, 0xad, 0x7c, + 0xd3, 0x7c, 0x02, 0x3e, 0x35, 0x20, 0x76, 0x37, 0xaa, 0xc6, 0xd0, 0x5d, + 0xf7, 0x30, 0x52, 0x2f, 0xa2, 0xfd, 0xa7, 0x7b, 0xf7, 0xe7, 0x37, 0xc1, + 0x7e, 0xdb, 0x9b, 0xc1, 0xec, 0x0b, 0x5e, 0xd1, 0x9b, 0xa5, 0x27, 0x92, + 0xbe, 0x1c, 0xea, 0x31, 0x3e, 0x56, 0x7b, 0x5a, 0xba, 0xb9, 0xb4, 0xc0, + 0xfd, 0x59, 0x6d, 0x4b, 0xb3, 0x51, 0xa2, 0x7c, 0x77, 0xc8, 0xbc, 0x9b, + 0xd1, 0x19, 0x78, 0x92, 0xd2, 0x02, 0x63, 0xe8, 0x1a, 0x87, 0xd7, 0x6f, + 0x88, 0xd1, 0x04, 0x33, 0x8a, 0x4e, 0x92, 0x8f, 0x9c, 0x83, 0x99, 0x1e, + 0x6f, 0x7f, 0xb0, 0x23, 0x89, 0x71, 0x9b, 0x5f, 0x0d, 0xb7, 0xb0, 0x93, + 0x34, 0x06, 0xee, 0x13, 0x97, 0xc8, 0x04, 0x0d, 0x60, 0x02, 0xbc, 0xa3, + 0x89, 0x6f, 0x8a, 0x42, 0x7a, 0x5f, 0xe0, 0xd8, 0x19, 0x2c, 0x06, 0xdb, + 0x2c, 0xc3, 0x63, 0x8a, 0x28, 0x7a, 0x51, 0x02, 0xf2, 0x74, 0xf9, 0xfb, + 0x77, 0x72, 0x69, 0x32, 0xd9, 0x25, 0xe8, 0x29, 0xd2, 0x21, 0xf5, 0xed, + 0x74, 0x8d, 0x5f, 0xac, 0xf2, 0x42, 0x0f, 0x62, 0x37, 0xb4, 0x8c, 0x45, + 0xa5, 0x70, 0x08, 0xd9, 0x4a, 0xec, 0x54, 0x86, 0x16, 0x40, 0x6a, 0xf8, + 0xa8, 0x0e, 0xda, 0xfd, 0xa3, 0xa8, 0xc4, 0xb4, 0x60, 0x37, 0xae, 0xd4, + 0x10, 0x56, 0x53, 0xe7, 0xef, 0x3a, 0x0c, 0xb4, 0xf4, 0x01, 0x04, 0x76, + 0xd6, 0x8d, 0x27, 0xac, 0xeb, 0xd7, 0x86, 0xd2, 0x95, 0x66, 0x3c, 0x26, + 0x8f, 0xee, 0x99, 0xa1, 0x6e, 0xdd, 0xa2, 0x62, 0x51, 0x4e, 0xa3, 0x8f, + 0xe5, 0x85, 0xff, 0xc8, 0x4a, 0xad, 0xb9, 0xc1, 0xcd, 0xbb, 0xa8, 0xf4, + 0xab, 0x26, 0x82, 0x6b, 0xd0, 0xbc, 0x95, 0xe9, 0xb8, 0x05, 0xa8, 0x88, + 0x94, 0xf5, 0x8f, 0xf5, 0x87, 0x3b, 0x45, 0x96, 0xb3, 0x81, 0xbe, 0x87, + 0xff, 0xef, 0x49, 0x57, 0xf9, 0x63, 0x23, 0xcf, 0xc2, 0xd1, 0x3d, 0x4f, + 0xa9, 0x25, 0xb4, 0xf3, 0x29, 0xef, 0x46, 0x9d, 0x86, 0x02, 0x5e, 0x16, + 0x28, 0xcf, 0x8e, 0xbc, 0x1b, 0x36, 0xb7, 0x73, 0xad, 0x4f, 0x73, 0xd1, + 0x5e, 0x2e, 0xcf, 0x67, 0x5d, 0x2c, 0x54, 0x66, 0x8b, 0xfa, 0xd9, 0x6b, + 0x3d, 0x0c, 0x2b, 0x9c, 0x8c, 0x31, 0xd2, 0x07, 0x15, 0xe5, 0x80, 0x4f, + 0x2e, 0xe0, 0x2d, 0xcd, 0x98, 0x9b, 0x6b, 0xdc, 0x65, 0x98, 0xa6, 0xc4, + 0x9e, 0xbb, 0x17, 0x19, 0x80, 0xa3, 0x1e, 0xd6, 0x59, 0x4a, 0x1c, 0x90, + 0xca, 0xbe, 0xd8, 0xc2, 0x40, 0xbe, 0x94, 0x18, 0x60, 0xac, 0x29, 0x0a, + 0xee, 0x4e, 0xdc, 0xba, 0x3b, 0x6f, 0x22, 0x7d, 0x63, 0x22, 0xd4, 0xbf, + 0x08, 0x53, 0x8c, 0xd0, 0x32, 0xe4, 0x6a, 0x2a, 0x9d, 0x3f, 0x05, 0xf0, + 0x06, 0x20, 0x0d, 0x06, 0xd8, 0x7f, 0xdb, 0xad, 0xa8, 0x91, 0x01, 0xd7, + 0x8e, 0x46, 0x78, 0xfb, 0x61, 0x6f, 0x77, 0x1c, 0xb6, 0x81, 0xfd, 0xfc, + 0x5f, 0xfb, 0x41, 0x21, 0x3e, 0x27, 0xee, 0x4a, 0xc5, 0xc4, 0x94, 0x3d, + 0x68, 0x97, 0x7c, 0x94, 0x66, 0x6a, 0xea, 0x80, 0x61, 0x4e, 0xc8, 0xcb, + 0xa9, 0x0c, 0xff, 0xd2, 0x53, 0x34, 0xbb, 0xfb, 0xe0, 0x4d, 0x14, 0x2a, + 0xc8, 0x64, 0x92, 0x9d, 0x66, 0xc5, 0x0a, 0x54, 0x79, 0x93, 0xfe, 0x6c, + 0xb7, 0x58, 0x34, 0x0a, 0x50, 0x4d, 0xf2, 0xf5, 0xab, 0xed, 0x19, 0xdb, + 0xf2, 0xac, 0x4b, 0xe5, 0x00, 0xd3, 0x53, 0xdc, 0xa5, 0xe2, 0x99, 0xe0, + 0x93, 0xc0, 0xd2, 0x5d, 0x6f, 0x06, 0x13, 0x87, 0x20, 0x89, 0x69, 0x18, + 0x3e, 0x40, 0xe0, 0x8c, 0x2d, 0x57, 0x74, 0x40, 0xe4, 0xc5, 0x24, 0x79, + 0xe4, 0x34, 0xa7, 0x1c, 0xa0, 0xe8, 0xe9, 0x9d, 0x40, 0xcc, 0xb5, 0xc1, + 0xcb, 0x2b, 0x3d, 0x53, 0x71, 0xf7, 0xca, 0xc3, 0x39, 0x12, 0x30, 0xcf, + 0xdb, 0xe0, 0x59, 0x25, 0x32, 0x39, 0x1c, 0x57, 0xa5, 0xbb, 0x1d, 0x6a, + 0xd1, 0xf5, 0xad, 0x00, 0x29, 0xb2, 0x01, 0x96, 0xfd, 0x02, 0x2c, 0xeb, + 0x27, 0xbd, 0x03, 0x8d, 0x09, 0x53, 0x31, 0xe9, 0xa7, 0x26, 0x2e, 0x92, + 0xa7, 0xcf, 0xba, 0x20, 0x64, 0xdc, 0xce, 0x32, 0x3e, 0x60, 0x4b, 0xa4, + 0xe4, 0x5c, 0xe1, 0x8e, 0x31, 0x75, 0x1c, 0x52, 0x00, 0xb2, 0xcf, 0xf5, + 0x60, 0x6c, 0x9e, 0xdd, 0x55, 0xc7, 0xdd, 0x42, 0xb9, 0x93, 0x14, 0xb6, + 0x35, 0xb4, 0xd6, 0xff, 0xd0, 0x4e, 0xca, 0xca, 0x84, 0x9f, 0x9b, 0x1f, + 0x26, 0x06, 0xda, 0xb0, 0x69, 0xaa, 0x54, 0x31, 0xe5, 0xdf, 0xd3, 0x73, + 0xdc, 0xa3, 0x8a, 0x56, 0x1b, 0x93, 0x6e, 0x85, 0x21, 0xd0, 0xb8, 0xbc, + 0x6b, 0x17, 0xb7, 0xf0, 0xe5, 0x06, 0x88, 0xe4, 0x41, 0xf5, 0x8f, 0x6c, + 0xca, 0x5f, 0xfd, 0x60, 0x63, 0x51, 0xdd, 0x3f, 0x0f, 0x46, 0x3d, 0x1d, + 0x61, 0xbf, 0xc1, 0xe9, 0x37, 0x16, 0x6c, 0x47, 0x59, 0xa4, 0x90, 0x92, + 0xe6, 0xe0, 0x16, 0xcd, 0x4a, 0x12, 0xcc, 0xd9, 0x5b, 0x1a, 0x3c, 0x3c, + 0x20, 0x83, 0x3a, 0x12, 0x6f, 0x7b, 0x1d, 0x4d, 0x1a, 0xb6, 0x59, 0xe0, + 0x16, 0xab, 0x4a, 0x9e, 0x43, 0x1f, 0xa6, 0xd5, 0x35, 0x5a, 0x91, 0xd6, + 0xcc, 0xb4, 0xa2, 0x50, 0xb4, 0x91, 0xbc, 0x1d, 0xfb, 0xe8, 0x19, 0x2c, + 0x90, 0x72, 0x1a, 0x1d, 0x3e, 0x92, 0x0d, 0x34, 0xae, 0x8c, 0xd2, 0xb4, + 0x47, 0xba, 0x80, 0x5d, 0x85, 0x9d, 0x29, 0x30, 0x12, 0x81, 0x7a, 0x56, + 0xd1, 0x07, 0xf4, 0xcc, 0x9f, 0xde, 0x2f, 0x66, 0xc6, 0x88, 0xd7, 0x54, + 0xcb, 0x70, 0xa6, 0xc9, 0xa0, 0x8c, 0x9b, 0x01, 0xb3, 0x32, 0x50, 0xe2, + 0x7a, 0x4d, 0xcd, 0xcd, 0xea, 0xae, 0xe1, 0x59, 0xf6, 0x76, 0x01, 0xd4, + 0x19, 0x5e, 0xd3, 0x74, 0x27, 0x47, 0x65, 0xa8, 0xe0, 0x11, 0x69, 0xa0, + 0x08, 0xe2, 0xb4, 0x5d, 0x23, 0x31, 0x93, 0xb3, 0x24, 0x3b, 0x2d, 0xb6, + 0x9a, 0x86, 0x6b, 0x6f, 0xec, 0xab, 0x17, 0xcf, 0x2c, 0xe7, 0x67, 0x72, + 0x25, 0x05, 0xe3, 0xfd, 0xb0, 0x14, 0xcf, 0x14, 0x48, 0xe7, 0x21, 0x31, + 0x0c, 0xa2, 0x91, 0xac, 0x19, 0x10, 0xdf, 0xf6, 0xee, 0x4c, 0x11, 0x14, + 0x40, 0xcb, 0xaf, 0xeb, 0x72, 0x49, 0x25, 0xd3, 0xbc, 0x2e, 0x28, 0x31, + 0xb3, 0x0b, 0x7a, 0xbe, 0x23, 0xae, 0x50, 0xcb, 0x39, 0xf1, 0xe7, 0x09, + 0x8d, 0x50, 0xdf, 0x39, 0xfa, 0x9a, 0x44, 0x9b, 0xab, 0xc8, 0x2f, 0x37, + 0xb0, 0x01, 0x58, 0x6b, 0x6a, 0x73, 0xe7, 0xb0, 0xd9, 0x5b, 0xc6, 0x93, + 0xdc, 0x37, 0x03, 0x56, 0xab, 0x1f, 0x9b, 0x2c, 0x86, 0xa8, 0xa2, 0x77, + 0x3c, 0x4f, 0x0a, 0xca, 0x5a, 0xe0, 0x1f, 0x42, 0x0e, 0x95, 0x4a, 0xab, + 0xe5, 0xad, 0xe8, 0xcc, 0xb9, 0x66, 0xb1, 0x1a, 0x5e, 0x39, 0x4b, 0x50, + 0x6e, 0x52, 0x56, 0xae, 0xb2, 0x8b, 0x32, 0x8d, 0x89, 0xda, 0x76, 0x32, + 0x10, 0xbd, 0xb5, 0xe6, 0xfb, 0xfa, 0xa6, 0x5c, 0xe2, 0x94, 0x68, 0x28, + 0xd8, 0x9e, 0x50, 0x76, 0xcc, 0xec, 0xf8, 0xab, 0x8f, 0xf1, 0x31, 0xc3, + 0x60, 0x43, 0x57, 0x4b, 0xb5, 0x9f, 0xa5, 0x92, 0xf2, 0x31, 0xc0, 0xe5, + 0x08, 0xf5, 0x82, 0x38, 0x1a, 0x91, 0x4f, 0xaf, 0x0d, 0x31, 0x06, 0x2b, + 0x0c, 0xa9, 0xce, 0x3d, 0x5b, 0x05, 0x1b, 0xa1, 0x7b, 0xce, 0x9b, 0xd2, + 0x6e, 0x8f, 0x3a, 0x18, 0x65, 0xdc, 0xb4, 0x9e, 0xd3, 0x1f, 0x1b, 0x14, + 0x04, 0x80, 0x63, 0x5b, 0x2c, 0x8e, 0x1e, 0x4c, 0xa4, 0x3f, 0xbc, 0xf4, + 0x05, 0x91, 0xde, 0xdb, 0x29, 0xd7, 0xa7, 0x15, 0xde, 0x21, 0xf0, 0x5b, + 0x0c, 0xae, 0xb3, 0xd4, 0x51, 0x45, 0xa5, 0x19, 0xcf, 0x0a, 0x26, 0xe1, + 0x57, 0x16, 0x39, 0x3f, 0xbd, 0x85, 0xe0, 0x24, 0xfd, 0x2e, 0x86, 0xca, + 0x4c, 0x95, 0x94, 0xaa, 0xfb, 0x5d, 0xa5, 0xe2, 0x7d, 0x35, 0x53, 0x08, + 0x48, 0xf4, 0x2b, 0x2a, 0xe9, 0xdc, 0xc4, 0xc4, 0x5a, 0x74, 0xbd, 0x49, + 0x1b, 0xcd, 0x16, 0x0a, 0x63, 0x0a, 0x77, 0x0b, 0xaa, 0xf2, 0xb6, 0x4a, + 0xd5, 0x69, 0x62, 0xbc, 0xf4, 0xb3, 0xff, 0x49, 0x8b, 0x0e, 0xa5, 0x9a, + 0x3b, 0x82, 0xe6, 0x88, 0x12, 0xe6, 0x1d, 0xca, 0xac, 0x26, 0x94, 0x87, + 0xd5, 0xd8, 0xe2, 0x85, 0x77, 0x17, 0x57, 0xb7, 0xb3, 0xf6, 0xa0, 0x6e, + 0x14, 0x43, 0xa6, 0x12, 0x26, 0x30, 0xaf, 0x95, 0xb2, 0x46, 0x3a, 0x92, + 0x39, 0xe9, 0x5d, 0x5a, 0xab, 0x2b, 0x27, 0x31, 0xf3, 0x33, 0x35, 0xaa, + 0x88, 0xc0, 0x09, 0x9d, 0xef, 0xfd, 0xf1, 0xf0, 0xa2, 0xce, 0x04, 0xca, + 0x5c, 0x96, 0x58, 0x65, 0x3e, 0xb5, 0x1c, 0x17, 0x23, 0x2e, 0x83, 0x83, + 0xff, 0x90, 0x94, 0x25, 0x00, 0x1c, 0xed, 0xc8, 0x23, 0xc9, 0xe9, 0xd5, + 0x0f, 0x38, 0x40, 0x74, 0x7d, 0x55, 0x2e, 0xab, 0x74, 0xcb, 0x61, 0x10, + 0x6d, 0x4f, 0x6a, 0xe3, 0xd5, 0xeb, 0xed, 0x8d, 0xd9, 0xbb, 0x13, 0xbc, + 0xd1, 0xbc, 0x59, 0xd7, 0xd8, 0x85, 0x7b, 0xc7, 0x59, 0xc9, 0xd9, 0xa3, + 0xfb, 0xad, 0x4d, 0x44, 0x17, 0x8f, 0x6b, 0xf2, 0x80, 0x38, 0xba, 0x4b, + 0x51, 0x33, 0x02, 0x86, 0x51, 0xbd, 0x04, 0xf4, 0xcf, 0xd5, 0xf3, 0x60, + 0x9e, 0xf7, 0x88, 0x2c, 0xc8, 0x64, 0x76, 0x1a, 0xa6, 0x12, 0x3f, 0xc6, + 0xb8, 0xb0, 0x26, 0xc1, 0x83, 0x0d, 0x8e, 0x1e, 0x34, 0x3f, 0x3a, 0x65, + 0xe5, 0xda, 0x23, 0x3c, 0x37, 0xbc, 0xd0, 0x9b, 0x9f, 0x0d, 0x95, 0x12, + 0xbf, 0xcf, 0x89, 0x72, 0x8b, 0xdd, 0x8c, 0x4b, 0xd0, 0xd6, 0x24, 0x3f, + 0xab, 0xb0, 0x1d, 0x81, 0x58, 0x0c, 0x36, 0x1e, 0x10, 0x6d, 0x74, 0x36, + 0x19, 0x08, 0x15, 0xf1, 0xfe, 0x4c, 0xd9, 0x4c, 0x1f, 0x60, 0xd6, 0x4b, + 0xbe, 0xf4, 0xa6, 0xce, 0x27, 0x8a, 0x5d, 0xee, 0x93, 0x9b, 0xf9, 0x66, + 0x19, 0x22, 0x1e, 0x61, 0x26, 0x01, 0x64, 0xac, 0x63, 0xb7, 0xd3, 0x9b, + 0x8e, 0x35, 0x43, 0x18, 0x44, 0x2e, 0x8f, 0xe5, 0x0e, 0xad, 0x05, 0xa9, + 0xd1, 0x9c, 0xd6, 0xf6, 0xf4, 0xa5, 0xa4, 0x52, 0xc4, 0x83, 0x10, 0x1f, + 0xa6, 0xc3, 0xc9, 0x68, 0x64, 0x17, 0xa9, 0x8b, 0xe5, 0x35, 0xd7, 0x8d, + 0xcd, 0xcc, 0x9e, 0x67, 0x97, 0xf6, 0xf3, 0xdd, 0x4e, 0xef, 0x12, 0xf7, + 0x82, 0x71, 0x74, 0xd9, 0xdd, 0xc1, 0x28, 0x54, 0xf3, 0x15, 0x1a, 0xa4, + 0x13, 0xf3, 0x46, 0x17, 0x03, 0x71, 0xf3, 0x43, 0x7a, 0x2f, 0x6d, 0x44, + 0x5b, 0xfc, 0x80, 0xbf, 0x99, 0x9c, 0x01, 0xfd, 0xeb, 0xb5, 0x55, 0x92, + 0xce, 0xd9, 0x09, 0x0c, 0xc1, 0xb0, 0x62, 0xac, 0xa5, 0xae, 0x68, 0xd4, + 0xbe, 0xf1, 0xd6, 0x7d, 0x6d, 0x5b, 0x05, 0xf9, 0x97, 0x97, 0x65, 0x58, + 0xe9, 0xdb, 0x5b, 0xca, 0x1d, 0xbe, 0xad, 0x33, 0x8e, 0x0d, 0xf8, 0x4d, + 0xe8, 0x98, 0x66, 0x0b, 0xf5, 0x82, 0xd5, 0x77, 0xcc, 0x5d, 0x4c, 0xd2, + 0x6b, 0x10, 0xc1, 0xc4, 0x86, 0xc7, 0x2e, 0x96, 0x27, 0x7f, 0x3e, 0x04, + 0x3a, 0x25, 0xfd, 0x48, 0x1f, 0x90, 0x24, 0xf3, 0x02, 0x26, 0x84, 0xd8, + 0x30, 0x79, 0x93, 0x40, 0x7a, 0xa7, 0x6f, 0x23, 0xe9, 0xd3, 0x8c, 0x4f, + 0xbd, 0xc1, 0xc4, 0xbe, 0x85, 0x9c, 0x9d, 0xf1, 0x0b, 0xbf, 0xdd, 0x5e, + 0xa1, 0x19, 0x85, 0x82, 0xf8, 0x4a, 0x9a, 0x93, 0xe0, 0x11, 0x9a, 0x52, + 0xaf, 0xba, 0xc3, 0x58, 0xc4, 0x3d, 0x8f, 0xee, 0xc0, 0x4f, 0xd3, 0xf2, + 0x26, 0x8b, 0x0c, 0x9f, 0x09, 0x49, 0x17, 0xc2, 0x1b, 0xa0, 0xbb, 0xcf, + 0x0b, 0x05, 0x68, 0x1a, 0xf4, 0xac, 0xf3, 0x0a, 0x95, 0x0b, 0x9e, 0xb2, + 0x72, 0xff, 0x98, 0x71, 0x2b, 0x19, 0x11, 0xe8, 0xe6, 0x74, 0x34, 0xe1, + 0xeb, 0x4b, 0x45, 0xe6, 0xaf, 0x1c, 0x6a, 0x79, 0xf5, 0xb6, 0x7c, 0x3d, + 0x49, 0x8d, 0x9a, 0xe3, 0xa0, 0xff, 0x9f, 0xa2, 0x87, 0x7c, 0x45, 0x0b, + 0x95, 0x3d, 0x35, 0x3e, 0x9f, 0x72, 0x6a, 0xd7, 0x47, 0x4f, 0xd3, 0x65, + 0xd1, 0x71, 0x5b, 0x18, 0x97, 0xf6, 0x7a, 0x2a, 0x16, 0x49, 0x58, 0xda, + 0xd2, 0x85, 0x0f, 0xdc, 0x27, 0xfd, 0x9d, 0x8e, 0x6e, 0xc7, 0xbf, 0xe8, + 0x4b, 0x27, 0x5d, 0x46, 0x12, 0x2c, 0xf5, 0xa5, 0xde, 0xa7, 0x0d, 0xfa, + 0x45, 0x67, 0xf9, 0xde, 0x2e, 0xba, 0xd9, 0x61, 0x2c, 0x65, 0x3d, 0xaf, + 0x14, 0x28, 0xbb, 0x80, 0x4b, 0xc4, 0xbf, 0x73, 0xf1, 0x54, 0x1c, 0x91, + 0x4d, 0x5b, 0x8d, 0x3d, 0x44, 0xa4, 0x64, 0xa4, 0xd8, 0x65, 0x90, 0xa6, + 0xb5, 0x7c, 0x7c, 0xba, 0xfb, 0x86, 0x64, 0xf0, 0x14, 0x61, 0x63, 0x5f, + 0x32, 0x95, 0xcf, 0x1b, 0xcf, 0x05, 0xda, 0xdb, 0x44, 0xc6, 0x06, 0x42, + 0x50, 0xcf, 0x5e, 0x68, 0x3d, 0xa8, 0x06, 0x69, 0x8c, 0x9a, 0x9c, 0xb2, + 0x46, 0xc4, 0x3b, 0xc8, 0x95, 0xbf, 0x9f, 0xd7, 0x3c, 0x5a, 0xc2, 0x2f, + 0xcd, 0xa0, 0x0b, 0xf9, 0xdc, 0x4d, 0xb9, 0x33, 0xf8, 0xd8, 0x15, 0xef, + 0x0c, 0xba, 0xf5, 0xe8, 0xa4, 0x27, 0x8d, 0x1b, 0x2e, 0xde, 0xca, 0x1d, + 0xcb, 0x01, 0x3f, 0x83, 0xbf, 0x20, 0xca, 0xf9, 0x0d, 0x24, 0x98, 0x4f, + 0xfd, 0x40, 0xda, 0x07, 0xe7, 0x49, 0xd0, 0x6f, 0x92, 0x57, 0xe8, 0x09, + 0xdf, 0xa8, 0x5e, 0x52, 0xe3, 0x3d, 0xbc, 0x8c, 0xc1, 0xf6, 0x4a, 0xd5, + 0x5c, 0xaf, 0x4b, 0x90, 0x58, 0x3b, 0x63, 0xb2, 0x80, 0x44, 0x46, 0x36, + 0x7c, 0xd1, 0x7e, 0xbe, 0x66, 0xe2, 0xfa, 0xf9, 0x32, 0x74, 0x1c, 0x5e, + 0x4c, 0x22, 0xe2, 0x57, 0x0a, 0x64, 0x0b, 0x20, 0x0a, 0x35, 0x56, 0xaf, + 0xac, 0xc2, 0xee, 0x9e, 0x2a, 0xa1, 0xf2, 0x1e, 0xe3, 0xc4, 0x9c, 0xa5, + 0xca, 0x97, 0x58, 0xb8, 0xc5, 0x80, 0xb6, 0xe1, 0xd8, 0xbc, 0xb3, 0x22, + 0xf3, 0xfc, 0x1d, 0x52, 0x1a, 0x5d, 0x68, 0x3d, 0x46, 0xd7, 0x6d, 0xd8, + 0xaf, 0x19, 0xc5, 0x74, 0x80, 0x62, 0x91, 0xda, 0x98, 0xe1, 0x00, 0xe8, + 0x24, 0xd1, 0xd3, 0xea, 0x8c, 0xf1, 0xc3, 0x74, 0xa9, 0xd9, 0x4f, 0xc8, + 0x8a, 0x26, 0xd8, 0x4c, 0x4f, 0x03, 0x11, 0xcd, 0x41, 0x6d, 0x97, 0x2a, + 0xdf, 0x04, 0xe3, 0x46, 0x94, 0xab, 0xe6, 0x2f, 0x74, 0x0e, 0xd9, 0x54, + 0x4e, 0x51, 0x70, 0x85, 0x99, 0x65, 0xcc, 0x96, 0xa1, 0x68, 0xaa, 0x3e, + 0xa7, 0xd0, 0x28, 0x0e, 0x40, 0x31, 0xb8, 0xe7, 0x1f, 0xfb, 0xe2, 0xee, + 0x66, 0x9f, 0xd9, 0xb1, 0x84, 0xb4, 0xb5, 0x33, 0x71, 0xd4, 0xb1, 0x09, + 0xc5, 0x6f, 0x6f, 0x75, 0xfc, 0x8a, 0xdb, 0x82, 0x91, 0xd3, 0xaf, 0x0a, + 0xe2, 0x11, 0x43, 0xc9, 0x2a, 0x72, 0x84, 0xa2, 0x5a, 0xda, 0x79, 0xd5, + 0xdb, 0x50, 0x55, 0x92, 0x90, 0x2e, 0x5e, 0x8e, 0x04, 0x31, 0x2a, 0x01, + 0xf8, 0xc7, 0x3a, 0x5c, 0xe4, 0x3c, 0x99, 0x91, 0x69, 0x05, 0xcb, 0x3c, + 0xcb, 0x5e, 0xca, 0x37, 0x91, 0xac, 0xec, 0x10, 0xfb, 0x00, 0x32, 0xbf, + 0x2d, 0x92, 0x1e, 0x46, 0x60, 0x66, 0xad, 0x52, 0xce, 0x2e, 0xb9, 0x73, + 0x3f, 0x18, 0x21, 0x4d, 0xcf, 0xb7, 0x1b, 0x19, 0x06, 0x71, 0x05, 0x6d, + 0xd9, 0xf4, 0x90, 0x2a, 0x24, 0xdb, 0xe8, 0x7e, 0x12, 0x50, 0x60, 0x60, + 0x33, 0x09, 0x9c, 0x6d, 0xe0, 0x88, 0x7d, 0xc9, 0xd4, 0xba, 0xf5, 0xe0, + 0xcd, 0x95, 0x75, 0x52, 0x85, 0x81, 0xfc, 0xc1, 0x55, 0x46, 0x39, 0xd6, + 0x66, 0x87, 0x4e, 0x5d, 0xa4, 0x27, 0xb6, 0xff, 0x1d, 0x40, 0x00, 0x86, + 0x20, 0x46, 0x12, 0x4f, 0x50, 0xe2, 0x63, 0x21, 0x08, 0x9b, 0x58, 0x7c, + 0xa0, 0x9b, 0x3e, 0x40, 0x99, 0x54, 0x23, 0x12, 0xfb, 0x6d, 0xad, 0xdb, + 0x3d, 0x22, 0xcf, 0xea, 0xfb, 0x60, 0x75, 0x12, 0x53, 0x3d, 0x2e, 0xd0, + 0xc4, 0x09, 0xcd, 0xde, 0xcf, 0xca, 0xac, 0xdf, 0x3a, 0x54, 0x8d, 0xef, + 0x50, 0x55, 0x45, 0x35, 0x17, 0x32, 0x56, 0x91, 0x00, 0x61, 0x10, 0x4f, + 0xb8, 0x04, 0x1d, 0x9b, 0x6a, 0xca, 0x67, 0x6c, 0x3b, 0xcc, 0x5a, 0x18, + 0xa8, 0x3e, 0x82, 0x45, 0xb5, 0x1e, 0xa2, 0xc5, 0xa9, 0xce, 0x87, 0x63, + 0xdf, 0xa3, 0x52, 0x7c, 0xc9, 0x9d, 0xe0, 0xfb, 0x39, 0x0a, 0xc4, 0x9c, + 0x40, 0xef, 0x6c, 0xea, 0x97, 0x70, 0xe6, 0xa3, 0xc6, 0x1c, 0x96, 0x23, + 0x03, 0xa5, 0x83, 0xee, 0x91, 0x8a, 0x4f, 0xb5, 0xcd, 0x72, 0x58, 0xc5, + 0x3b, 0xe7, 0xe5, 0xc1, 0x45, 0xfd, 0x75, 0xf1, 0x59, 0xd5, 0xb0, 0x65, + 0x0d, 0x0f, 0x8b, 0x2c, 0x03, 0x7a, 0x52, 0xff, 0xa9, 0x6e, 0x4c, 0xad, + 0x05, 0x21, 0x14, 0x6d, 0x58, 0x2d, 0xe1, 0x86, 0x59, 0x24, 0xe5, 0x66, + 0x37, 0xb7, 0x6e, 0x3e, 0xe3, 0x1d, 0x07, 0xcf, 0x4b, 0x99, 0xdd, 0x8e, + 0xa4, 0x9c, 0x1a, 0xf4, 0x04, 0x35, 0x2d, 0x4d, 0xc1, 0x70, 0xc5, 0x65, + 0x69, 0xbe, 0x2d, 0xca, 0x37, 0x29, 0x72, 0x2f, 0x07, 0x40, 0xf9, 0xa5, + 0x74, 0x7c, 0xc7, 0x20, 0x72, 0x16, 0xfa, 0xb1, 0x8b, 0x94, 0x1d, 0x8c, + 0xa5, 0x39, 0xd2, 0x29, 0xdb, 0x27, 0x7d, 0x73, 0x0e, 0x47, 0x1e, 0x18, + 0xf7, 0x6a, 0x45, 0xce, 0x36, 0x4f, 0x50, 0xcd, 0xc9, 0xdc, 0x0c, 0x6d, + 0x0e, 0x90, 0x1f, 0x2c, 0x89, 0x67, 0x4b, 0x69, 0x9b, 0x06, 0x6d, 0x32, + 0xbc, 0xe0, 0xdf, 0x2d, 0xb2, 0x27, 0xc9, 0x4e, 0xce, 0x0b, 0xa0, 0x2b, + 0xfe, 0x3f, 0x55, 0x89, 0xb8, 0xe1, 0x06, 0x5d, 0x0c, 0x91, 0xe8, 0x40, + 0x3e, 0x9a, 0x47, 0x2a, 0xdf, 0x5d, 0x3e, 0xb0, 0xc8, 0x4c, 0x3a, 0x73, + 0x4b, 0xa4, 0xb2, 0x48, 0xc7, 0xfd, 0x63, 0x6e, 0x80, 0xe6, 0xfd, 0x5b, + 0xeb, 0xd8, 0x52, 0xd3, 0x3e, 0x9c, 0x5e, 0x41, 0xe9, 0x5e, 0x41, 0xcc, + 0xba, 0x60, 0xf0, 0xba, 0xeb, 0x31, 0x10, 0xf5, 0x19, 0xd9, 0x42, 0x2b, + 0x03, 0x3e, 0xbf, 0xe2, 0x8e, 0x5e, 0x62, 0x8b, 0x70, 0x83, 0x6b, 0x84, + 0x08, 0xfd, 0xac, 0x85, 0x6b, 0xbb, 0x63, 0x9a, 0xcc, 0x61, 0x27, 0x3b, + 0xa3, 0xc7, 0x14, 0x1b, 0x8b, 0xca, 0x4c, 0x58, 0x43, 0x33, 0xfd, 0x4a, + 0xb8, 0xb7, 0x40, 0x1a, 0x71, 0xaa, 0x89, 0xa2, 0x33, 0x72, 0xd7, 0xb3, + 0x2a, 0x84, 0x8d, 0x91, 0x83, 0x51, 0x56, 0x93, 0x61, 0x21, 0xfe, 0x51, + 0xab, 0x05, 0x47, 0x40, 0x66, 0x1e, 0x80, 0x52, 0xc0, 0xcc, 0x10, 0x56, + 0xa1, 0x51, 0x58, 0x2b, 0x02, 0x22, 0x98, 0x30, 0xec, 0x0c, 0x1a, 0x08, + 0x7b, 0xcb, 0x93, 0x3e, 0xa8, 0x51, 0x73, 0x0f, 0x25, 0x2a, 0x4f, 0x24, + 0x7d, 0xce, 0x22, 0x8f, 0xfb, 0xf6, 0xc4, 0x65, 0x39, 0xe6, 0x3d, 0x75, + 0x68, 0xe4, 0x5c, 0xb5, 0xf5, 0xdf, 0x4e, 0xce, 0xd9, 0x4b, 0x34, 0x88, + 0x50, 0x5f, 0xd4, 0xf5, 0xe6, 0x33, 0x08, 0xa3, 0xfc, 0x20, 0x8b, 0x86, + 0x44, 0x02, 0xd0, 0xb2, 0x9d, 0xb7, 0x40, 0x97, 0x85, 0x59, 0xe0, 0xae, + 0x4b, 0xc4, 0xb9, 0xf5, 0xda, 0xce, 0xf8, 0x3b, 0x8d, 0xe1, 0xeb, 0xff, + 0xfc, 0xe5, 0x47, 0x3a, 0xa6, 0x4f, 0x8a, 0x26, 0xdf, 0x77, 0xae, 0xac, + 0xda, 0x47, 0xb5, 0xf1, 0x76, 0xaf, 0x2b, 0xba, 0x1d, 0x86, 0xad, 0x4f, + 0x08, 0x7b, 0xee, 0x14, 0xfb, 0x24, 0xc5, 0x87, 0x2a, 0x98, 0x8e, 0x25, + 0xd9, 0x36, 0x34, 0x8e, 0x45, 0x44, 0x0a, 0xea, 0x3d, 0xf5, 0xd4, 0x61, + 0xe8, 0x25, 0x79, 0x1d, 0xf7, 0xd8, 0x98, 0xc5, 0x60, 0x0b, 0x2f, 0x0b, + 0xd4, 0x21, 0xc5, 0xb4, 0x48, 0x4b, 0x0f, 0x0c, 0x58, 0x5b, 0x9d, 0x6f, + 0xbf, 0x83, 0x6a, 0x47, 0xa7, 0x66, 0xb6, 0x5d, 0xd8, 0x6f, 0xac, 0xf5, + 0x34, 0x16, 0x62, 0xa3, 0xf7, 0x4e, 0x33, 0xc2, 0x99, 0x90, 0xdb, 0x02, + 0x99, 0xde, 0x1f, 0xd2, 0xf8, 0x85, 0xcc, 0x95, 0x01, 0x31, 0x77, 0xd9, + 0xf8, 0xa9, 0xed, 0x69, 0x74, 0x31, 0xce, 0x5a, 0xdd, 0x14, 0x43, 0x60, + 0xfe, 0xc9, 0x00, 0x6e, 0x6a, 0x61, 0x86, 0xde, 0xe1, 0xc7, 0x98, 0xe5, + 0x01, 0xd2, 0x6e, 0x43, 0xed, 0xc5, 0x9d, 0x29, 0x23, 0x83, 0xb0, 0xa1, + 0x77, 0x79, 0xe9, 0x73, 0xb1, 0x35, 0x34, 0xd6, 0x35, 0x78, 0x87, 0xe4, + 0xb6, 0x63, 0x2c, 0x6e, 0x64, 0x9f, 0x81, 0xb8, 0x3d, 0x26, 0xe9, 0x1c, + 0xe5, 0x9a, 0x8e, 0xbb, 0x64, 0xe9, 0xd4, 0xf6, 0x8c, 0x52, 0x3d, 0x58, + 0x15, 0xd4, 0x42, 0x33, 0x21, 0xb4, 0x2a, 0x58, 0xbe, 0x73, 0x43, 0x8b, + 0x1e, 0x8d, 0x1f, 0x2f, 0x6d, 0x93, 0x66, 0xfe, 0x54, 0xed, 0x73, 0x3c, + 0xbc, 0xa3, 0x45, 0x10, 0xc0, 0x22, 0xe9, 0x1e, 0x6f, 0x7d, 0x4a, 0xfa, + 0x75, 0x48, 0x86, 0xab, 0x67, 0xcf, 0xa5, 0x51, 0xca, 0xeb, 0xc0, 0x54, + 0x9e, 0x2f, 0x7b, 0xe8, 0x8d, 0x9b, 0xf4, 0xb2, 0xbf, 0x33, 0x26, 0x6d, + 0x68, 0x96, 0x80, 0x5c, 0xba, 0x09, 0x29, 0x6e, 0x69, 0x70, 0x70, 0x09, + 0xf7, 0xbd, 0xd4, 0xf2, 0x31, 0x14, 0xe0, 0xdd, 0xb7, 0x39, 0xb4, 0x15, + 0x5b, 0xe1, 0xf8, 0x13, 0x19, 0x9b, 0xea, 0xbe, 0xd3, 0xbd, 0xde, 0xeb, + 0xc9, 0xf9, 0x70, 0x63, 0x91, 0x3e, 0x25, 0x4b, 0x1c, 0x39, 0xc9, 0x50, + 0x5c, 0x8b, 0x2c, 0xb9, 0xa3, 0xd5, 0x52, 0x13, 0xe6, 0xe8, 0xfb, 0x79, + 0xc5, 0x37, 0xef, 0x48, 0x61, 0xa6, 0x32, 0xd1, 0x04, 0xa9, 0xbc, 0xe7, + 0x1c, 0xf5, 0x9d, 0x81, 0x59, 0x34, 0xbb, 0x74, 0x79, 0x1d, 0x14, 0x49, + 0xdb, 0xe4, 0x96, 0x76, 0x6f, 0xd8, 0xed, 0x60, 0x22, 0x07, 0xbe, 0xad, + 0x9e, 0xad, 0x2e, 0x57, 0x7f, 0xb2, 0x7c, 0xbe, 0xff, 0x5f, 0xff, 0xdf, + 0x0a, 0xc4, 0x33, 0x03, 0x4d, 0x73, 0x9a, 0x67, 0x29, 0xbb, 0x53, 0x25, + 0xfa, 0x00, 0x62, 0x3e, 0x00, 0x84, 0x54, 0x93, 0x8f, 0xaf, 0x52, 0x50, + 0x28, 0xad, 0x8f, 0x1a, 0xf4, 0x36, 0x77, 0xa5, 0x47, 0xba, 0x9d, 0xd7, + 0x3c, 0x3f, 0x73, 0xb4, 0xd0, 0x64, 0x0a, 0x53, 0x0b, 0x83, 0xa9, 0x74, + 0x5a, 0x1b, 0xa7, 0x5b, 0xd2, 0x57, 0xa3, 0xd5, 0xe5, 0x8e, 0xb9, 0x7a, + 0xb9, 0xd2, 0xbd, 0x87, 0x32, 0xd0, 0xb3, 0xc5, 0xc2, 0x74, 0x6e, 0xbc, + 0x27, 0x92, 0x8f, 0xb8, 0x1c, 0x11, 0x0c, 0x10, 0x54, 0x91, 0xc8, 0xc0, + 0xb9, 0xa2, 0xd5, 0xe1, 0xb2, 0xe6, 0x05, 0xc7, 0x9b, 0xa6, 0xad, 0x40, + 0x45, 0xb5, 0x4c, 0x3d, 0x63, 0xc1, 0xf3, 0xc7, 0x11, 0x19, 0x46, 0xd5, + 0x39, 0x4a, 0x5d, 0x3c, 0x04, 0x5b, 0xd1, 0x0f, 0x7b, 0xe5, 0x7b, 0x21, + 0x0c, 0x40, 0xb9, 0x5f, 0x15, 0xf4, 0x54, 0xfb, 0xbc, 0x1d, 0xf0, 0x8a, + 0x2f, 0x14, 0xc5, 0x70, 0xe3, 0xcf, 0xcb, 0x6b, 0x2f, 0x58, 0x6a, 0xcb, + 0xd8, 0xd9, 0xb5, 0x19, 0xf6, 0x7b, 0x30, 0x48, 0x09, 0xa9, 0x04, 0xb2, + 0xa0, 0x4e, 0x7e, 0xb7, 0x79, 0x75, 0x50, 0x57, 0x0d, 0x92, 0x77, 0x45, + 0xb0, 0x65, 0xbb, 0x03, 0x57, 0xbc, 0xfa, 0x2f, 0x6c, 0x99, 0xa6, 0x15, + 0x05, 0xd2, 0x15, 0xf4, 0x4c, 0xa8, 0x33, 0x62, 0x82, 0x18, 0xca, 0x86, + 0x4c, 0x5a, 0xb7, 0xd8, 0x4c, 0xdd, 0x54, 0x1b, 0xeb, 0xf1, 0x08, 0xdf, + 0xdc, 0x01, 0xae, 0xe2, 0x1b, 0x43, 0x98, 0x67, 0xab, 0xf1, 0xc5, 0xaf, + 0x52, 0x34, 0xb4, 0x08, 0x64, 0x43, 0xc8, 0x90, 0xb1, 0x94, 0xe7, 0x78, + 0xff, 0x31, 0xc4, 0x1b, 0x06, 0x91, 0x35, 0x6a, 0xc2, 0x34, 0x7b, 0xfa, + 0x8a, 0x42, 0xee, 0x5e, 0x00, 0xbe, 0x27, 0x2e, 0x97, 0xb3, 0xcd, 0x52, + 0xed, 0xff, 0xef, 0xa4, 0xd2, 0x78, 0xcb, 0x82, 0xb7, 0x26, 0xe4, 0xd7, + 0xf2, 0xd5, 0x61, 0x85, 0x70, 0x9b, 0xed, 0x74, 0xc5, 0x27, 0xa5, 0x70, + 0xaf, 0xc9, 0xd5, 0xc1, 0x5c, 0x92, 0xc0, 0x2d, 0x42, 0x11, 0x7b, 0xb6, + 0x05, 0x93, 0xc0, 0x1b, 0x63, 0x68, 0xa3, 0x54, 0x74, 0x64, 0x93, 0x43, + 0x6a, 0x02, 0x13, 0x98, 0x55, 0x2c, 0x6d, 0x1b, 0x21, 0x68, 0x01, 0x04, + 0x39, 0xb5, 0xc1, 0x69, 0x43, 0xf6, 0x22, 0x7e, 0xe8, 0xfd, 0xb5, 0x3c, + 0x06, 0x16, 0xa6, 0x1c, 0x8e, 0x25, 0x36, 0x13, 0xbe, 0x72, 0x55, 0x97, + 0xd2, 0x52, 0x07, 0x24, 0x9c, 0x4c, 0x36, 0xa6, 0x57, 0x1a, 0x9e, 0x3c, + 0xd3, 0xde, 0xc2, 0xf4, 0xaa, 0x72, 0x0a, 0x2a, 0xa1, 0xde, 0x9b, 0x73, + 0x1d, 0xa2, 0xbd, 0xf2, 0x97, 0x10, 0x75, 0xdb, 0x00, 0xeb, 0x44, 0x80, + 0xdc, 0x85, 0x9a, 0x7f, 0xcc, 0x3d, 0xee, 0x85, 0x32, 0x36, 0xcf, 0x46, + 0x66, 0xf3, 0x4b, 0x18, 0xc0, 0x3e, 0xc0, 0xcd, 0x58, 0x7b, 0x4a, 0x89, + 0xc8, 0x3f, 0xf8, 0x43, 0x03, 0x0b, 0xd1, 0x1e, 0xae, 0xb5, 0x42, 0x50, + 0x92, 0x53, 0xd9, 0x20, 0xae, 0xbb, 0x17, 0x45, 0xf7, 0x3e, 0x05, 0xa7, + 0xf8, 0xe1, 0xb9, 0xd3, 0xd9, 0x34, 0x91, 0x34, 0xd7, 0xa0, 0xdb, 0xb0, + 0x63, 0x91, 0x0e, 0x45, 0x57, 0xcc, 0xa2, 0x1d, 0xc6, 0x9a, 0xe6, 0xfc, + 0x70, 0x59, 0x93, 0x07, 0x91, 0x19, 0x01, 0x59, 0xe6, 0x71, 0x81, 0x63, + 0x71, 0xbe, 0x43, 0x56, 0xa1, 0xa8, 0x5e, 0x42, 0x60, 0x59, 0xe6, 0x49, + 0x40, 0x89, 0x3c, 0x32, 0x46, 0xf0, 0x7e, 0x2e, 0x2d, 0x1d, 0x6f, 0x02, + 0x6b, 0x99, 0x4f, 0x6e, 0x95, 0xeb, 0x1a, 0x2d, 0x89, 0xd9, 0xd9, 0x1b, + 0x41, 0x13, 0x7c, 0xbb, 0x94, 0x34, 0x6c, 0x14, 0x5d, 0x80, 0xea, 0xbc, + 0x6a, 0x38, 0xea, 0xb7, 0xb3, 0x2d, 0xad, 0x0d, 0x2f, 0x64, 0xa4, 0x6e, + 0xb7, 0x87, 0x16, 0x51, 0x51, 0xe3, 0x0d, 0x0e, 0x3b, 0x57, 0x55, 0xbd, + 0xf6, 0xfd, 0x29, 0x2c, 0xe5, 0x75, 0x8e, 0xae, 0x13, 0xe3, 0x6f, 0x6b, + 0x76, 0x03, 0x50, 0xb0, 0xf9, 0xeb, 0x21, 0x9e, 0x2e, 0x5c, 0x51, 0xab, + 0x0b, 0x63, 0x13, 0xb7, 0x83, 0xf8, 0x80, 0x4d, 0x3a, 0x30, 0x7b, 0x3f, + 0x1b, 0xc2, 0xae, 0x6f, 0x7e, 0x2d, 0xc3, 0xee, 0xeb, 0xa0, 0x8a, 0x6d, + 0xe8, 0x9a, 0x5d, 0xd8, 0x2b, 0x7f, 0xed, 0x71, 0xf9, 0x8d, 0xb6, 0x85, + 0xa3, 0x3e, 0xf0, 0x8e, 0x60, 0xf4, 0x58, 0xe6, 0x36, 0x0a, 0x20, 0x54, + 0x0f, 0x09, 0xe5, 0xa7, 0x49, 0x22, 0xef, 0x2f, 0xda, 0xed, 0x72, 0x7b, + 0x6a, 0xf5, 0x61, 0x3d, 0x11, 0x65, 0x9e, 0x01, 0x32, 0x52, 0x7b, 0x73, + 0xa3, 0xc5, 0x41, 0x1b, 0xe1, 0x8d, 0x75, 0x84, 0x10, 0x7c, 0x54, 0xb3, + 0xfd, 0xb1, 0x19, 0x16, 0xc7, 0x2b, 0x4e, 0xc3, 0x42, 0x0d, 0x43, 0x76, + 0x16, 0x05, 0x48, 0xe4, 0x52, 0x6b, 0x92, 0x9a, 0xb4, 0x31, 0x8f, 0x1c, + 0x20, 0x63, 0xa2, 0x21, 0x4d, 0xe4, 0x2d, 0xb1, 0xae, 0x69, 0xd3, 0x4d, + 0xf4, 0x2e, 0x5c, 0xde, 0xdf, 0x5e, 0xf7, 0xf9, 0x79, 0x9b, 0x43, 0xed, + 0x5c, 0xb5, 0xbf, 0x22, 0xee, 0xc7, 0x17, 0xd1, 0x72, 0xa1, 0x57, 0x59, + 0x2f, 0x4c, 0x55, 0x23, 0x38, 0xda, 0xaf, 0x74, 0x9e, 0x92, 0xc0, 0xb5, + 0x59, 0x5b, 0x50, 0xec, 0xab, 0x47, 0x72, 0xb2, 0x66, 0x7e, 0xc4, 0x5d, + 0x4a, 0xe2, 0xb9, 0x37, 0xae, 0x3a, 0x2a, 0x5d, 0x90, 0x9d, 0x0b, 0x6c, + 0xf5, 0x4d, 0x57, 0x5c, 0xf9, 0x03, 0xb4, 0x7d, 0xa0, 0x22, 0xc4, 0x4d, + 0x88, 0x65, 0x6c, 0x2d, 0xb1, 0x50, 0x07, 0x04, 0xe3, 0xe3, 0x5a, 0x47, + 0x40, 0xf9, 0xac, 0xe1, 0x7d, 0xd8, 0x2e, 0xb4, 0x59, 0xdf, 0x9d, 0xc3, + 0xc1, 0xf5, 0xb9, 0x78, 0x7a, 0x75, 0x69, 0x26, 0x90, 0xbf, 0x7b, 0x8d, + 0x23, 0x19, 0xe8, 0x08, 0xb8, 0x1e, 0x4e, 0x31, 0x37, 0x2a, 0xd4, 0x13, + 0x1e, 0x45, 0xff, 0x41, 0xda, 0xb1, 0x70, 0x44, 0x7e, 0x55, 0xdd, 0xe9, + 0x05, 0x6c, 0x6f, 0x3e, 0x88, 0x15, 0x35, 0x0d, 0xa7, 0xf6, 0xc0, 0x8d, + 0x0b, 0x29, 0x29, 0xfc, 0x37, 0xbd, 0x88, 0x97, 0x07, 0xdc, 0x32, 0x6e, + 0x2b, 0x97, 0x29, 0xab, 0x9f, 0xc8, 0x44, 0x60, 0x3b, 0x3c, 0xa8, 0x75, + 0x50, 0xbb, 0x16, 0x3e, 0x36, 0x33, 0xb4, 0xf5, 0x57, 0x86, 0xe3, 0x38, + 0x82, 0x46, 0x29, 0xff, 0xa6, 0xfb, 0x56, 0x6a, 0x6f, 0x46, 0x1c, 0x8e, + 0x32, 0x75, 0x37, 0x00, 0xbf, 0x49, 0xce, 0xc3, 0x4d, 0x94, 0xe0, 0x18, + 0x21, 0xa7, 0xd4, 0x96, 0x97, 0x8e, 0xd5, 0x1a, 0x40, 0x66, 0xbd, 0x06, + 0x65, 0x75, 0x74, 0x36, 0xf0, 0x8a, 0xcb, 0xb7, 0x37, 0x74, 0x03, 0xa8, + 0x70, 0x5e, 0x2d, 0x2c, 0xa7, 0x58, 0x44, 0xf0, 0xc3, 0x02, 0x8a, 0xa7, + 0x13, 0x8e, 0x8e, 0xb4, 0x8f, 0x27, 0x5d, 0x79, 0x27, 0xd4, 0xe7, 0x8e, + 0x94, 0x49, 0x5e, 0x96, 0xfd, 0x24, 0xa3, 0x79, 0xa4, 0xaa, 0x3c, 0x00, + 0xb0, 0xae, 0x5d, 0xeb, 0xca, 0x12, 0xb3, 0x2a, 0x69, 0xd6, 0xa8, 0x6d, + 0x2a, 0x52, 0xe7, 0x7b, 0x8c, 0xc9, 0x0a, 0xa7, 0xcc, 0x5a, 0xe9, 0x28, + 0x7c, 0x2c, 0x9c, 0xb2, 0xb3, 0x28, 0x22, 0xdb, 0xfe, 0x48, 0xef, 0xcd, + 0x75, 0xa9, 0xcf, 0x23, 0x1c, 0xa2, 0x70, 0x41, 0xdc, 0xd7, 0xe3, 0x30, + 0xb6, 0x70, 0x21, 0x06, 0x15, 0xfd, 0x67, 0xd0, 0x74, 0x10, 0x6f, 0x3e, + 0x8f, 0xde, 0x7c, 0x83, 0x0f, 0xa7, 0x8a, 0x49, 0x35, 0xde, 0x48, 0x14, + 0xbd, 0xf0, 0x53, 0x26, 0xe7, 0x18, 0xd5, 0xb0, 0x56, 0x8f, 0x81, 0x35, + 0x11, 0xf3, 0x50, 0x68, 0x72, 0x5a, 0x76, 0x06, 0x6f, 0xe4, 0x89, 0x9b, + 0x12, 0x57, 0x91, 0x95, 0x1e, 0x9c, 0xd6, 0x9f, 0x7a, 0x4c, 0xd0, 0xc4, + 0x11, 0xb8, 0x64, 0xc9, 0x49, 0xc6, 0xce, 0x37, 0xbf, 0xe8, 0xd0, 0x7f, + 0x0a, 0x2e, 0xd0, 0xdf, 0x85, 0xd7, 0xb9, 0x84, 0x03, 0x25, 0xbc, 0xfe, + 0x52, 0x4d, 0xd3, 0xb5, 0x3a, 0x71, 0x4d, 0xd9, 0x08, 0xa1, 0x07, 0xc3, + 0xd8, 0x25, 0x4c, 0x61, 0x73, 0x4a, 0xd9, 0x12, 0x3f, 0x11, 0xd7, 0xe7, + 0x88, 0x60, 0x02, 0xfb, 0x75, 0xc7, 0x64, 0x0c, 0xc5, 0x2c, 0xa1, 0x62, + 0x6f, 0x3a, 0xb6, 0xf8, 0xfc, 0xf7, 0x4f, 0xe5, 0x9d, 0x2b, 0xd6, 0xd2, + 0x62, 0x83, 0x4f, 0xa0, 0xdc, 0xf1, 0xbd, 0x98, 0xae, 0x46, 0x30, 0xbe, + 0xb7, 0xb4, 0x74, 0xa5, 0xbd, 0xa6, 0x37, 0x9e, 0x24, 0x69, 0x5e, 0x49, + 0x32, 0x53, 0x61, 0xfc, 0x7b, 0xf5, 0xce, 0x5c, 0x7e, 0xa3, 0xf5, 0xb1, + 0x14, 0x1f, 0xdb, 0x90, 0x85, 0x06, 0x54, 0x15, 0x0d, 0x7c, 0x01, 0xba, + 0xbc, 0xea, 0x15, 0xe5, 0x8d, 0xe9, 0xc3, 0x5f, 0x11, 0x18, 0x91, 0xef, + 0x23, 0x2a, 0x38, 0x05, 0x83, 0xff, 0x67, 0x1b, 0x81, 0xc4, 0x55, 0x97, + 0x07, 0x57, 0xf7, 0xfa, 0x9a, 0x35, 0xdb, 0x0f, 0x1f, 0x39, 0xf4, 0x8d, + 0x1c, 0x86, 0x16, 0xdc, 0x6c, 0x55, 0x96, 0x70, 0x24, 0xc5, 0x70, 0x86, + 0xe5, 0x94, 0x21, 0x3f, 0xe7, 0x93, 0xec, 0xdd, 0xe3, 0xce, 0xea, 0x00, + 0x87, 0xfb, 0x55, 0xe9, 0x0b, 0xc3, 0x9d, 0xf1, 0x06, 0x2a, 0x9a, 0x4d, + 0xbe, 0xe9, 0x4c, 0xee, 0x8d, 0xc0, 0x93, 0x57, 0x3f, 0xd2, 0xf1, 0x99, + 0x32, 0x45, 0x9d, 0x62, 0x2c, 0xa6, 0xaa, 0xbb, 0xab, 0x37, 0x6f, 0xe6, + 0x62, 0xb8, 0x50, 0x6a, 0xc5, 0xa9, 0xc5, 0xae, 0x57, 0xe1, 0x46, 0x03, + 0xbf, 0x8d, 0xde, 0x62, 0xd3, 0xb0, 0x38, 0x66, 0x99, 0xce, 0x19, 0x7e, + 0x67, 0x7e, 0xea, 0x48, 0x89, 0xf1, 0xaa, 0xb9, 0xbb, 0x2d, 0x6f, 0x90, + 0x4c, 0xc4, 0x34, 0xed, 0x77, 0x10, 0x69, 0xa7, 0x46, 0x5c, 0xab, 0x59, + 0x13, 0x88, 0x81, 0xe4, 0xe7, 0x5a, 0x5c, 0x6d, 0xa7, 0x55, 0xf0, 0x02, + 0xa5, 0xe4, 0xad, 0xc9, 0x25, 0x7b, 0x8a, 0xbf, 0xa2, 0x99, 0x4d, 0xff, + 0x4f, 0x3e, 0x9a, 0x1f, 0x06, 0xc0, 0xdb, 0x89, 0xfe, 0x9c, 0x11, 0x57, + 0xb5, 0xcb, 0x38, 0x06, 0x15, 0x27, 0x60, 0x1f, 0x53, 0x3e, 0xa9, 0x56, + 0xbe, 0x05, 0x34, 0xc1, 0xcd, 0x0e, 0x48, 0x8d, 0x2a, 0xc8, 0x3e, 0xee, + 0xd6, 0x4b, 0x21, 0xea, 0xa4, 0xf7, 0x56, 0xe2, 0xfd, 0x8e, 0x58, 0xfd, + 0xbc, 0x05, 0xa5, 0xa6, 0x2e, 0xc2, 0xcf, 0x55, 0x42, 0xae, 0xba, 0x49, + 0x35, 0x03, 0xea, 0xa0, 0xdd, 0x06, 0xb7, 0x32, 0xa8, 0x1c, 0x1d, 0x00, + 0xa8, 0x19, 0xdf, 0xb8, 0x95, 0xe4, 0x5a, 0x2a, 0x5e, 0x24, 0x2e, 0x7b, + 0x46, 0x57, 0x4d, 0x06, 0x20, 0x17, 0x09, 0x72, 0x2f, 0x1d, 0x61, 0xc1, + 0x69, 0x1a, 0x96, 0x73, 0x63, 0x57, 0x07, 0xf7, 0x70, 0xd8, 0x26, 0x23, + 0xe2, 0x16, 0x2e, 0xdb, 0x92, 0x22, 0x96, 0xfa, 0xe0, 0x65, 0xa8, 0x3b, + 0xc3, 0x9c, 0x12, 0x80, 0x81, 0xf5, 0xf0, 0xc0, 0xa8, 0x47, 0xda, 0xee, + 0xf8, 0xea, 0xfe, 0x29, 0x7d, 0xde, 0x9f, 0xa3, 0x76, 0x1d, 0xb2, 0x8f, + 0xa9, 0xa6, 0x97, 0xff, 0x95, 0x62, 0xb4, 0x25, 0xdc, 0xeb, 0x10, 0x44, + 0xc7, 0x98, 0x18, 0x30, 0x0a, 0x0e, 0xbe, 0xdd, 0x3b, 0xff, 0x01, 0xfa, + 0xf2, 0x63, 0x99, 0x61, 0x83, 0xb4, 0x0c, 0x23, 0x64, 0xaa, 0xe5, 0x0a, + 0xc1, 0x27, 0xed, 0xbb, 0x5f, 0x65, 0x5e, 0x95, 0x4c, 0xdd, 0x75, 0xe0, + 0xa4, 0x71, 0xc8, 0xa0, 0x42, 0xd2, 0x17, 0xab, 0x4c, 0xf0, 0x3e, 0x7b, + 0x7e, 0x11, 0x83, 0x6e, 0x12, 0x57, 0x57, 0x9c, 0xa5, 0xb2, 0xb8, 0xc9, + 0x79, 0x8a, 0x69, 0x28, 0x0f, 0x7a, 0x42, 0x7f, 0x80, 0x2e, 0x09, 0x87, + 0xbc, 0xea, 0xb8, 0x53, 0x0e, 0x93, 0x0c, 0xc2, 0xf4, 0x34, 0xd6, 0x0a, + 0xe7, 0x5d, 0x9b, 0x62, 0x4c, 0xb1, 0x41, 0x7d, 0x38, 0x37, 0x38, 0x5a, + 0xbd, 0xe2, 0xee, 0xa7, 0x72, 0x70, 0x2d, 0xc3, 0xe2, 0x60, 0x2c, 0x07, + 0x60, 0x3a, 0x85, 0xd5, 0xf6, 0x31, 0xb3, 0x39, 0x65, 0x5a, 0x92, 0xa4, + 0x78, 0x15, 0x46, 0xce, 0x58, 0xa7, 0x05, 0x3f, 0x2a, 0xd3, 0xa9, 0x2c, + 0x83, 0xc5, 0x52, 0x2c, 0xc5, 0x81, 0xee, 0x1b, 0x67, 0x10, 0xe5, 0x33, + 0x94, 0x87, 0x8c, 0x5e, 0x63, 0x66, 0xbc, 0x97, 0x47, 0xfb, 0x2f, 0x3e, + 0x7e, 0x5e, 0x17, 0x2d, 0xe9, 0x71, 0xab, 0x97, 0x68, 0x90, 0x82, 0xcf, + 0xf3, 0xc1, 0x8e, 0x03, 0x1a, 0x37, 0xf9, 0xd9, 0x67, 0x72, 0x21, 0xff, + 0x1c, 0x3b, 0x8f, 0xd7, 0x02, 0xf1, 0x25, 0x93, 0xe9, 0x9e, 0x67, 0xe4, + 0xa4, 0x08, 0x81, 0xf1, 0x94, 0x9a, 0x03, 0xcd, 0xb4, 0xe5, 0xb9, 0xc9, + 0x7b, 0x1c, 0xbe, 0x82, 0xf0, 0x55, 0xd3, 0xf0, 0xf7, 0x58, 0x53, 0xbe, + 0xf7, 0x05, 0xbb, 0x6a, 0x0a, 0x69, 0x11, 0x9c, 0xaf, 0xe6, 0x55, 0xc4, + 0xec, 0x05, 0xbb, 0x4a, 0x76, 0x85, 0x60, 0x86, 0xbf, 0xa9, 0x50, 0x31, + 0x35, 0xe1, 0x26, 0x6a, 0x78, 0xf9, 0xc8, 0xae, 0xdf, 0x0e, 0xec, 0xf8, + 0x66, 0xfb, 0x23, 0xdb, 0xc3, 0x39, 0xf8, 0x62, 0xea, 0x93, 0x8c, 0xff, + 0x14, 0x73, 0x48, 0x24, 0x71, 0x4e, 0xd5, 0xce, 0xce, 0x0b, 0xf6, 0xe8, + 0x63, 0x78, 0x91, 0x49, 0x55, 0x32, 0x8c, 0x30, 0xbc, 0xe4, 0x3c, 0xb0, + 0x90, 0x78, 0x7f, 0xa1, 0x64, 0x06, 0x77, 0x56, 0xc2, 0xd5, 0xe7, 0x3b, + 0xcb, 0x82, 0x32, 0xfc, 0xc7, 0x6e, 0x1a, 0xc7, 0xa3, 0x71, 0xf3, 0x69, + 0x01, 0x15, 0x1e, 0xab, 0x84, 0xb9, 0x44, 0x70, 0x28, 0x29, 0xf3, 0x08, + 0xb2, 0x9c, 0xe4, 0x72, 0x24, 0xff, 0x68, 0xcb, 0xfe, 0xb2, 0x6b, 0x52, + 0x42, 0x16, 0xb3, 0x02, 0x76, 0x90, 0xd3, 0x80, 0xd3, 0xab, 0xfe, 0x3a, + 0xe7, 0x51, 0x1d, 0x55, 0x93, 0x32, 0xc6, 0xce, 0xd4, 0x89, 0x19, 0x9a, + 0x04, 0xe7, 0x58, 0x8a, 0x39, 0x7d, 0x11, 0x6b, 0xf6, 0x94, 0x95, 0x35, + 0x43, 0x42, 0xc4, 0xed, 0x7a, 0x2b, 0xb1, 0xac, 0x84, 0x5d, 0x20, 0xfb, + 0x87, 0xe3, 0x4a, 0xfd, 0x55, 0x8e, 0x97, 0x1c, 0xa8, 0x05, 0x55, 0x8f, + 0x87, 0xe6, 0x0b, 0xc5, 0x22, 0x0e, 0x78, 0xee, 0x48, 0xe4, 0x8e, 0x85, + 0xdd, 0x63, 0x17, 0x61, 0x96, 0xbe, 0xe0, 0xe5, 0x79, 0xa6, 0x45, 0x78, + 0xb6, 0xa5, 0x5a, 0xd5, 0xf8, 0xe0, 0xd8, 0x82, 0x20, 0x57, 0x33, 0x75, + 0xda, 0x36, 0x72, 0xd3, 0xd7, 0x0a, 0xf3, 0xfc, 0xe6, 0xdc, 0x6c, 0x7e, + 0xa3, 0x71, 0x87, 0xa2, 0x44, 0xeb, 0x48, 0x8b, 0x07, 0xb9, 0x56, 0x6b, + 0x0d, 0x27, 0x0f, 0x4b, 0x7f, 0x58, 0x3d, 0xab, 0xff, 0xcc, 0x84, 0x16, + 0xfb, 0x55, 0x32, 0xdd, 0xb5, 0x0c, 0x38, 0x5b, 0x22, 0x1b, 0x98, 0x19, + 0xfe, 0xdf, 0x38, 0xb0, 0x6f, 0x21, 0x2e, 0x16, 0xf2, 0x54, 0xf8, 0x38, + 0x2b, 0x7b, 0x90, 0x88, 0x68, 0x27, 0x72, 0x59, 0x39, 0xa0, 0x49, 0xc8, + 0xcd, 0xe7, 0x8c, 0xd8, 0x68, 0xff, 0xc4, 0x80, 0x2a, 0x79, 0xd9, 0x02, + 0x23, 0x41, 0x92, 0x73, 0xe5, 0x71, 0xe5, 0x55, 0x8c, 0xcb, 0x2b, 0x46, + 0x7f, 0x4a, 0xf5, 0x4c, 0x01, 0x6a, 0x9f, 0xba, 0x45, 0x0d, 0x81, 0x8e, + 0x3c, 0x9e, 0x08, 0x61, 0x26, 0x2b, 0x63, 0x58, 0x8d, 0xb4, 0xce, 0x9f, + 0x72, 0x3c, 0x91, 0x2a, 0x69, 0x0a, 0x02, 0x73, 0xa8, 0x95, 0x3e, 0x5b, + 0xe0, 0x44, 0x23, 0x9f, 0xe2, 0xc8, 0x11, 0x65, 0xf6, 0xdd, 0xc4, 0xe9, + 0x91, 0xe3, 0x79, 0x7f, 0x0e, 0x8e, 0x12, 0xcc, 0x60, 0xbf, 0x76, 0xb4, + 0x96, 0x68, 0x70, 0xed, 0xac, 0xeb, 0xb8, 0x2d, 0xb6, 0x63, 0x88, 0x79, + 0x6a, 0x04, 0xe7, 0x02, 0xcd, 0xf3, 0x3a, 0x89, 0x05, 0x8a, 0x34, 0x9a, + 0x55, 0x38, 0xd4, 0xd1, 0xb0, 0xde, 0xab, 0xe7, 0xc9, 0xa6, 0x62, 0x85, + 0x7f, 0x7d, 0x15, 0x13, 0xa6, 0x80, 0x2a, 0x25, 0xfe, 0x47, 0xeb, 0x73, + 0x86, 0xcf, 0x71, 0x07, 0xb4, 0xb0, 0xce, 0x8a, 0x31, 0x0b, 0x00, 0xc9, + 0x06, 0x62, 0x76, 0x0e, 0x20, 0x7f, 0xdc, 0xf2, 0xb4, 0xb3, 0x35, 0x78, + 0xbe, 0x12, 0x72, 0x6c, 0x47, 0x42, 0x1e, 0x74, 0x08, 0x11, 0x69, 0xbd, + 0x7c, 0x55, 0xf5, 0x61, 0xbf, 0x9b, 0x6d, 0x84, 0xbf, 0xa6, 0xda, 0x5e, + 0x10, 0x67, 0x41, 0x7e, 0x96, 0x18, 0x15, 0x1a, 0x42, 0xdf, 0x07, 0xd1, + 0x84, 0x63, 0xe2, 0xd9, 0xd9, 0x1f, 0x75, 0xbd, 0x1a, 0x03, 0x64, 0xa3, + 0x7a, 0x15, 0x72, 0x16, 0x73, 0x35, 0x8b, 0x09, 0x34, 0xdd, 0xee, 0xf8, + 0xdc, 0x8c, 0x8a, 0x1b, 0x10, 0x67, 0xc4, 0x46, 0xfd, 0xbc, 0xdc, 0xad, + 0xa3, 0x99, 0xa3, 0x77, 0xd5, 0x19, 0x05, 0x44, 0x7c, 0x3a, 0xbb, 0xae, + 0xe8, 0xe8, 0x90, 0xf7, 0x28, 0xf4, 0xc1, 0x47, 0x92, 0x46, 0x9a, 0x9a, + 0xef, 0x32, 0x2e, 0x80, 0xf9, 0x74, 0x9e, 0xac, 0x48, 0x9c, 0x05, 0xd6, + 0x2d, 0x6f, 0x1d, 0xee, 0xa5, 0xd7, 0x06, 0x24, 0x68, 0x55, 0xf0, 0xbb, + 0x4d, 0x7f, 0xc9, 0x8c, 0x3b, 0xa7, 0xc3, 0x03, 0xfc, 0xf1, 0x34, 0x14, + 0x53, 0x11, 0x00, 0x68, 0x74, 0x16, 0x99, 0x65, 0xb2, 0xcd, 0x1a, 0x09, + 0x17, 0x80, 0x1d, 0x8a, 0x66, 0xa8, 0x2c, 0x93, 0xcd, 0x6f, 0x32, 0x52, + 0x93, 0x0d, 0x2b, 0xaa, 0x05, 0x1d, 0x45, 0xe4, 0x62, 0x70, 0xe8, 0x64, + 0x48, 0xb8, 0x4d, 0xd8, 0xa0, 0x85, 0x04, 0x42, 0xcd, 0x19, 0x5c, 0x4f, + 0x05, 0xa8, 0x1f, 0x06, 0x58, 0xd4, 0x16, 0x62, 0x48, 0x57, 0x31, 0x9d, + 0xc8, 0x63, 0x53, 0x38, 0xde, 0x32, 0xb4, 0x1d, 0x52, 0x11, 0xe1, 0x82, + 0xf1, 0x7e, 0x9d, 0x39, 0x2b, 0x8f, 0xa2, 0x6f, 0x93, 0xc7, 0x60, 0x98, + 0x43, 0xec, 0x37, 0x72, 0x79, 0x0a, 0x1d, 0x32, 0xbd, 0x7c, 0x4f, 0x6f, + 0xf4, 0xb4, 0x63, 0x18, 0x9d, 0x20, 0xca, 0xfb, 0x4c, 0x12, 0x6a, 0x73, + 0x06, 0x1a, 0xfb, 0xc7, 0x03, 0xbf, 0x9d, 0x3e, 0xe3, 0x67, 0x8f, 0x0d, + 0x71, 0xce, 0x0b, 0xfb, 0x56, 0x32, 0x71, 0x82, 0xea, 0xb4, 0xff, 0x91, + 0x96, 0xa4, 0x9a, 0x59, 0x90, 0x20, 0x5b, 0xd3, 0x1f, 0x15, 0x00, 0xac, + 0xd4, 0xce, 0x7f, 0xaf, 0x96, 0x78, 0x04, 0xcf, 0x48, 0x75, 0xec, 0x3e, + 0x00, 0x69, 0x67, 0x9d, 0xe2, 0x2a, 0x87, 0x6d, 0x81, 0xfe, 0xf1, 0xcf, + 0x5b, 0xc2, 0x23, 0x41, 0x15, 0xa4, 0xcd, 0xb6, 0x38, 0x1c, 0xc0, 0xe3, + 0x45, 0xd6, 0x4b, 0x50, 0x8b, 0x92, 0xc7, 0xd2, 0x23, 0x89, 0x58, 0x15, + 0x65, 0x3f, 0xca, 0x56, 0x05, 0xa0, 0x82, 0x5a, 0x48, 0x12, 0xd4, 0x87, + 0x2f, 0x8d, 0x19, 0x96, 0xbd, 0x96, 0xce, 0xaa, 0x76, 0x59, 0x78, 0x63, + 0x39, 0x34, 0x74, 0x8c, 0x4d, 0xdf, 0xd8, 0x8e, 0x2b, 0x33, 0x52, 0xeb, + 0x1e, 0xeb, 0x23, 0x9c, 0x01, 0x67, 0x28, 0x6a, 0x40, 0xd0, 0x14, 0xa2, + 0x58, 0x03, 0xe6, 0x87, 0x97, 0x1f, 0xfc, 0x79, 0x03, 0x52, 0x8e, 0xa4, + 0xa5, 0x39, 0x48, 0x4a, 0xed, 0x52, 0x96, 0x58, 0xe0, 0xf3, 0x7a, 0xba, + 0x8b, 0x2b, 0x38, 0xd1, 0x10, 0x63, 0x9d, 0xdf, 0x6e, 0xf8, 0xca, 0x03, + 0xec, 0x5e, 0x26, 0xc4, 0x4e, 0xaf, 0x0c, 0xf8, 0x1e, 0x5c, 0x38, 0x3b, + 0xb2, 0x55, 0xea, 0x6a, 0x7c, 0xec, 0x42, 0x54, 0x55, 0xf5, 0x59, 0xc6, + 0x0d, 0xfe, 0x16, 0x5d, 0x05, 0x2e, 0x09, 0xa0, 0x53, 0xc6, 0xe2, 0x60, + 0x12, 0x5d, 0xc7, 0x52, 0x71, 0x02, 0x5b, 0x6d, 0xe9, 0xad, 0x6b, 0xe6, + 0x0d, 0xb8, 0x6f, 0xbf, 0xa4, 0x12, 0x5d, 0x8e, 0x4a, 0x02, 0xba, 0xf4, + 0xd5, 0x2c, 0xaa, 0x22, 0x23, 0xd8, 0xb0, 0x31, 0xdf, 0x7d, 0x5b, 0x40, + 0x7d, 0xab, 0xf3, 0x8e, 0x5c, 0xee, 0x7d, 0xae, 0x34, 0x68, 0x2e, 0x97, + 0x38, 0x60, 0x5c, 0x03, 0xc3, 0x30, 0x4b, 0xb8, 0x91, 0xdd, 0xad, 0x27, + 0x04, 0xad, 0x39, 0x15, 0xcb, 0x9a, 0x84, 0xa2, 0x9e, 0xb0, 0x02, 0xcc, + 0x41, 0x28, 0xa9, 0x2d, 0x80, 0x6e, 0x8d, 0xe1, 0x5c, 0x9f, 0xab, 0x87, + 0x0b, 0x9c, 0x98, 0x02, 0x75, 0x62, 0x93, 0xb4, 0x92, 0xf9, 0x2c, 0xc8, + 0xd6, 0xbf, 0x2d, 0xe6, 0x66, 0xa6, 0x87, 0x7c, 0xfd, 0xd5, 0xe3, 0x1b, + 0xaa, 0xba, 0x73, 0xe2, 0x59, 0xef, 0x03, 0x97, 0x62, 0x18, 0xde, 0x3b, + 0x73, 0x58, 0x2e, 0xe2, 0x6b, 0x7b, 0x5d, 0xbe, 0x4b, 0x7d, 0xa8, 0x96, + 0x84, 0x4e, 0x39, 0xc0, 0xa8, 0x98, 0xef, 0x8d, 0xa4, 0x2a, 0xb2, 0x33, + 0x04, 0x2e, 0x4a, 0x1f, 0x89, 0x86, 0xf3, 0x09, 0xd2, 0x50, 0x61, 0x12, + 0xf1, 0x4e, 0x18, 0xda, 0x33, 0x9c, 0x94, 0x4a, 0x8c, 0xd7, 0x87, 0xd1, + 0x47, 0x37, 0x33, 0xbe, 0x4d, 0x59, 0x11, 0x88, 0x01, 0x41, 0x9d, 0xb5, + 0xb8, 0xcc, 0x48, 0x63, 0x7f, 0x81, 0x26, 0x47, 0x02, 0x0e, 0xe0, 0x0a, + 0xe1, 0xb1, 0xe7, 0xa7, 0x14, 0x66, 0xcb, 0xcf, 0x6e, 0x55, 0x33, 0xb6, + 0x10, 0x44, 0xf2, 0x5e, 0x73, 0x24, 0xe2, 0x70, 0xc4, 0x90, 0x62, 0xf1, + 0xfa, 0x35, 0xd0, 0xc6, 0xaa, 0x8a, 0xf9, 0x79, 0xd1, 0x3f, 0xed, 0x96, + 0x04, 0x21, 0xb3, 0xac, 0x87, 0x88, 0xa8, 0x7c, 0x37, 0x15, 0x3a, 0x9d, + 0x4b, 0xd5, 0xf0, 0x00, 0xdc, 0x05, 0xa1, 0x3d, 0x0f, 0xff, 0xab, 0xf9, + 0x0a, 0x68, 0x01, 0x85, 0x28, 0x7b, 0x91, 0x86, 0x4a, 0xe9, 0x3f, 0xf1, + 0x44, 0x7e, 0x8b, 0xed, 0xb0, 0xd1, 0x6e, 0xbf, 0x89, 0xe8, 0x5d, 0x61, + 0xd3, 0xe7, 0xb4, 0x01, 0x74, 0x38, 0x3c, 0xb8, 0xdb, 0x3d, 0x7e, 0xe5, + 0xfc, 0x8f, 0x2a, 0x6d, 0xfa, 0x60, 0x5e, 0x84, 0x03, 0x4b, 0x24, 0x0a, + 0x1c, 0x65, 0x82, 0xd2, 0x17, 0xa5, 0x3b, 0xdf, 0x27, 0xb0, 0xef, 0xb8, + 0xad, 0x19, 0x61, 0x5f, 0xd5, 0xaa, 0x0b, 0x40, 0x0e, 0x9f, 0x31, 0xd5, + 0xfd, 0xbd, 0xc1, 0x3d, 0x6a, 0x84, 0xe5, 0x0d, 0x31, 0xfa, 0x1f, 0x1e, + 0x8d, 0x90, 0x65, 0x9f, 0x47, 0x20, 0xaa, 0xe3, 0xc7, 0x08, 0x6b, 0xb9, + 0x1b, 0x74, 0xc8, 0xf0, 0x40, 0x91, 0x2c, 0xd2, 0x92, 0x2e, 0x0e, 0x45, + 0x7e, 0xdc, 0xfb, 0xb4, 0xd5, 0x99, 0xe3, 0x50, 0x17, 0x9a, 0x80, 0x84, + 0xc6, 0x42, 0x75, 0xd6, 0xeb, 0xc4, 0xa4, 0xb6, 0xbf, 0xeb, 0xfa, 0xdd, + 0x58, 0x39, 0x61, 0x6c, 0xe5, 0x66, 0x29, 0x68, 0x22, 0x71, 0x1e, 0x92, + 0xa9, 0x5e, 0x67, 0xc1, 0x30, 0x3f, 0x06, 0x37, 0xd6, 0x4c, 0xaf, 0x68, + 0x06, 0xb3, 0xf9, 0x02, 0x75, 0xcd, 0x6a, 0xea, 0xc0, 0x28, 0xa6, 0xb7, + 0xbb, 0xce, 0xaa, 0x91, 0x48, 0x6a, 0x68, 0x57, 0x13, 0xff, 0x42, 0x53, + 0x96, 0x72, 0xe9, 0x1f, 0x30, 0xb7, 0x60, 0x1a, 0x45, 0x1c, 0x77, 0x67, + 0x1d, 0x1b, 0xf8, 0x3b, 0xae, 0x3c, 0xd0, 0xb3, 0x41, 0xfd, 0xcc, 0x0f, + 0x31, 0xa3, 0xc1, 0x89, 0x24, 0xca, 0xde, 0xcb, 0x3c, 0x2c, 0xac, 0x1c, + 0x0b, 0x43, 0xee, 0xb4, 0x11, 0x22, 0x24, 0xa3, 0x67, 0xf0, 0x1e, 0x52, + 0x7d, 0xc7, 0x70, 0x07, 0x57, 0x6c, 0x75, 0x9d, 0xa1, 0xac, 0x3c, 0xb6, + 0xa7, 0x37, 0xb6, 0x14, 0xf0, 0x1a, 0xa3, 0x47, 0x4d, 0xe8, 0xd4, 0x3e, + 0x72, 0x08, 0x6c, 0xfe, 0x69, 0x54, 0xe0, 0x69, 0xc9, 0xb3, 0x54, 0x28, + 0x1c, 0xd8, 0xb2, 0xc6, 0x20, 0x07, 0x98, 0xaf, 0xbb, 0x59, 0x1c, 0x1d, + 0x14, 0x33, 0x5f, 0xa3, 0x31, 0x97, 0xb4, 0x70, 0xa6, 0xce, 0x0d, 0x8f, + 0x9d, 0xe7, 0x41, 0x83, 0xa5, 0xfe, 0x08, 0x20, 0x67, 0xd2, 0xe0, 0xe9, + 0x7c, 0x56, 0xee, 0x0f, 0x0a, 0x94, 0x83, 0x0c, 0x7f, 0xe3, 0x26, 0x7b, + 0xd2, 0x09, 0x45, 0xa3, 0x5a, 0x7c, 0x70, 0xcc, 0x3a, 0x92, 0xbe, 0x5f, + 0x42, 0xa2, 0x09, 0x80, 0x90, 0xdc, 0x35, 0xc9, 0x8d, 0xb1, 0xe6, 0x72, + 0xee, 0x67, 0x33, 0x01, 0x74, 0xc5, 0x25, 0x4f, 0x8a, 0x29, 0x08, 0xd8, + 0x2b, 0x40, 0x18, 0x72, 0x0d, 0x48, 0x0b, 0x15, 0x07, 0x06, 0xb4, 0x52, + 0x94, 0x24, 0x61, 0xc9, 0xe6, 0x5b, 0x57, 0x69, 0xa6, 0xa3, 0xfd, 0x99, + 0xc6, 0x7a, 0x42, 0x92, 0x21, 0x9b, 0x45, 0xc4, 0x04, 0x2a, 0x94, 0x50, + 0xe4, 0x72, 0x11, 0x6a, 0x57, 0x6a, 0x82, 0xe1, 0x12, 0x3a, 0x20, 0x5d, + 0xb5, 0x15, 0xc6, 0xbe, 0x2d, 0xaa, 0xb7, 0x42, 0x55, 0x91, 0xc5, 0x04, + 0xd8, 0x03, 0x78, 0x69, 0x5c, 0x9c, 0xd7, 0x10, 0x6d, 0x17, 0xc7, 0xba, + 0xe4, 0x82, 0xc1, 0x4e, 0xa3, 0x71, 0x7b, 0x47, 0x81, 0x6d, 0x6b, 0xbd, + 0xc0, 0xfa, 0x0f, 0xec, 0x85, 0x24, 0xfb, 0x7d, 0x58, 0x5e, 0x38, 0xdc, + 0xf3, 0x5f, 0x41, 0xb7, 0x31, 0x49, 0x27, 0xf2, 0x39, 0xf9, 0xe7, 0xe6, + 0x19, 0xb5, 0x24, 0x78, 0xb9, 0x9d, 0x4e, 0xcf, 0x8e, 0x1b, 0x32, 0xe2, + 0xba, 0xf5, 0xb3, 0x59, 0xc1, 0xab, 0xd3, 0xae, 0xf6, 0xf6, 0x1f, 0x04, + 0x34, 0x86, 0x5d, 0x24, 0xa5, 0x03, 0xea, 0xfd, 0x70, 0xc9, 0xa7, 0x17, + 0xd3, 0x5a, 0x67, 0xe2, 0x19, 0x05, 0x3a, 0x50, 0xbc, 0x28, 0x08, 0xa3, + 0xac, 0x75, 0x52, 0x23, 0xe9, 0xb7, 0xb8, 0xa9, 0x12, 0x56, 0xf3, 0x9c, + 0xed, 0x7a, 0xab, 0x13, 0x31, 0x4c, 0x8b, 0xa8, 0xdb, 0x7c, 0x84, 0xc7, + 0xba, 0xa9, 0x83, 0x4d, 0x41, 0x6a, 0xfb, 0x78, 0x12, 0xd4, 0x6f, 0xb2, + 0xbc, 0xf6, 0x6f, 0xc2, 0xbd, 0x71, 0x7c, 0x13, 0xcf, 0x8d, 0xe6, 0x86, + 0x07, 0x83, 0x12, 0x38, 0xb1, 0x3d, 0x39, 0xe6, 0x50, 0x5c, 0x29, 0x55, + 0x27, 0xd7, 0x42, 0xbb, 0xc9, 0x33, 0x5e, 0xed, 0xf4, 0xdc, 0x05, 0x13, + 0xb1, 0xc3, 0xfd, 0xc7, 0x32, 0x58, 0x02, 0x45, 0xbb, 0x36, 0xe5, 0x24, + 0x0e, 0x37, 0x85, 0x18, 0xa5, 0x2d, 0x57, 0xb1, 0x04, 0x48, 0xb2, 0x51, + 0xa4, 0x18, 0xcc, 0x4a, 0x44, 0x9b, 0xc4, 0xb2, 0x66, 0x15, 0x89, 0x9f, + 0x28, 0xca, 0x29, 0x38, 0x90, 0x11, 0x07, 0xbd, 0x63, 0x97, 0x1c, 0xcb, + 0x51, 0x66, 0x85, 0xb0, 0xa9, 0x9e, 0xd8, 0xbe, 0x9b, 0x95, 0x50, 0x1e, + 0x53, 0x89, 0x51, 0x67, 0xe5, 0x07, 0xb7, 0x21, 0xa5, 0x0f, 0xd0, 0x06, + 0xf9, 0xfd, 0xc7, 0xaf, 0x0e, 0x6c, 0x6c, 0xf3, 0x0f, 0xe9, 0x0b, 0x55, + 0xa6, 0x69, 0xf1, 0xc4, 0xc9, 0x98, 0x30, 0xf5, 0xed, 0xfa, 0x1b, 0xd6, + 0x31, 0xfe, 0xd3, 0x24, 0x31, 0xae, 0xcb, 0x16, 0xf2, 0x01, 0x9d, 0x08, + 0xab, 0xd4, 0xed, 0x43, 0x13, 0x8a, 0x9c, 0x65, 0xca, 0xaf, 0xdd, 0xf2, + 0xd9, 0x3a, 0xe4, 0x4d, 0x4d, 0x0f, 0x3f, 0x73, 0x73, 0x1b, 0x27, 0x8e, + 0x4e, 0xce, 0x1c, 0x5d, 0x16, 0xfa, 0xdf, 0x03, 0x8c, 0x8b, 0xb1, 0x34, + 0xc1, 0xde, 0xe2, 0xec, 0x0a, 0x1b, 0xc3, 0x6a, 0x99, 0x2a, 0x34, 0x5a, + 0x74, 0x91, 0x64, 0x40, 0x6f, 0xd1, 0xa8, 0x00, 0xc1, 0xed, 0x70, 0x41, + 0x97, 0xd9, 0xc0, 0xf1, 0x64, 0x97, 0x61, 0xda, 0x0f, 0xd9, 0x55, 0x7f, + 0x9c, 0xbb, 0xce, 0x66, 0xce, 0x43, 0xc1, 0x33, 0x5d, 0x9e, 0xc2, 0x32, + 0x48, 0xc6, 0x60, 0x7d, 0xae, 0xda, 0x06, 0x36, 0x53, 0xbc, 0x85, 0x08, + 0xee, 0xc4, 0x19, 0x6a, 0xf5, 0xb6, 0xe3, 0xb4, 0x52, 0x70, 0x73, 0xd4, + 0x9a, 0x75, 0xa0, 0x57, 0x40, 0xf3, 0x61, 0x16, 0x2c, 0x48, 0x3f, 0x13, + 0xdc, 0x55, 0x7f, 0x94, 0x78, 0x23, 0x87, 0x3e, 0x86, 0xdd, 0x3e, 0x27, + 0xf9, 0xcf, 0x06, 0x6c, 0xcf, 0x97, 0x4d, 0x51, 0xa2, 0xe7, 0x96, 0x64, + 0x1e, 0x56, 0x51, 0x31, 0x1e, 0xbf, 0xce, 0x71, 0x97, 0x90, 0x87, 0xc2, + 0x34, 0x74, 0x95, 0x25, 0xb6, 0x68, 0x1d, 0x3c, 0x88, 0xbe, 0xce, 0x62, + 0x47, 0x97, 0xfc, 0x77, 0x2b, 0x8f, 0xe3, 0x8a, 0xff, 0xef, 0x70, 0x09, + 0x88, 0xc7, 0xb1, 0xda, 0x8b, 0x1c, 0x69, 0xa5, 0x5e, 0xe6, 0xf9, 0x2d, + 0x52, 0x32, 0x5b, 0x7f, 0x63, 0xa7, 0xc6, 0xa5, 0xd2, 0xf0, 0xd1, 0xd4, + 0x4c, 0x7f, 0x94, 0x2e, 0xf7, 0x67, 0x76, 0x25, 0x0e, 0x91, 0x86, 0x05, + 0xa3, 0xa4, 0xaf, 0x1a, 0x1c, 0xb3, 0x5e, 0x52, 0x69, 0x64, 0x7b, 0xa5, + 0x00, 0xd0, 0x18, 0x76, 0xa7, 0xbb, 0x1e, 0x80, 0x33, 0x43, 0xbb, 0x22, + 0x35, 0xe1, 0x15, 0xd9, 0x6f, 0xb8, 0xc5, 0x61, 0x35, 0xe4, 0xc4, 0x21, + 0x18, 0x78, 0x40, 0x2c, 0x2b, 0x42, 0xb4, 0xa2, 0x27, 0x20, 0x1b, 0xc6, + 0x53, 0x75, 0x11, 0xfe, 0x1d, 0xec, 0x0e, 0xbe, 0xa9, 0x7e, 0x3b, 0x19, + 0x72, 0xbe, 0x55, 0xaa, 0xc8, 0x89, 0x5e, 0xf5, 0x53, 0xf3, 0x8d, 0xaa, + 0xab, 0xa2, 0xec, 0x0b, 0x6b, 0x06, 0x2c, 0xe3, 0xd6, 0xb8, 0x52, 0x70, + 0x50, 0x48, 0x52, 0x46, 0x93, 0x08, 0xab, 0xec, 0xe8, 0xe9, 0xda, 0xb7, + 0x24, 0xce, 0xc3, 0xe3, 0xdf, 0xdb, 0x76, 0x19, 0xbd, 0xca, 0x0c, 0x68, + 0xb4, 0xdb, 0xfd, 0xb7, 0xc8, 0x2b, 0x1b, 0x70, 0xf4, 0x95, 0x8d, 0x0a, + 0xd8, 0xd1, 0xc5, 0xd7, 0xa5, 0x2a, 0xa2, 0x67, 0x2d, 0x9f, 0x7b, 0x0a, + 0xee, 0x40, 0x9e, 0x49, 0x29, 0xb2, 0x5c, 0x45, 0xa4, 0xb5, 0x08, 0xb9, + 0x80, 0x1e, 0xb7, 0x60, 0xb9, 0xc9, 0xf0, 0xe9, 0xfb, 0x9e, 0x58, 0x18, + 0x0f, 0x3a, 0x59, 0x51, 0xbb, 0xfd, 0x83, 0x93, 0xe3, 0xd4, 0xbe, 0xb5, + 0x1e, 0x09, 0xfb, 0x3e, 0x7c, 0x6e, 0x22, 0x5c, 0xe1, 0x27, 0xde, 0x8d, + 0x48, 0xff, 0x4b, 0x36, 0xa8, 0x73, 0xad, 0xe0, 0x4a, 0x78, 0x36, 0x5b, + 0x45, 0xd3, 0x98, 0x24, 0xf6, 0x23, 0x86, 0x6e, 0x5c, 0x38, 0xe8, 0x5d, + 0x09, 0x8a, 0x89, 0x31, 0x04, 0x01, 0xcb, 0x38, 0x2f, 0xdc, 0xa1, 0xb6, + 0x32, 0x79, 0x48, 0x61, 0x29, 0xb8, 0x8f, 0x35, 0x0c, 0xb7, 0x13, 0xb7, + 0x4c, 0x74, 0x5c, 0xbe, 0x61, 0x02, 0x31, 0xd9, 0x8f, 0xbe, 0xbb, 0xdb, + 0x50, 0xfb, 0x12, 0x06, 0xd7, 0xac, 0xd0, 0xf1, 0xc9, 0xaf, 0x9b, 0x5f, + 0xc4, 0x51, 0x2f, 0xb2, 0x99, 0xe4, 0xfd, 0xaa, 0x43, 0xea, 0xcd, 0xf2, + 0xaa, 0xf9, 0xbe, 0x80, 0xf3, 0x17, 0x52, 0x18, 0x21, 0x03, 0x0f, 0x04, + 0x82, 0xbd, 0x4c, 0x34, 0x40, 0x4b, 0x7d, 0x51, 0x8c, 0xc4, 0x09, 0x64, + 0xbb, 0x8b, 0xd5, 0xc5, 0xc8, 0xdf, 0x63, 0x33, 0x8a, 0x48, 0x7d, 0x97, + 0x5e, 0x54, 0xa5, 0xea, 0x32, 0x7c, 0x47, 0xbc, 0xd4, 0x2d, 0x90, 0x56, + 0xaa, 0xd8, 0xb5, 0x12, 0x77, 0x4b, 0x86, 0x45, 0x57, 0xf5, 0x31, 0xdb, + 0x4c, 0xef, 0xa5, 0x85, 0x26, 0x4a, 0xee, 0xa5, 0x3f, 0x5a, 0xcc, 0x84, + 0x7c, 0x8f, 0x2a, 0x42, 0x0e, 0x1b, 0xa1, 0x7d, 0x27, 0x76, 0x34, 0x73, + 0x5e, 0x51, 0x6f, 0xd6, 0x49, 0xc1, 0x8a, 0xd3, 0x85, 0x62, 0x12, 0xc9, + 0x45, 0x79, 0xc2, 0xb0, 0xa8, 0xfd, 0xf5, 0x25, 0x1d, 0x65, 0x74, 0xd0, + 0x3f, 0x11, 0x65, 0xcc, 0x2b, 0x4e, 0x21, 0x5f, 0x0f, 0x40, 0x5d, 0x05, + 0xc5, 0xc2, 0x02, 0x42, 0x4f, 0x68, 0x7a, 0x97, 0x53, 0x69, 0x03, 0x3e, + 0x1f, 0x27, 0xda, 0x89, 0x5d, 0x8b, 0x2e, 0xdf, 0x32, 0xa7, 0x46, 0x4b, + 0x70, 0x17, 0x31, 0xa8, 0x96, 0xe8, 0xf1, 0x1a, 0x6e, 0x7b, 0xd1, 0xb3, + 0xe2, 0x19, 0x58, 0xf8, 0xb1, 0xbd, 0x91, 0x1f, 0xf5, 0x8d, 0xc2, 0x5e, + 0x52, 0x7c, 0xf2, 0x53, 0xd0, 0x23, 0xa0, 0x9e, 0xb0, 0x57, 0x8d, 0x17, + 0x5a, 0x9e, 0x01, 0xfd, 0x39, 0x29, 0x85, 0x69, 0xa1, 0xac, 0xe2, 0x89, + 0x0c, 0xc2, 0xf9, 0x85, 0xef, 0xd8, 0xc1, 0x5f, 0x34, 0xae, 0xc3, 0x41, + 0xd7, 0x5e, 0x87, 0xce, 0xae, 0xa5, 0x37, 0xb6, 0x95, 0xe4, 0xff, 0x03, + 0x6d, 0x17, 0x0d, 0x3c, 0x21, 0x9e, 0x21, 0x6b, 0x37, 0x62, 0x45, 0x7e, + 0xa8, 0x88, 0x0a, 0x9a, 0x91, 0x21, 0x2d, 0x63, 0xc1, 0xa6, 0xc5, 0x2a, + 0xf6, 0x37, 0xda, 0xd6, 0x94, 0x24, 0x17, 0xb9, 0x9a, 0x44, 0x07, 0x75, + 0x19, 0x5b, 0xe2, 0xd4, 0xfa, 0x08, 0xb2, 0x12, 0x1c, 0x3d, 0x17, 0xca, + 0x2a, 0xc9, 0xc1, 0xfb, 0x4d, 0xa1, 0x4f, 0x0c, 0x57, 0x7c, 0x92, 0x35, + 0xa6, 0xc8, 0x70, 0x56, 0x64, 0x43, 0x1d, 0x68, 0x6b, 0x4c, 0x18, 0xf8, + 0x5c, 0xa3, 0xe2, 0x96, 0x91, 0xf5, 0x4d, 0x6b, 0x24, 0x62, 0x48, 0x03, + 0xba, 0xf4, 0x99, 0xb9, 0x12, 0x61, 0xf9, 0x04, 0x47, 0x31, 0x72, 0x80, + 0x2f, 0xe3, 0x81, 0x47, 0xb1, 0x06, 0x6c, 0x83, 0xa4, 0x35, 0xed, 0x3b, + 0xc6, 0x01, 0x3e, 0x59, 0xf9, 0x00, 0x4e, 0x5d, 0x93, 0x58, 0xeb, 0x71, + 0x1f, 0x1f, 0xa8, 0x25, 0x29, 0x0c, 0x8e, 0x24, 0xdd, 0x34, 0x70, 0x98, + 0x39, 0xd7, 0x61, 0x0b, 0x44, 0x51, 0x06, 0xaf, 0xab, 0x91, 0xe4, 0xbf, + 0x11, 0x74, 0x52, 0xb1, 0xe4, 0x7e, 0x1d, 0xd1, 0x08, 0x63, 0x64, 0x56, + 0xcd, 0x59, 0x56, 0xd8, 0xa3, 0xc5, 0xb7, 0xa1, 0x72, 0x42, 0x33, 0x56, + 0x55, 0x8a, 0x81, 0x16, 0x79, 0xfe, 0x49, 0xf3, 0xc8, 0xf6, 0xb7, 0x58, + 0xcb, 0x74, 0x5a, 0xb7, 0x68, 0x06, 0x00, 0x24, 0xdb, 0x47, 0x52, 0x38, + 0xcd, 0xf3, 0x1a, 0xbe, 0x19, 0x18, 0x54, 0xdf, 0xb6, 0x49, 0xff, 0x00, + 0xb8, 0x26, 0xba, 0xcf, 0x99, 0x70, 0xaf, 0x6b, 0xf6, 0xbd, 0x59, 0x2d, + 0xce, 0xb6, 0x99, 0x6b, 0x5d, 0xcd, 0xc3, 0xd1, 0x89, 0x0f, 0x85, 0x30, + 0xd5, 0xc9, 0x98, 0x3f, 0x78, 0x66, 0xed, 0x53, 0x33, 0x1b, 0x0e, 0x26, + 0xfe, 0x98, 0x4e, 0x83, 0xd7, 0x6a, 0x92, 0xc7, 0xc1, 0x47, 0xee, 0x3b, + 0xa8, 0x20, 0xfa, 0xf4, 0x98, 0x79, 0x5a, 0x7b, 0x2b, 0x20, 0xe3, 0x50, + 0xc3, 0xf5, 0x04, 0xf6, 0x3a, 0xcd, 0xe0, 0x68, 0x20, 0x6f, 0xb0, 0x56, + 0x6c, 0x21, 0x26, 0xee, 0x11, 0x76, 0xdf, 0xd7, 0x82, 0xae, 0x8e, 0xa8, + 0x6a, 0x1c, 0xde, 0x87, 0x90, 0x90, 0x57, 0x25, 0x85, 0xba, 0x39, 0x60, + 0x5c, 0x77, 0x9f, 0xb0, 0x5b, 0x18, 0x87, 0x6b, 0x99, 0x9f, 0xe8, 0xf6, + 0xbd, 0xe2, 0xc6, 0x12, 0x8b, 0xc2, 0x32, 0x9d, 0x01, 0x7b, 0x05, 0x0f, + 0xa5, 0x75, 0x0a, 0xa9, 0xbc, 0x5c, 0x51, 0x3d, 0xca, 0x9f, 0x2c, 0x00, + 0xb8, 0x24, 0x40, 0xc1, 0xcc, 0xf5, 0x3b, 0xb3, 0xae, 0x3a, 0x3c, 0x9f, + 0x65, 0x5f, 0x4d, 0xf5, 0x5b, 0x30, 0x6e, 0x16, 0xb9, 0x52, 0x8c, 0x56, + 0xa2, 0x06, 0xaf, 0xda, 0x23, 0xef, 0x1a, 0x08, 0x4d, 0xf1, 0xe0, 0x59, + 0xa2, 0xe9, 0x69, 0x7f, 0x8b, 0xb3, 0x60, 0x85, 0x18, 0xd6, 0x18, 0x2e, + 0xfb, 0x2c, 0x4d, 0x83, 0x68, 0xb8, 0x7f, 0x63, 0x15, 0x88, 0xa2, 0x8f, + 0xb9, 0x3c, 0xdb, 0x54, 0x91, 0xa9, 0x03, 0x8b, 0x62, 0xcc, 0x69, 0x98, + 0xa4, 0x3b, 0xec, 0xfb, 0x52, 0x4c, 0xf4, 0x82, 0xab, 0xb4, 0x02, 0x09, + 0x56, 0x41, 0xeb, 0x2f, 0xda, 0xab, 0x6b, 0x4a, 0x5b, 0x8e, 0x4a, 0x62, + 0x57, 0xc3, 0xde, 0xd5, 0xdf, 0x35, 0x3e, 0x3c, 0x6c, 0x86, 0x2d, 0x85, + 0xc0, 0xed, 0x69, 0x29, 0x8b, 0xd1, 0x86, 0xa4, 0x37, 0xff, 0xcb, 0x62, + 0x82, 0x72, 0x40, 0xed, 0x6b, 0x18, 0x49, 0x35, 0x04, 0xc8, 0xcb, 0x96, + 0x27, 0x5c, 0x6f, 0x80, 0x7f, 0x12, 0xed, 0x18, 0x17, 0x6c, 0x28, 0x4f, + 0x82, 0x2c, 0xc9, 0x7a, 0x50, 0x26, 0x41, 0x96, 0x9c, 0x4f, 0x69, 0x8c, + 0xd3, 0x32, 0xf5, 0x3a, 0x03, 0x20, 0xb0, 0x53, 0x6a, 0x0b, 0xae, 0x51, + 0x9f, 0x88, 0x04, 0x5b, 0xb1, 0xce, 0x34, 0x12, 0x78, 0xcc, 0x55, 0x27, + 0x28, 0xbb, 0x63, 0x38, 0xcb, 0xee, 0x46, 0x17, 0x89, 0x92, 0x3a, 0x47, + 0x11, 0x86, 0xdc, 0x06, 0x3f, 0xc4, 0xc1, 0x83, 0x89, 0x1b, 0x03, 0xc2, + 0x19, 0xe2, 0x93, 0x04, 0x45, 0xfa, 0xb9, 0xdd, 0x25, 0x85, 0xfe, 0x62, + 0x68, 0x85, 0xa4, 0xc7, 0xbc, 0xb2, 0x91, 0x3f, 0x39, 0x91, 0x30, 0x91, + 0xfb, 0x54, 0x37, 0xd2, 0x90, 0x8a, 0x9f, 0x20, 0x3a, 0x6f, 0x1f, 0xdb, + 0x5f, 0x2e, 0x5f, 0x26, 0x06, 0x37, 0xa0, 0x92, 0x38, 0x53, 0x76, 0xc0, + 0x9e, 0x79, 0x5e, 0xa9, 0x78, 0x7e, 0x77, 0x70, 0x83, 0xd4, 0xd1, 0x11, + 0x45, 0xbf, 0xa3, 0x65, 0x7a, 0x17, 0x76, 0xc6, 0x4f, 0x0e, 0x59, 0x83, + 0xf1, 0xc1, 0xc5, 0xf0, 0x5a, 0xd8, 0x21, 0x07, 0xdc, 0xb9, 0xca, 0x1e, + 0xa0, 0x78, 0xab, 0x7a, 0x12, 0x7a, 0x1d, 0x2a, 0x45, 0xfa, 0xd8, 0xdc, + 0xa7, 0x30, 0x36, 0x6c, 0x12, 0xad, 0xa3, 0xbc, 0xe3, 0xcb, 0xa5, 0x25, + 0x00, 0xef, 0x4a, 0xba, 0x42, 0x04, 0x0a, 0x73, 0x43, 0x00, 0x79, 0xa8, + 0xee, 0xdf, 0x71, 0x4d, 0x02, 0x2c, 0xb2, 0x0f, 0xe2, 0x85, 0x06, 0x91, + 0x31, 0x95, 0x51, 0xb8, 0x5c, 0xfd, 0x8e, 0x52, 0x1b, 0xbe, 0x01, 0xe5, + 0x4d, 0xf6, 0x8b, 0xc6, 0x9a, 0x0b, 0xcd, 0x44, 0x60, 0xab, 0x7c, 0x44, + 0xcb, 0x69, 0xbf, 0x60, 0x55, 0x87, 0x4c, 0xa5, 0x6b, 0xc2, 0x57, 0xe1, + 0xed, 0x9a, 0xa9, 0xcd, 0x0b, 0x31, 0xab, 0xf8, 0x35, 0x87, 0x1b, 0x3d, + 0xa5, 0xff, 0xf3, 0x0b, 0xdd, 0x90, 0x78, 0xbe, 0x8a, 0xef, 0xd8, 0x79, + 0x5c, 0x04, 0xd0, 0xac, 0x4e, 0x2c, 0x7d, 0x9c, 0x4e, 0x48, 0x7c, 0x36, + 0xb6, 0x20, 0xba, 0x70, 0x0a, 0xed, 0x26, 0x96, 0xad, 0xc8, 0xd8, 0x57, + 0x81, 0xc4, 0x3a, 0xbb, 0x95, 0x01, 0x97, 0xc4, 0x5e, 0xaf, 0x5d, 0x7f, + 0x7f, 0x47, 0x92, 0x5e, 0x26, 0xd2, 0x9a, 0x60, 0x2d, 0xb4, 0x8a, 0x27, + 0xb6, 0xe4, 0x8d, 0xc9, 0x49, 0xcb, 0x7c, 0xab, 0x7e, 0x0b, 0x51, 0x05, + 0xca, 0xfc, 0x7b, 0x70, 0x43, 0x04, 0x54, 0xb8, 0x11, 0x86, 0x7a, 0x77, + 0xb8, 0x35, 0x68, 0x1e, 0x8a, 0xb8, 0x57, 0xdb, 0x46, 0xe1, 0x2e, 0xfe, + 0x92, 0x97, 0xc0, 0x69, 0x5f, 0x89, 0x63, 0xf1, 0x75, 0xc6, 0xe8, 0x22, + 0x3d, 0x8b, 0x72, 0xff, 0xba, 0x6b, 0x03, 0x31, 0x35, 0x84, 0x1b, 0xdd, + 0xae, 0x2b, 0xaa, 0x89, 0xfe, 0xa9, 0x42, 0xa1, 0xc3, 0x4c, 0xe6, 0x79, + 0xe0, 0x07, 0xe9, 0xb8, 0x2e, 0xf8, 0x20, 0x1d, 0xe0, 0x6d, 0x6d, 0x26, + 0x09, 0x29, 0xf2, 0x9d, 0x2e, 0x8f, 0x5b, 0x73, 0xfd, 0xfe, 0xc7, 0xf2, + 0x91, 0x29, 0xae, 0x9d, 0xac, 0x86, 0x5c, 0x6f, 0x71, 0xc8, 0x13, 0x8c, + 0xd7, 0xdf, 0x6b, 0x53, 0xe7, 0x70, 0x24, 0x3e, 0xe7, 0xb0, 0x20, 0x28, + 0x8c, 0xfb, 0xa4, 0xd5, 0xd5, 0xfc, 0xc7, 0x98, 0x79, 0x9e, 0xbd, 0xd0, + 0x7e, 0xdb, 0x51, 0x20, 0x57, 0xe3, 0x7d, 0x81, 0x3b, 0x3f, 0xb8, 0x05, + 0x2f, 0xce, 0x78, 0xf9, 0xce, 0x8e, 0x9a, 0x17, 0x1d, 0x47, 0x0e, 0xd8, + 0x71, 0xe6, 0x5f, 0x1f, 0xe1, 0xea, 0xef, 0xb7, 0x58, 0xf3, 0xe7, 0xec, + 0x07, 0x9d, 0xd0, 0x6e, 0xb0, 0x3d, 0x82, 0xdb, 0x56, 0x8b, 0x66, 0xe4, + 0xaf, 0x82, 0xf2, 0xc5, 0xcb, 0x2e, 0x45, 0x0c, 0x4a, 0x34, 0x87, 0x07, + 0xf4, 0xfe, 0xbc, 0xd4, 0xa0, 0xbe, 0x69, 0x47, 0x96, 0x4d, 0x1e, 0xbf, + 0x23, 0x80, 0xb5, 0xd5, 0x01, 0xb8, 0x7a, 0xb7, 0x47, 0x3e, 0xc1, 0xb0, + 0xe1, 0xd2, 0x20, 0xce, 0xb6, 0x7f, 0x3d, 0x77, 0xde, 0xc6, 0x65, 0x85, + 0x44, 0xc3, 0xe2, 0x70, 0x4e, 0xde, 0x49, 0x22, 0xde, 0x43, 0xef, 0x4d, + 0x97, 0xde, 0x83, 0x41, 0xcc, 0x1c, 0x47, 0xbf, 0x10, 0xc8, 0xb3, 0xf8, + 0x38, 0xc0, 0xd9, 0x71, 0xd5, 0xaf, 0x9a, 0xf4, 0xba, 0xf3, 0xc2, 0x74, + 0x9f, 0x8e, 0x10, 0x1a, 0xed, 0x74, 0x02, 0xa3, 0xc2, 0x3c, 0x52, 0xd1, + 0x59, 0x1f, 0xd9, 0x95, 0x6b, 0xbe, 0xc4, 0xe9, 0x64, 0xe2, 0x70, 0xb6, + 0xe5, 0x1a, 0x29, 0x40, 0xab, 0xd2, 0xd1, 0x2c, 0xee, 0xe8, 0xff, 0x5c, + 0x01, 0xf2, 0xe2, 0xcf, 0xb2, 0xc4, 0xf0, 0x7e, 0x1d, 0x84, 0xdf, 0x88, + 0x0b, 0xad, 0x46, 0xbc, 0x9c, 0xf2, 0xc2, 0x38, 0x66, 0xdb, 0x63, 0x0a, + 0x16, 0x2e, 0xcf, 0x6d, 0x5a, 0xeb, 0x8d, 0x2e, 0xe3, 0xbc, 0x63, 0xfc, + 0x9c, 0xd7, 0xda, 0xfc, 0x85, 0x77, 0x08, 0x91, 0x32, 0x67, 0xb3, 0xf9, + 0x25, 0x4a, 0x4b, 0x2b, 0x37, 0x73, 0x43, 0xba, 0xc1, 0xb4, 0xdf, 0x64, + 0x6d, 0x88, 0xa5, 0x59, 0xcc, 0x12, 0x24, 0x23, 0xc4, 0xbe, 0xa9, 0x34, + 0xed, 0xd9, 0x24, 0x78, 0xdd, 0xb0, 0xc6, 0xec, 0x55, 0xae, 0xcd, 0x77, + 0x02, 0xf3, 0xc8, 0xd7, 0x4d, 0xbc, 0x28, 0xbf, 0xd7, 0x18, 0xb2, 0x1f, + 0xfe, 0x14, 0x59, 0x65, 0x42, 0xd8, 0x41, 0x09, 0xea, 0x0e, 0x11, 0xa6, + 0x64, 0x13, 0x6a, 0xa6, 0x0c, 0xfd, 0xf9, 0xdc, 0xf2, 0xc4, 0x8d, 0x7b, + 0x13, 0x0e, 0x11, 0xca, 0x48, 0xc4, 0xb8, 0xbc, 0xcb, 0xb3, 0x62, 0x39, + 0xaa, 0x92, 0x3c, 0x92, 0x36, 0x7e, 0x62, 0x22, 0xbe, 0x72, 0xa9, 0xeb, + 0x9d, 0x83, 0x26, 0x98, 0x43, 0xdb, 0x72, 0xe3, 0xcd, 0x90, 0x8a, 0x6f, + 0x5f, 0x88, 0x2e, 0x8d, 0xa6, 0xba, 0x2b, 0xb5, 0x28, 0x8d, 0x91, 0x4f, + 0x30, 0xf1, 0x2c, 0xfe, 0xeb, 0x4a, 0xb3, 0x28, 0x55, 0x48, 0x3d, 0x97, + 0xf5, 0x18, 0xbd, 0xba, 0x48, 0xff, 0xfe, 0x63, 0x51, 0xa1, 0x71, 0x16, + 0x11, 0xe9, 0xee, 0xa1, 0x18, 0x9f, 0x8d, 0xdd, 0x8f, 0x4d, 0xe1, 0x4d, + 0x1b, 0x5f, 0xfc, 0x9e, 0xcd, 0xef, 0x7e, 0x9c, 0x2d, 0xaa, 0x62, 0x06, + 0xec, 0x3f, 0xb5, 0x8a, 0x52, 0x24, 0x45, 0x14, 0x30, 0xf5, 0x24, 0x7b, + 0x23, 0xbb, 0x14, 0xb0, 0xf4, 0x97, 0x53, 0x9a, 0x3f, 0x0a, 0xa2, 0xfb, + 0x03, 0x14, 0xfd, 0x8b, 0xdf, 0x89, 0xdb, 0x10, 0xec, 0xf2, 0xea, 0x29, + 0xc4, 0x5e, 0xc8, 0xec, 0xed, 0x6a, 0x7d, 0x4d, 0x43, 0x99, 0xc6, 0x5a, + 0xd9, 0x71, 0x7d, 0x18, 0xee, 0xcb, 0x44, 0x36, 0xe3, 0xe1, 0x7c, 0x14, + 0xc5, 0xfe, 0xa3, 0xb6, 0x21, 0x74, 0xe7, 0x24, 0xf7, 0xd9, 0xdc, 0x4e, + 0x49, 0x7b, 0xb2, 0xb5, 0xdf, 0x70, 0x6a, 0xcd, 0xc6, 0xee, 0x62, 0x95, + 0x5c, 0x10, 0xe7, 0x71, 0x4c, 0x38, 0xc3, 0x1d, 0x9f, 0xf0, 0x77, 0x46, + 0xdc, 0xc1, 0x7e, 0xb0, 0x1d, 0x46, 0x81, 0xb6, 0xe5, 0x77, 0x00, 0x80, + 0x14, 0x52, 0xa1, 0x6e, 0xfc, 0x33, 0xb7, 0x70, 0xcf, 0xfb, 0x23, 0x49, + 0x74, 0xf8, 0x02, 0xd7, 0x72, 0x0c, 0x5a, 0xd5, 0xfa, 0xe6, 0x55, 0x66, + 0x08, 0x9c, 0x9a, 0x87, 0x44, 0x92, 0xa3, 0xa0, 0xe3, 0x9c, 0xdf, 0x4b, + 0x55, 0xff, 0x1c, 0xe7, 0xda, 0xdb, 0xc8, 0xa8, 0xa2, 0x89, 0x03, 0xd4, + 0xd9, 0xfd, 0xd9, 0xf3, 0x67, 0xde, 0xa1, 0x42, 0xa4, 0x05, 0xbf, 0xd6, + 0x4b, 0x3d, 0x6b, 0x30, 0xc5, 0xbf, 0xbb, 0xbb, 0x3c, 0xa5, 0xbc, 0x19, + 0x63, 0x0f, 0x32, 0x88, 0x8a, 0x2a, 0xa8, 0x91, 0xaa, 0x28, 0xb0, 0x6d, + 0x74, 0x19, 0xe4, 0x50, 0x3a, 0x71, 0x29, 0x7e, 0x77, 0xa5, 0x75, 0x77, + 0x04, 0x74, 0xae, 0xc1, 0xef, 0x51, 0xb7, 0x71, 0x2f, 0xae, 0x6c, 0x24, + 0x22, 0x03, 0xb4, 0xa5, 0x84, 0xdc, 0xfc, 0x79, 0x69, 0xbd, 0xf5, 0x32, + 0x82, 0xb1, 0x64, 0x18, 0xc1, 0x7a, 0x9b, 0x01, 0x6e, 0x34, 0xa4, 0x0a, + 0xa2, 0x1b, 0x26, 0x9a, 0x70, 0xb2, 0x7e, 0x4c, 0xa2, 0xf2, 0xfc, 0x6a, + 0x32, 0x47, 0x5d, 0x84, 0x7f, 0xe0, 0x90, 0xdc, 0x3c, 0x48, 0xff, 0xff, + 0x8a, 0x74, 0xf3, 0x98, 0x49, 0x25, 0x15, 0xa5, 0x12, 0x75, 0x1c, 0xfc, + 0xb0, 0x4b, 0x9d, 0x5b, 0x80, 0x95, 0x16, 0x4a, 0x66, 0x36, 0xc5, 0xe2, + 0x78, 0xdc, 0xce, 0x63, 0xd9, 0xfc, 0xb1, 0x40, 0x78, 0x91, 0x4e, 0x02, + 0xcf, 0x03, 0x68, 0x22, 0x96, 0x46, 0x76, 0x0b, 0x0e, 0x3d, 0x63, 0x20, + 0x32, 0x25, 0xf6, 0x9c, 0x42, 0xc7, 0x7b, 0x28, 0xdb, 0xcc, 0xef, 0xeb, + 0x81, 0x82, 0xb5, 0xdb, 0x43, 0xce, 0x28, 0x81, 0xd6, 0xfa, 0x05, 0x6a, + 0xaa, 0xa2, 0xe0, 0x37, 0xbc, 0x1b, 0x24, 0x93, 0x08, 0x40, 0x67, 0xfe, + 0xdb, 0xce, 0x81, 0x1c, 0xe0, 0x58, 0x2d, 0xc8, 0xde, 0x8a, 0x53, 0x6d, + 0xee, 0x59, 0x49, 0x87, 0x6a, 0x7c, 0x3b, 0x48, 0xdf, 0x26, 0xe9, 0xf6, + 0xc5, 0x0c, 0x63, 0x41, 0x44, 0x48, 0x92, 0xa8, 0x1a, 0x66, 0x64, 0xb3, + 0x8f, 0x51, 0xfa, 0x6b, 0x78, 0x83, 0x9f, 0x96, 0x58, 0xa9, 0x7b, 0x5e, + 0x1f, 0xaf, 0xde, 0x95, 0xa7, 0xa6, 0xc5, 0x09, 0x17, 0x9b, 0x76, 0x93, + 0xa7, 0xba, 0x52, 0x2d, 0xba, 0x80, 0x23, 0x24, 0x5c, 0x0d, 0x11, 0xc4, + 0xc6, 0x1d, 0x14, 0x5a, 0x08, 0xc5, 0xc7, 0x8b, 0x76, 0xba, 0x05, 0x5e, + 0xf2, 0x78, 0xca, 0xa3, 0x76, 0xec, 0x56, 0x76, 0xfd, 0x66, 0x8b, 0xa6, + 0x07, 0x7f, 0xa8, 0xde, 0xc4, 0x80, 0x5b, 0x78, 0xe9, 0xee, 0x4d, 0x37, + 0x31, 0xee, 0x49, 0xd6, 0x50, 0xa2, 0xdd, 0x81, 0x7a, 0x20, 0x0f, 0x3f, + 0x42, 0x30, 0x26, 0xa0, 0xc6, 0x7b, 0x91, 0xa6, 0x50, 0x87, 0x65, 0xd9, + 0x85, 0xdd, 0x73, 0x72, 0x5f, 0x26, 0x1f, 0x11, 0x7d, 0xed, 0xf1, 0xa5, + 0x4d, 0x9a, 0x11, 0xfa, 0x0b, 0xf0, 0xcf, 0xb4, 0x8d, 0xe4, 0x68, 0xf8, + 0xab, 0x81, 0x4c, 0xb3, 0x05, 0xea, 0x3f, 0x65, 0x4d, 0x94, 0x61, 0x94, + 0x00, 0x50, 0xa1, 0x24, 0xc0, 0xe9, 0x7c, 0x23, 0xce, 0xbe, 0x5d, 0x54, + 0x1a, 0x44, 0x82, 0x53, 0xb6, 0x83, 0x9f, 0x92, 0x32, 0x0c, 0xf2, 0xac, + 0xef, 0x92, 0x37, 0x52, 0xe1, 0xa0, 0x94, 0xe0, 0xd6, 0x3f, 0x04, 0x57, + 0xb7, 0x22, 0xec, 0x6a, 0x28, 0xe1, 0xec, 0xd6, 0xae, 0x2a, 0x39, 0x2b, + 0x39, 0xe6, 0x9a, 0x77, 0x5d, 0xae, 0xc3, 0x75, 0x23, 0x6a, 0x8d, 0xae, + 0x49, 0xf3, 0xe1, 0x98, 0x44, 0xa2, 0x50, 0xb4, 0xb5, 0xdd, 0x77, 0xc6, + 0x00, 0x65, 0xe8, 0x7e, 0xe4, 0x0f, 0x31, 0xbd, 0x14, 0x9a, 0x87, 0x68, + 0x03, 0xf1, 0x30, 0x19, 0x7c, 0x7f, 0xcc, 0x42, 0x1b, 0x3c, 0x8b, 0xb5, + 0x44, 0x3f, 0x6e, 0xed, 0xd7, 0x75, 0xa4, 0x30, 0x26, 0x4d, 0x3d, 0x94, + 0x98, 0x27, 0x54, 0x29, 0x91, 0xf4, 0xf4, 0x6d, 0x22, 0x3c, 0x86, 0x84, + 0x9b, 0x91, 0x58, 0xe6, 0xfe, 0x0d, 0x44, 0xb5, 0xe0, 0x75, 0x48, 0x68, + 0x84, 0x7a, 0x81, 0xb0, 0xb7, 0x3e, 0x6f, 0xc8, 0x09, 0x2e, 0x60, 0x3a, + 0xaf, 0xa5, 0xbc, 0x32, 0x37, 0x4a, 0x2c, 0xa7, 0x9a, 0xbe, 0x45, 0xce, + 0x31, 0xcd, 0x07, 0x6f, 0xae, 0x1e, 0xa7, 0x5a, 0x45, 0x38, 0x9a, 0xf8, + 0x41, 0xe6, 0xa7, 0x58, 0x1b, 0x08, 0x70, 0x4f, 0xe5, 0x33, 0xc6, 0xc2, + 0x18, 0xd8, 0xba, 0x33, 0xbd, 0xef, 0x85, 0x06, 0xd3, 0x87, 0x87, 0x67, + 0x22, 0xfe, 0x6d, 0x34, 0x5c, 0x54, 0x31, 0xde, 0x46, 0xb7, 0xb2, 0x15, + 0xbd, 0x21, 0x0f, 0x8b, 0xde, 0xa9, 0x51, 0x88, 0xbf, 0x37, 0xd1, 0x94, + 0xf9, 0x9a, 0x21, 0x66, 0x66, 0x18, 0x6a, 0xf2, 0x3d, 0xf4, 0xec, 0x43, + 0xb0, 0xbe, 0xc3, 0xc6, 0x12, 0xc6, 0xe6, 0x91, 0xd1, 0x3f, 0xa6, 0x51, + 0xa5, 0x94, 0x5e, 0x00, 0x3c, 0xdf, 0xae, 0x94, 0xbf, 0xff, 0xfd, 0x34, + 0x68, 0x62, 0x87, 0xc2, 0x77, 0x64, 0x79, 0x61, 0x0e, 0xc4, 0x61, 0x14, + 0x2f, 0xc8, 0x57, 0x38, 0xfb, 0xe2, 0x3e, 0x09, 0x5b, 0x7a, 0x93, 0xce, + 0x06, 0xae, 0x27, 0xe5, 0x2d, 0x9b, 0x9b, 0xb0, 0x39, 0x59, 0x48, 0x92, + 0x6c, 0x86, 0xe1, 0xb5, 0x44, 0x34, 0xf1, 0xe0, 0xe0, 0xc5, 0x66, 0x52, + 0x19, 0x98, 0x2d, 0x98, 0xf9, 0xd5, 0x6e, 0x2b, 0xa0, 0x45, 0x4f, 0xc9, + 0x6b, 0xe8, 0xd4, 0x08, 0xf6, 0xaa, 0x61, 0xa2, 0x43, 0x16, 0xf1, 0x82, + 0x8f, 0x68, 0xf3, 0xf0, 0x9a, 0x12, 0x9b, 0x58, 0x0e, 0x52, 0x1c, 0x9b, + 0x62, 0x9f, 0xae, 0x2a, 0xf5, 0x3c, 0x8e, 0x5d, 0xce, 0x50, 0x0a, 0x86, + 0x49, 0x59, 0x84, 0x44, 0xdf, 0x7f, 0xb3, 0xd1, 0x46, 0x28, 0xc3, 0x4a, + 0x5e, 0x05, 0xd3, 0x03, 0xdc, 0x02, 0x15, 0x65, 0xcc, 0x43, 0x1c, 0x25, + 0x4d, 0x01, 0xf9, 0xd9, 0xc5, 0x82, 0x89, 0x65, 0x52, 0x26, 0xa8, 0xa6, + 0x96, 0x65, 0x88, 0x7c, 0xa2, 0xc0, 0xf6, 0x22, 0xda, 0xb1, 0xac, 0xb3, + 0xed, 0xf3, 0x82, 0xf6, 0x30, 0x45, 0x0f, 0x65, 0x77, 0xe1, 0x5f, 0x43, + 0x3c, 0x3c, 0x02, 0xc1, 0xae, 0x1e, 0xbb, 0x07, 0x99, 0xd9, 0xba, 0x2f, + 0x47, 0xdb, 0xa3, 0xd9, 0x35, 0xdc, 0x08, 0x18, 0x7e, 0xf1, 0x56, 0xea, + 0x0f, 0x1e, 0x9a, 0x74, 0x2d, 0x7c, 0xf7, 0xc3, 0x8f, 0x79, 0x60, 0x1b, + 0x8c, 0xfa, 0xec, 0x2d, 0xb0, 0xb6, 0x86, 0xa9, 0xb8, 0x6f, 0xb6, 0xf6, + 0x81, 0xff, 0x84, 0xf0, 0xb1, 0xb3, 0x9b, 0xb9, 0x92, 0x13, 0x18, 0xc3, + 0xda, 0x8a, 0x9b, 0x33, 0x8d, 0x9a, 0xb3, 0x9e, 0x43, 0x87, 0x97, 0x44, + 0x3a, 0xec, 0x36, 0x62, 0x3e, 0x3d, 0x49, 0xbc, 0x23, 0xfb, 0x1a, 0xb9, + 0x52, 0xf4, 0x9b, 0xe6, 0x86, 0xbd, 0x49, 0xc7, 0x1f, 0x42, 0xa7, 0x49, + 0x0e, 0xb6, 0xd5, 0x01, 0x3e, 0x74, 0x2b, 0x77, 0xc1, 0xeb, 0x9c, 0xb5, + 0x11, 0x03, 0x8a, 0xc6, 0x09, 0xcb, 0x48, 0xe3, 0x97, 0x6c, 0x8a, 0x10, + 0xe3, 0x57, 0xe3, 0x47, 0xd5, 0x55, 0x91, 0x80, 0x74, 0xe5, 0xac, 0x8e, + 0xaa, 0x87, 0x7a, 0xa8, 0xb8, 0xe7, 0xff, 0xc8, 0xea, 0x4a, 0x4d, 0x5e, + 0x05, 0x45, 0x6f, 0x44, 0x39, 0x1f, 0x0d, 0xd5, 0xfa, 0x9b, 0x17, 0x35, + 0x03, 0x55, 0x6b, 0x02, 0x2f, 0x34, 0xe8, 0xbf, 0x69, 0x62, 0x4f, 0x71, + 0x9c, 0x7d, 0x52, 0xe5, 0x17, 0xf3, 0xd7, 0xea, 0x51, 0x7e, 0x2a, 0xd6, + 0xfc, 0xc5, 0x72, 0x5a, 0x14, 0xf9, 0x3a, 0xaa, 0xb3, 0xc5, 0x91, 0x4d, + 0xed, 0x63, 0x5d, 0xbd, 0x7a, 0x5a, 0x51, 0x00, 0xa9, 0x61, 0x76, 0xf2, + 0x3e, 0x21, 0x81, 0xc0, 0xec, 0x4e, 0x17, 0xe4, 0xae, 0xc5, 0xad, 0x2f, + 0xb7, 0x14, 0xc2, 0x57, 0xc5, 0x17, 0xc4, 0xe9, 0x35, 0x87, 0xf1, 0x9b, + 0x54, 0xf9, 0x82, 0xd8, 0x11, 0x5a, 0x2f, 0xc8, 0x9b, 0x4b, 0xca, 0xa4, + 0xb8, 0x17, 0x02, 0x85, 0xa1, 0x09, 0xd7, 0x5a, 0xad, 0x9a, 0xa6, 0x40, + 0x79, 0xe2, 0x58, 0xdb, 0x3d, 0x0f, 0xe6, 0x53, 0xda, 0x39, 0x4e, 0x31, + 0xf7, 0xee, 0x6b, 0x6f, 0x2d, 0x08, 0x08, 0x02, 0x6f, 0x81, 0xd7, 0xff, + 0x90, 0x4a, 0xa1, 0x6e, 0x29, 0x0e, 0x5c, 0xdb, 0xc0, 0x44, 0x9b, 0x67, + 0x1d, 0x21, 0x7b, 0xf6, 0x08, 0xe3, 0xb6, 0xd8, 0xbf, 0x3b, 0xe4, 0xdd, + 0xf1, 0x2d, 0x62, 0x34, 0x8b, 0x8a, 0x47, 0x76, 0x41, 0x23, 0xb7, 0x8f, + 0x76, 0x44, 0x60, 0x9d, 0x14, 0x94, 0x67, 0x62, 0x5c, 0x2f, 0x72, 0x67, + 0xdf, 0x6a, 0xa1, 0x1a, 0xa3, 0x0e, 0x83, 0xe2, 0x08, 0x34, 0xc1, 0x97, + 0xc1, 0x3c, 0x0e, 0xc0, 0x0c, 0x3b, 0x32, 0x6e, 0x1a, 0x25, 0x03, 0xc2, + 0x3b, 0x92, 0xdc, 0x2c, 0x93, 0xc6, 0xad, 0xe5, 0x63, 0xb2, 0x0c, 0x3f, + 0x43, 0x33, 0xaa, 0x75, 0x05, 0xe8, 0x9c, 0xec, 0xfd, 0x2a, 0x96, 0x6a, + 0xc6, 0xcc, 0xbc, 0x8d, 0x7e, 0x1f, 0x9c, 0x6d, 0x47, 0x79, 0x8b, 0x78, + 0x2c, 0x13, 0x38, 0x4f, 0xbb, 0x2f, 0x1b, 0x4f, 0x13, 0x3d, 0x0e, 0x6b, + 0x30, 0x8d, 0x52, 0x71, 0xab, 0x6d, 0x5e, 0x3b, 0x03, 0xc7, 0x92, 0x7f, + 0x9e, 0xa7, 0x06, 0xd4, 0x10, 0x41, 0xd4, 0xd7, 0xf9, 0xbf, 0x2d, 0xb2, + 0x38, 0x8b, 0x6b, 0x83, 0x64, 0xfe, 0x2b, 0x1b, 0x34, 0xa1, 0x03, 0xca, + 0x94, 0xfb, 0x0a, 0xf1, 0x44, 0xf2, 0x9c, 0x69, 0x7a, 0x75, 0xa7, 0x87, + 0x2b, 0x27, 0xb9, 0x47, 0xed, 0x12, 0x09, 0x1d, 0x0d, 0xf1, 0x49, 0x9b, + 0xee, 0x68, 0xdd, 0xc0, 0x8d, 0x71, 0xa4, 0x01, 0xa2, 0xd8, 0xf4, 0x8b, + 0x58, 0xf2, 0xd4, 0xfc, 0x5b, 0x77, 0xff, 0xe3, 0x44, 0xb6, 0xd6, 0x0f, + 0x8a, 0x1b, 0x84, 0x8d, 0x6f, 0x2a, 0x3d, 0xeb, 0x09, 0x92, 0x5d, 0xe7, + 0xcb, 0x76, 0xa9, 0x92, 0x3b, 0x8f, 0x37, 0x2a, 0x95, 0xb5, 0xf3, 0x44, + 0xd2, 0x9c, 0x87, 0xd8, 0x65, 0x5b, 0x55, 0xbe, 0x24, 0xa3, 0x8c, 0x48, + 0xa8, 0xc9, 0xe7, 0x55, 0x4a, 0xfd, 0x8f, 0xa1, 0xcb, 0xe1, 0xcf, 0x08, + 0x89, 0xc7, 0x2a, 0xa6, 0xaa, 0xb9, 0xf1, 0x74, 0xa6, 0xbd, 0x6f, 0x9e, + 0x0e, 0x24, 0x73, 0x27, 0xf6, 0xc3, 0xe2, 0x1a, 0xd4, 0x85, 0x45, 0x54, + 0x60, 0x4b, 0xa2, 0xe7, 0x15, 0xe1, 0x94, 0x57, 0xf6, 0x2f, 0x4f, 0x29, + 0x75, 0xf8, 0x5f, 0xea, 0x18, 0x0c, 0xc5, 0xd4, 0x0a, 0x3e, 0x8c, 0x28, + 0xbe, 0x21, 0xf4, 0x6b, 0x29, 0x93, 0x37, 0x19, 0x8a, 0xd5, 0xa9, 0x33, + 0x65, 0xff, 0x6d, 0xeb, 0xb5, 0x73, 0x41, 0x19, 0x9e, 0x36, 0x9b, 0x9c, + 0x3b, 0x1e, 0x06, 0xa9, 0xd1, 0xdc, 0x16, 0x6c, 0x74, 0x34, 0xa2, 0x17, + 0x5e, 0x2c, 0xbf, 0xc6, 0x41, 0xf4, 0xa4, 0xa1, 0x83, 0xd2, 0x99, 0xf1, + 0x8c, 0xf6, 0xd2, 0xe8, 0x14, 0xc2, 0xb4, 0x8c, 0xfa, 0xf9, 0x77, 0x08, + 0x05, 0x55, 0x89, 0x3f, 0x6b, 0x8c, 0x24, 0x88, 0x22, 0x77, 0x63, 0x6f, + 0x2b, 0xea, 0x38, 0x18, 0x16, 0x05, 0x93, 0x26, 0x61, 0x61, 0x07, 0xf8, + 0x86, 0x7a, 0xa2, 0x41, 0xfc, 0x21, 0x21, 0x67, 0xcd, 0x4f, 0x8b, 0xad, + 0x87, 0xe7, 0x13, 0x85, 0x0d, 0xeb, 0x7e, 0xf2, 0x38, 0x21, 0x28, 0x1c, + 0x22, 0xaa, 0x71, 0xa4, 0xdc, 0xce, 0xaa, 0x39, 0x66, 0xcc, 0x05, 0x5b, + 0x7f, 0xbb, 0x4b, 0xe1, 0xfb, 0x3e, 0xa6, 0xdd, 0x1a, 0xd3, 0xdc, 0xc6, + 0x4f, 0x3a, 0x9a, 0x79, 0x11, 0x2d, 0x2a, 0x0b, 0xf3, 0x61, 0xdc, 0x88, + 0x3c, 0x53, 0xb7, 0xfb, 0x2c, 0xf9, 0xb3, 0x5c, 0xbd, 0xfb, 0x2b, 0x5d, + 0x0a, 0x28, 0xe1, 0x8e, 0x26, 0xed, 0x4d, 0x86, 0x7b, 0x73, 0xef, 0xd7, + 0x18, 0x74, 0xfc, 0x01, 0x17, 0x62, 0xdf, 0x0f, 0x4d, 0x06, 0x36, 0xb9, + 0xa6, 0xa7, 0xba, 0xaf, 0x38, 0x8b, 0x91, 0x0f, 0x29, 0x54, 0xc6, 0x42, + 0x0b, 0x56, 0xec, 0xa4, 0x97, 0x3a, 0x9b, 0x62, 0xdc, 0xc0, 0xef, 0x8a, + 0x6e, 0x88, 0xee, 0xb6, 0x35, 0xff, 0xf1, 0xe8, 0x5b, 0xd1, 0xa3, 0x81, + 0xc4, 0x26, 0x85, 0xf3, 0xcf, 0x5d, 0x3a, 0x7a, 0x43, 0x36, 0xee, 0x14, + 0xe0, 0x05, 0x14, 0xe9, 0x2f, 0x6b, 0xe6, 0x71, 0x13, 0x06, 0x81, 0x5d, + 0xd2, 0xf9, 0x65, 0xe0, 0x9e, 0x96, 0xf2, 0xa7, 0x64, 0x24, 0x5f, 0x3e, + 0x51, 0x98, 0xec, 0x69, 0xd4, 0x6e, 0xb3, 0x43, 0x4c, 0x62, 0x8e, 0xc3, + 0xea, 0x75, 0x1a, 0xd9, 0x3d, 0x4a, 0xfa, 0x38, 0x29, 0xaa, 0x96, 0x8b, + 0xeb, 0xe7, 0x47, 0xd5, 0x7e, 0x9b, 0x61, 0x7c, 0x7c, 0x98, 0x1e, 0xd8, + 0xcf, 0x67, 0x1f, 0xe5, 0xe4, 0x41, 0x76, 0xca, 0x7a, 0x15, 0xcb, 0xdf, + 0x53, 0xfd, 0xa3, 0x75, 0x3f, 0x4b, 0xe5, 0xf4, 0x76, 0x14, 0xbd, 0xa9, + 0x41, 0xf4, 0x39, 0x80, 0x36, 0x60, 0xef, 0xea, 0xba, 0xfa, 0x56, 0x80, + 0xe9, 0x58, 0x41, 0x8e, 0x01, 0x9f, 0x28, 0xa7, 0x0b, 0x7a, 0x91, 0x7f, + 0xf9, 0xf8, 0xef, 0x69, 0x05, 0xfd, 0x02, 0x54, 0xbf, 0x4f, 0x53, 0xda, + 0x79, 0x57, 0x93, 0x10, 0x16, 0x55, 0xa7, 0x4e, 0xfd, 0xc2, 0x64, 0xc1, + 0xa7, 0xc0, 0xa6, 0x2a, 0x55, 0xa8, 0xf2, 0x3d, 0x5a, 0xfb, 0xa1, 0x2d, + 0x10, 0x04, 0x8e, 0x48, 0xc1, 0x11, 0x6b, 0x0a, 0xa7, 0x78, 0x77, 0x7b, + 0x12, 0x01, 0x76, 0x22, 0xba, 0x3a, 0x83, 0x6f, 0x67, 0x68, 0x46, 0xb7, + 0x47, 0x0b, 0x8d, 0x71, 0x20, 0x1a, 0x5a, 0xba, 0x61, 0x37, 0x3c, 0xd0, + 0x12, 0xeb, 0x55, 0xf2, 0x4c, 0x33, 0x11, 0x75, 0xf3, 0x7d, 0xa5, 0xdb, + 0x88, 0xcc, 0xf7, 0x60, 0xf2, 0x0e, 0x16, 0xc0, 0xb1, 0xad, 0xa9, 0x95, + 0xc1, 0x6c, 0x0d, 0x61, 0x12, 0xd4, 0x15, 0x23, 0x6a, 0x0e, 0xaa, 0x32, + 0x57, 0xc0, 0x4c, 0x9a, 0x54, 0x3b, 0x17, 0xef, 0x06, 0xbf, 0xa4, 0xa5, + 0x1e, 0x57, 0x9d, 0x20, 0xac, 0x0a, 0xb8, 0x59, 0xeb, 0x0f, 0x15, 0x3b, + 0x78, 0xe4, 0x39, 0x04, 0xcc, 0x61, 0x73, 0xfd, 0x0b, 0x41, 0x74, 0xd2, + 0x81, 0xfc, 0xa1, 0x93, 0x14, 0xac, 0x46, 0x57, 0xbf, 0xd6, 0x31, 0x1d, + 0x0b, 0xd1, 0xd1, 0xdd, 0x69, 0x62, 0x74, 0x4f, 0x82, 0x84, 0x78, 0x75, + 0x81, 0x6c, 0x7d, 0x71, 0xb8, 0x01, 0xa5, 0xf1, 0x14, 0x6b, 0xcc, 0xe4, + 0x44, 0x51, 0x16, 0x3e, 0x02, 0x7f, 0x3b, 0x68, 0x23, 0x8c, 0x1e, 0x6f, + 0xd7, 0x3e, 0xe4, 0xd2, 0x9b, 0x99, 0x22, 0x74, 0x39, 0x14, 0x07, 0xc6, + 0xef, 0x7f, 0xab, 0x43, 0xc3, 0x03, 0x20, 0xfd, 0xa9, 0xc8, 0x1b, 0x28, + 0x6f, 0xf4, 0xf9, 0xcb, 0x40, 0x90, 0x37, 0xcf, 0xa4, 0x1e, 0x5c, 0x7a, + 0x30, 0x0e, 0xd4, 0x47, 0xc6, 0x70, 0xaa, 0x08, 0x39, 0xad, 0x25, 0x2f, + 0x24, 0xb2, 0x37, 0x56, 0x32, 0xec, 0xd9, 0xa3, 0x23, 0xf5, 0xc7, 0x8f, + 0x3a, 0x4a, 0x2a, 0x7c, 0x59, 0x34, 0x67, 0x0b, 0x8d, 0x5f, 0x1b, 0xe8, + 0x83, 0x59, 0xcb, 0x9e, 0xfc, 0x96, 0x29, 0xc2, 0x1c, 0x57, 0xf8, 0x4c, + 0xf5, 0xdd, 0xf1, 0xff, 0x7b, 0x92, 0x37, 0x17, 0x96, 0xa2, 0x7e, 0xfe, + 0x5c, 0x1a, 0xcb, 0xb4, 0xcd, 0xdc, 0x93, 0x92, 0x30, 0xea, 0x54, 0xbe, + 0x1f, 0x89, 0xde, 0xab, 0xdf, 0x08, 0x75, 0x20, 0x37, 0x19, 0x71, 0x8e, + 0xa6, 0xd5, 0x3c, 0xe2, 0x0b, 0x9e, 0x40, 0xae, 0xd5, 0x89, 0x43, 0xe2, + 0x1b, 0x31, 0x66, 0xa8, 0xae, 0x93, 0x38, 0xb2, 0xdb, 0x4f, 0x62, 0x94, + 0x33, 0x62, 0x73, 0x6a, 0x32, 0xff, 0x8f, 0x50, 0x9c, 0x65, 0xe2, 0x23, + 0x22, 0xab, 0x89, 0xd9, 0x68, 0x0b, 0x93, 0x42, 0x08, 0x35, 0x5a, 0x6e, + 0x5d, 0xf5, 0x74, 0xb1, 0x15, 0x11, 0x25, 0x47, 0x30, 0x7e, 0xdb, 0x7c, + 0xcc, 0x0e, 0xa4, 0x62, 0x95, 0x3d, 0x17, 0xd1, 0x69, 0xbd, 0xfa, 0x88, + 0xe1, 0xcb, 0x12, 0x6c, 0x8d, 0xd1, 0x35, 0x2c, 0x0e, 0x72, 0xd6, 0x57, + 0x57, 0x79, 0x0f, 0x68, 0xc3, 0x9c, 0x38, 0x08, 0x2a, 0xfc, 0x83, 0xbd, + 0x9c, 0xd3, 0x67, 0xa7, 0xdd, 0x4c, 0x36, 0x80, 0x3b, 0xab, 0x0c, 0xc4, + 0x36, 0xfd, 0x34, 0x83, 0xef, 0x87, 0x87, 0x8d, 0x8c, 0x5f, 0x9d, 0xbe, + 0x80, 0xc4, 0x8a, 0x93, 0x87, 0x40, 0x5c, 0xd9, 0x0c, 0x72, 0xa5, 0x8b, + 0xcd, 0xb6, 0x5b, 0xc9, 0x01, 0x81, 0x3f, 0x29, 0xc5, 0x98, 0x97, 0xab, + 0xfb, 0x15, 0xf7, 0x9a, 0x73, 0x10, 0x25, 0xff, 0xbb, 0xc0, 0xa8, 0xe1, + 0x06, 0xbc, 0xda, 0xaf, 0x7f, 0xdc, 0x90, 0x1a, 0xe0, 0x74, 0x0d, 0x38, + 0x64, 0xce, 0xc3, 0x32, 0xf2, 0xd7, 0x81, 0x5c, 0x90, 0xf4, 0x40, 0x8f, + 0x94, 0x89, 0x7e, 0x29, 0x35, 0x8b, 0xef, 0x8a, 0x8f, 0xa7, 0x4d, 0x2a, + 0xc1, 0x24, 0x77, 0xf3, 0x50, 0x78, 0xb5, 0xcb, 0x46, 0x1b, 0x30, 0xfb, + 0xc4, 0x5d, 0x7c, 0xd5, 0xea, 0x6f, 0x9c, 0x07, 0xc2, 0x88, 0x70, 0x78, + 0x5f, 0x9b, 0x6e, 0x51, 0x6d, 0x5d, 0xc8, 0x2d, 0xac, 0x32, 0x70, 0x7b, + 0x57, 0xbf, 0x2d, 0x85, 0x40, 0x48, 0xea, 0xd4, 0x85, 0x51, 0x5f, 0x37, + 0xc6, 0xbd, 0x48, 0xd3, 0x98, 0xe8, 0x97, 0x2d, 0x71, 0xd9, 0xd1, 0x18, + 0x67, 0xc9, 0x21, 0x64, 0xa0, 0xa4, 0x40, 0x69, 0x52, 0x55, 0x5a, 0x63, + 0x00, 0x1c, 0x83, 0xed, 0xf5, 0x6f, 0x73, 0x34, 0x03, 0xcd, 0x38, 0x4a, + 0xda, 0x0c, 0x4d, 0x15, 0xc4, 0x58, 0x45, 0xe2, 0x3e, 0xc8, 0xf1, 0x86, + 0x11, 0x81, 0x06, 0xb7, 0x70, 0x17, 0x32, 0xe8, 0xb4, 0x91, 0x78, 0xb3, + 0xe3, 0x12, 0x8e, 0xfc, 0x22, 0xe5, 0x24, 0x68, 0x75, 0x8e, 0xaa, 0x26, + 0x64, 0x28, 0xd5, 0x9c, 0x05, 0xc8, 0x47, 0xdb, 0xab, 0x12, 0xd9, 0x49, + 0xb0, 0xea, 0xb4, 0x57, 0x93, 0xa4, 0x7d, 0xad, 0x91, 0x4b, 0xbc, 0x02, + 0x64, 0xd8, 0xfd, 0xce, 0xc0, 0x34, 0x19, 0x72, 0xb2, 0x88, 0x63, 0xb3, + 0x21, 0xe5, 0xf7, 0xdf, 0xa0, 0x87, 0x3f, 0x4b, 0x9f, 0xd7, 0xb2, 0x6c, + 0x99, 0xd2, 0xbd, 0x10, 0xb7, 0xa5, 0x2f, 0x1b, 0x5f, 0x93, 0xeb, 0x92, + 0xaf, 0x0e, 0xe3, 0x46, 0xba, 0xe3, 0xf0, 0x47, 0x21, 0x03, 0x0c, 0x2e, + 0x41, 0xf0, 0x71, 0x1e, 0xe0, 0x42, 0x40, 0x58, 0x1d, 0x95, 0xd0, 0x43, + 0xe6, 0x6e, 0x4f, 0x14, 0x2e, 0xe7, 0xa4, 0x91, 0xa7, 0x2f, 0x90, 0x58, + 0x3f, 0x5f, 0xf1, 0x6d, 0x83, 0x89, 0x53, 0x85, 0xd0, 0x98, 0x47, 0xa1, + 0x4f, 0x27, 0x16, 0xbc, 0x49, 0x9a, 0x11, 0xd7, 0x63, 0xbd, 0xbd, 0xbf, + 0x7d, 0x0e, 0xf0, 0x31, 0x84, 0xbe, 0x34, 0x49, 0x96, 0x0e, 0xf5, 0x70, + 0x81, 0x0f, 0xcd, 0xe6, 0xa1, 0xde, 0x44, 0xa8, 0x01, 0x9f, 0x6a, 0xe1, + 0x77, 0xa9, 0xbc, 0x54, 0x24, 0xe9, 0xc2, 0x41, 0x1d, 0xb8, 0x36, 0xfc, + 0xc9, 0x29, 0x69, 0x7a, 0xf8, 0x22, 0x95, 0x53, 0x1c, 0xb0, 0xee, 0x77, + 0x8c, 0x74, 0x57, 0x0f, 0x82, 0x74, 0xaa, 0x46, 0x62, 0x2e, 0x46, 0x14, + 0x3b, 0xe9, 0x76, 0x16, 0x09, 0xeb, 0x1e, 0x1a, 0x24, 0x68, 0x7c, 0x7d, + 0x94, 0xb4, 0xc8, 0x75, 0xb5, 0xdb, 0x76, 0x65, 0x4b, 0xd5, 0x77, 0xc6, + 0x8c, 0x79, 0x53, 0x44, 0x03, 0x87, 0x86, 0x5b, 0x34, 0xad, 0xad, 0xe0, + 0x0c, 0x56, 0x9c, 0x29, 0x0a, 0xa2, 0xf2, 0x2f, 0x86, 0xaf, 0xb2, 0xa0, + 0x2e, 0xdd, 0xd3, 0xe5, 0x9b, 0x35, 0x8b, 0xfe, 0xb9, 0xe8, 0x55, 0x01, + 0xa8, 0xd7, 0x42, 0x4e, 0x0e, 0xad, 0x24, 0x9a, 0xbf, 0x2b, 0x35, 0x27, + 0xd2, 0x91, 0x83, 0xc3, 0x2e, 0x38, 0x49, 0xaa, 0x4d, 0x76, 0x42, 0x8b, + 0x05, 0x8f, 0x7b, 0xaa, 0xc9, 0x4f, 0xca, 0x8d, 0x9b, 0x48, 0x84, 0x25, + 0x41, 0x04, 0x85, 0x36, 0x07, 0x38, 0x9e, 0xd9, 0x3e, 0x3e, 0x08, 0xb4, + 0x8d, 0x44, 0x2b, 0x52, 0x00, 0xf4, 0x57, 0x8d, 0xd1, 0xb6, 0x4d, 0xc8, + 0xdb, 0xd5, 0x59, 0xa9, 0x61, 0xef, 0xf1, 0xcf, 0x48, 0x07, 0x9e, 0xd2, + 0xd6, 0x6d, 0xa3, 0xce, 0x2e, 0x18, 0xfd, 0x52, 0x90, 0xfc, 0x07, 0x49, + 0xc4, 0xfe, 0x2a, 0x75, 0x44, 0x0d, 0x2d, 0x0e, 0x53, 0xc3, 0x84, 0x36, + 0x95, 0x95, 0xd7, 0xaa, 0x34, 0xcd, 0x5f, 0x43, 0xde, 0xb5, 0xd1, 0x03, + 0x49, 0x80, 0xab, 0xcc, 0x3a, 0x32, 0xf9, 0xef, 0xaa, 0x6d, 0x08, 0xa1, + 0x6e, 0x0e, 0x09, 0xbe, 0x07, 0x00, 0x7b, 0xba, 0x45, 0x6c, 0x96, 0xf6, + 0xb6, 0x94, 0x7d, 0xf5, 0xfb, 0x4b, 0x7d, 0x40, 0x23, 0xee, 0xb6, 0x64, + 0x7b, 0xcd, 0x1a, 0xea, 0x44, 0xa7, 0x0a, 0xf1, 0x16, 0x46, 0x32, 0x2f, + 0xf0, 0x94, 0xb1, 0x9c, 0x50, 0x03, 0x3e, 0x8c, 0x33, 0x76, 0x88, 0x4c, + 0x70, 0x53, 0x42, 0xda, 0x65, 0xc9, 0xf8, 0x59, 0xf3, 0xe7, 0x2f, 0x9e, + 0x3c, 0xf2, 0x44, 0x2e, 0x4e, 0x51, 0xa1, 0x58, 0x27, 0x27, 0x92, 0x3b, + 0x68, 0x23, 0xc3, 0xb7, 0x16, 0xa4, 0x77, 0x33, 0x7f, 0x1d, 0x0b, 0x4b, + 0x63, 0x9a, 0x03, 0x82, 0xc6, 0x29, 0xe3, 0x3e, 0xd8, 0xad, 0xd2, 0xb0, + 0x69, 0x1a, 0xf8, 0x96, 0xef, 0x79, 0x62, 0x05, 0xe2, 0x9f, 0x3d, 0x5c, + 0x34, 0x59, 0xfd, 0xee, 0x80, 0x10, 0x49, 0x5f, 0xd6, 0xb2, 0xf6, 0xb6, + 0x40, 0x2a, 0xf9, 0x47, 0xe8, 0xbc, 0x92, 0x18, 0xef, 0x29, 0x47, 0x71, + 0x01, 0x87, 0x27, 0xde, 0xcf, 0xc5, 0x00, 0xf4, 0x33, 0x93, 0xb3, 0xcb, + 0x52, 0x12, 0xab, 0x28, 0x23, 0xbe, 0xc1, 0x4e, 0x9b, 0x13, 0x55, 0xbc, + 0x42, 0xfc, 0x86, 0x89, 0x38, 0xfa, 0xfc, 0x78, 0x7d, 0x66, 0x84, 0x6f, + 0x7a, 0xc2, 0xb5, 0x34, 0x15, 0x57, 0x25, 0x96, 0x5b, 0xc8, 0x1f, 0x1b, + 0x88, 0x99, 0x76, 0x66, 0x91, 0xe5, 0x7b, 0x35, 0x1c, 0xd7, 0x48, 0xf6, + 0x10, 0x28, 0x92, 0xa9, 0xdc, 0x23, 0x51, 0x58, 0x8a, 0x22, 0xf4, 0x04, + 0x8c, 0xe8, 0xb5, 0x51, 0x38, 0x94, 0xd6, 0x24, 0x22, 0x34, 0xc9, 0x19, + 0x10, 0x72, 0x5c, 0xbf, 0x6f, 0x43, 0xb8, 0x0e, 0x37, 0x6d, 0xa4, 0x0b, + 0x5a, 0xde, 0xc8, 0x04, 0x5d, 0x4c, 0x45, 0x37, 0x18, 0x4b, 0x98, 0x8e, + 0xa0, 0xc3, 0x43, 0x4c, 0xf0, 0x28, 0x41, 0x7a, 0xb0, 0xae, 0x82, 0xce, + 0x37, 0xb3, 0xaa, 0x08, 0x23, 0x0e, 0xa0, 0x3b, 0x50, 0x11, 0xfd, 0x77, + 0xad, 0x54, 0x89, 0xa1, 0xb1, 0x58, 0x59, 0x36, 0xfe, 0xf3, 0x0f, 0xd7, + 0xf8, 0xf8, 0x21, 0x5c, 0x57, 0xa4, 0x8d, 0xe4, 0xe3, 0xe4, 0xd8, 0x29, + 0x16, 0xe4, 0x02, 0x47, 0x24, 0x61, 0x88, 0x56, 0xa2, 0x21, 0x06, 0x3f, + 0xa2, 0xd2, 0xb9, 0x45, 0x7f, 0xd9, 0xb7, 0x58, 0x6a, 0xf1, 0x0c, 0x6f, + 0x77, 0x08, 0x2b, 0xe0, 0xf7, 0xff, 0xcb, 0x10, 0x33, 0xd7, 0x86, 0xd6, + 0xe0, 0x73, 0xa4, 0x22, 0xf7, 0xd5, 0x58, 0xbc, 0xcc, 0x45, 0x3c, 0x2d, + 0xf7, 0xe7, 0x2f, 0x7a, 0x0a, 0xde, 0x0b, 0x3c, 0xaf, 0x00, 0xe8, 0x04, + 0xa6, 0x23, 0x22, 0x13, 0x67, 0xe5, 0x69, 0x28, 0x9d, 0xfe, 0xd5, 0x98, + 0xf5, 0xa6, 0x8a, 0x33, 0x85, 0xd7, 0x93, 0x36, 0x0a, 0x28, 0x30, 0x3f, + 0xb5, 0x5f, 0x45, 0x13, 0x5c, 0x2d, 0x44, 0xcc, 0x3f, 0x96, 0xae, 0xe2, + 0x1f, 0x9b, 0x70, 0x2d, 0xa9, 0x76, 0x43, 0x56, 0x81, 0xa5, 0x0e, 0xab, + 0xfb, 0xab, 0xb3, 0xa5, 0xa3, 0xa1, 0x6a, 0xdb, 0x02, 0x8d, 0x60, 0x3e, + 0x8f, 0xf2, 0xe5, 0x10, 0x88, 0xfd, 0x1e, 0x16, 0x81, 0x04, 0x89, 0x93, + 0x0b, 0xf6, 0x53, 0xb5, 0x10, 0xbd, 0x4e, 0xf5, 0x53, 0x00, 0xd0, 0xfe, + 0x36, 0x57, 0xc4, 0x93, 0xfd, 0xd5, 0x24, 0x29, 0x34, 0xf7, 0xfc, 0x58, + 0x60, 0x0b, 0x20, 0xbb, 0xcb, 0x46, 0xba, 0x50, 0x16, 0x3c, 0x12, 0x16, + 0x9e, 0x75, 0xbf, 0x59, 0x4c, 0x73, 0x30, 0xb4, 0x6b, 0x8d, 0x77, 0x99, + 0x30, 0x13, 0x84, 0x94, 0xef, 0xd9, 0xe4, 0x7b, 0x35, 0x18, 0x1c, 0x33, + 0x40, 0x60, 0x68, 0xdc, 0xee, 0x27, 0xc1, 0x8b, 0x65, 0x6e, 0x56, 0x48, + 0x94, 0x2b, 0x07, 0x16, 0x29, 0xab, 0x39, 0xf2, 0x0c, 0x52, 0x1b, 0x13, + 0x3b, 0x22, 0x86, 0x8f, 0x85, 0x32, 0x9d, 0x53, 0xb4, 0x41, 0xbc, 0x81, + 0x2b, 0xf8, 0x74, 0x75, 0x4c, 0x2d, 0x0f, 0xb6, 0x5b, 0xcf, 0x37, 0x74, + 0x37, 0x68, 0xfa, 0x23, 0x92, 0xec, 0xe6, 0x2a, 0xb7, 0x82, 0xb5, 0xfa, + 0xff, 0xdb, 0x35, 0x71, 0xba, 0x05, 0x3a, 0x91, 0x52, 0x78, 0xac, 0x82, + 0xe2, 0x07, 0x69, 0xa1, 0x5c, 0x89, 0xdd, 0xae, 0x07, 0x7b, 0x72, 0x10, + 0x33, 0x70, 0xa2, 0x76, 0xa1, 0x15, 0x11, 0xb6, 0x00, 0xb6, 0xa2, 0x9a, + 0xd0, 0x12, 0xe7, 0xfc, 0xe4, 0xb8, 0xce, 0xb1, 0xb1, 0xb2, 0x9c, 0x09, + 0x15, 0x15, 0xeb, 0xeb, 0x78, 0xe2, 0xbd, 0x78, 0xe8, 0x43, 0xc9, 0xe1, + 0xe8, 0x0e, 0x7c, 0xc5, 0x5a, 0x71, 0x48, 0x0e, 0x75, 0x5e, 0x58, 0x24, + 0x41, 0xb1, 0x21, 0xb6, 0xa6, 0x0b, 0x1f, 0xc2, 0xd9, 0xd0, 0xc0, 0x3c, + 0x63, 0x54, 0x05, 0x83, 0x5f, 0xb8, 0x95, 0x13, 0xe0, 0x26, 0xa7, 0xe4, + 0x67, 0x6f, 0x45, 0xbe, 0x99, 0xf5, 0x4c, 0xc4, 0x33, 0x07, 0x26, 0x96, + 0xba, 0x4c, 0x8d, 0xf5, 0x75, 0x4c, 0x77, 0x02, 0x93, 0xa8, 0x38, 0xec, + 0xbc, 0x30, 0x3a, 0x1a, 0xe5, 0xf0, 0x7f, 0x39, 0xde, 0xe4, 0xda, 0x3f, + 0xf9, 0x06, 0x06, 0xe1, 0xd4, 0x8d, 0x58, 0x7f, 0xc5, 0x08, 0xa5, 0x67, + 0x89, 0x7a, 0xf4, 0x19, 0x23, 0x1d, 0x29, 0x92, 0xf9, 0x78, 0x38, 0x39, + 0x16, 0x8b, 0x1d, 0x25, 0x2d, 0x31, 0xb0, 0x8d, 0xaf, 0x93, 0x65, 0x69, + 0x2f, 0x70, 0xaa, 0xd1, 0x51, 0xca, 0xdc, 0xce, 0x36, 0x72, 0xc2, 0x81, + 0x5c, 0xa6, 0xd4, 0x6b, 0x22, 0xcf, 0xc3, 0xc9, 0x55, 0x29, 0xf5, 0x35, + 0x22, 0x4f, 0x23, 0x6d, 0x21, 0x86, 0xdb, 0xbb, 0xd5, 0x1c, 0x33, 0x84, + 0xa0, 0x5f, 0x93, 0xfb, 0xaa, 0x4d, 0x09, 0xee, 0x7b, 0xaa, 0x2c, 0xb3, + 0xfa, 0x3f, 0xbc, 0xcf, 0x74, 0xfe, 0x88, 0x88, 0x5f, 0x04, 0xc8, 0xad, + 0x1f, 0x63, 0xbe, 0xd0, 0x71, 0x41, 0xf2, 0x36, 0xf9, 0xdf, 0x3c, 0x07, + 0xba, 0x03, 0x96, 0xe1, 0x67, 0x8d, 0x43, 0x1d, 0xdc, 0x35, 0xe5, 0x3c, + 0x3a, 0x9c, 0x3f, 0x07, 0x2c, 0x12, 0xcf, 0x0d, 0x6e, 0x50, 0x20, 0xda, + 0x2e, 0x7e, 0xda, 0x67, 0x8e, 0x01, 0x74, 0xb7, 0x76, 0xef, 0xd4, 0xa8, + 0x05, 0x2d, 0x4d, 0xea, 0x4a, 0x93, 0x15, 0xb4, 0x3f, 0x6b, 0x2b, 0xf8, + 0x17, 0x45, 0x4d, 0x1d, 0xf4, 0xc2, 0x95, 0xff, 0x23, 0xe7, 0x46, 0x2c, + 0x5c, 0x71, 0x9a, 0x4e, 0xf8, 0xaa, 0x48, 0x7a, 0x78, 0xe5, 0x43, 0xdf, + 0xba, 0xb9, 0x29, 0x78, 0xd6, 0xca, 0xfb, 0x5c, 0xef, 0xea, 0x8e, 0xbc, + 0xe8, 0x80, 0x8b, 0xad, 0xa1, 0xeb, 0x0a, 0x58, 0x1d, 0xf9, 0x5b, 0x05, + 0x34, 0xb8, 0xbc, 0xd4, 0xeb, 0x55, 0x49, 0xa7, 0xf5, 0xb7, 0xdb, 0x22, + 0x55, 0xe3, 0xc8, 0xfc, 0x81, 0x2a, 0x4b, 0x93, 0x8c, 0x20, 0xf4, 0x7f, + 0x86, 0x8d, 0xf2, 0xe6, 0x72, 0xfc, 0x47, 0x8b, 0x91, 0x8e, 0x6d, 0x64, + 0xa3, 0x20, 0x72, 0x3f, 0xa8, 0x4c, 0xc5, 0x63, 0x59, 0xe8, 0x43, 0x22, + 0x5d, 0xf3, 0x80, 0x60, 0x1d, 0xb3, 0x9b, 0x6c, 0x8c, 0xb3, 0xe5, 0x88, + 0xfd, 0xf9, 0xc3, 0xee, 0x86, 0xa9, 0x8c, 0x03, 0x2d, 0xfc, 0x35, 0x9a, + 0x6a, 0xe6, 0xdd, 0x05, 0x94, 0x04, 0x9f, 0x42, 0x70, 0x7b, 0x27, 0xd5, + 0xa1, 0xa8, 0x22, 0x87, 0x1d, 0x2d, 0x12, 0xe9, 0xa0, 0xf0, 0xb8, 0xcc, + 0x01, 0xa2, 0x86, 0xda, 0x9f, 0xbe, 0xe6, 0x63, 0x89, 0x8b, 0xb9, 0x40, + 0xc7, 0x77, 0x49, 0x5e, 0xd7, 0xf7, 0xb5, 0xc6, 0xdb, 0xab, 0x37, 0x73, + 0x3b, 0x61, 0x97, 0xe5, 0x07, 0x26, 0xd6, 0xa3, 0x05, 0x4e, 0xe9, 0x39, + 0x68, 0x84, 0x6a, 0x2c, 0x0c, 0x0c, 0x0a, 0xc8, 0xda, 0x09, 0x51, 0x45, + 0xfa, 0x79, 0xd0, 0x3c, 0x36, 0xd3, 0x3e, 0xe0, 0x4f, 0xee, 0x0d, 0xde, + 0xa6, 0x24, 0x37, 0x1b, 0xd8, 0x93, 0x29, 0x7d, 0x6c, 0x2a, 0xec, 0xbc, + 0x06, 0xfd, 0x54, 0x1f, 0x5d, 0x24, 0x06, 0x8a, 0xcf, 0xbe, 0x06, 0xd3, + 0x1d, 0x8b, 0x9f, 0x17, 0xfb, 0x9a, 0x9b, 0xdf, 0x74, 0x74, 0x5d, 0x3e, + 0x65, 0x7d, 0x24, 0x01, 0xd7, 0x05, 0x97, 0xae, 0x7e, 0xc0, 0xd3, 0x46, + 0x20, 0xdc, 0x4f, 0x9e, 0x5c, 0xdd, 0x0b, 0xf2, 0x57, 0xed, 0xeb, 0xd1, + 0xc9, 0x50, 0xb3, 0xf7, 0x2a, 0x2b, 0x7b, 0x3d, 0xd5, 0x5b, 0xb9, 0x15, + 0xaa, 0xb8, 0xa7, 0x37, 0x23, 0xb1, 0x40, 0xe1, 0x4f, 0x2f, 0xd8, 0x97, + 0xa5, 0x25, 0x11, 0x82, 0xf6, 0xec, 0xc9, 0x94, 0x53, 0x1c, 0xb8, 0x0b, + 0xf7, 0xfb, 0xc1, 0xd2, 0x75, 0x07, 0x42, 0xee, 0xb1, 0xbc, 0x3d, 0x1d, + 0x96, 0x0b, 0x87, 0xd8, 0xab, 0x19, 0x46, 0x76, 0xcb, 0x4e, 0x70, 0xe1, + 0x75, 0x36, 0x9c, 0x30, 0x41, 0xd0, 0x11, 0xb8, 0x74, 0xd6, 0x8c, 0x03, + 0x1e, 0x66, 0xf5, 0xef, 0xa6, 0x3c, 0x3d, 0x6c, 0xb2, 0x87, 0x39, 0xea, + 0x64, 0x51, 0x74, 0x7e, 0x95, 0xe1, 0xfd, 0xd6, 0x0e, 0x17, 0x5c, 0x71, + 0xef, 0x2c, 0x8b, 0x4f, 0x98, 0xea, 0x15, 0x4d, 0x08, 0x1a, 0x80, 0x6b, + 0x86, 0x55, 0xc6, 0x0d, 0x3b, 0x04, 0xf2, 0xcf, 0xe1, 0xce, 0x93, 0xe7, + 0x14, 0x91, 0x79, 0xc8, 0x3c, 0x67, 0xe4, 0xb4, 0xb2, 0x99, 0x89, 0x7c, + 0x84, 0xd9, 0xe4, 0x5c, 0xe9, 0x13, 0x2d, 0x28, 0x28, 0xd6, 0x82, 0x87, + 0x4b, 0xe3, 0x02, 0xc7, 0x56, 0xeb, 0x04, 0x05, 0xc2, 0xbf, 0xb2, 0xf9, + 0x56, 0xb1, 0x9e, 0x61, 0xf7, 0xe8, 0x2a, 0x20, 0x01, 0x5a, 0xe0, 0x66, + 0x94, 0xfb, 0xd1, 0xb5, 0x56, 0xb3, 0xc7, 0x0b, 0x97, 0xa0, 0xc7, 0x8c, + 0xe8, 0xcf, 0x0c, 0xc1, 0xb5, 0x06, 0x5f, 0x03, 0x10, 0x36, 0xd7, 0x9f, + 0x87, 0xc3, 0x7d, 0x2d, 0xd4, 0xd0, 0x15, 0x08, 0x71, 0x2f, 0x0d, 0x15, + 0x82, 0x97, 0xfb, 0x55, 0xff, 0x84, 0xea, 0x52, 0x44, 0x82, 0xc3, 0x0b, + 0x22, 0x5a, 0xb9, 0x98, 0xe7, 0x18, 0x7c, 0x27, 0x16, 0xb3, 0x04, 0xd4, + 0xba, 0x84, 0x4a, 0xdc, 0x42, 0x52, 0xe5, 0x86, 0xed, 0x4a, 0x9e, 0x78, + 0x9a, 0x3c, 0x16, 0xc9, 0x93, 0x24, 0xf4, 0x4e, 0x98, 0x5e, 0xc0, 0x05, + 0x90, 0xd0, 0x87, 0x4c, 0x51, 0xdc, 0xfc, 0x19, 0x54, 0xb8, 0x21, 0x9b, + 0xf1, 0x2a, 0x42, 0x12, 0xb2, 0x57, 0x28, 0x71, 0x1e, 0x7b, 0x4d, 0x7f, + 0x94, 0xa2, 0x46, 0x2b, 0x65, 0x59, 0x15, 0xc4, 0xcb, 0x40, 0x0c, 0xa9, + 0xb1, 0x6d, 0x62, 0x2b, 0x60, 0x24, 0x2d, 0x66, 0x73, 0xaa, 0xc8, 0x90, + 0x15, 0xa8, 0x50, 0x1d, 0xa4, 0x95, 0x7a, 0x3c, 0x2c, 0xaf, 0xad, 0x99, + 0x9d, 0xf7, 0xee, 0xe8, 0x9c, 0x19, 0xc1, 0x6e, 0x0c, 0xad, 0x36, 0xa9, + 0x84, 0x8b, 0x0e, 0x7c, 0xc7, 0x4e, 0xd2, 0x2b, 0x00, 0x56, 0xec, 0x29, + 0xc5, 0x0d, 0x95, 0x20, 0x0d, 0x23, 0x11, 0xac, 0x8d, 0xb2, 0xb0, 0x6e, + 0xe0, 0xaf, 0x75, 0xcb, 0xfc, 0xfb, 0x6e, 0xd2, 0x24, 0x41, 0xf2, 0x96, + 0x7b, 0x07, 0x2a, 0xdd, 0xd2, 0xe1, 0xbf, 0xad, 0x6b, 0x37, 0x9e, 0xe0, + 0x1c, 0x29, 0x30, 0xdc, 0x75, 0xc8, 0xc4, 0xa5, 0x0d, 0xb1, 0x13, 0xea, + 0xa2, 0x32, 0x54, 0x03, 0xf3, 0x79, 0x48, 0x94, 0x02, 0x06, 0x0e, 0x0d, + 0x15, 0x22, 0xa2, 0x0f, 0x73, 0x7d, 0xe4, 0x6c, 0x81, 0x5d, 0xae, 0xc0, + 0xf1, 0x31, 0x4a, 0x2d, 0xe6, 0x44, 0x73, 0x89, 0x5a, 0xb8, 0x94, 0x19, + 0xd9, 0x2e, 0xc3, 0xcb, 0x28, 0x5b, 0x71, 0x2b, 0xf5, 0xf0, 0xe6, 0x9d, + 0xb2, 0xa9, 0x62, 0xec, 0x37, 0xf1, 0xd0, 0x10, 0x22, 0xfc, 0x33, 0xcc, + 0x4a, 0xf2, 0x07, 0x8a, 0x3e, 0x4d, 0xb6, 0x1a, 0xd5, 0x24, 0x69, 0xf3, + 0x7c, 0x82, 0x30, 0xce, 0x64, 0xb3, 0x4f, 0x46, 0x74, 0x5a, 0xf6, 0x51, + 0xe6, 0x07, 0xdb, 0x5d, 0x00, 0xf0, 0xd9, 0x97, 0x85, 0x1d, 0x0c, 0xd5, + 0x9f, 0x79, 0x7d, 0x27, 0x06, 0xfa, 0x70, 0x2b, 0x27, 0xd9, 0xce, 0x93, + 0x93, 0xdd, 0xb4, 0xa9, 0xf2, 0xb8, 0x83, 0xa0, 0xe5, 0x96, 0x02, 0x7b, + 0xc3, 0xdf, 0x22, 0x71, 0xf3, 0xb2, 0x92, 0xb6, 0xd8, 0x2f, 0x90, 0x3f, + 0xdb, 0x9a, 0xc1, 0x5f, 0x13, 0x45, 0x34, 0x93, 0x34, 0x7f, 0xa3, 0xeb, + 0x19, 0xe7, 0xfa, 0xeb, 0x25, 0x12, 0x57, 0x9a, 0x08, 0x2d, 0xa9, 0x1b, + 0xe9, 0x18, 0x9e, 0x49, 0xab, 0x46, 0xa7, 0x70, 0xa4, 0x9d, 0xd3, 0xcd, + 0x66, 0x21, 0xf1, 0x2a, 0xbd, 0x07, 0xf3, 0xa9, 0x11, 0x70, 0xed, 0x32, + 0xc2, 0xa0, 0xea, 0x8c, 0x2b, 0xb8, 0xfa, 0xcb, 0xeb, 0x16, 0xf9, 0x1e, + 0xe6, 0x45, 0x06, 0x6a, 0x54, 0xa5, 0x34, 0xef, 0x7c, 0x8f, 0xdd, 0x9a, + 0xf8, 0xc6, 0x2e, 0x16, 0xec, 0xac, 0x92, 0xef, 0x40, 0xf5, 0x46, 0xc1, + 0xdf, 0x6a, 0xd0, 0x0a, 0x66, 0xeb, 0x62, 0x81, 0xfc, 0x16, 0x65, 0x52, + 0x8e, 0xb7, 0xbe, 0xfa, 0xc5, 0xf2, 0xc4, 0x17, 0x75, 0x13, 0x15, 0x4b, + 0x76, 0x05, 0xe0, 0xcd, 0x93, 0x9a, 0xb1, 0xed, 0x43, 0x7d, 0x9e, 0x18, + 0x90, 0xcd, 0x90, 0x49, 0x3c, 0xc3, 0x91, 0x4e, 0xd7, 0xbc, 0x1c, 0x8c, + 0x24, 0x46, 0x2c, 0xb0, 0x26, 0x64, 0x42, 0xa4, 0xbf, 0xa9, 0xec, 0xd9, + 0x9a, 0x00, 0xb4, 0x9d, 0xb3, 0x80, 0x2b, 0x59, 0xf4, 0x6e, 0x5a, 0x1e, + 0x48, 0x4b, 0x04, 0xc6, 0xb8, 0x32, 0xfa, 0x77, 0x65, 0xfd, 0x2c, 0x95, + 0x69, 0xad, 0x89, 0x69, 0x60, 0x2c, 0x54, 0x90, 0x53, 0x49, 0x95, 0x08, + 0xaf, 0xea, 0x07, 0x75, 0xe3, 0x92, 0x44, 0x67, 0x97, 0x07, 0xc7, 0xc7, + 0xd1, 0xef, 0xf8, 0xfc, 0x2b, 0xda, 0x03, 0x92, 0x8e, 0x6a, 0x68, 0x86, + 0x11, 0x6b, 0xd0, 0x05, 0xce, 0xf9, 0x69, 0x99, 0x08, 0xd4, 0x34, 0x36, + 0x3a, 0xb4, 0x2a, 0xbd, 0x35, 0x3c, 0x23, 0x26, 0x1e, 0x71, 0x10, 0xb9, + 0x86, 0xe6, 0x15, 0x0c, 0xe4, 0x10, 0xc7, 0x84, 0x4c, 0x23, 0x98, 0xd4, + 0x23, 0x33, 0x72, 0x02, 0xee, 0x20, 0xfd, 0xe4, 0x3e, 0xdc, 0x4f, 0x88, + 0xef, 0xdf, 0xa0, 0xb3, 0x66, 0x89, 0x2c, 0xce, 0x89, 0x37, 0x84, 0x73, + 0x12, 0x20, 0xd3, 0xe2, 0xbf, 0x8e, 0xac, 0x92, 0xf1, 0xef, 0x1d, 0x03, + 0xdd, 0x92, 0x9b, 0xb0, 0xb2, 0x66, 0x13, 0x4b, 0xbf, 0x34, 0x7b, 0x75, + 0x5b, 0xb6, 0x48, 0x09, 0x73, 0xfb, 0x87, 0x72, 0x55, 0xdd, 0xa8, 0x59, + 0x77, 0x1d, 0x4f, 0xae, 0xf3, 0xe7, 0xce, 0x22, 0x6c, 0x45, 0xb0, 0x21, + 0x41, 0xc0, 0xcf, 0xcd, 0x31, 0x6b, 0x72, 0x08, 0xfb, 0x8d, 0x92, 0x87, + 0xe8, 0x17, 0xe7, 0x1b, 0x78, 0x19, 0xbf, 0x65, 0x03, 0x24, 0xbe, 0x57, + 0xe7, 0xa4, 0x76, 0x04, 0x6a, 0x6e, 0x62, 0x67, 0x65, 0x35, 0x53, 0x3d, + 0x80, 0x74, 0xe2, 0xe8, 0xe6, 0x1c, 0x82, 0x31, 0xfd, 0x1b, 0x3f, 0x89, + 0x48, 0xac, 0x20, 0xf1, 0x61, 0xfa, 0x06, 0xb2, 0xc8, 0x5f, 0xda, 0x70, + 0xd2, 0x67, 0x21, 0x29, 0xac, 0xf1, 0x8c, 0x85, 0x2d, 0xc3, 0x4c, 0x09, + 0x79, 0xcb, 0xf7, 0x47, 0x3c, 0xa5, 0xd8, 0x36, 0xaa, 0x57, 0xff, 0x71, + 0xeb, 0x4b, 0xdb, 0xf8, 0x62, 0xaf, 0x74, 0x86, 0x62, 0x1d, 0x61, 0x18, + 0xfa, 0x15, 0x0b, 0xdd, 0x24, 0x22, 0xc8, 0x71, 0x62, 0x6b, 0x9c, 0xa4, + 0x26, 0xa3, 0x38, 0xa7, 0xe3, 0xd8, 0x19, 0xc1, 0x95, 0x1b, 0x5b, 0x6c, + 0xbc, 0xcd, 0x7b, 0xa7, 0xb7, 0xfa, 0xfb, 0xca, 0x30, 0x80, 0xfc, 0xfc, + 0x72, 0x17, 0x7d, 0xa6, 0x2b, 0x01, 0x1a, 0x40, 0xfb, 0x8d, 0x4b, 0x58, + 0x4d, 0x99, 0xdc, 0x76, 0xc3, 0x95, 0x21, 0xd1, 0x46, 0x7b, 0xf8, 0xad, + 0xec, 0x08, 0xb5, 0x3c, 0x89, 0xc7, 0x54, 0x02, 0xbc, 0x0f, 0xe9, 0x96, + 0x27, 0x19, 0xcb, 0x12, 0x9c, 0x6a, 0x1e, 0x0c, 0xdb, 0x93, 0x9f, 0xce, + 0xfa, 0x31, 0x03, 0xa5, 0x19, 0xa9, 0xbe, 0x73, 0x41, 0x2a, 0x45, 0xf0, + 0xdf, 0x37, 0x73, 0x3c, 0x83, 0x91, 0x0f, 0x19, 0x26, 0xd7, 0x5f, 0xbf, + 0x98, 0xda, 0x3b, 0x51, 0xef, 0x05, 0xd5, 0x17, 0x1a, 0xd5, 0x07, 0x20, + 0x7d, 0x60, 0xe3, 0x13, 0x14, 0xbe, 0xdb, 0x17, 0x2c, 0x8d, 0x34, 0xab, + 0x90, 0x62, 0x86, 0x1e, 0x42, 0xee, 0x9f, 0xf2, 0x4e, 0x4e, 0x5b, 0xe9, + 0x3d, 0x92, 0xcc, 0xce, 0x12, 0x86, 0x9d, 0x03, 0x1e, 0xb7, 0xf9, 0x9c, + 0x35, 0xb6, 0x13, 0xeb, 0x57, 0x7d, 0xc4, 0x8c, 0xd9, 0x4d, 0x6c, 0x34, + 0xed, 0x27, 0xae, 0x28, 0xb5, 0xd7, 0x23, 0xef, 0xa3, 0x4a, 0x3d, 0xae, + 0x46, 0xe6, 0xfa, 0x85, 0xe1, 0x92, 0x89, 0x28, 0x42, 0x34, 0x93, 0x3e, + 0x34, 0xb2, 0x83, 0x1c, 0xb9, 0xff, 0x91, 0x6b, 0x4a, 0xd8, 0xc8, 0xad, + 0x75, 0xe8, 0xba, 0xdd, 0xea, 0xc0, 0x86, 0xde, 0x35, 0xbd, 0xbe, 0x20, + 0x3e, 0x1f, 0x65, 0xc1, 0x0a, 0x27, 0x0b, 0x0c, 0x16, 0xb0, 0x63, 0x82, + 0xbe, 0x79, 0xc4, 0x02, 0x31, 0x69, 0x8c, 0x3b, 0x7d, 0x70, 0x8a, 0xde, + 0xe1, 0xc0, 0x9d, 0xda, 0x50, 0x7f, 0xa7, 0xcc, 0xda, 0xf6, 0xc7, 0x10, + 0xcf, 0x2e, 0x89, 0x35, 0x13, 0xe6, 0x50, 0x07, 0x93, 0x68, 0x95, 0x28, + 0x94, 0x03, 0x2c, 0x02, 0x94, 0x00, 0x0e, 0xe7, 0x0f, 0x20, 0x27, 0xa6, + 0xfe, 0x0a, 0x6b, 0xf2, 0x57, 0x76, 0x09, 0x7c, 0x89, 0x06, 0x5e, 0x57, + 0x21, 0x13, 0xcb, 0x2c, 0xe1, 0xbc, 0x44, 0x02, 0xc2, 0x76, 0xaf, 0x35, + 0xa5, 0x55, 0x3d, 0x8d, 0x2e, 0x49, 0x04, 0x61, 0x8b, 0x7a, 0x20, 0xbb, + 0xab, 0xaf, 0x77, 0x64, 0xfa, 0xa0, 0x2a, 0x18, 0x7a, 0xba, 0xa2, 0x6d, + 0xdb, 0x69, 0xab, 0x0c, 0x13, 0x85, 0x7e, 0x42, 0xc5, 0x61, 0x29, 0xcc, + 0xc7, 0x68, 0xa6, 0x5f, 0xe1, 0x9b, 0xc8, 0x63, 0xc0, 0x65, 0x35, 0x42, + 0x4d, 0xda, 0x1c, 0x16, 0x99, 0x31, 0x3d, 0x3c, 0xbe, 0x62, 0x8d, 0xe3, + 0x53, 0xcb, 0x0d, 0x94, 0x0f, 0x04, 0xef, 0x38, 0x15, 0x0d, 0x82, 0x20, + 0xa7, 0x2a, 0x19, 0x4a, 0x25, 0xa6, 0xb5, 0xa8, 0x2e, 0x24, 0x25, 0x5a, + 0x40, 0x76, 0x6b, 0x67, 0x62, 0xe5, 0x48, 0xe7, 0xa2, 0xa2, 0xc9, 0x10, + 0x2e, 0x8d, 0xdd, 0x26, 0x81, 0x93, 0x7d, 0x26, 0x6b, 0xcf, 0xb8, 0x70, + 0xab, 0xf8, 0xcf, 0xbb, 0x1a, 0x18, 0xa4, 0x0b, 0xa3, 0x5e, 0xe1, 0xa1, + 0xd6, 0x8a, 0xb8, 0x02, 0x05, 0x4d, 0x87, 0x92, 0x77, 0xff, 0xa2, 0x9e, + 0x0a, 0x50, 0x7c, 0xe9, 0x79, 0x2c, 0xb0, 0xaa, 0x93, 0x9b, 0x00, 0x82, + 0x62, 0x61, 0x0b, 0x12, 0x7c, 0xc8, 0x1d, 0xba, 0xe2, 0x40, 0x1a, 0x21, + 0x31, 0xc0, 0xf9, 0x4f, 0x38, 0xc5, 0x2e, 0x96, 0x5c, 0x05, 0x8d, 0x94, + 0x42, 0x2f, 0xf0, 0xfa, 0xcd, 0x50, 0x3d, 0xb1, 0x44, 0x18, 0x93, 0xc9, + 0xa4, 0x41, 0x89, 0x98, 0x00, 0xed, 0xc0, 0x43, 0x12, 0xf2, 0xba, 0xaf, + 0x9f, 0xcc, 0x75, 0x26, 0xcb, 0xba, 0xfc, 0x87, 0x2a, 0x00, 0x9a, 0x0b, + 0xce, 0xd0, 0xdc, 0xe1, 0x49, 0x02, 0x8d, 0xf4, 0x25, 0x2b, 0xed, 0x5c, + 0x3d, 0xbf, 0xe8, 0x05, 0xa7, 0xfa, 0x36, 0x08, 0x71, 0xec, 0x6d, 0x4f, + 0x1d, 0x4f, 0x71, 0x14, 0x0c, 0x0e, 0x92, 0xe5, 0xfb, 0x4e, 0x1e, 0xca, + 0x43, 0xcd, 0xf6, 0x08, 0x29, 0x8b, 0xff, 0xc6, 0xe3, 0x31, 0x1e, 0xed, + 0x7d, 0x14, 0xcb, 0xc1, 0xa0, 0x1e, 0xaf, 0x9c, 0x70, 0xf2, 0x10, 0x42, + 0xd5, 0xb8, 0x94, 0x07, 0xd7, 0xef, 0x87, 0x8a, 0x63, 0x82, 0x20, 0x59, + 0xbf, 0x8a, 0x99, 0x07, 0x1a, 0x15, 0x77, 0x87, 0x5f, 0xab, 0xd1, 0x1f, + 0xec, 0xc8, 0x37, 0xa8, 0x93, 0xb8, 0x7c, 0xba, 0x80, 0xf2, 0x70, 0x67, + 0xec, 0x98, 0x1f, 0x06, 0x68, 0xcd, 0xf8, 0x7d, 0xaf, 0xd3, 0x3a, 0x90, + 0x38, 0x6a, 0x85, 0xfe, 0x00, 0xa6, 0x90, 0x1b, 0x91, 0xc6, 0x9a, 0x47, + 0xa3, 0x75, 0x9e, 0x19, 0xb2, 0x10, 0x6a, 0x0c, 0x78, 0x1a, 0xb3, 0x95, + 0xe7, 0xc3, 0x3d, 0xd0, 0x42, 0x59, 0x94, 0x96, 0x32, 0x6a, 0x4d, 0xdc, + 0xd2, 0x5d, 0xf4, 0xe7, 0xfb, 0x0e, 0xde, 0xa1, 0x8b, 0x7b, 0xf8, 0x6d, + 0x66, 0x66, 0xbe, 0x4b, 0xbe, 0x91, 0xf4, 0x32, 0x39, 0xb2, 0x55, 0xa0, + 0xbb, 0x80, 0xfc, 0x0d, 0xca, 0x3a, 0x3d, 0x95, 0xbb, 0xcc, 0xab, 0xf5, + 0x43, 0x73, 0x6a, 0x52, 0xa9, 0xd2, 0xbc, 0x9c, 0xf9, 0x69, 0x3b, 0x65, + 0x01, 0x7d, 0xc3, 0x73, 0xdf, 0xe1, 0x49, 0x40, 0x33, 0xe6, 0xbc, 0xf0, + 0xff, 0x57, 0x23, 0x9d, 0x4c, 0x19, 0x2d, 0x7d, 0x3a, 0x79, 0x21, 0xf0, + 0x18, 0xf2, 0x45, 0x34, 0x40, 0x54, 0x7f, 0x67, 0x02, 0x1c, 0xd0, 0x76, + 0x65, 0x0c, 0xcf, 0xe1, 0xb2, 0x26, 0xc1, 0x68, 0xcb, 0x06, 0x8d, 0xf5, + 0xb0, 0x80, 0x3d, 0x57, 0xfb, 0xf6, 0x9f, 0x41, 0x6f, 0x0c, 0xbb, 0x6f, + 0x7d, 0x18, 0x93, 0x80, 0xe5, 0xd9, 0x8e, 0xbd, 0xa3, 0x0d, 0x63, 0x1f, + 0x7b, 0x61, 0x64, 0x28, 0xc4, 0xdd, 0x3a, 0x88, 0xf3, 0x26, 0xd7, 0xcd, + 0x31, 0x9b, 0x93, 0x06, 0x03, 0x98, 0x87, 0x86, 0xc1, 0x42, 0x17, 0xd5, + 0x7b, 0xf2, 0x62, 0x3e, 0xf9, 0xa9, 0x7c, 0xb5, 0x5f, 0x67, 0x82, 0x7c, + 0x0b, 0x8d, 0x21, 0xd6, 0x24, 0xa3, 0x06, 0xc5, 0xa7, 0x80, 0xb6, 0x58, + 0x7f, 0xb5, 0x72, 0xbd, 0x1a, 0xba, 0x4a, 0x53, 0x9a, 0x85, 0xa0, 0xea, + 0x91, 0xbb, 0x62, 0x12, 0x9c, 0x20, 0xc7, 0xa5, 0x84, 0xcd, 0x2a, 0x57, + 0xe8, 0xed, 0xd5, 0xcf, 0x2f, 0x7c, 0x2a, 0x1c, 0xc1, 0xd9, 0xc8, 0x74, + 0x06, 0x97, 0x76, 0xee, 0x6d, 0x6f, 0x6d, 0x81, 0x8d, 0xe9, 0xed, 0x7f, + 0x83, 0xc9, 0xc6, 0x3b, 0xec, 0xea, 0x89, 0x36, 0x4e, 0xa7, 0xa3, 0x36, + 0xaf, 0xe0, 0x30, 0x27, 0xbb, 0x9c, 0x28, 0x3f, 0x89, 0x21, 0x37, 0x1a, + 0x23, 0xee, 0x3d, 0x8e, 0xe5, 0xad, 0x2b, 0x58, 0x17, 0x82, 0x7f, 0x24, + 0xf6, 0x78, 0x04, 0x00, 0x7e, 0x96, 0x01, 0x38, 0x52, 0x4f, 0xd3, 0x6f, + 0xc1, 0x22, 0x83, 0x87, 0xf6, 0x42, 0x6b, 0xd7, 0xc9, 0x6b, 0xda, 0x76, + 0x20, 0x0a, 0x79, 0xdf, 0x00, 0x83, 0x5b, 0x53, 0x70, 0x09, 0x03, 0x5a, + 0x96, 0xd8, 0x46, 0xfe, 0xed, 0x68, 0x3b, 0x42, 0xce, 0x2d, 0x0a, 0x53, + 0x7a, 0xe7, 0x70, 0xac, 0x19, 0x12, 0xfc, 0x3c, 0x72, 0x28, 0xb8, 0x72, + 0x7b, 0x60, 0x93, 0xbb, 0xde, 0xc7, 0x39, 0xed, 0x77, 0xab, 0xef, 0x50, + 0x9d, 0x0f, 0x64, 0x18, 0xd0, 0x7d, 0x6e, 0xd1, 0x15, 0x3a, 0x8c, 0xe7, + 0x74, 0x16, 0xf7, 0x7f, 0xd1, 0x02, 0x74, 0x03, 0x21, 0x26, 0xb3, 0xab, + 0x86, 0xd4, 0x0a, 0x41, 0xdc, 0x56, 0x8c, 0xff, 0xa5, 0x9e, 0xb2, 0xaa, + 0x30, 0xb0, 0x36, 0x2b, 0xf2, 0x95, 0x32, 0x4c, 0x9d, 0x4f, 0x51, 0x54, + 0xef, 0xa4, 0x02, 0x96, 0x4e, 0x40, 0x58, 0xd8, 0x07, 0x75, 0x0f, 0x50, + 0x1b, 0x1a, 0x04, 0xa4, 0xa7, 0xba, 0x65, 0xc9, 0x86, 0x75, 0x53, 0xe8, + 0xdc, 0x74, 0xbf, 0x79, 0x23, 0xc0, 0x47, 0x6a, 0x62, 0xc2, 0xee, 0x68, + 0xf5, 0xd1, 0x71, 0x07, 0x00, 0xf2, 0xb9, 0x1e, 0x37, 0x9c, 0x13, 0x93, + 0xf3, 0x84, 0xc2, 0x94, 0x4d, 0x99, 0xab, 0xb4, 0x10, 0x73, 0x31, 0x1a, + 0xb1, 0x80, 0xd5, 0xb8, 0xca, 0xa3, 0xe4, 0x13, 0x22, 0x20, 0xfd, 0x00, + 0xff, 0x75, 0xb2, 0xe5, 0xc8, 0xdf, 0x12, 0x5a, 0x9c, 0xa3, 0xf6, 0xbd, + 0xd9, 0xb5, 0x3b, 0x32, 0x05, 0x62, 0x64, 0x65, 0x3d, 0x78, 0xb7, 0xe3, + 0xe4, 0x4e, 0x0d, 0xe1, 0xc3, 0x5b, 0x90, 0x26, 0x57, 0xf6, 0x40, 0xff, + 0x8e, 0xb9, 0x6c, 0x00, 0x3f, 0xb1, 0x8f, 0x83, 0xf5, 0x22, 0x2a, 0x3a, + 0x4e, 0x3b, 0x22, 0x6f, 0x0c, 0x8b, 0xa5, 0xe0, 0x34, 0xc6, 0xda, 0x7d, + 0xb0, 0xd6, 0xb5, 0x95, 0x5c, 0x47, 0x55, 0xa3, 0xa0, 0x8f, 0x86, 0x7d, + 0x6f, 0x4e, 0xfa, 0x56, 0x3e, 0x00, 0x6c, 0x8a, 0x04, 0xaa, 0xaa, 0x6f, + 0x37, 0x3b, 0xea, 0x28, 0x39, 0x8c, 0x92, 0x25, 0x0c, 0xfe, 0xd5, 0x9d, + 0x2f, 0xdd, 0x61, 0x6f, 0x67, 0xe8, 0x27, 0x3c, 0x09, 0x6f, 0x3a, 0x14, + 0x97, 0xa9, 0xfe, 0x12, 0x36, 0x43, 0xb5, 0x1e, 0x79, 0x38, 0x91, 0xca, + 0x09, 0xe7, 0xac, 0xb7, 0xb3, 0x90, 0x40, 0x97, 0xf2, 0x70, 0x6e, 0x8d, + 0xbc, 0xcf, 0x9b, 0x8b, 0xac, 0x00, 0xa7, 0x41, 0xc7, 0xb7, 0xfd, 0x1f, + 0x2d, 0x35, 0x1c, 0x9c, 0x21, 0x94, 0xa5, 0xd1, 0xae, 0xee, 0x06, 0x62, + 0x70, 0xf9, 0xcb, 0x5c, 0x0a, 0xbb, 0xc9, 0x23, 0x7e, 0x9d, 0x4d, 0xb1, + 0xa0, 0x3d, 0xa2, 0x7a, 0x2c, 0xd9, 0x23, 0x36, 0x5d, 0x22, 0xdd, 0x74, + 0x55, 0x11, 0x43, 0x45, 0xd4, 0xdb, 0x21, 0x73, 0xe8, 0x29, 0x68, 0x16, + 0x3d, 0x21, 0xc6, 0x66, 0x07, 0x10, 0x95, 0x31, 0x0f, 0x32, 0x7c, 0xcc, + 0x92, 0x8a, 0x5b, 0x97, 0x89, 0xcd, 0xe5, 0x6c, 0x49, 0xc4, 0xa5, 0xc0, + 0x5e, 0xd9, 0x1e, 0xc9, 0xae, 0x2a, 0x41, 0xae, 0x65, 0xc4, 0x10, 0xdd, + 0x78, 0x6d, 0xb4, 0xd1, 0xb1, 0x0b, 0x11, 0x91, 0x1e, 0xe5, 0x1d, 0xdd, + 0x8e, 0xe0, 0x1c, 0x5e, 0x0c, 0xac, 0xf2, 0x28, 0x01, 0xa7, 0xda, 0xb1, + 0xfc, 0x39, 0xdc, 0xce, 0x40, 0x16, 0x68, 0x8e, 0x7d, 0x67, 0xee, 0xce, + 0x5b, 0xbf, 0x88, 0xc2, 0xef, 0xa4, 0x1e, 0xba, 0xf3, 0xc7, 0xda, 0x35, + 0xd4, 0x6b, 0x75, 0x8d, 0xce, 0x24, 0x2c, 0x19, 0xaf, 0x56, 0x11, 0x5b, + 0x8b, 0xbe, 0x27, 0xd1, 0x2b, 0xa6, 0x7e, 0xc6, 0x7c, 0xda, 0x8c, 0x33, + 0xf7, 0x9b, 0x71, 0x98, 0x0a, 0x22, 0x3f, 0x45, 0x7f, 0x34, 0xae, 0x04, + 0x88, 0x45, 0x37, 0x11, 0x31, 0x56, 0x1d, 0x4f, 0x57, 0x2c, 0xd2, 0xbf, + 0x72, 0x16, 0x38, 0x28, 0x49, 0xbd, 0xa4, 0x9f, 0x64, 0x65, 0x3a, 0x52, + 0xa6, 0x4c, 0xd6, 0x0c, 0xa9, 0xc8, 0xb4, 0x39, 0x7c, 0x43, 0x9b, 0xaf, + 0x0a, 0xb9, 0x06, 0xc3, 0xc8, 0x6c, 0x4b, 0x4b, 0xae, 0xed, 0xf4, 0x53, + 0xa3, 0x0e, 0xb0, 0xc7, 0xe3, 0xaa, 0xac, 0x25, 0xf7, 0xd2, 0xf9, 0xee, + 0xd9, 0x15, 0x13, 0x21, 0x25, 0xd1, 0x33, 0x20, 0x11, 0xc8, 0xe3, 0x19, + 0x56, 0x94, 0x01, 0x59, 0x3d, 0x38, 0x48, 0x02, 0x64, 0x64, 0xa7, 0xb5, + 0x73, 0x6e, 0xac, 0xc7, 0x17, 0xe0, 0xe0, 0x17, 0xb4, 0xe3, 0x8b, 0xfd, + 0x20, 0x05, 0xca, 0x3e, 0x2f, 0x83, 0x49, 0xf2, 0xbf, 0xd3, 0x3c, 0x40, + 0x42, 0xdd, 0x1d, 0xab, 0x40, 0xc6, 0x79, 0x81, 0xb9, 0x26, 0x88, 0x22, + 0xa7, 0x36, 0xdb, 0xe5, 0xf2, 0xe6, 0x22, 0xad, 0xb6, 0x6f, 0x04, 0x62, + 0x33, 0xfa, 0x09, 0xc7, 0x12, 0x2b, 0x99, 0xba, 0xbc, 0xef, 0xd8, 0xbd, + 0xf8, 0x22, 0x90, 0x1b, 0xe3, 0x78, 0xb0, 0x8f, 0x8a, 0x9a, 0x20, 0xaf, + 0xf3, 0xd2, 0x23, 0xc2, 0xd1, 0x37, 0x01, 0x67, 0xf8, 0x36, 0x0e, 0xd6, + 0x5d, 0x4d, 0x9b, 0x50, 0xe7, 0x02, 0x80, 0x1b, 0x04, 0x70, 0x21, 0xc4, + 0x7b, 0x38, 0x30, 0x06, 0x92, 0x75, 0x94, 0x4f, 0x32, 0x06, 0x01, 0xba, + 0xb1, 0xc3, 0x3b, 0xc1, 0x9e, 0xa1, 0x34, 0x84, 0x54, 0x11, 0x9f, 0x59, + 0xa9, 0x01, 0x78, 0xbd, 0x1b, 0x2f, 0xf4, 0xba, 0xeb, 0x93, 0xf2, 0x11, + 0x36, 0xae, 0xca, 0xa2, 0x83, 0xac, 0x09, 0x0e, 0xd5, 0xc1, 0xb6, 0x63, + 0x6c, 0xec, 0x82, 0x53, 0x79, 0xb6, 0x38, 0x11, 0x73, 0xb8, 0x69, 0x15, + 0x0f, 0x37, 0xed, 0x06, 0x2c, 0x03, 0xcb, 0x36, 0x21, 0x0e, 0xf8, 0xfc, + 0x73, 0xf2, 0xaf, 0x49, 0x20, 0x6b, 0x9f, 0xc7, 0x97, 0x4f, 0xc8, 0x31, + 0x45, 0x92, 0xfc, 0xc7, 0x28, 0x37, 0x54, 0x18, 0xee, 0xc8, 0xca, 0x7e, + 0xda, 0x4c, 0xc5, 0x82, 0x5c, 0x6c, 0xfe, 0x38, 0x67, 0x3b, 0xcb, 0xfa, + 0x56, 0x5f, 0x67, 0x8f, 0x45, 0x1a, 0x62, 0x86, 0xbf, 0xa8, 0xdb, 0x6e, + 0x38, 0xc6, 0xa8, 0xbb, 0x0f, 0x12, 0x46, 0xe1, 0x8b, 0xac, 0x1f, 0x11, + 0x88, 0xd2, 0x86, 0xd3, 0x37, 0x3b, 0xe5, 0x5e, 0xf0, 0x15, 0x50, 0x37, + 0x64, 0x1a, 0x51, 0x2d, 0xfb, 0x92, 0x99, 0x39, 0xe5, 0xe9, 0xbd, 0x03, + 0x8c, 0xf2, 0x80, 0xeb, 0x15, 0x47, 0xb1, 0xd0, 0xd3, 0x5a, 0x2a, 0x01, + 0x0d, 0x53, 0x91, 0xd0, 0x7b, 0x3d, 0x48, 0x26, 0x2d, 0xd5, 0x70, 0x34, + 0xd3, 0x6f, 0x81, 0xbb, 0x9e, 0xbe, 0xec, 0x4b, 0x72, 0x37, 0x85, 0xbe, + 0x30, 0x06, 0x77, 0x3e, 0xfb, 0x81, 0x1d, 0x93, 0x3f, 0xc5, 0x89, 0xb9, + 0x90, 0xc4, 0x48, 0x92, 0x06, 0xae, 0x4f, 0x1a, 0xc0, 0x32, 0xf9, 0x64, + 0xa6, 0x56, 0xd4, 0x27, 0x0b, 0x0d, 0xd2, 0xa4, 0x39, 0x8a, 0x40, 0xe3, + 0x61, 0x1c, 0x2c, 0xdf, 0xcd, 0x7b, 0xcc, 0x84, 0x29, 0xe8, 0xab, 0xda, + 0x01, 0x08, 0xc0, 0x0c, 0xc6, 0xdc, 0x32, 0x83, 0x3e, 0xf3, 0x47, 0xa2, + 0xc9, 0xbd, 0xce, 0x2b, 0x09, 0x48, 0x58, 0xdc, 0x99, 0x9b, 0x2f, 0x37, + 0x9a, 0x05, 0xc5, 0x92, 0x11, 0x80, 0xfe, 0x3c, 0x0a, 0xfa, 0x1b, 0x47, + 0xb9, 0xf5, 0x5e, 0x59, 0x71, 0x10, 0x37, 0x28, 0x90, 0x11, 0xe7, 0x64, + 0x1c, 0xbe, 0x3d, 0xe3, 0x40, 0x4d, 0x42, 0x28, 0xfd, 0xfb, 0xb1, 0x06, + 0x30, 0xae, 0xfe, 0x49, 0x78, 0x0e, 0x07, 0x3c, 0x97, 0x30, 0x3a, 0x46, + 0xce, 0xb7, 0xb3, 0x0e, 0xfa, 0x73, 0xa7, 0xe4, 0x79, 0x72, 0x42, 0xc6, + 0xf5, 0x85, 0x16, 0x7c, 0x71, 0xd8, 0x98, 0x6b, 0xc4, 0xec, 0x45, 0xb2, + 0x3b, 0x0c, 0x2a, 0xea, 0x12, 0xc2, 0x1a, 0x77, 0xb4, 0xfd, 0x3d, 0x79, + 0xab, 0x12, 0xa7, 0x41, 0x84, 0x58, 0x39, 0x72, 0x3f, 0x5b, 0x9d, 0x97, + 0x8f, 0x33, 0x0e, 0xce, 0x6d, 0x15, 0x84, 0x68, 0x2a, 0x06, 0xe6, 0x72, + 0x25, 0x71, 0x63, 0x86, 0x49, 0xfc, 0x78, 0xce, 0x7e, 0x38, 0xbe, 0x15, + 0x38, 0xa7, 0x7a, 0xed, 0x87, 0xbd, 0x3e, 0x95, 0x35, 0xb9, 0xa6, 0x79, + 0xef, 0xf4, 0x62, 0xa0, 0x78, 0x45, 0xd5, 0xef, 0x3d, 0x06, 0x9f, 0xaa, + 0x91, 0x7c, 0xf2, 0x66, 0xcb, 0x51, 0x8a, 0x19, 0x4e, 0x56, 0x68, 0xcb, + 0xb8, 0x0e, 0xb5, 0xa1, 0x1a, 0x93, 0xb9, 0xc8, 0xef, 0x61, 0xa5, 0xc0, + 0x87, 0x28, 0xfa, 0x46, 0x53, 0x39, 0x84, 0x68, 0x67, 0x6d, 0x2a, 0x33, + 0x77, 0x32, 0x3d, 0xfc, 0xe5, 0x28, 0x29, 0xd3, 0xd4, 0xbe, 0xfa, 0x25, + 0xb3, 0x39, 0x56, 0x4f, 0x4d, 0x28, 0x36, 0x1a, 0x6c, 0xa9, 0x32, 0x19, + 0x87, 0x1f, 0x38, 0x9a, 0xce, 0xe9, 0xec, 0x01, 0xcc, 0xc7, 0xca, 0xf7, + 0xfc, 0xe5, 0xf6, 0x41, 0x79, 0x27, 0xac, 0x48, 0xea, 0x81, 0x29, 0xbb, + 0xfb, 0x8b, 0xc9, 0x68, 0xcc, 0xe7, 0xd0, 0xf6, 0xf7, 0x51, 0x81, 0x7a, + 0xd5, 0x6a, 0x31, 0x57, 0xb3, 0x76, 0xdc, 0x52, 0x62, 0x2a, 0xdc, 0x80, + 0xda, 0x36, 0x96, 0x46, 0x09, 0x8d, 0x3b, 0xf6, 0xb2, 0x84, 0xf7, 0xc9, + 0xff, 0x7e, 0x29, 0x31, 0x49, 0x80, 0xbb, 0xdf, 0xa3, 0x8e, 0x37, 0xe0, + 0x41, 0x12, 0x12, 0x36, 0x23, 0x6a, 0xfb, 0xc8, 0x5a, 0x48, 0x36, 0x72, + 0x4e, 0x9f, 0xfb, 0x82, 0x8b, 0xc5, 0x31, 0x52, 0xa2, 0x49, 0x69, 0xa6, + 0xba, 0xc1, 0xa5, 0xd3, 0xdd, 0x9e, 0x0a, 0x3c, 0x57, 0xb8, 0xe6, 0x77, + 0x77, 0x0c, 0x22, 0x91, 0x2d, 0x10, 0x90, 0x1a, 0x66, 0x4b, 0x0c, 0x95, + 0xb8, 0xf9, 0x25, 0xa9, 0x50, 0x3e, 0x18, 0x0d, 0xc2, 0x12, 0xbd, 0xef, + 0x33, 0x57, 0x9f, 0x59, 0x86, 0xac, 0xaa, 0xfa, 0xb0, 0xcc, 0x47, 0x12, + 0x1e, 0x3a, 0x92, 0x04, 0x4b, 0x52, 0xdf, 0xc5, 0x83, 0xd8, 0xe5, 0xf4, + 0xdb, 0x47, 0x20, 0x68, 0xb2, 0xc7, 0x9c, 0xb0, 0x7a, 0xf6, 0xa3, 0xbc, + 0xf6, 0x3b, 0xe3, 0xfb, 0xd5, 0x90, 0x7a, 0x9f, 0x45, 0x59, 0x63, 0x19, + 0x4e, 0xe2, 0x03, 0x75, 0x81, 0x0a, 0xb4, 0x90, 0x0d, 0x98, 0x29, 0xe9, + 0x57, 0xbb, 0x22, 0x5e, 0x2e, 0x8e, 0xd3, 0x2d, 0x6b, 0xdb, 0xc0, 0x9c, + 0x57, 0x94, 0x12, 0x1f, 0x32, 0x64, 0x88, 0xd3, 0x71, 0xaf, 0x03, 0x21, + 0x56, 0x1b, 0x9b, 0x7d, 0x7a, 0xd4, 0xd5, 0xe2, 0xf1, 0x7f, 0xcd, 0xb2, + 0x9d, 0xc6, 0xa6, 0x15, 0x34, 0xfa, 0x50, 0x61, 0xce, 0x57, 0x4f, 0x11, + 0x50, 0x3e, 0x1b, 0x69, 0x4e, 0x38, 0x8b, 0xc0, 0xf3, 0x9c, 0x48, 0x9f, + 0x33, 0x00, 0x49, 0xec, 0xd0, 0xfb, 0xdd, 0x34, 0xf0, 0x2f, 0xcc, 0xab, + 0xf9, 0x76, 0x4e, 0x24, 0x71, 0xf4, 0xe1, 0xe6, 0x68, 0x70, 0x02, 0x56, + 0x5e, 0xf2, 0xe0, 0x82, 0xcd, 0xb3, 0x80, 0xb1, 0xe3, 0xb3, 0xc8, 0x60, + 0xc1, 0x06, 0xc0, 0xd4, 0xb2, 0x70, 0x1b, 0xa0, 0xf2, 0x2d, 0x99, 0xbc, + 0x17, 0xd1, 0x1a, 0x4a, 0x7b, 0x6b, 0xdd, 0xa5, 0xc1, 0x9b, 0xdb, 0x14, + 0x26, 0x05, 0xb3, 0x98, 0x7a, 0x10, 0x0b, 0x33, 0xeb, 0xb0, 0x75, 0x88, + 0xc9, 0xa6, 0xa3, 0xbd, 0xac, 0x9d, 0xd5, 0x06, 0x77, 0x67, 0xf9, 0x71, + 0xb1, 0x05, 0x04, 0x89, 0x35, 0xf9, 0x10, 0x64, 0xf5, 0x23, 0x21, 0xb0, + 0x8b, 0xc8, 0xee, 0x13, 0x47, 0x29, 0x3e, 0x7a, 0x73, 0xa1, 0xf0, 0x33, + 0xb4, 0xd1, 0x19, 0x21, 0x4a, 0x9d, 0xc5, 0xb7, 0xfa, 0xff, 0x56, 0x76, + 0x5a, 0xbf, 0x08, 0xe2, 0xff, 0xee, 0x5c, 0x12, 0x13, 0x84, 0xb5, 0x48, + 0x24, 0x35, 0x01, 0xc3, 0x44, 0xf4, 0xeb, 0x53, 0x3c, 0xc6, 0xf9, 0x7b, + 0xb4, 0xa2, 0xb4, 0xd4, 0xc6, 0x44, 0x37, 0xd1, 0x72, 0xe2, 0x23, 0x41, + 0x5c, 0xb1, 0x21, 0xff, 0x08, 0xef, 0xd3, 0xad, 0x00, 0x37, 0xc9, 0xf5, + 0x96, 0x80, 0x7a, 0xd6, 0x45, 0x83, 0x8b, 0x58, 0x10, 0xa0, 0xe1, 0xd8, + 0xf7, 0x1a, 0x3e, 0xee, 0x73, 0xf2, 0xcb, 0x17, 0x08, 0x10, 0x78, 0xed, + 0xbb, 0x11, 0xbc, 0xbd, 0xbb, 0xb3, 0xd1, 0xfd, 0x40, 0x72, 0x38, 0x86, + 0x60, 0xc4, 0xcc, 0xdc, 0xf8, 0xf7, 0xc7, 0xd7, 0x5c, 0xdf, 0x2d, 0xde, + 0xdf, 0x94, 0xe5, 0xbc, 0x86, 0xaf, 0x7d, 0xff, 0xf0, 0x82, 0xe5, 0x38, + 0x48, 0x9c, 0xfe, 0xdd, 0xfd, 0x04, 0x40, 0x04, 0xbc, 0x8a, 0x62, 0xc2, + 0x8f, 0xd2, 0x31, 0xd4, 0x15, 0xd1, 0x76, 0x73, 0x78, 0xb6, 0xcd, 0xa4, + 0x11, 0xfa, 0xb5, 0xeb, 0x00, 0x59, 0x5d, 0xe1, 0x37, 0x26, 0x6c, 0xe6, + 0x38, 0xfc, 0xb2, 0x25, 0x79, 0x3c, 0x82, 0xb5, 0xbd, 0x01, 0x7f, 0x7b, + 0x77, 0xb0, 0x85, 0x9f, 0x24, 0x36, 0xca, 0xe7, 0x86, 0x0f, 0xa0, 0xc5, + 0xb4, 0x84, 0xb9, 0xa6, 0x12, 0x0a, 0x64, 0xed, 0x39, 0xf3, 0xbb, 0x79, + 0x5c, 0x5a, 0xaf, 0xee, 0xa8, 0x95, 0x7a, 0x37, 0x6d, 0x6f, 0x82, 0x71, + 0xca, 0xf5, 0xe4, 0x3d, 0x79, 0x7d, 0xeb, 0x01, 0xf3, 0x84, 0x13, 0x2f, + 0xec, 0x71, 0x4a, 0xe7, 0x15, 0xd0, 0x2e, 0xd9, 0x81, 0x15, 0x14, 0x0d, + 0xfa, 0x8a, 0x89, 0x7f, 0x05, 0xf0, 0x7d, 0x23, 0x02, 0xbb, 0x6c, 0xcf, + 0x4c, 0xf8, 0xb8, 0xb0, 0x28, 0x92, 0xc1, 0x05, 0x6d, 0xf9, 0xb5, 0xa5, + 0x2a, 0x17, 0xc9, 0x98, 0xe2, 0x3b, 0x69, 0x3c, 0x34, 0x35, 0xb2, 0x2a, + 0x36, 0xe2, 0x9e, 0x18, 0x4a, 0x5e, 0x1e, 0xa7, 0x05, 0x82, 0xeb, 0x75, + 0xa8, 0x8b, 0x77, 0x58, 0x67, 0x7b, 0x12, 0xec, 0x34, 0x1e, 0xbe, 0x54, + 0x17, 0xc2, 0xfa, 0x9a, 0xd2, 0x70, 0xe1, 0x31, 0x63, 0x30, 0x39, 0xe4, + 0xa9, 0x10, 0xe0, 0x03, 0x24, 0xbf, 0xa0, 0x2c, 0xb3, 0xd6, 0x6a, 0xf4, + 0x3d, 0x46, 0xbb, 0x42, 0xa2, 0x86, 0x96, 0x1a, 0x49, 0x72, 0xa3, 0x8a, + 0x24, 0x66, 0x84, 0xf8, 0xb9, 0xc0, 0xe5, 0x42, 0x6d, 0x0e, 0xc3, 0x42, + 0x8e, 0x34, 0xe6, 0x7f, 0x17, 0x75, 0xc6, 0xca, 0xcd, 0x66, 0x23, 0x5e, + 0x1e, 0xf2, 0x81, 0x38, 0x5c, 0x73, 0xf7, 0x28, 0x52, 0xcb, 0x13, 0xc3, + 0x44, 0x1d, 0x95, 0x42, 0x7a, 0x2c, 0xd4, 0x56, 0xa5, 0xfd, 0xbc, 0x6a, + 0x74, 0x05, 0x18, 0xb2, 0xe6, 0x3d, 0x9f, 0xe9, 0xaa, 0xff, 0xae, 0x9b, + 0xe9, 0x06, 0x94, 0x83, 0x63, 0xb8, 0x48, 0xbd, 0xe4, 0x76, 0x35, 0x5b, + 0x94, 0x78, 0xc9, 0x8f, 0xb6, 0xa9, 0xf5, 0x31, 0xd9, 0x52, 0x2f, 0x7e, + 0x58, 0x67, 0x64, 0x64, 0x58, 0xe3, 0xec, 0x95, 0x0e, 0x55, 0xe4, 0x72, + 0xb1, 0x62, 0x0c, 0xf1, 0x83, 0xc1, 0xf5, 0x19, 0x9f, 0x3c, 0xf0, 0x2d, + 0xdc, 0xcb, 0xa6, 0xa0, 0x24, 0xd8, 0x5e, 0x2e, 0xf7, 0xaf, 0x2b, 0x00, + 0xcc, 0x89, 0xc1, 0x73, 0x80, 0x89, 0x44, 0xe9, 0x0e, 0x7b, 0xd1, 0x10, + 0xfc, 0xaa, 0x69, 0xa1, 0xa6, 0xa6, 0x7f, 0x95, 0x81, 0x0e, 0x88, 0x77, + 0xc2, 0x44, 0x81, 0x34, 0xea, 0xc6, 0x3b, 0x60, 0x14, 0xe6, 0x7d, 0x49, + 0x9c, 0x88, 0x8e, 0xa4, 0x0a, 0xc8, 0x90, 0x23, 0x0d, 0x49, 0x36, 0xdf, + 0x2d, 0xea, 0xf7, 0xb1, 0xee, 0xea, 0xb3, 0xf5, 0xb4, 0xf9, 0x93, 0xc8, + 0x97, 0x60, 0xb8, 0xe1, 0x2e, 0x9d, 0xd7, 0x36, 0x0c, 0x21, 0x62, 0x16, + 0xce, 0x9e, 0xe3, 0x54, 0x8a, 0xcb, 0x08, 0x48, 0x0c, 0x65, 0xee, 0x74, + 0x82, 0x01, 0xa8, 0x6e, 0xf5, 0x94, 0xc2, 0x62, 0x77, 0x31, 0xa6, 0xb1, + 0x47, 0x26, 0xa7, 0x6c, 0x51, 0x2f, 0xc1, 0xde, 0xed, 0x0f, 0xee, 0x5a, + 0xcd, 0xdc, 0x39, 0xe6, 0xa6, 0xcc, 0xfe, 0xc4, 0xb8, 0xd0, 0x6a, 0x96, + 0xf0, 0x90, 0xa3, 0xbb, 0x58, 0xaf, 0x36, 0x6f, 0x73, 0x4d, 0x9f, 0xa3, + 0x81, 0xb8, 0x05, 0xcd, 0x8a, 0x4a, 0xa4, 0xca, 0x05, 0xd1, 0x53, 0x67, + 0xb2, 0x9a, 0xef, 0xff, 0x06, 0x6c, 0xa8, 0x62, 0x74, 0xc6, 0xb4, 0x2f, + 0xaa, 0x90, 0x39, 0x22, 0x45, 0x04, 0xb0, 0xbc, 0x15, 0x86, 0xe0, 0x50, + 0x39, 0xb0, 0x1e, 0xb8, 0x46, 0x38, 0x4a, 0x3c, 0xe8, 0x8c, 0xbb, 0x8c, + 0xe8, 0x6d, 0xb5, 0x23, 0x57, 0xf2, 0x30, 0xa2, 0x15, 0x06, 0xa3, 0x79, + 0x7f, 0xbc, 0x54, 0x29, 0x27, 0xca, 0xc9, 0x18, 0xd6, 0xa2, 0x36, 0x70, + 0xfa, 0x93, 0x59, 0x80, 0x23, 0xd0, 0x63, 0xa1, 0xb0, 0xb3, 0xdc, 0x3e, + 0xd1, 0xb2, 0xbf, 0x2f, 0xd0, 0x6a, 0x3e, 0x76, 0x97, 0x60, 0x9a, 0x2a, + 0x56, 0x81, 0x25, 0x65, 0x65, 0x29, 0x44, 0x29, 0xe2, 0xc1, 0x72, 0x19, + 0x27, 0xd1, 0x59, 0xb3, 0x5d, 0xb2, 0xf1, 0x3b, 0xee, 0xbf, 0xa1, 0xd5, + 0x1d, 0x4d, 0x51, 0xb1, 0x93, 0x79, 0x15, 0x2c, 0x4f, 0x06, 0x90, 0x58, + 0x9c, 0x0f, 0xa7, 0x36, 0x87, 0xe1, 0x6b, 0x6e, 0xab, 0x09, 0x3f, 0xcb, + 0x79, 0x1c, 0xbd, 0x74, 0x8e, 0xec, 0xce, 0x1b, 0x66, 0x80, 0x71, 0x97, + 0x94, 0x0a, 0x56, 0xe4, 0xf6, 0xee, 0xb9, 0x99, 0x7a, 0x25, 0x8d, 0xa4, + 0x6e, 0xc3, 0xa0, 0xbe, 0x42, 0x2b, 0x4f, 0xc8, 0xd9, 0x08, 0xe5, 0x7f, + 0x59, 0x10, 0xab, 0x5d, 0xcd, 0x7c, 0x57, 0x7a, 0x2f, 0x10, 0x36, 0xa8, + 0x0b, 0x26, 0x74, 0xfe, 0xd2, 0x71, 0x8a, 0xc7, 0x34, 0xec, 0xe9, 0xd7, + 0x23, 0x3e, 0xc9, 0x83, 0x6d, 0x0b, 0xa0, 0x38, 0x2a, 0x06, 0x54, 0x52, + 0xd5, 0xbc, 0xe9, 0x3d, 0x17, 0xba, 0x5a, 0xc3, 0x94, 0x41, 0xd3, 0xb9, + 0xd7, 0x4b, 0x3d, 0xb5, 0x7c, 0xde, 0x00, 0x5b, 0x6d, 0x7f, 0xed, 0xef, + 0x05, 0x5f, 0xa9, 0x2a, 0x6c, 0x22, 0x57, 0x22, 0xee, 0xfa, 0xb4, 0xd3, + 0xbf, 0xab, 0x53, 0xe7, 0x97, 0xbf, 0x3a, 0x3e, 0x48, 0x14, 0xc8, 0x2a, + 0x72, 0xec, 0xc0, 0x81, 0x8f, 0x9b, 0x8b, 0xc1, 0x09, 0x10, 0x3b, 0x1c, + 0xec, 0xe7, 0xca, 0xc4, 0x1b, 0xe4, 0x6f, 0xbc, 0x95, 0xf0, 0x83, 0xc0, + 0x15, 0xbf, 0xee, 0xf7, 0xfc, 0xc9, 0x42, 0x04, 0xa4, 0xf7, 0x48, 0x04, + 0xc5, 0x44, 0x77, 0x5e, 0x3c, 0x1a, 0xdb, 0xf1, 0xc6, 0x9d, 0x37, 0xac, + 0x63, 0x80, 0xc1, 0xbc, 0xe9, 0x8b, 0x63, 0xed, 0x32, 0x27, 0x6e, 0xa2, + 0xd0, 0x11, 0x11, 0xf2, 0x33, 0x0b, 0xc8, 0x24, 0x31, 0x94, 0xba, 0xa9, + 0xef, 0x7e, 0xef, 0x62, 0x27, 0x66, 0xcf, 0xe7, 0x4d, 0xd1, 0xc8, 0x7f, + 0x4c, 0x04, 0x9b, 0xd5, 0xe3, 0x71, 0x13, 0x45, 0x30, 0x61, 0x84, 0x5e, + 0xc3, 0xe2, 0x22, 0x40, 0xcf, 0x53, 0xc6, 0x93, 0xa2, 0xc6, 0x58, 0xfb, + 0x25, 0x82, 0xcc, 0x5f, 0x9e, 0x12, 0x4f, 0xa3, 0x78, 0xfe, 0x25, 0xfc, + 0x52, 0x6c, 0xeb, 0xc4, 0xf0, 0x63, 0x19, 0x4a, 0x60, 0x6f, 0x75, 0xd4, + 0xbb, 0xaa, 0xd7, 0xf6, 0x7b, 0x3e, 0xbc, 0x14, 0xc3, 0x0b, 0xc2, 0xe5, + 0x0d, 0x3e, 0xa2, 0xc9, 0x0f, 0xab, 0x8f, 0xe7, 0x84, 0x26, 0x52, 0x10, + 0xfc, 0x5c, 0x44, 0x4f, 0x64, 0x59, 0x8a, 0xc7, 0xe9, 0xa4, 0x63, 0x77, + 0x98, 0xce, 0x96, 0xfe, 0x01, 0xe7, 0xc4, 0x43, 0x62, 0xee, 0x27, 0x94, + 0xc8, 0x3a, 0xaf, 0x33, 0x3d, 0x5c, 0xca, 0x55, 0x89, 0xac, 0xc1, 0x8e, + 0xd3, 0xb4, 0xf8, 0x50, 0x53, 0x58, 0xb3, 0x4a, 0x33, 0x3e, 0x27, 0x42, + 0x82, 0x99, 0xa9, 0x0b, 0xea, 0xe8, 0x26, 0x23, 0x4a, 0x24, 0xb1, 0x46, + 0x5f, 0x19, 0x9e, 0xb1, 0xab, 0xe7, 0x2c, 0x2c, 0xad, 0x86, 0xd2, 0xeb, + 0x7f, 0x67, 0x51, 0xec, 0x0a, 0x73, 0x42, 0x01, 0x17, 0x25, 0x0d, 0xa5, + 0x06, 0x8b, 0xed, 0xa2, 0x92, 0x34, 0x56, 0xcc, 0xb1, 0x2a, 0x87, 0xf5, + 0xd2, 0xdf, 0x92, 0xb9, 0x01, 0x93, 0xc7, 0x94, 0x01, 0x10, 0xb4, 0x1b, + 0x5e, 0xf2, 0x1d, 0x89, 0x42, 0x9f, 0x48, 0xf5, 0x13, 0xfe, 0x1d, 0x61, + 0xda, 0x09, 0xaf, 0x82, 0x9c, 0x9a, 0x47, 0x9c, 0x9d, 0x3a, 0xbf, 0x96, + 0x0a, 0x06, 0xfa, 0x23, 0x58, 0x7e, 0x8e, 0x76, 0x42, 0xa6, 0x26, 0xa4, + 0xb6, 0x8c, 0xad, 0xbf, 0x0b, 0x58, 0x8d, 0x15, 0x10, 0xe7, 0x20, 0x5c, + 0x4e, 0x76, 0xb7, 0xae, 0xf0, 0x45, 0x07, 0x71, 0x6b, 0x98, 0x5d, 0xc0, + 0x46, 0xdf, 0xb7, 0x79, 0xc6, 0xda, 0x11, 0xc8, 0xf8, 0x37, 0xe1, 0x5e, + 0x21, 0xfe, 0xab, 0xbe, 0x7a, 0xdd, 0xd7, 0xe7, 0x35, 0xdd, 0x30, 0xc6, + 0x59, 0xbd, 0xce, 0x7f, 0x15, 0xa2, 0x92, 0xec, 0x23, 0x43, 0x9f, 0x0c, + 0x8f, 0x19, 0x2c, 0xed, 0xbb, 0xb4, 0x5e, 0x20, 0xa8, 0x26, 0xe3, 0x72, + 0x00, 0x83, 0x98, 0xab, 0xb8, 0x48, 0x70, 0x37, 0xb5, 0xe7, 0xdd, 0x6b, + 0x2e, 0x54, 0xc3, 0x93, 0x0f, 0xab, 0x63, 0x2b, 0x71, 0xa8, 0x2f, 0x02, + 0x15, 0x61, 0x51, 0xb3, 0x5d, 0xd7, 0xb4, 0xd1, 0x7f, 0x40, 0xcc, 0xc2, + 0x0f, 0xf4, 0xaf, 0xe7, 0x0c, 0x49, 0x28, 0xee, 0x3b, 0xe9, 0x04, 0x01, + 0x1b, 0x85, 0x5e, 0xa2, 0xa8, 0x3d, 0x50, 0xd5, 0xe3, 0x41, 0xe4, 0x36, + 0x8c, 0x9c, 0xb4, 0x47, 0xb1, 0x18, 0x5e, 0x5f, 0x8b, 0xb2, 0xf6, 0x40, + 0xdd, 0x7f, 0x40, 0xa7, 0x6d, 0xa9, 0x1e, 0x8d, 0xfb, 0x16, 0x2c, 0x53, + 0xf7, 0x44, 0xcc, 0x7a, 0x1a, 0x7b, 0x3d, 0x7d, 0xd7, 0x7f, 0x95, 0x87, + 0x08, 0x1f, 0x31, 0x3b, 0x24, 0xb3, 0x52, 0xc0, 0xdf, 0x0f, 0x72, 0xd5, + 0xe9, 0xe9, 0x1b, 0xd2, 0x29, 0x64, 0x2d, 0x96, 0xcb, 0xc8, 0xe7, 0x83, + 0x33, 0x85, 0x39, 0xf2, 0x3c, 0x93, 0x78, 0x8f, 0xa9, 0x08, 0xf4, 0x3a, + 0x95, 0xa9, 0xf9, 0xdc, 0xc1, 0xee, 0x25, 0x0e, 0xb5, 0xd2, 0xb8, 0xb5, + 0xdb, 0x0b, 0x50, 0xd8, 0x0f, 0xef, 0x18, 0xaa, 0x96, 0x7f, 0x0e, 0x7b, + 0x01, 0xdd, 0xaa, 0x79, 0xaa, 0x72, 0x5e, 0x3b, 0x4b, 0xd2, 0x6f, 0x4d, + 0x17, 0xda, 0xbd, 0xa9, 0xce, 0x91, 0x33, 0xe3, 0xee, 0x30, 0x9b, 0xcb, + 0xf3, 0x48, 0x58, 0x53, 0xa3, 0x65, 0x9b, 0x3a, 0xfb, 0xf9, 0xf9, 0x8b, + 0x84, 0x57, 0xcb, 0x6e, 0x6c, 0x1d, 0x66, 0xdc, 0x1f, 0x1d, 0xe5, 0x54, + 0x50, 0xa9, 0xe0, 0x77, 0xf0, 0x94, 0xd2, 0x29, 0xdd, 0x0e, 0xb0, 0x08, + 0x42, 0x58, 0x89, 0x95, 0x55, 0xc9, 0x11, 0xd6, 0xd0, 0xda, 0x28, 0x4e, + 0x25, 0x4d, 0x1c, 0xd3, 0x4b, 0x84, 0x91, 0x8a, 0x1c, 0xe3, 0x94, 0x42, + 0x94, 0xa0, 0x0e, 0xda, 0x5a, 0x5e, 0xc5, 0xa4, 0xab, 0x32, 0x8d, 0xa0, + 0xb1, 0x71, 0x1e, 0x29, 0x73, 0x97, 0x46, 0x5d, 0x34, 0x74, 0x9d, 0x1e, + 0xa5, 0xe1, 0x55, 0x14, 0xb2, 0x23, 0x5d, 0xe0, 0x2f, 0x1b, 0xae, 0x8f, + 0xea, 0x44, 0x70, 0x6c, 0x08, 0x9f, 0x7d, 0x2a, 0xef, 0x69, 0x72, 0xdc, + 0x18, 0xe6, 0x70, 0xf4, 0xe9, 0x55, 0x99, 0x26, 0xcc, 0x0a, 0x85, 0x99, + 0xe5, 0x11, 0xa3, 0x35, 0xc4, 0xe3, 0xa8, 0x16, 0x99, 0x7f, 0xce, 0x79, + 0xbb, 0x5f, 0x63, 0xae, 0x03, 0x7d, 0x18, 0x98, 0xa2, 0x90, 0x0a, 0xe0, + 0xf7, 0x11, 0x8a, 0xdb, 0xba, 0x3a, 0xbe, 0x0d, 0xd2, 0x0d, 0x4c, 0xed, + 0xfe, 0xe7, 0xd3, 0xbf, 0x75, 0x7c, 0xed, 0x03, 0x5a, 0x15, 0x92, 0x92, + 0x00, 0x42, 0x6b, 0x38, 0x78, 0x56, 0x9b, 0xee, 0xb9, 0x58, 0xc4, 0x71, + 0xdf, 0xcd, 0xdb, 0x8d, 0x3c, 0xc3, 0x05, 0x03, 0x5c, 0xbe, 0xe9, 0x6d, + 0x03, 0x50, 0x10, 0x48, 0x82, 0x2d, 0x14, 0xde, 0xdd, 0xa8, 0x39, 0x07, + 0x1a, 0xce, 0x0c, 0x45, 0xa1, 0xa2, 0xc2, 0x1d, 0xeb, 0x79, 0x25, 0xa5, + 0x63, 0x37, 0x46, 0x1b, 0x3c, 0xf1, 0x45, 0xee, 0xfa, 0x2f, 0x4c, 0x78, + 0xa9, 0x32, 0x8c, 0x92, 0xcb, 0x0a, 0x64, 0x0f, 0xf6, 0xed, 0x68, 0x1d, + 0x55, 0xe5, 0x6c, 0x8f, 0x97, 0x00, 0x3d, 0x76, 0x14, 0x45, 0xb0, 0x6f, + 0x5b, 0x1c, 0x72, 0x4c, 0x6d, 0x21, 0x49, 0x1a, 0xbb, 0x1d, 0x1f, 0xd3, + 0x7e, 0x2d, 0x19, 0x2f, 0xc4, 0x4d, 0x9b, 0x58, 0xe3, 0x41, 0xdc, 0x9f, + 0x87, 0x1e, 0x70, 0x38, 0x9a, 0x95, 0x2b, 0x1b, 0xc8, 0xb6, 0x0b, 0x17, + 0x86, 0x7d, 0xc4, 0x29, 0x7d, 0x00, 0x14, 0xe3, 0x10, 0xb3, 0x91, 0xff, + 0xe6, 0x8f, 0x32, 0x59, 0xef, 0x8e, 0x53, 0xfd, 0xc1, 0xfe, 0xe9, 0x6a, + 0xc8, 0x02, 0xb7, 0x74, 0xbd, 0x98, 0x86, 0x6f, 0x0d, 0x91, 0x12, 0xd3, + 0x1b, 0x88, 0x83, 0x09, 0x34, 0xbf, 0xda, 0xf4, 0x1c, 0x84, 0x9a, 0x0f, + 0x2b, 0x94, 0xce, 0xc2, 0xe6, 0x60, 0x19, 0xa3, 0x2d, 0xcf, 0x38, 0xef, + 0xa9, 0x43, 0x60, 0x72, 0x32, 0x8d, 0x0d, 0x77, 0xe1, 0x32, 0xf5, 0xa1, + 0xa9, 0xbb, 0xbf, 0x9f, 0x77, 0xde, 0x32, 0x00, 0xb6, 0xf3, 0x94, 0x38, + 0x72, 0x85, 0x19, 0xa4, 0xca, 0x40, 0x0b, 0x68, 0x8c, 0x79, 0x10, 0x24, + 0x75, 0xe4, 0x09, 0x3d, 0x33, 0x39, 0x5f, 0xe6, 0xd2, 0xaa, 0xb5, 0x0c, + 0xcf, 0xe4, 0x0d, 0x79, 0xfe, 0x38, 0x6b, 0xe8, 0x43, 0x3c, 0x5d, 0x33, + 0x35, 0x2b, 0xda, 0x6a, 0x73, 0x95, 0xc4, 0x8d, 0xe7, 0x30, 0x49, 0x93, + 0x0e, 0x9e, 0xe6, 0xe2, 0x5d, 0xf9, 0x95, 0x13, 0x23, 0x00, 0xc0, 0xe5, + 0xd7, 0x6f, 0x3c, 0x0d, 0x94, 0x9e, 0xdd, 0xf9, 0x54, 0xa8, 0xcc, 0xfc, + 0x6c, 0x40, 0xa6, 0x52, 0x1f, 0x30, 0x6b, 0x3b, 0x52, 0x39, 0xaa, 0xe4, + 0xc8, 0xfd, 0x90, 0x54, 0xa0, 0xf0, 0x2c, 0x57, 0x97, 0x58, 0x88, 0xb1, + 0xbe, 0xee, 0x95, 0xd8, 0x15, 0x20, 0x45, 0xd2, 0x29, 0x81, 0xb6, 0x6e, + 0xf9, 0x8e, 0xc0, 0x69, 0x23, 0xa9, 0xa7, 0xd1, 0x34, 0x63, 0xbc, 0xcc, + 0xdd, 0x6c, 0xb4, 0x93, 0x85, 0xf1, 0xee, 0xfb, 0x9b, 0x56, 0x68, 0xdd, + 0x8f, 0xae, 0xca, 0x1e, 0xdf, 0x3c, 0x22, 0x96, 0x67, 0x18, 0xba, 0x24, + 0xe2, 0x2f, 0xe1, 0x71, 0x7e, 0x3f, 0x4c, 0x73, 0xbe, 0x4b, 0x1a, 0xae, + 0x39, 0x85, 0x70, 0x54, 0x88, 0x2f, 0xd4, 0xc8, 0xbd, 0x87, 0xcf, 0x64, + 0x1f, 0x01, 0x03, 0x0a, 0x35, 0xa2, 0x95, 0x4b, 0xba, 0x46, 0x21, 0x0b, + 0x03, 0x7d, 0x00, 0x7f, 0x8d, 0xb6, 0x72, 0xaa, 0x27, 0x24, 0x71, 0xbf, + 0x41, 0xc0, 0x87, 0x52, 0xbf, 0x7a, 0xfa, 0x6c, 0xeb, 0x0a, 0x83, 0xbe, + 0xbc, 0x03, 0x2f, 0xfc, 0xf1, 0x62, 0x4d, 0x03, 0x5a, 0xcf, 0xfa, 0xfe, + 0x84, 0x9e, 0xa1, 0x8d, 0x31, 0x9e, 0x30, 0xa2, 0x44, 0x17, 0xd9, 0x94, + 0x71, 0x46, 0x73, 0x8f, 0xd9, 0x08, 0xa7, 0xe9, 0x12, 0xd7, 0x96, 0x00, + 0x34, 0x75, 0x49, 0x45, 0xcc, 0xc3, 0xc6, 0x63, 0x81, 0x85, 0x1d, 0x7e, + 0x88, 0x0b, 0x01, 0xaa, 0x87, 0x9d, 0x38, 0xff, 0x22, 0xe8, 0xac, 0xe8, + 0x44, 0x99, 0xf2, 0x6e, 0xab, 0xfd, 0x39, 0xd0, 0x6d, 0xeb, 0xac, 0xf1, + 0x82, 0xd9, 0x56, 0x6a, 0x3b, 0x5d, 0x35, 0xaf, 0xa2, 0x4f, 0xf5, 0x0a, + 0xaf, 0x94, 0xad, 0x14, 0xae, 0xed, 0xb9, 0xc6, 0xe9, 0xa2, 0x23, 0x09, + 0x88, 0x43, 0x00, 0x4d, 0x4e, 0x11, 0xcf, 0xbd, 0x9a, 0xad, 0xc1, 0xbd, + 0xb3, 0x23, 0x8f, 0x1c, 0x09, 0xdf, 0x4d, 0xd6, 0xf0, 0x14, 0x82, 0x44, + 0x26, 0xd7, 0x86, 0xe4, 0x12, 0x3f, 0x61, 0x82, 0x9d, 0xa0, 0x84, 0x54, + 0xeb, 0x19, 0x35, 0x89, 0x79, 0xbb, 0xe5, 0xac, 0xb6, 0x25, 0x0b, 0x39, + 0x8c, 0x17, 0xb8, 0xc4, 0x93, 0x80, 0x5c, 0x64, 0x91, 0x49, 0x4c, 0xe2, + 0x6b, 0xc1, 0xee, 0xe4, 0xc1, 0x3c, 0x9b, 0xd4, 0x91, 0xa2, 0xb9, 0x09, + 0x52, 0xb4, 0xf1, 0xff, 0xff, 0x27, 0xa1, 0x01, 0x0c, 0x78, 0x46, 0x9c, + 0xd4, 0x77, 0x65, 0xf5, 0x2b, 0x52, 0x89, 0xed, 0xe8, 0x7d, 0xd3, 0x11, + 0x87, 0x40, 0x60, 0xc0, 0xbf, 0x2b, 0x9d, 0xf4, 0x49, 0x42, 0xf5, 0xbb, + 0xe2, 0xc5, 0xb0, 0xf5, 0x78, 0xe3, 0xe6, 0x90, 0x9e, 0xc9, 0x31, 0xed, + 0x4f, 0x05, 0xe2, 0x2a, 0x20, 0xde, 0x66, 0x93, 0x92, 0x40, 0x75, 0x17, + 0xb7, 0xdd, 0x7c, 0x94, 0xbd, 0x73, 0x75, 0xd9, 0x0d, 0x29, 0xaa, 0x76, + 0x92, 0x9f, 0xe1, 0x5c, 0xd9, 0xa3, 0x97, 0x24, 0x7b, 0xaa, 0xc7, 0x7c, + 0x4e, 0x75, 0x5e, 0x59, 0xe1, 0xef, 0x31, 0xef, 0x77, 0x32, 0xa1, 0xf3, + 0x7c, 0x84, 0xb4, 0x7a, 0x44, 0x31, 0x23, 0x7a, 0x2a, 0x88, 0x4d, 0x6c, + 0x1b, 0x3b, 0xf6, 0x99, 0xc0, 0xd2, 0x26, 0xa4, 0x7a, 0x04, 0x60, 0x0a, + 0x02, 0x5d, 0xc3, 0xc5, 0xd3, 0x2b, 0x85, 0xc9, 0xc8, 0x74, 0x3c, 0xd7, + 0xfa, 0x40, 0x01, 0x54, 0x48, 0x32, 0x97, 0x46, 0x6d, 0x72, 0x7f, 0xce, + 0x66, 0x73, 0x8a, 0x89, 0x14, 0x67, 0x34, 0x1f, 0xa2, 0xcd, 0x0c, 0x83, + 0xdf, 0xcd, 0x8a, 0xe4, 0xb1, 0xfd, 0xae, 0x0c, 0x82, 0xe3, 0x56, 0xf3, + 0x4a, 0x27, 0xf3, 0x21, 0x71, 0xc4, 0xec, 0xf9, 0x63, 0xef, 0x20, 0x47, + 0x0c, 0xa9, 0xaa, 0x3f, 0xfe, 0x95, 0xa9, 0x85, 0x2c, 0xb5, 0x07, 0x2f, + 0xbc, 0x59, 0x42, 0x47, 0xa7, 0x91, 0x59, 0x41, 0x2f, 0x90, 0x4e, 0xfb, + 0x72, 0xcc, 0xa5, 0x47, 0xad, 0x66, 0xb9, 0xaf, 0xf5, 0x5a, 0x07, 0x9d, + 0x34, 0x69, 0x98, 0x43, 0x0a, 0xbb, 0xd5, 0x7a, 0xa7, 0x26, 0x30, 0x9f, + 0x28, 0xee, 0x91, 0x6a, 0x79, 0x01, 0xed, 0x53, 0x8c, 0xa9, 0xbf, 0x41, + 0x86, 0xeb, 0x7a, 0xff, 0x1b, 0x6c, 0xbd, 0x7b, 0x31, 0x33, 0x89, 0xf8, + 0x2a, 0xd2, 0x69, 0x5a, 0x4c, 0x5e, 0x2d, 0x48, 0x92, 0x95, 0x4a, 0x76, + 0xff, 0x21, 0x7f, 0x92, 0x4d, 0x4b, 0x32, 0x81, 0x96, 0xfb, 0x73, 0xb3, + 0x99, 0x60, 0x09, 0xf9, 0xc3, 0x80, 0x53, 0x9a, 0x6e, 0x95, 0x15, 0xe3, + 0x25, 0x53, 0xfc, 0xc8, 0xd1, 0x7b, 0xc7, 0x33, 0x64, 0x85, 0xbf, 0x28, + 0xa5, 0x4f, 0x0a, 0x3b, 0x95, 0x49, 0xb2, 0x40, 0x3c, 0x79, 0x2f, 0x96, + 0xfb, 0xcd, 0x27, 0x79, 0x44, 0x3e, 0x5c, 0x73, 0x3f, 0x16, 0x4b, 0x77, + 0xda, 0x3d, 0x7c, 0xd1, 0xcc, 0xf9, 0xfa, 0x17, 0x5e, 0xed, 0x3b, 0xb2, + 0xcb, 0x23, 0x96, 0xa4, 0xa2, 0x38, 0xda, 0xbc, 0xca, 0x12, 0x7f, 0x7f, + 0x0d, 0xd7, 0xc4, 0x48, 0x64, 0xac, 0xf0, 0x01, 0x71, 0x02, 0x01, 0x4f, + 0x60, 0x12, 0xf7, 0x16, 0x2b, 0x5c, 0xaf, 0xcb, 0xcb, 0xee, 0x01, 0x5d, + 0xac, 0x38, 0xf5, 0x6b, 0x29, 0x82, 0x7a, 0x1c, 0xf4, 0x82, 0xee, 0x98, + 0x08, 0x88, 0xa2, 0x1b, 0xfe, 0x0c, 0x7d, 0x7d, 0xee, 0x93, 0x68, 0x08, + 0x46, 0x3b, 0xa0, 0xe9, 0xe4, 0x82, 0x17, 0x4f, 0x72, 0x8e, 0x1b, 0x4c, + 0x00, 0xa7, 0xce, 0x53, 0x02, 0xb8, 0x08, 0xa4, 0xac, 0x56, 0xd3, 0x63, + 0x17, 0xbe, 0x6d, 0xfd, 0xaf, 0x85, 0x29, 0x10, 0x58, 0xeb, 0xd4, 0x8f, + 0x64, 0x68, 0x8e, 0x5e, 0x80, 0x9e, 0x35, 0x5e, 0xe3, 0x65, 0x24, 0x3e, + 0x72, 0x19, 0xcd, 0xd9, 0xd8, 0x69, 0x22, 0x8a, 0xeb, 0xd4, 0xe8, 0xbf, + 0x53, 0x4c, 0xe9, 0xf4, 0xa6, 0x3c, 0xde, 0x35, 0xea, 0x66, 0x4b, 0x4e, + 0x21, 0xf3, 0x63, 0x7d, 0xfd, 0x81, 0x7f, 0xaf, 0x9c, 0x7a, 0xd5, 0x9d, + 0xdb, 0x83, 0xc3, 0x4d, 0xff, 0xe1, 0xf0, 0x4e, 0x1c, 0x9d, 0xfb, 0x51, + 0x92, 0xcc, 0x60, 0xa6, 0x9e, 0xec, 0xd2, 0x80, 0xf0, 0x62, 0x22, 0x95, + 0x30, 0x48, 0xee, 0x7c, 0x52, 0xa2, 0xd2, 0x89, 0x0d, 0xc4, 0x2e, 0xe7, + 0xa2, 0x3d, 0x45, 0x82, 0xdf, 0xde, 0x96, 0x7e, 0x38, 0x8b, 0x6a, 0xb0, + 0xf1, 0xa7, 0x9a, 0xb3, 0xd8, 0xa0, 0xef, 0x03, 0x2c, 0x48, 0x44, 0xf8, + 0xd3, 0x42, 0x27, 0x28, 0x84, 0x38, 0x9d, 0xd0, 0x90, 0xfd, 0xc2, 0x02, + 0x59, 0xdd, 0x0a, 0xf3, 0x28, 0xfd, 0x54, 0x8b, 0x70, 0xc7, 0x74, 0x25, + 0xc5, 0xc0, 0x51, 0x6d, 0xcd, 0x90, 0x7a, 0xb1, 0x87, 0x1b, 0x55, 0xf8, + 0xae, 0x7f, 0x92, 0xdf, 0x3c, 0x58, 0xb3, 0x46, 0x64, 0x2e, 0xab, 0xa2, + 0x33, 0xff, 0xe5, 0x6e, 0xb9, 0xb7, 0x68, 0xc4, 0x40, 0x14, 0x83, 0xe0, + 0xac, 0xdc, 0x23, 0x7e, 0x4c, 0x1c, 0xec, 0x84, 0xfc, 0x28, 0x03, 0x43, + 0xb1, 0x43, 0xba, 0xd7, 0x9d, 0xd3, 0x31, 0x95, 0x6f, 0x31, 0xe4, 0x1c, + 0xde, 0x45, 0xaf, 0x50, 0xf4, 0xac, 0x9f, 0xd6, 0xf4, 0x77, 0xdc, 0x31, + 0xc4, 0x1c, 0x30, 0x72, 0x92, 0xf4, 0xdd, 0x26, 0xb4, 0x3f, 0xe2, 0xd6, + 0x1f, 0x22, 0xba, 0xd8, 0xaa, 0xd4, 0x61, 0xbb, 0x98, 0x11, 0xe8, 0x15, + 0x1e, 0x09, 0x91, 0xfd, 0xc3, 0x56, 0xe8, 0xd5, 0xc0, 0xdd, 0xf8, 0xfc, + 0xc9, 0x2a, 0x6b, 0xe7, 0x90, 0xfc, 0xc6, 0x09, 0xfe, 0x7b, 0xd9, 0x26, + 0x1e, 0xda, 0xf3, 0xc2, 0x3a, 0xba, 0x3e, 0x5c, 0x9e, 0x8a, 0xe7, 0x97, + 0x9c, 0xc4, 0x2b, 0xb9, 0x70, 0xc3, 0xe6, 0xc9, 0x18, 0xc8, 0xa2, 0x1a, + 0x8c, 0xd8, 0x6a, 0xfb, 0x9d, 0x43, 0x5d, 0x20, 0x82, 0xbd, 0xc1, 0x57, + 0x1b, 0xe7, 0x46, 0x1b, 0x7b, 0x9c, 0xd8, 0x4c, 0x53, 0x4a, 0x06, 0x4e, + 0x71, 0xd8, 0x37, 0x3c, 0x99, 0xc9, 0xfd, 0xba, 0x76, 0xc9, 0x7a, 0x65, + 0x21, 0xa3, 0x55, 0xa4, 0x7a, 0xbb, 0xa6, 0x34, 0x84, 0xc9, 0xd9, 0x0e, + 0x10, 0xde, 0xab, 0x36, 0xa0, 0x36, 0xa0, 0xcb, 0x66, 0x87, 0x4c, 0x77, + 0x0d, 0xd7, 0xd7, 0x9d, 0x26, 0x56, 0x78, 0x40, 0x4c, 0xae, 0xe1, 0x95, + 0xf9, 0x05, 0xae, 0xe3, 0x72, 0x58, 0xdd, 0x30, 0x4e, 0x8a, 0xb3, 0xb4, + 0x91, 0xfd, 0xaf, 0x42, 0xf2, 0x67, 0x81, 0x13, 0x6c, 0xfa, 0xf0, 0xd7, + 0x8b, 0x61, 0x8c, 0x33, 0x2e, 0xec, 0x98, 0xf7, 0x55, 0x0c, 0x16, 0x6d, + 0x1d, 0xf8, 0x2e, 0x95, 0xb8, 0x24, 0x3e, 0x2e, 0xff, 0x0a, 0x28, 0x05, + 0x6f, 0x29, 0xc3, 0x11, 0x9c, 0x62, 0x2b, 0x70, 0xd2, 0x60, 0x3d, 0xd7, + 0x43, 0x01, 0xac, 0x37, 0x69, 0x9e, 0xb3, 0xac, 0x6b, 0x29, 0x1a, 0x02, + 0xeb, 0xa1, 0x45, 0x11, 0xdc, 0xeb, 0x64, 0xb9, 0x59, 0x99, 0x3a, 0xe7, + 0xab, 0xd6, 0x47, 0x21, 0x60, 0x3a, 0xe9, 0x78, 0xa8, 0x9f, 0xe4, 0x4d, + 0xb0, 0x11, 0xe3, 0x7b, 0x4c, 0x58, 0x20, 0xd2, 0xe6, 0x45, 0xdb, 0x49, + 0x3c, 0x50, 0x66, 0x88, 0x9b, 0xd6, 0x11, 0xcb, 0x54, 0x17, 0xa1, 0x84, + 0xf0, 0x2d, 0xf8, 0xe3, 0xa0, 0x67, 0x27, 0xbc, 0x3e, 0x9d, 0x67, 0x54, + 0xa6, 0x04, 0xf2, 0x99, 0x54, 0x00, 0x21, 0x37, 0xb1, 0x34, 0x59, 0x3b, + 0x74, 0x37, 0xb4, 0x83, 0x0a, 0x50, 0xcc, 0x63, 0x31, 0xc0, 0x85, 0x53, + 0xa8, 0x13, 0xf8, 0x9d, 0x9a, 0x30, 0x37, 0x9d, 0xdb, 0x58, 0xa2, 0xbc, + 0xa8, 0x30, 0xa7, 0x38, 0xd6, 0xea, 0x4d, 0xb1, 0x42, 0xc5, 0xaa, 0x81, + 0x4a, 0x66, 0x71, 0x2a, 0x78, 0x01, 0xdb, 0x37, 0xe2, 0x77, 0x4f, 0x56, + 0xab, 0xf5, 0xcf, 0x94, 0x0d, 0xf7, 0x8b, 0x4a, 0x48, 0xd7, 0xb1, 0x58, + 0x3f, 0x37, 0x44, 0x09, 0x8e, 0x8f, 0x07, 0xb8, 0x77, 0xa9, 0x04, 0x02, + 0xa2, 0x63, 0x6c, 0xab, 0x04, 0x42, 0x0c, 0xbb, 0x0b, 0x17, 0xfb, 0xe3, + 0x4f, 0xab, 0xb1, 0x10, 0xcc, 0xc5, 0x6b, 0x18, 0x1b, 0xe2, 0xca, 0x84, + 0xbb, 0x07, 0xca, 0xf0, 0x1b, 0x0d, 0x52, 0x8b, 0x8f, 0x17, 0xc0, 0x2e, + 0x43, 0xc4, 0x77, 0x89, 0x91, 0xbd, 0x9a, 0xa1, 0xbd, 0xed, 0x41, 0x2a, + 0x77, 0xd4, 0x31, 0x54, 0x98, 0xdc, 0x20, 0x51, 0x45, 0xc1, 0x7f, 0x1b, + 0xd8, 0x6d, 0x10, 0x19, 0xb3, 0x87, 0x07, 0xe0, 0xb9, 0x81, 0xbd, 0x60, + 0x7b, 0x9a, 0x3a, 0x10, 0x9b, 0x3b, 0x92, 0xf3, 0x79, 0x47, 0x32, 0x8a, + 0x8c, 0xe8, 0x98, 0x57, 0xe4, 0x6e, 0x18, 0xd5, 0xbe, 0x2d, 0x2f, 0xa0, + 0xd8, 0x41, 0x0b, 0x5f, 0x07, 0x82, 0x68, 0x37, 0xdd, 0x83, 0x6a, 0x39, + 0xd1, 0x09, 0x66, 0x37, 0x97, 0xc3, 0x20, 0x3a, 0x4d, 0x53, 0xd3, 0x23, + 0x55, 0xaf, 0x2b, 0x91, 0xe2, 0xca, 0x4c, 0x3e, 0xaa, 0xfa, 0xa4, 0x4b, + 0x9c, 0x62, 0x17, 0x9d, 0xe5, 0x7b, 0xdf, 0xf0, 0x7d, 0xdf, 0xe2, 0x83, + 0x9a, 0xd9, 0xf3, 0xe8, 0xb3, 0xd5, 0x86, 0xa7, 0x7b, 0xe5, 0x8f, 0x7c, + 0x52, 0x7e, 0x4c, 0xc1, 0x74, 0xe2, 0xaa, 0x24, 0xee, 0x95, 0xb6, 0xaf, + 0xbb, 0x77, 0xd7, 0x9d, 0xab, 0xf5, 0x44, 0xfc, 0x94, 0xe8, 0x2c, 0x87, + 0x3d, 0xbb, 0x94, 0x5d, 0x74, 0xf8, 0x0e, 0x92, 0xad, 0x47, 0x63, 0x0d, + 0xde, 0x6b, 0x3b, 0x9d, 0x0a, 0x54, 0xb6, 0x22, 0x24, 0x3e, 0xab, 0xed, + 0xe7, 0x97, 0xc4, 0x4b, 0x6c, 0x1d, 0x1f, 0xec, 0x59, 0x52, 0x47, 0xc2, + 0x2d, 0x11, 0x51, 0x31, 0x4b, 0x06, 0xe9, 0x5e, 0x2f, 0x1e, 0x34, 0x5e, + 0x9b, 0xe1, 0x5d, 0xc2, 0x4d, 0xde, 0xe4, 0x81, 0xfc, 0xa6, 0x5d, 0x0e, + 0xd1, 0x48, 0x18, 0x37, 0x53, 0x74, 0x83, 0x97, 0x32, 0x0c, 0x01, 0x5b, + 0xaf, 0xf8, 0x38, 0xac, 0x6e, 0x4d, 0x97, 0x8c, 0xbe, 0xf1, 0x79, 0x2d, + 0xc6, 0x04, 0x43, 0x50, 0x34, 0x90, 0x8e, 0x25, 0x27, 0x18, 0x3d, 0xa7, + 0x9f, 0x96, 0xa4, 0x0f, 0x70, 0x0d, 0xcb, 0x81, 0xc8, 0xac, 0xa2, 0xe1, + 0x78, 0xf2, 0xbc, 0xa5, 0x11, 0xd6, 0x6e, 0x5f, 0xb6, 0x48, 0xbe, 0x3b, + 0x77, 0x3d, 0x09, 0x90, 0xb3, 0xf5, 0xa5, 0x49, 0x24, 0x0b, 0xce, 0x05, + 0xe2, 0xd2, 0xf8, 0x8a, 0x83, 0xe3, 0x10, 0xa0, 0xa8, 0x9f, 0x80, 0x6e, + 0x9a, 0x0f, 0xc7, 0xb0, 0x75, 0x9f, 0x34, 0xd2, 0xcd, 0x10, 0x4d, 0xb6, + 0xcb, 0x40, 0x7b, 0xd8, 0x9d, 0x17, 0xf5, 0x94, 0xaa, 0x26, 0xa9, 0x1f, + 0x15, 0xc2, 0x34, 0x42, 0xf8, 0xcf, 0xf0, 0x16, 0x32, 0x56, 0xdd, 0x05, + 0xb8, 0x53, 0xac, 0xfb, 0xb1, 0xd7, 0xda, 0xbf, 0x0d, 0x98, 0x71, 0x31, + 0xe7, 0x49, 0x5a, 0x04, 0xdf, 0xc4, 0x65, 0xc1, 0x09, 0x4b, 0x85, 0xe1, + 0x2d, 0x68, 0x19, 0xce, 0x9e, 0x20, 0xcf, 0x4e, 0x48, 0x9c, 0xe1, 0xf2, + 0xe9, 0xff, 0x2d, 0x89, 0x4c, 0xf4, 0x59, 0x11, 0x7d, 0x30, 0x74, 0x63, + 0xc4, 0xc2, 0x57, 0xe6, 0x90, 0x86, 0x89, 0xfd, 0x49, 0x02, 0x10, 0xae, + 0xa0, 0xc2, 0xc4, 0x5e, 0x07, 0xbc, 0x84, 0x04, 0x9a, 0x50, 0x54, 0x8b, + 0xdf, 0x6f, 0x29, 0xa2, 0x5e, 0xa8, 0x73, 0xf7, 0xe9, 0x26, 0x89, 0x72, + 0x1c, 0x58, 0x53, 0xfa, 0x3f, 0x6f, 0x9a, 0x93, 0x7e, 0xa0, 0xe9, 0xf2, + 0xc0, 0xf0, 0x7e, 0x3b, 0xb0, 0xa9, 0x97, 0xf8, 0xb7, 0x0e, 0x27, 0x17, + 0x92, 0x59, 0x7d, 0xd5, 0x6e, 0x17, 0x10, 0xc0, 0xc8, 0x3d, 0xbc, 0xbc, + 0xfc, 0xac, 0x4f, 0x36, 0x24, 0x98, 0xd1, 0x2a, 0xbe, 0x4f, 0xc6, 0xca, + 0xf6, 0xe4, 0x14, 0xeb, 0xa1, 0xa6, 0x8b, 0xb9, 0x09, 0xe1, 0x77, 0x2f, + 0x68, 0xb7, 0x65, 0x54, 0xb1, 0xef, 0x65, 0x48, 0x47, 0x26, 0xe8, 0x4f, + 0x60, 0x5d, 0x1c, 0x12, 0x74, 0x15, 0x9d, 0x41, 0x6a, 0x83, 0x8d, 0xb6, + 0xb8, 0x93, 0x31, 0x41, 0xbe, 0xf9, 0xe6, 0x78, 0x7e, 0x57, 0x63, 0x77, + 0x76, 0x4e, 0xbe, 0xbe, 0xea, 0xe8, 0x45, 0x4f, 0xc8, 0x97, 0x07, 0x87, + 0x29, 0xe6, 0x66, 0xc1, 0xc8, 0x42, 0x07, 0x2c, 0x95, 0xfa, 0xa4, 0x56, + 0x00, 0x13, 0x9c, 0x6c, 0x83, 0x94, 0x1f, 0x15, 0xff, 0x15, 0x35, 0x3a, + 0x57, 0x96, 0x13, 0x4c, 0xfb, 0x8d, 0x55, 0x41, 0x81, 0xfb, 0x94, 0x8b, + 0xec, 0xc3, 0x2f, 0xdc, 0xf0, 0x4e, 0x7c, 0x62, 0xbc, 0x3e, 0x8f, 0xd3, + 0x64, 0x08, 0xa6, 0xf4, 0xbc, 0x11, 0xd9, 0xc3, 0xcc, 0x56, 0xef, 0xba, + 0x04, 0xa7, 0xef, 0x81, 0xf5, 0xa9, 0x0a, 0x5d, 0xc0, 0xbc, 0xfa, 0x1c, + 0x96, 0xd3, 0xbd, 0x9b, 0xe0, 0x8d, 0x28, 0x12, 0x4b, 0x7d, 0x14, 0x6d, + 0xd4, 0x02, 0xb1, 0xad, 0x9d, 0x22, 0x58, 0x25, 0x81, 0x14, 0x63, 0x29, + 0x27, 0x93, 0x48, 0x35, 0x27, 0x33, 0x31, 0xc8, 0x1d, 0xd9, 0xd5, 0x28, + 0x90, 0xc5, 0x6b, 0x79, 0x06, 0x3e, 0xa8, 0xde, 0x62, 0x73, 0x67, 0xf9, + 0xa7, 0xd2, 0x22, 0xdc, 0x16, 0xb9, 0x5e, 0x0f, 0xd2, 0xb5, 0x54, 0x6b, + 0x66, 0x07, 0xa5, 0x87, 0x8d, 0xfb, 0xa1, 0x34, 0x16, 0x0e, 0x95, 0xf1, + 0x8f, 0x73, 0xcd, 0x93, 0x11, 0x94, 0xc2, 0xa3, 0x5e, 0xcb, 0xe2, 0xed, + 0x87, 0xd8, 0xf1, 0x2c, 0x3a, 0x27, 0xa5, 0x44, 0x4d, 0xb8, 0x05, 0x70, + 0xe0, 0xf5, 0x3e, 0xd4, 0x9f, 0xc8, 0x7d, 0x2f, 0x3a, 0x86, 0x00, 0xbf, + 0x79, 0xc7, 0x2d, 0x85, 0xad, 0x5e, 0xb5, 0xe4, 0xc6, 0x2c, 0x40, 0xdb, + 0x46, 0x41, 0xbc, 0x40, 0xfe, 0xef, 0x16, 0x19, 0x1a, 0x3b, 0x7d, 0x0f, + 0xe3, 0x92, 0x81, 0x2a, 0x59, 0x6c, 0xdf, 0xf5, 0xed, 0xf4, 0x4a, 0xbf, + 0xb4, 0x93, 0x71, 0xe1, 0x4a, 0x70, 0x04, 0xc6, 0xc6, 0x20, 0xd7, 0x84, + 0xcd, 0xd6, 0x8f, 0x88, 0x0b, 0xfd, 0x7b, 0xcb, 0x11, 0x06, 0xf9, 0x60, + 0x82, 0xb9, 0x34, 0xde, 0x21, 0x8f, 0xf4, 0x2e, 0xe1, 0xd6, 0x07, 0x74, + 0xe0, 0xd0, 0x27, 0x56, 0x94, 0x8e, 0xa9, 0x93, 0x85, 0xbb, 0xf9, 0x3d, + 0x0e, 0x1e, 0x3e, 0xf2, 0x1b, 0xfb, 0xf5, 0x8c, 0xd4, 0xd1, 0x5e, 0x06, + 0xe2, 0xdc, 0x83, 0x97, 0x64, 0x1c, 0x48, 0x26, 0xbe, 0x85, 0x13, 0x6a, + 0x18, 0xd5, 0xc4, 0x20, 0xbc, 0xb2, 0xb9, 0x23, 0x6d, 0x8a, 0xfc, 0x32, + 0xdd, 0x1f, 0xb0, 0x7e, 0x07, 0x40, 0xdc, 0xbf, 0x82, 0x32, 0xc5, 0x21, + 0xb8, 0xb3, 0x94, 0xe5, 0xf7, 0x3a, 0x6e, 0xdb, 0xe2, 0x76, 0xe0, 0x04, + 0x14, 0x03, 0xf0, 0x19, 0x86, 0xd8, 0xbc, 0xca, 0x25, 0x36, 0xce, 0xe5, + 0xf5, 0x16, 0x91, 0x39, 0x2b, 0x69, 0xcf, 0x1e, 0x99, 0x72, 0xd7, 0xf8, + 0x8e, 0x88, 0xad, 0x0a, 0xf5, 0x14, 0x9f, 0x6b, 0x13, 0xa4, 0x32, 0xc0, + 0x68, 0x0b, 0x90, 0x29, 0x92, 0x9c, 0xbc, 0x94, 0x7b, 0x7a, 0x13, 0x0f, + 0x7a, 0x49, 0xd2, 0x16, 0xe3, 0x8a, 0xb4, 0x2e, 0x6f, 0x6c, 0x21, 0x2d, + 0x22, 0x68, 0x1f, 0xb1, 0x15, 0xff, 0xd3, 0x69, 0x68, 0x41, 0x52, 0x89, + 0x6b, 0xe8, 0x9c, 0x28, 0xbc, 0xf2, 0xf0, 0xd2, 0x81, 0x40, 0xae, 0xb8, + 0x2b, 0xe2, 0x28, 0xf6, 0x95, 0x70, 0x1d, 0xba, 0xe2, 0xe8, 0x6e, 0x9a, + 0x2d, 0x69, 0xad, 0xfb, 0xd3, 0x4f, 0xa3, 0x8c, 0x1d, 0x46, 0x1e, 0x6b, + 0xa8, 0xb0, 0x00, 0x23, 0x93, 0xf3, 0x95, 0xe5, 0x59, 0xc1, 0x87, 0x3e, + 0x5f, 0xc0, 0x66, 0x6b, 0x00, 0x6f, 0x0b, 0xee, 0xcc, 0xf2, 0x9c, 0xcb, + 0x49, 0x73, 0x3d, 0x61, 0xbf, 0x41, 0x7a, 0xcf, 0x69, 0xd4, 0x83, 0x03, + 0xfc, 0x63, 0x89, 0x64, 0x44, 0x5f, 0x94, 0x28, 0x51, 0xcc, 0xfd, 0x17, + 0xa9, 0x7d, 0xe9, 0xee, 0x58, 0xb4, 0x15, 0x51, 0x47, 0xca, 0x23, 0xc3, + 0x0d, 0x6e, 0xd9, 0x8f, 0xaa, 0x3f, 0x98, 0x23, 0x3f, 0xfa, 0xef, 0xd8, + 0x91, 0x64, 0xfa, 0x3e, 0x45, 0x09, 0xc2, 0x4d, 0x19, 0xc9, 0xe2, 0x17, + 0x8e, 0x87, 0x8d, 0x36, 0xbc, 0xf7, 0xa2, 0xd2, 0x4e, 0xca, 0x5b, 0x8e, + 0x7d, 0xa5, 0xbb, 0xe7, 0xf9, 0x85, 0x61, 0xf3, 0x37, 0xe2, 0x51, 0x52, + 0x5f, 0x41, 0x9f, 0xb0, 0x82, 0x82, 0xc1, 0x81, 0xea, 0xfd, 0x07, 0xe4, + 0x40, 0x7e, 0x16, 0x7f, 0x3e, 0xcb, 0xa9, 0x2d, 0x34, 0x29, 0x47, 0x14, + 0x60, 0xcd, 0xe5, 0xc5, 0xc0, 0x7f, 0x3e, 0x2d, 0x50, 0xad, 0xd8, 0xcb, + 0x9c, 0xf7, 0xc3, 0xa5, 0xfb, 0x86, 0x61, 0x78, 0x9e, 0x32, 0x4c, 0xce, + 0x8e, 0x21, 0xda, 0x76, 0x2d, 0x4b, 0x80, 0xd1, 0xeb, 0x78, 0xe6, 0x26, + 0x21, 0x8b, 0xc9, 0x86, 0x89, 0xb0, 0x3d, 0x70, 0xdd, 0x33, 0x21, 0xb3, + 0x50, 0xc4, 0x93, 0x06, 0xd1, 0x1e, 0x27, 0xc4, 0x6c, 0xd2, 0xc2, 0x5b, + 0xa9, 0xea, 0x95, 0x94, 0x3c, 0xc6, 0xef, 0xb9, 0xa4, 0x79, 0xa3, 0xb5, + 0x5f, 0xda, 0x02, 0x83, 0x20, 0xa5, 0x30, 0x79, 0x62, 0x3b, 0x7b, 0xd8, + 0x9a, 0xbf, 0x94, 0xa0, 0xcd, 0xa2, 0x7e, 0xd0, 0x64, 0xf1, 0x14, 0x82, + 0x03, 0xb9, 0x93, 0xc3, 0xe2, 0x0e, 0xde, 0x8d, 0xdb, 0x99, 0x8a, 0x26, + 0x9a, 0x73, 0xb9, 0xc9, 0x8f, 0x95, 0x12, 0x49, 0x22, 0x28, 0x8e, 0x6a, + 0x10, 0xc7, 0x7a, 0x35, 0x4d, 0xc3, 0xc7, 0x03, 0x35, 0x12, 0x70, 0x02, + 0x39, 0x6f, 0xa5, 0x3d, 0xca, 0xf4, 0xbd, 0x10, 0x6e, 0x43, 0x77, 0x54, + 0xa5, 0x3e, 0x65, 0xf5, 0x27, 0x63, 0x87, 0xf1, 0xd1, 0x3b, 0xa9, 0xdc, + 0xc2, 0x87, 0x40, 0xc6, 0x7c, 0xc5, 0x3a, 0x4e, 0xfc, 0x72, 0x12, 0x8a, + 0x4a, 0xfb, 0xbe, 0xcb, 0xb3, 0x77, 0x10, 0x2e, 0xeb, 0xb3, 0x0c, 0x67, + 0xf4, 0x48, 0x67, 0xe2, 0x8f, 0x32, 0xeb, 0x04, 0xf0, 0x61, 0x3b, 0xdc, + 0x77, 0x49, 0xac, 0x3e, 0x89, 0xef, 0x6f, 0x2e, 0x9d, 0x8b, 0xdd, 0x7c, + 0x60, 0x58, 0xb1, 0x71, 0xbf, 0xb2, 0x2e, 0xb2, 0xbe, 0x76, 0xdb, 0xaa, + 0x32, 0x0d, 0xb6, 0x66, 0xc8, 0xd1, 0x70, 0x52, 0xbc, 0x2b, 0x62, 0x28, + 0xa3, 0xc8, 0x1c, 0x5e, 0x62, 0xac, 0x69, 0xc4, 0x2f, 0xc0, 0xc3, 0xfa, + 0x41, 0xc4, 0xae, 0xcd, 0x3f, 0x5e, 0xcf, 0x6f, 0x07, 0x58, 0x75, 0xd8, + 0x0a, 0x9c, 0x33, 0x4c, 0x3c, 0x0d, 0x91, 0xad, 0xe4, 0x9d, 0xc0, 0x21, + 0x70, 0x8c, 0x2f, 0xcc, 0xc3, 0x31, 0xff, 0xd7, 0x24, 0x5b, 0xc0, 0xc0, + 0x14, 0xdb, 0x32, 0x2e, 0xc8, 0x80, 0x20, 0x96, 0x9d, 0x9b, 0xf6, 0x97, + 0x08, 0xdb, 0xed, 0x97, 0x46, 0xbf, 0x1d, 0xa0, 0x3a, 0xad, 0x92, 0x48, + 0x54, 0xd6, 0x48, 0xa9, 0xef, 0xc0, 0xb2, 0x3e, 0x5c, 0x48, 0x7b, 0xea, + 0x26, 0x1b, 0xe6, 0xef, 0x99, 0xd0, 0xd3, 0xbd, 0x69, 0x23, 0xc8, 0x3e, + 0x23, 0x83, 0x44, 0xd8, 0x47, 0xfa, 0x46, 0x9f, 0xec, 0xcc, 0x8d, 0x96, + 0x6a, 0x68, 0x5f, 0xe9, 0xae, 0x81, 0xe9, 0x56, 0xf6, 0x28, 0xb7, 0xe0, + 0xa8, 0x10, 0xe1, 0xfa, 0x9e, 0x8e, 0x15, 0xaf, 0x69, 0x15, 0x75, 0xc6, + 0x4a, 0x93, 0x80, 0xb7, 0x70, 0x1e, 0x03, 0xea, 0x3e, 0x2b, 0x4c, 0x34, + 0xc2, 0x85, 0xdb, 0x98, 0x81, 0xa8, 0x3e, 0x26, 0xf1, 0xdc, 0x39, 0xdc, + 0xbb, 0x69, 0x85, 0x31, 0x78, 0xc2, 0x09, 0xab, 0x65, 0x3c, 0xa2, 0x83, + 0xa9, 0x8e, 0x44, 0xfe, 0xfb, 0xc1, 0x8b, 0x34, 0x6b, 0xb7, 0x65, 0x7b, + 0xb3, 0x20, 0x3d, 0x84, 0x11, 0xdc, 0xcd, 0x27, 0xcf, 0x82, 0xce, 0x5b, + 0xc8, 0x91, 0x45, 0x3d, 0xac, 0xa5, 0x2b, 0x69, 0xf4, 0xe1, 0xab, 0x76, + 0x4d, 0x6d, 0x79, 0x08, 0xad, 0x7f, 0xb7, 0x1c, 0x74, 0xed, 0x81, 0xc1, + 0xe2, 0xaf, 0x20, 0xad, 0x83, 0x34, 0x2e, 0xd1, 0x7c, 0x30, 0xd6, 0x3f, + 0x1f, 0x4d, 0xaf, 0x6f, 0x3b, 0x26, 0x4f, 0x28, 0x0d, 0x2f, 0x58, 0x26, + 0xa3, 0x7f, 0x45, 0x35, 0xbf, 0x48, 0xcc, 0xda, 0x7a, 0xdb, 0xe9, 0x4e, + 0x5d, 0x52, 0x90, 0x74, 0x67, 0x4b, 0x2b, 0x99, 0xb0, 0x1a, 0x27, 0x4c, + 0x93, 0x2a, 0xbe, 0x80, 0xb7, 0x8b, 0xd5, 0x19, 0x3d, 0x50, 0x47, 0x09, + 0x1d, 0x30, 0x07, 0x08, 0x86, 0x6d, 0xbb, 0x83, 0x6b, 0x2f, 0x76, 0x06, + 0x27, 0x48, 0x58, 0xf3, 0x1a, 0xc7, 0x3d, 0x96, 0xbb, 0x4c, 0xbf, 0x4d, + 0x3f, 0x7c, 0x05, 0x45, 0x34, 0x14, 0xef, 0xab, 0x14, 0xb9, 0xd9, 0x58, + 0xfe, 0x1e, 0x22, 0x7e, 0x71, 0x83, 0xe2, 0xdb, 0xc0, 0xfb, 0x76, 0x30, + 0x78, 0x4e, 0x70, 0x4c, 0x19, 0x79, 0xdb, 0xba, 0x67, 0x93, 0x5f, 0x9c, + 0x28, 0x57, 0x1e, 0xab, 0x58, 0x5e, 0x26, 0x2b, 0xee, 0xad, 0x17, 0x39, + 0xa2, 0x04, 0x35, 0xdd, 0xb0, 0x24, 0x76, 0x78, 0x06, 0xf6, 0x34, 0x82, + 0xd4, 0x1b, 0xf0, 0xf1, 0x4f, 0xa7, 0xe8, 0x80, 0x86, 0x76, 0xa5, 0x75, + 0x2b, 0x8e, 0xd6, 0x7a, 0x6e, 0x1c, 0x20, 0x53, 0x1a, 0xf0, 0x49, 0xe7, + 0x78, 0x63, 0xc4, 0x34, 0xfa, 0xe4, 0x2c, 0xae, 0x01, 0x40, 0x3c, 0xf1, + 0xb6, 0xf8, 0xc6, 0xf1, 0xaf, 0xca, 0xe0, 0xf8, 0xf0, 0x0f, 0xbc, 0xcc, + 0x60, 0x27, 0xf4, 0x93, 0x28, 0xe4, 0xa3, 0x6a, 0x56, 0xc8, 0x21, 0x64, + 0x3e, 0xd8, 0x41, 0x9b, 0x91, 0xe9, 0x45, 0x04, 0x1d, 0x79, 0x9b, 0x0b, + 0x36, 0x5c, 0xf6, 0x24, 0xd1, 0xd6, 0xea, 0xa0, 0xf7, 0xbd, 0xc7, 0xba, + 0xd5, 0xf0, 0xb2, 0xff, 0xdb, 0x30, 0x2c, 0x74, 0x85, 0xb1, 0xe0, 0x64, + 0xe6, 0xc6, 0x9e, 0x16, 0x20, 0xe8, 0x38, 0x4a, 0xed, 0xf5, 0xa2, 0xc1, + 0x90, 0xfd, 0x17, 0xf9, 0x3e, 0xff, 0x27, 0x06, 0xd6, 0xee, 0x7d, 0xa0, + 0x2f, 0xf9, 0x78, 0xea, 0x42, 0x06, 0xd6, 0xe6, 0xad, 0x47, 0xa5, 0xb4, + 0xa6, 0x72, 0xa6, 0xa4, 0x03, 0x7b, 0x32, 0xf1, 0xaa, 0xe3, 0x55, 0x5b, + 0xfc, 0xdd, 0x87, 0x86, 0x90, 0xd2, 0xcb, 0x27, 0x43, 0xdf, 0x6c, 0x96, + 0x34, 0x23, 0xf9, 0xeb, 0x13, 0x7b, 0x19, 0x88, 0xb5, 0x6d, 0x52, 0x9c, + 0xaf, 0xcb, 0xb9, 0xc0, 0xff, 0x0c, 0xc8, 0xc6, 0x0e, 0x8f, 0x0f, 0x8f, + 0x4f, 0xc4, 0xb4, 0x5b, 0xf2, 0x84, 0x25, 0xea, 0x62, 0xb9, 0x8b, 0x5e, + 0xef, 0x82, 0x76, 0x14, 0x28, 0xd8, 0xcb, 0xae, 0x85, 0x60, 0x1d, 0xf6, + 0x47, 0xd0, 0x04, 0xa6, 0xee, 0x48, 0xc9, 0xc1, 0x7f, 0x81, 0x50, 0xf0, + 0x00, 0x1d, 0x60, 0x5e, 0x77, 0x1b, 0x09, 0x3a, 0x6e, 0xac, 0xb0, 0x1e, + 0xbf, 0xcd, 0xce, 0x2a, 0xe3, 0x36, 0x41, 0xec, 0xef, 0x28, 0xa6, 0x37, + 0x84, 0x16, 0x92, 0xb6, 0xdc, 0x64, 0xfc, 0x83, 0x2c, 0xcd, 0x41, 0xed, + 0x12, 0x7c, 0x97, 0x3f, 0xc2, 0xaf, 0xc7, 0xbd, 0x16, 0xdf, 0x45, 0x2a, + 0x0d, 0x7a, 0xbe, 0xd0, 0xc7, 0x9e, 0x77, 0xc3, 0x05, 0xc6, 0x3b, 0x8b, + 0x2c, 0x9f, 0xf8, 0x24, 0xe1, 0xc0, 0x9f, 0x69, 0xa6, 0x24, 0xdd, 0x0b, + 0x40, 0xae, 0x13, 0x5a, 0x41, 0xb4, 0x53, 0xc7, 0x24, 0x49, 0x51, 0x30, + 0xd7, 0xc5, 0xac, 0x92, 0xc0, 0x88, 0x0f, 0x8c, 0x60, 0x7b, 0xe6, 0xd4, + 0x60, 0x0e, 0x87, 0x02, 0x1f, 0x5a, 0xee, 0xc6, 0x10, 0xc9, 0x3b, 0xb5, + 0xf2, 0xb7, 0x15, 0x59, 0xb6, 0x2d, 0x9c, 0xa7, 0x83, 0x46, 0x5b, 0x73, + 0x89, 0x24, 0x65, 0x6c, 0xb9, 0x39, 0x55, 0x16, 0x2e, 0xec, 0xa4, 0x8d, + 0xe0, 0xba, 0x60, 0x58, 0xac, 0xda, 0xa3, 0x28, 0x04, 0x72, 0x82, 0x0b, + 0x78, 0x02, 0x3b, 0x18, 0x52, 0x58, 0xd5, 0xa6, 0x1a, 0x85, 0xfa, 0x6e, + 0x05, 0x58, 0x1f, 0x9e, 0x13, 0x3c, 0x2a, 0xe3, 0x5d, 0x94, 0x53, 0xae, + 0x5e, 0xff, 0x1d, 0x6c, 0x36, 0xf3, 0x7f, 0x4a, 0xde, 0xf7, 0x72, 0xe2, + 0xb3, 0xd3, 0xe4, 0xe2, 0x55, 0x4c, 0xef, 0x4e, 0x76, 0xff, 0xa6, 0x44, + 0xa9, 0xa5, 0x12, 0xb5, 0x58, 0xc8, 0x42, 0xbc, 0xf6, 0xaf, 0x43, 0x7f, + 0xc9, 0x72, 0x54, 0xbe, 0xe7, 0xa9, 0xf1, 0xde, 0x78, 0xaa, 0xd7, 0x56, + 0x4a, 0xee, 0x2e, 0x26, 0xfc, 0xb9, 0xd0, 0xba, 0x47, 0x81, 0xa8, 0x28, + 0x8c, 0x54, 0xaa, 0x86, 0x02, 0xe5, 0xdb, 0xbc, 0x26, 0x37, 0x3e, 0x24, + 0x32, 0x58, 0x21, 0xe3, 0x1f, 0xc0, 0xd4, 0xb2, 0x04, 0xf8, 0xf9, 0x20, + 0xdb, 0x9a, 0xea, 0x66, 0xc9, 0x74, 0x54, 0x40, 0x65, 0xb7, 0x81, 0x55, + 0x4a, 0x63, 0x0a, 0x01, 0xf7, 0xbc, 0xfe, 0x92, 0x2a, 0xa7, 0xf3, 0x29, + 0xb5, 0xc7, 0x6e, 0x08, 0x58, 0x03, 0x59, 0x1c, 0x5c, 0x09, 0x44, 0xa7, + 0x82, 0x43, 0xc9, 0x40, 0x01, 0x6d, 0xf0, 0x40, 0xf5, 0xf1, 0x4d, 0xb2, + 0x30, 0x47, 0xbe, 0x32, 0x4d, 0x86, 0x22, 0xd5, 0xd8, 0xb5, 0xe1, 0x39, + 0xc9, 0xe2, 0x61, 0xed, 0xa6, 0xd7, 0x74, 0x62, 0x23, 0x49, 0x94, 0xe9, + 0x8e, 0xed, 0x4f, 0x04, 0xd1, 0x86, 0x44, 0xb9, 0x51, 0x7a, 0x75, 0x7f, + 0x91, 0xc5, 0xc1, 0x1e, 0x53, 0x54, 0xf2, 0xf5, 0xfc, 0x15, 0xf6, 0x9a, + 0x1b, 0x67, 0x15, 0xb8, 0x0a, 0x4e, 0x54, 0xa0, 0xc0, 0x00, 0xe1, 0xc8, + 0xb7, 0x3b, 0x14, 0xd1, 0x77, 0xe0, 0x77, 0x23, 0x1c, 0x27, 0x74, 0xb9, + 0x01, 0x2b, 0xf2, 0x99, 0x4e, 0x05, 0xa9, 0x94, 0x04, 0x6c, 0xfd, 0x27, + 0x6c, 0x24, 0x38, 0x98, 0x46, 0x2c, 0x15, 0x1b, 0x5f, 0xa6, 0x4e, 0x17, + 0x65, 0x8f, 0xb5, 0xd5, 0x79, 0x2c, 0xc3, 0xc6, 0xbd, 0xe7, 0xe4, 0x15, + 0xca, 0x7a, 0xaf, 0x07, 0xc7, 0x78, 0xb5, 0x17, 0xc9, 0xc8, 0x89, 0xc7, + 0xd7, 0x5f, 0x2b, 0x95, 0xa5, 0xf8, 0x1d, 0x86, 0xa7, 0x8c, 0x4b, 0xdb, + 0x8e, 0x3b, 0xe2, 0xe6, 0x74, 0x0e, 0x09, 0x68, 0x6a, 0x1e, 0x58, 0x8a, + 0xc9, 0x75, 0x3f, 0xc4, 0x20, 0x53, 0x37, 0xc9, 0x29, 0x91, 0x79, 0x6d, + 0xc9, 0x2b, 0xba, 0xa6, 0x55, 0xb7, 0x7a, 0x02, 0x52, 0xc1, 0x2c, 0x6b, + 0x9b, 0xad, 0x2f, 0x13, 0x38, 0xdb, 0xc2, 0x2d, 0x5a, 0x77, 0x54, 0x45, + 0xad, 0x19, 0xa4, 0x9c, 0x2a, 0xff, 0x21, 0xb6, 0xfc, 0xa5, 0x24, 0xce, + 0xe7, 0x84, 0xe0, 0x00, 0xa0, 0xf5, 0xbb, 0x3e, 0x38, 0xb5, 0xac, 0x4d, + 0xb5, 0xb0, 0xef, 0xe6, 0xb4, 0xf9, 0xa3, 0x25, 0xa5, 0x55, 0x6a, 0xcc, + 0x95, 0xa8, 0x2f, 0x97, 0x70, 0xe8, 0xee, 0x14, 0x55, 0x53, 0x43, 0xe7, + 0x5b, 0xaa, 0xc6, 0xab, 0xbf, 0x91, 0x9b, 0x24, 0x64, 0x42, 0x71, 0x1f, + 0x2f, 0xfd, 0xef, 0xed, 0xc6, 0x80, 0x6e, 0xc5, 0x97, 0x87, 0xa0, 0xc6, + 0x01, 0xca, 0x49, 0x32, 0xe5, 0x47, 0xac, 0x0a, 0x41, 0xca, 0xb5, 0x48, + 0x7f, 0x7b, 0xa5, 0xa7, 0x4d, 0x3b, 0x0c, 0x4c, 0x94, 0x31, 0x70, 0xb7, + 0x53, 0xa0, 0xf8, 0xda, 0x72, 0x14, 0x40, 0x7c, 0x9c, 0x9a, 0x7f, 0x08, + 0x27, 0x65, 0x1c, 0x5a, 0x5c, 0xb0, 0x50, 0xb7, 0x16, 0x7c, 0x85, 0x8e, + 0x09, 0x26, 0x6e, 0x45, 0xa6, 0x72, 0x30, 0x5d, 0x0c, 0x24, 0xd3, 0x3e, + 0x42, 0x2d, 0x4f, 0x4a, 0xcf, 0x4d, 0xfc, 0x30, 0xe1, 0xf0, 0xde, 0x84, + 0x92, 0xe9, 0x9e, 0x62, 0x0e, 0xe0, 0x6a, 0x87, 0x98, 0xce, 0xf6, 0xfa, + 0x7a, 0x23, 0x4f, 0x9a, 0x5d, 0xd6, 0xff, 0x51, 0x64, 0xa2, 0xc4, 0x7f, + 0x03, 0x06, 0xde, 0x32, 0x91, 0xc1, 0x0e, 0xc0, 0x71, 0x6b, 0xf6, 0xc8, + 0x97, 0x14, 0xb9, 0x4f, 0x3c, 0x5a, 0x00, 0xad, 0xd7, 0xa2, 0x3f, 0x89, + 0x9d, 0x14, 0xe0, 0xdf, 0xc1, 0x92, 0x84, 0xb0, 0xde, 0xff, 0x07, 0xb9, + 0xdc, 0x8d, 0x60, 0xca, 0x1e, 0x91, 0x03, 0x7b, 0x9c, 0x46, 0xe1, 0xe5, + 0xf1, 0xf0, 0x8e, 0x11, 0x79, 0xdb, 0x5d, 0xe8, 0x9e, 0xcf, 0xf6, 0x5d, + 0xa9, 0x94, 0x9d, 0xcb, 0xa4, 0xf0, 0x3f, 0x0b, 0xd3, 0xd5, 0xf9, 0xff, + 0x01, 0xb4, 0x9e, 0xec, 0x7f, 0xbd, 0x44, 0x20, 0x8f, 0x0f, 0x2a, 0xb1, + 0xfa, 0x8a, 0x1d, 0xb5, 0x2f, 0x2b, 0x76, 0x73, 0x8d, 0xe9, 0x60, 0x98, + 0x94, 0xe4, 0x53, 0x3b, 0xe7, 0xfe, 0x4c, 0xc2, 0xd0, 0x61, 0x8f, 0x90, + 0x4e, 0x86, 0xe6, 0xe8, 0xf8, 0x72, 0x01, 0xf5, 0x6b, 0xd8, 0xa0, 0x52, + 0x67, 0x88, 0xd3, 0xd6, 0x2e, 0x8b, 0x07, 0x7c, 0xcb, 0x2d, 0x12, 0x6a, + 0x4d, 0xc8, 0x62, 0x70, 0x4e, 0x36, 0xd9, 0xdd, 0xec, 0xe4, 0xf3, 0xf0, + 0x3c, 0x70, 0x0d, 0x3e, 0xf6, 0x3d, 0xce, 0xae, 0xb2, 0x9a, 0xef, 0x03, + 0x85, 0x28, 0xea, 0x7c, 0x4c, 0x2e, 0x7c, 0xf9, 0xa4, 0xd8, 0xc3, 0x91, + 0xef, 0xac, 0xc8, 0x33, 0x22, 0xb3, 0xaf, 0x2e, 0x9e, 0x03, 0x49, 0xfa, + 0x95, 0xda, 0x60, 0x31, 0x7a, 0x80, 0x40, 0x68, 0xc6, 0xec, 0x03, 0x66, + 0x01, 0xe9, 0x1c, 0xe6, 0xae, 0x7a, 0x05, 0x70, 0x30, 0x37, 0xab, 0x2c, + 0xa7, 0x80, 0x50, 0x34, 0x67, 0x35, 0xd2, 0x6d, 0x85, 0x58, 0xc9, 0xdc, + 0xec, 0x51, 0x17, 0x3a, 0x31, 0xcf, 0x02, 0x1a, 0xee, 0x85, 0x0d, 0x41, + 0x28, 0xf6, 0x22, 0xb2, 0x25, 0xf6, 0x29, 0xf7, 0xba, 0x8c, 0xee, 0x47, + 0xf0, 0xc9, 0xa6, 0xb2, 0xf8, 0x53, 0x33, 0x43, 0xc4, 0x21, 0xed, 0xb0, + 0x19, 0x45, 0x9d, 0x5d, 0xe3, 0x27, 0xbe, 0x8b, 0x8c, 0x10, 0x59, 0x74, + 0xf7, 0xad, 0xd0, 0xf6, 0x2c, 0x56, 0x80, 0xc5, 0xe5, 0x3a, 0xe2, 0xc2, + 0xf1, 0x79, 0x41, 0xaf, 0x04, 0x10, 0xfb, 0x35, 0x85, 0xb9, 0x9a, 0x28, + 0x30, 0x04, 0x4b, 0x06, 0xd4, 0x5d, 0x4c, 0x7b, 0x46, 0x84, 0x65, 0xff, + 0x4d, 0xe5, 0x86, 0x37, 0xe6, 0x16, 0x8c, 0x9d, 0xb1, 0x3b, 0xe6, 0x57, + 0xd7, 0x87, 0x54, 0x30, 0x21, 0xb4, 0xe4, 0x3e, 0x96, 0x58, 0xf3, 0x7b, + 0x76, 0xe9, 0x7e, 0x60, 0x7c, 0x9b, 0x2b, 0x39, 0x5a, 0x4d, 0x3d, 0x8d, + 0xe9, 0x13, 0x91, 0x5f, 0x05, 0xe1, 0x10, 0x6c, 0x0d, 0xe6, 0x7b, 0xec, + 0x9a, 0xaf, 0xaf, 0xd2, 0x01, 0xd6, 0x4c, 0x25, 0xd6, 0x6f, 0x7b, 0x34, + 0xa2, 0x8e, 0xdf, 0x22, 0x9d, 0x08, 0xcc, 0x05, 0xbf, 0x24, 0x07, 0x13, + 0x81, 0x2f, 0x93, 0x07, 0x2b, 0xf7, 0xe2, 0x8d, 0x18, 0xac, 0x31, 0xa1, + 0x55, 0x4d, 0xf2, 0x64, 0x73, 0x5c, 0x8a, 0x42, 0xf5, 0x56, 0xf3, 0x14, + 0xf3, 0x3d, 0xf0, 0x36, 0x98, 0xe1, 0x5d, 0x72, 0x69, 0xae, 0x48, 0xdb, + 0x54, 0xe5, 0xa9, 0xac, 0x9f, 0xce, 0x1d, 0xd5, 0xe4, 0x72, 0x4c, 0x01, + 0x8e, 0x7d, 0x23, 0x4b, 0xe4, 0xdb, 0x66, 0xbb, 0x7b, 0x41, 0xa3, 0x33, + 0xbb, 0x72, 0xd2, 0xc8, 0x3d, 0xad, 0x1a, 0x2c, 0x28, 0x70, 0x52, 0x4d, + 0xff, 0x67, 0x28, 0x8c, 0xa8, 0x92, 0x50, 0x91, 0x32, 0xdb, 0x6c, 0xc3, + 0x96, 0xe5, 0x67, 0xc8, 0xf2, 0x4b, 0xd8, 0x9c, 0x5c, 0x11, 0x73, 0x60, + 0xce, 0xda, 0xf9, 0x02, 0x77, 0x91, 0xb4, 0x87, 0x93, 0x39, 0x45, 0x0d, + 0x6b, 0x44, 0x2c, 0xf0, 0xd7, 0x50, 0xe9, 0xa4, 0xd8, 0x8e, 0xf6, 0xec, + 0xcc, 0x50, 0x4d, 0x2a, 0xc3, 0x46, 0x70, 0x50, 0x8d, 0x1b, 0x29, 0x14, + 0xef, 0x2c, 0x0b, 0xae, 0xa7, 0x4a, 0xd5, 0x5b, 0x54, 0xdc, 0xb6, 0xe8, + 0xb6, 0x05, 0x8d, 0x0e, 0x06, 0x9f, 0x77, 0x93, 0xe4, 0xe2, 0xf9, 0x00, + 0x71, 0x62, 0xc8, 0x03, 0x8c, 0x41, 0x84, 0xd0, 0x48, 0xfb, 0xe1, 0x09, + 0x2c, 0x9f, 0x95, 0x4a, 0x3f, 0x58, 0x31, 0xfe, 0x1c, 0x8d, 0x60, 0x79, + 0xef, 0xa9, 0xca, 0x46, 0x92, 0xeb, 0x5d, 0xf4, 0x18, 0xda, 0x6a, 0x96, + 0xdb, 0x22, 0x66, 0x8f, 0xb1, 0xde, 0xec, 0xdc, 0x49, 0xc2, 0x84, 0x43, + 0xcc, 0xfc, 0x83, 0x45, 0xef, 0xe3, 0x99, 0xd2, 0x7b, 0x74, 0x94, 0xf7, + 0x34, 0x41, 0x25, 0xe5, 0x63, 0x9a, 0xff, 0x66, 0xa6, 0x3c, 0x98, 0x3d, + 0x83, 0x5c, 0x63, 0xf2, 0x3a, 0xf6, 0xd2, 0x8d, 0x5c, 0x6e, 0x22, 0xaf, + 0xec, 0xd0, 0x09, 0xdc, 0x6f, 0x05, 0xd0, 0x7c, 0xad, 0xcc, 0x32, 0x7a, + 0x08, 0x6b, 0x47, 0x9f, 0x94, 0x91, 0xf1, 0xfa, 0x72, 0xa5, 0x8e, 0x65, + 0x79, 0x4f, 0xe7, 0x7e, 0xa5, 0xd9, 0xba, 0x4d, 0x25, 0x8f, 0x7c, 0x0b, + 0xc7, 0xf9, 0x7b, 0x55, 0x3b, 0xe3, 0x81, 0x39, 0x8b, 0xd3, 0xf9, 0x8c, + 0x4c, 0xe4, 0x7c, 0xc3, 0x73, 0x98, 0x95, 0xea, 0x1b, 0x23, 0xb8, 0x87, + 0x67, 0xec, 0x67, 0x0d, 0x27, 0x2d, 0xac, 0x54, 0x53, 0x18, 0xd6, 0xb2, + 0x03, 0x97, 0xdb, 0x27, 0x09, 0x07, 0xda, 0x85, 0xe8, 0x87, 0xdd, 0x1a, + 0xf8, 0x9f, 0x9e, 0xec, 0xf9, 0xfd, 0x0b, 0x28, 0x50, 0xc1, 0x06, 0xc6, + 0xe0, 0x96, 0x46, 0xa4, 0x7d, 0x0d, 0x23, 0x11, 0x8b, 0x67, 0xb6, 0xc4, + 0xe2, 0x4c, 0xf4, 0xe8, 0x0e, 0x0a, 0x2a, 0x25, 0x5d, 0x16, 0xa1, 0x37, + 0xa2, 0x8d, 0x15, 0x33, 0x9a, 0x3b, 0xf4, 0x4b, 0xd0, 0xcd, 0x94, 0x17, + 0x1c, 0x80, 0x36, 0xc4, 0xe8, 0xab, 0x38, 0xdb, 0x09, 0x67, 0xcf, 0xdb, + 0xa3, 0x3e, 0xbd, 0xa4, 0x20, 0x27, 0x9b, 0x1b, 0xf8, 0x8d, 0x5f, 0xa5, + 0x82, 0x51, 0x36, 0x50, 0x24, 0x5a, 0x49, 0x59, 0x00, 0xa2, 0x66, 0x97, + 0x07, 0xd2, 0x59, 0x4c, 0x7d, 0x5e, 0xfc, 0xd1, 0xc0, 0x9d, 0xa6, 0x4c, + 0xa2, 0xd4, 0xd2, 0xfa, 0xda, 0xd0, 0xba, 0x9b, 0x17, 0x95, 0x7f, 0x5f, + 0xe3, 0x4c, 0xe6, 0x4a, 0x55, 0xe1, 0x3b, 0x6e, 0xaf, 0x8b, 0x05, 0x99, + 0x8d, 0x5e, 0x3a, 0x3d, 0x64, 0x9d, 0x2f, 0x4e, 0x55, 0xe0, 0x58, 0xab, + 0x46, 0xdf, 0x1d, 0xdf, 0x6b, 0xf7, 0xbf, 0xad, 0x01, 0xc5, 0xe0, 0xe7, + 0x2d, 0x3d, 0x89, 0xe5, 0x58, 0x21, 0xd4, 0xd5, 0xb5, 0xce, 0x52, 0x7f, + 0x75, 0x22, 0xd9, 0x9d, 0xbb, 0xe7, 0x31, 0xbd, 0x3d, 0x20, 0x5c, 0xad, + 0x0b, 0x5e, 0x65, 0xeb, 0x23, 0xe7, 0x8d, 0x54, 0x9d, 0x3f, 0x43, 0x51, + 0x49, 0x00, 0xf1, 0xee, 0xbc, 0x2f, 0x9f, 0x2f, 0xa8, 0x45, 0x1f, 0x57, + 0xf5, 0xf4, 0x2f, 0xcc, 0x98, 0xa2, 0x24, 0x57, 0x81, 0xe1, 0x81, 0x93, + 0x44, 0x8d, 0x26, 0xcd, 0xec, 0x78, 0x54, 0xad, 0x12, 0xd7, 0xd0, 0x5b, + 0x4f, 0xbe, 0xda, 0x0e, 0x06, 0x1d, 0x13, 0xc9, 0xab, 0x1f, 0x8b, 0x5b, + 0xcb, 0x13, 0x7e, 0x09, 0x6b, 0x47, 0x85, 0xfe, 0xab, 0x08, 0x77, 0xae, + 0x1f, 0x80, 0x27, 0xb5, 0xec, 0x89, 0xc8, 0xd4, 0x44, 0x08, 0xba, 0xb0, + 0xdb, 0x52, 0xb4, 0x8f, 0x05, 0x13, 0xc6, 0x1e, 0xa1, 0x49, 0x47, 0xd8, + 0xe8, 0x73, 0xca, 0x74, 0x59, 0xe2, 0x41, 0xe1, 0x14, 0x16, 0x7b, 0x47, + 0x67, 0x6a, 0xf8, 0x8e, 0x1d, 0xe8, 0x38, 0xd4, 0xee, 0xa9, 0x7e, 0x3a, + 0x3b, 0xe7, 0xea, 0x15, 0x52, 0x25, 0x54, 0x78, 0x85, 0x7e, 0x50, 0xfc, + 0x30, 0x01, 0x34, 0x4a, 0x97, 0xe8, 0x0a, 0xa4, 0x00, 0xa4, 0x3e, 0x68, + 0x70, 0x1f, 0x1c, 0x9f, 0x73, 0x7c, 0xbe, 0xe1, 0x7e, 0x23, 0x2d, 0x7d, + 0x26, 0x9b, 0x92, 0x5e, 0x92, 0x38, 0x57, 0x85, 0xcf, 0x9d, 0x18, 0x00, + 0x05, 0xe7, 0xfd, 0x38, 0x4f, 0x87, 0x03, 0x1e, 0xbe, 0xb4, 0xb5, 0x3f, + 0x4e, 0xe4, 0x70, 0xe0, 0x7f, 0x6c, 0xfc, 0xad, 0x8b, 0x0c, 0x22, 0xae, + 0xfd, 0xad, 0x7e, 0x2b, 0x75, 0xc5, 0x01, 0x68, 0x64, 0x96, 0x1f, 0xa0, + 0xff, 0xdc, 0x72, 0x42, 0x21, 0x60, 0xd1, 0xe1, 0x0a, 0x17, 0x5f, 0xe2, + 0x7e, 0xe8, 0xe5, 0x04, 0xa5, 0x66, 0x53, 0x66, 0xfb, 0xd1, 0x95, 0x0c, + 0xb8, 0xff, 0xf3, 0x92, 0x9a, 0x23, 0xe2, 0xc8, 0xa4, 0x9b, 0xe2, 0xc6, + 0x40, 0xfd, 0xf3, 0x3c, 0x2d, 0x0d, 0x27, 0xf4, 0xfa, 0xdc, 0xa2, 0x56, + 0x3f, 0x50, 0x78, 0x3e, 0x62, 0x80, 0xa9, 0x0c, 0x4f, 0x34, 0xe6, 0xd6, + 0x5d, 0xc2, 0xb0, 0x81, 0xc5, 0x67, 0xf6, 0x27, 0x9d, 0x7c, 0x99, 0x35, + 0xbe, 0x9a, 0x6c, 0x05, 0x28, 0x63, 0xac, 0xc0, 0x01, 0x5e, 0x7b, 0xa0, + 0xe6, 0x7f, 0x33, 0xb3, 0xe3, 0xf7, 0x11, 0xb0, 0x7d, 0x03, 0xb5, 0xcb, + 0xf7, 0x20, 0xed, 0xee, 0x1d, 0x04, 0x6b, 0x05, 0xa3, 0x86, 0x71, 0x84, + 0x4f, 0xe7, 0x84, 0x6d, 0xbe, 0xed, 0xda, 0x35, 0xd1, 0x44, 0x05, 0x33, + 0x5b, 0xa7, 0xef, 0xa0, 0xc1, 0x69, 0x80, 0xe2, 0xe1, 0x52, 0xf4, 0xb1, + 0x14, 0x25, 0x1b, 0x90, 0x96, 0xa0, 0xef, 0x2d, 0xdb, 0x21, 0x8f, 0x36, + 0x75, 0x7c, 0xe1, 0xa6, 0x43, 0x46, 0x0c, 0x7c, 0xd0, 0xfa, 0xcd, 0xea, + 0x49, 0x21, 0x34, 0xa4, 0xbb, 0xcc, 0x39, 0x26, 0x64, 0xf9, 0xa3, 0x68, + 0xed, 0xcf, 0x6f, 0xc0, 0x8b, 0xe0, 0x26, 0x4c, 0x8c, 0x70, 0x69, 0xde, + 0x97, 0x1a, 0x23, 0xbc, 0xc7, 0xba, 0xc8, 0xb3, 0xfd, 0x97, 0x01, 0x1b, + 0x7c, 0x1e, 0x79, 0xd3, 0xf1, 0xbb, 0xd8, 0xab, 0xd8, 0xd7, 0x93, 0xcf, + 0x0f, 0xa4, 0x8c, 0x37, 0x2e, 0xb1, 0x8b, 0xdc, 0x26, 0xd4, 0x8e, 0xd7, + 0xb9, 0x00, 0xc0, 0x71, 0xfc, 0xef, 0x80, 0xf4, 0xaf, 0xda, 0xf9, 0xc7, + 0xda, 0x84, 0xf1, 0xcb, 0x77, 0xd5, 0x9e, 0x9f, 0xf7, 0x6d, 0x82, 0x10, + 0x32, 0x2d, 0xc7, 0xa0, 0x70, 0xe3, 0xec, 0xc7, 0x7d, 0x0c, 0x8d, 0xf5, + 0x01, 0x03, 0xbe, 0x14, 0x3b, 0x8e, 0x0b, 0xa5, 0x1c, 0x6b, 0x7b, 0x6d, + 0xad, 0x2c, 0x84, 0x7e, 0xdb, 0x7d, 0x9a, 0x93, 0x89, 0x9f, 0x89, 0xec, + 0x7b, 0xb4, 0xcf, 0xe2, 0x74, 0x17, 0xa0, 0xc6, 0x71, 0x20, 0x28, 0x85, + 0xb3, 0x16, 0xf1, 0x9d, 0x9b, 0x2a, 0xc0, 0xb6, 0x52, 0x7c, 0x54, 0x22, + 0x56, 0x46, 0x16, 0x99, 0x54, 0xea, 0xa1, 0xfa, 0xc8, 0x4e, 0x83, 0x6c, + 0xb8, 0xfd, 0x8c, 0x37, 0x90, 0x3c, 0x29, 0x0b, 0x02, 0x69, 0x34, 0xec, + 0xca, 0xb3, 0x2e, 0x7d, 0x41, 0x08, 0x1b, 0x0b, 0x7e, 0x93, 0x99, 0xa7, + 0xff, 0x2c, 0xe9, 0x60, 0xc5, 0xc3, 0xea, 0xe3, 0xc8, 0xde, 0x08, 0x51, + 0x50, 0x3d, 0xbf, 0x59, 0xb5, 0xa3, 0x10, 0xd3, 0x8a, 0x23, 0x6f, 0xb4, + 0xfd, 0x1a, 0x24, 0x49, 0x72, 0x97, 0xa4, 0x9c, 0xd4, 0x8f, 0xcd, 0x38, + 0x22, 0x65, 0x9a, 0xfc, 0x9b, 0xff, 0xcf, 0xef, 0xdb, 0x21, 0x2a, 0x98, + 0x3f, 0xdd, 0x5f, 0x36, 0xe9, 0x3f, 0xca, 0x5d, 0x8d, 0x23, 0x0c, 0x7f, + 0xc4, 0xa5, 0xbd, 0xdf, 0xd1, 0x8c, 0xf7, 0xce, 0x79, 0x45, 0xac, 0x99, + 0x16, 0xba, 0xd8, 0xd4, 0xc8, 0x6f, 0x64, 0x7e, 0x82, 0xa8, 0x76, 0xc9, + 0x9d, 0x07, 0x22, 0xc0, 0xf9, 0x0d, 0x34, 0x1c, 0x12, 0xf5, 0x20, 0x8d, + 0xaf, 0xbb, 0x68, 0xb7, 0x7f, 0x04, 0xa9, 0xf0, 0x27, 0xf2, 0x21, 0xa6, + 0x6c, 0xe0, 0x9f, 0xfa, 0xf8, 0xde, 0x1f, 0x1d, 0xac, 0xa7, 0x8a, 0x5f, + 0x93, 0xf5, 0xbe, 0xdc, 0x0f, 0xd0, 0xdd, 0x64, 0x85, 0x37, 0x11, 0xc1, + 0xf1, 0xb5, 0x9e, 0xc8, 0x2a, 0x9f, 0x09, 0xce, 0xe6, 0xb3, 0x87, 0x1e, + 0xc5, 0xc5, 0x94, 0xe3, 0xf7, 0x49, 0xe9, 0x60, 0x5c, 0xcf, 0x96, 0x0c, + 0x21, 0xb9, 0xac, 0xf5, 0xad, 0x40, 0xf1, 0x18, 0x2c, 0xe2, 0xcb, 0xb2, + 0x47, 0x08, 0xfb, 0x08, 0x7d, 0x1e, 0xf3, 0x45, 0x57, 0x96, 0x22, 0x7a, + 0x1f, 0x05, 0x82, 0xa8, 0xd0, 0x2f, 0x7f, 0xd1, 0x41, 0x39, 0x72, 0xb4, + 0x8b, 0xf6, 0xed, 0x44, 0xaa, 0x9b, 0xd0, 0x2e, 0x43, 0x5e, 0x02, 0x23, + 0x86, 0xed, 0x53, 0xda, 0xa4, 0xf9, 0xb1, 0xf7, 0x6b, 0x75, 0xc8, 0x99, + 0x4b, 0x16, 0x8b, 0x67, 0x8b, 0x9c, 0x0e, 0x09, 0x43, 0xa5, 0x03, 0xa5, + 0x1b, 0x82, 0x15, 0x6c, 0xb5, 0x1c, 0x1b, 0xd2, 0xb9, 0x43, 0x12, 0xad, + 0x7d, 0x75, 0x10, 0x45, 0xc3, 0x57, 0xf8, 0x96, 0x4e, 0xda, 0xca, 0x40, + 0xea, 0xcb, 0x7f, 0x04, 0xf3, 0x33, 0xdb, 0xe6, 0x3c, 0x4a, 0x05, 0xaa, + 0xf4, 0x42, 0xc8, 0x16, 0x15, 0xa4, 0x06, 0x64, 0xbf, 0xd4, 0x15, 0x3e, + 0x50, 0x15, 0x38, 0xcb, 0xc2, 0x6e, 0x16, 0xf8, 0x55, 0x6f, 0xbb, 0xe8, + 0x01, 0xd1, 0x82, 0xec, 0x4a, 0x44, 0xc2, 0xdb, 0x99, 0x25, 0xaa, 0x73, + 0xcb, 0x54, 0x30, 0x31, 0xc1, 0x02, 0xd7, 0xed, 0x4c, 0x37, 0x9f, 0x56, + 0x54, 0xa3, 0xbe, 0xd1, 0x39, 0x4a, 0x53, 0x34, 0x64, 0x3a, 0x14, 0xfe, + 0xe5, 0x15, 0x45, 0xe0, 0x5c, 0xa5, 0x3a, 0x92, 0x80, 0xb2, 0xfe, 0xe4, + 0x6c, 0x77, 0x0f, 0xae, 0xa0, 0xb3, 0x49, 0xeb, 0x1c, 0x11, 0xbb, 0x81, + 0x85, 0x6a, 0xe0, 0x81, 0x25, 0x8a, 0xd8, 0xdf, 0x6b, 0xf5, 0x46, 0x38, + 0xaf, 0x5b, 0x2b, 0xe2, 0x16, 0x20, 0x45, 0x7a, 0x55, 0x2c, 0x5d, 0xda, + 0xd9, 0xdf, 0x80, 0xe2, 0x6c, 0x66, 0x03, 0x66, 0x05, 0xe5, 0x87, 0x0a, + 0xf0, 0x10, 0x02, 0xe4, 0x4b, 0xd9, 0xde, 0x0d, 0x8f, 0xf0, 0x17, 0xa4, + 0x67, 0xa4, 0xe6, 0x2a, 0x14, 0x49, 0xbe, 0xb1, 0x3f, 0xa0, 0xe6, 0x7b, + 0x98, 0xe2, 0x4d, 0xe0, 0x49, 0x46, 0xa6, 0xdf, 0x03, 0x01, 0x3b, 0x56, + 0xf1, 0x49, 0xea, 0x70, 0x19, 0x68, 0x86, 0x15, 0xd5, 0xbc, 0xc1, 0x79, + 0xec, 0x90, 0x7c, 0x33, 0x73, 0x0a, 0x09, 0x02, 0x3c, 0xf4, 0x16, 0x7c, + 0x0c, 0x41, 0x69, 0xd4, 0x70, 0xdf, 0x73, 0x0a, 0xc5, 0x1e, 0x07, 0x4c, + 0x44, 0xb3, 0x6c, 0xfe, 0x5b, 0x60, 0x93, 0xb9, 0x11, 0x7c, 0x9c, 0x4b, + 0x29, 0xb2, 0xdb, 0x4c, 0x45, 0x6e, 0x21, 0x76, 0xd2, 0x5b, 0x8c, 0x1b, + 0xbd, 0x20, 0xb7, 0xe1, 0x83, 0x4c, 0x9f, 0x34, 0x57, 0x69, 0x8b, 0x84, + 0x54, 0x53, 0xab, 0x4a, 0xc5, 0xed, 0x94, 0x3d, 0x2c, 0xb2, 0x91, 0x0b, + 0x29, 0x64, 0xb5, 0xa2, 0x8d, 0x48, 0x9e, 0x8a, 0x2a, 0xed, 0x53, 0x4a, + 0x33, 0xa5, 0x8b, 0xaf, 0xb9, 0xd7, 0xae, 0x1b, 0x0e, 0xe7, 0xda, 0x36, + 0x3a, 0x68, 0x00, 0xe2, 0xec, 0xd8, 0xa2, 0xd3, 0x7d, 0x94, 0x72, 0x58, + 0x7c, 0xc4, 0x08, 0xfc, 0x48, 0x45, 0x54, 0x39, 0xb3, 0x1f, 0xa0, 0xe3, + 0x65, 0xd9, 0x0f, 0xa8, 0x33, 0x7a, 0x80, 0xcf, 0x16, 0x0f, 0x67, 0xfd, + 0x67, 0xd2, 0x59, 0x02, 0x22, 0x3d, 0x0c, 0x06, 0x38, 0x55, 0xf3, 0x54, + 0x89, 0x56, 0xa8, 0xf8, 0x62, 0x0b, 0x58, 0x90, 0xd2, 0xaf, 0xdf, 0xdb, + 0xc1, 0xff, 0x00, 0x8f, 0x77, 0xff, 0xe1, 0xb2, 0xca, 0xd4, 0xe1, 0x50, + 0x45, 0xbc, 0xd4, 0x83, 0x36, 0x44, 0x45, 0xd0, 0xcc, 0xee, 0x42, 0xc3, + 0x3d, 0x56, 0x35, 0xcf, 0x00, 0x81, 0x97, 0xd9, 0x9b, 0x14, 0x85, 0xc2, + 0xad, 0x63, 0xd1, 0xbd, 0xba, 0x54, 0x80, 0x66, 0x75, 0x96, 0x23, 0xdd, + 0xea, 0x3a, 0x66, 0x5a, 0xd7, 0x89, 0xb5, 0x6f, 0xbd, 0xf0, 0xf8, 0x83, + 0x09, 0xc9, 0xb8, 0x77, 0x87, 0x6a, 0x62, 0x40, 0x09, 0xfc, 0x42, 0xd1, + 0x32, 0x75, 0x4b, 0xa6, 0x5d, 0x90, 0x88, 0x9c, 0xe2, 0x9e, 0x64, 0x9e, + 0x3c, 0x0e, 0x64, 0xe2, 0x6d, 0xd2, 0xdb, 0xa3, 0x87, 0x30, 0x1f, 0x42, + 0x87, 0x8d, 0xf2, 0xc6, 0x70, 0x6c, 0xa2, 0x43, 0xec, 0x95, 0x27, 0x9a, + 0xa3, 0xfc, 0xd3, 0x09, 0x1c, 0x61, 0x18, 0xce, 0xd3, 0x6f, 0x1e, 0x90, + 0x9a, 0x58, 0x0b, 0xea, 0x0b, 0x79, 0x5e, 0x17, 0x40, 0x65, 0xef, 0x6e, + 0xc1, 0xff, 0x88, 0x14, 0x0c, 0xe3, 0x8e, 0x36, 0x55, 0xd0, 0xee, 0x2a, + 0x28, 0x96, 0x2a, 0x45, 0xa1, 0x33, 0xd6, 0xfa, 0xfe, 0x9f, 0x3a, 0x85, + 0x0f, 0x12, 0xdb, 0xbb, 0xe7, 0x02, 0x6a, 0xb3, 0xf2, 0xad, 0x30, 0xc6, + 0x1e, 0x32, 0xc3, 0x3a, 0x30, 0x8c, 0x89, 0x11, 0x4d, 0x89, 0x56, 0x38, + 0xb6, 0x19, 0x15, 0xc1, 0x4e, 0x4f, 0x2b, 0x1d, 0xc8, 0x69, 0xb3, 0x51, + 0x9b, 0xf6, 0x68, 0x1e, 0x7a, 0xc4, 0x27, 0x75, 0x01, 0x3b, 0xed, 0x14, + 0x46, 0xe6, 0x1a, 0x95, 0x66, 0x17, 0x24, 0xea, 0xfa, 0x4b, 0xde, 0x2a, + 0xd5, 0xaf, 0x90, 0xf6, 0x28, 0x96, 0x03, 0x45, 0x63, 0x73, 0xe8, 0x78, + 0x91, 0x27, 0xbe, 0xe5, 0xda, 0x4b, 0x4f, 0x2a, 0xa5, 0xed, 0x43, 0xed, + 0x44, 0x9d, 0x65, 0x34, 0x8e, 0x5f, 0x50, 0xb8, 0x75, 0x4c, 0xb7, 0x14, + 0x59, 0xe8, 0x4a, 0xcf, 0xfa, 0x00, 0x4b, 0x12, 0x82, 0x9b, 0x94, 0x95, + 0xa7, 0x65, 0xcd, 0x66, 0x49, 0x69, 0xdf, 0x29, 0xf4, 0x50, 0xfc, 0xe3, + 0x06, 0x4f, 0xc7, 0xbf, 0x1a, 0x44, 0xd2, 0x37, 0xad, 0x3d, 0x06, 0xae, + 0x07, 0xa2, 0x87, 0xc1, 0xc3, 0x55, 0x6f, 0xcf, 0x2c, 0xe6, 0xc4, 0xb5, + 0xa3, 0xa7, 0x1a, 0x68, 0x4d, 0xdb, 0xe7, 0x13, 0x11, 0xf8, 0x7a, 0xbf, + 0x20, 0xb8, 0x8f, 0x33, 0x88, 0x41, 0x54, 0x03, 0x1f, 0x9f, 0xa8, 0x69, + 0xef, 0x4c, 0x34, 0xb2, 0xd9, 0xe6, 0x4e, 0x95, 0x37, 0x87, 0xae, 0x84, + 0x08, 0xce, 0x54, 0x75, 0x9c, 0xb5, 0x03, 0x3c, 0x58, 0x6f, 0x6c, 0x23, + 0x07, 0xc5, 0x99, 0x59, 0xd2, 0x11, 0x2c, 0x51, 0x51, 0x54, 0xaa, 0x7a, + 0xdf, 0xaa, 0x1a, 0xcf, 0x61, 0xd1, 0xfc, 0x30, 0x61, 0xe2, 0x47, 0x6e, + 0xfd, 0x21, 0xe8, 0x1b, 0x0c, 0x65, 0x4c, 0xcc, 0x71, 0x0e, 0x06, 0x82, + 0x07, 0x17, 0x75, 0x55, 0xbe, 0xdb, 0xd8, 0x96, 0x75, 0x94, 0xe2, 0xb0, + 0xd6, 0x2c, 0xa3, 0xc1, 0x64, 0x10, 0x59, 0xd4, 0x88, 0x10, 0x6f, 0xcb, + 0x7b, 0xaa, 0xa6, 0x5b, 0x84, 0xca, 0x99, 0x35, 0x80, 0x50, 0xc4, 0x4c, + 0x2c, 0xc2, 0x71, 0x19, 0x1d, 0x4c, 0x22, 0xbd, 0xc3, 0x20, 0x4d, 0x61, + 0xcb, 0x39, 0xdc, 0xeb, 0x6a, 0x27, 0x25, 0x78, 0xdd, 0x80, 0xfc, 0x65, + 0x80, 0x4c, 0xde, 0x93, 0x79, 0x8b, 0x7e, 0xad, 0xb7, 0x4f, 0xb7, 0xf7, + 0xda, 0x9c, 0xfc, 0xee, 0xcc, 0x04, 0xd9, 0x97, 0xe9, 0x0d, 0x23, 0x6f, + 0xb5, 0x91, 0xdc, 0xd0, 0x39, 0x41, 0xf5, 0x84, 0x5c, 0xbb, 0xe7, 0xc4, + 0xb5, 0x26, 0xac, 0xd6, 0x83, 0x3a, 0xab, 0x16, 0x44, 0x09, 0x93, 0x7a, + 0xcd, 0xb3, 0x2c, 0xe1, 0x19, 0x45, 0xd2, 0x97, 0x9f, 0x4d, 0x5e, 0x71, + 0x00, 0x76, 0x6c, 0x6e, 0x39, 0x54, 0x32, 0x8a, 0xc0, 0xee, 0x79, 0x17, + 0x6a, 0xa7, 0x20, 0x22, 0xdc, 0xf6, 0x03, 0xfe, 0x28, 0x79, 0x03, 0x21, + 0x72, 0x05, 0xa9, 0x56, 0x95, 0xdc, 0x65, 0xd8, 0x3d, 0x6d, 0xab, 0xc1, + 0x76, 0x80, 0xb6, 0xe3, 0x3a, 0x01, 0x5c, 0x4e, 0x02, 0xa7, 0x4d, 0xc4, + 0xa7, 0xea, 0x64, 0xfc, 0x11, 0x6a, 0xaa, 0x10, 0x36, 0x28, 0x1f, 0x57, + 0x55, 0x91, 0x7f, 0xc5, 0xc1, 0xe8, 0x72, 0x70, 0x91, 0x94, 0x23, 0x91, + 0x87, 0x19, 0xf0, 0x31, 0xab, 0x4b, 0x20, 0x38, 0xf9, 0x08, 0x3d, 0x0e, + 0xa4, 0x27, 0xca, 0x92, 0xa8, 0x7c, 0x57, 0x27, 0x38, 0x38, 0x98, 0xd4, + 0xb2, 0x95, 0x73, 0x26, 0x5c, 0x8f, 0x09, 0x03, 0x2e, 0xb2, 0xab, 0x89, + 0x5b, 0xe8, 0x8b, 0x48, 0x2b, 0x54, 0x4d, 0xe7, 0x55, 0xc9, 0xff, 0x34, + 0x6d, 0xca, 0x24, 0x71, 0xa2, 0x28, 0x11, 0x92, 0xdb, 0xdd, 0xa4, 0xf6, + 0x57, 0x31, 0x01, 0x8f, 0x73, 0x0d, 0x0f, 0x5a, 0x3e, 0xa1, 0x0e, 0xcd, + 0x73, 0x86, 0x3e, 0x80, 0xef, 0x6e, 0x0a, 0x11, 0xa2, 0x77, 0x36, 0xbf, + 0x00, 0x72, 0x46, 0x8d, 0x0e, 0xf9, 0x7f, 0xa6, 0x4d, 0x4e, 0x6d, 0x0d, + 0x3e, 0x3b, 0x42, 0xe1, 0x31, 0xb3, 0x6e, 0x3d, 0xe8, 0x43, 0x23, 0xb5, + 0x87, 0x89, 0x4a, 0xc1, 0xad, 0x7f, 0xde, 0x0e, 0x3c, 0x91, 0x3f, 0x30, + 0xe3, 0xe9, 0xd8, 0x35, 0x76, 0x86, 0xcc, 0x0d, 0xae, 0x7f, 0x49, 0x45, + 0x80, 0x2f, 0xa5, 0x30, 0x44, 0x5b, 0xc1, 0xfd, 0x8c, 0xde, 0xf7, 0x84, + 0xc7, 0x29, 0x84, 0xda, 0xc6, 0x91, 0x11, 0x2c, 0xf9, 0x2e, 0xa2, 0x83, + 0x57, 0x6b, 0x80, 0x52, 0x55, 0x9f, 0x6f, 0x1c, 0xee, 0xc9, 0xad, 0x83, + 0x52, 0xe2, 0xc8, 0x6a, 0x14, 0x89, 0x07, 0xa0, 0xf2, 0x2b, 0x89, 0x77, + 0x6d, 0x3c, 0x2e, 0xca, 0xc7, 0x55, 0x08, 0x68, 0x87, 0xee, 0xa8, 0x14, + 0xe2, 0x11, 0xa8, 0x8f, 0xf1, 0x27, 0x5c, 0xe3, 0x2e, 0x92, 0x0b, 0x6a, + 0xc5, 0xc8, 0xec, 0xde, 0xcc, 0x8c, 0x6e, 0x9a, 0x5b, 0xbd, 0x19, 0x55, + 0x8b, 0x6c, 0x90, 0x9b, 0x9f, 0x1e, 0xa1, 0xd5, 0xef, 0xda, 0xef, 0x9c, + 0x88, 0x48, 0xbc, 0x3e, 0x9b, 0xbe, 0x1b, 0xd4, 0x36, 0x82, 0x1a, 0xff, + 0x6d, 0x9e, 0x54, 0xfa, 0x4f, 0xd1, 0x1d, 0x43, 0x5a, 0xb2, 0x8b, 0x2b, + 0x9c, 0x11, 0x0b, 0x31, 0x8e, 0x1b, 0x01, 0xce, 0xa5, 0x5a, 0xdc, 0x8c, + 0x44, 0x9c, 0xee, 0xba, 0xde, 0x9b, 0x28, 0xd1, 0x1c, 0x62, 0xa3, 0x09, + 0x8c, 0x35, 0x89, 0x53, 0x02, 0x9d, 0x74, 0xd2, 0x81, 0xf7, 0xc2, 0x87, + 0xe2, 0x7f, 0xe3, 0x34, 0xd3, 0x62, 0x2d, 0x69, 0x19, 0x29, 0xf8, 0x55, + 0x66, 0xaa, 0x36, 0xe0, 0x95, 0xae, 0x4d, 0x9c, 0xe9, 0x48, 0xf9, 0xb5, + 0xe1, 0xe2, 0x1b, 0x1d, 0x3c, 0x3b, 0xc5, 0x76, 0x8f, 0x4b, 0x9a, 0xf3, + 0xb4, 0xf1, 0xd1, 0x23, 0x65, 0xc6, 0xac, 0x90, 0xdc, 0xff, 0x10, 0x6d, + 0xf3, 0xc3, 0x60, 0xc4, 0xda, 0xe0, 0xa6, 0x6c, 0x6e, 0x8f, 0x0f, 0xbf, + 0x4f, 0x2f, 0x3e, 0xff, 0x78, 0x49, 0x4c, 0x42, 0x2c, 0x8c, 0x5b, 0x50, + 0x05, 0x85, 0xe9, 0xbd, 0x21, 0x24, 0x2f, 0x82, 0x7d, 0x1b, 0x3a, 0x1e, + 0xf6, 0xad, 0x73, 0x5d, 0x97, 0xc2, 0x0d, 0x81, 0xa9, 0x6c, 0xa4, 0x84, + 0xae, 0xec, 0xe9, 0x92, 0x82, 0x1b, 0x0a, 0x29, 0x5b, 0xea, 0xf9, 0xb9, + 0xe0, 0xf3, 0x1f, 0x56, 0xbb, 0xce, 0xa8, 0x4e, 0xa1, 0x8a, 0x9d, 0x80, + 0xe7, 0xd8, 0x86, 0xf8, 0xd4, 0x71, 0x06, 0xb9, 0xf1, 0xb3, 0x43, 0xc4, + 0xba, 0x06, 0xc7, 0x85, 0xbb, 0xf8, 0x4e, 0x99, 0x5c, 0x12, 0xb8, 0xd5, + 0x3a, 0x5b, 0x12, 0x84, 0x7f, 0x6a, 0x2a, 0x43, 0x11, 0x1e, 0x08, 0xd2, + 0xc1, 0x7b, 0xfc, 0x09, 0x1a, 0x0d, 0xef, 0x84, 0x10, 0x93, 0xf3, 0x50, + 0x50, 0x37, 0x80, 0x42, 0xe1, 0xe4, 0xc3, 0x9d, 0xde, 0x83, 0xeb, 0x3b, + 0xe4, 0x2e, 0xac, 0xcf, 0x0f, 0xea, 0x4a, 0x1b, 0xae, 0x65, 0x72, 0x80, + 0xe4, 0x05, 0xe4, 0xd6, 0x12, 0x5a, 0xac, 0xde, 0x80, 0x7b, 0xc6, 0xf7, + 0x0e, 0xbe, 0xd5, 0x49, 0xb3, 0xe1, 0x44, 0x78, 0x2e, 0x31, 0xfb, 0xef, + 0x76, 0x86, 0x48, 0xa9, 0x8d, 0xfe, 0xe9, 0xb4, 0x3f, 0xc1, 0xec, 0xed, + 0x6f, 0x5b, 0xd7, 0x5e, 0x2a, 0x32, 0x29, 0xd1, 0x2d, 0x3d, 0x78, 0x33, + 0x01, 0xf1, 0x57, 0xe8, 0x78, 0x2e, 0x31, 0xd3, 0x56, 0x77, 0xf7, 0xdf, + 0xcb, 0x22, 0xc7, 0x64, 0xf2, 0xc0, 0xa2, 0x05, 0xef, 0x51, 0x01, 0x53, + 0xaf, 0x1d, 0x29, 0x35, 0x6b, 0x68, 0xe5, 0xbb, 0x36, 0x37, 0x1a, 0xa0, + 0xff, 0x81, 0x67, 0xc8, 0xa0, 0x66, 0x11, 0xe9, 0xb9, 0xfb, 0xe7, 0xa8, + 0xc5, 0xd6, 0xe2, 0x57, 0x13, 0xeb, 0xea, 0xa7, 0x10, 0x35, 0x32, 0xac, + 0x04, 0x8d, 0x5d, 0xa4, 0x4f, 0x13, 0x50, 0x9f, 0xdc, 0xc8, 0x65, 0x86, + 0x86, 0x91, 0x64, 0x60, 0x60, 0xe6, 0x67, 0x99, 0x82, 0x1e, 0xbb, 0x8a, + 0xb6, 0x69, 0xf2, 0xcb, 0x3a, 0xe0, 0x27, 0xcd, 0x52, 0x71, 0x85, 0xd1, + 0x44, 0xe1, 0x0e, 0x4b, 0x16, 0x08, 0x6c, 0x73, 0x1b, 0x83, 0x71, 0xb8, + 0xac, 0xf6, 0x5c, 0x14, 0x5b, 0xc8, 0x26, 0x5c, 0x74, 0x91, 0xb0, 0xdd, + 0x8e, 0x1e, 0x35, 0xc8, 0x51, 0x40, 0xb8, 0x57, 0xc9, 0xdf, 0x8e, 0x55, + 0xb7, 0xcc, 0xf4, 0xc1, 0xbf, 0x7f, 0xd9, 0xa6, 0x46, 0xa3, 0x99, 0x80, + 0x15, 0xa2, 0x2f, 0x08, 0x10, 0x1c, 0xf3, 0xe5, 0x04, 0x30, 0x74, 0xde, + 0x57, 0xe0, 0xce, 0x97, 0x57, 0xb9, 0x53, 0x2a, 0xda, 0xb0, 0xb7, 0x82, + 0x4e, 0x69, 0xcf, 0xac, 0x0c, 0x0d, 0xf9, 0xc4, 0xb5, 0xc9, 0xda, 0x35, + 0xd2, 0x31, 0xf3, 0x84, 0xd4, 0x1f, 0xe1, 0x5f, 0x07, 0xc5, 0x33, 0xae, + 0xa4, 0x1a, 0xf1, 0x53, 0x54, 0x0e, 0xef, 0xa6, 0x4f, 0x49, 0xb3, 0xc3, + 0xa1, 0x7e, 0x76, 0x68, 0xf3, 0x9b, 0xd9, 0x29, 0xf7, 0x8e, 0x6f, 0x4e, + 0xe3, 0xd8, 0x26, 0x7c, 0x13, 0xc9, 0x34, 0x74, 0xa2, 0x82, 0xb3, 0x2e, + 0xb7, 0xec, 0xb7, 0x94, 0xbc, 0x59, 0x74, 0xa6, 0x1d, 0x98, 0xb1, 0x53, + 0x44, 0xf0, 0x7c, 0x76, 0x92, 0x12, 0xb7, 0x7f, 0x3e, 0x0b, 0x0d, 0xc9, + 0x9b, 0x13, 0x97, 0xcc, 0x54, 0x62, 0x95, 0xfc, 0x5b, 0x13, 0x23, 0xe3, + 0x5d, 0xa8, 0xcd, 0x77, 0x34, 0xf1, 0xc5, 0x95, 0x2a, 0x0a, 0xbb, 0x3b, + 0x55, 0x8e, 0x80, 0xc1, 0x58, 0xdd, 0x90, 0xba, 0x23, 0x40, 0x98, 0x9b, + 0xa9, 0xa1, 0x4f, 0xc5, 0x03, 0x3c, 0x8b, 0x2d, 0xe5, 0x9d, 0xb1, 0xe2, + 0xf3, 0x6c, 0x9f, 0x6d, 0x47, 0xad, 0x82, 0x3b, 0xaf, 0xe6, 0x5a, 0x84, + 0xbb, 0x56, 0x56, 0xe2, 0xda, 0xa0, 0x60, 0x6e, 0xd5, 0x1d, 0xbc, 0xa5, + 0x56, 0xcd, 0x7d, 0xb6, 0x0b, 0xa4, 0x32, 0xf7, 0x09, 0x91, 0x2c, 0x55, + 0x6b, 0x61, 0x60, 0x1e, 0xbc, 0x46, 0x29, 0xc6, 0xe5, 0x42, 0x52, 0x43, + 0xfd, 0x4a, 0xeb, 0xfd, 0x27, 0xee, 0x4e, 0x21, 0xbc, 0xe5, 0x71, 0xd0, + 0x41, 0xad, 0xad, 0x90, 0x13, 0x72, 0xfe, 0xe0, 0x3f, 0x1f, 0x2d, 0x0b, + 0x93, 0x0c, 0xd9, 0xbc, 0x2b, 0x11, 0x9c, 0x7b, 0x3d, 0x18, 0x98, 0x3e, + 0xd4, 0xf3, 0x46, 0x24, 0x15, 0xfb, 0x86, 0x90, 0x66, 0x05, 0xbb, 0x75, + 0xe8, 0xd7, 0xd6, 0x8c, 0x58, 0x03, 0x0c, 0x2f, 0x00, 0x35, 0xe1, 0xea, + 0x25, 0xdc, 0xb8, 0x89, 0xbd, 0x74, 0xe9, 0x68, 0x43, 0xe0, 0x85, 0xee, + 0x20, 0xb3, 0xd9, 0x4c, 0xf7, 0x7f, 0xad, 0x31, 0x69, 0xe9, 0x33, 0xc8, + 0x27, 0xed, 0x50, 0x8e, 0x2b, 0x73, 0x96, 0x86, 0xc9, 0x36, 0x25, 0x0e, + 0x31, 0x47, 0xba, 0x1b, 0xe4, 0xd8, 0x6d, 0x4c, 0xab, 0x9d, 0x73, 0x03, + 0x2e, 0x8c, 0x25, 0x07, 0xd0, 0xc4, 0x7a, 0x63, 0x10, 0xcd, 0xe9, 0x6a, + 0xb0, 0x2d, 0xa3, 0xab, 0xf3, 0x57, 0x96, 0xd3, 0xe5, 0x3f, 0x75, 0xc0, + 0x61, 0xd1, 0x09, 0xec, 0xd0, 0x22, 0xb9, 0x5f, 0x5c, 0xcc, 0x32, 0xf5, + 0xc1, 0x73, 0x9b, 0x2f, 0x84, 0x13, 0x9c, 0xf6, 0x15, 0x16, 0x30, 0x5f, + 0xf5, 0x83, 0x77, 0xb9, 0x59, 0xc7, 0x1b, 0xa1, 0xca, 0xae, 0x2d, 0xfc, + 0xb6, 0x63, 0xa9, 0x0a, 0x97, 0xdd, 0x59, 0x24, 0xe2, 0xe3, 0xde, 0x56, + 0x11, 0x69, 0x9a, 0x90, 0xb0, 0x3d, 0x7c, 0xc3, 0x1e, 0xf5, 0xe5, 0x59, + 0xc6, 0x8b, 0x36, 0xde, 0x88, 0x50, 0x14, 0xe7, 0xd5, 0x4d, 0xe0, 0x16, + 0xaa, 0x0d, 0x3d, 0x82, 0x63, 0x8a, 0xd4, 0x7e, 0x47, 0x65, 0xa3, 0x9a, + 0xf6, 0x91, 0x16, 0x56, 0xcf, 0xcd, 0x0b, 0xe4, 0xda, 0xe2, 0x72, 0x6a, + 0x8d, 0x01, 0xcb, 0x7a, 0x15, 0x9c, 0xf9, 0xc1, 0xfd, 0x22, 0xdf, 0xe8, + 0xb7, 0x30, 0xbf, 0x15, 0x18, 0x84, 0x8a, 0xd2, 0xae, 0xcb, 0x79, 0x67, + 0x30, 0x52, 0x66, 0xeb, 0x0e, 0xb5, 0x8b, 0x16, 0x5e, 0x63, 0x7e, 0xcb, + 0x21, 0x80, 0x38, 0xfc, 0x99, 0x59, 0x82, 0xf5, 0xcc, 0x49, 0xe8, 0xb2, + 0x18, 0x02, 0x04, 0x14, 0x89, 0x62, 0x4b, 0xfd, 0x63, 0xef, 0x6c, 0xd6, + 0xb8, 0xe7, 0x28, 0x63, 0x5d, 0xa3, 0xd5, 0x01, 0x19, 0xc8, 0xc7, 0x6e, + 0x4c, 0x9b, 0xb1, 0x9e, 0x18, 0x40, 0x8b, 0x7e, 0x46, 0x21, 0x19, 0xe6, + 0xef, 0x70, 0x11, 0x17, 0xe1, 0x94, 0xf3, 0xd6, 0x26, 0x1b, 0x1c, 0x83, + 0xde, 0x43, 0x8e, 0xa2, 0x6c, 0x84, 0x03, 0x94, 0x48, 0x6b, 0x50, 0x70, + 0x9b, 0xdf, 0x40, 0xfc, 0xa2, 0xed, 0xbd, 0xa3, 0x77, 0xa3, 0x0b, 0x8c, + 0x79, 0x6e, 0x15, 0x3b, 0xff, 0xe3, 0x78, 0x35, 0x17, 0x6f, 0xe6, 0x01, + 0xdd, 0x1b, 0x4b, 0xc9, 0x40, 0x35, 0x8f, 0x98, 0x82, 0x9e, 0xa5, 0xe8, + 0xa0, 0xeb, 0x66, 0x19, 0xf8, 0xc7, 0xba, 0xc4, 0x9f, 0x35, 0x78, 0xa3, + 0x62, 0x18, 0x75, 0x75, 0xce, 0x49, 0x41, 0xa4, 0x3c, 0x5d, 0x70, 0x46, + 0xf0, 0x01, 0xf1, 0xb0, 0xfc, 0x4a, 0xc2, 0xd1, 0x5e, 0xf8, 0x0a, 0x36, + 0x0f, 0x72, 0xef, 0xbb, 0x51, 0xa6, 0xbc, 0xb7, 0xae, 0xa3, 0x0e, 0x4e, + 0x61, 0xa1, 0x59, 0x53, 0x1c, 0x87, 0xfb, 0x20, 0x96, 0xd1, 0xff, 0x5a, + 0xcd, 0x98, 0x6b, 0x5c, 0x36, 0x1b, 0xdc, 0xa3, 0x6d, 0x8e, 0x17, 0x4f, + 0x27, 0x23, 0xc9, 0x84, 0xb9, 0x6b, 0xd9, 0x2d, 0x37, 0x12, 0x0e, 0xfa, + 0xcb, 0x7f, 0xc3, 0x59, 0x9d, 0x72, 0x55, 0xd7, 0x0a, 0x26, 0xb9, 0x22, + 0x3f, 0x7a, 0x02, 0x17, 0x78, 0x97, 0x21, 0xd2, 0x46, 0x93, 0xba, 0x61, + 0x49, 0xa9, 0x36, 0x52, 0xe4, 0x5e, 0x6d, 0xc6, 0x27, 0x03, 0x3b, 0x22, + 0x51, 0x82, 0xcd, 0x5d, 0x64, 0x40, 0x65, 0xcf, 0x8b, 0x8c, 0x13, 0xf0, + 0x2f, 0xd4, 0x6b, 0x37, 0x04, 0xcd, 0xa1, 0xf3, 0x04, 0xf7, 0x0c, 0x1a, + 0x99, 0xf4, 0xa6, 0x22, 0xe4, 0x92, 0x57, 0x9b, 0x17, 0x5f, 0x14, 0xfa, + 0x2b, 0x7b, 0xef, 0x07, 0x08, 0x2c, 0x9f, 0x7e, 0x4a, 0xb4, 0xc8, 0x1e, + 0x48, 0x28, 0x77, 0xbd, 0x38, 0x4a, 0xdd, 0x34, 0xe6, 0x4c, 0x4c, 0xe6, + 0xda, 0x02, 0xe7, 0x67, 0x70, 0x72, 0xa8, 0x5d, 0xc6, 0x9c, 0x14, 0x68, + 0x16, 0xcb, 0x2a, 0x09, 0x9d, 0x6f, 0xda, 0x67, 0x3b, 0x87, 0x74, 0xfe, + 0xf5, 0x20, 0x17, 0xac, 0xd5, 0x92, 0xd9, 0x80, 0x72, 0xac, 0x1d, 0x7a, + 0x03, 0x41, 0x78, 0x5c, 0x56, 0xbb, 0x57, 0xd0, 0x96, 0x8c, 0xd7, 0xbf, + 0x24, 0xaa, 0x77, 0xa3, 0x31, 0x6d, 0xb4, 0x97, 0x37, 0xdf, 0x3a, 0x1a, + 0x26, 0x6a, 0x7a, 0x2e, 0xf5, 0x48, 0xb4, 0x89, 0xf7, 0x39, 0x50, 0x57, + 0x59, 0x8d, 0x7b, 0xd5, 0x77, 0x72, 0x30, 0x33, 0xbc, 0x45, 0xc9, 0x0f, + 0x3b, 0xc4, 0x9a, 0xc8, 0x15, 0xe7, 0x6a, 0xf3, 0xbc, 0x52, 0x9e, 0x95, + 0x26, 0xbc, 0x2e, 0x0d, 0xb0, 0x8d, 0x03, 0x3e, 0x1e, 0x8a, 0x21, 0x50, + 0x07, 0x8c, 0x88, 0x6a, 0xb1, 0xa3, 0x52, 0xbf, 0xec, 0x6f, 0xc6, 0x1d, + 0x09, 0xeb, 0xe2, 0xd1, 0x8a, 0x4c, 0xbf, 0xd1, 0x66, 0xbe, 0x67, 0x16, + 0xbe, 0x54, 0x39, 0x71, 0xaa, 0x12, 0x96, 0x4c, 0xec, 0xc4, 0xd8, 0x32, + 0x0f, 0xca, 0x67, 0x27, 0xf8, 0x3b, 0x19, 0xb7, 0x71, 0x30, 0x52, 0x9c, + 0xd1, 0x63, 0xfd, 0xa8, 0xd7, 0xc7, 0x6c, 0x77, 0x61, 0x15, 0x2e, 0x8b, + 0x07, 0xc7, 0x7b, 0x2a, 0x0c, 0x1c, 0xd0, 0x9c, 0x6f, 0xbf, 0x19, 0x3b, + 0x8e, 0x92, 0xdd, 0xce, 0xa5, 0xda, 0x7f, 0x8f, 0xd2, 0x52, 0xa8, 0x77, + 0xa0, 0x6b, 0x9c, 0x39, 0x32, 0xc5, 0x1e, 0xbd, 0x7e, 0xdf, 0x6a, 0xad, + 0xdb, 0xa9, 0x45, 0x3e, 0x04, 0xbc, 0x48, 0xb6, 0xe7, 0x22, 0xcf, 0x76, + 0xac, 0x90, 0x38, 0x60, 0xe1, 0xfd, 0x93, 0xc6, 0xcc, 0x6d, 0xc0, 0x3d, + 0xe0, 0x14, 0x3f, 0x25, 0x59, 0xf9, 0x9a, 0x48, 0xfe, 0xb6, 0xb0, 0x22, + 0xec, 0xb8, 0x29, 0x11, 0x08, 0x8c, 0x03, 0xb7, 0x3d, 0xb8, 0xd0, 0x98, + 0x05, 0x9b, 0x9e, 0x61, 0xac, 0xfb, 0xf3, 0x1a, 0x5e, 0x3a, 0xf0, 0xa2, + 0xf5, 0x2a, 0x19, 0x2e, 0xfa, 0x21, 0x3f, 0x0b, 0x48, 0xf6, 0x3a, 0x79, + 0x8f, 0x59, 0x8d, 0xb0, 0xa7, 0x34, 0x98, 0xae, 0xa0, 0x5f, 0xc8, 0x8b, + 0xe3, 0x7f, 0x07, 0x7b, 0x2d, 0xfc, 0x4c, 0x04, 0xb9, 0xe6, 0x14, 0x04, + 0x3f, 0x6c, 0xdf, 0x09, 0x35, 0xfa, 0x71, 0xd9, 0x29, 0x29, 0x9c, 0x78, + 0xce, 0xf4, 0x62, 0xb1, 0xc2, 0x54, 0xd2, 0xbe, 0xfc, 0x7e, 0x64, 0x28, + 0xe7, 0x46, 0x96, 0xfa, 0xbf, 0x1d, 0x9b, 0x50, 0x9b, 0x01, 0x44, 0x7e, + 0x6e, 0x88, 0xb9, 0x27, 0x00, 0x3e, 0xf3, 0x54, 0x45, 0xac, 0x77, 0x72, + 0x81, 0xbd, 0xbc, 0xe6, 0x9e, 0xdb, 0x8f, 0x1f, 0x9b, 0x5d, 0x82, 0x17, + 0x7c, 0x0d, 0x3d, 0xfa, 0xcc, 0x4e, 0xa5, 0xc9, 0x7e, 0x45, 0xd6, 0xf8, + 0x65, 0xd7, 0x2f, 0xb2, 0x63, 0x58, 0x04, 0x57, 0xc4, 0x0c, 0x2d, 0xc4, + 0x33, 0x19, 0x02, 0x35, 0x36, 0xdd, 0x15, 0x5b, 0x50, 0xdc, 0x30, 0xbc, + 0xb8, 0x9a, 0x0e, 0x95, 0x76, 0xe0, 0x45, 0xed, 0x7c, 0xe9, 0x73, 0x12, + 0x32, 0xaf, 0xe3, 0xf1, 0x69, 0x17, 0x7d, 0x22, 0x9c, 0x9b, 0xae, 0x12, + 0xfc, 0xdd, 0xeb, 0xe7, 0xef, 0x6c, 0x2b, 0xb4, 0xe5, 0x95, 0x6c, 0xad, + 0x6b, 0xac, 0x84, 0x20, 0x75, 0xa3, 0xeb, 0x86, 0xc7, 0x13, 0x84, 0x11, + 0x70, 0x64, 0xe7, 0xfd, 0x5c, 0x8e, 0xc8, 0x60, 0xa2, 0x93, 0x8a, 0xad, + 0xd0, 0x6f, 0x2d, 0x83, 0x22, 0x7d, 0x99, 0xd2, 0x8e, 0x7e, 0x67, 0x88, + 0x38, 0xec, 0x83, 0x15, 0xd4, 0x71, 0xd5, 0x47, 0xcd, 0xab, 0x44, 0xe1, + 0xce, 0xf1, 0x94, 0xee, 0xe6, 0x14, 0x8e, 0xea, 0x4f, 0xa5, 0x18, 0xa3, + 0x2c, 0xb6, 0x1f, 0xae, 0xfb, 0x2a, 0x3a, 0x59, 0xae, 0xf7, 0xb0, 0x47, + 0x5d, 0x5c, 0xc8, 0x86, 0x9c, 0x36, 0xb7, 0x76, 0x83, 0x06, 0xda, 0x53, + 0xb3, 0x52, 0x43, 0x43, 0x79, 0xdb, 0xd6, 0xff, 0x68, 0xf5, 0xce, 0xf3, + 0x76, 0xda, 0xfa, 0x6e, 0x26, 0x7a, 0xc3, 0x1d, 0xc4, 0x0b, 0xf1, 0x95, + 0x8b, 0x57, 0x62, 0x00, 0x93, 0x4a, 0xa0, 0xe1, 0x3b, 0x78, 0x8f, 0x78, + 0x16, 0x40, 0xa2, 0x9c, 0x71, 0x5c, 0x54, 0x5d, 0x1e, 0xdf, 0x47, 0xfe, + 0x0b, 0xdf, 0x87, 0x40, 0x1c, 0x4a, 0x0e, 0x77, 0xed, 0x18, 0x7c, 0x36, + 0xb8, 0x92, 0xa1, 0x14, 0x2c, 0x29, 0x0a, 0x17, 0xef, 0xf5, 0x79, 0x25, + 0x15, 0x66, 0x59, 0x6e, 0x1e, 0xd5, 0x94, 0x16, 0x11, 0x9a, 0x48, 0x71, + 0xf6, 0x59, 0xb9, 0x45, 0xde, 0x8f, 0xc4, 0x06, 0xc7, 0xdd, 0x31, 0xd7, + 0x35, 0x18, 0xb4, 0x7f, 0x02, 0x13, 0x4e, 0xc6, 0x16, 0x25, 0x3c, 0xfe, + 0x2c, 0xdd, 0xec, 0x32, 0x58, 0x9c, 0xbb, 0x84, 0x1c, 0x67, 0x74, 0x1a, + 0x0a, 0xb0, 0x08, 0xf4, 0xb3, 0x5d, 0xb0, 0x69, 0x11, 0x6f, 0xab, 0x75, + 0x88, 0x5f, 0xa2, 0xa7, 0xc9, 0x9a, 0x89, 0x4a, 0x9c, 0x83, 0xb8, 0x10, + 0x73, 0x0c, 0x55, 0x50, 0x6e, 0xbc, 0xd3, 0x0c, 0x78, 0xb4, 0x7a, 0x0b, + 0x18, 0xe2, 0x95, 0x52, 0xf1, 0x8c, 0xc9, 0xf7, 0x48, 0x4d, 0x5b, 0xc5, + 0x9e, 0x02, 0x37, 0x77, 0x32, 0xfc, 0x96, 0x51, 0xcd, 0xe0, 0x3a, 0xd4, + 0xed, 0xfe, 0xa8, 0x38, 0x4b, 0x97, 0x8c, 0x02, 0x42, 0x53, 0x5b, 0x0c, + 0x7c, 0xc8, 0x9b, 0x83, 0x44, 0x87, 0x0f, 0xa3, 0x00, 0x75, 0xa1, 0x9e, + 0xc7, 0xcc, 0xde, 0x10, 0xe2, 0x54, 0x48, 0x87, 0x74, 0x16, 0x50, 0xce, + 0x89, 0xdf, 0x41, 0x42, 0x8b, 0x49, 0xf8, 0x95, 0xde, 0x33, 0x30, 0x84, + 0x58, 0xcb, 0xc2, 0x89, 0x74, 0xef, 0x82, 0xf0, 0x6b, 0xe6, 0xdf, 0x37, + 0xc5, 0x6a, 0xe2, 0xcb, 0x83, 0xd1, 0x4d, 0xca, 0x5b, 0xd1, 0xd1, 0x0f, + 0x51, 0x30, 0x14, 0x4b, 0x82, 0x28, 0x35, 0x40, 0xe7, 0xbb, 0xda, 0x66, + 0xbe, 0x59, 0x08, 0x48, 0x1f, 0xe5, 0xee, 0x24, 0xb7, 0x67, 0x53, 0xd9, + 0x4e, 0x19, 0x96, 0xe0, 0xb5, 0x56, 0x77, 0x23, 0xaa, 0xb0, 0x9d, 0x2f, + 0x60, 0xa8, 0xc1, 0x2b, 0xa6, 0x7c, 0x3a, 0x05, 0xa4, 0xbf, 0x70, 0x35, + 0x10, 0xca, 0x7e, 0xf4, 0xe8, 0x39, 0xda, 0x21, 0xc0, 0xd2, 0xbc, 0xbb, + 0xb6, 0x65, 0x37, 0x51, 0xc9, 0xcc, 0x7c, 0x52, 0xe9, 0x55, 0x7f, 0x84, + 0x8a, 0x75, 0xdf, 0x17, 0x2b, 0x81, 0x93, 0x6f, 0x36, 0x50, 0x86, 0xdc, + 0x4f, 0xc6, 0x9a, 0x58, 0xae, 0x20, 0x40, 0x0b, 0x4d, 0xf3, 0x57, 0x05, + 0x3b, 0x1a, 0x09, 0xd1, 0x9d, 0xe9, 0xa9, 0x40, 0x0e, 0x52, 0x78, 0x8f, + 0x09, 0x1c, 0xc5, 0x89, 0x38, 0x7b, 0x75, 0x10, 0x1e, 0xb3, 0x0e, 0xcb, + 0x97, 0x72, 0xc2, 0xda, 0x62, 0xf5, 0xde, 0x17, 0xb9, 0xbc, 0xc2, 0x1e, + 0xcb, 0x25, 0x01, 0x0d, 0x1e, 0x81, 0x43, 0x0f, 0xa9, 0x5a, 0x30, 0x93, + 0xd0, 0xbf, 0xbe, 0xcc, 0x6e, 0xe1, 0x41, 0xb9, 0x72, 0x90, 0xee, 0x8c, + 0x26, 0xf7, 0xd8, 0x3e, 0x49, 0x60, 0xb7, 0xc6, 0x77, 0x40, 0x81, 0xb2, + 0xfd, 0xec, 0x26, 0x7b, 0xcf, 0xb2, 0x55, 0x3f, 0x8f, 0x19, 0x74, 0x0a, + 0x5c, 0xbb, 0xc3, 0xf6, 0x1f, 0x23, 0x52, 0x50, 0x92, 0xa1, 0x5e, 0xf9, + 0xea, 0x98, 0xcd, 0x90, 0x3d, 0x71, 0x93, 0x3e, 0x48, 0x2e, 0x28, 0xed, + 0x90, 0xaf, 0xeb, 0xae, 0xae, 0x82, 0x65, 0xf2, 0x89, 0x8f, 0x2b, 0x3d, + 0xc2, 0x89, 0xb8, 0xbe, 0x7e, 0xd7, 0x2a, 0xb2, 0x7b, 0xb0, 0x41, 0xa7, + 0x3b, 0x52, 0xfc, 0x9a, 0x92, 0x7d, 0x8d, 0x5e, 0x10, 0x52, 0x9a, 0x42, + 0x3e, 0xa5, 0x44, 0x93, 0x39, 0xe7, 0x9d, 0xd5, 0x15, 0x2a, 0xc2, 0x46, + 0xd4, 0x8a, 0x5c, 0xbc, 0x6a, 0x78, 0x13, 0x8e, 0x3c, 0x03, 0xa9, 0xe9, + 0x84, 0x78, 0xe6, 0x78, 0xfc, 0xb3, 0x8d, 0x3c, 0x5c, 0xaa, 0x0a, 0xd2, + 0xc1, 0xd1, 0x59, 0xaa, 0x44, 0x2d, 0xc2, 0xcf, 0x91, 0xf3, 0x03, 0x52, + 0xa8, 0x2c, 0x6f, 0x48, 0xb6, 0xfc, 0x5e, 0x50, 0x4e, 0xe0, 0xe2, 0x1e, + 0xb1, 0x18, 0x82, 0xca, 0xa0, 0x08, 0xf0, 0xd0, 0x7f, 0x82, 0x10, 0x05, + 0x52, 0xf9, 0xab, 0xe4, 0x69, 0x16, 0x35, 0x48, 0x37, 0x4d, 0xf6, 0xea, + 0xff, 0x18, 0x74, 0xa9, 0xea, 0x28, 0x39, 0x3e, 0x0d, 0x52, 0x7a, 0xd2, + 0x4b, 0xa1, 0x60, 0x88, 0x03, 0x16, 0x3b, 0xaa, 0x68, 0x28, 0x4b, 0x86, + 0xd1, 0x53, 0xe4, 0xfa, 0x34, 0xf1, 0x8b, 0xef, 0x69, 0x6a, 0x1d, 0xae, + 0xd6, 0xed, 0x6c, 0xfb, 0x23, 0xb9, 0x0d, 0x28, 0x74, 0x95, 0x11, 0x13, + 0x32, 0x3b, 0x4a, 0x66, 0x1d, 0xe6, 0xfb, 0x14, 0x85, 0x4c, 0x78, 0xb4, + 0xe2, 0xac, 0x51, 0x67, 0xd8, 0xe0, 0x77, 0x56, 0x31, 0x16, 0x1a, 0x56, + 0xfa, 0x3d, 0x50, 0xc6, 0xd9, 0x49, 0xfc, 0x3a, 0x47, 0xcf, 0xb4, 0x48, + 0x76, 0x80, 0x30, 0x0c, 0xcc, 0x59, 0x87, 0xa6, 0x64, 0xed, 0x3e, 0xc6, + 0x61, 0xd9, 0x3b, 0xd7, 0xaf, 0x84, 0x57, 0x1a, 0x05, 0xab, 0x2e, 0x0a, + 0x95, 0x82, 0xcc, 0xdd, 0xda, 0x8f, 0x2f, 0x66, 0xbe, 0xfc, 0x27, 0xe7, + 0xdd, 0x95, 0x1a, 0x7c, 0x55, 0x55, 0xe0, 0x46, 0xca, 0xd1, 0xe2, 0x5e, + 0x1c, 0x80, 0xe2, 0xb9, 0x2e, 0x4e, 0x89, 0x79, 0xbc, 0x38, 0xc4, 0xaf, + 0x93, 0x29, 0x5a, 0x89, 0x7b, 0x22, 0x1d, 0x1a, 0x27, 0xf9, 0x8a, 0x61, + 0xf4, 0xd0, 0x4e, 0x78, 0x8e, 0x08, 0xcd, 0xb8, 0x13, 0xce, 0x4b, 0xf2, + 0x2b, 0x2d, 0x4f, 0x41, 0x20, 0xa4, 0xb5, 0x6e, 0x60, 0xb8, 0xf6, 0xf9, + 0x55, 0x11, 0xce, 0xa8, 0x72, 0xc1, 0xf9, 0xbb, 0x2c, 0xd6, 0x0a, 0x63, + 0x4a, 0xdb, 0x8a, 0xa5, 0x2c, 0xc7, 0x46, 0xb8, 0x51, 0x60, 0xab, 0x13, + 0xd1, 0x8b, 0x6e, 0xcd, 0xaf, 0x7e, 0xb0, 0x17, 0x60, 0x63, 0x78, 0x46, + 0x5d, 0x8b, 0xaf, 0x58, 0x42, 0x57, 0xef, 0x87, 0x09, 0xa7, 0x44, 0x0e, + 0xab, 0xe8, 0xf8, 0x1c, 0x6f, 0x6b, 0xde, 0x0f, 0x2f, 0x9f, 0x70, 0x76, + 0x40, 0x94, 0xca, 0x8e, 0x8b, 0x1a, 0x28, 0x18, 0x32, 0x75, 0x70, 0xa8, + 0x63, 0xca, 0xf5, 0x21, 0x5d, 0xc0, 0xb8, 0x7a, 0x96, 0x08, 0x86, 0x00, + 0x8e, 0xfb, 0xab, 0x10, 0xef, 0x26, 0xf5, 0x25, 0x7a, 0x42, 0xff, 0x21, + 0xc8, 0x44, 0x12, 0x3a, 0x18, 0x36, 0x5f, 0xdf, 0xf2, 0xc0, 0x3a, 0xef, + 0x03, 0x3d, 0xdc, 0xdb, 0xd3, 0x37, 0xa7, 0x25, 0x84, 0x3d, 0xaf, 0x54, + 0xc9, 0x81, 0xae, 0x33, 0x98, 0x85, 0x26, 0xcd, 0xdd, 0xa3, 0x7b, 0x53, + 0xcf, 0x15, 0x48, 0x64, 0xce, 0x97, 0x6d, 0xd4, 0x17, 0xc7, 0x1a, 0x13, + 0x36, 0xc8, 0xfe, 0x2a, 0x61, 0x91, 0xe6, 0xe5, 0xde, 0x95, 0xf2, 0xa7, + 0x08, 0xfb, 0xef, 0x20, 0x1d, 0x3f, 0x97, 0xae, 0xa9, 0x0c, 0x99, 0x33, + 0x25, 0x59, 0x41, 0x21, 0xd9, 0xae, 0x88, 0x04, 0x5b, 0x29, 0xd3, 0x7a, + 0xae, 0xce, 0x82, 0x42, 0x70, 0x70, 0xf9, 0xfd, 0x24, 0xdd, 0x30, 0xf5, + 0x50, 0x4b, 0x8e, 0x06, 0xc7, 0x14, 0x14, 0x9e, 0xa8, 0x8f, 0x0c, 0xdc, + 0xe1, 0x88, 0x36, 0x3c, 0x23, 0xcd, 0xd7, 0x72, 0xb4, 0xbb, 0x50, 0xd7, + 0x06, 0xbc, 0x03, 0x4c, 0xeb, 0x2b, 0x59, 0xac, 0x0f, 0xf4, 0xfb, 0x02, + 0x32, 0x65, 0x50, 0xf7, 0x78, 0xc5, 0xa1, 0xde, 0x11, 0x8f, 0x38, 0x4b, + 0xee, 0xc9, 0x84, 0xe6, 0x14, 0x09, 0x0a, 0x08, 0x7b, 0xf0, 0x78, 0x2f, + 0x56, 0xbc, 0xe1, 0x59, 0x43, 0x5b, 0x26, 0x0d, 0x7a, 0xe5, 0x6a, 0x64, + 0xe1, 0x29, 0xb9, 0xec, 0x2c, 0x56, 0x84, 0x36, 0xb3, 0x2c, 0x94, 0xfc, + 0x12, 0x87, 0x4c, 0x42, 0xbf, 0x36, 0x39, 0x66, 0x67, 0xab, 0xc7, 0x46, + 0xb4, 0x4d, 0x2d, 0xdb, 0xf7, 0xa5, 0x89, 0x05, 0xdb, 0x75, 0x6f, 0x7d, + 0x68, 0x43, 0xc9, 0xbc, 0x09, 0x57, 0x53, 0x6e, 0x43, 0xb6, 0xeb, 0xde, + 0x23, 0x3c, 0x80, 0x15, 0x4a, 0xb9, 0x97, 0xcb, 0xe8, 0x0a, 0x56, 0x7b, + 0xb3, 0x04, 0x0c, 0x65, 0x92, 0x4a, 0x93, 0x47, 0xa4, 0xba, 0x45, 0xc0, + 0x98, 0xa3, 0x58, 0x26, 0x9b, 0x33, 0x0d, 0xdf, 0x32, 0x05, 0xde, 0xe9, + 0x1b, 0x2b, 0xa8, 0x68, 0xaa, 0x1d, 0xd0, 0xb5, 0xd5, 0x00, 0xe0, 0x57, + 0x26, 0x3d, 0x43, 0xb4, 0x6d, 0xc4, 0x7e, 0xb0, 0x98, 0x22, 0x94, 0xe4, + 0x57, 0x22, 0x39, 0x49, 0xdd, 0x64, 0xea, 0xc0, 0x79, 0xcd, 0x94, 0x5f, + 0x55, 0xf7, 0x02, 0x60, 0x7f, 0xd5, 0x99, 0x42, 0x34, 0x03, 0xe1, 0x41, + 0x3d, 0xa2, 0xf6, 0xd9, 0xd6, 0x56, 0x57, 0x03, 0x23, 0x68, 0x70, 0x9e, + 0x96, 0x79, 0xdf, 0x27, 0x07, 0xf4, 0xd1, 0x0e, 0xec, 0x20, 0xd0, 0x1a, + 0x43, 0x08, 0xa9, 0x90, 0x7a, 0x07, 0xf2, 0x23, 0x65, 0xe7, 0xae, 0x16, + 0xdd, 0x09, 0x70, 0x40, 0x0b, 0xb3, 0x1a, 0x14, 0xb0, 0xc4, 0x1c, 0x9c, + 0x0c, 0x7a, 0xeb, 0x54, 0xc6, 0x6d, 0xef, 0xf4, 0xa6, 0x7a, 0x39, 0x20, + 0x3b, 0x4b, 0x57, 0x60, 0x85, 0xf0, 0x4b, 0x68, 0x18, 0x8b, 0x5b, 0xef, + 0x05, 0x1c, 0x99, 0xe2, 0xda, 0x5a, 0xa8, 0xd4, 0x7e, 0x97, 0x12, 0x02, + 0x21, 0x20, 0x65, 0x93, 0x5a, 0x10, 0xa3, 0x3d, 0x21, 0xeb, 0x6d, 0x41, + 0x76, 0x9b, 0xe5, 0x0b, 0xbc, 0x45, 0xff, 0xbf, 0xeb, 0xf3, 0xaa, 0xbe, + 0x85, 0xee, 0x50, 0x13, 0x1c, 0x28, 0x7e, 0xcb, 0xca, 0xc6, 0xe4, 0xdf, + 0xfa, 0x6b, 0xed, 0x5e, 0xb8, 0x13, 0x08, 0xe4, 0xda, 0x0a, 0x68, 0x2b, + 0x1d, 0xca, 0xd3, 0xac, 0xc4, 0x16, 0xa6, 0xc0, 0xc4, 0x46, 0xa9, 0x95, + 0x3a, 0x07, 0x9d, 0xa6, 0x7a, 0x56, 0xc4, 0xf9, 0x47, 0x4f, 0x2f, 0x08, + 0x1e, 0x12, 0x99, 0x9a, 0x2b, 0xa3, 0x66, 0x4b, 0xfa, 0x9e, 0xf6, 0x33, + 0x1f, 0x5a, 0x79, 0xdc, 0x74, 0x8f, 0xad, 0xfd, 0xf1, 0x42, 0x72, 0x3b, + 0xfe, 0xe1, 0x1c, 0x3f, 0xe9, 0x56, 0x94, 0x68, 0x8b, 0x0b, 0x5a, 0x43, + 0xf4, 0xa5, 0x63, 0xe6, 0xe5, 0x8e, 0x9e, 0x76, 0xd0, 0x9d, 0x00, 0x22, + 0x02, 0xce, 0xef, 0x18, 0x83, 0xd9, 0x7b, 0xc8, 0xcd, 0xc4, 0x8f, 0x74, + 0x76, 0x0c, 0x37, 0x31, 0x39, 0xdc, 0xa0, 0xfb, 0xdf, 0x2d, 0x84, 0x88, + 0x11, 0xa7, 0xdb, 0x77, 0xc8, 0xce, 0x99, 0xfe, 0x03, 0xf9, 0x43, 0xc3, + 0xbb, 0x7e, 0x4a, 0xac, 0x14, 0x53, 0xaf, 0xcb, 0x18, 0xed, 0x6d, 0xe9, + 0x5f, 0x03, 0x93, 0x3f, 0xa6, 0xcb, 0x21, 0xb6, 0x40, 0x5e, 0xfc, 0xe8, + 0x8b, 0xfe, 0xaf, 0x1c, 0x66, 0xaa, 0x82, 0xfb, 0x84, 0xf2, 0xa2, 0x5c, + 0x87, 0x9d, 0x2b, 0x70, 0x96, 0x4b, 0xc2, 0xa9, 0x61, 0x89, 0x8f, 0x67, + 0xd5, 0x24, 0x2a, 0xea, 0x25, 0x43, 0xed, 0x39, 0x8d, 0x5e, 0xa7, 0xdf, + 0x9e, 0x16, 0xf3, 0xd3, 0x31, 0x00, 0x93, 0x55, 0xd4, 0x49, 0xa4, 0x2e, + 0xf9, 0x12, 0x95, 0xe1, 0xf3, 0x7d, 0xf9, 0x76, 0x54, 0x3e, 0x96, 0xc0, + 0x07, 0x63, 0x3f, 0x3f, 0xb8, 0x2d, 0x2f, 0x7c, 0x44, 0xdd, 0x29, 0xf5, + 0x72, 0xd1, 0x2f, 0xb6, 0xfe, 0xeb, 0x5f, 0x5c, 0xcc, 0xce, 0x93, 0x4d, + 0x64, 0x23, 0x71, 0x5e, 0x05, 0x88, 0x2e, 0x73, 0x77, 0xef, 0x13, 0x1d, + 0x37, 0xa6, 0xc1, 0x99, 0xa8, 0x57, 0xa5, 0x1c, 0x21, 0xc1, 0x0b, 0xcf, + 0x47, 0xe6, 0x99, 0x90, 0xa7, 0x3d, 0x73, 0x5f, 0x5f, 0x60, 0x9a, 0x5d, + 0xed, 0x31, 0xba, 0xc8, 0xf1, 0xe9, 0x6a, 0x9c, 0xe4, 0x63, 0x9a, 0x21, + 0x78, 0x85, 0xd6, 0x2e, 0x80, 0xc9, 0x3b, 0x26, 0xd6, 0x66, 0x44, 0x46, + 0xcc, 0x37, 0xc1, 0xb6, 0x37, 0x0e, 0x59, 0xf2, 0x94, 0x2f, 0x6e, 0x64, + 0x78, 0xb4, 0x15, 0x76, 0x88, 0x01, 0x2b, 0xd0, 0x95, 0x12, 0xdd, 0xf2, + 0x92, 0xc1, 0xe2, 0x30, 0xa4, 0xdf, 0x5b, 0xce, 0x2a, 0xc4, 0x3f, 0xc8, + 0x8d, 0xb7, 0x7f, 0x12, 0x6f, 0x85, 0xc9, 0xf5, 0x6a, 0xf0, 0xb4, 0x77, + 0x50, 0xb0, 0xe7, 0x6f, 0x45, 0x6d, 0xc8, 0xb6, 0x34, 0x68, 0x70, 0x0d, + 0x62, 0x02, 0xff, 0x02, 0x76, 0x44, 0x86, 0x4d, 0xf5, 0xc4, 0xfe, 0xbd, + 0x3d, 0x72, 0x6b, 0x7e, 0x68, 0x10, 0x9d, 0xf2, 0xa7, 0x1a, 0xa6, 0xad, + 0x7f, 0x3c, 0xb0, 0x18, 0x20, 0x25, 0xdd, 0x02, 0xd0, 0x08, 0xa5, 0xc4, + 0xba, 0x28, 0x79, 0x92, 0x34, 0x0d, 0xc2, 0x48, 0x8e, 0x6f, 0x6d, 0x21, + 0x36, 0x17, 0x8c, 0x35, 0x6a, 0xf0, 0xa2, 0xf6, 0xb9, 0x26, 0x02, 0x47, + 0xa8, 0x4d, 0x28, 0x34, 0xfb, 0x60, 0xaa, 0xb2, 0x49, 0x8a, 0x49, 0xf0, + 0xc9, 0x86, 0x83, 0x0b, 0x44, 0xbe, 0x52, 0x52, 0x6f, 0x3c, 0x4e, 0x42, + 0x4e, 0xc0, 0x13, 0xdc, 0x5d, 0xcc, 0x8d, 0xf5, 0x98, 0xeb, 0x25, 0x82, + 0x97, 0xe0, 0xca, 0x0a, 0x2a, 0xd2, 0x73, 0x1a, 0xd8, 0x2f, 0xb9, 0x8b, + 0x64, 0x18, 0x75, 0xfa, 0xc0, 0xe1, 0xce, 0xaa, 0x93, 0x32, 0x53, 0x44, + 0x00, 0x02, 0x0c, 0xab, 0x2d, 0xb7, 0x19, 0x89, 0xeb, 0x71, 0x1e, 0x9d, + 0xaf, 0xec, 0xd1, 0xd5, 0x58, 0xd0, 0xf8, 0x71, 0xe4, 0xbe, 0xf7, 0x1c, + 0x49, 0x3f, 0xd3, 0x87, 0x26, 0x7f, 0x1d, 0x5b, 0xe7, 0x6a, 0x9f, 0x5e, + 0xb2, 0xd2, 0x97, 0xf3, 0x26, 0x74, 0xaf, 0x70, 0xe3, 0x1b, 0x8a, 0xda, + 0xc2, 0x0d, 0xad, 0xc3, 0xe6, 0xf2, 0x90, 0x1b, 0x97, 0x06, 0x94, 0x43, + 0x7e, 0xf6, 0x17, 0xb7, 0xb0, 0x22, 0x1d, 0xd7, 0x4b, 0x6e, 0xb8, 0x35, + 0xda, 0xa0, 0x18, 0xc3, 0x9c, 0x80, 0xfa, 0xb0, 0x18, 0xae, 0x56, 0x83, + 0x90, 0x3f, 0x77, 0xf5, 0x3d, 0x32, 0xae, 0xf7, 0x88, 0xdf, 0x79, 0xa5, + 0x92, 0x4e, 0xeb, 0x2b, 0x14, 0xe4, 0xa6, 0x7c, 0xe7, 0x6f, 0xf1, 0x68, + 0x45, 0xab, 0x59, 0x32, 0x6d, 0x68, 0x69, 0xb0, 0x7a, 0xe2, 0x8d, 0xa4, + 0x12, 0x98, 0xe0, 0x55, 0xf2, 0xa4, 0x39, 0x10, 0x8e, 0xd4, 0x02, 0x39, + 0x25, 0x4f, 0xe4, 0xb2, 0x04, 0xdf, 0x43, 0x65, 0x7f, 0x00, 0x38, 0xf3, + 0x10, 0xac, 0x2b, 0xc0, 0x3b, 0x9b, 0xe1, 0x3f, 0x70, 0x47, 0x3e, 0x6b, + 0xbb, 0x10, 0x89, 0x57, 0x49, 0x04, 0xfc, 0xbe, 0x71, 0x95, 0x8e, 0x03, + 0x8a, 0x99, 0x74, 0x51, 0xa1, 0x8d, 0x79, 0xe0, 0x46, 0xff, 0x1f, 0xee, + 0xc0, 0x60, 0x7f, 0xe7, 0xed, 0x4f, 0x8e, 0xc2, 0xd9, 0x5f, 0xd0, 0xb8, + 0xb4, 0x3d, 0x41, 0xcf, 0xc2, 0xfe, 0xab, 0x3b, 0xaf, 0xbc, 0xb7, 0x47, + 0xb2, 0x46, 0xfd, 0x04, 0xeb, 0xaa, 0x4b, 0x71, 0x4e, 0xd7, 0x6f, 0x7d, + 0x7c, 0x74, 0xd1, 0x9c, 0x89, 0x04, 0x1b, 0xeb, 0xac, 0x01, 0xe9, 0x4c, + 0xce, 0xa9, 0x8f, 0x4b, 0x39, 0x75, 0x36, 0x2f, 0x1c, 0xb5, 0xf9, 0x4d, + 0xfd, 0x0c, 0xca, 0xc0, 0x25, 0xb8, 0x13, 0x87, 0xa3, 0xa0, 0xc2, 0xba, + 0xe9, 0x69, 0xaa, 0xb1, 0x74, 0xc0, 0x99, 0xd1, 0x98, 0x97, 0x6c, 0xde, + 0xf5, 0xbe, 0xa2, 0x3a, 0x80, 0x68, 0x90, 0x07, 0xd5, 0x11, 0xe7, 0x54, + 0x68, 0x79, 0x25, 0x7e, 0xdc, 0xa6, 0xce, 0x11, 0x7f, 0xfb, 0x6e, 0xeb, + 0x3d, 0x15, 0xe6, 0x15, 0xd1, 0xc8, 0xe2, 0x23, 0xff, 0x31, 0x62, 0x47, + 0x82, 0xe1, 0x27, 0xce, 0xe4, 0x19, 0x2e, 0x5f, 0x21, 0x8e, 0x62, 0x57, + 0x35, 0xdf, 0x02, 0x8a, 0x55, 0x06, 0x68, 0xb1, 0x26, 0x5d, 0x75, 0x55, + 0xc7, 0xdc, 0x44, 0xae, 0xf6, 0xfc, 0xb8, 0xa5, 0x20, 0xd5, 0x68, 0xbd, + 0x05, 0x80, 0xe4, 0x58, 0xef, 0x16, 0xa5, 0xeb, 0x3e, 0xf3, 0x3c, 0xc4, + 0x30, 0x55, 0xc1, 0x25, 0x77, 0xbd, 0x92, 0x14, 0xbb, 0x1e, 0xdd, 0x0f, + 0x28, 0x1c, 0x8e, 0xe8, 0x53, 0x65, 0x94, 0x7e, 0x46, 0xae, 0x79, 0x2a, + 0xf0, 0xdb, 0x54, 0x7c, 0x7d, 0xd4, 0xf0, 0x5a, 0x32, 0xbb, 0x41, 0x13, + 0x74, 0x15, 0x2e, 0x43, 0x29, 0xaf, 0x56, 0x95, 0x00, 0x4c, 0x78, 0x01, + 0xd0, 0x14, 0xae, 0x4f, 0x59, 0x4b, 0x37, 0x4d, 0x7a, 0x35, 0xbd, 0x26, + 0x87, 0x7d, 0x54, 0x7f, 0xbb, 0x7c, 0x9a, 0x4b, 0x29, 0xff, 0xe2, 0xb2, + 0x64, 0xb1, 0x70, 0xaf, 0xea, 0xa8, 0xa4, 0x30, 0x1e, 0x77, 0xc0, 0x73, + 0x73, 0xae, 0xab, 0x29, 0x40, 0xf1, 0xbe, 0x56, 0x52, 0x89, 0x9f, 0xfd, + 0x6f, 0x27, 0xe4, 0xf4, 0x81, 0x63, 0xa1, 0x80, 0x05, 0x68, 0x55, 0xb3, + 0x09, 0xc4, 0x65, 0x57, 0xad, 0xcb, 0x52, 0xf0, 0x2f, 0x3c, 0xab, 0x14, + 0x2a, 0x8a, 0xdb, 0xff, 0xa3, 0x40, 0x6e, 0x70, 0x91, 0xaa, 0xd9, 0xcd, + 0x9c, 0x62, 0xd0, 0x01, 0xd5, 0x86, 0xc7, 0x5d, 0x44, 0x1b, 0xd7, 0x55, + 0x85, 0x32, 0x3e, 0x1f, 0x75, 0xed, 0xaf, 0x52, 0xc3, 0x91, 0x87, 0x3d, + 0x5d, 0xb1, 0x26, 0x81, 0x27, 0x31, 0xe6, 0x0a, 0x26, 0x85, 0xab, 0x2a, + 0x34, 0xe0, 0xc5, 0x48, 0x53, 0xcf, 0x02, 0x2c, 0xf5, 0x64, 0x0f, 0x67, + 0xb2, 0x38, 0x33, 0x74, 0x2b, 0x3b, 0x91, 0xf6, 0x2c, 0x34, 0x83, 0xfa, + 0xab, 0x2d, 0x8a, 0x0d, 0x70, 0xfb, 0xbe, 0xdb, 0xc4, 0x10, 0x11, 0xd5, + 0x51, 0x91, 0xa6, 0xdd, 0x56, 0x59, 0x02, 0xb6, 0x86, 0x2b, 0x4a, 0x8f, + 0x89, 0x88, 0xa0, 0x48, 0x05, 0xd5, 0x7c, 0xce, 0xa3, 0x6d, 0xb8, 0xa3, + 0x6a, 0xcc, 0x92, 0x31, 0x99, 0xd6, 0x73, 0x2c, 0x4d, 0xd1, 0xcf, 0xcf, + 0x15, 0x37, 0x85, 0xaf, 0xb5, 0x53, 0xbb, 0xb9, 0x03, 0xbb, 0x26, 0x68, + 0x89, 0x3e, 0x33, 0xbd, 0x54, 0xcc, 0x54, 0xcc, 0x3a, 0xb1, 0x67, 0x46, + 0x82, 0xf6, 0xc9, 0xe0, 0xcd, 0x63, 0x74, 0x0d, 0x14, 0xda, 0xa0, 0x4c, + 0xa6, 0x1f, 0xfe, 0xd5, 0xd3, 0x41, 0x99, 0x90, 0x48, 0x29, 0x09, 0x9c, + 0xba, 0xa0, 0x57, 0x25, 0xde, 0x1b, 0xab, 0x8e, 0x87, 0x73, 0x2f, 0xee, + 0xe5, 0x76, 0xea, 0x5a, 0x23, 0xc8, 0xba, 0x47, 0xd6, 0xd5, 0xce, 0x5e, + 0xa0, 0xc2, 0x18, 0xca, 0x92, 0x8b, 0x03, 0x02, 0x2e, 0xe2, 0x80, 0xd3, + 0x74, 0x8e, 0x45, 0xbd, 0x08, 0x90, 0x64, 0x50, 0xbd, 0xe1, 0x31, 0xd9, + 0x25, 0x49, 0x77, 0xd7, 0xf4, 0x34, 0xd0, 0xe3, 0x7e, 0xa8, 0x5c, 0x74, + 0xda, 0x72, 0x7c, 0x48, 0xdb, 0x9d, 0xdd, 0x81, 0x8c, 0x09, 0x34, 0x15, + 0x5d, 0x18, 0xba, 0x7d, 0x4b, 0x78, 0x77, 0xb5, 0x85, 0xaf, 0x08, 0x08, + 0x1a, 0x5d, 0x41, 0x15, 0xbe, 0xe6, 0x07, 0xd2, 0xdf, 0x35, 0x3c, 0x6a, + 0xc5, 0xd8, 0x9f, 0x00, 0x68, 0xb6, 0xea, 0x06, 0x52, 0x78, 0xe6, 0xa1, + 0x12, 0x63, 0x82, 0x5e, 0xe5, 0x14, 0x9b, 0x35, 0xa7, 0x38, 0xc1, 0xaa, + 0x3b, 0x56, 0x2d, 0x45, 0x58, 0x37, 0xf1, 0x34, 0x07, 0xe6, 0x3b, 0xf2, + 0x09, 0xb8, 0xa6, 0xbe, 0x6c, 0xaf, 0x52, 0x68, 0x70, 0x04, 0x80, 0x35, + 0x4f, 0x75, 0x1e, 0x28, 0xc0, 0xcb, 0xb5, 0x12, 0xea, 0x89, 0xcb, 0x05, + 0xe9, 0x2c, 0x5c, 0xe3, 0x27, 0xc9, 0x1d, 0x79, 0x13, 0xd2, 0x12, 0x39, + 0xec, 0x5d, 0xa3, 0x42, 0x0d, 0x68, 0x40, 0xc2, 0x3a, 0x9b, 0x8a, 0xb2, + 0xbd, 0xac, 0x4c, 0xf1, 0x70, 0x3f, 0xdc, 0x44, 0x23, 0xa2, 0xf6, 0x5c, + 0xaf, 0xcb, 0x20, 0x00, 0xb6, 0x31, 0x00, 0x64, 0x93, 0xb4, 0x40, 0xca, + 0xc7, 0x1d, 0x2d, 0xdc, 0x06, 0xf1, 0xfa, 0x59, 0xa8, 0x2c, 0xef, 0xd3, + 0x81, 0x96, 0xb5, 0x5d, 0xbd, 0xe2, 0xcf, 0x16, 0x4c, 0x6e, 0x74, 0x58, + 0x8a, 0x93, 0x9f, 0x07, 0xdd, 0x28, 0x0f, 0xae, 0x4d, 0xb6, 0x5e, 0x9d, + 0x60, 0x6d, 0x8c, 0x2c, 0x6a, 0x4c, 0x72, 0xd0, 0x1b, 0x4f, 0xb1, 0xcd, + 0x92, 0xd6, 0x49, 0xdc, 0x6d, 0x41, 0x31, 0x68, 0xa2, 0x71, 0x75, 0x76, + 0xa5, 0xd9, 0x56, 0x81, 0xf9, 0xd7, 0x89, 0x7e, 0x9e, 0x08, 0x91, 0xa5, + 0xb0, 0x88, 0xbd, 0x08, 0x30, 0xc0, 0x6d, 0xdc, 0xdf, 0xd9, 0x2a, 0xfd, + 0x8e, 0x82, 0x6c, 0x31, 0xd9, 0x83, 0x02, 0x99, 0x59, 0x5e, 0x99, 0xab, + 0x20, 0xce, 0x20, 0x4d, 0xd4, 0xdf, 0xa9, 0x84, 0x5e, 0x68, 0x4b, 0x17, + 0x65, 0xc2, 0x86, 0xd5, 0xf4, 0x0b, 0xc1, 0x89, 0xc6, 0x77, 0x8d, 0x23, + 0x15, 0xc6, 0x6d, 0xdd, 0x1c, 0x32, 0x37, 0x55, 0x91, 0x94, 0xe1, 0x03, + 0x4f, 0x75, 0x3a, 0xee, 0x1a, 0xcc, 0xc2, 0x5a, 0xb8, 0xe0, 0xfd, 0xf4, + 0x05, 0x51, 0x45, 0xb9, 0x23, 0x47, 0xb1, 0xd7, 0xe3, 0x51, 0xd7, 0x87, + 0x03, 0xab, 0x50, 0x55, 0x03, 0x2a, 0xf9, 0x3d, 0x7f, 0xe8, 0xe3, 0x0b, + 0xae, 0xde, 0x40, 0x18, 0xe0, 0xd7, 0x9c, 0x96, 0x81, 0x4e, 0x58, 0x46, + 0x8d, 0x4d, 0x1a, 0x50, 0xb8, 0x25, 0x56, 0xfe, 0x9b, 0x5e, 0xdf, 0x26, + 0x1e, 0x94, 0x73, 0x92, 0xb9, 0x9e, 0x5e, 0xb6, 0x2f, 0x9c, 0x29, 0x7f, + 0x60, 0xea, 0xea, 0x44, 0x06, 0xa1, 0x9b, 0xd3, 0x8d, 0xf8, 0x70, 0x78, + 0xb1, 0xa8, 0x0e, 0x9f, 0xbd, 0x1b, 0xd5, 0xbc, 0x87, 0x18, 0x7d, 0x45, + 0x7e, 0xdd, 0xe3, 0x8a, 0x43, 0x21, 0x0c, 0x60, 0xab, 0x9d, 0xd4, 0x4f, + 0xd0, 0x64, 0x48, 0x63, 0xa7, 0x90, 0xef, 0x19, 0x8b, 0x87, 0xfc, 0xe8, + 0x7d, 0x57, 0x71, 0x51, 0xa2, 0xb5, 0x09, 0x08, 0x64, 0x28, 0xc6, 0x99, + 0x79, 0xe6, 0x8d, 0xb1, 0xd3, 0x08, 0x2e, 0xae, 0x7c, 0xb6, 0x6a, 0xc2, + 0xef, 0xb9, 0x92, 0x89, 0x25, 0x60, 0x0e, 0x8b, 0x9b, 0x22, 0x78, 0x24, + 0x7b, 0xc3, 0x4e, 0xf1, 0x41, 0x51, 0xa4, 0xdb, 0xe8, 0x38, 0x3c, 0x36, + 0xa9, 0xc9, 0xd5, 0x0a, 0x4d, 0x80, 0x3a, 0x9e, 0x7b, 0x5c, 0x5e, 0x68, + 0x36, 0x8b, 0x27, 0x73, 0x27, 0x04, 0xdc, 0xea, 0x26, 0x0d, 0xd0, 0x68, + 0x24, 0xb7, 0x40, 0xea, 0x8e, 0xf1, 0x4b, 0xa4, 0x7a, 0xec, 0x22, 0x5a, + 0x2f, 0x0a, 0x62, 0x45, 0xc0, 0x00, 0x82, 0xcf, 0xb9, 0x31, 0xd2, 0x9c, + 0xbc, 0x5b, 0xa3, 0x57, 0xb2, 0x21, 0x5b, 0x45, 0x5c, 0x65, 0x1c, 0x3a, + 0xa0, 0x40, 0x73, 0x70, 0x9f, 0x11, 0x78, 0xb1, 0xca, 0x60, 0x01, 0x3f, + 0x30, 0x36, 0xe0, 0xdf, 0xad, 0x88, 0x57, 0x8e, 0x10, 0xc9, 0xa7, 0x87, + 0x83, 0x3d, 0x8e, 0xdb, 0x48, 0x13, 0x52, 0x33, 0x87, 0x43, 0xb7, 0x89, + 0x65, 0xdf, 0x52, 0x1c, 0xd0, 0x77, 0xb7, 0x1f, 0xfc, 0x78, 0x32, 0x2b, + 0x97, 0x75, 0xbb, 0x38, 0x8e, 0x15, 0xaf, 0x50, 0xac, 0x79, 0xe6, 0xe1, + 0xdd, 0x7b, 0x44, 0x9b, 0x73, 0xe9, 0xa7, 0x34, 0x42, 0x30, 0x74, 0x93, + 0xb1, 0xdf, 0x37, 0x03, 0x4d, 0x8d, 0x92, 0xfb, 0xde, 0x12, 0x92, 0xa9, + 0x41, 0xc6, 0x16, 0x34, 0x99, 0x27, 0x7d, 0x76, 0x59, 0xcb, 0x47, 0x27, + 0x24, 0xb0, 0x4b, 0xc8, 0x8d, 0x32, 0x97, 0xd4, 0x98, 0xb1, 0xbc, 0x4c, + 0x50, 0xed, 0xff, 0x48, 0x08, 0x5d, 0xa1, 0x48, 0x86, 0x71, 0x92, 0x15, + 0xd5, 0x52, 0x52, 0xed, 0xdc, 0x7f, 0x14, 0x8d, 0x34, 0xfa, 0x14, 0xa2, + 0x6c, 0x0b, 0x2a, 0xd1, 0xee, 0xc0, 0x7b, 0xfb, 0x76, 0xef, 0xee, 0x2a, + 0xd3, 0xed, 0x20, 0x8a, 0x56, 0x27, 0x73, 0x6b, 0x98, 0x65, 0x64, 0x73, + 0xae, 0xd5, 0x01, 0xae, 0xce, 0x1c, 0x29, 0xcb, 0x8a, 0x74, 0xfb, 0x55, + 0x1a, 0x1c, 0xb3, 0x5e, 0xf1, 0x44, 0x5f, 0x76, 0x41, 0x47, 0xd0, 0x29, + 0xb7, 0x72, 0xd7, 0x27, 0x08, 0xf9, 0x0d, 0xbe, 0x8b, 0x52, 0xa9, 0x16, + 0xaa, 0x89, 0xd9, 0x74, 0x1c, 0xc1, 0x85, 0xb3, 0x04, 0x10, 0x69, 0xe1, + 0x1a, 0x88, 0x1e, 0xd6, 0x0b, 0x90, 0xc4, 0x75, 0x2e, 0xca, 0x8b, 0x8d, + 0xf2, 0x67, 0xdc, 0x07, 0x42, 0x4c, 0xe3, 0x89, 0x4d, 0x06, 0x8a, 0x87, + 0x3a, 0xe7, 0xb8, 0x9d, 0x62, 0x32, 0x1c, 0xfa, 0x25, 0xd8, 0xd7, 0x5e, + 0xe1, 0xa8, 0xa6, 0x7b, 0xc1, 0xcf, 0x51, 0x72, 0xd3, 0xf3, 0x07, 0x13, + 0x24, 0x90, 0x2f, 0xd9, 0x98, 0x4d, 0x5c, 0x77, 0xce, 0xaa, 0xe5, 0x11, + 0x29, 0xc0, 0x8e, 0x67, 0xc5, 0x6f, 0xf1, 0xa4, 0x13, 0xb3, 0xc4, 0xe7, + 0x77, 0x8d, 0xc6, 0x02, 0x15, 0x51, 0x32, 0xb6, 0x4f, 0xd1, 0x34, 0xa0, + 0x12, 0x48, 0x15, 0x1a, 0xee, 0xba, 0xf5, 0x82, 0x51, 0x79, 0xa0, 0x57, + 0x67, 0xd4, 0x7b, 0x63, 0x73, 0x70, 0x3d, 0x1b, 0x80, 0x8c, 0x28, 0xdf, + 0x53, 0xcc, 0xf4, 0xe0, 0x91, 0xd6, 0x7a, 0x8d, 0xc9, 0x19, 0x4d, 0x9e, + 0xde, 0x34, 0x48, 0x01, 0x1f, 0xdc, 0xe3, 0x58, 0xd1, 0x3b, 0xe5, 0xff, + 0x57, 0x2e, 0xee, 0x96, 0x24, 0x0f, 0xbc, 0xfe, 0x2a, 0xc9, 0x9e, 0xdd, + 0xce, 0x31, 0xbd, 0xb6, 0x8f, 0xa8, 0x6b, 0x98, 0x4c, 0xef, 0x26, 0x3d, + 0x3a, 0xc1, 0x5d, 0xb5, 0x7f, 0x5c, 0x6c, 0x22, 0x2e, 0x03, 0x47, 0xf9, + 0x0c, 0xfa, 0x44, 0x60, 0xf8, 0x2b, 0x03, 0x08, 0x36, 0xff, 0x0d, 0xf6, + 0x72, 0xb9, 0x5e, 0x6a, 0x25, 0xac, 0x6a, 0xe1, 0x94, 0x27, 0x45, 0x7d, + 0x35, 0xf1, 0x5d, 0x2f, 0x35, 0x08, 0x58, 0x59, 0x61, 0x1d, 0x6f, 0x79, + 0x19, 0x88, 0xaa, 0xc7, 0x59, 0x25, 0xae, 0xcd, 0xce, 0x4f, 0x63, 0x91, + 0x47, 0x7b, 0x8c, 0xa3, 0x1e, 0xe1, 0x0b, 0xc8, 0x5d, 0xbe, 0xfd, 0x29, + 0x39, 0xfe, 0x79, 0x5f, 0xf6, 0x5f, 0x01, 0x05, 0x91, 0xdc, 0x4e, 0xa4, + 0xc5, 0x62, 0x91, 0x97, 0x9a, 0x47, 0x11, 0x2e, 0xf4, 0x99, 0xf8, 0x5e, + 0x3d, 0xc2, 0x58, 0x40, 0x3a, 0x82, 0xed, 0x25, 0x57, 0xd6, 0x08, 0xd7, + 0xe7, 0x54, 0xab, 0x6c, 0x36, 0xda, 0xf1, 0x3e, 0x1c, 0x8a, 0xb3, 0x28, + 0xe6, 0x77, 0xe2, 0x05, 0xca, 0x82, 0xf9, 0x40, 0x39, 0x15, 0xed, 0x0f, + 0xd2, 0x02, 0x1e, 0x7f, 0x88, 0xcc, 0x5c, 0xe4, 0x57, 0x7f, 0x1a, 0xcf, + 0x43, 0xb3, 0x49, 0x0b, 0x23, 0xca, 0x67, 0x35, 0x8e, 0xf7, 0x9c, 0xce, + 0x1e, 0xa0, 0x63, 0xd4, 0x54, 0x8a, 0xd8, 0x01, 0x8d, 0xc9, 0x91, 0x20, + 0xe4, 0x19, 0x7b, 0x99, 0x0c, 0xe5, 0x25, 0x4b, 0x8e, 0xc8, 0x55, 0x08, + 0xc8, 0x89, 0x7c, 0x1f, 0x29, 0x38, 0x3f, 0x48, 0x9d, 0xa5, 0x10, 0x14, + 0xb8, 0x34, 0xd9, 0x7c, 0xd0, 0x69, 0x75, 0x69, 0xf2, 0xcc, 0x65, 0x36, + 0x52, 0x0a, 0x70, 0x1a, 0x39, 0x89, 0x1f, 0x91, 0x29, 0x72, 0x61, 0xc5, + 0x24, 0x2e, 0x13, 0x9e, 0xc1, 0x2d, 0xe4, 0x12, 0x2b, 0xbd, 0xb8, 0x50, + 0xf4, 0x0d, 0xcd, 0x40, 0xb8, 0x64, 0x6b, 0x8d, 0x45, 0x15, 0x1d, 0x96, + 0xc6, 0xe5, 0xe8, 0xf4, 0x97, 0xc0, 0xd9, 0x75, 0x72, 0xca, 0x6f, 0xfc, + 0x05, 0x22, 0xb9, 0x92, 0xbd, 0x77, 0xe5, 0x81, 0x4d, 0xd8, 0x0e, 0x70, + 0xf6, 0xc7, 0xd1, 0xcd, 0x29, 0xd1, 0x9d, 0x73, 0x30, 0xc0, 0xd0, 0x77, + 0x69, 0x18, 0x76, 0x47, 0xd1, 0x02, 0x62, 0x8b, 0xb7, 0xb2, 0xc1, 0xe3, + 0xc7, 0x65, 0x53, 0x15, 0x6f, 0xac, 0xb4, 0xb6, 0xc4, 0x82, 0xce, 0x70, + 0x46, 0x5e, 0x67, 0xb8, 0x0e, 0x07, 0xc6, 0x17, 0xc9, 0x6e, 0xc0, 0x56, + 0xf0, 0x29, 0xad, 0x05, 0x7f, 0x07, 0x5a, 0xef, 0xb1, 0xa3, 0xa4, 0xcb, + 0x0d, 0x77, 0xeb, 0x64, 0xee, 0x91, 0xda, 0xe5, 0x9b, 0xe4, 0x5f, 0xab, + 0x8f, 0x8d, 0x04, 0xef, 0x9f, 0xcd, 0x0b, 0xc0, 0x5a, 0x8c, 0x17, 0x97, + 0x97, 0xfc, 0x20, 0x41, 0x5a, 0x49, 0x0f, 0xbd, 0x3a, 0xaa, 0x95, 0xac, + 0x67, 0x7b, 0x0b, 0xe3, 0x5b, 0x21, 0x35, 0xa3, 0x12, 0xa5, 0x45, 0x44, + 0x5b, 0x29, 0x8d, 0xe4, 0xfb, 0x0b, 0x9c, 0xae, 0x5e, 0x95, 0x0b, 0xc7, + 0x5c, 0x84, 0xbf, 0x5c, 0xcd, 0x39, 0xf8, 0x20, 0xe5, 0x99, 0xde, 0x44, + 0xa2, 0x9b, 0xbd, 0xc6, 0x74, 0x75, 0x23, 0xff, 0xe4, 0x80, 0xa9, 0x75, + 0xcf, 0x4d, 0xbf, 0x45, 0x38, 0xae, 0x18, 0x98, 0xfc, 0x45, 0xfa, 0x09, + 0xa9, 0x9d, 0xa7, 0x92, 0x58, 0xb6, 0x76, 0x79, 0xfa, 0xcc, 0x44, 0x7e, + 0x6f, 0xb2, 0xa7, 0x8a, 0x6a, 0x62, 0x79, 0x5c, 0x80, 0x7c, 0x34, 0x15, + 0x09, 0x85, 0xc4, 0x09, 0xb8, 0x2c, 0x9a, 0x36, 0xbe, 0x18, 0x94, 0x87, + 0x0f, 0xd7, 0x84, 0x15, 0x9f, 0xd8, 0x11, 0x22, 0x37, 0xd3, 0xf6, 0xd5, + 0xf2, 0x55, 0xaf, 0xd8, 0xb1, 0x09, 0x5e, 0x34, 0xe2, 0xf4, 0x6d, 0xbf, + 0x8a, 0xb2, 0xbd, 0xc0, 0xd8, 0xaa, 0x2a, 0x13, 0xb7, 0x6e, 0x64, 0x9b, + 0x1f, 0x9f, 0x6e, 0x8f, 0x44, 0xdb, 0xb8, 0x66, 0x2e, 0x57, 0x50, 0xa1, + 0x3e, 0x3c, 0x9c, 0x61, 0xef, 0xb5, 0xa7, 0x4d, 0xa0, 0xbe, 0x4f, 0x80, + 0x83, 0x4a, 0x59, 0xc0, 0x9b, 0x1b, 0x6d, 0x7b, 0xfa, 0xef, 0x62, 0x92, + 0x0d, 0x0b, 0x9b, 0xe6, 0x5e, 0xc2, 0x84, 0x09, 0x9e, 0x81, 0xc0, 0x33, + 0xb9, 0x05, 0xae, 0xed, 0x73, 0x9c, 0xc9, 0x48, 0x92, 0x3a, 0xff, 0x59, + 0x7e, 0x53, 0xd9, 0xc1, 0x9a, 0xdd, 0xf1, 0x69, 0x02, 0x8a, 0x7a, 0xda, + 0x6e, 0xcc, 0xf5, 0x98, 0xe0, 0x95, 0xb0, 0x17, 0x36, 0x7b, 0x6a, 0xd8, + 0x69, 0x17, 0xad, 0x46, 0xdf, 0x9e, 0x09, 0x16, 0xb9, 0x71, 0x64, 0xc8, + 0xd9, 0xd7, 0xa6, 0x38, 0x9f, 0x3a, 0x02, 0xf6, 0x1e, 0x28, 0xf6, 0xe8, + 0x18, 0x81, 0x9e, 0x1a, 0xd6, 0x49, 0x73, 0x8b, 0x9e, 0x15, 0xad, 0x82, + 0x7c, 0x63, 0x74, 0x63, 0xd0, 0x5d, 0xa8, 0xf0, 0x45, 0xed, 0xb1, 0x59, + 0x7c, 0x65, 0xa6, 0x79, 0x07, 0xec, 0x5c, 0x86, 0x80, 0x3e, 0x4e, 0xb3, + 0xc8, 0x80, 0xb4, 0xdb, 0x57, 0xaa, 0xb7, 0x17, 0x9f, 0xc2, 0xc8, 0x40, + 0xf2, 0x00, 0xe9, 0xc5, 0x70, 0x7b, 0xad, 0xa6, 0xf6, 0x69, 0xf2, 0x34, + 0xb7, 0x6d, 0xf1, 0xcd, 0x7b, 0x5f, 0xb7, 0x7a, 0x70, 0x7e, 0x62, 0x30, + 0xa2, 0xa4, 0x92, 0xbe, 0x2f, 0x9f, 0xc3, 0x4d, 0x83, 0x53, 0x23, 0x9f, + 0x39, 0x94, 0x6b, 0x5f, 0xd1, 0x03, 0x32, 0xb3, 0x85, 0x95, 0x85, 0xd0, + 0x37, 0x36, 0xed, 0x9a, 0x9f, 0xf1, 0xbb, 0x27, 0xa4, 0xbb, 0xfa, 0xf2, + 0x02, 0x13, 0x7b, 0x21, 0x50, 0xfc, 0x7f, 0x8b, 0x89, 0xee, 0x08, 0x20, + 0xc9, 0x18, 0x9a, 0x31, 0xf4, 0x7c, 0x2e, 0xbc, 0x4b, 0xb1, 0x04, 0xfc, + 0x01, 0xc6, 0xc6, 0x87, 0x75, 0x73, 0x29, 0x76, 0x24, 0x2e, 0x17, 0x87, + 0x64, 0xc8, 0xad, 0xed, 0x4d, 0x68, 0x91, 0xd6, 0x5f, 0x8d, 0xda, 0x9e, + 0xf7, 0x12, 0x05, 0x4f, 0xe6, 0x6c, 0xa3, 0x4f, 0x58, 0x69, 0x74, 0xa6, + 0x99, 0x56, 0x99, 0x22, 0x92, 0x57, 0x1a, 0x38, 0x12, 0x32, 0x35, 0x1c, + 0xab, 0x57, 0x5b, 0x74, 0x36, 0xb7, 0xf0, 0xab, 0x1d, 0x6a, 0x41, 0xc0, + 0x7c, 0xd8, 0x0b, 0x4e, 0xf8, 0x3a, 0x11, 0x6a, 0x75, 0x17, 0x66, 0x37, + 0xf9, 0xbe, 0x92, 0x43, 0x76, 0x31, 0xfe, 0x35, 0xfe, 0x47, 0xc1, 0x5d, + 0x79, 0x94, 0x46, 0x71, 0xcb, 0xbd, 0x73, 0x9e, 0x8a, 0x4b, 0x1c, 0x68, + 0x47, 0x5b, 0x77, 0x5c, 0x78, 0x5d, 0x71, 0x0a, 0xfd, 0xec, 0x41, 0x34, + 0xa4, 0xeb, 0x50, 0xf1, 0x15, 0xa4, 0xda, 0x39, 0x74, 0x76, 0xf0, 0x3b, + 0x6c, 0x25, 0xf6, 0xb2, 0x87, 0x17, 0x38, 0x39, 0x7c, 0x35, 0x45, 0x06, + 0xec, 0xe5, 0x7e, 0xbb, 0x55, 0x23, 0x12, 0x02, 0xa8, 0x19, 0x33, 0x2c, + 0xe8, 0x94, 0xd2, 0xdf, 0x3d, 0xd6, 0xa7, 0xee, 0xef, 0xb4, 0xe3, 0x3d, + 0x9e, 0x81, 0xcc, 0x61, 0xf8, 0x04, 0xe6, 0x38, 0x4b, 0xd9, 0x1e, 0xe4, + 0x27, 0x0a, 0x45, 0x52, 0xf8, 0x80, 0x61, 0x05, 0x25, 0x7a, 0xc2, 0xf4, + 0x6a, 0x80, 0x19, 0x9c, 0xc0, 0xb5, 0x7a, 0xd5, 0x94, 0xcf, 0x86, 0xa5, + 0x19, 0xe2, 0x27, 0x1e, 0x42, 0x9f, 0x09, 0xb6, 0xa7, 0xea, 0xf4, 0x28, + 0x54, 0x2d, 0x6b, 0x7f, 0xd9, 0xa9, 0x70, 0x0a, 0xcf, 0x70, 0xe8, 0x96, + 0x7c, 0x39, 0x7b, 0xe8, 0xa8, 0x60, 0x8a, 0xfa, 0xe2, 0x5c, 0xcd, 0x34, + 0x43, 0xf9, 0x41, 0x1f, 0x45, 0x1b, 0x61, 0x7a, 0x04, 0x57, 0x84, 0x7b, + 0xe9, 0x53, 0x8c, 0x15, 0x61, 0x78, 0x5a, 0xa4, 0xab, 0xe0, 0x1f, 0xce, + 0x05, 0xe9, 0x8f, 0x95, 0xb4, 0xfd, 0x3e, 0x4e, 0x96, 0x31, 0xee, 0x58, + 0x4f, 0x4e, 0x79, 0xf7, 0x32, 0x30, 0xc9, 0x6c, 0x63, 0xd3, 0xfd, 0xb0, + 0xe2, 0x99, 0x1e, 0x30, 0x44, 0x94, 0x49, 0x88, 0x02, 0xc1, 0x04, 0x1c, + 0x50, 0x3d, 0x45, 0x30, 0x94, 0xe9, 0xef, 0x7a, 0xf1, 0x30, 0x88, 0xab, + 0x40, 0xae, 0x69, 0x47, 0x48, 0x30, 0xc7, 0x2d, 0x40, 0x7f, 0x47, 0x02, + 0xee, 0x90, 0xc6, 0xb3, 0xea, 0x8f, 0x6c, 0x3f, 0x06, 0x6b, 0x14, 0x89, + 0xef, 0x70, 0x60, 0x74, 0xa7, 0x12, 0xb1, 0x32, 0xb9, 0xf4, 0x24, 0xa9, + 0x59, 0xf0, 0xa5, 0xe0, 0x61, 0x0d, 0xfd, 0x35, 0x7a, 0x76, 0x4c, 0x68, + 0x26, 0xa6, 0x1b, 0x59, 0x5f, 0x92, 0xb1, 0x66, 0x95, 0x7f, 0xcd, 0x54, + 0xe6, 0x2c, 0x27, 0x8c, 0xdd, 0xa3, 0x7c, 0x03, 0x50, 0x51, 0xc4, 0x2e, + 0xa4, 0x53, 0x69, 0x63, 0x54, 0x39, 0x18, 0x1a, 0xcf, 0x7c, 0x2d, 0x08, + 0x3a, 0x0a, 0xf9, 0x5b, 0xe7, 0x8e, 0x7a, 0x40, 0x21, 0x6a, 0x27, 0xb0, + 0xb7, 0x04, 0xf5, 0x61, 0xfe, 0xc1, 0xfe, 0x44, 0xdf, 0xdd, 0x86, 0x7a, + 0x6e, 0x96, 0x57, 0x2d, 0xa5, 0xed, 0x41, 0x05, 0xa3, 0x1a, 0x15, 0x13, + 0x20, 0x20, 0x2a, 0x34, 0x9e, 0x33, 0xc5, 0x8b, 0x5b, 0x30, 0x74, 0x46, + 0x16, 0x95, 0xe6, 0x06, 0x9f, 0xd2, 0xf0, 0x46, 0xf7, 0x34, 0x34, 0x18, + 0x42, 0xb6, 0x78, 0x5a, 0x72, 0xdc, 0x94, 0xfc, 0x7c, 0xc6, 0x06, 0x83, + 0x85, 0x13, 0x5e, 0x4e, 0x07, 0x8c, 0xdf, 0x99, 0x0b, 0xf4, 0x51, 0xf0, + 0x68, 0x24, 0x26, 0xc0, 0x16, 0x82, 0xd2, 0x81, 0xac, 0xba, 0x5b, 0x4f, + 0xc1, 0x69, 0x0f, 0xca, 0x5e, 0x34, 0x1a, 0x45, 0xb5, 0x8b, 0x07, 0x42, + 0xff, 0xbb, 0xd9, 0x45, 0xa8, 0xed, 0x03, 0xb7, 0x54, 0xa1, 0x41, 0xf3, + 0xfd, 0xbe, 0xbe, 0x29, 0x8f, 0x07, 0x06, 0x52, 0xee, 0x19, 0x99, 0xcc, + 0x2c, 0xb2, 0x48, 0x76, 0xca, 0x9e, 0xc8, 0xfc, 0x1d, 0x8b, 0x7f, 0xd8, + 0x19, 0x09, 0xaf, 0x95, 0xd2, 0x0e, 0x87, 0x4d, 0x4e, 0xc3, 0x86, 0xda, + 0x6d, 0x36, 0xdd, 0xf4, 0x92, 0x84, 0x1c, 0x9b, 0xa0, 0x74, 0xc5, 0x8e, + 0xcb, 0xc6, 0x67, 0xc1, 0x76, 0x85, 0x14, 0x4d, 0x79, 0x15, 0xd4, 0xed, + 0xcf, 0xb2, 0x73, 0x7c, 0x20, 0x8b, 0x2b, 0xfc, 0x80, 0xad, 0x76, 0x54, + 0x66, 0x36, 0x1f, 0xd6, 0xe9, 0xba, 0x5a, 0x5e, 0xd3, 0xf5, 0x6f, 0x86, + 0x0d, 0x3f, 0xaf, 0x53, 0x7a, 0xd4, 0x68, 0x97, 0x74, 0x04, 0x8d, 0xd9, + 0xa9, 0xad, 0x3e, 0x2f, 0x9d, 0x60, 0x1f, 0x04, 0xca, 0x2b, 0xeb, 0x01, + 0x2e, 0xbf, 0x35, 0xc6, 0x38, 0xbf, 0xe7, 0x47, 0xa3, 0x9b, 0x32, 0xa4, + 0x30, 0xf3, 0xb5, 0x2d, 0x06, 0xf5, 0xed, 0xd5, 0xf8, 0x58, 0xda, 0x4e, + 0xd7, 0x8a, 0x68, 0x0c, 0xb9, 0x24, 0x6f, 0x86, 0x21, 0xcf, 0x57, 0x1b, + 0x53, 0x09, 0xdb, 0x4b, 0xf1, 0xbc, 0xaa, 0x1f, 0x8c, 0x12, 0x6d, 0x17, + 0xd2, 0x96, 0xd3, 0x32, 0x57, 0x98, 0x5a, 0x58, 0xf6, 0x16, 0x33, 0x7c, + 0x64, 0x38, 0xce, 0x7d, 0xda, 0x6a, 0x3c, 0xb8, 0xf2, 0xb1, 0xcb, 0x03, + 0x23, 0xf6, 0x8b, 0x0d, 0xc7, 0x2e, 0xde, 0x93, 0x58, 0x43, 0x2a, 0x81, + 0x40, 0x89, 0x82, 0x17, 0x87, 0x10, 0xda, 0x31, 0x2d, 0x1b, 0xd5, 0xed, + 0x8f, 0x00, 0x8c, 0x63, 0xaa, 0x1f, 0xfe, 0xef, 0xe0, 0x06, 0xf1, 0x18, + 0x4a, 0xb1, 0x9b, 0x37, 0x5d, 0x15, 0x25, 0x42, 0xa5, 0x1f, 0x1d, 0x55, + 0x02, 0xc3, 0xd6, 0xeb, 0x32, 0x9d, 0x58, 0x9d, 0xa4, 0xc8, 0x42, 0x9c, + 0x15, 0xd4, 0xe6, 0x30, 0xde, 0x63, 0xd2, 0x7a, 0x32, 0x76, 0xe1, 0x02, + 0x4e, 0x58, 0x9b, 0x62, 0x6a, 0x8d, 0xc1, 0xc9, 0x03, 0xd2, 0x90, 0x54, + 0x9b, 0xec, 0x6f, 0xcd, 0x5c, 0x09, 0x39, 0x5e, 0xde, 0x55, 0x75, 0x93, + 0x3c, 0x9c, 0xea, 0x53, 0xf5, 0xb7, 0xcc, 0xad, 0x40, 0xe6, 0xac, 0x6e, + 0x26, 0xf1, 0x0a, 0x3d, 0x63, 0xd2, 0xdf, 0x8d, 0x55, 0x12, 0xd9, 0x1b, + 0xef, 0xc5, 0x76, 0x41, 0xea, 0xd8, 0xfc, 0x8a, 0xb7, 0xb3, 0x9a, 0x86, + 0xfe, 0x58, 0xfc, 0xd6, 0xe8, 0x48, 0xad, 0x0a, 0x8c, 0xbf, 0x6c, 0x53, + 0x3a, 0x9a, 0x7c, 0x72, 0x6b, 0xff, 0x57, 0xe0, 0x30, 0x5e, 0x18, 0x10, + 0xae, 0xd2, 0x9e, 0xc0, 0xd3, 0x55, 0x70, 0x6c, 0xf1, 0xa2, 0xd9, 0xe1, + 0x6b, 0xdf, 0x88, 0x95, 0xc4, 0x43, 0x5d, 0x98, 0xb4, 0xb8, 0xa5, 0x81, + 0x3a, 0x54, 0x48, 0x02, 0xcb, 0xcd, 0x6e, 0xef, 0xf4, 0x9c, 0xbb, 0x88, + 0x75, 0xb3, 0x05, 0x15, 0x44, 0x51, 0x73, 0xa8, 0x9d, 0xc7, 0xc6, 0x54, + 0x8e, 0x82, 0x5a, 0x16, 0x70, 0x0b, 0xa1, 0xee, 0xa7, 0x78, 0xbf, 0xfe, + 0x49, 0x3b, 0x92, 0x07, 0xe4, 0x13, 0xba, 0xdd, 0x3d, 0x97, 0x64, 0x85, + 0x6b, 0xcc, 0xeb, 0x14, 0x50, 0x28, 0x2d, 0x01, 0x6b, 0xe3, 0x4f, 0x9e, + 0x80, 0x0e, 0x39, 0x9d, 0x84, 0x72, 0xc0, 0x7c, 0x4a, 0x4e, 0x8f, 0xe1, + 0xac, 0xc8, 0x64, 0xb5, 0x10, 0xc1, 0x82, 0x65, 0xf5, 0xaa, 0x9c, 0xb3, + 0xf0, 0xcf, 0xbd, 0xa3, 0xa7, 0xf3, 0xf7, 0x2f, 0xa9, 0xb3, 0xdf, 0x11, + 0x8c, 0x9b, 0xdc, 0x66, 0x35, 0x6c, 0x37, 0x30, 0x35, 0xee, 0x3b, 0x06, + 0x12, 0xae, 0x2c, 0xb2, 0x05, 0x5c, 0x06, 0x7c, 0xac, 0xb4, 0xad, 0x11, + 0x47, 0xfc, 0xfb, 0x89, 0x91, 0x36, 0xca, 0xd0, 0x1c, 0xe8, 0xa5, 0x85, + 0xcf, 0x8c, 0x38, 0x03, 0x03, 0xcd, 0xd2, 0x4c, 0xde, 0xbb, 0xbd, 0x55, + 0x39, 0x91, 0xca, 0x46, 0xcd, 0x7d, 0x02, 0xa6, 0x3e, 0x13, 0xfa, 0x7f, + 0xd1, 0xbf, 0x61, 0xd3, 0xaf, 0xd8, 0xe7, 0xca, 0xff, 0x48, 0x78, 0x28, + 0xd0, 0x0e, 0xb5, 0x8f, 0x13, 0x1b, 0x1b, 0xd9, 0x07, 0x07, 0xed, 0x8a, + 0x5e, 0x90, 0xa0, 0x7f, 0x7d, 0xbb, 0x1e, 0x49, 0x43, 0x80, 0x25, 0xed, + 0x37, 0xf1, 0x7d, 0xbf, 0xdf, 0x9d, 0x1f, 0xd5, 0xb8, 0x5a, 0xfa, 0xad, + 0xde, 0xb0, 0xeb, 0x6e, 0xcd, 0x48, 0xe6, 0x88, 0xc3, 0x66, 0x9d, 0xb2, + 0x20, 0xca, 0x5a, 0x2f, 0x95, 0x72, 0xd6, 0xf0, 0x83, 0x5e, 0xd2, 0x5e, + 0x28, 0x9b, 0xcb, 0xd5, 0xc9, 0xac, 0xbd, 0xa7, 0x73, 0x56, 0xad, 0x6b, + 0xc1, 0x4a, 0x0d, 0x13, 0xe8, 0x52, 0x9a, 0x4b, 0xf3, 0x76, 0xbf, 0x0e, + 0xf6, 0xa2, 0x03, 0x81, 0x69, 0xca, 0x7d, 0xf1, 0xf5, 0x51, 0xe1, 0xf7, + 0x98, 0x0c, 0x77, 0xd9, 0xe0, 0x1c, 0x23, 0xf5, 0x3c, 0x57, 0x8c, 0xe6, + 0x56, 0xbc, 0x71, 0xe5, 0xa7, 0xf7, 0x8a, 0x57, 0x30, 0xbf, 0xf0, 0xca, + 0x85, 0xc3, 0x1a, 0x6a, 0x8e, 0x81, 0x5c, 0xcd, 0x4c, 0x9a, 0x69, 0xe1, + 0xee, 0x58, 0x05, 0x43, 0x52, 0x4f, 0x86, 0x0c, 0xc9, 0x66, 0xe6, 0x7f, + 0x64, 0x33, 0xc9, 0xf7, 0x02, 0x57, 0x39, 0xc2, 0x98, 0x5d, 0xe6, 0xd3, + 0xa8, 0x33, 0x12, 0x59, 0x6a, 0xd1, 0x17, 0xa8, 0x0e, 0x10, 0x05, 0x41, + 0x87, 0xe4, 0x0b, 0x6e, 0xa0, 0x79, 0x85, 0x8b, 0x75, 0x0c, 0xe3, 0x6a, + 0xf7, 0xdf, 0xdf, 0x9f, 0x3f, 0x3e, 0xd1, 0x4c, 0x5d, 0x3f, 0xc7, 0xc6, + 0x00, 0x1f, 0xf5, 0xf4, 0x42, 0x6f, 0xc9, 0xbb, 0x3b, 0x59, 0x99, 0x59, + 0x54, 0x2c, 0xf8, 0x59, 0xce, 0x83, 0xbb, 0xe2, 0xd2, 0x92, 0xd7, 0xea, + 0x9d, 0xef, 0x6c, 0x21, 0x25, 0x0d, 0xff, 0x41, 0x11, 0xe2, 0xb0, 0xe7, + 0x85, 0x7a, 0x48, 0x35, 0xeb, 0x56, 0xe5, 0x03, 0x74, 0x16, 0x82, 0xf8, + 0x4a, 0x00, 0x82, 0x2c, 0x25, 0x54, 0x92, 0xcb, 0xf3, 0x96, 0x66, 0x25, + 0xd9, 0x8c, 0xd5, 0xa0, 0xe0, 0xae, 0x68, 0x89, 0x01, 0x78, 0x99, 0xa4, + 0x1a, 0xed, 0xc2, 0x4b, 0x1e, 0xc9, 0xaf, 0xbe, 0xfd, 0x7b, 0xbe, 0x93, + 0xbf, 0xe3, 0x81, 0xb8, 0x0d, 0x8b, 0xb6, 0x2d, 0x4e, 0x05, 0x18, 0xd8, + 0xe9, 0x13, 0xc4, 0xe3, 0x0d, 0x4d, 0x86, 0x4a, 0x95, 0x86, 0xcb, 0x63, + 0xed, 0x7e, 0x5a, 0x95, 0x52, 0x71, 0x49, 0x77, 0xe6, 0x49, 0xfd, 0x05, + 0xcb, 0x12, 0x2d, 0x99, 0x8b, 0xaf, 0x2f, 0xc8, 0x65, 0x38, 0xce, 0xe6, + 0xfa, 0x21, 0x22, 0x50, 0x22, 0xbc, 0x97, 0x5f, 0xef, 0xc9, 0x43, 0xd2, + 0x21, 0x4e, 0x1a, 0x39, 0xdc, 0x2f, 0x18, 0x97, 0xf8, 0x9a, 0x7b, 0xd8, + 0xf8, 0x23, 0xb6, 0xbb, 0x22, 0x7d, 0xec, 0xe6, 0xc2, 0xd7, 0x0f, 0x88, + 0x0c, 0xaa, 0x69, 0x60, 0x5c, 0x5a, 0xe1, 0xc7, 0x11, 0x39, 0x44, 0x31, + 0x8a, 0xc1, 0x51, 0xd2, 0xb1, 0x4f, 0x2a, 0xeb, 0xf8, 0xaa, 0x75, 0x74, + 0x5a, 0x5c, 0x69, 0xc9, 0xf1, 0xc3, 0xe4, 0x06, 0x85, 0x3c, 0xcd, 0x5c, + 0x09, 0xf3, 0x5d, 0xab, 0xdd, 0x81, 0x58, 0x9d, 0xe4, 0xbf, 0xa6, 0x1e, + 0xde, 0x4e, 0x75, 0x90, 0x77, 0x2b, 0x61, 0xf2, 0xc2, 0xe1, 0x15, 0xb2, + 0x1d, 0x58, 0x72, 0x87, 0x60, 0x30, 0xc6, 0xad, 0x1b, 0x7f, 0x06, 0xf4, + 0x1f, 0x8d, 0xfb, 0x7c, 0xdb, 0xaa, 0xba, 0x72, 0xca, 0xbf, 0xd0, 0x69, + 0xa4, 0xf1, 0xc0, 0xf2, 0x3d, 0xb3, 0xdd, 0xc9, 0x94, 0x87, 0xc9, 0x3b, + 0xb3, 0x1e, 0xef, 0x0a, 0xe9, 0xc7, 0x27, 0xd9, 0x08, 0xc5, 0x48, 0xb9, + 0x83, 0x0c, 0xfd, 0xb9, 0x17, 0xe8, 0x52, 0xda, 0xe3, 0x02, 0xd5, 0x01, + 0x1c, 0x09, 0x95, 0x4a, 0x0c, 0x00, 0x81, 0xee, 0x09, 0xbe, 0xbb, 0x04, + 0xd8, 0x43, 0xb6, 0xea, 0x71, 0x3a, 0x44, 0xbe, 0x30, 0x5e, 0x63, 0x8e, + 0x12, 0xfa, 0x05, 0x1c, 0x5b, 0x29, 0xda, 0xee, 0x83, 0x9b, 0x6f, 0x4d, + 0x53, 0x0e, 0x6b, 0x32, 0x34, 0xfe, 0xe9, 0x99, 0x4e, 0x2f, 0x27, 0xae, + 0xf0, 0x1e, 0xf1, 0xb1, 0x06, 0xdb, 0x99, 0x6f, 0x00, 0x92, 0x6c, 0x4f, + 0xed, 0x9d, 0xfa, 0x21, 0x3f, 0xc1, 0x4a, 0xb8, 0x17, 0xd6, 0x66, 0x55, + 0xd0, 0x73, 0xd0, 0xd8, 0xbb, 0x34, 0xf3, 0x69, 0x3e, 0xdc, 0x41, 0x44, + 0x7c, 0xb5, 0x0a, 0x3b, 0x51, 0xc1, 0x10, 0x45, 0x1b, 0x82, 0xa5, 0x51, + 0x3f, 0x84, 0x36, 0x9f, 0x54, 0x7a, 0x2c, 0x15, 0x58, 0xa3, 0x5a, 0x14, + 0x5c, 0x2f, 0x0e, 0x18, 0xf2, 0xdd, 0xad, 0x74, 0x67, 0x24, 0xff, 0xed, + 0x89, 0x4d, 0xc8, 0xca, 0x34, 0x07, 0x8e, 0x8e, 0x95, 0x22, 0xcd, 0xec, + 0x33, 0xa0, 0x69, 0x97, 0xbe, 0xe1, 0x7f, 0xbb, 0x95, 0x26, 0x20, 0x4f, + 0x55, 0xeb, 0x6c, 0x23, 0x91, 0x6f, 0x78, 0x67, 0x00, 0xce, 0x79, 0x08, + 0xc9, 0xbb, 0xef, 0x86, 0xbc, 0x8d, 0x6a, 0x01, 0x4f, 0x2b, 0xae, 0xbd, + 0x48, 0x01, 0x66, 0xd9, 0x06, 0x1f, 0x0d, 0xd0, 0xfc, 0x4c, 0x14, 0x93, + 0x2b, 0x9c, 0x90, 0xbf, 0xd9, 0xe9, 0x59, 0xfe, 0x19, 0x23, 0xe7, 0x5a, + 0x61, 0xba, 0x0d, 0xc0, 0xb7, 0x4b, 0x5e, 0xf6, 0x6f, 0x0f, 0x58, 0xf6, + 0x9a, 0x3c, 0xbe, 0xff, 0xdb, 0x33, 0x4f, 0x01, 0x55, 0x1a, 0x34, 0x61, + 0x98, 0x05, 0xf0, 0xcd, 0x10, 0x90, 0x60, 0xa6, 0xb1, 0xbc, 0x20, 0xa2, + 0x61, 0x07, 0xf2, 0x50, 0xf3, 0x0d, 0x86, 0x46, 0x58, 0x82, 0xb6, 0x83, + 0xa9, 0x1c, 0xd8, 0xf8, 0xb4, 0x8b, 0x12, 0x7f, 0xf3, 0x43, 0x63, 0x30, + 0x1d, 0x87, 0x83, 0x7a, 0xa3, 0x77, 0x50, 0x38, 0x24, 0x55, 0xe9, 0xa0, + 0x9d, 0xf9, 0x20, 0x1e, 0x4a, 0xfb, 0x40, 0x33, 0x54, 0x92, 0xb9, 0xf6, + 0xa6, 0xcb, 0xb6, 0xb5, 0x0e, 0x04, 0x79, 0x56, 0x9e, 0xdb, 0xf6, 0x65, + 0xa7, 0xb2, 0xb9, 0x53, 0x01, 0x59, 0xcc, 0x1b, 0x83, 0xfd, 0x8f, 0xd1, + 0xa6, 0xd4, 0x05, 0xe3, 0x28, 0x23, 0x4e, 0x8e, 0x75, 0x68, 0xf6, 0x13, + 0x6f, 0x51, 0x01, 0x51, 0xe1, 0x49, 0x72, 0xf0, 0x45, 0x2f, 0xa4, 0x52, + 0xa1, 0x8a, 0x28, 0x8b, 0x79, 0x1d, 0xf7, 0x87, 0xd9, 0x80, 0xac, 0xcc, + 0xae, 0xdf, 0x9e, 0x9d, 0x61, 0xcd, 0xb4, 0x8a, 0xa1, 0x36, 0xe9, 0x56, + 0xa2, 0xe9, 0x18, 0xf6, 0x79, 0x78, 0x9d, 0xef, 0x72, 0x84, 0xf3, 0xd2, + 0x44, 0xd1, 0xf6, 0x94, 0xde, 0x1d, 0x11, 0x47, 0xd4, 0x5c, 0x24, 0x17, + 0x54, 0xc1, 0xcd, 0xac, 0xe9, 0x05, 0x98, 0x66, 0xd9, 0xa4, 0xc0, 0x0a, + 0xbb, 0x75, 0xa4, 0xd3, 0x97, 0xe7, 0x55, 0x69, 0xa0, 0xa0, 0xd8, 0x94, + 0x7a, 0xe9, 0xd8, 0x6b, 0x02, 0x96, 0x51, 0x70, 0x43, 0x56, 0x65, 0xe6, + 0x43, 0x5c, 0xd9, 0xe3, 0x72, 0x41, 0xb8, 0x9a, 0x7c, 0xc9, 0x10, 0xb2, + 0xe1, 0x1f, 0x3e, 0x07, 0x10, 0x13, 0xf8, 0x66, 0x49, 0x2e, 0x3a, 0xa4, + 0xb5, 0xc7, 0x95, 0x63, 0x54, 0x47, 0xf0, 0xa1, 0x13, 0x80, 0x35, 0x4b, + 0x32, 0x21, 0x3f, 0x19, 0x18, 0xe3, 0x70, 0x40, 0x19, 0x29, 0x06, 0x34, + 0x1f, 0x45, 0xa7, 0xe2, 0x7b, 0x42, 0xe3, 0xc9, 0x17, 0x93, 0x6d, 0x93, + 0xe3, 0xea, 0xbe, 0x66, 0x61, 0x0c, 0xfb, 0xc3, 0xc5, 0xa9, 0x8b, 0x01, + 0xf7, 0xf1, 0x9b, 0xed, 0x68, 0x88, 0x0a, 0xe8, 0x28, 0x50, 0x36, 0xe2, + 0x41, 0xd7, 0x89, 0x97, 0x81, 0xe9, 0x7f, 0x70, 0xdf, 0xd3, 0x47, 0xdb, + 0xef, 0xee, 0x6e, 0x64, 0xcd, 0xd9, 0x17, 0x2d, 0xa9, 0x8a, 0x41, 0x4d, + 0x3b, 0x7b, 0x7b, 0x36, 0x96, 0x66, 0xa2, 0xc7, 0x15, 0x73, 0x50, 0x2b, + 0x6f, 0xf4, 0x60, 0x83, 0x94, 0x63, 0xa6, 0xf8, 0x44, 0x32, 0xaf, 0xd5, + 0xe2, 0x97, 0xd9, 0x2d, 0x96, 0x94, 0xdc, 0x47, 0x29, 0x55, 0xc1, 0xd8, + 0x67, 0xe5, 0x6a, 0x57, 0xbb, 0x91, 0xe1, 0x67, 0xef, 0xc7, 0x6f, 0xde, + 0xe8, 0xde, 0x0b, 0x94, 0xd7, 0x41, 0x58, 0xae, 0xfe, 0x29, 0x63, 0x98, + 0x76, 0x03, 0x03, 0x02, 0x86, 0xd7, 0xc0, 0x9b, 0x42, 0x61, 0x27, 0x91, + 0xaa, 0x86, 0x00, 0xc5, 0x6c, 0x8a, 0x1f, 0x69, 0xd5, 0x14, 0x58, 0x2a, + 0x10, 0x76, 0xdd, 0x5b, 0xbe, 0xff, 0x90, 0xe6, 0xd3, 0x51, 0xb1, 0x92, + 0x0b, 0x55, 0xf2, 0x94, 0x7c, 0x15, 0x44, 0x3f, 0xa5, 0x48, 0xea, 0x14, + 0xaa, 0x52, 0x28, 0x2b, 0xf9, 0x8e, 0xca, 0x9a, 0x35, 0x51, 0x64, 0x27, + 0x4e, 0x0d, 0xc8, 0x35, 0xcb, 0xb1, 0x74, 0x1b, 0xd5, 0x03, 0x33, 0x38, + 0x75, 0x5b, 0xe4, 0x52, 0xaa, 0x5c, 0xb6, 0x30, 0xec, 0xdb, 0x29, 0x67, + 0x5c, 0x69, 0x3a, 0x18, 0xd6, 0x9b, 0x2a, 0x0b, 0x94, 0x2d, 0x6d, 0xfb, + 0x1e, 0xa7, 0x46, 0x60, 0x6d, 0x13, 0x40, 0x09, 0xea, 0x8e, 0x00, 0x66, + 0x60, 0x98, 0x4e, 0x1b, 0x0a, 0x95, 0x5e, 0x47, 0x86, 0x44, 0x33, 0x94, + 0x15, 0x71, 0xdf, 0xbd, 0x9d, 0x7e, 0xfa, 0x5f, 0xcc, 0x17, 0x96, 0x6c, + 0x83, 0xb0, 0x20, 0xba, 0x71, 0x3e, 0x0e, 0x3b, 0x72, 0x0c, 0xf8, 0xff, + 0x42, 0x01, 0x80, 0x2c, 0x4a, 0xca, 0x97, 0x75, 0xd4, 0xb2, 0x11, 0xea, + 0x5b, 0xad, 0xd2, 0xc8, 0x2f, 0xb7, 0x12, 0x2b, 0x20, 0x3b, 0xf1, 0x17, + 0x3f, 0x67, 0xce, 0xdb, 0xe8, 0x56, 0x45, 0xbd, 0x38, 0x64, 0xaf, 0x9e, + 0x00, 0x75, 0xbc, 0xc0, 0xdf, 0x08, 0x98, 0x8e, 0x61, 0x52, 0xf6, 0xa8, + 0xee, 0xfb, 0x9a, 0x8e, 0x4e, 0x7e, 0xd9, 0x17, 0xce, 0x51, 0x02, 0x87, + 0xcf, 0xdc, 0xd0, 0xda, 0x18, 0xdf, 0x99, 0x9e, 0x0a, 0x3b, 0xfa, 0x25, + 0xff, 0x4e, 0x6d, 0xb5, 0xb8, 0x60, 0xff, 0x07, 0x65, 0x57, 0x55, 0x02, + 0x30, 0xff, 0xf9, 0x13, 0x2e, 0xb1, 0xe0, 0x08, 0xae, 0x67, 0x53, 0x50, + 0xb1, 0xd7, 0xbb, 0x50, 0xf9, 0xb8, 0x63, 0x24, 0x02, 0xea, 0xfe, 0x23, + 0x66, 0xd9, 0x13, 0x0e, 0xaf, 0x38, 0x18, 0xff, 0x54, 0x6c, 0x7f, 0xda, + 0xec, 0x15, 0x51, 0xaf, 0xff, 0xdb, 0xc9, 0x03, 0x9d, 0xf2, 0xe7, 0x50, + 0x10, 0x08, 0xce, 0xf6, 0xf4, 0xa2, 0x64, 0x21, 0xf7, 0x2f, 0x43, 0x70, + 0xe4, 0x3b, 0xca, 0x06, 0x46, 0x49, 0xc3, 0xc3, 0xc2, 0x17, 0xb3, 0x88, + 0xdd, 0xbc, 0x6a, 0xc9, 0xc5, 0xd1, 0x34, 0xcd, 0x01, 0xfd, 0x3a, 0xa3, + 0x69, 0xab, 0x45, 0xd5, 0x84, 0xe1, 0xd2, 0x2f, 0xdf, 0xa3, 0xcd, 0xc3, + 0xb0, 0x9f, 0x16, 0xa3, 0x0f, 0xf0, 0x12, 0x6e, 0x91, 0x5e, 0xcc, 0x73, + 0xf2, 0x13, 0x66, 0xef, 0x88, 0x4e, 0x0a, 0x35, 0x68, 0xd8, 0x71, 0x24, + 0x07, 0xa3, 0x5d, 0xd2, 0x04, 0x14, 0x57, 0x64, 0xe6, 0xca, 0xea, 0x94, + 0xa7, 0x2f, 0x6a, 0x45, 0xa6, 0x56, 0xdb, 0x6a, 0x3d, 0x06, 0x82, 0xae, + 0xbc, 0x0d, 0x34, 0x0b, 0x86, 0x80, 0x38, 0xae, 0x00, 0x09, 0x84, 0x13, + 0x2b, 0xfb, 0x8d, 0x6a, 0x37, 0x70, 0x7e, 0x34, 0xd9, 0x03, 0x41, 0x9f, + 0x95, 0xc5, 0x6a, 0x59, 0xf0, 0x3c, 0x72, 0x7a, 0xbb, 0x8a, 0x4c, 0x35, + 0x0d, 0xe2, 0x95, 0x85, 0x6f, 0xc6, 0x95, 0x96, 0x0e, 0x12, 0x4e, 0x19, + 0xce, 0xa6, 0xda, 0xf0, 0x67, 0xf9, 0x63, 0xb7, 0x9f, 0xba, 0xfe, 0x1f, + 0xd4, 0x6a, 0xbc, 0x8d, 0x02, 0x99, 0x2f, 0x1d, 0xff, 0x40, 0x89, 0x25, + 0xa1, 0x4a, 0x22, 0x3a, 0x81, 0xa6, 0x87, 0xff, 0x9b, 0xac, 0xc4, 0xc0, + 0x9d, 0x72, 0xf1, 0xdf, 0xc9, 0xf4, 0xde, 0xfb, 0xa5, 0x5d, 0x0a, 0x4b, + 0xf4, 0x85, 0x2e, 0x63, 0xec, 0x01, 0x3c, 0x74, 0x4a, 0x2e, 0xb1, 0x71, + 0xc0, 0xf9, 0xb4, 0xc2, 0x40, 0xb4, 0x9f, 0x71, 0xb9, 0x09, 0xc4, 0x6a, + 0x2a, 0x94, 0xd8, 0xf5, 0x5f, 0x86, 0x68, 0x84, 0xe3, 0x29, 0x24, 0xf3, + 0x5c, 0xb0, 0xae, 0x88, 0xe1, 0x0e, 0x5a, 0x01, 0x09, 0xd8, 0x50, 0x3d, + 0xc2, 0x65, 0x9a, 0x17, 0xfa, 0x83, 0x01, 0x5b, 0xee, 0xf3, 0x3d, 0xd8, + 0x8b, 0x19, 0xb6, 0x5f, 0x2c, 0x37, 0xf1, 0xe4, 0x39, 0xde, 0x41, 0x72, + 0xac, 0x2b, 0xfa, 0x42, 0x65, 0x38, 0x18, 0xf1, 0x22, 0xaf, 0xff, 0xc3, + 0xd4, 0x3b, 0xbd, 0x34, 0xd0, 0xda, 0xde, 0x79, 0x94, 0x6c, 0x45, 0xa8, + 0x66, 0x0c, 0x3c, 0xc9, 0xd8, 0x39, 0x78, 0x6c, 0x79, 0x04, 0x17, 0xc8, + 0x78, 0x98, 0xb9, 0x80, 0x38, 0xfb, 0x79, 0x35, 0xac, 0xd6, 0x0b, 0xfc, + 0x26, 0x1d, 0x47, 0x6a, 0x23, 0xad, 0x20, 0xa1, 0x45, 0x5d, 0xce, 0x48, + 0x99, 0xdb, 0x21, 0x77, 0xa0, 0xf3, 0x16, 0x0b, 0x86, 0x21, 0xa1, 0x6c, + 0x2b, 0xaf, 0xed, 0x44, 0x18, 0xb4, 0x47, 0x1d, 0xb4, 0x24, 0xc8, 0xcd, + 0x01, 0xe5, 0xa3, 0x4b, 0x36, 0xe6, 0xf5, 0xa3, 0x32, 0x3c, 0x83, 0x74, + 0x11, 0x60, 0x9b, 0xf9, 0x5b, 0xf5, 0x2c, 0xe9, 0x9e, 0x85, 0xf0, 0xb4, + 0x79, 0x9b, 0x09, 0x8a, 0x1e, 0xa3, 0x54, 0xc1, 0x01, 0x36, 0x65, 0xc5, + 0xd4, 0x39, 0x31, 0x73, 0x74, 0x04, 0xbe, 0x8b, 0x28, 0xba, 0xbd, 0xe6, + 0xa6, 0x3e, 0x80, 0x8a, 0x86, 0x8b, 0x48, 0x50, 0x66, 0xd0, 0xc8, 0xea, + 0xdc, 0xa3, 0x2e, 0xa6, 0xc2, 0x46, 0x03, 0xb8, 0x33, 0xf4, 0xc7, 0x29, + 0xce, 0xff, 0x20, 0x59, 0x67, 0xe6, 0x1b, 0xb8, 0xc7, 0x9f, 0x89, 0x9e, + 0x61, 0x8b, 0xba, 0x78, 0x23, 0xbd, 0x06, 0xe6, 0x6c, 0x99, 0xd4, 0xab, + 0xb7, 0x78, 0x7a, 0x75, 0xe0, 0x97, 0x92, 0x63, 0xd9, 0xcf, 0xca, 0xc4, + 0x83, 0xec, 0x19, 0x28, 0x1c, 0xcd, 0xca, 0xe9, 0x01, 0x4d, 0x06, 0xf2, + 0xbb, 0xf8, 0x51, 0xeb, 0xc6, 0x13, 0x99, 0x4d, 0xba, 0x55, 0x7c, 0xf3, + 0xe7, 0xc9, 0xf9, 0xab, 0x41, 0xf1, 0xf9, 0x22, 0xb0, 0x69, 0x09, 0xff, + 0xa2, 0x58, 0x3a, 0xfe, 0x15, 0x6c, 0xb5, 0x58, 0xbf, 0xde, 0xbb, 0x69, + 0x9e, 0x71, 0xc7, 0x3c, 0x8d, 0xba, 0xc2, 0xe6, 0x00, 0x09, 0xe2, 0x4f, + 0xc4, 0x8b, 0xdb, 0x3c, 0xf2, 0xfc, 0xd3, 0x4f, 0xaa, 0xfd, 0x37, 0xad, + 0x0d, 0x1d, 0x36, 0x49, 0x9c, 0x05, 0x65, 0x13, 0xaa, 0xc5, 0x1f, 0x3b, + 0xd1, 0xe2, 0x7e, 0x28, 0x64, 0xd1, 0xc9, 0x80, 0x32, 0xcc, 0x86, 0x3c, + 0xc9, 0x15, 0xd2, 0xfd, 0x37, 0x2d, 0x74, 0x44, 0x19, 0xa6, 0xc8, 0x35, + 0x9a, 0x33, 0xd0, 0x7f, 0x15, 0xaa, 0xaf, 0xeb, 0x9c, 0xa1, 0xb5, 0x8d, + 0x9f, 0xe0, 0xc6, 0x06, 0x5f, 0x92, 0x78, 0xac, 0x3c, 0xd9, 0xfa, 0xe7, + 0x4e, 0x08, 0x2b, 0xd7, 0x29, 0x9a, 0xf2, 0x1b, 0xf9, 0xd5, 0xb7, 0x83, + 0x5b, 0x6a, 0x82, 0x7e, 0x46, 0x8f, 0xf4, 0x14, 0x9b, 0x34, 0x82, 0x80, + 0xc1, 0xaf, 0xce, 0xc4, 0x02, 0x5a, 0xf5, 0x54, 0xa9, 0xc7, 0x12, 0x3b, + 0xc9, 0x6a, 0xae, 0x98, 0x4a, 0xc9, 0x2f, 0x2d, 0xa6, 0xb0, 0x95, 0x9d, + 0x44, 0x23, 0x93, 0xa2, 0xe2, 0xe3, 0x8d, 0x43, 0x2b, 0x65, 0xc8, 0x2b, + 0xac, 0xf9, 0x8b, 0x44, 0xee, 0x37, 0xcb, 0x4c, 0x48, 0x10, 0x81, 0x65, + 0x42, 0x91, 0x3a, 0x66, 0x82, 0x7b, 0xe3, 0x0d, 0x2b, 0xec, 0x37, 0x66, + 0x19, 0xd6, 0xb5, 0xca, 0x10, 0xe4, 0xfb, 0x88, 0x9e, 0x1d, 0x31, 0xe6, + 0x61, 0x88, 0x71, 0xf1, 0x77, 0xed, 0xdb, 0xa6, 0x9a, 0xbe, 0x7b, 0x86, + 0xdd, 0xe6, 0x48, 0x8b, 0x5e, 0xe7, 0x2c, 0x61, 0x84, 0xab, 0xa0, 0x0c, + 0x01, 0x93, 0xf9, 0xd6, 0xbf, 0xca, 0xcc, 0xd5, 0x79, 0x5b, 0xb6, 0xc4, + 0xbf, 0xbb, 0x82, 0xa2, 0x6a, 0xbe, 0x8c, 0x6d, 0x1f, 0x8e, 0x29, 0x13, + 0x4b, 0xa3, 0x14, 0xd2, 0xfa, 0x5b, 0xe8, 0xe3, 0xd2, 0x7b, 0x71, 0xd0, + 0x4b, 0x9d, 0x4a, 0x55, 0xb2, 0xee, 0xb8, 0x0d, 0xb0, 0xa7, 0xbc, 0x48, + 0x6f, 0xcd, 0x9f, 0xe8, 0xb2, 0x14, 0x78, 0x5b, 0x04, 0x62, 0xf9, 0x34, + 0x03, 0xfb, 0x17, 0x05, 0xf5, 0x9e, 0x37, 0x17, 0xa5, 0xe1, 0x32, 0x6c, + 0x3f, 0x0f, 0x9c, 0xc7, 0x8c, 0xd5, 0x62, 0x41, 0x5a, 0xe6, 0x10, 0x7f, + 0xe5, 0x86, 0x06, 0x2c, 0x30, 0x3c, 0x7c, 0x80, 0xdc, 0xc3, 0x62, 0xf1, + 0xcd, 0x2a, 0x42, 0x89, 0xeb, 0x2f, 0x11, 0x47, 0x82, 0x98, 0xfe, 0x98, + 0x63, 0x4b, 0xab, 0xd8, 0x26, 0xd5, 0xeb, 0x49, 0x08, 0x19, 0x2a, 0x70, + 0xfd, 0x70, 0x37, 0x95, 0xbd, 0xf0, 0x8a, 0x5c, 0xfe, 0x76, 0xd4, 0x27, + 0x45, 0xf6, 0xa6, 0xd1, 0xfb, 0x24, 0xca, 0xac, 0x8c, 0x09, 0x98, 0x58, + 0x06, 0xde, 0x9a, 0x72, 0x4f, 0x5b, 0x4f, 0x91, 0x41, 0x0a, 0x6c, 0xc8, + 0x67, 0x5a, 0xf3, 0xa0, 0xbb, 0x3a, 0x9e, 0x65, 0x82, 0xb2, 0x14, 0xbe, + 0x4e, 0x2b, 0xd9, 0x4d, 0x8b, 0xab, 0x43, 0x1c, 0xdd, 0xcd, 0x4b, 0x05, + 0xd9, 0x19, 0xa5, 0xb5, 0xc1, 0xe1, 0x67, 0x9a, 0xf0, 0x55, 0xb3, 0x6a, + 0x92, 0x0b, 0x1a, 0xf1, 0xdd, 0xe0, 0xac, 0x8c, 0x2e, 0xc9, 0x08, 0xef, + 0x21, 0xb9, 0x8f, 0xf2, 0x1e, 0x36, 0xb3, 0x21, 0xb6, 0x3c, 0xa6, 0x0e, + 0xe4, 0x0a, 0x90, 0x1f, 0xdd, 0xd9, 0xa5, 0x4f, 0x55, 0x57, 0xbe, 0x02, + 0xca, 0x67, 0xa7, 0x7e, 0x35, 0xd7, 0xd9, 0x5a, 0x5a, 0x67, 0x64, 0x1f, + 0x52, 0x68, 0xbf, 0x36, 0x6e, 0x20, 0x5d, 0x32, 0x86, 0x7c, 0x7c, 0xf3, + 0x5c, 0xe1, 0xe8, 0x32, 0x09, 0xf3, 0x72, 0x1b, 0x03, 0x4b, 0x25, 0xb0, + 0x9a, 0x7a, 0x0c, 0xba, 0x95, 0xde, 0x74, 0xf9, 0xff, 0x2d, 0x36, 0xcb, + 0x19, 0x60, 0xc7, 0x16, 0x1b, 0x0f, 0xe0, 0x8f, 0x56, 0x0a, 0x6e, 0x94, + 0x01, 0x9a, 0x2c, 0x9b, 0xfa, 0x47, 0x1c, 0x11, 0xb5, 0xbd, 0x31, 0xe9, + 0xe4, 0x5f, 0x13, 0xd9, 0x4e, 0xe7, 0xd8, 0xfa, 0xc0, 0x45, 0xd5, 0x18, + 0xed, 0xed, 0xdd, 0xbe, 0x87, 0xa6, 0x97, 0x90, 0xa1, 0x58, 0xed, 0x0c, + 0xe7, 0xb9, 0xee, 0xa6, 0xe0, 0x0b, 0x21, 0x35, 0x30, 0x46, 0xc4, 0x86, + 0x00, 0x49, 0x81, 0xbb, 0x26, 0xff, 0x03, 0x3d, 0xc3, 0xb4, 0xf2, 0x80, + 0xbc, 0x54, 0xa4, 0x76, 0x27, 0x2f, 0xed, 0xcc, 0x2d, 0x3e, 0x2a, 0xdc, + 0x5d, 0x6e, 0x92, 0x17, 0x8f, 0x2f, 0xef, 0x87, 0xb4, 0xac, 0x58, 0xc1, + 0x52, 0x5c, 0x27, 0x5b, 0xdb, 0x5a, 0x4b, 0x1e, 0xb5, 0x1a, 0x2b, 0x8c, + 0x69, 0x2a, 0x69, 0xf4, 0xa3, 0x1d, 0x6a, 0xe0, 0x65, 0x1a, 0x28, 0x9a, + 0x1c, 0x7d, 0x7e, 0xe6, 0x04, 0x8d, 0xed, 0x25, 0x0e, 0xc7, 0x53, 0x56, + 0x74, 0x6f, 0x90, 0x25, 0x5e, 0xed, 0x17, 0xe2, 0x85, 0xc0, 0xfb, 0x4d, + 0x74, 0xc0, 0x13, 0xfc, 0xa6, 0xeb, 0x1c, 0x98, 0x2a, 0x7e, 0x46, 0x20, + 0x3f, 0x55, 0x12, 0x8f, 0xd5, 0x79, 0x6d, 0x19, 0x64, 0x27, 0x3b, 0xd9, + 0x3e, 0xa8, 0x9a, 0xe1, 0xa6, 0x05, 0xc5, 0x43, 0x63, 0x3f, 0xc6, 0x94, + 0x88, 0x6f, 0x7c, 0x55, 0xdc, 0xa5, 0x62, 0x7a, 0x3f, 0xd8, 0x7f, 0x52, + 0xdf, 0x94, 0x7e, 0xdc, 0x52, 0xb8, 0x98, 0x45, 0x10, 0x1f, 0xbf, 0x13, + 0xfb, 0xdc, 0xc0, 0xcd, 0x36, 0xa8, 0x69, 0x7d, 0x9b, 0x33, 0x5b, 0xf2, + 0xd3, 0x92, 0x7a, 0x27, 0xd3, 0x08, 0x24, 0xd3, 0x84, 0x69, 0x5d, 0x6e, + 0x03, 0xba, 0x5e, 0xac, 0x66, 0x86, 0xa3, 0xc0, 0x2a, 0x19, 0x71, 0xfb, + 0x2f, 0xb2, 0x67, 0x82, 0x13, 0xc7, 0xe7, 0xdd, 0x1b, 0xeb, 0x25, 0x7d, + 0x1e, 0x2f, 0xfd, 0xcf, 0x55, 0x54, 0x2e, 0x17, 0xf8, 0x32, 0x73, 0x75, + 0x01, 0x39, 0x7d, 0xd4, 0x61, 0x03, 0x68, 0x76, 0x89, 0xe3, 0x45, 0x2d, + 0x1f, 0x3b, 0x12, 0x58, 0xd4, 0x22, 0x5c, 0x5b, 0x40, 0xb2, 0x14, 0xe4, + 0x11, 0xff, 0x9a, 0xe4, 0x21, 0x2a, 0x2e, 0xbe, 0xdd, 0x63, 0xd6, 0x33, + 0xf0, 0x00, 0xc7, 0x79, 0x02, 0x56, 0xa2, 0xce, 0xe5, 0xce, 0x10, 0x90, + 0x90, 0x90, 0x85, 0xc4, 0x90, 0x5f, 0x1a, 0xe0, 0xdd, 0xf2, 0x0d, 0x54, + 0x66, 0x59, 0x7b, 0x6e, 0x8b, 0xcb, 0x10, 0xe9, 0xda, 0xf5, 0x03, 0xc7, + 0x08, 0x3a, 0x85, 0x7d, 0x44, 0xde, 0x2e, 0xd8, 0xc7, 0xa1, 0x5d, 0x0a, + 0x90, 0x4a, 0x1b, 0xaa, 0x59, 0x99, 0x04, 0xe5, 0xf1, 0x2d, 0x29, 0x9b, + 0xbe, 0xbf, 0xbf, 0x09, 0x1e, 0xfe, 0x37, 0xfe, 0x10, 0x30, 0xc8, 0x3c, + 0x99, 0xbf, 0xa9, 0x93, 0xac, 0xf1, 0x8e, 0x97, 0x98, 0xe7, 0x58, 0xf2, + 0x6d, 0x55, 0xe8, 0xfa, 0x2e, 0x9e, 0xf7, 0x27, 0x1f, 0xb1, 0x1c, 0x44, + 0x86, 0x99, 0xb7, 0x59, 0x16, 0x83, 0x5f, 0x73, 0xd1, 0x2b, 0x0c, 0x30, + 0x03, 0x57, 0x43, 0x6f, 0xf8, 0xbe, 0xa3, 0x26, 0xd5, 0x19, 0x4c, 0xdd, + 0xd8, 0x5e, 0x0f, 0xe1, 0x41, 0x33, 0xb6, 0x66, 0x29, 0x7e, 0xe8, 0x21, + 0x00, 0xbc, 0xea, 0x39, 0xc1, 0x53, 0xc6, 0x6f, 0xa2, 0x78, 0x35, 0x01, + 0x11, 0x4d, 0x51, 0xb8, 0xc3, 0xc9, 0x1b, 0x56, 0xa8, 0x4e, 0x9a, 0x91, + 0x1a, 0xb7, 0x76, 0x9a, 0xe5, 0x1e, 0x98, 0x57, 0x9d, 0x89, 0x03, 0x77, + 0x91, 0x02, 0xda, 0x67, 0x48, 0x62, 0xc2, 0x03, 0x5b, 0x78, 0xa4, 0xfe, + 0x12, 0x15, 0xb3, 0xa2, 0x50, 0x6d, 0x11, 0x41, 0xe2, 0x13, 0x70, 0x08, + 0x35, 0x27, 0xbf, 0x8d, 0x04, 0x97, 0xb3, 0xee, 0xb7, 0x01, 0x2f, 0x1e, + 0x08, 0x52, 0x35, 0xdd, 0x89, 0xd5, 0x0c, 0x1d, 0xf1, 0x04, 0x33, 0x17, + 0xe6, 0xd6, 0x6c, 0x07, 0xc5, 0x3b, 0x79, 0xb1, 0xc6, 0x7a, 0x5c, 0x5b, + 0xc9, 0xd8, 0xa5, 0x65, 0xd3, 0x68, 0xcf, 0x00, 0x60, 0xa9, 0x6e, 0xa9, + 0xb5, 0xd6, 0xa2, 0x0c, 0xae, 0xc4, 0x8e, 0x33, 0x3a, 0x6c, 0xc5, 0xc4, + 0x27, 0x80, 0xcf, 0x56, 0x39, 0xcf, 0xe5, 0xce, 0xd8, 0xee, 0xe4, 0xd2, + 0x42, 0xc0, 0x74, 0xac, 0xb6, 0xa1, 0x1e, 0x5d, 0x00, 0x84, 0x0a, 0x28, + 0x00, 0x5c, 0x8c, 0xf3, 0x0f, 0x3e, 0x62, 0x16, 0xe9, 0x59, 0x6e, 0x42, + 0xd2, 0xf3, 0x01, 0xcc, 0x9a, 0x38, 0x83, 0xae, 0xe2, 0x1a, 0x7f, 0x85, + 0x07, 0x1f, 0x3c, 0x3d, 0xa3, 0xc4, 0x02, 0x91, 0x17, 0x1f, 0x77, 0x1f, + 0x8a, 0xd3, 0xfa, 0x2e, 0xe3, 0x9b, 0x38, 0x6f, 0xc5, 0xdc, 0x5e, 0x5b, + 0x5f, 0x92, 0x2f, 0xfd, 0x46, 0xd6, 0x72, 0xe2, 0xa2, 0x6b, 0x7d, 0x9d, + 0xe9, 0x5b, 0x3e, 0x59, 0x7e, 0xfd, 0x16, 0x29, 0x24, 0x82, 0xbe, 0x16, + 0x1b, 0x54, 0xc8, 0x42, 0xea, 0xb8, 0x9d, 0xbe, 0x3a, 0x21, 0x83, 0x18, + 0xfc, 0x41, 0xd0, 0x3a, 0x56, 0x12, 0x22, 0x7d, 0x5f, 0x44, 0xf1, 0x35, + 0x33, 0x03, 0xda, 0x14, 0x0f, 0x60, 0x6e, 0x22, 0x96, 0x80, 0x55, 0x01, + 0xb0, 0x5b, 0xbe, 0xcc, 0x60, 0x72, 0xd5, 0xfe, 0x7c, 0xb6, 0x86, 0x7e, + 0x17, 0x5f, 0x57, 0x3e, 0xde, 0x42, 0x7e, 0xfe, 0x4f, 0x51, 0x84, 0xe5, + 0x79, 0x75, 0x0f, 0x3d, 0x0d, 0x7c, 0x98, 0x63, 0xac, 0x5e, 0x29, 0x9d, + 0x62, 0x03, 0x3d, 0x91, 0xec, 0x11, 0x9b, 0x1e, 0x2e, 0x43, 0xbe, 0x72, + 0x86, 0x28, 0xa5, 0x30, 0xb2, 0x74, 0x4b, 0x47, 0x77, 0x9e, 0xde, 0xb1, + 0x91, 0xd0, 0xa8, 0x68, 0xeb, 0xec, 0xdf, 0xfd, 0x3e, 0x83, 0xba, 0x57, + 0xda, 0x62, 0x3f, 0xb2, 0x65, 0xee, 0x86, 0x74, 0xcc, 0x02, 0xad, 0xe2, + 0xbb, 0x30, 0x79, 0x3a, 0x3d, 0x44, 0x08, 0xd1, 0xf0, 0xc1, 0x05, 0x10, + 0xaa, 0xe4, 0x45, 0xd2, 0x78, 0xa7, 0x18, 0xb7, 0x09, 0x88, 0x00, 0xc4, + 0x4a, 0xe4, 0x98, 0xb2, 0xb9, 0x97, 0x69, 0x97, 0xe4, 0x10, 0x7c, 0x46, + 0xb7, 0xf9, 0x90, 0xcd, 0x43, 0xe2, 0x95, 0x9a, 0xa0, 0x2b, 0x19, 0x51, + 0x3a, 0x95, 0x7a, 0x7b, 0x59, 0x72, 0xd2, 0xc1, 0x0d, 0x83, 0x3f, 0x2b, + 0x5f, 0x5f, 0x2a, 0xe1, 0xf6, 0x81, 0x84, 0x53, 0x9c, 0x04, 0xcf, 0x6e, + 0x03, 0xea, 0x6c, 0x24, 0x67, 0xcb, 0x52, 0x00, 0x72, 0xe2, 0x27, 0x61, + 0x83, 0x1f, 0xe7, 0x93, 0x00, 0x80, 0xed, 0x75, 0xa7, 0xdb, 0x1c, 0x27, + 0xd3, 0x66, 0xe0, 0x2e, 0x44, 0xe2, 0xc8, 0xc3, 0x35, 0x63, 0xb4, 0xb3, + 0xc2, 0xd4, 0x99, 0x90, 0xc5, 0x07, 0xbc, 0x85, 0x61, 0xc5, 0x99, 0x11, + 0xd6, 0xd5, 0x0b, 0x8d, 0xa4, 0x6b, 0x3f, 0x92, 0xc8, 0x23, 0xa2, 0x48, + 0x8d, 0xd9, 0x37, 0x04, 0x65, 0xc8, 0x60, 0x42, 0x32, 0x93, 0xdc, 0xb4, + 0x01, 0xc2, 0x91, 0x20, 0xff, 0x38, 0xa5, 0x01, 0xf2, 0x27, 0x86, 0xdf, + 0xfe, 0xe1, 0xd7, 0x0d, 0x1f, 0x86, 0x1b, 0x0b, 0x03, 0xb3, 0x9d, 0x20, + 0xab, 0x84, 0xbd, 0xe3, 0x61, 0x68, 0xcc, 0xd9, 0xef, 0xad, 0xbd, 0xb6, + 0xd8, 0xb0, 0xf2, 0xd4, 0x25, 0x84, 0x83, 0x53, 0x8e, 0xc3, 0xbd, 0x9e, + 0x9f, 0x81, 0xf7, 0xf6, 0x5c, 0x69, 0x94, 0xb0, 0xe1, 0x54, 0x6a, 0x4d, + 0x56, 0x49, 0x45, 0x19, 0xfc, 0xb2, 0xaa, 0x5a, 0x67, 0x7f, 0x76, 0x5b, + 0x74, 0x2f, 0xd7, 0x22, 0xe3, 0x0e, 0x8f, 0x99, 0x15, 0x15, 0xc3, 0xbb, + 0x4e, 0xd6, 0x37, 0x63, 0xbf, 0x1e, 0x67, 0xfd, 0x9d, 0x07, 0x12, 0x30, + 0x86, 0x08, 0x50, 0x9c, 0x2a, 0x80, 0x33, 0x74, 0xec, 0xec, 0x81, 0xbb, + 0x05, 0xf8, 0x87, 0x96, 0xbf, 0x28, 0x14, 0xee, 0xd1, 0x92, 0x27, 0x58, + 0x29, 0x7e, 0xd0, 0xb1, 0xd6, 0x55, 0xde, 0xb6, 0x2a, 0x27, 0x49, 0x7e, + 0x9d, 0x73, 0x33, 0xc8, 0xbd, 0xc6, 0x60, 0x6a, 0xbc, 0x7c, 0x3d, 0x25, + 0x43, 0x12, 0xa9, 0x12, 0x88, 0x2e, 0x7f, 0xa2, 0x78, 0x41, 0xcf, 0xd1, + 0x90, 0x79, 0xc2, 0x0f, 0x78, 0x03, 0xa8, 0x53, 0x90, 0x87, 0xb5, 0xc9, + 0x7d, 0xac, 0xbc, 0xb1, 0x65, 0x60, 0xbe, 0x0f, 0x02, 0x59, 0x66, 0x84, + 0x51, 0xbc, 0xbc, 0x6d, 0x1e, 0xcf, 0x22, 0xa3, 0xb1, 0xac, 0x97, 0x28, + 0x6b, 0x9d, 0x59, 0x4d, 0x06, 0x48, 0x2b, 0xa9, 0x8f, 0x61, 0x43, 0xa5, + 0x03, 0x02, 0x50, 0xa4, 0x77, 0x27, 0xd3, 0x8f, 0x39, 0xe2, 0xde, 0x12, + 0x15, 0x66, 0x57, 0x4a, 0xbf, 0xbc, 0x24, 0x60, 0x41, 0xd4, 0xe5, 0x21, + 0x05, 0x10, 0xb6, 0x0c, 0xf4, 0x90, 0xf5, 0xff, 0x09, 0x21, 0x35, 0x3a, + 0x4a, 0xcd, 0xee, 0xb6, 0xbb, 0xae, 0x6e, 0x02, 0x46, 0x5e, 0x67, 0x32, + 0xb3, 0x4f, 0x4e, 0xa2, 0x37, 0x78, 0x3d, 0xdd, 0x71, 0x1b, 0x56, 0x18, + 0x4f, 0xa5, 0xc3, 0xe6, 0x82, 0xe2, 0x86, 0x3a, 0xc4, 0x31, 0x96, 0xd7, + 0x0b, 0x0c, 0xe7, 0x97, 0x67, 0xce, 0x48, 0x2b, 0x62, 0xb0, 0x2d, 0x6e, + 0x4d, 0x09, 0xb9, 0x95, 0x81, 0x98, 0x11, 0x79, 0x24, 0xcb, 0x24, 0x1e, + 0x69, 0xa8, 0x7c, 0xa3, 0x8a, 0x54, 0x53, 0xa8, 0x5e, 0x6d, 0xc4, 0x73, + 0x96, 0xde, 0xdf, 0x61, 0x5b, 0x99, 0x3e, 0xa4, 0x70, 0xc4, 0x29, 0xfb, + 0xd7, 0x39, 0x32, 0x6f, 0xba, 0x5d, 0xf8, 0xb3, 0x07, 0x0c, 0x21, 0xc2, + 0x26, 0x96, 0xe1, 0xf2, 0x7e, 0xd2, 0x54, 0xf6, 0xaa, 0x7c, 0xf8, 0x2a, + 0xee, 0x7d, 0xf3, 0xc4, 0xd4, 0xd1, 0xe2, 0xf2, 0xcb, 0x4f, 0x96, 0x12, + 0x68, 0x82, 0x3d, 0x06, 0x60, 0xfa, 0xc6, 0xb5, 0x32, 0x18, 0xbf, 0x24, + 0x80, 0x36, 0xcb, 0x5a, 0x80, 0x3c, 0x06, 0xc6, 0x81, 0xb3, 0xe0, 0x2c, + 0x6a, 0x8f, 0x82, 0xf0, 0xb2, 0x63, 0x1d, 0xf3, 0x8a, 0x0f, 0x29, 0x4e, + 0x8c, 0xec, 0x72, 0xc5, 0xb0, 0x7e, 0xe8, 0x81, 0xaa, 0x48, 0xbc, 0x15, + 0x72, 0x94, 0xee, 0x88, 0x28, 0x42, 0x2f, 0xf0, 0x63, 0xf7, 0xe2, 0x3d, + 0x2e, 0x56, 0x12, 0x98, 0x55, 0x8a, 0xfe, 0x5b, 0xe6, 0xb1, 0x3d, 0x0b, + 0xd7, 0x3c, 0xd5, 0xd8, 0x6b, 0x60, 0x6b, 0x53, 0x98, 0x70, 0x6a, 0x1b, + 0x22, 0x8f, 0x68, 0xac, 0xce, 0xab, 0xcf, 0x03, 0xd6, 0x56, 0x86, 0xf7, + 0xed, 0x63, 0x11, 0xbf, 0x21, 0xf9, 0x26, 0xd6, 0xbd, 0xf0, 0x32, 0x38, + 0xf9, 0xbb, 0x12, 0x5c, 0xc6, 0x72, 0x04, 0xda, 0x32, 0xe5, 0xb2, 0x1e, + 0x3f, 0x04, 0x46, 0xa5, 0xca, 0x0d, 0x76, 0x9c, 0x41, 0xfb, 0xac, 0x97, + 0x82, 0x10, 0x23, 0x53, 0x04, 0xb9, 0x76, 0xc9, 0x44, 0xf9, 0xe5, 0x56, + 0x5f, 0x94, 0xf0, 0x8b, 0x64, 0x25, 0xc7, 0x67, 0xfe, 0x03, 0x19, 0xf3, + 0xa1, 0x80, 0x06, 0xb7, 0x2a, 0x6d, 0x33, 0xed, 0xe7, 0x63, 0xdb, 0x34, + 0x3b, 0x79, 0x8d, 0x6e, 0x17, 0x4c, 0x6d, 0x04, 0x53, 0x91, 0xe0, 0xbf, + 0xdb, 0x7e, 0xb2, 0x31, 0x6b, 0xfc, 0xea, 0xb0, 0xec, 0x17, 0x48, 0xe2, + 0x79, 0x52, 0x04, 0xd4, 0x93, 0x3d, 0x7b, 0x7a, 0x27, 0xb7, 0x48, 0x64, + 0xd5, 0xa0, 0x98, 0xc5, 0x78, 0x55, 0x32, 0x1d, 0xb8, 0xfe, 0x6d, 0x52, + 0xf4, 0xdb, 0x51, 0x6c, 0x78, 0x38, 0xcb, 0x85, 0x0b, 0x90, 0x82, 0xab, + 0x1b, 0x0f, 0x13, 0x06, 0x38, 0xfd, 0x64, 0x3b, 0x2a, 0x85, 0x3e, 0x49, + 0x73, 0x4f, 0xb0, 0xac, 0x00, 0x53, 0x17, 0xb6, 0xfe, 0xd8, 0xa9, 0x8f, + 0x89, 0x3a, 0x6f, 0x80, 0xbd, 0xd0, 0xf0, 0xe8, 0xa6, 0xfe, 0x46, 0x1a, + 0x86, 0x6d, 0xeb, 0x5d, 0xd5, 0x72, 0xfb, 0xc5, 0x66, 0x02, 0x85, 0x6c, + 0x85, 0x5a, 0x66, 0x17, 0x7b, 0x7c, 0x97, 0xf6, 0x52, 0xc7, 0xbe, 0xf6, + 0x4b, 0x9d, 0xdc, 0x8f, 0x5a, 0x80, 0xd6, 0x76, 0x70, 0xce, 0xd4, 0xef, + 0x52, 0xc9, 0xd9, 0xd1, 0xe3, 0x39, 0x48, 0xc8, 0x92, 0xbf, 0x74, 0xa9, + 0xf9, 0x01, 0x4d, 0xd3, 0x55, 0x8f, 0x01, 0x8c, 0x7d, 0x45, 0x5a, 0x6c, + 0x16, 0xac, 0x77, 0xdc, 0x1a, 0x9b, 0x01, 0x4c, 0x00, 0xf4, 0xe9, 0x58, + 0xd8, 0x07, 0x86, 0xb0, 0x76, 0x2e, 0x21, 0xfd, 0xe1, 0x0d, 0xa7, 0xbe, + 0x32, 0xe9, 0xf8, 0x6f, 0x31, 0xf4, 0x8a, 0x35, 0xac, 0xcf, 0xcc, 0x22, + 0xe7, 0xfe, 0xa5, 0xc6, 0xdf, 0xc0, 0x32, 0xe0, 0x97, 0xee, 0xe3, 0x5c, + 0x4c, 0x4d, 0xb8, 0x7d, 0x3f, 0xf1, 0xc2, 0xb6, 0x5f, 0xec, 0x86, 0x46, + 0xd1, 0xa8, 0x7f, 0x90, 0xe1, 0x98, 0xe5, 0x9d, 0x97, 0xdf, 0x08, 0x32, + 0x31, 0xc3, 0x33, 0x3b, 0x62, 0x32, 0xdf, 0xe3, 0x8c, 0xa7, 0x96, 0x90, + 0xfb, 0x2b, 0x3e, 0x3d, 0xa1, 0x1a, 0x4c, 0xcf, 0x16, 0xc6, 0x19, 0xa8, + 0xbe, 0x20, 0xcc, 0xae, 0x97, 0xd9, 0x30, 0x76, 0x27, 0x9c, 0xec, 0xc6, + 0xbe, 0xb5, 0x04, 0x26, 0x39, 0xdf, 0x95, 0xcb, 0xd4, 0xe4, 0x3e, 0x55, + 0xad, 0x9c, 0xba, 0x9f, 0x20, 0x33, 0xc9, 0x88, 0x8c, 0x77, 0xc6, 0xe1, + 0xd1, 0x3a, 0xd1, 0x2e, 0x3e, 0xec, 0x5c, 0x3f, 0x25, 0xb9, 0xce, 0xb4, + 0xa2, 0xa2, 0xff, 0xca, 0x07, 0xd8, 0x29, 0x47, 0x9a, 0x09, 0xdf, 0xe0, + 0xa1, 0x8a, 0x10, 0x2f, 0xf4, 0x6d, 0x5b, 0xdd, 0x51, 0xbc, 0x57, 0x54, + 0x6b, 0xd3, 0x16, 0xe1, 0xb3, 0x32, 0xfc, 0x46, 0x8b, 0x3e, 0xb8, 0xb8, + 0x8e, 0x7b, 0x48, 0x8a, 0x54, 0x93, 0xd5, 0xf0, 0x6f, 0x97, 0x0b, 0x1a, + 0xa4, 0xbd, 0xbe, 0x6c, 0xfc, 0x31, 0x69, 0x57, 0xd5, 0x62, 0xa6, 0x5f, + 0xda, 0x38, 0xe8, 0x41, 0xb3, 0x87, 0x04, 0x13, 0x3b, 0xe9, 0x06, 0xe8, + 0x1b, 0x3d, 0x95, 0x18, 0x11, 0xdf, 0xdb, 0x95, 0x69, 0x63, 0x11, 0x7e, + 0x49, 0x25, 0xe7, 0x1c, 0x08, 0x63, 0xd2, 0x1b, 0x65, 0x8d, 0xd5, 0x52, + 0x4f, 0x7f, 0x27, 0x6f, 0x4e, 0x2f, 0x9f, 0x66, 0x02, 0x3b, 0x52, 0x74, + 0xa4, 0x2f, 0x64, 0x42, 0x11, 0x39, 0xb0, 0xfa, 0x98, 0x60, 0x72, 0xfe, + 0x34, 0x33, 0x31, 0x9f, 0xc5, 0xce, 0x81, 0x5f, 0x6d, 0x35, 0x4a, 0xf3, + 0xcc, 0x09, 0x1f, 0xf5, 0x5c, 0xc7, 0x07, 0xa2, 0xcf, 0xd2, 0xfe, 0xed, + 0x28, 0xdf, 0xc6, 0x88, 0x31, 0x47, 0xc6, 0xbf, 0x8c, 0x23, 0x03, 0x36, + 0x34, 0xea, 0xfb, 0xf9, 0xc4, 0x51, 0x90, 0x94, 0xca, 0xdb, 0x85, 0xd0, + 0x5b, 0x1f, 0xa5, 0xc3, 0x66, 0x8b, 0x31, 0x0b, 0xc9, 0x7f, 0xff, 0xe9, + 0x50, 0xfc, 0xaf, 0x3f, 0x56, 0xb6, 0xca, 0x03, 0x7e, 0xeb, 0xce, 0x8b, + 0x36, 0x90, 0xe8, 0xf0, 0x7d, 0x17, 0x89, 0x45, 0xbd, 0x72, 0x11, 0x73, + 0x4a, 0xe8, 0x33, 0x14, 0xe7, 0x77, 0x90, 0xd8, 0xa5, 0xb3, 0xc9, 0xed, + 0xbf, 0xff, 0xf6, 0xe9, 0x64, 0xdc, 0xcc, 0x6e, 0xe7, 0x2e, 0x41, 0xfa, + 0x6c, 0xb9, 0x64, 0x6c, 0xb1, 0x8b, 0xf7, 0xc7, 0x64, 0x4a, 0x01, 0xf2, + 0x08, 0x4f, 0xca, 0xaf, 0xf3, 0x37, 0x9b, 0x76, 0x43, 0x4c, 0xb0, 0xc3, + 0xeb, 0x70, 0xfe, 0xfd, 0xc7, 0xa6, 0x17, 0x30, 0xfd, 0x53, 0xfc, 0xe8, + 0xf1, 0xb8, 0x3c, 0x70, 0xa6, 0xcf, 0xf9, 0x2d, 0xcb, 0x84, 0xe4, 0x0b, + 0x35, 0x79, 0xe7, 0xad, 0x20, 0xc2, 0x90, 0xaf, 0xfc, 0x8c, 0x52, 0xcc, + 0x68, 0x95, 0x86, 0x69, 0x2a, 0xba, 0x51, 0x65, 0xc2, 0xe6, 0x7a, 0x02, + 0x53, 0x00, 0x66, 0x4c, 0x38, 0x0c, 0xa5, 0xf2, 0x37, 0x58, 0xc0, 0x79, + 0x3f, 0x12, 0x5a, 0xc8, 0x60, 0xde, 0x02, 0x54, 0x16, 0xca, 0x32, 0x00, + 0x4c, 0x51, 0x07, 0xd8, 0x8b, 0x6f, 0xbc, 0xc0, 0x15, 0xa0, 0xb3, 0xc0, + 0x1f, 0xfc, 0xca, 0x75, 0x21, 0xcf, 0xb7, 0x4d, 0x96, 0xea, 0x26, 0xfe, + 0xbb, 0x31, 0xdf, 0x2e, 0x5b, 0x38, 0x0b, 0x5a, 0x3e, 0xf5, 0x96, 0x81, + 0x4d, 0x0c, 0x10, 0x6b, 0xe8, 0x39, 0x44, 0xd1, 0x4c, 0xb2, 0x35, 0x04, + 0xd7, 0xd5, 0x36, 0x38, 0xa0, 0xd9, 0x8d, 0xd2, 0x83, 0x1e, 0xd8, 0x95, + 0x48, 0x2a, 0x73, 0xf7, 0x66, 0x3b, 0xa1, 0xb1, 0xb9, 0x5b, 0x15, 0x00, + 0x26, 0xa3, 0x54, 0x8e, 0x5a, 0x5e, 0xb6, 0x2a, 0xf0, 0x2e, 0x6e, 0xd1, + 0x47, 0xa3, 0x76, 0x95, 0xf9, 0x8e, 0x35, 0x13, 0xca, 0x46, 0x9a, 0x18, + 0xd3, 0x05, 0x76, 0x29, 0x4e, 0xa8, 0x81, 0x0b, 0xb9, 0x6f, 0xd1, 0x97, + 0xe6, 0x4f, 0x60, 0xfd, 0x97, 0x02, 0x34, 0xd0, 0x52, 0x57, 0x24, 0x63, + 0x57, 0xb2, 0x08, 0x4a, 0xf8, 0x90, 0x48, 0xf9, 0x49, 0x68, 0x53, 0xfd, + 0x51, 0x7e, 0x10, 0xfa, 0x61, 0xaa, 0x71, 0x46, 0xf4, 0xd7, 0x2c, 0xbb, + 0x0b, 0x80, 0x7c, 0xcf, 0xbe, 0x4b, 0xdd, 0xe2, 0xbf, 0x20, 0x91, 0x56, + 0xeb, 0x71, 0x33, 0x0f, 0xf7, 0x27, 0x7f, 0x73, 0x66, 0x3b, 0x6b, 0x4c, + 0x8a, 0x29, 0x6e, 0x99, 0x28, 0x36, 0xa4, 0xc1, 0x25, 0x4d, 0x73, 0x41, + 0x33, 0x2d, 0xdf, 0x2f, 0x18, 0xe1, 0xf5, 0xb4, 0x89, 0xc3, 0x80, 0x94, + 0x63, 0xf9, 0x92, 0xc9, 0xe7, 0xe6, 0x83, 0x82, 0x31, 0x97, 0x25, 0x01, + 0x05, 0x91, 0xf8, 0x54, 0x8f, 0x28, 0xae, 0x35, 0xa3, 0x00, 0xa8, 0x18, + 0x19, 0xff, 0xf9, 0xb1, 0xc5, 0xea, 0x4c, 0x10, 0xd7, 0xb2, 0x1e, 0x83, + 0x44, 0x06, 0x63, 0x2b, 0x80, 0xb7, 0xa8, 0x40, 0x26, 0x88, 0x56, 0x95, + 0xd6, 0xd0, 0xc5, 0x7f, 0x7e, 0xa9, 0x0a, 0x41, 0xf7, 0xf8, 0x04, 0xb7, + 0x5d, 0xda, 0x5e, 0xba, 0x8c, 0xa8, 0x7e, 0x36, 0xb0, 0x72, 0x46, 0x53, + 0x15, 0xc8, 0x08, 0x8a, 0x6c, 0x32, 0xc4, 0xa0, 0x37, 0xcb, 0xdd, 0xcc, + 0xac, 0x23, 0xd2, 0xe8, 0x9b, 0xdf, 0x47, 0xed, 0xc4, 0x07, 0x99, 0x66, + 0x30, 0x0a, 0x6a, 0xfa, 0x35, 0xd8, 0x0d, 0x90, 0xd8, 0xc0, 0x19, 0x1a, + 0x05, 0x17, 0x6c, 0x3a, 0xd3, 0xaa, 0x11, 0xbb, 0x1d, 0x81, 0x73, 0x90, + 0xda, 0x66, 0x11, 0x1a, 0x6d, 0x27, 0xd1, 0xd9, 0xa2, 0xb4, 0x1a, 0x78, + 0x44, 0x0b, 0xfc, 0x8d, 0x54, 0x9f, 0xe9, 0xc9, 0x2f, 0xab, 0xd3, 0x8e, + 0xf7, 0x3c, 0x9b, 0xed, 0x35, 0x74, 0xd5, 0x4b, 0xb4, 0x22, 0xdf, 0xda, + 0x91, 0x9b, 0x8a, 0x1b, 0x98, 0x03, 0x75, 0xa2, 0xb5, 0x39, 0xc3, 0xbe, + 0x0a, 0x63, 0xda, 0xa2, 0xea, 0xb8, 0x7d, 0xbc, 0x22, 0x1d, 0x33, 0x55, + 0xe3, 0x57, 0x5a, 0xde, 0x8b, 0x8f, 0xee, 0x28, 0x46, 0x8b, 0x24, 0xad, + 0xb8, 0x84, 0x9a, 0x68, 0xc3, 0x8e, 0x13, 0x08, 0xfc, 0x5a, 0x51, 0x3d, + 0x53, 0x1f, 0x62, 0xe9, 0xc8, 0x78, 0xec, 0x82, 0x43, 0x02, 0xad, 0xd3, + 0x7c, 0xa3, 0x02, 0xf7, 0x27, 0xed, 0x49, 0x48, 0xd1, 0x6b, 0x2f, 0xae, + 0x01, 0xed, 0xe4, 0xec, 0xd3, 0x08, 0x27, 0xdc, 0x12, 0x23, 0x2c, 0x3e, + 0xd6, 0x20, 0x0c, 0xbf, 0x30, 0x2e, 0x98, 0x18, 0xca, 0xa3, 0xd6, 0x1e, + 0xf0, 0xf7, 0xe2, 0xcd, 0x9a, 0x26, 0x45, 0x5a, 0x16, 0x5a, 0x91, 0xf7, + 0x6a, 0xc7, 0x7e, 0xb6, 0xa5, 0xfb, 0xd5, 0x5a, 0xbb, 0x47, 0xa7, 0x36, + 0x90, 0xe5, 0xbb, 0x6b, 0x28, 0x0c, 0x3f, 0xa2, 0xaf, 0x57, 0x1c, 0x46, + 0x06, 0x03, 0xb1, 0x86, 0x4f, 0x5a, 0x72, 0xac, 0x02, 0x43, 0x0a, 0xd6, + 0xdf, 0xe0, 0x77, 0xc2, 0xa0, 0x78, 0x1b, 0xe9, 0x69, 0x8d, 0xf0, 0x6b, + 0x4f, 0x97, 0xfc, 0xea, 0xfa, 0xa5, 0xbf, 0x41, 0x78, 0xd8, 0x6e, 0xa1, + 0x6c, 0xae, 0xfe, 0x76, 0xdb, 0x02, 0xed, 0xf6, 0xdb, 0xcf, 0x0b, 0xc0, + 0xfe, 0x21, 0x77, 0x8d, 0x68, 0x08, 0x1a, 0xe4, 0x80, 0x2e, 0x93, 0xf2, + 0x85, 0xeb, 0x63, 0x97, 0x7a, 0xf2, 0xd8, 0x26, 0xed, 0xe6, 0x52, 0xe9, + 0xcf, 0x05, 0x7e, 0x81, 0x0a, 0xe8, 0xab, 0x9e, 0x2c, 0x8f, 0xe2, 0x5b, + 0x23, 0x75, 0x4c, 0x4a, 0xd1, 0xf6, 0xd1, 0x86, 0x11, 0x0d, 0x4f, 0xb7, + 0xa8, 0xae, 0x40, 0x84, 0xf0, 0xa1, 0xea, 0x12, 0x40, 0xf6, 0x76, 0xd1, + 0x4f, 0x44, 0xd1, 0xd6, 0x19, 0xaa, 0x96, 0xba, 0x27, 0x4a, 0x96, 0xd9, + 0x20, 0x62, 0xff, 0x8e, 0x2d, 0x04, 0xbb, 0x42, 0x82, 0x47, 0x36, 0x84, + 0xd2, 0x15, 0x7a, 0xa3, 0x30, 0x56, 0x73, 0x6a, 0x2f, 0x83, 0xac, 0xd1, + 0xee, 0x1c, 0xf6, 0x9c, 0x1f, 0xdc, 0x22, 0x08, 0x8f, 0xf2, 0x99, 0x8d, + 0x41, 0x2e, 0x7d, 0xa6, 0xb4, 0x36, 0xd4, 0x9c, 0x22, 0x30, 0x0e, 0x81, + 0xfe, 0x49, 0xec, 0xd5, 0x66, 0xd7, 0xd4, 0xca, 0xe7, 0x7e, 0x8b, 0x62, + 0x91, 0x04, 0xe8, 0xe2, 0x48, 0x39, 0x4b, 0x95, 0x82, 0x01, 0x61, 0xfd, + 0x40, 0xca, 0x50, 0xe4, 0xa0, 0x92, 0x24, 0x72, 0x2e, 0x18, 0x25, 0x8f, + 0x7d, 0x99, 0x7c, 0xe7, 0x83, 0x19, 0xe6, 0x67, 0x80, 0xbc, 0x87, 0xd5, + 0xff, 0xa1, 0x51, 0x32, 0x17, 0x44, 0x37, 0xc7, 0x3f, 0x52, 0x6c, 0x75, + 0x99, 0x35, 0xd3, 0x39, 0x07, 0xdf, 0x80, 0xfb, 0x59, 0xb9, 0xcc, 0x0e, + 0x7e, 0x74, 0xd5, 0xfd, 0x64, 0xcc, 0x40, 0x99, 0x75, 0xaf, 0xae, 0xb6, + 0xaf, 0x12, 0xce, 0xdd, 0xa5, 0x05, 0x9e, 0xa0, 0x59, 0x55, 0x6c, 0xfd, + 0x65, 0x21, 0x70, 0x04, 0x27, 0x70, 0x08, 0x46, 0xd5, 0x79, 0xcd, 0x32, + 0x49, 0xb5, 0x3e, 0xee, 0x96, 0xd3, 0x29, 0x7d, 0x59, 0x1a, 0xf8, 0x00, + 0x9c, 0x25, 0xc2, 0xbb, 0x94, 0x4f, 0x67, 0x17, 0xe7, 0xe1, 0x34, 0x3a, + 0xe3, 0x52, 0x43, 0x40, 0x31, 0x72, 0x23, 0xc0, 0x13, 0xd2, 0xf6, 0xf0, + 0xb9, 0x66, 0xdd, 0xa1, 0xb6, 0xc0, 0x19, 0xb0, 0xfa, 0x11, 0xfc, 0x9c, + 0xbf, 0xff, 0x87, 0xe1, 0x8a, 0x14, 0xea, 0xb7, 0x8a, 0xd2, 0xfe, 0x54, + 0x20, 0x94, 0x0b, 0xa6, 0x17, 0x08, 0x91, 0x32, 0xa2, 0xe2, 0x29, 0x56, + 0x56, 0xe7, 0x6b, 0x32, 0x24, 0xe2, 0xaa, 0xf1, 0x63, 0x19, 0x5a, 0x1c, + 0x45, 0xff, 0x10, 0xac, 0xa1, 0xb7, 0xfc, 0x6f, 0x28, 0xc3, 0x1f, 0xb3, + 0x24, 0x72, 0x35, 0xaa, 0xb7, 0x46, 0x55, 0x01, 0xa7, 0xe3, 0xd3, 0xaf, + 0xfa, 0x7f, 0x08, 0x3b, 0xb2, 0xeb, 0xce, 0xa8, 0xf6, 0xc5, 0xc8, 0xb3, + 0x37, 0xa0, 0x31, 0x44, 0xcc, 0x91, 0xee, 0x68, 0x9f, 0x8c, 0x59, 0x88, + 0x1e, 0x7c, 0xbe, 0x39, 0x00, 0x8b, 0x40, 0x41, 0xa9, 0x39, 0x6b, 0xf6, + 0x46, 0x80, 0x4b, 0x71, 0x4c, 0x52, 0xd8, 0x1e, 0xb8, 0x56, 0x48, 0x26, + 0xb8, 0xf1, 0x44, 0x86, 0x26, 0x5d, 0x56, 0xed, 0x97, 0x1c, 0x11, 0x22, + 0xea, 0x7c, 0x84, 0xf8, 0x57, 0x38, 0x86, 0x70, 0xa5, 0x33, 0xe2, 0x5c, + 0x40, 0x0a, 0xb0, 0xaf, 0xea, 0x7a, 0x70, 0xcf, 0x5b, 0x91, 0xe6, 0xa2, + 0xb0, 0x6b, 0x1b, 0x2b, 0x5b, 0x23, 0x38, 0xa7, 0xe0, 0x8d, 0xa4, 0xd4, + 0xc4, 0xc1, 0xaf, 0xaa, 0xf4, 0x00, 0x57, 0x21, 0x21, 0x6f, 0x8a, 0xf0, + 0x70, 0x87, 0xc0, 0x76, 0xf1, 0x77, 0x2f, 0xe7, 0x21, 0x4b, 0xa0, 0x20, + 0xaf, 0x49, 0xe1, 0x51, 0xe4, 0x38, 0x66, 0x08, 0xa2, 0x5f, 0xf2, 0xd8, + 0x0f, 0xd8, 0xd1, 0x7d, 0xa1, 0x62, 0xcd, 0x64, 0xd6, 0x56, 0xd6, 0x4b, + 0xcd, 0x03, 0x1a, 0x4a, 0x3b, 0x74, 0x44, 0x99, 0x04, 0xfe, 0xe5, 0xd3, + 0xb6, 0x74, 0xa4, 0xa2, 0xa9, 0x9e, 0xb3, 0x76, 0x8b, 0xe6, 0x84, 0xb0, + 0xd8, 0x80, 0xa7, 0x10, 0xa5, 0x91, 0xac, 0x9c, 0x79, 0xea, 0xa6, 0x8c, + 0x54, 0x67, 0x41, 0x7b, 0xcf, 0x07, 0xc4, 0x83, 0xef, 0xbc, 0x95, 0xab, + 0x01, 0xa5, 0x56, 0x05, 0x94, 0xd1, 0x5d, 0xfa, 0xcd, 0x4f, 0xc1, 0xf4, + 0x39, 0x12, 0x36, 0xae, 0xe2, 0x05, 0x7e, 0xb5, 0xda, 0xdb, 0x4a, 0xe0, + 0x59, 0x86, 0xec, 0xec, 0x9d, 0x5c, 0xe9, 0x18, 0x6a, 0x5b, 0x44, 0x8b, + 0x24, 0x3f, 0x1b, 0x79, 0x98, 0x89, 0xf1, 0x15, 0xd2, 0x89, 0xd2, 0x83, + 0x93, 0x75, 0x92, 0xc2, 0x35, 0x18, 0xea, 0x0a, 0x6c, 0xc0, 0xeb, 0x30, + 0x10, 0x1c, 0xc3, 0xe3, 0xb9, 0x8f, 0x73, 0xc1, 0xce, 0x8b, 0x91, 0x81, + 0x9c, 0x75, 0x83, 0x83, 0xf1, 0x56, 0xfc, 0xc5, 0xdd, 0xc9, 0x65, 0x40, + 0x6c, 0x65, 0xc4, 0xdf, 0x64, 0xf5, 0x19, 0x1c, 0x5c, 0xbd, 0x22, 0x86, + 0xb1, 0x09, 0xb3, 0x88, 0x8d, 0xec, 0x12, 0xaa, 0x9c, 0x6a, 0x05, 0x53, + 0x25, 0xa7, 0x3f, 0x74, 0x1d, 0xef, 0xd8, 0xf0, 0x45, 0x56, 0x3b, 0x0e, + 0x53, 0xb9, 0x0d, 0x39, 0x68, 0xff, 0xa6, 0x97, 0x64, 0x62, 0x79, 0xd0, + 0xb8, 0x21, 0x5d, 0xed, 0x9f, 0xe5, 0x2f, 0x20, 0x0e, 0x0b, 0x61, 0x34, + 0xfe, 0x3e, 0x4a, 0x06, 0x3a, 0x51, 0xb1, 0x2a, 0x38, 0xda, 0x4f, 0xed, + 0x7e, 0xcb, 0x89, 0x8e, 0x05, 0x7d, 0xf8, 0x0f, 0x52, 0xbe, 0x23, 0x8e, + 0x80, 0xd3, 0x76, 0x26, 0xb5, 0xd7, 0x13, 0xa2, 0x1a, 0x1e, 0x45, 0x3a, + 0x38, 0xf1, 0x51, 0x41, 0x2c, 0x1c, 0xe2, 0xf4, 0x8c, 0x97, 0x2a, 0x61, + 0xdd, 0x4d, 0xf5, 0x51, 0xda, 0x4e, 0xed, 0xb3, 0x9a, 0xa9, 0xc6, 0x5c, + 0xe5, 0xc6, 0xbb, 0xd6, 0x94, 0xeb, 0x42, 0x13, 0xa2, 0x2e, 0x10, 0x5c, + 0x30, 0xfb, 0x84, 0x28, 0xd0, 0xc9, 0x7c, 0x3c, 0x61, 0xd2, 0xbb, 0x45, + 0x80, 0xa2, 0xe1, 0xed, 0x11, 0x86, 0xb2, 0xb7, 0x47, 0x17, 0x40, 0x35, + 0xad, 0x8b, 0xcc, 0x66, 0xe0, 0xc0, 0x2a, 0xa2, 0x4d, 0x75, 0xae, 0xf2, + 0xa2, 0xba, 0xa8, 0x4b, 0xc1, 0x4c, 0x51, 0x31, 0x06, 0x96, 0xf5, 0x2c, + 0x5c, 0xf6, 0x11, 0xae, 0x68, 0x3c, 0x89, 0xe4, 0x33, 0xc7, 0x88, 0xab, + 0x2f, 0xee, 0xd6, 0xe4, 0xbc, 0x4e, 0xb2, 0x4d, 0x88, 0x16, 0x6a, 0xc5, + 0x0b, 0xfd, 0xe3, 0x0f, 0x5b, 0xf3, 0x5e, 0xbd, 0x40, 0x21, 0x2e, 0x20, + 0x4f, 0x96, 0xc6, 0x82, 0x4a, 0xf4, 0x3c, 0xae, 0xbf, 0xd0, 0x05, 0xc0, + 0x92, 0x2c, 0xea, 0x18, 0x07, 0x0e, 0x56, 0xe9, 0xc7, 0xfb, 0xdb, 0xc3, + 0xb3, 0x4e, 0x1c, 0x9a, 0x3c, 0x7a, 0x24, 0x72, 0x1d, 0xe6, 0x47, 0x43, + 0xf3, 0xec, 0x1b, 0xa8, 0xfa, 0x9c, 0x31, 0x1a, 0x6a, 0x3c, 0x0e, 0xca, + 0xc5, 0x3b, 0x5f, 0x2e, 0x5a, 0x4d, 0xfd, 0x30, 0x96, 0x4d, 0x3e, 0x60, + 0xe1, 0x23, 0xb1, 0x42, 0xc9, 0xdb, 0x10, 0xf2, 0xac, 0x07, 0xc9, 0x73, + 0xf6, 0x85, 0xd2, 0xb3, 0xe8, 0x3d, 0x68, 0xef, 0x0e, 0x43, 0x32, 0xd2, + 0x4d, 0xa2, 0x4a, 0x54, 0xe8, 0xa8, 0xc9, 0xa5, 0x68, 0xb6, 0x81, 0xb7, + 0x20, 0xa7, 0x2e, 0x67, 0x68, 0xfc, 0xd1, 0x1b, 0xea, 0x68, 0xb5, 0xc0, + 0xf4, 0xbb, 0x45, 0xa0, 0xbb, 0xf4, 0x9f, 0x40, 0xb1, 0x11, 0xb1, 0x85, + 0x9c, 0x53, 0xfa, 0x5d, 0x4c, 0x30, 0x5f, 0xad, 0x7b, 0xf7, 0x52, 0x6f, + 0x38, 0x3f, 0xe4, 0x95, 0xa2, 0x6b, 0xde, 0xa6, 0x4c, 0xe0, 0x34, 0xd6, + 0x9a, 0x8a, 0x85, 0x59, 0xb5, 0xe2, 0xa4, 0xb9, 0x34, 0xcc, 0x05, 0x84, + 0x55, 0x25, 0x8c, 0xc7, 0x83, 0x8e, 0x56, 0xd8, 0x94, 0xce, 0x10, 0x2c, + 0x1b, 0x97, 0xc4, 0x09, 0x73, 0x99, 0x2e, 0xa6, 0xb9, 0x96, 0x62, 0x23, + 0x84, 0xc7, 0xda, 0xef, 0x94, 0xb3, 0x02, 0x3b, 0xd8, 0x9e, 0xba, 0x19, + 0x92, 0x78, 0x03, 0x55, 0xdd, 0x6f, 0xe4, 0x99, 0x16, 0xc8, 0xeb, 0x14, + 0x55, 0xaa, 0x70, 0xb9, 0xbc, 0x68, 0x9f, 0x8f, 0xd1, 0xe2, 0x5e, 0x8b, + 0x63, 0xdb, 0x15, 0xff, 0x8e, 0xde, 0x49, 0xaa, 0xbe, 0x60, 0xb8, 0xc4, + 0xa5, 0xf7, 0x6d, 0xbb, 0x1e, 0x92, 0x61, 0x00, 0x65, 0x6c, 0x4b, 0x12, + 0xb7, 0xc3, 0x77, 0x50, 0xb9, 0x7f, 0xb0, 0xd7, 0x51, 0xd1, 0x7f, 0xe7, + 0x8f, 0xb4, 0x3c, 0xf7, 0xc0, 0x88, 0x45, 0xc6, 0x51, 0x92, 0x45, 0xd9, + 0x71, 0x43, 0x2e, 0xfa, 0x84, 0xf5, 0xd8, 0xc9, 0xa8, 0x8b, 0xdf, 0x54, + 0xcf, 0x05, 0xba, 0x4d, 0x7a, 0xb2, 0x0f, 0xed, 0x44, 0x56, 0xc5, 0x70, + 0xff, 0xa1, 0x24, 0xc6, 0xb8, 0x95, 0xad, 0xf4, 0xf5, 0x2a, 0x87, 0xea, + 0x3d, 0x04, 0xfc, 0x41, 0x3f, 0xea, 0xa1, 0x97, 0x9a, 0x6f, 0x76, 0x70, + 0xf3, 0xf6, 0xe5, 0xf8, 0x7e, 0xf7, 0x03, 0x6f, 0xce, 0x6b, 0x80, 0xeb, + 0x15, 0xda, 0x65, 0x5d, 0x07, 0x72, 0x62, 0x16, 0x64, 0x7e, 0x71, 0x0c, + 0xf8, 0x80, 0xe9, 0x84, 0xc4, 0xea, 0x55, 0xea, 0xaa, 0x57, 0xb5, 0x4e, + 0x10, 0x34, 0xcb, 0x6a, 0x76, 0x9e, 0x49, 0x44, 0xa8, 0x2b, 0x83, 0xfe, + 0x69, 0x92, 0xfa, 0x8f, 0xc6, 0x7a, 0x1d, 0xd1, 0x59, 0xab, 0x5d, 0xd3, + 0x24, 0xe4, 0xc1, 0xa2, 0x38, 0xd6, 0x0e, 0xce, 0xa9, 0x8b, 0xa6, 0xbc, + 0xf0, 0x92, 0x54, 0x8a, 0xdb, 0xa0, 0x09, 0xc9, 0xe3, 0x43, 0x3e, 0xa6, + 0x1c, 0xf2, 0x6f, 0x48, 0x3e, 0xe0, 0x47, 0xa5, 0xac, 0x2e, 0x75, 0x38, + 0x24, 0xc7, 0xe0, 0x22, 0xa3, 0xe4, 0x7a, 0x8d, 0xec, 0xb5, 0x96, 0x66, + 0x0a, 0x51, 0x49, 0x31, 0xf5, 0xaf, 0xfa, 0xbe, 0x61, 0xd0, 0xb5, 0x6a, + 0x7c, 0xea, 0x7f, 0x1a, 0x61, 0x3a, 0xf3, 0x7d, 0x2f, 0x6f, 0x44, 0xfd, + 0x24, 0x0f, 0x47, 0xd4, 0x17, 0xa3, 0x6f, 0x63, 0x4d, 0x02, 0xd1, 0xe3, + 0x23, 0x7d, 0xfe, 0x54, 0x86, 0x43, 0xb7, 0xf6, 0x8e, 0xab, 0xf6, 0x01, + 0xe4, 0x82, 0x76, 0xdf, 0x03, 0xb2, 0x08, 0x0d, 0x27, 0x81, 0x4c, 0x1b, + 0x8f, 0x7f, 0x5a, 0xd0, 0xb5, 0x74, 0x26, 0x65, 0x4b, 0xd9, 0x46, 0x4d, + 0xe9, 0x45, 0x58, 0x64, 0xa4, 0x87, 0x05, 0xf1, 0x86, 0xd3, 0x6a, 0xa9, + 0x53, 0x7b, 0x12, 0x7a, 0x99, 0xe7, 0xc9, 0x8d, 0x1d, 0x97, 0x94, 0x00, + 0x2f, 0x15, 0x63, 0xba, 0xcf, 0x82, 0x30, 0x74, 0xde, 0xfe, 0xd3, 0x1d, + 0x13, 0x37, 0x2d, 0xc0, 0xa7, 0x5d, 0xf7, 0x49, 0xe9, 0x58, 0x7b, 0x91, + 0xfc, 0xd2, 0x29, 0xda, 0xf5, 0x99, 0x03, 0x3c, 0x11, 0xd5, 0x61, 0x26, + 0xd2, 0xe8, 0x42, 0xc9, 0xca, 0xda, 0xd5, 0xc9, 0xef, 0xb9, 0x2f, 0x23, + 0x37, 0x2a, 0xf5, 0xd1, 0x8f, 0x02, 0x42, 0x6d, 0x11, 0x70, 0x32, 0x6f, + 0x67, 0xc6, 0x38, 0x07, 0x06, 0x61, 0x6f, 0xb7, 0x58, 0xf0, 0xcc, 0x56, + 0x3a, 0x36, 0xf3, 0xb1, 0x7a, 0x2f, 0x22, 0x28, 0x22, 0xf8, 0xad, 0xba, + 0xb8, 0xa2, 0xf4, 0x77, 0x89, 0x38, 0xe2, 0xa1, 0x63, 0x6e, 0xc2, 0x7a, + 0x23, 0xea, 0x8a, 0xd5, 0x8d, 0x2c, 0x21, 0x2d, 0x78, 0x50, 0xd7, 0xe6, + 0x58, 0x43, 0x15, 0x33, 0x48, 0xc4, 0xc6, 0x7a, 0x65, 0xae, 0x5d, 0x02, + 0xd3, 0xcd, 0xa0, 0x83, 0xfd, 0x81, 0x16, 0x18, 0x7d, 0x1c, 0x82, 0x7b, + 0xca, 0xf4, 0x82, 0xd2, 0x67, 0xe6, 0x78, 0x83, 0x63, 0x54, 0xc9, 0x25, + 0x44, 0xde, 0xb7, 0x19, 0xa8, 0x28, 0xb7, 0x93, 0x88, 0xce, 0xa1, 0x03, + 0x2c, 0x8c, 0xc8, 0xa9, 0xab, 0x25, 0x2e, 0xa2, 0xd1, 0x07, 0xf8, 0xda, + 0x90, 0xb1, 0xf6, 0xf0, 0xd9, 0x28, 0x46, 0xba, 0x8b, 0x4a, 0xfb, 0x56, + 0x9b, 0x4c, 0x57, 0x65, 0x81, 0x39, 0xf5, 0xcb, 0x63, 0x91, 0xf6, 0x8f, + 0x55, 0x81, 0xc8, 0xc6, 0x68, 0xf1, 0x19, 0x7e, 0x09, 0x66, 0x97, 0x1d, + 0xd3, 0x03, 0xa1, 0xeb, 0xd7, 0x30, 0x9a, 0xd0, 0x0e, 0x7c, 0x15, 0x71, + 0x95, 0x3c, 0x85, 0x94, 0x7f, 0xbe, 0x93, 0xe3, 0xcb, 0x83, 0x4e, 0xc6, + 0x0b, 0x80, 0x50, 0x92, 0x55, 0x71, 0xf0, 0x16, 0x58, 0x52, 0x46, 0x0c, + 0xef, 0x33, 0x6f, 0x12, 0xb0, 0xf5, 0x67, 0xda, 0xd1, 0xcc, 0xbc, 0xf8, + 0x1b, 0x3c, 0x4b, 0xf7, 0x92, 0x0b, 0xc4, 0x48, 0xa2, 0xdb, 0x7b, 0x8c, + 0x71, 0xb7, 0x68, 0x00, 0x2e, 0x25, 0x45, 0xb6, 0xc4, 0x23, 0x08, 0x21, + 0x52, 0x90, 0x96, 0xa9, 0xdc, 0xb9, 0xce, 0x45, 0x83, 0xa7, 0x55, 0xce, + 0xf0, 0x95, 0x3a, 0xc0, 0xc2, 0xb8, 0x01, 0xae, 0x8c, 0x26, 0x0b, 0x54, + 0x2a, 0x8d, 0x8f, 0xb2, 0x33, 0x4d, 0x0f, 0x76, 0x66, 0x81, 0x09, 0x7e, + 0xaa, 0x6d, 0x3f, 0x38, 0x39, 0x4c, 0xdf, 0x3f, 0x70, 0x5f, 0x89, 0xe4, + 0x33, 0xf6, 0x92, 0x3b, 0xd9, 0xc7, 0xe9, 0xc2, 0x68, 0x28, 0xdf, 0xef, + 0xd1, 0xb8, 0x1c, 0xb9, 0xaa, 0xbb, 0xf9, 0xf8, 0x03, 0xcf, 0xeb, 0x4d, + 0x58, 0x89, 0x38, 0x92, 0xf7, 0x07, 0x90, 0xc0, 0xe4, 0x43, 0x18, 0xca, + 0x6c, 0x08, 0xce, 0x81, 0xe9, 0xff, 0x5a, 0x5d, 0x1c, 0x1f, 0x35, 0x60, + 0x39, 0x47, 0x16, 0xc9, 0x97, 0x53, 0x1c, 0x2f, 0x91, 0x85, 0xfe, 0x51, + 0xbd, 0x06, 0x8d, 0x6b, 0x9c, 0xb2, 0xe0, 0x94, 0xe7, 0xde, 0x7d, 0x8d, + 0x78, 0xc4, 0x18, 0x42, 0x43, 0x35, 0x44, 0x91, 0x42, 0x57, 0x82, 0x41, + 0x16, 0x8b, 0x69, 0x0b, 0x83, 0xc9, 0x17, 0xa7, 0x0f, 0x0c, 0xc4, 0x45, + 0xe7, 0xab, 0x74, 0xe5, 0xd1, 0xd3, 0xd8, 0x63, 0x87, 0xed, 0xcd, 0x45, + 0xc4, 0x14, 0x9f, 0xfc, 0xdc, 0x75, 0xd6, 0x2d, 0x9d, 0xe5, 0x51, 0xce, + 0x78, 0x5a, 0xc9, 0x9a, 0xb4, 0x56, 0xc3, 0x0e, 0xe5, 0xde, 0xe7, 0x6f, + 0x3e, 0x6c, 0xd3, 0x36, 0xb4, 0x36, 0x1b, 0xc0, 0x40, 0x0c, 0x8d, 0xfe, + 0x02, 0x58, 0x11, 0x26, 0xff, 0x5e, 0xb4, 0x84, 0x42, 0x9d, 0xa4, 0x95, + 0xd7, 0xdb, 0x56, 0xca, 0xd6, 0xcc, 0x79, 0x97, 0xbe, 0xa9, 0x9a, 0xbb, + 0x25, 0x80, 0xb7, 0x33, 0x4c, 0x45, 0x3b, 0x42, 0x68, 0x53, 0xf7, 0x71, + 0x6e, 0xa6, 0xf5, 0xad, 0xdc, 0xe1, 0xc9, 0xc8, 0x1c, 0xdc, 0xd8, 0x5f, + 0xa2, 0xb5, 0x42, 0x1d, 0x53, 0x08, 0x6a, 0x31, 0x52, 0xdb, 0xd1, 0x04, + 0x4d, 0xfc, 0x52, 0x27, 0xad, 0x9e, 0x4a, 0xcc, 0xa6, 0x32, 0x22, 0xf9, + 0xec, 0x74, 0x70, 0x16, 0xfc, 0xc3, 0x56, 0x0b, 0x37, 0xeb, 0x3e, 0xd5, + 0x5c, 0xa0, 0x31, 0x7f, 0x78, 0xe9, 0x80, 0xd3, 0x01, 0xfa, 0x3a, 0xe8, + 0xda, 0xd0, 0x49, 0xc1, 0x2c, 0x18, 0x76, 0x30, 0x42, 0xcc, 0x01, 0x3c, + 0xf5, 0x4d, 0x04, 0xa7, 0x65, 0xd5, 0xa5, 0x22, 0x33, 0x36, 0xac, 0x97, + 0x2a, 0x10, 0xa8, 0xf7, 0x63, 0xb3, 0xcb, 0x78, 0x4f, 0xcf, 0xfc, 0x62, + 0x60, 0x89, 0x68, 0xe6, 0x0a, 0xd1, 0xa7, 0x9e, 0x86, 0x6e, 0x9b, 0xfa, + 0xf1, 0x0d, 0xe3, 0xb8, 0x06, 0x50, 0xd5, 0x51, 0xa8, 0x36, 0x8e, 0xa8, + 0xa1, 0x38, 0xc8, 0xbe, 0x84, 0x39, 0x1c, 0x02, 0x3d, 0x47, 0x49, 0x17, + 0xa4, 0x9a, 0x98, 0x39, 0x80, 0xf1, 0x9a, 0x13, 0x4a, 0xdf, 0x44, 0x69, + 0x2d, 0x4a, 0x0c, 0x41, 0x17, 0xbf, 0xbe, 0xbf, 0x87, 0xa2, 0xd0, 0x13, + 0xa8, 0xb6, 0x1e, 0x98, 0x84, 0xfd, 0xe3, 0xd9, 0xff, 0x42, 0xaa, 0x6e, + 0x61, 0xe3, 0xb4, 0x9c, 0x66, 0xbc, 0x10, 0xf2, 0x9f, 0xaf, 0x29, 0x7d, + 0xc9, 0x5d, 0xc7, 0xd3, 0x45, 0x44, 0x2b, 0x6f, 0x81, 0x57, 0x28, 0x94, + 0xb7, 0xa2, 0xd5, 0x7d, 0xcf, 0x89, 0xd0, 0xe8, 0x83, 0x4c, 0x5a, 0xec, + 0x3c, 0xa2, 0x69, 0xb9, 0xfd, 0x9b, 0x8b, 0xb7, 0xa1, 0x86, 0xa9, 0x65, + 0xf9, 0x85, 0x0f, 0x2e, 0x9b, 0xf0, 0xbe, 0x48, 0xec, 0x1c, 0x39, 0x4c, + 0xe0, 0x8b, 0xa9, 0x86, 0xa8, 0x05, 0x7e, 0xdc, 0x3a, 0x83, 0x8f, 0x19, + 0x17, 0xb0, 0x8b, 0x4a, 0x6a, 0xc2, 0xe5, 0xf5, 0xc7, 0x5f, 0x91, 0x16, + 0x7f, 0xc1, 0x4f, 0xd9, 0x9d, 0xfe, 0x54, 0x0f, 0xdf, 0x57, 0xf1, 0x90, + 0xa9, 0xf1, 0x9f, 0x1b, 0x5c, 0xa2, 0xdc, 0x6e, 0x2b, 0x39, 0xb4, 0x50, + 0xe2, 0x52, 0xd7, 0xf9, 0x48, 0xc0, 0xc2, 0xdd, 0x9e, 0x86, 0xd9, 0x10, + 0x44, 0xb4, 0x31, 0xef, 0xbf, 0x36, 0x13, 0xfa, 0x15, 0x06, 0x3a, 0xa0, + 0xca, 0x37, 0x49, 0xb2, 0x05, 0x7d, 0x95, 0x41, 0xbb, 0x12, 0xcc, 0xf5, + 0xe6, 0xdf, 0xfd, 0x96, 0x9c, 0x30, 0xa3, 0xc7, 0x36, 0x1b, 0xf5, 0x21, + 0x55, 0xce, 0x63, 0xa9, 0xa9, 0x7a, 0x47, 0xf8, 0xdc, 0xb8, 0xfb, 0xde, + 0x0a, 0xee, 0xf9, 0x7a, 0x5d, 0xb9, 0x56, 0xb3, 0x28, 0xca, 0x2f, 0x8b, + 0xb7, 0xc7, 0x1a, 0x99, 0x18, 0x13, 0xdd, 0x2a, 0xff, 0xc7, 0x5c, 0x84, + 0x98, 0x42, 0x33, 0x23, 0x8f, 0x8b, 0x55, 0xcb, 0xed, 0x32, 0x59, 0xfe, + 0x6d, 0xda, 0x7c, 0xfb, 0xac, 0x74, 0xac, 0x66, 0xde, 0x40, 0x2a, 0x4e, + 0x49, 0x96, 0x1d, 0xc8, 0xca, 0x1b, 0xcd, 0xdf, 0x46, 0x05, 0xf2, 0x16, + 0x92, 0x71, 0x2c, 0xa3, 0x1c, 0x56, 0x4f, 0xc9, 0x1f, 0x32, 0x53, 0xce, + 0xb1, 0xd0, 0x50, 0x3d, 0xa9, 0x78, 0x9c, 0x57, 0x61, 0x2c, 0x0e, 0x21, + 0x6e, 0xf1, 0xd6, 0x20, 0x79, 0xc8, 0x2c, 0xad, 0x00, 0x96, 0xae, 0xe6, + 0xa6, 0x66, 0x1c, 0xd8, 0x1f, 0x46, 0xec, 0x74, 0x2e, 0xc4, 0xff, 0xd7, + 0x38, 0x5c, 0x77, 0x05, 0xf3, 0x5d, 0xaa, 0x3f, 0x7f, 0x62, 0x76, 0xf7, + 0xc9, 0xea, 0x1c, 0x14, 0xbf, 0xd8, 0x3d, 0x59, 0x8a, 0xed, 0x9e, 0x5b, + 0x73, 0xbb, 0x45, 0x29, 0x01, 0x34, 0xb7, 0xff, 0x46, 0xe7, 0xe7, 0xeb, + 0x23, 0x08, 0x57, 0xb7, 0x23, 0x01, 0xea, 0xdb, 0x3c, 0xbe, 0xdf, 0x7e, + 0xb3, 0x3b, 0xe9, 0xef, 0x16, 0x6f, 0x3e, 0x72, 0x4e, 0x4c, 0x98, 0x78, + 0x70, 0xe0, 0x17, 0x20, 0xdb, 0x8a, 0x72, 0xb9, 0x8d, 0x21, 0xba, 0x9c, + 0x69, 0xf4, 0x7b, 0x70, 0xfd, 0x46, 0xfe, 0x79, 0xab, 0x1a, 0xe1, 0x91, + 0x1a, 0x3d, 0xd2, 0x3f, 0x64, 0x5c, 0xa7, 0xb4, 0x4d, 0xf9, 0xce, 0xd5, + 0x6a, 0xc7, 0x45, 0x52, 0x07, 0xdf, 0x93, 0x19, 0xc3, 0x9d, 0xe5, 0x3c, + 0xd3, 0xa5, 0xcc, 0x93, 0xb6, 0xbb, 0x6b, 0x58, 0xdc, 0x48, 0x83, 0xfa, + 0x1b, 0x95, 0x29, 0x44, 0x9d, 0x1f, 0x2c, 0x07, 0x6d, 0x27, 0xdd, 0xf5, + 0x7b, 0xb3, 0x79, 0xb6, 0xf0, 0x36, 0xb0, 0xad, 0xe9, 0x2d, 0x2c, 0xdd, + 0x1b, 0x73, 0xff, 0xc9, 0xaa, 0xb4, 0xc9, 0x6b, 0x1b, 0x2a, 0xe7, 0x45, + 0x24, 0x77, 0x8b, 0x8c, 0x21, 0xc4, 0xf3, 0x04, 0x71, 0x06, 0xbd, 0xcc, + 0xec, 0x94, 0x5c, 0x92, 0x6d, 0x64, 0x91, 0x98, 0xe8, 0x5a, 0x97, 0x18, + 0x72, 0x0c, 0xdc, 0x2a, 0x8b, 0xd9, 0x18, 0x13, 0x25, 0x11, 0xa6, 0xf1, + 0x81, 0x1e, 0x4d, 0x63, 0x70, 0xdc, 0xd9, 0x9b, 0x2b, 0xe7, 0x3f, 0x0e, + 0x96, 0xe8, 0x8c, 0x73, 0x68, 0xdb, 0x20, 0x65, 0x82, 0xd1, 0x76, 0x36, + 0xd2, 0x84, 0x42, 0x6e, 0xd2, 0x6b, 0xa5, 0xd8, 0x50, 0x10, 0x3f, 0x8e, + 0x51, 0x05, 0x06, 0xce, 0xd9, 0xd0, 0x0c, 0x1b, 0x42, 0xc2, 0x10, 0x8c, + 0x14, 0x8e, 0x34, 0xc8, 0x0d, 0xdc, 0x64, 0x49, 0x6e, 0x70, 0x78, 0xb3, + 0x8b, 0x98, 0x07, 0x9b, 0x75, 0x96, 0x51, 0x7e, 0x18, 0xb3, 0x51, 0x68, + 0x92, 0x22, 0x23, 0xf0, 0x43, 0x51, 0xb6, 0x3a, 0x71, 0x70, 0x5e, 0x60, + 0xa8, 0x9d, 0xa3, 0x36, 0xca, 0x72, 0x29, 0xfc, 0xdb, 0xd5, 0xee, 0x5d, + 0x95, 0x40, 0xf2, 0xf1, 0xe1, 0x26, 0xa1, 0x8b, 0x7a, 0x3f, 0x77, 0x4d, + 0xaf, 0xa9, 0x1d, 0x60, 0x4f, 0x41, 0xbd, 0x7f, 0x4d, 0xeb, 0xa5, 0xca, + 0x57, 0xea, 0xa4, 0x89, 0xbd, 0x15, 0xe0, 0x64, 0xd9, 0x5e, 0x54, 0x9b, + 0x0c, 0x38, 0x28, 0x45, 0xe5, 0xe4, 0x2e, 0x8a, 0x80, 0x1c, 0xe8, 0x3b, + 0x8e, 0xe7, 0x41, 0xb1, 0x91, 0x46, 0x31, 0x44, 0xe1, 0x51, 0x59, 0x15, + 0x2a, 0xb7, 0x1f, 0xb3, 0x91, 0x69, 0xf2, 0xf3, 0xbe, 0x41, 0x28, 0x94, + 0xdb, 0xf3, 0x94, 0xd5, 0x10, 0x30, 0xa4, 0x8e, 0xb7, 0x43, 0x9e, 0xbf, + 0x50, 0x7c, 0x91, 0xb4, 0x48, 0x3b, 0x18, 0x66, 0x53, 0xe1, 0xa9, 0x11, + 0xa7, 0x80, 0x6d, 0x91, 0x9a, 0xa5, 0x4d, 0x4e, 0x9d, 0x1e, 0xb5, 0xfd, + 0x7b, 0x43, 0x85, 0x98, 0x3e, 0x32, 0x29, 0xc0, 0xe0, 0xd2, 0xbf, 0xbb, + 0xf1, 0x82, 0x45, 0x05, 0x94, 0xe6, 0x62, 0x44, 0x27, 0xd1, 0x49, 0x50, + 0x3c, 0xd6, 0x1a, 0xd9, 0x3c, 0x9e, 0x67, 0x58, 0x88, 0xe2, 0x7c, 0x8b, + 0xa7, 0x10, 0xdf, 0x29, 0xef, 0x07, 0x95, 0xbc, 0xa5, 0x76, 0xf8, 0x61, + 0xd8, 0x7c, 0xaf, 0x26, 0x4c, 0x71, 0xec, 0xd6, 0xac, 0xf3, 0xcf, 0xdd, + 0x14, 0x8f, 0x22, 0x66, 0x98, 0x8f, 0xbc, 0x7b, 0xcf, 0x8e, 0xae, 0xb0, + 0x26, 0x8d, 0xf0, 0x9e, 0xdc, 0xf0, 0xc6, 0xbf, 0x31, 0xae, 0x53, 0x72, + 0x2b, 0xc7, 0x01, 0xed, 0xa0, 0x24, 0xf8, 0x8e, 0xf5, 0x1f, 0x53, 0x2d, + 0xbc, 0xf7, 0xbc, 0x10, 0xbe, 0xb8, 0x44, 0xd2, 0x28, 0x02, 0x26, 0x30, + 0x42, 0xe9, 0x82, 0x60, 0x93, 0xdb, 0xd0, 0x13, 0x1c, 0xfe, 0xa8, 0x2d, + 0x3f, 0xf0, 0xc9, 0xce, 0x00, 0x83, 0xf9, 0xfe, 0x1b, 0x05, 0xf5, 0x78, + 0xe7, 0xb0, 0x32, 0xd7, 0xec, 0xbc, 0x14, 0x4c, 0x94, 0x40, 0xd8, 0x35, + 0x87, 0xed, 0xa3, 0xd1, 0xb0, 0xd5, 0xb5, 0x1c, 0xb5, 0x75, 0xf8, 0x76, + 0xe4, 0x15, 0x9b, 0x93, 0x55, 0x4f, 0x60, 0x0a, 0xa8, 0x8a, 0x26, 0x1f, + 0x95, 0x97, 0xd7, 0xb3, 0xfa, 0x85, 0x74, 0xf6, 0x04, 0x70, 0x5b, 0x1d, + 0x24, 0x62, 0xa3, 0x78, 0x92, 0xea, 0xe0, 0xd1, 0x9e, 0x13, 0xa0, 0xab, + 0xba, 0x14, 0x78, 0x6b, 0x8a, 0xb1, 0xd8, 0xf8, 0x34, 0xfe, 0x32, 0xed, + 0xa5, 0x3b, 0x74, 0x07, 0xb2, 0x6b, 0x40, 0xa5, 0x82, 0x49, 0x58, 0xe1, + 0x20, 0x4c, 0x1b, 0x5d, 0x31, 0x45, 0x60, 0xf1, 0x41, 0x02, 0xdd, 0x99, + 0x9a, 0x8e, 0x99, 0x59, 0x81, 0x81, 0x19, 0x03, 0x23, 0xe2, 0x6a, 0x71, + 0x3d, 0x7f, 0xde, 0x62, 0xfa, 0xbd, 0x30, 0x7a, 0x3e, 0xbf, 0x68, 0x08, + 0xdb, 0x9e, 0xd7, 0x38, 0x6a, 0x9c, 0x2c, 0x3a, 0x7b, 0xf2, 0xdd, 0x69, + 0x77, 0x36, 0xcc, 0xe9, 0x73, 0x0e, 0xc8, 0x65, 0x73, 0xf2, 0x6f, 0x79, + 0x9f, 0x2f, 0x00, 0x84, 0xb9, 0x93, 0x45, 0x91, 0x3c, 0x5b, 0x34, 0x35, + 0x52, 0xfe, 0x46, 0x5b, 0x79, 0xc4, 0xb5, 0x8f, 0xbf, 0xc2, 0x3e, 0x89, + 0x62, 0x44, 0x83, 0xa8, 0x29, 0x50, 0xb7, 0x1e, 0x5a, 0x0f, 0xaf, 0x0a, + 0x21, 0x14, 0x81, 0x51, 0x5f, 0xfd, 0x65, 0x26, 0xa7, 0xec, 0x22, 0xa6, + 0x00, 0x04, 0xb8, 0x16, 0x57, 0x7b, 0xc9, 0x54, 0x7b, 0x1f, 0x3b, 0xad, + 0x9e, 0x34, 0x54, 0xc9, 0x91, 0x38, 0xb9, 0x94, 0x73, 0x69, 0xfe, 0x01, + 0xa6, 0x40, 0xeb, 0x69, 0x33, 0xe2, 0x3e, 0xcb, 0xee, 0x45, 0x66, 0xe0, + 0x64, 0xe2, 0x7e, 0xa7, 0xb5, 0x5f, 0x51, 0x25, 0xc9, 0xcd, 0x42, 0xae, + 0x1e, 0xd3, 0x22, 0xbc, 0x0a, 0xce, 0x98, 0xda, 0x8c, 0x53, 0x25, 0xde, + 0x38, 0x88, 0x97, 0x5b, 0xa6, 0xca, 0xe5, 0x0c, 0x35, 0x52, 0xd8, 0xfe, + 0x28, 0xc5, 0xba, 0x5e, 0xd1, 0x6f, 0x28, 0x1b, 0x98, 0xa7, 0x1f, 0x42, + 0x4a, 0xe9, 0x9a, 0x8d, 0x31, 0xb3, 0xcf, 0x64, 0xf8, 0xbd, 0x6f, 0x4c, + 0x6b, 0xfe, 0x65, 0xc1, 0xa3, 0xed, 0x4b, 0x76, 0x26, 0xe2, 0xce, 0x9d, + 0xe1, 0x8a, 0xe4, 0x73, 0xe3, 0xe0, 0x26, 0xe2, 0xab, 0x33, 0x89, 0xd2, + 0xb2, 0x11, 0x2c, 0xff, 0x07, 0x55, 0xb7, 0xea, 0x6e, 0x86, 0x66, 0x3b, + 0x44, 0xad, 0x8b, 0x9d, 0xe1, 0x36, 0x20, 0x5e, 0xfb, 0x3f, 0xcb, 0x2e, + 0x67, 0x39, 0x4f, 0xbd, 0x45, 0x79, 0x8b, 0x9e, 0x9d, 0x46, 0x43, 0xd8, + 0x4a, 0xdf, 0x9b, 0xa5, 0xd5, 0xe8, 0x37, 0x8b, 0x94, 0x76, 0x9e, 0xec, + 0x55, 0xf2, 0xb3, 0xd0, 0x98, 0xe0, 0xf9, 0x74, 0xe7, 0x29, 0xbf, 0x9f, + 0xef, 0x67, 0x6f, 0x4a, 0xb0, 0x34, 0xd5, 0x70, 0x7e, 0x28, 0x31, 0x80, + 0x83, 0xf8, 0x1a, 0xb4, 0xc4, 0xfb, 0x6c, 0xe1, 0x65, 0xd7, 0x07, 0x4d, + 0x9b, 0xd0, 0x51, 0x9f, 0x8f, 0x6d, 0x07, 0x31, 0xb3, 0x90, 0x43, 0xa9, + 0x4b, 0xce, 0x80, 0xb4, 0xa8, 0x8f, 0x63, 0x64, 0xab, 0x8a, 0xff, 0x86, + 0x1b, 0xec, 0xf7, 0xfe, 0xb5, 0xdf, 0xcb, 0x80, 0x81, 0xec, 0x06, 0xda, + 0xb1, 0x04, 0xcc, 0x6a, 0x03, 0x39, 0x79, 0x4a, 0x04, 0x6b, 0x3c, 0x2c, + 0x88, 0xc8, 0x3f, 0x50, 0x66, 0xb1, 0xe9, 0xae, 0x17, 0x57, 0x54, 0x41, + 0xa2, 0x05, 0xf5, 0xdd, 0x47, 0xe7, 0xab, 0x1e, 0x22, 0x7f, 0xc3, 0xee, + 0x7d, 0x37, 0x48, 0x07, 0xad, 0x89, 0xd3, 0x40, 0x2d, 0x34, 0x88, 0x81, + 0x44, 0x7a, 0x14, 0xea, 0x42, 0xb2, 0x7e, 0xd5, 0x3f, 0xc8, 0xf6, 0x68, + 0x63, 0x45, 0x58, 0x3b, 0xf9, 0x0b, 0xc5, 0x31, 0x60, 0x9a, 0x60, 0xed, + 0xee, 0x90, 0x05, 0x78, 0x15, 0xea, 0xb9, 0xeb, 0xff, 0x8f, 0x51, 0xaa, + 0xe3, 0x30, 0xfd, 0xfc, 0xef, 0x2c, 0xe0, 0x4f, 0xf1, 0x21, 0xf6, 0x69, + 0x02, 0x33, 0x31, 0x1b, 0xf3, 0xee, 0x23, 0x85, 0xf5, 0xf9, 0xa1, 0x33, + 0xb6, 0xe2, 0x13, 0x99, 0x98, 0xbf, 0xa4, 0x62, 0x5f, 0x79, 0xd7, 0x0e, + 0x7c, 0xaa, 0x96, 0x36, 0x7b, 0x31, 0xe4, 0xf0, 0xe6, 0x2b, 0x4c, 0x39, + 0xcf, 0x2d, 0x63, 0xb9, 0x72, 0xe6, 0xe3, 0x97, 0xb4, 0xf7, 0x45, 0xea, + 0x44, 0xc9, 0xdf, 0xc5, 0x47, 0xb0, 0x1a, 0x80, 0x0e, 0x9e, 0x38, 0x09, + 0xc8, 0x83, 0x7c, 0xb0, 0x55, 0x63, 0x68, 0xda, 0x18, 0x61, 0xf7, 0xa1, + 0x08, 0xec, 0xdf, 0xe4, 0x98, 0xd9, 0xc9, 0xbf, 0xdd, 0x68, 0x71, 0x0c, + 0x3f, 0x8a, 0x5d, 0x01, 0x83, 0xaa, 0x9e, 0x67, 0xbf, 0xb5, 0x6a, 0x37, + 0xea, 0x0c, 0x8c, 0x41, 0x04, 0x41, 0x6f, 0xfa, 0xf4, 0x5f, 0xc8, 0x8b, + 0x1f, 0xf8, 0xf6, 0x16, 0x52, 0x33, 0x0b, 0x79, 0x39, 0xd2, 0xbd, 0x55, + 0x0a, 0x07, 0x72, 0x0c, 0xc6, 0x2e, 0x02, 0xd2, 0x36, 0x0c, 0x9f, 0xba, + 0xa4, 0x28, 0xc9, 0x23, 0xae, 0xd3, 0xcf, 0x66, 0x53, 0x98, 0x24, 0xea, + 0x9e, 0xe3, 0x1b, 0x8d, 0x61, 0x53, 0x88, 0xa8, 0x2d, 0xf6, 0xa2, 0xff, + 0xec, 0xaf, 0xf8, 0x5a, 0x29, 0x03, 0x28, 0xbb, 0xf6, 0x8d, 0xff, 0xba, + 0x47, 0xd3, 0x0d, 0xef, 0x90, 0xac, 0x3d, 0x6b, 0xcf, 0x13, 0x96, 0xea, + 0x78, 0x83, 0x95, 0x26, 0xd6, 0x7e, 0xde, 0x88, 0x8c, 0x68, 0x6c, 0x96, + 0xce, 0xf8, 0xe1, 0x32, 0x58, 0x79, 0x13, 0x99, 0x6d, 0x5f, 0xe1, 0xeb, + 0x6d, 0x20, 0x9d, 0x8a, 0x82, 0x77, 0xfd, 0x83, 0x57, 0xaa, 0xae, 0xe2, + 0x03, 0x33, 0xa6, 0xa6, 0xba, 0xd4, 0x94, 0x17, 0x96, 0x47, 0x5e, 0xd0, + 0x52, 0xd7, 0xe8, 0x86, 0xc4, 0x85, 0xad, 0xa6, 0xf5, 0x9d, 0x9a, 0x4f, + 0x9d, 0x83, 0xce, 0x1e, 0x9e, 0x24, 0x44, 0x75, 0x99, 0x88, 0x94, 0x55, + 0xb3, 0x97, 0x00, 0xe8, 0x56, 0x71, 0x91, 0xb3, 0x23, 0x67, 0x56, 0xab, + 0xd7, 0xda, 0x13, 0x19, 0xdf, 0xf5, 0x47, 0x97, 0x1e, 0x45, 0xdf, 0x85, + 0x7f, 0x5e, 0x34, 0xd8, 0xd7, 0x22, 0x6a, 0x20, 0x5b, 0xac, 0x8e, 0xb1, + 0x88, 0xa1, 0x73, 0xd5, 0xf2, 0x46, 0xd7, 0xfe, 0xce, 0xae, 0xff, 0x0f, + 0xa6, 0xfc, 0x6f, 0xb3, 0xdf, 0x02, 0x78, 0x3e, 0xc7, 0xea, 0x1d, 0x1f, + 0xeb, 0x5e, 0xb6, 0x60, 0x11, 0xba, 0xab, 0x06, 0x58, 0x9f, 0x52, 0x0c, + 0x73, 0x8d, 0x6a, 0x03, 0xb9, 0x73, 0x57, 0xff, 0x81, 0xcf, 0x16, 0x15, + 0x90, 0x9e, 0x9f, 0x14, 0xef, 0x9a, 0xb3, 0xff, 0xb3, 0xb2, 0x32, 0xd5, + 0x46, 0x7f, 0x0c, 0x70, 0x8b, 0xcf, 0xf6, 0x0d, 0x9f, 0x8c, 0x49, 0x21, + 0x93, 0xf9, 0x32, 0x0c, 0x41, 0x00, 0x6b, 0x43, 0x18, 0xd4, 0x61, 0x78, + 0xf3, 0x04, 0x9b, 0x87, 0x7e, 0xb2, 0x87, 0xa7, 0x95, 0x72, 0x22, 0x64, + 0x3a, 0xf2, 0xa5, 0x8a, 0x34, 0xae, 0x29, 0x4d, 0x0c, 0xe0, 0x92, 0xb5, + 0xca, 0xb9, 0xaf, 0xe6, 0x4c, 0x19, 0x77, 0x4b, 0x25, 0xc9, 0xf0, 0xc0, + 0x7a, 0x15, 0xdb, 0xd0, 0x4e, 0x66, 0xc3, 0x48, 0xa0, 0xb0, 0x6c, 0x36, + 0x9f, 0x68, 0xf9, 0x7d, 0xa1, 0x47, 0x82, 0x93, 0x43, 0xfa, 0x75, 0x11, + 0x54, 0xe5, 0xea, 0x88, 0x67, 0x62, 0x3c, 0xe6, 0xcf, 0x8e, 0xd5, 0x10, + 0xe5, 0x8f, 0x8c, 0xd3, 0x76, 0xd9, 0x03, 0xce, 0xc0, 0x5d, 0xb9, 0x74, + 0xb0, 0xdf, 0xe9, 0x67, 0x43, 0xcd, 0xc7, 0x3f, 0xe8, 0x7a, 0x5c, 0xb5, + 0xf3, 0x0a, 0x74, 0x3d, 0x2d, 0x2c, 0x21, 0x91, 0x56, 0xee, 0x9c, 0xb0, + 0xdd, 0xdd, 0xc9, 0xcc, 0xe9, 0x46, 0x7a, 0x0a, 0xb6, 0xee, 0x16, 0x82, + 0x9d, 0xb2, 0xb7, 0xa3, 0x6f, 0x54, 0xa4, 0x88, 0xa2, 0x7d, 0xd8, 0xfb, + 0xc4, 0x34, 0x8f, 0x2e, 0x76, 0x11, 0x24, 0x6b, 0xdf, 0x28, 0x29, 0x77, + 0xfa, 0xa6, 0x7e, 0xab, 0xae, 0x16, 0x11, 0xe6, 0x6b, 0x7b, 0xb4, 0xd6, + 0x87, 0x37, 0x87, 0xf7, 0xed, 0xb9, 0xf9, 0x65, 0x28, 0x6a, 0x22, 0xe2, + 0x75, 0x75, 0xee, 0x7f, 0xc9, 0x0b, 0x27, 0xf7, 0xb0, 0x3c, 0x3c, 0xe8, + 0x83, 0xa2, 0xe1, 0xb6, 0xe6, 0x2d, 0x56, 0xd5, 0xdb, 0x5e, 0x22, 0xd1, + 0x14, 0xb5, 0xdf, 0x22, 0x75, 0x06, 0x7a, 0x96, 0xa8, 0x27, 0xbc, 0xb0, + 0x83, 0x95, 0xe4, 0x7a, 0x76, 0xc0, 0xa0, 0xdf, 0x3c, 0x47, 0x4e, 0xd5, + 0xe3, 0xf1, 0x8b, 0x1d, 0xa8, 0x86, 0xdf, 0x96, 0x55, 0x4f, 0xdc, 0x88, + 0xe0, 0xf8, 0xeb, 0x8f, 0xab, 0xf3, 0x25, 0xb4, 0xb1, 0xd2, 0x21, 0xa4, + 0x14, 0xe7, 0x01, 0xb1, 0x49, 0xee, 0x7f, 0x74, 0x06, 0x12, 0x61, 0x94, + 0x37, 0x01, 0x23, 0xc3, 0x97, 0xc9, 0x44, 0x76, 0x02, 0x22, 0xbf, 0xc0, + 0xbb, 0x7b, 0x93, 0xf0, 0x31, 0x08, 0xa4, 0x98, 0xba, 0xf6, 0x47, 0xb3, + 0xd1, 0xb0, 0x8c, 0x78, 0xaa, 0xd8, 0x01, 0xb2, 0x66, 0x6c, 0x56, 0x97, + 0xc9, 0xa8, 0xd1, 0x47, 0x44, 0xa1, 0x24, 0xa0, 0x55, 0x08, 0x95, 0x34, + 0x1b, 0x2f, 0x00, 0x62, 0x0c, 0x7f, 0x4c, 0x66, 0x09, 0x8a, 0x43, 0xf0, + 0xf6, 0x5c, 0x14, 0xc3, 0xec, 0xe2, 0x01, 0x68, 0xb0, 0x38, 0x08, 0x7a, + 0x19, 0x22, 0x76, 0x54, 0xe1, 0x55, 0x71, 0x30, 0x92, 0xc2, 0x7d, 0x09, + 0xb7, 0xf3, 0xa4, 0x41, 0x1c, 0x8b, 0xec, 0x6b, 0x2f, 0x98, 0x1b, 0x4d, + 0x5d, 0xa8, 0x59, 0x5e, 0x22, 0xe5, 0x25, 0x55, 0x52, 0x6d, 0x72, 0x7f, + 0xbf, 0x1b, 0xc5, 0xf2, 0xc7, 0xd1, 0x90, 0x0d, 0x02, 0x8a, 0xfe, 0x83, + 0x7a, 0xa8, 0x1c, 0x87, 0x15, 0x68, 0x95, 0x5c, 0xb4, 0xa4, 0xac, 0x7c, + 0x54, 0x78, 0x82, 0xe1, 0xa9, 0xfc, 0xcf, 0x7d, 0x4c, 0xc4, 0x5e, 0x24, + 0x95, 0xce, 0x0a, 0x6b, 0xaf, 0x6e, 0xcc, 0x68, 0x0d, 0x52, 0x08, 0xfd, + 0xf3, 0x79, 0x3c, 0x9b, 0x9c, 0x59, 0x2b, 0xe5, 0x95, 0x5b, 0xe1, 0xb2, + 0x8a, 0xad, 0xd1, 0x68, 0xcf, 0x29, 0x29, 0x00, 0x86, 0x97, 0x81, 0x3f, + 0x7d, 0x91, 0xa1, 0x87, 0xea, 0x31, 0x40, 0x03, 0xff, 0x6d, 0xf6, 0xe5, + 0x7c, 0x0a, 0x39, 0x0a, 0x07, 0xbd, 0x40, 0x8b, 0x50, 0x66, 0x11, 0x71, + 0x3a, 0x2f, 0xc4, 0x23, 0x7c, 0x2c, 0xd5, 0x36, 0xc5, 0x93, 0xa8, 0x1d, + 0xdd, 0xff, 0x74, 0x4c, 0x48, 0x84, 0xba, 0xa4, 0xab, 0x3d, 0x50, 0xe3, + 0xa2, 0xa4, 0x35, 0xca, 0x8b, 0x7d, 0x8b, 0x23, 0x34, 0xbc, 0xc0, 0xff, + 0xac, 0x8e, 0x8b, 0xcf, 0xe6, 0x48, 0x39, 0x79, 0x22, 0x6a, 0x5e, 0xee, + 0xa6, 0x21, 0xa5, 0x9f, 0xbf, 0x58, 0xb0, 0x20, 0xfe, 0x31, 0x04, 0x42, + 0x43, 0x76, 0x70, 0x74, 0xdc, 0xe7, 0xa8, 0x55, 0x15, 0xdc, 0xf9, 0x71, + 0x22, 0x30, 0x0c, 0xe2, 0x2e, 0xe3, 0xc9, 0x02, 0x21, 0x48, 0xce, 0x98, + 0xe7, 0x25, 0xb4, 0x59, 0x4a, 0xcb, 0x6a, 0xf6, 0x9e, 0x62, 0x7b, 0x02, + 0x65, 0x56, 0xb0, 0x71, 0x5b, 0xe0, 0x25, 0x33, 0x6a, 0x17, 0x50, 0xf8, + 0x18, 0xdd, 0xde, 0x51, 0x5c, 0x98, 0xee, 0xe2, 0x75, 0x2a, 0x4a, 0x60, + 0x44, 0x6c, 0x38, 0xbb, 0x42, 0xdf, 0xb0, 0xf0, 0x6a, 0xc5, 0xf5, 0xa2, + 0x4b, 0xe6, 0xb0, 0xff, 0x6e, 0xcd, 0xf0, 0xaf, 0xfe, 0xc1, 0x6e, 0x9f, + 0x4c, 0x7e, 0xfe, 0x42, 0x79, 0xfc, 0x48, 0x0c, 0xc6, 0x51, 0x01, 0x2b, + 0xfc, 0x21, 0x13, 0xd7, 0x55, 0xa1, 0xb1, 0x5f, 0x64, 0x85, 0xcd, 0x5a, + 0xcc, 0x7c, 0xb3, 0x9e, 0x91, 0x5d, 0x11, 0x9e, 0x70, 0x5f, 0xcb, 0xb0, + 0x33, 0x0c, 0xe2, 0x6b, 0x7f, 0xbe, 0x31, 0x64, 0x4e, 0xb0, 0x38, 0x5f, + 0x39, 0x58, 0x4c, 0xf8, 0x27, 0xfc, 0x56, 0x4e, 0xca, 0xc4, 0x5f, 0xd1, + 0xa4, 0x08, 0x0a, 0xcc, 0xd1, 0x6b, 0xb3, 0x63, 0xd6, 0x65, 0xab, 0x21, + 0xbe, 0xef, 0x2a, 0x6c, 0x8e, 0x9e, 0x1e, 0xbc, 0xb3, 0x6a, 0xee, 0x3d, + 0x59, 0x3c, 0x9c, 0xd7, 0xf3, 0xf4, 0xa7, 0x5e, 0xde, 0xaf, 0xa7, 0xe3, + 0x33, 0x04, 0x56, 0xe3, 0x76, 0x3f, 0xcd, 0x21, 0x30, 0xb3, 0x67, 0xa4, + 0x26, 0xb2, 0x32, 0x3c, 0x7c, 0xfa, 0xfc, 0xd7, 0x80, 0x85, 0x6f, 0xbb, + 0x4b, 0x5b, 0x65, 0xb7, 0x8b, 0x3a, 0x54, 0x50, 0x8a, 0xc5, 0xc7, 0xcc, + 0x71, 0x4d, 0x23, 0x24, 0x80, 0x16, 0x1b, 0xf2, 0x74, 0x2d, 0xaf, 0x4d, + 0x9c, 0x7e, 0x82, 0x6d, 0x62, 0x89, 0xb0, 0xfa, 0xb6, 0x71, 0xdd, 0x46, + 0xb2, 0x9e, 0xa7, 0xf2, 0x35, 0x16, 0x28, 0xf8, 0x43, 0xa1, 0x7b, 0xea, + 0x0e, 0x7f, 0xcc, 0x33, 0x19, 0x0e, 0xcf, 0x1a, 0x91, 0x91, 0x72, 0xea, + 0x8b, 0x86, 0x7e, 0x96, 0x11, 0x67, 0xe4, 0x44, 0xfb, 0xf4, 0x28, 0x33, + 0x15, 0x4f, 0xbd, 0xc4, 0xed, 0x84, 0xa5, 0xfe, 0x09, 0x33, 0xf3, 0x74, + 0xdc, 0x41, 0x62, 0xb9, 0x92, 0xa8, 0x27, 0x5d, 0x39, 0x40, 0x88, 0xde, + 0x82, 0xe3, 0x64, 0x96, 0xa7, 0x1a, 0xe7, 0xab, 0x3a, 0xc6, 0xac, 0x73, + 0x4c, 0x66, 0x0b, 0xb3, 0x71, 0x30, 0x7c, 0xda, 0x46, 0x69, 0x5e, 0xdb, + 0xad, 0xc2, 0x11, 0xda, 0xb4, 0xf0, 0xcc, 0xbd, 0x5f, 0x6e, 0x48, 0x32, + 0x23, 0x89, 0x98, 0x17, 0x9c, 0x1d, 0x29, 0xa3, 0x04, 0x33, 0x98, 0xfb, + 0x48, 0x35, 0x6c, 0xac, 0xb1, 0x93, 0xd4, 0x96, 0x05, 0x1d, 0xa2, 0xcc, + 0x2e, 0x3c, 0x78, 0x52, 0x7f, 0x38, 0x46, 0x82, 0x38, 0x62, 0xfd, 0xcf, + 0x20, 0xf3, 0x62, 0x82, 0x8a, 0x72, 0xe9, 0xa7, 0x9e, 0x2c, 0x65, 0xec, + 0x66, 0xeb, 0x21, 0x12, 0xdd, 0xc2, 0x8f, 0xc0, 0x12, 0x8d, 0x93, 0xa6, + 0x95, 0x24, 0x4e, 0x7a, 0x12, 0x17, 0x9f, 0xa0, 0x3d, 0xdd, 0x6f, 0xfc, + 0x42, 0x66, 0x5f, 0xdd, 0xbb, 0x82, 0x62, 0xfa, 0x2d, 0xd2, 0x2a, 0xca, + 0xf4, 0x33, 0xc3, 0x3d, 0x42, 0xa9, 0x43, 0x93, 0x2f, 0x00, 0xd7, 0x46, + 0x0e, 0xcc, 0x1d, 0x42, 0xad, 0xac, 0x4f, 0xf1, 0x23, 0x76, 0x91, 0x13, + 0xb9, 0x1f, 0x40, 0xde, 0x76, 0xf2, 0x3a, 0xcf, 0xc9, 0x0b, 0xc2, 0xf1, + 0x60, 0x5f, 0x3c, 0x86, 0x69, 0x0d, 0x5e, 0xc6, 0xfa, 0x5a, 0x48, 0x7e, + 0x9f, 0xfa, 0x55, 0xe7, 0x1d, 0x1f, 0x8e, 0x2e, 0x0c, 0x10, 0x08, 0xca, + 0x2f, 0xbe, 0x2d, 0x2a, 0x2a, 0x4a, 0xe1, 0x78, 0xf8, 0x5a, 0x11, 0x15, + 0x81, 0xab, 0xf7, 0xa1, 0x13, 0xd0, 0x8a, 0x60, 0x74, 0xb0, 0xed, 0x4a, + 0x97, 0x21, 0x24, 0xc5, 0xc4, 0xe1, 0x17, 0x95, 0x61, 0x94, 0x80, 0x6d, + 0xbf, 0x6b, 0x55, 0xdd, 0x41, 0x07, 0x61, 0x21, 0xa8, 0x50, 0x6f, 0x2e, + 0x67, 0x68, 0x30, 0x91, 0x07, 0xe4, 0x4f, 0xd8, 0x4a, 0xd4, 0x72, 0x29, + 0x33, 0xc7, 0x12, 0x43, 0xb9, 0xe7, 0xd2, 0x7b, 0x96, 0xa4, 0x74, 0x6f, + 0xbf, 0xe2, 0xd0, 0x90, 0xa7, 0x93, 0x56, 0x6b, 0x8a, 0xee, 0x32, 0x9e, + 0xc3, 0x44, 0xa4, 0x7f, 0xe1, 0xcd, 0x03, 0xd4, 0xac, 0x88, 0x68, 0x8d, + 0xe8, 0xe8, 0xda, 0xc0, 0x4a, 0x4f, 0xae, 0x0f, 0x53, 0x88, 0x87, 0xc8, + 0xd4, 0x4f, 0x20, 0xfb, 0x0a, 0x42, 0x3c, 0x88, 0x6a, 0x44, 0x7d, 0x13, + 0xf2, 0x97, 0xd0, 0x8a, 0x1b, 0x57, 0xaa, 0x71, 0xb2, 0x01, 0x1b, 0x22, + 0x97, 0xa9, 0xb9, 0xc6, 0x8c, 0xd8, 0x93, 0x17, 0x3e, 0x06, 0xe8, 0x9f, + 0x9d, 0x38, 0x11, 0xe2, 0x9f, 0x83, 0x65, 0x30, 0x63, 0x46, 0xf2, 0xe3, + 0xdf, 0x2d, 0x88, 0x74, 0x51, 0x61, 0xfc, 0x50, 0xca, 0x54, 0xe1, 0x6f, + 0x30, 0xc7, 0x16, 0xa6, 0x2a, 0x03, 0xc7, 0x26, 0x12, 0xcc, 0x8b, 0x12, + 0xe2, 0xa9, 0x33, 0x5a, 0xad, 0xe2, 0x4e, 0x9b, 0x35, 0x27, 0x32, 0x92, + 0xb7, 0x97, 0xd0, 0xad, 0x15, 0x5f, 0x2c, 0x92, 0x6a, 0xc3, 0xae, 0xe9, + 0x08, 0xf7, 0x37, 0x2f, 0xf0, 0x32, 0xf6, 0x3e, 0xeb, 0x45, 0x4a, 0x25, + 0xd6, 0x48, 0xf5, 0xca, 0x2c, 0x73, 0x2e, 0x8f, 0x17, 0x60, 0x78, 0x74, + 0x01, 0x02, 0x22, 0xd8, 0x6f, 0x27, 0x66, 0xde, 0xf5, 0x3c, 0xf8, 0x4b, + 0x3b, 0x82, 0xb4, 0xfb, 0xdb, 0x74, 0xf8, 0xf6, 0x93, 0x40, 0xd2, 0x3b, + 0xac, 0x41, 0xf0, 0x24, 0x7b, 0xe1, 0x8a, 0xcd, 0x1a, 0xda, 0x4f, 0x19, + 0x21, 0x95, 0x13, 0x34, 0xc1, 0x5c, 0x9c, 0x74, 0xba, 0xd3, 0xf1, 0xed, + 0xd4, 0x58, 0x27, 0xa0, 0x45, 0xa1, 0xfc, 0x39, 0x61, 0xcb, 0x2a, 0x34, + 0x8e, 0x94, 0x32, 0xc6, 0x2c, 0xf5, 0x35, 0x6c, 0x58, 0xd4, 0xbd, 0x4e, + 0x82, 0x76, 0xb9, 0xe8, 0x16, 0xdd, 0x60, 0x88, 0x8f, 0x0c, 0xe8, 0x65, + 0x87, 0xd5, 0xd0, 0x48, 0x85, 0xdf, 0x1c, 0xc3, 0xcd, 0x87, 0xf7, 0xed, + 0x73, 0x4a, 0x9b, 0xe4, 0x2b, 0x15, 0x30, 0xf1, 0x12, 0x09, 0xa0, 0x4c, + 0x72, 0xff, 0x84, 0x77, 0x83, 0xcc, 0x91, 0xb0, 0x7c, 0x31, 0x9d, 0x83, + 0x63, 0x33, 0x11, 0x15, 0x7e, 0x2a, 0x7c, 0x01, 0x4f, 0x92, 0xab, 0xcf, + 0x13, 0xc6, 0xa1, 0xd1, 0xd8, 0x73, 0x32, 0x4a, 0xb2, 0xe0, 0x2c, 0xf0, + 0x00, 0x0f, 0xff, 0x9c, 0x05, 0x7e, 0x8d, 0x14, 0xbc, 0xf1, 0x6f, 0xfa, + 0xb1, 0x52, 0x2b, 0x70, 0x1f, 0x01, 0xca, 0x57, 0xd5, 0x9e, 0x51, 0x68, + 0xbf, 0x83, 0x92, 0xf6, 0xc4, 0x0b, 0xbf, 0xe1, 0xf4, 0x29, 0x71, 0x4c, + 0xbc, 0x88, 0x9f, 0xe5, 0x43, 0x29, 0x5e, 0x5c, 0xb8, 0xf5, 0x22, 0x6c, + 0x03, 0x69, 0x21, 0x7a, 0x91, 0x16, 0x92, 0x08, 0x41, 0x01, 0xb6, 0xa9, + 0xf2, 0x1b, 0xbf, 0x64, 0xa4, 0x26, 0xf1, 0x62, 0x12, 0xec, 0xa2, 0x34, + 0x85, 0xd9, 0x97, 0xfc, 0xd4, 0xe8, 0xed, 0x0d, 0x88, 0x1d, 0x4d, 0xb4, + 0x38, 0xcc, 0x4d, 0xbc, 0xca, 0xd7, 0x7f, 0xd9, 0x74, 0xbf, 0x43, 0x1d, + 0xb3, 0x5a, 0x22, 0x17, 0x7e, 0x1d, 0xab, 0x68, 0x94, 0x22, 0xff, 0x03, + 0x18, 0x02, 0xaa, 0x71, 0xd2, 0x9c, 0xcd, 0x26, 0xcc, 0x84, 0x03, 0xc8, + 0x2c, 0x5c, 0x9c, 0xd3, 0x14, 0x3f, 0xbf, 0x40, 0x00, 0xc9, 0x40, 0xac, + 0xc5, 0xde, 0x87, 0x75, 0x91, 0xb8, 0xa0, 0x50, 0x6f, 0x7e, 0x6c, 0x67, + 0xdd, 0x18, 0x34, 0xa8, 0xeb, 0x9c, 0x55, 0x4a, 0xc9, 0xa2, 0x46, 0xa1, + 0x79, 0xfd, 0x04, 0xb5, 0xee, 0x51, 0xf7, 0x62, 0x78, 0x55, 0x0c, 0xd3, + 0xc3, 0x14, 0x19, 0x3c, 0x16, 0x65, 0xde, 0x2f, 0x6d, 0xa4, 0x75, 0x23, + 0x23, 0x4d, 0x79, 0xa4, 0x15, 0x17, 0x1a, 0x92, 0xf9, 0xb8, 0xf5, 0x70, + 0x06, 0x64, 0x12, 0xdc, 0x0b, 0xa4, 0x7b, 0x78, 0xa4, 0x63, 0xdf, 0x95, + 0xbf, 0x5c, 0xf8, 0x50, 0x82, 0x06, 0xeb, 0xcc, 0x48, 0x4e, 0xc5, 0x5c, + 0x11, 0xcb, 0x28, 0x6a, 0xd3, 0xe8, 0x0a, 0x61, 0x35, 0x3c, 0xc2, 0x38, + 0x48, 0xb9, 0x73, 0x45, 0xc1, 0x21, 0x4e, 0x42, 0x2b, 0x2b, 0x38, 0xad, + 0x8d, 0x2d, 0x09, 0x55, 0x42, 0x7d, 0x83, 0xa1, 0x14, 0x12, 0x05, 0x98, + 0x4e, 0x46, 0x54, 0x6b, 0x8d, 0xfb, 0xc8, 0x55, 0x29, 0x2b, 0x33, 0xa7, + 0x44, 0x6c, 0x46, 0xa1, 0x66, 0x8f, 0xb3, 0x28, 0x5f, 0x27, 0x7f, 0x33, + 0xb3, 0xbc, 0xcd, 0x65, 0x6f, 0x4d, 0xee, 0x78, 0x2d, 0x9c, 0x5f, 0xfc, + 0x17, 0xb8, 0x0c, 0xb6, 0x6e, 0x6b, 0x01, 0x64, 0x7c, 0x50, 0x3a, 0x5b, + 0xf3, 0xd4, 0x5e, 0xa2, 0xa0, 0xa0, 0x1e, 0xce, 0x50, 0x66, 0xda, 0xb3, + 0xaa, 0xae, 0x89, 0xee, 0x70, 0x41, 0x68, 0x07, 0x0a, 0xf9, 0xfe, 0xda, + 0xb7, 0x65, 0xd7, 0x14, 0x00, 0xc7, 0x9f, 0xd5, 0xae, 0x54, 0x3d, 0x80, + 0xab, 0x7c, 0xb8, 0x55, 0x64, 0xc0, 0x5a, 0x77, 0xae, 0x74, 0xff, 0x37, + 0xbf, 0x1c, 0xd0, 0x71, 0x6d, 0x15, 0x60, 0x9f, 0x88, 0xc1, 0xd3, 0xd9, + 0xa5, 0x3f, 0x39, 0x9d, 0xf8, 0x68, 0xd2, 0xdf, 0x45, 0xbd, 0x77, 0x0b, + 0x58, 0x08, 0xe4, 0x19, 0x3b, 0x77, 0x4a, 0xf6, 0xf6, 0x6d, 0x80, 0x41, + 0xf9, 0xb1, 0x09, 0x94, 0x60, 0x62, 0xd0, 0x45, 0xef, 0x7f, 0x72, 0xc8, + 0x12, 0x89, 0x56, 0xe7, 0xb0, 0x23, 0x8d, 0xb8, 0xde, 0x75, 0xfa, 0x62, + 0x5f, 0x53, 0xb2, 0x73, 0x48, 0x84, 0xee, 0x36, 0x3c, 0x4b, 0x1e, 0x8f, + 0x08, 0x3d, 0xb4, 0xa7, 0xa9, 0x02, 0xee, 0xb1, 0x59, 0xb5, 0xbd, 0xe5, + 0x81, 0x81, 0x8b, 0xb4, 0xf1, 0x44, 0x44, 0xa6, 0x04, 0x9e, 0x2f, 0x24, + 0xa6, 0x65, 0xf5, 0x1f, 0x3e, 0x1b, 0x7d, 0x56, 0x6d, 0x3c, 0x3e, 0x51, + 0xe2, 0x29, 0xdf, 0x40, 0x6e, 0x9f, 0x22, 0x5f, 0xe4, 0x93, 0x72, 0x2c, + 0x11, 0x1d, 0xf6, 0xef, 0xbe, 0x24, 0xd2, 0xde, 0xe0, 0xbc, 0xa4, 0x5b, + 0x9b, 0x38, 0xf6, 0x6a, 0x68, 0x6c, 0xea, 0x31, 0x2c, 0xb2, 0x3b, 0x9d, + 0xde, 0x11, 0x55, 0xb0, 0x5d, 0x89, 0x88, 0x2d, 0x3d, 0x1c, 0x5d, 0xda, + 0xf6, 0x09, 0x8b, 0x42, 0xd6, 0x1e, 0xc0, 0x4e, 0xe0, 0xf4, 0xea, 0xe7, + 0xf9, 0x2f, 0x98, 0xef, 0x6d, 0x6c, 0xb6, 0xfb, 0x4b, 0x4a, 0xb5, 0x69, + 0x73, 0x37, 0x4f, 0xa7, 0x41, 0x16, 0xcb, 0x53, 0xa5, 0x63, 0x2a, 0x2f, + 0x0f, 0x55, 0xe6, 0x80, 0x12, 0x35, 0xcc, 0xcc, 0x06, 0x84, 0xec, 0x65, + 0x5c, 0x5f, 0x6f, 0xe8, 0x70, 0xb2, 0xff, 0x6e, 0x97, 0xa9, 0xe7, 0x08, + 0xf5, 0xbb, 0x7e, 0xd2, 0x78, 0xb6, 0x05, 0x1b, 0xaf, 0x08, 0xce, 0xb0, + 0x92, 0xdb, 0x25, 0x40, 0x11, 0x26, 0x21, 0xac, 0x0b, 0xeb, 0x15, 0x65, + 0x85, 0x86, 0xde, 0xe4, 0xb7, 0xef, 0xea, 0xcf, 0xa2, 0xd7, 0x59, 0xec, + 0x6e, 0x01, 0xd2, 0x6d, 0x4d, 0x2e, 0xfa, 0x5f, 0x21, 0xe7, 0x16, 0x1a, + 0xea, 0x18, 0xe4, 0x30, 0x9e, 0x99, 0x28, 0xc2, 0x92, 0x7b, 0xd6, 0x97, + 0x2d, 0xc0, 0x7f, 0xf6, 0x61, 0x68, 0xe0, 0xcc, 0x56, 0x82, 0x5a, 0x22, + 0x1f, 0xc3, 0x22, 0xbf, 0x6f, 0x13, 0x27, 0x19, 0x06, 0x3a, 0x57, 0xd3, + 0xb7, 0x04, 0x45, 0xe6, 0x62, 0xac, 0x07, 0x05, 0x32, 0x81, 0x1f, 0x78, + 0x3b, 0x8d, 0x7d, 0xd7, 0xff, 0x1b, 0xe6, 0x42, 0x5a, 0x55, 0x6f, 0xc0, + 0x24, 0x5e, 0x1e, 0x82, 0x73, 0x92, 0x7d, 0xf5, 0xca, 0xd9, 0x02, 0xb6, + 0x9f, 0x61, 0xd6, 0xcb, 0x5e, 0x80, 0x0c, 0x9b, 0xac, 0x37, 0xa5, 0xc6, + 0xbf, 0x8f, 0xa3, 0x1a, 0x9f, 0x96, 0xc2, 0xda, 0xe8, 0xbc, 0x3d, 0x44, + 0xab, 0x8e, 0x45, 0x30, 0xb4, 0xef, 0x96, 0xf4, 0x5a, 0xff, 0x7a, 0x81, + 0x79, 0xee, 0x22, 0x02, 0xd4, 0x80, 0xdc, 0x10, 0xf5, 0x38, 0x04, 0x28, + 0x5d, 0xc2, 0x1c, 0x9c, 0x1a, 0x22, 0x29, 0x1b, 0x7f, 0x25, 0x67, 0xe7, + 0x37, 0xb6, 0x3b, 0xbb, 0x09, 0x9b, 0xbd, 0x00, 0xca, 0xec, 0x99, 0x4f, + 0x81, 0x9d, 0xc7, 0x92, 0xad, 0x17, 0x21, 0x51, 0x65, 0x3b, 0x89, 0x7e, + 0x5e, 0xeb, 0xe9, 0x32, 0xc0, 0xb8, 0x22, 0xb4, 0xaf, 0x5e, 0x08, 0x67, + 0xf4, 0x48, 0x69, 0x25, 0xf0, 0x13, 0xcd, 0xbe, 0x7b, 0x80, 0x53, 0x5e, + 0xde, 0xc8, 0xc1, 0xd2, 0x57, 0x24, 0xad, 0xf3, 0x55, 0x6e, 0x2e, 0x2f, + 0x4c, 0x4a, 0xf4, 0xa6, 0xe7, 0x61, 0xe9, 0xb5, 0xed, 0x3f, 0xb6, 0x94, + 0xd9, 0x1e, 0xc6, 0x46, 0x2b, 0x8d, 0xaf, 0xde, 0xf0, 0xd5, 0x4b, 0x2c, + 0x54, 0x62, 0x5e, 0xa8, 0x31, 0xa5, 0x63, 0x24, 0x18, 0x96, 0x93, 0x7d, + 0x8f, 0x41, 0xfd, 0x2c, 0x11, 0xec, 0xe2, 0x63, 0x1e, 0x3f, 0xf2, 0x92, + 0xd0, 0xa7, 0xa2, 0x81, 0xfb, 0x9c, 0x77, 0xc0, 0x26, 0x2f, 0x44, 0x80, + 0x87, 0xc5, 0x42, 0x62, 0x21, 0xdb, 0xaa, 0x4d, 0xc9, 0x33, 0xc1, 0x5b, + 0x97, 0x37, 0xcc, 0x9a, 0xc4, 0xc7, 0xfd, 0x1e, 0xf8, 0x5b, 0x1a, 0xa7, + 0x97, 0x5c, 0xbb, 0xe3, 0xb8, 0xc0, 0xec, 0x8e, 0xd6, 0x0d, 0x31, 0xe7, + 0x1c, 0x0c, 0x9b, 0x37, 0xc7, 0x81, 0x61, 0xc8, 0xb1, 0xbb, 0x26, 0x4b, + 0x23, 0x4f, 0xdd, 0xe8, 0x40, 0x1c, 0x08, 0x18, 0x16, 0xa4, 0xe7, 0x64, + 0x46, 0x69, 0x81, 0xf9, 0xf4, 0xf3, 0xa6, 0x5e, 0x33, 0x3d, 0x6e, 0xdf, + 0x1b, 0x6b, 0x9f, 0xe0, 0xfc, 0x68, 0xfd, 0xf1, 0xc9, 0x32, 0xc3, 0x4a, + 0x75, 0xe8, 0xf9, 0x52, 0x31, 0x85, 0x71, 0xa8, 0x59, 0xe5, 0x52, 0x3e, + 0x5c, 0x76, 0x18, 0x9e, 0xf1, 0x4e, 0x50, 0x1b, 0xb8, 0x6a, 0xeb, 0x79, + 0xef, 0x94, 0xd0, 0xfa, 0xb9, 0x2c, 0x8a, 0x8c, 0x47, 0xf0, 0x9d, 0xc0, + 0x3d, 0xc8, 0xe3, 0xc1, 0xcc, 0x61, 0xc8, 0x7f, 0x68, 0xd4, 0x18, 0x04, + 0x74, 0x66, 0x5a, 0xbb, 0xef, 0xd7, 0x3e, 0x41, 0xfa, 0x08, 0x79, 0xf0, + 0xc3, 0x4a, 0x32, 0x90, 0x40, 0x52, 0xee, 0x2d, 0xb4, 0x22, 0x70, 0xee, + 0xa5, 0xe1, 0xf5, 0xd2, 0xd4, 0x66, 0x81, 0x6d, 0x92, 0xff, 0x5f, 0x96, + 0x19, 0xc6, 0x8b, 0x11, 0x03, 0x02, 0x59, 0xfe, 0x55, 0xd6, 0x18, 0x59, + 0xec, 0x97, 0x92, 0x24, 0x77, 0x6b, 0xc9, 0x59, 0xd4, 0xfa, 0x1a, 0x39, + 0xae, 0x2a, 0x57, 0x3f, 0xda, 0xe4, 0x03, 0xce, 0x7e, 0x73, 0xfd, 0xd5, + 0x81, 0x38, 0xc1, 0x24, 0x1d, 0x6d, 0xa3, 0xc0, 0x9f, 0x28, 0xbf, 0xe6, + 0x29, 0x7c, 0x67, 0xcf, 0xe1, 0x2c, 0x87, 0xe0, 0xf0, 0x7b, 0x5f, 0x41, + 0xc1, 0x26, 0x94, 0xd4, 0x2a, 0x24, 0x27, 0x94, 0xb9, 0xa4, 0xd7, 0xbb, + 0x8f, 0xfa, 0x23, 0x00, 0x8f, 0x4f, 0x7e, 0x3b, 0x27, 0xd2, 0xa0, 0x71, + 0x4b, 0x6b, 0x2b, 0xfa, 0x6a, 0x4b, 0x19, 0x52, 0x03, 0x8a, 0x58, 0x2e, + 0x07, 0x78, 0x9e, 0x1c, 0xe3, 0x0f, 0xc5, 0x54, 0x50, 0x9d, 0x85, 0x69, + 0x07, 0x92, 0xda, 0x10, 0x8d, 0xec, 0x05, 0x94, 0x28, 0xca, 0x8b, 0x9b, + 0x94, 0x52, 0x7b, 0xef, 0x02, 0xf0, 0xbe, 0xf8, 0x49, 0x47, 0xd7, 0xa7, + 0xf5, 0xaf, 0xed, 0xf1, 0x96, 0x67, 0xa4, 0xa2, 0xb8, 0x0f, 0x7e, 0x0c, + 0x27, 0x3a, 0x71, 0x00, 0x32, 0xaa, 0x28, 0xa2, 0x73, 0xef, 0xc8, 0x05, + 0x87, 0x1c, 0x6f, 0x6d, 0x8b, 0xae, 0x5e, 0x4b, 0x8b, 0xe4, 0x5f, 0x01, + 0xeb, 0x58, 0x6e, 0xe9, 0x2c, 0xd0, 0xd4, 0xed, 0xb8, 0x3f, 0x19, 0x58, + 0x18, 0x78, 0x95, 0xee, 0x31, 0x6d, 0xb6, 0xca, 0x6b, 0x7c, 0xa2, 0x2a, + 0x6d, 0x5e, 0x94, 0x18, 0x57, 0x35, 0x39, 0x82, 0x56, 0x77, 0xeb, 0x7d, + 0xbd, 0x88, 0x2e, 0x0e, 0x1c, 0x13, 0x07, 0x61, 0xae, 0x93, 0x39, 0x57, + 0x21, 0xff, 0xba, 0x96, 0xa1, 0x29, 0x9b, 0x73, 0x14, 0xac, 0x26, 0xf7, + 0xa0, 0x17, 0x67, 0xb2, 0x5f, 0x3a, 0x1b, 0x54, 0x53, 0x6a, 0x26, 0x4d, + 0xb1, 0xfd, 0xdf, 0x6c, 0x8d, 0xa2, 0xbd, 0x8f, 0x4e, 0x2d, 0x01, 0x9e, + 0xf1, 0xcb, 0xff, 0x37, 0x90, 0xbc, 0x0f, 0xee, 0xa0, 0x26, 0x45, 0x74, + 0xd6, 0x4e, 0xcd, 0xe1, 0x94, 0xc9, 0x9d, 0xcc, 0xb9, 0xd5, 0x56, 0x3a, + 0x58, 0x43, 0x32, 0xa3, 0x74, 0x63, 0x06, 0x12, 0xc0, 0x59, 0xc1, 0x39, + 0xdc, 0x47, 0x25, 0x64, 0x58, 0xed, 0xfc, 0x6f, 0xba, 0x57, 0xee, 0x02, + 0xda, 0x18, 0x80, 0x9b, 0x3a, 0x92, 0x38, 0xf2, 0x37, 0x6a, 0x69, 0x12, + 0xb7, 0xc4, 0x41, 0x94, 0xb8, 0x98, 0xdf, 0x6e, 0x7b, 0x41, 0x04, 0x86, + 0xd9, 0x96, 0xf4, 0x52, 0xa0, 0x1b, 0x95, 0x05, 0xbf, 0xf6, 0xc8, 0x78, + 0x7a, 0x19, 0x7a, 0x64, 0x3e, 0x95, 0x6e, 0x9e, 0xa0, 0xe2, 0xb9, 0x60, + 0x83, 0xbf, 0xea, 0x93, 0x87, 0x0b, 0x39, 0xc9, 0x08, 0xf9, 0x91, 0xe2, + 0xf3, 0x00, 0x39, 0x0b, 0xee, 0xdf, 0x56, 0x6d, 0x26, 0xaa, 0x9f, 0x24, + 0xac, 0xb2, 0xc3, 0x15, 0x4f, 0x03, 0x10, 0x8f, 0x41, 0x02, 0xf4, 0x75, + 0x0b, 0xde, 0xa6, 0x9b, 0x50, 0x5a, 0x56, 0xac, 0x49, 0x88, 0x8c, 0x98, + 0xe1, 0x0c, 0xc5, 0x70, 0x14, 0x61, 0x7a, 0xca, 0xa8, 0x0e, 0x14, 0xf5, + 0xf4, 0x0f, 0xae, 0x48, 0x18, 0xe4, 0x99, 0xa1, 0xf0, 0xa3, 0xf6, 0xa3, + 0x7b, 0xb8, 0xf5, 0xc7, 0x35, 0x87, 0x1f, 0xb1, 0xb8, 0x23, 0x07, 0xfa, + 0xd3, 0xc1, 0xfb, 0x79, 0xfa, 0x3d, 0x29, 0xa6, 0x9e, 0x6b, 0x53, 0xf8, + 0x58, 0xfd, 0xb0, 0x1c, 0x3a, 0x4c, 0x62, 0x5e, 0x49, 0xc5, 0xa7, 0x1f, + 0xc3, 0xdd, 0x86, 0x66, 0xd8, 0x58, 0x57, 0xdd, 0x6d, 0x82, 0x58, 0x0e, + 0x02, 0x2b, 0x16, 0xe2, 0x37, 0x2a, 0xfd, 0x98, 0xf9, 0xa0, 0x8b, 0xed, + 0xba, 0x05, 0xda, 0xc6, 0xb8, 0x65, 0x19, 0xca, 0x0c, 0xfc, 0xd7, 0xbf, + 0xd9, 0xe8, 0xc0, 0x49, 0x19, 0x3f, 0x36, 0x2d, 0x44, 0x9a, 0xd6, 0xaa, + 0x26, 0xdc, 0x59, 0x0f, 0x22, 0xa3, 0xf6, 0xf5, 0x63, 0x50, 0x76, 0xe3, + 0x28, 0xd7, 0x20, 0x16, 0x0b, 0xb9, 0x65, 0xf8, 0xca, 0x67, 0xc2, 0xa6, + 0x89, 0x9f, 0x63, 0x65, 0xdd, 0x64, 0x25, 0x50, 0xee, 0x09, 0x11, 0x49, + 0xb9, 0x63, 0x8b, 0x7c, 0x36, 0x59, 0xa1, 0xe8, 0x34, 0x91, 0x65, 0xb6, + 0x52, 0xf3, 0x69, 0x5c, 0xb7, 0xef, 0xc9, 0xd7, 0x58, 0xa1, 0x66, 0x69, + 0xed, 0x87, 0x24, 0x9d, 0xde, 0x2f, 0x8e, 0x44, 0x43, 0xaa, 0xed, 0x2f, + 0x76, 0x19, 0xe1, 0x96, 0x4b, 0x03, 0x11, 0x33, 0xa6, 0xe9, 0x45, 0x15, + 0xa9, 0xa8, 0x18, 0x02, 0xd7, 0xb8, 0x6e, 0x00, 0x2c, 0x23, 0x1c, 0x2d, + 0x37, 0x05, 0x02, 0x00, 0x93, 0x71, 0x67, 0xf2, 0x51, 0x65, 0xfb, 0x56, + 0x2d, 0x4a, 0x79, 0xdd, 0xb7, 0x08, 0x79, 0xc2, 0x1d, 0xac, 0x8b, 0x5f, + 0xb6, 0x1d, 0x12, 0x6a, 0x05, 0xb0, 0xb6, 0xe8, 0x8a, 0x88, 0x13, 0xc5, + 0xd9, 0x33, 0x0d, 0x69, 0x98, 0xec, 0x53, 0xbf, 0xaa, 0xcd, 0x06, 0x66, + 0x74, 0x94, 0x23, 0x8f, 0x82, 0xcf, 0xd2, 0x7f, 0xa5, 0x71, 0x96, 0x3b, + 0xdb, 0xf5, 0x7d, 0xbf, 0x68, 0xdc, 0xf6, 0x2a, 0xae, 0xb0, 0x9a, 0x0d, + 0x48, 0xc4, 0xa5, 0x33, 0xfa, 0xc1, 0xd0, 0x6f, 0x3c, 0x28, 0x2b, 0xc5, + 0xfd, 0x30, 0x08, 0xa5, 0xb5, 0x13, 0xcf, 0x63, 0x5a, 0x60, 0xac, 0x37, + 0x8f, 0x75, 0xf8, 0xb6, 0x30, 0xfc, 0x0b, 0x4a, 0x33, 0xfd, 0x69, 0x78, + 0xd0, 0x4c, 0x05, 0x2c, 0x37, 0xec, 0x5c, 0x27, 0xaf, 0x99, 0x23, 0x51, + 0xa9, 0x65, 0x77, 0x95, 0x5c, 0x76, 0xea, 0x2f, 0xb3, 0xb0, 0xfa, 0x75, + 0x40, 0x3b, 0x50, 0xa0, 0xe8, 0xff, 0x6c, 0xf1, 0x45, 0x99, 0xca, 0x79, + 0xf6, 0x4f, 0x18, 0xb6, 0xec, 0x78, 0xfe, 0x5d, 0x1d, 0x2b, 0x50, 0xce, + 0xd8, 0x95, 0x82, 0xeb, 0xf8, 0xc9, 0xb7, 0x57, 0x73, 0xec, 0x20, 0x2c, + 0x97, 0xa6, 0x53, 0x91, 0x0a, 0xce, 0xee, 0x5e, 0x2d, 0xfb, 0x48, 0x4f, + 0x3c, 0xb7, 0x3c, 0xdf, 0xb1, 0xe6, 0x32, 0x70, 0x3b, 0x36, 0x4e, 0x0d, + 0x5a, 0x60, 0x3a, 0x21, 0x8d, 0x13, 0x7b, 0x3f, 0xd1, 0x86, 0x5b, 0x12, + 0x37, 0xfd, 0x48, 0x83, 0x5a, 0x0e, 0xcd, 0x87, 0x68, 0x3e, 0x90, 0x6c, + 0x35, 0xc3, 0xa0, 0x92, 0xcd, 0x70, 0xc8, 0x45, 0xc7, 0x1d, 0x1d, 0xc7, + 0xdd, 0xc2, 0xf6, 0xc8, 0x8b, 0xf7, 0xcb, 0x9d, 0x6d, 0x0a, 0x9b, 0x4b, + 0xc6, 0x2e, 0xc3, 0x29, 0x69, 0x9e, 0x43, 0x5c, 0x36, 0x6b, 0xc2, 0x82, + 0xe5, 0x03, 0x8c, 0x71, 0xb3, 0x0a, 0x88, 0xe4, 0x0e, 0xf2, 0x0e, 0xeb, + 0xa8, 0x73, 0x50, 0xd9, 0x45, 0x3d, 0x6f, 0x67, 0x03, 0x0a, 0x3b, 0x49, + 0x78, 0xbc, 0xf3, 0x59, 0xb3, 0xe4, 0xd6, 0x3f, 0xcd, 0x80, 0xec, 0x70, + 0x95, 0x26, 0x41, 0x94, 0x20, 0x4d, 0x7f, 0x7e, 0xdd, 0xa0, 0x8f, 0xf9, + 0xfe, 0xac, 0xd4, 0xf0, 0xbd, 0x71, 0x6a, 0x0f, 0x39, 0xd4, 0xec, 0xa1, + 0x8a, 0x79, 0x6d, 0x1a, 0x46, 0x8c, 0xf1, 0xbd, 0x76, 0xc1, 0x08, 0x79, + 0x73, 0xf5, 0x43, 0x0d, 0x47, 0x15, 0x6b, 0x66, 0x3e, 0x12, 0xff, 0x74, + 0x6c, 0x21, 0x15, 0xd6, 0x73, 0x3d, 0x90, 0x37, 0xae, 0x97, 0x2f, 0x0b, + 0xd6, 0xf3, 0x2f, 0x80, 0xfe, 0x42, 0x39, 0xc7, 0xa0, 0x80, 0x06, 0x49, + 0xf0, 0x40, 0xfa, 0x1c, 0x6d, 0x7a, 0x6b, 0x10, 0x00, 0x78, 0x6a, 0xb5, + 0x4a, 0x08, 0x8a, 0x99, 0xf4, 0x17, 0x04, 0xb8, 0x2d, 0x8d, 0xef, 0xa4, + 0xfa, 0x4a, 0xf0, 0xeb, 0x59, 0x40, 0xb3, 0xef, 0xec, 0xfd, 0x2c, 0xa5, + 0x68, 0xba, 0x6c, 0x1c, 0xf1, 0xe0, 0xaa, 0x5c, 0xaf, 0x2a, 0x3f, 0xd7, + 0x2c, 0xb3, 0xab, 0x79, 0x55, 0x6c, 0x13, 0x8f, 0x24, 0x1b, 0xa1, 0xb1, + 0x6c, 0x6c, 0x02, 0x77, 0x3b, 0x0c, 0x36, 0x16, 0xfd, 0x16, 0x36, 0xd1, + 0x61, 0x8b, 0xe7, 0x1e, 0x8d, 0x27, 0xa8, 0x89, 0xcc, 0x00, 0xb8, 0x68, + 0x38, 0xac, 0x19, 0x04, 0x3a, 0xea, 0x3e, 0x31, 0x09, 0x15, 0x09, 0xe9, + 0x67, 0x48, 0x8a, 0x49, 0x86, 0xdf, 0x40, 0x20, 0xd3, 0x94, 0xb7, 0xa8, + 0x13, 0x2a, 0xbb, 0x6c, 0xfd, 0xaa, 0xc6, 0xb2, 0xc2, 0x38, 0x4a, 0x0a, + 0x08, 0xd1, 0x06, 0x29, 0xb7, 0x58, 0x99, 0xe2, 0xbb, 0x1e, 0xa3, 0xca, + 0xb1, 0x36, 0x86, 0x47, 0x9e, 0x14, 0x91, 0xc1, 0x2a, 0x48, 0xed, 0xfd, + 0x37, 0xb8, 0x36, 0x21, 0x6c, 0x6e, 0x48, 0x76, 0x81, 0xe9, 0xb4, 0x1e, + 0x9b, 0x81, 0x58, 0xb9, 0xe4, 0x3d, 0x5a, 0x96, 0x17, 0xae, 0x23, 0xc5, + 0xcd, 0x5e, 0x4d, 0x9f, 0x09, 0xda, 0x5b, 0x15, 0x6b, 0xb9, 0xbb, 0x1e, + 0xd4, 0xaa, 0xea, 0xc1, 0x47, 0x01, 0x7f, 0x92, 0x0e, 0x7a, 0x94, 0xee, + 0xbe, 0x42, 0xa1, 0x71, 0x5f, 0x4d, 0x63, 0xde, 0x8a, 0x40, 0xe5, 0x68, + 0xe8, 0x6f, 0xaf, 0xb2, 0xbf, 0x63, 0xbb, 0x75, 0x0b, 0x4a, 0xce, 0xb5, + 0x57, 0x71, 0x2d, 0x33, 0x98, 0x8c, 0x92, 0x9f, 0xc7, 0x9f, 0xe8, 0x07, + 0xf5, 0xd0, 0xa8, 0x2c, 0x41, 0x5e, 0x25, 0x30, 0x3d, 0x3f, 0x25, 0x89, + 0xd4, 0xd4, 0x33, 0x4e, 0x19, 0x22, 0x54, 0xbb, 0xef, 0x56, 0x01, 0x17, + 0xc1, 0x87, 0x62, 0x13, 0x96, 0xa9, 0x09, 0xe9, 0x31, 0x3e, 0x28, 0x67, + 0xee, 0xb0, 0xd2, 0x78, 0x7e, 0x2f, 0x49, 0xab, 0xb4, 0xb0, 0x3b, 0x75, + 0xf5, 0xf2, 0xaa, 0x08, 0x66, 0xdb, 0x3b, 0x34, 0x79, 0xac, 0x1c, 0x45, + 0xb3, 0xb9, 0x99, 0xdc, 0x8a, 0x03, 0x9b, 0x36, 0x08, 0x4f, 0xe8, 0x54, + 0x3c, 0x09, 0xe9, 0x59, 0x8c, 0x56, 0xd8, 0xa6, 0xf7, 0x67, 0x4e, 0x20, + 0xcc, 0x80, 0xd3, 0x1a, 0x68, 0xa3, 0x12, 0x95, 0xa5, 0x4f, 0x14, 0x0a, + 0xe4, 0xd8, 0x4e, 0x85, 0xbc, 0x60, 0x3a, 0xd2, 0x6d, 0xd6, 0xf1, 0xbc, + 0xcd, 0x1a, 0xa2, 0x5e, 0x37, 0xae, 0x0e, 0xec, 0xbd, 0x7e, 0xf7, 0xe1, + 0x67, 0x62, 0x2c, 0x58, 0x3b, 0x99, 0x2e, 0xec, 0x44, 0xb1, 0x39, 0x2c, + 0x09, 0x1a, 0x57, 0x2b, 0x4b, 0x41, 0xda, 0xcd, 0x61, 0x3f, 0x8e, 0x82, + 0x08, 0x24, 0x51, 0x35, 0x1d, 0x79, 0x4c, 0x80, 0x77, 0x39, 0x06, 0xd4, + 0x40, 0x47, 0x96, 0x9d, 0xc1, 0xb9, 0xae, 0x94, 0xb3, 0xd9, 0x1a, 0x27, + 0xfb, 0x9f, 0xee, 0x82, 0xaf, 0xfb, 0xc0, 0x32, 0x72, 0xcd, 0x21, 0x20, + 0xc5, 0x55, 0x86, 0x9c, 0xa2, 0x81, 0xc0, 0x62, 0xdf, 0x5a, 0x16, 0x43, + 0xdd, 0x37, 0xfe, 0xd1, 0x2a, 0x33, 0xef, 0x71, 0x22, 0xc0, 0x94, 0x94, + 0x81, 0x82, 0xe3, 0xe2, 0x3f, 0x64, 0x0a, 0x5b, 0x1a, 0x4c, 0xd3, 0xb3, + 0x16, 0x3d, 0x34, 0xf6, 0x14, 0xb2, 0x61, 0xc3, 0x15, 0x20, 0x9c, 0x80, + 0x6d, 0x23, 0xde, 0x9e, 0xb7, 0xe3, 0xbc, 0x1e, 0x39, 0xe0, 0x22, 0xa1, + 0x11, 0x56, 0x03, 0xf2, 0x6d, 0x6e, 0x39, 0xde, 0x50, 0x07, 0x61, 0x92, + 0x86, 0x96, 0x4d, 0xee, 0x9b, 0x3e, 0xbd, 0xcc, 0xe7, 0x02, 0xdf, 0xce, + 0xca, 0x46, 0x71, 0xc2, 0x64, 0x6b, 0x9c, 0x0a, 0x78, 0xae, 0x0e, 0xde, + 0x10, 0x3c, 0x68, 0x28, 0xbe, 0xab, 0x94, 0xf9, 0xe3, 0xa9, 0xe8, 0x9c, + 0x67, 0x0d, 0xa9, 0x48, 0xf0, 0x55, 0x0e, 0x4a, 0x7d, 0x3f, 0x03, 0x3a, + 0x6b, 0x30, 0x8b, 0xf8, 0x2e, 0x81, 0x2e, 0xc4, 0xd6, 0x9a, 0xfa, 0xb9, + 0x65, 0xe1, 0x95, 0xad, 0x1e, 0x5e, 0x0d, 0xae, 0x04, 0x92, 0x45, 0x22, + 0x69, 0x6e, 0xb3, 0x68, 0xe0, 0x97, 0xae, 0xd4, 0xf1, 0xd0, 0xfb, 0xae, + 0x6a, 0x51, 0x90, 0x8d, 0x82, 0x4a, 0xd3, 0x74, 0xbb, 0x51, 0xe3, 0xdc, + 0x2e, 0xd6, 0xba, 0xa6, 0x51, 0x96, 0x07, 0x9d, 0xc4, 0xab, 0xb7, 0xb1, + 0x9e, 0xd1, 0x45, 0xdd, 0x15, 0x94, 0x33, 0x23, 0x9e, 0x7c, 0xfc, 0x8b, + 0x55, 0x8c, 0x47, 0x02, 0xeb, 0xcb, 0x31, 0x6e, 0xbd, 0xc9, 0xae, 0x9c, + 0x62, 0xad, 0x32, 0xa2, 0x46, 0xd2, 0xe3, 0x39, 0x09, 0x7c, 0x97, 0xcd, + 0x20, 0x3a, 0x9c, 0x05, 0xa6, 0x3c, 0xad, 0xc8, 0xb1, 0xa2, 0xa9, 0x24, + 0x9a, 0xd9, 0x6a, 0x3c, 0xaa, 0x9d, 0xcd, 0x93, 0xc0, 0x99, 0x5f, 0x7d, + 0x0b, 0xe3, 0xc2, 0xb2, 0x57, 0x2c, 0x7a, 0x22, 0x4c, 0x99, 0xf6, 0x90, + 0xe9, 0xae, 0x17, 0x94, 0xfb, 0xb1, 0x74, 0x36, 0xcc, 0x13, 0x4b, 0xec, + 0x57, 0x3f, 0x3b, 0x40, 0xbb, 0x03, 0xe5, 0xd7, 0x4f, 0xf8, 0xe8, 0x71, + 0x29, 0x0e, 0x54, 0x9c, 0xe7, 0x5d, 0xc9, 0x81, 0xc7, 0x34, 0x06, 0x49, + 0x14, 0xd7, 0x62, 0x14, 0x30, 0xe8, 0xd3, 0x45, 0x88, 0xcb, 0x05, 0x9f, + 0xed, 0xa2, 0x1c, 0x5e, 0x32, 0x27, 0x11, 0x50, 0xc9, 0x6a, 0x86, 0x89, + 0x33, 0x28, 0x3a, 0xeb, 0x22, 0xe8, 0x29, 0xd8, 0x7b, 0x6d, 0x1a, 0x05, + 0xa4, 0xe8, 0xc7, 0x2e, 0x0c, 0x5d, 0x4a, 0x73, 0x21, 0xa7, 0x04, 0x20, + 0x4c, 0x04, 0x56, 0x90, 0x91, 0xc2, 0xd3, 0x8d, 0xfa, 0x63, 0x23, 0xa2, + 0xb7, 0x45, 0x13, 0x8d, 0x94, 0xd5, 0x34, 0xfd, 0xf3, 0xba, 0x6e, 0xd6, + 0x7a, 0x4f, 0x5c, 0x55, 0xd3, 0x1c, 0x9a, 0x93, 0x47, 0x82, 0x04, 0xb6, + 0x79, 0xd1, 0x0b, 0x90, 0xa7, 0x81, 0x90, 0x12, 0xea, 0x9d, 0xd9, 0x7c, + 0x5f, 0xff, 0x83, 0x10, 0x6b, 0xb7, 0x50, 0xa3, 0x4f, 0xdd, 0xc0, 0xc5, + 0x34, 0x4f, 0x78, 0xef, 0x44, 0x2f, 0x2e, 0x54, 0x15, 0x97, 0x64, 0xdd, + 0x96, 0x6d, 0x7d, 0x53, 0xa5, 0xa7, 0x3b, 0x49, 0xd4, 0x54, 0xf8, 0xc5, + 0x01, 0x99, 0x7a, 0xbc, 0xab, 0x6a, 0xab, 0xca, 0x95, 0x36, 0xe3, 0x8a, + 0x51, 0x24, 0x2f, 0x15, 0x50, 0xf3, 0x02, 0xf0, 0x76, 0x26, 0x38, 0xd3, + 0x0a, 0xdc, 0x35, 0x2d, 0xe4, 0xff, 0x37, 0x7a, 0x45, 0xd4, 0x78, 0xa3, + 0x19, 0x93, 0x91, 0x04, 0x7b, 0x42, 0x95, 0x16, 0x02, 0x99, 0x83, 0x31, + 0x21, 0x0d, 0x23, 0xa0, 0xa1, 0xb6, 0x6b, 0x39, 0x46, 0x1d, 0x85, 0x2c, + 0x2a, 0x0c, 0x89, 0x84, 0xb4, 0x6f, 0xd0, 0xbf, 0xcb, 0x70, 0x18, 0x2e, + 0xd8, 0xfd, 0x18, 0xce, 0xb6, 0xc0, 0xa2, 0xa5, 0x37, 0xdd, 0x40, 0x18, + 0x58, 0xe6, 0xb9, 0xe2, 0xf8, 0x5e, 0xb6, 0x2c, 0x79, 0xda, 0xd5, 0x2f, + 0x82, 0x3e, 0x41, 0x83, 0xab, 0x85, 0x49, 0xe6, 0x92, 0x9c, 0xa6, 0xf1, + 0xf8, 0x6e, 0x1c, 0x7f, 0x0b, 0x2a, 0x4d, 0x5c, 0x0a, 0x8a, 0x21, 0xc3, + 0x33, 0xe3, 0xca, 0x67, 0x79, 0x41, 0x66, 0x3e, 0xe4, 0xd4, 0x18, 0x7a, + 0xef, 0x65, 0x80, 0x47, 0x03, 0x43, 0x6c, 0xa7, 0x0b, 0xad, 0xb4, 0xc1, + 0x97, 0x8b, 0xa5, 0x78, 0xae, 0x6c, 0x6d, 0xf3, 0xc8, 0xbd, 0x58, 0xc3, + 0x69, 0x71, 0x8f, 0x1b, 0x70, 0xbd, 0xb0, 0xb7, 0x38, 0x86, 0xb3, 0xf8, + 0xd0, 0x59, 0x2f, 0x6d, 0x08, 0x49, 0x50, 0x7d, 0x2d, 0xb5, 0xca, 0xc8, + 0x21, 0xdf, 0x50, 0xe6, 0x6d, 0x89, 0x28, 0xa9, 0xc2, 0x07, 0xcf, 0xaf, + 0xd1, 0x95, 0x08, 0xfa, 0xb5, 0x61, 0x86, 0x5a, 0xfd, 0xa9, 0x82, 0xc4, + 0x2d, 0x6e, 0x4c, 0x02, 0xa5, 0x62, 0x36, 0x13, 0x54, 0x35, 0x35, 0x22, + 0x2c, 0x45, 0xc4, 0x10, 0x46, 0x65, 0xbe, 0x8e, 0xd8, 0xe5, 0xce, 0xe9, + 0x77, 0xfe, 0xba, 0x0a, 0xf6, 0x73, 0xe3, 0x9b, 0x8d, 0x70, 0x96, 0xbe, + 0x93, 0xa6, 0xe4, 0x9c, 0x1b, 0x14, 0x6b, 0x6b, 0x0a, 0xbd, 0x7b, 0x82, + 0xd8, 0xf8, 0xaa, 0x66, 0xce, 0x27, 0x9f, 0xdb, 0x81, 0x52, 0xac, 0xfc, + 0x15, 0x65, 0x00, 0x04, 0x5a, 0x8b, 0x5b, 0x20, 0xce, 0xd7, 0x5f, 0xf5, + 0xf5, 0xe3, 0x4b, 0x61, 0x86, 0xc4, 0x97, 0x99, 0xc5, 0xd2, 0x8d, 0x11, + 0xfc, 0x19, 0x81, 0x0a, 0x20, 0xd4, 0x73, 0xcc, 0x2c, 0xc9, 0x38, 0xc2, + 0x46, 0x9e, 0xb2, 0x8a, 0xbe, 0x00, 0xf2, 0x78, 0x55, 0x35, 0x9c, 0x52, + 0x96, 0x19, 0x85, 0x32, 0xc1, 0xea, 0xef, 0x31, 0x67, 0xd2, 0x70, 0xfc, + 0xcf, 0x0f, 0xbf, 0xda, 0x46, 0x44, 0xf5, 0x50, 0x68, 0xf7, 0x95, 0x43, + 0x8c, 0x84, 0x84, 0x47, 0x74, 0x59, 0x74, 0xe8, 0xa2, 0x54, 0x56, 0x73, + 0x25, 0xd4, 0x63, 0xd0, 0xa9, 0xac, 0xb8, 0x18, 0x78, 0x6b, 0x41, 0x98, + 0xef, 0x08, 0xac, 0x94, 0x32, 0xa4, 0x56, 0xcc, 0xe8, 0x2e, 0x9c, 0x4e, + 0x02, 0x43, 0x5e, 0xda, 0xc8, 0xe5, 0x5e, 0x48, 0xf3, 0x1d, 0x9b, 0x7d, + 0x93, 0x16, 0x9e, 0xd2, 0xf5, 0xf6, 0x3a, 0x19, 0x32, 0x87, 0xd4, 0xd8, + 0x47, 0x5f, 0xe6, 0x09, 0x38, 0x75, 0x0f, 0x76, 0x06, 0x12, 0x23, 0x2c, + 0x43, 0x9c, 0xa5, 0x45, 0x48, 0xbf, 0xde, 0xd7, 0x8e, 0x52, 0x93, 0x75, + 0x29, 0x3c, 0x9d, 0xc1, 0xa3, 0x9c, 0x45, 0x69, 0x09, 0xf3, 0x43, 0xd5, + 0x5a, 0xc6, 0xb9, 0xba, 0x9a, 0x9a, 0x7e, 0x51, 0xb9, 0x51, 0x9e, 0xdc, + 0x13, 0x3b, 0x9f, 0xd9, 0xaf, 0xd2, 0x48, 0x9e, 0xea, 0x93, 0x92, 0x6d, + 0x9f, 0x3f, 0x5a, 0x46, 0xa4, 0x9e, 0x2c, 0x96, 0xa6, 0x0a, 0x5e, 0x07, + 0xc5, 0x4e, 0x60, 0x1d, 0xa5, 0xca, 0x41, 0xdd, 0xd1, 0x8c, 0x90, 0x14, + 0xdf, 0xe3, 0x44, 0xe1, 0x43, 0xdc, 0xea, 0x84, 0x91, 0xcf, 0x19, 0x56, + 0xb9, 0x0c, 0xff, 0x08, 0xc9, 0x20, 0xfd, 0x0c, 0x20, 0x85, 0x2a, 0x83, + 0x78, 0xc6, 0xa3, 0xf2, 0x49, 0x35, 0x83, 0xaf, 0xeb, 0x35, 0x06, 0x2f, + 0x9b, 0x19, 0xbe, 0xb6, 0xc3, 0xbc, 0x25, 0xf5, 0x87, 0x52, 0x8d, 0x88, + 0xd3, 0xa6, 0x9a, 0x1f, 0xbb, 0x3f, 0xab, 0x6b, 0x07, 0x7c, 0x0b, 0x7c, + 0x61, 0x28, 0xf0, 0xfe, 0x50, 0xbe, 0xc8, 0xd4, 0xb0, 0xc9, 0x3c, 0x69, + 0x5e, 0x31, 0x7f, 0x4d, 0x73, 0xff, 0x27, 0xd9, 0x50, 0x90, 0x62, 0xf6, + 0xb1, 0x47, 0x6d, 0x07, 0xbc, 0x5d, 0x11, 0x51, 0x38, 0x06, 0xf2, 0x9d, + 0x8e, 0xef, 0x41, 0xe9, 0x88, 0x64, 0x2a, 0x32, 0xcc, 0xcc, 0x41, 0x8c, + 0x75, 0x13, 0x7f, 0x8a, 0x7b, 0x26, 0xac, 0x44, 0x80, 0x03, 0xdb, 0x7f, + 0x6b, 0xf5, 0x67, 0xc2, 0x62, 0xd0, 0x29, 0xf1, 0x16, 0xec, 0x29, 0x8c, + 0x89, 0xc6, 0xe4, 0x7e, 0xd0, 0x1f, 0x89, 0x58, 0x06, 0x0e, 0xc8, 0xf9, + 0xa1, 0xd4, 0x89, 0x96, 0x99, 0x8e, 0xfd, 0xa7, 0x6c, 0x48, 0xb9, 0x76, + 0x6f, 0xff, 0x17, 0xb8, 0x4d, 0x4d, 0x42, 0x65, 0x87, 0x4a, 0x48, 0xa4, + 0x2a, 0x95, 0x55, 0x07, 0xaf, 0xa8, 0x60, 0x3f, 0xad, 0x2a, 0x89, 0x7c, + 0x00, 0xba, 0x82, 0x13, 0x98, 0x08, 0x72, 0x3a, 0xc3, 0xd5, 0xfa, 0xb6, + 0xb6, 0x61, 0x35, 0xfe, 0xcb, 0x02, 0x5e, 0xef, 0x70, 0x69, 0x8b, 0xa4, + 0xe2, 0x62, 0x34, 0x52, 0x5d, 0x72, 0xde, 0x15, 0x70, 0x51, 0x74, 0x04, + 0x05, 0x05, 0xba, 0x3c, 0x23, 0x24, 0x62, 0x5e, 0x15, 0x44, 0x69, 0x61, + 0xe0, 0xee, 0x66, 0x85, 0xe5, 0xb7, 0x6f, 0x4f, 0x11, 0xa5, 0xa2, 0x66, + 0x46, 0x69, 0x2d, 0xc9, 0x78, 0x20, 0x79, 0x5c, 0xac, 0xb7, 0xb9, 0x6e, + 0x3a, 0xbd, 0x29, 0x9e, 0xd8, 0x3d, 0xb3, 0xb4, 0xc2, 0x68, 0xc2, 0x78, + 0xe6, 0x4b, 0x9a, 0x75, 0xf3, 0x88, 0xfe, 0x67, 0x78, 0x4c, 0x9b, 0xde, + 0xa9, 0x9b, 0xee, 0x07, 0xb4, 0x72, 0x6d, 0xa6, 0x14, 0xc1, 0x5a, 0x3e, + 0x7d, 0x19, 0x38, 0x98, 0x08, 0x93, 0x1b, 0x64, 0xec, 0xb2, 0xb8, 0x14, + 0x66, 0xec, 0x09, 0x9e, 0x6b, 0xf9, 0xd9, 0xb4, 0x34, 0xc4, 0x39, 0x7b, + 0x22, 0xf2, 0x6e, 0xd0, 0x06, 0x49, 0x52, 0x7f, 0x9a, 0x5c, 0xa3, 0x3b, + 0xa3, 0x3d, 0xcb, 0xb2, 0xe5, 0xd8, 0xfb, 0x43, 0x3e, 0x4d, 0xa4, 0xb0, + 0x70, 0x93, 0xec, 0x27, 0xf1, 0x7e, 0x6d, 0xee, 0x70, 0x35, 0x96, 0x4e, + 0x6e, 0xfc, 0x3b, 0xa6, 0x84, 0x29, 0xac, 0x8e, 0x38, 0x58, 0xc3, 0x93, + 0x28, 0xf6, 0xf1, 0x74, 0x14, 0x1a, 0x1e, 0xb3, 0x86, 0x97, 0x3e, 0x42, + 0x2e, 0x00, 0xf6, 0x82, 0x4d, 0xaa, 0xa9, 0x2d, 0xea, 0xd2, 0xd0, 0xe9, + 0x54, 0x10, 0x4c, 0xbc, 0x61, 0xc3, 0xe1, 0x03, 0x58, 0x2a, 0x3a, 0xaa, + 0x2f, 0x28, 0xcf, 0x0e, 0x98, 0x36, 0xac, 0x15, 0x20, 0x65, 0xd8, 0x49, + 0x9a, 0xe9, 0x28, 0xbd, 0x47, 0xf6, 0xc5, 0x25, 0x26, 0x36, 0x94, 0x29, + 0x51, 0x52, 0x91, 0xdd, 0xc0, 0xbd, 0xf8, 0x6e, 0xdc, 0xcc, 0x61, 0x7f, + 0xde, 0x83, 0x5e, 0x99, 0x4d, 0x60, 0x58, 0xa1, 0x81, 0xc6, 0x9c, 0xda, + 0xc8, 0x0a, 0x20, 0x55, 0x0b, 0x60, 0x5b, 0xc2, 0xc3, 0x80, 0x5b, 0xc9, + 0x52, 0xe4, 0x6c, 0xb5, 0xfd, 0xaf, 0x23, 0xb9, 0x98, 0x72, 0x9d, 0x6d, + 0xcc, 0xa0, 0xa7, 0xa8, 0x6d, 0x8c, 0x9e, 0xac, 0xee, 0x71, 0xe4, 0xf0, + 0x40, 0x7c, 0x77, 0xa8, 0x94, 0x91, 0xc8, 0x31, 0x3c, 0x15, 0x6a, 0x23, + 0xe3, 0x02, 0x49, 0xa5, 0x99, 0x78, 0x08, 0x6b, 0x18, 0x8e, 0x21, 0x50, + 0x4a, 0x49, 0x4e, 0xa6, 0xe7, 0xfd, 0x6c, 0xfa, 0xdd, 0xfd, 0x19, 0xc8, + 0x54, 0x0b, 0xaf, 0x3a, 0x4a, 0xa4, 0x56, 0x65, 0x8e, 0x68, 0xaa, 0x35, + 0x82, 0xd7, 0xb4, 0xda, 0x7c, 0x88, 0xa9, 0xe4, 0x0b, 0xdc, 0x3e, 0xcd, + 0xf9, 0x89, 0xdf, 0xf9, 0x93, 0x92, 0x37, 0x0d, 0x39, 0xef, 0xe7, 0xf0, + 0xc2, 0xf3, 0x52, 0x9e, 0x52, 0xdd, 0x75, 0x36, 0x1b, 0x2b, 0xd9, 0x3a, + 0xcf, 0x3a, 0xf9, 0xb8, 0x61, 0x42, 0xbb, 0xd8, 0x00, 0x28, 0x90, 0xe9, + 0x44, 0x1e, 0xd9, 0xae, 0xad, 0xb3, 0xf7, 0x45, 0x42, 0x50, 0x73, 0xcf, + 0x7d, 0x71, 0x28, 0x63, 0x2e, 0x72, 0x57, 0x0f, 0xff, 0x88, 0x8a, 0x44, + 0x4d, 0xe4, 0x32, 0x53, 0x6a, 0x4e, 0xea, 0x91, 0xba, 0x5c, 0x13, 0x03, + 0xdf, 0xcc, 0x58, 0xdf, 0xa5, 0x05, 0x4c, 0x96, 0x10, 0xb4, 0x52, 0x74, + 0xb9, 0x03, 0x1f, 0x97, 0xaa, 0xef, 0x9d, 0xc0, 0xfe, 0xb5, 0xb2, 0x1a, + 0xc4, 0x1d, 0xf0, 0xb9, 0xa7, 0xa1, 0xc6, 0xd2, 0x59, 0xc7, 0xe8, 0x24, + 0x85, 0x04, 0xcd, 0x91, 0xca, 0xc1, 0x02, 0x29, 0xa7, 0xac, 0x58, 0xfa, + 0x6f, 0xf5, 0x1e, 0x03, 0x53, 0xe3, 0x88, 0xee, 0x3f, 0x85, 0xd2, 0x0e, + 0x03, 0x8e, 0x3f, 0x45, 0xd5, 0x18, 0x37, 0xbc, 0xe1, 0xf8, 0xf4, 0x59, + 0x17, 0x29, 0xe2, 0x63, 0x22, 0xa2, 0xb4, 0x25, 0xa2, 0x4b, 0x0e, 0x0e, + 0x8d, 0xf1, 0x43, 0x2e, 0x42, 0x11, 0x44, 0x58, 0xb8, 0x94, 0xbb, 0x18, + 0xbf, 0x99, 0xde, 0x13, 0x38, 0xe4, 0xa8, 0xbb, 0xb2, 0xf4, 0x35, 0xbc, + 0x53, 0x4d, 0x97, 0xcd, 0x0b, 0xab, 0xbe, 0xe5, 0x85, 0x75, 0x53, 0xbb, + 0x75, 0x3a, 0xd7, 0x5a, 0xfe, 0x53, 0x28, 0x35, 0x1a, 0xf6, 0xc7, 0x35, + 0x7f, 0xcc, 0xf1, 0xb8, 0x29, 0x2c, 0x60, 0x09, 0xc4, 0xac, 0xb2, 0x5a, + 0xe6, 0x73, 0xb8, 0xe3, 0xd4, 0x98, 0x2d, 0xeb, 0xbb, 0xfe, 0xe5, 0x20, + 0xf9, 0xf7, 0x82, 0x3b, 0xd6, 0xd1, 0x09, 0x61, 0x91, 0xde, 0x2f, 0x2c, + 0x84, 0xfb, 0xa5, 0x0f, 0x7f, 0xdb, 0xb0, 0xed, 0x6d, 0x03, 0xe2, 0x19, + 0x7b, 0x74, 0x07, 0x87, 0x12, 0xbb, 0x0d, 0x2b, 0xe7, 0xd4, 0x17, 0x4d, + 0xde, 0x09, 0xe9, 0x90, 0xc4, 0x30, 0xe5, 0xc7, 0x3d, 0xd3, 0x13, 0x44, + 0x2b, 0x52, 0xbf, 0xaf, 0x0e, 0x92, 0xfc, 0x16, 0xa9, 0x0c, 0x7f, 0xe4, + 0xc4, 0x7a, 0xe4, 0x49, 0x74, 0x50, 0x9a, 0xf8, 0xf9, 0x25, 0x91, 0x35, + 0x0c, 0x27, 0x79, 0x7d, 0x4a, 0x4e, 0x94, 0xd2, 0xac, 0xa1, 0x13, 0x6d, + 0x80, 0xca, 0xa6, 0xe8, 0x04, 0x40, 0xb7, 0x2a, 0x13, 0x85, 0x3e, 0x35, + 0x62, 0x19, 0xc4, 0x54, 0xe7, 0x8e, 0x74, 0x01, 0xcc, 0x77, 0xb2, 0xee, + 0x59, 0x50, 0xad, 0xc1, 0x85, 0xa6, 0xf4, 0x73, 0x97, 0x9e, 0xd1, 0xa5, + 0x90, 0x74, 0x78, 0xf3, 0x6b, 0x99, 0x8c, 0xe1, 0xdb, 0x47, 0xf8, 0xdb, + 0x43, 0xd7, 0x6d, 0x4d, 0x0b, 0x23, 0xd3, 0x9d, 0xd9, 0x70, 0xe0, 0x87, + 0x1e, 0x16, 0x0d, 0xaa, 0x53, 0xd2, 0x4f, 0x32, 0x1a, 0x1c, 0x43, 0xc0, + 0x9e, 0xa2, 0x85, 0xd7, 0xea, 0x8d, 0x12, 0xb8, 0xc2, 0xa4, 0x4d, 0x0e, + 0x90, 0xad, 0xdc, 0xe7, 0x54, 0x3a, 0xe9, 0x9a, 0x9e, 0x00, 0xfb, 0xb1, + 0xfa, 0xa5, 0x1c, 0x4c, 0x17, 0xff, 0x38, 0xb8, 0x75, 0x0e, 0xf2, 0x78, + 0xe8, 0x3a, 0x75, 0x48, 0xc8, 0xd8, 0x72, 0x65, 0x9e, 0x36, 0x98, 0x5b, + 0xb7, 0xc3, 0xfb, 0xec, 0xa0, 0x5a, 0xf7, 0x60, 0x30, 0xcf, 0x20, 0x9d, + 0x6e, 0x83, 0x10, 0x7e, 0x73, 0x33, 0xc1, 0x93, 0x0c, 0x58, 0x80, 0x51, + 0xf0, 0x16, 0x77, 0x29, 0xf9, 0x7f, 0x2e, 0xd9, 0x42, 0x38, 0x8b, 0x83, + 0xc6, 0x9d, 0x0d, 0xbb, 0xdd, 0x58, 0x89, 0x5c, 0xb7, 0xaa, 0x6f, 0xdf, + 0x51, 0x88, 0xb6, 0x78, 0x8a, 0xe0, 0x99, 0xc8, 0x89, 0x6b, 0xeb, 0x71, + 0xfc, 0x8a, 0xc0, 0xd0, 0x2b, 0x5b, 0x1c, 0xda, 0x0c, 0x07, 0x6b, 0x30, + 0xa6, 0xff, 0xea, 0xd4, 0xcd, 0x4d, 0xb8, 0x88, 0x37, 0x2a, 0x0f, 0x7d, + 0x40, 0xc1, 0x0b, 0x66, 0xdd, 0x99, 0x9e, 0xd1, 0x54, 0x51, 0x6c, 0xf7, + 0x05, 0x15, 0x2b, 0x76, 0x1e, 0xa5, 0x1e, 0x36, 0x10, 0x33, 0xf6, 0xba, + 0x3a, 0x7e, 0x1f, 0x06, 0xbc, 0x51, 0x93, 0x06, 0x19, 0xc6, 0x49, 0x9d, + 0x3a, 0x08, 0x8f, 0xb4, 0x48, 0xd8, 0xe2, 0x45, 0x06, 0x74, 0x81, 0x05, + 0x00, 0x25, 0xb8, 0x56, 0x98, 0x99, 0xa3, 0xb5, 0x75, 0xb3, 0x43, 0x23, + 0x47, 0x34, 0x9a, 0x9a, 0x68, 0x93, 0x0a, 0x31, 0xf7, 0xdc, 0xa1, 0x1c, + 0x11, 0xb1, 0x89, 0x48, 0x5d, 0xfd, 0x6f, 0xb3, 0xa9, 0x76, 0xf8, 0x5e, + 0xa5, 0xf2, 0xeb, 0x4d, 0xf6, 0x21, 0xe8, 0x65, 0xda, 0x59, 0x46, 0x77, + 0x90, 0x7a, 0x54, 0x4f, 0x17, 0x17, 0x6a, 0x53, 0xbe, 0xe8, 0xbc, 0x30, + 0x67, 0x03, 0x1c, 0xf1, 0xee, 0x5a, 0x1a, 0x87, 0x7e, 0xd1, 0x2b, 0xcd, + 0x24, 0x92, 0x6f, 0xb1, 0xa9, 0x38, 0xf8, 0x0e, 0x66, 0x04, 0xca, 0x24, + 0x83, 0x8c, 0x01, 0xdb, 0xb9, 0x28, 0x61, 0x74, 0xe9, 0xe0, 0x80, 0x18, + 0x15, 0x6b, 0xeb, 0x68, 0x0e, 0x2a, 0x3b, 0x15, 0x40, 0xcd, 0x45, 0xd9, + 0x1a, 0x43, 0x54, 0x1d, 0xfc, 0x4e, 0xf2, 0x16, 0xa9, 0x71, 0x2f, 0x08, + 0xc5, 0x97, 0xf6, 0xc1, 0x5f, 0x66, 0x45, 0x92, 0x32, 0x6d, 0x66, 0xf9, + 0x2b, 0x07, 0xfc, 0x73, 0x32, 0x22, 0x52, 0x4f, 0xfe, 0x70, 0xb5, 0x56, + 0x6e, 0x94, 0x54, 0x8e, 0x5f, 0xd3, 0x25, 0xda, 0xd8, 0x12, 0xa0, 0x57, + 0x33, 0x1e, 0x0d, 0xba, 0xed, 0x1f, 0x55, 0x75, 0x73, 0xd7, 0x1a, 0x7f, + 0x34, 0x42, 0x65, 0x9a, 0xae, 0x3a, 0x82, 0xb0, 0x1c, 0x0d, 0xc4, 0xd7, + 0x17, 0x13, 0x5c, 0xa0, 0xc7, 0x64, 0xdd, 0xaf, 0xba, 0x5a, 0xdc, 0x14, + 0x0b, 0x33, 0xa2, 0x3d, 0x90, 0x0a, 0xc1, 0xa4, 0xbc, 0x2c, 0x40, 0xc4, + 0xca, 0x63, 0x67, 0x4e, 0x94, 0x6d, 0xb9, 0x51, 0xf8, 0x70, 0x02, 0x90, + 0x17, 0xfd, 0xf9, 0xa8, 0xa4, 0xc5, 0xb0, 0xb0, 0xc1, 0xd8, 0xfa, 0x3a, + 0xca, 0x75, 0x2f, 0x02, 0x5c, 0x2b, 0xec, 0x15, 0x8f, 0xc6, 0x53, 0x12, + 0x17, 0x26, 0x66, 0x3a, 0xf4, 0xf9, 0x35, 0x26, 0x1c, 0xb5, 0xf7, 0xca, + 0x9a, 0xd1, 0x37, 0x5d, 0x09, 0xbd, 0x30, 0x9c, 0x97, 0x4c, 0xb6, 0xd9, + 0x3b, 0x28, 0x1d, 0xe6, 0x95, 0x2b, 0xa2, 0x92, 0x75, 0x75, 0x79, 0xa1, + 0x34, 0xd3, 0x8c, 0xc3, 0x0b, 0x2e, 0x68, 0xd0, 0x5a, 0x50, 0x9d, 0x86, + 0xe1, 0x3d, 0x08, 0xbe, 0xcd, 0xae, 0xa4, 0x65, 0x5e, 0x89, 0x67, 0xdb, + 0x19, 0xff, 0x06, 0x7d, 0xcc, 0xd2, 0x5c, 0xcf, 0x0b, 0xa4, 0xf2, 0x06, + 0x5a, 0xb0, 0x43, 0xa0, 0x66, 0x00, 0xae, 0x62, 0xf2, 0xc5, 0x21, 0xfd, + 0x83, 0xc5, 0x10, 0xe7, 0xd1, 0x76, 0x96, 0xca, 0xca, 0x96, 0xb8, 0x17, + 0x24, 0x5c, 0x59, 0x7b, 0x62, 0x1d, 0x92, 0xd2, 0xce, 0x9e, 0x4f, 0x0f, + 0x07, 0x7c, 0xef, 0xe5, 0xa6, 0x92, 0x2f, 0xd3, 0x42, 0xb7, 0xaf, 0xca, + 0x50, 0x83, 0xb2, 0x53, 0x6c, 0xbc, 0x1d, 0xf9, 0x5d, 0xb0, 0x6f, 0x0e, + 0x5a, 0xb8, 0x17, 0x26, 0x47, 0x62, 0x28, 0xd2, 0xd5, 0xb3, 0x5d, 0xc6, + 0x40, 0x86, 0x1c, 0xcb, 0xcb, 0x26, 0xf1, 0xe5, 0x36, 0x31, 0xcb, 0x67, + 0xd4, 0xc4, 0x14, 0x43, 0x2d, 0x26, 0x49, 0xb5, 0x89, 0x04, 0xd2, 0x8b, + 0x56, 0xbd, 0x77, 0xa7, 0x01, 0x8a, 0x4d, 0x33, 0xcc, 0x58, 0xdd, 0x9f, + 0x35, 0x75, 0xa2, 0x63, 0xa9, 0x74, 0x62, 0x12, 0x6a, 0xd6, 0x72, 0x16, + 0x0a, 0x2a, 0x02, 0xc3, 0xa0, 0x1d, 0xc2, 0x07, 0xd2, 0xbe, 0x54, 0x00, + 0x26, 0xf2, 0xe7, 0xb6, 0xd1, 0x9e, 0xdc, 0x05, 0x46, 0x6a, 0x18, 0x68, + 0x32, 0x7f, 0x4c, 0x7c, 0xc7, 0xe9, 0xc9, 0x7e, 0x9d, 0x7b, 0x2e, 0xf5, + 0xbb, 0xc7, 0xc8, 0xff, 0x6c, 0x2f, 0x93, 0x84, 0xfb, 0x66, 0x7d, 0x09, + 0x02, 0x7c, 0xcf, 0x81, 0x51, 0x49, 0x88, 0x9d, 0xe0, 0x77, 0x52, 0x87, + 0x67, 0xfa, 0xe8, 0xda, 0x6a, 0x5b, 0xd5, 0x02, 0xfa, 0x7e, 0xab, 0xa1, + 0xd4, 0x73, 0x8b, 0x93, 0xbd, 0x08, 0x9a, 0x7c, 0x19, 0x6b, 0x74, 0x68, + 0x99, 0xd9, 0x19, 0xd6, 0x92, 0x23, 0xd1, 0xe9, 0x12, 0xd7, 0xd5, 0xcb, + 0x16, 0xf6, 0x26, 0xef, 0x6d, 0x35, 0x58, 0x87, 0xdb, 0xa2, 0xaa, 0xd7, + 0x87, 0x8c, 0xfd, 0x0a, 0x1b, 0x9a, 0x90, 0x3b, 0xf4, 0x96, 0x88, 0x3f, + 0xa4, 0x28, 0x4b, 0x51, 0x17, 0x6a, 0x98, 0xbb, 0xfa, 0x02, 0x31, 0x11, + 0x55, 0xca, 0x8a, 0x65, 0x39, 0x7c, 0x63, 0xa2, 0x78, 0x7e, 0xe9, 0xaf, + 0x44, 0xbc, 0xa2, 0xd8, 0xcc, 0xac, 0x0b, 0x91, 0xb5, 0x49, 0xe8, 0x4e, + 0xd8, 0xb7, 0x19, 0x06, 0x2c, 0x33, 0x6c, 0xcb, 0xb1, 0x0d, 0x25, 0x6c, + 0x6c, 0x33, 0x31, 0xf7, 0xaf, 0xe8, 0x52, 0xcd, 0x68, 0x96, 0x03, 0x4c, + 0xb5, 0xc9, 0xc8, 0x6e, 0xa2, 0x07, 0xf0, 0xde, 0x95, 0x9a, 0x95, 0x0f, + 0x29, 0xdc, 0x8f, 0x6a, 0xbf, 0x41, 0x33, 0x99, 0x58, 0x25, 0xbc, 0x6c, + 0x7c, 0x8f, 0xa7, 0x83, 0x1a, 0xbc, 0xc4, 0xcf, 0x4e, 0xb2, 0x76, 0x6a, + 0x59, 0xb1, 0x89, 0x94, 0x02, 0xd2, 0x67, 0xae, 0x35, 0x74, 0x75, 0x96, + 0xf7, 0x9f, 0x16, 0x8c, 0x34, 0x46, 0x37, 0xcd, 0xa3, 0x62, 0xd0, 0x12, + 0x47, 0x0c, 0xfa, 0x8a, 0xe2, 0x35, 0x63, 0x9c, 0x9e, 0x0f, 0xd6, 0x68, + 0x71, 0x72, 0x26, 0xc1, 0x1c, 0x30, 0x80, 0x86, 0xb6, 0x78, 0xa1, 0xc8, + 0x99, 0x54, 0x56, 0xa8, 0xc5, 0x80, 0x02, 0x29, 0x8e, 0x24, 0x1b, 0xc7, + 0x36, 0x60, 0xa7, 0xd2, 0x18, 0xd1, 0xcc, 0x4f, 0x30, 0x96, 0xf9, 0xa3, + 0xbd, 0xf5, 0xdb, 0x87, 0x2e, 0x97, 0x05, 0x16, 0x2f, 0x65, 0xcb, 0x9f, + 0x8e, 0x7b, 0xbd, 0x3d, 0x8a, 0x49, 0xcb, 0x00, 0x06, 0xfb, 0x59, 0xed, + 0x1c, 0x04, 0x9c, 0xbd, 0xfc, 0xc3, 0xf5, 0xb2, 0xbb, 0x95, 0xf6, 0x07, + 0xd7, 0x7a, 0x01, 0xa5, 0xf6, 0x46, 0x5d, 0xc5, 0xb6, 0x05, 0x20, 0x8e, + 0xf7, 0xa0, 0x6c, 0x76, 0x3a, 0xcb, 0xe9, 0x87, 0xb5, 0x69, 0xa3, 0x6a, + 0xb5, 0xa2, 0xaf, 0xe1, 0xe9, 0xd9, 0x76, 0x4e, 0xb8, 0xe9, 0x84, 0xac, + 0xb2, 0xfd, 0x7c, 0x48, 0x5c, 0x20, 0x50, 0x20, 0x7a, 0x57, 0x50, 0x11, + 0xef, 0x8a, 0x49, 0x76, 0x24, 0xde, 0x38, 0xd4, 0x06, 0x62, 0xf9, 0x5c, + 0xaa, 0xa5, 0xa4, 0x85, 0x97, 0xfe, 0xd0, 0x8c, 0x18, 0x27, 0x9f, 0xbf, + 0xad, 0xae, 0x4a, 0x9f, 0x1a, 0x2f, 0xa8, 0x1b, 0x40, 0x61, 0x07, 0x8d, + 0x0f, 0x71, 0x0d, 0x3c, 0x71, 0x88, 0x8f, 0xf6, 0xc7, 0xf0, 0x0f, 0x73, + 0x9c, 0xb4, 0x8c, 0x4c, 0xb1, 0xab, 0x1d, 0xab, 0x4d, 0xd3, 0xde, 0x5c, + 0xa7, 0xba, 0x31, 0x0a, 0x8a, 0x5f, 0xf7, 0xf4, 0xba, 0x59, 0x92, 0x18, + 0xea, 0xd9, 0x26, 0xec, 0xe9, 0x68, 0x78, 0x3e, 0xa3, 0x56, 0xf0, 0x71, + 0xfe, 0x1e, 0xc2, 0x07, 0x0d, 0xd0, 0xef, 0x23, 0xdd, 0x96, 0x56, 0xae, + 0x8e, 0xd6, 0x0f, 0x9c, 0x4a, 0x98, 0xce, 0xba, 0x58, 0x1f, 0x8d, 0xb6, + 0x38, 0x13, 0x7b, 0x91, 0x84, 0xcd, 0x0f, 0xe1, 0x32, 0xbf, 0x63, 0x2c, + 0xd2, 0x5e, 0x1a, 0xdf, 0x54, 0xaa, 0x8c, 0x97, 0x93, 0x19, 0xa0, 0xd9, + 0xf5, 0xcf, 0xf1, 0xcc, 0xdd, 0xe3, 0xaf, 0xe6, 0x6f, 0x6b, 0xcf, 0xa4, + 0xaa, 0x8c, 0xb7, 0x3c, 0x2a, 0xea, 0x35, 0x8d, 0x89, 0x80, 0x35, 0x1c, + 0x6d, 0x05, 0x69, 0x91, 0xa9, 0xd1, 0x90, 0x4a, 0x14, 0xc9, 0xb6, 0x7a, + 0xe5, 0x46, 0x05, 0x9e, 0x1c, 0xd2, 0xbc, 0xda, 0xc9, 0xf0, 0xd3, 0xba, + 0x24, 0xa6, 0x89, 0x71, 0x3d, 0x03, 0x22, 0x29, 0xb7, 0xbf, 0xc5, 0x6a, + 0xb7, 0xa0, 0x8d, 0xd0, 0x58, 0x5a, 0x5e, 0xac, 0x07, 0x4c, 0x6d, 0x79, + 0x35, 0x71, 0x97, 0x2e, 0x34, 0x4b, 0x06, 0x13, 0xcd, 0x28, 0x33, 0x6d, + 0x17, 0x3a, 0x68, 0xe3, 0xa8, 0x97, 0x2c, 0x66, 0x9e, 0x76, 0x92, 0xf1, + 0xb7, 0x3c, 0x58, 0xdf, 0xd2, 0xe2, 0x91, 0x2e, 0x39, 0x3e, 0x41, 0xb0, + 0xb8, 0xde, 0xe6, 0xc8, 0x1f, 0xa7, 0xc7, 0x24, 0x2a, 0x1d, 0xd7, 0x3c, + 0xe0, 0xe0, 0x36, 0xab, 0xd5, 0x80, 0xb7, 0x29, 0x09, 0x4e, 0x1c, 0xc2, + 0x42, 0xad, 0x74, 0x8b, 0x64, 0xb2, 0xa0, 0xc6, 0xf4, 0xc3, 0xde, 0x17, + 0xbc, 0x00, 0x23, 0xdd, 0x43, 0xab, 0xa9, 0xac, 0x16, 0x34, 0x95, 0x4d, + 0x0f, 0xda, 0x5e, 0xed, 0xd4, 0x7e, 0xb7, 0xc0, 0x53, 0xed, 0x18, 0x52, + 0x2a, 0xba, 0x04, 0x12, 0xad, 0x3e, 0x5d, 0x72, 0xb4, 0x9e, 0xb3, 0x7b, + 0x59, 0xf2, 0xa2, 0xea, 0x06, 0x20, 0x5d, 0x81, 0x55, 0x91, 0x86, 0xfc, + 0xb8, 0x4d, 0x8b, 0x4e, 0x35, 0x36, 0xe7, 0x34, 0x90, 0x46, 0x94, 0x51, + 0xf6, 0x29, 0x72, 0x1b, 0x7c, 0x18, 0x3d, 0x9e, 0x2c, 0x0a, 0x05, 0x60, + 0x60, 0xd8, 0xec, 0xda, 0x79, 0x9a, 0x29, 0x22, 0x59, 0x2c, 0xf9, 0x7f, + 0xd0, 0x8e, 0x59, 0x63, 0xe1, 0x62, 0xb2, 0x2e, 0x5f, 0xef, 0x80, 0x23, + 0xb3, 0x28, 0xde, 0xfe, 0x77, 0xb3, 0x3e, 0x39, 0x28, 0x62, 0xdd, 0x3c, + 0x04, 0xe5, 0x8d, 0x09, 0x78, 0x59, 0x51, 0x3b, 0xdd, 0x71, 0x8f, 0xd9, + 0x63, 0xfa, 0x20, 0x7a, 0x45, 0xab, 0x36, 0x7b, 0xfe, 0x12, 0xcf, 0x72, + 0x47, 0xb1, 0x4b, 0x3c, 0x2f, 0x8e, 0x3b, 0x25, 0x03, 0x1e, 0x84, 0xa0, + 0x2d, 0x49, 0xa5, 0x81, 0x51, 0x53, 0x9f, 0xf9, 0xd9, 0x26, 0xb7, 0x07, + 0xed, 0x57, 0x7e, 0xe0, 0x55, 0x15, 0x9a, 0xba, 0x82, 0xfa, 0x15, 0x1e, + 0x57, 0x62, 0x85, 0x28, 0x3e, 0x95, 0xdb, 0x93, 0x96, 0xef, 0x5f, 0x00, + 0xd4, 0x8a, 0x7d, 0xa7, 0xbf, 0x17, 0x40, 0x40, 0xaf, 0x92, 0xf4, 0xf2, + 0x9f, 0xeb, 0x55, 0x42, 0x25, 0x07, 0x84, 0x9c, 0x40, 0x90, 0xb3, 0x9e, + 0x29, 0x59, 0xb6, 0xf2, 0x16, 0xbb, 0x72, 0x32, 0x81, 0x78, 0xae, 0x8b, + 0xc2, 0x18, 0x14, 0x81, 0x41, 0x9a, 0x7d, 0x6a, 0xea, 0x68, 0x20, 0xa0, + 0x3f, 0xef, 0x1f, 0x28, 0x98, 0xb6, 0x66, 0xd7, 0xe8, 0x95, 0x9b, 0x8e, + 0x5f, 0xfb, 0xce, 0x59, 0xd8, 0x5b, 0xc8, 0x15, 0x82, 0xd2, 0x1a, 0xb2, + 0x2e, 0xce, 0xfe, 0xaa, 0x1d, 0x4c, 0x2b, 0xfc, 0x0c, 0x63, 0x06, 0x1e, + 0xce, 0x72, 0xae, 0xb6, 0xae, 0xed, 0x2f, 0x3a, 0xc9, 0x60, 0xf3, 0x25, + 0x7c, 0x99, 0x3d, 0xaf, 0xd6, 0xc7, 0x40, 0xe3, 0xed, 0x8f, 0x9d, 0x7a, + 0x76, 0x02, 0x5a, 0x24, 0xb6, 0xee, 0x1a, 0x09, 0x70, 0x19, 0x06, 0x1f, + 0x8f, 0x18, 0x28, 0x3c, 0xe6, 0xe8, 0x14, 0xad, 0x97, 0x9e, 0x2a, 0x49, + 0x62, 0x44, 0x53, 0xce, 0xca, 0xff, 0x4f, 0xa2, 0xce, 0x69, 0xaf, 0xd3, + 0x9a, 0x2b, 0x00, 0xc9, 0xac, 0xdf, 0x63, 0xb9, 0x75, 0x87, 0xb2, 0xfc, + 0x47, 0x17, 0x3d, 0xb2, 0x31, 0xfe, 0x48, 0xbc, 0x2d, 0xf5, 0xcc, 0xb2, + 0xa6, 0x76, 0x56, 0xbf, 0xae, 0x84, 0xd9, 0xdc, 0xf6, 0x3d, 0x8d, 0x13, + 0x84, 0xc3, 0x7f, 0xde, 0x80, 0x00, 0x95, 0xea, 0x5e, 0xf9, 0xc8, 0x72, + 0x59, 0xa8, 0x85, 0x11, 0x68, 0x50, 0x4b, 0x3f, 0x4f, 0x85, 0x9b, 0x54, + 0xe3, 0xbb, 0xb9, 0xd7, 0xf5, 0xf6, 0x1b, 0x63, 0xbf, 0xe4, 0x5f, 0x0b, + 0xdc, 0xba, 0xfa, 0x40, 0x3e, 0x95, 0x7e, 0x42, 0x03, 0xfc, 0x54, 0xf6, + 0xd8, 0x5b, 0x18, 0x09, 0xf2, 0xb2, 0x40, 0x73, 0x32, 0x26, 0x50, 0x5c, + 0xca, 0x8b, 0x07, 0x79, 0xd6, 0x4b, 0x06, 0x4b, 0xe3, 0xfc, 0x20, 0xbd, + 0x1e, 0xb1, 0xc5, 0x1e, 0x81, 0x04, 0xca, 0xe1, 0x16, 0xa2, 0xfd, 0xaa, + 0x4c, 0x09, 0x3f, 0xf3, 0xe7, 0x37, 0x5e, 0x1c, 0xd3, 0x7d, 0x41, 0xe4, + 0x8e, 0x8d, 0xf5, 0xc4, 0xc5, 0x9a, 0x85, 0x24, 0x0c, 0xde, 0x63, 0x8c, + 0xbe, 0x00, 0xf4, 0x20, 0x89, 0xda, 0x5c, 0x5d, 0x25, 0x1a, 0xe1, 0xa7, + 0xb1, 0xcc, 0xad, 0x83, 0x3c, 0x3e, 0xce, 0x39, 0xc6, 0xb8, 0xa1, 0xca, + 0x49, 0xcb, 0x98, 0xc1, 0x54, 0xda, 0x06, 0xb0, 0xf8, 0x72, 0x57, 0x18, + 0xda, 0x1d, 0x0b, 0xa4, 0xa2, 0x8f, 0x39, 0xf4, 0xc9, 0x50, 0xf3, 0xea, + 0x5c, 0x94, 0x06, 0x10, 0x5f, 0xf5, 0x93, 0xeb, 0x82, 0x7a, 0xf4, 0x0b, + 0x30, 0xdf, 0x72, 0xcd, 0x49, 0x88, 0xd4, 0xd3, 0x28, 0x8a, 0x8c, 0x29, + 0x96, 0xf2, 0xc8, 0x81, 0x7f, 0x2c, 0x54, 0x67, 0x33, 0xed, 0x4b, 0x59, + 0xad, 0x05, 0xd8, 0xad, 0xc1, 0x59, 0x63, 0xf4, 0x24, 0xcb, 0x18, 0x7c, + 0x69, 0xe9, 0x78, 0x78, 0xd2, 0x48, 0x1c, 0x06, 0x58, 0xd2, 0xf7, 0x55, + 0xaa, 0x36, 0x7a, 0x94, 0x05, 0x8c, 0x8a, 0xdd, 0xb5, 0x85, 0xd6, 0x18, + 0x56, 0x50, 0x9c, 0x0f, 0xea, 0x62, 0x3b, 0x8f, 0x3c, 0xb8, 0x1b, 0x54, + 0x4a, 0x73, 0xaa, 0x0c, 0x48, 0xf7, 0x24, 0xfd, 0x76, 0xb4, 0xe4, 0x90, + 0xa3, 0x9a, 0x7f, 0x34, 0x62, 0x53, 0x7b, 0x2d, 0x59, 0x57, 0x6e, 0x60, + 0x31, 0xba, 0xea, 0xfc, 0x44, 0xf2, 0xb7, 0x8f, 0x06, 0x38, 0x5e, 0xe8, + 0x56, 0xc0, 0xd8, 0xf9, 0x1c, 0x03, 0x9c, 0xcf, 0xc7, 0x57, 0x34, 0x71, + 0x70, 0x2e, 0xa6, 0x96, 0x49, 0x35, 0x5e, 0x2d, 0x12, 0x5b, 0xcc, 0xbd, + 0x49, 0x85, 0x5d, 0xfe, 0x90, 0xbf, 0x91, 0x80, 0x6f, 0xe3, 0x88, 0x2c, + 0x02, 0x8d, 0x62, 0x6d, 0x38, 0x74, 0x72, 0x17, 0xb3, 0x29, 0x33, 0xf1, + 0xb3, 0x67, 0xb7, 0x05, 0xff, 0x1c, 0xda, 0xb6, 0x07, 0xbe, 0x99, 0x4c, + 0x3d, 0x30, 0x69, 0x3a, 0xb3, 0xdc, 0xb1, 0x38, 0x24, 0xa1, 0x24, 0x4a, + 0x3b, 0x70, 0x71, 0x66, 0x97, 0x5c, 0xf1, 0xf5, 0x2d, 0xd6, 0x4e, 0x68, + 0x22, 0x71, 0x7d, 0x59, 0x63, 0xb9, 0xaa, 0xd7, 0x28, 0x76, 0x24, 0xe1, + 0xfd, 0xda, 0x6a, 0x3c, 0x8c, 0xf5, 0x40, 0x43, 0x27, 0xe0, 0xa1, 0x83, + 0x07, 0x32, 0xe6, 0x17, 0x65, 0xc0, 0x5f, 0xb9, 0xb9, 0xa6, 0x22, 0x03, + 0x69, 0x4b, 0xb0, 0x15, 0x5e, 0x26, 0xb9, 0x46, 0x4a, 0x8c, 0xc8, 0x2b, + 0x7b, 0x54, 0x83, 0x35, 0x84, 0x00, 0x07, 0xc3, 0x55, 0x8c, 0xbb, 0x3c, + 0x57, 0x4d, 0xe6, 0x14, 0x9d, 0x84, 0xfc, 0x13, 0x97, 0x44, 0x80, 0x5d, + 0xac, 0x10, 0xd1, 0x19, 0x29, 0x7b, 0xb3, 0x3f, 0xbe, 0xc5, 0x87, 0x43, + 0x21, 0x72, 0x7e, 0xc5, 0x79, 0x42, 0x38, 0xd1, 0x9f, 0x6b, 0x94, 0xd4, + 0x66, 0x77, 0x86, 0xb1, 0x41, 0x0d, 0x88, 0x09, 0xd5, 0xdf, 0x3d, 0xb3, + 0xc5, 0x45, 0x5c, 0xea, 0xe8, 0xac, 0x48, 0xef, 0xb4, 0x66, 0x8a, 0x7c, + 0xdb, 0x71, 0x9f, 0x9a, 0x96, 0xbb, 0x0b, 0xd2, 0xf8, 0xf4, 0xe7, 0xb8, + 0xe0, 0x07, 0xf6, 0x04, 0x25, 0xd9, 0xc5, 0x4f, 0x61, 0xb4, 0x22, 0xbd, + 0xfb, 0x8f, 0x6d, 0xb4, 0x7a, 0xfd, 0xfd, 0xfb, 0x83, 0xcc, 0x8c, 0x47, + 0x71, 0xe5, 0xbc, 0x35, 0x20, 0x53, 0x32, 0x73, 0x0c, 0x88, 0x2a, 0xcc, + 0x63, 0xae, 0xab, 0x6c, 0xb8, 0xb4, 0x7e, 0x60, 0x8c, 0xf4, 0x9d, 0xbd, + 0x7f, 0xf7, 0xc8, 0x07, 0x9f, 0xd7, 0xfc, 0x17, 0x0f, 0x06, 0x1f, 0x72, + 0xf8, 0x9b, 0x58, 0x76, 0x13, 0xf6, 0x49, 0x89, 0xeb, 0x31, 0x81, 0x5e, + 0x71, 0x73, 0x83, 0x74, 0x34, 0xa4, 0xe8, 0x36, 0xa4, 0x22, 0xe7, 0x24, + 0xde, 0xa7, 0x4a, 0x99, 0x6f, 0x7f, 0xf7, 0x80, 0xfa, 0x98, 0x6b, 0xdd, + 0xa6, 0x06, 0x6a, 0xc7, 0x5d, 0x4c, 0x91, 0x53, 0xcf, 0x2c, 0x35, 0xd8, + 0xb6, 0xc0, 0xfd, 0x4b, 0x92, 0xdc, 0x4b, 0xeb, 0x1c, 0x72, 0x3a, 0x14, + 0xdc, 0xaf, 0x89, 0xc7, 0x05, 0x02, 0xb1, 0xb9, 0x51, 0x25, 0xbc, 0x49, + 0x77, 0x8f, 0x6a, 0x22, 0x2b, 0x7a, 0xa2, 0x19, 0x6d, 0x3b, 0xcd, 0x18, + 0x2e, 0xf4, 0x5a, 0xbe, 0x53, 0x0a, 0xaa, 0xbb, 0x61, 0x30, 0x03, 0x55, + 0x4a, 0x94, 0xa0, 0x7e, 0xb4, 0xc5, 0x57, 0x84, 0x17, 0x7b, 0x5d, 0x61, + 0x46, 0x58, 0x05, 0x71, 0x1f, 0x2e, 0x3e, 0x05, 0xf9, 0x35, 0xab, 0x34, + 0xce, 0x0a, 0xc1, 0xd2, 0xf4, 0x97, 0x9d, 0xbf, 0x9b, 0xe2, 0xc6, 0xfb, + 0x65, 0x1b, 0x6e, 0x64, 0x2f, 0xd5, 0xd7, 0x46, 0x18, 0x87, 0xdc, 0xf0, + 0x7d, 0xc3, 0x7d, 0xe7, 0x3a, 0x16, 0xde, 0x5b, 0x6c, 0x34, 0x3c, 0x9c, + 0x7e, 0xdd, 0xa8, 0xae, 0xd0, 0x28, 0xde, 0xe6, 0x7c, 0x4d, 0x9e, 0xec, + 0x8b, 0x2f, 0xfe, 0xd8, 0x59, 0xfd, 0x47, 0x18, 0xef, 0x63, 0xd8, 0x36, + 0xe8, 0xe3, 0xe3, 0xac, 0xce, 0x53, 0x85, 0x5c, 0x3d, 0x3a, 0xed, 0xb1, + 0x35, 0xa0, 0xf1, 0xca, 0x29, 0xd2, 0x64, 0x70, 0x47, 0x4b, 0xb0, 0x2f, + 0x0b, 0xcb, 0xda, 0x45, 0x03, 0xd7, 0xd3, 0xf0, 0xd8, 0xc6, 0x39, 0xb1, + 0x6b, 0xb0, 0xa5, 0x7c, 0xcb, 0x42, 0xd4, 0xc2, 0xc2, 0xa5, 0xff, 0x7e, + 0x4a, 0x13, 0xff, 0x0a, 0x06, 0x90, 0x6f, 0x5b, 0xaf, 0xc8, 0xd0, 0x61, + 0x24, 0x7f, 0xfe, 0x5f, 0x07, 0x56, 0x84, 0x98, 0x89, 0x1b, 0xaa, 0xef, + 0x35, 0x89, 0xe0, 0x62, 0x3d, 0x89, 0xb0, 0x90, 0xda, 0xa5, 0x3f, 0x50, + 0xa6, 0xf4, 0x34, 0x0d, 0x1f, 0xfc, 0x73, 0xa7, 0x02, 0x4a, 0xc2, 0x0b, + 0xe7, 0x96, 0x10, 0x09, 0x26, 0xca, 0x9b, 0xa4, 0x0f, 0x76, 0x0f, 0xed, + 0x01, 0x09, 0x04, 0x89, 0x46, 0x3f, 0x4a, 0x1d, 0xaa, 0xd2, 0xc6, 0x91, + 0xc6, 0x8e, 0x7d, 0xf3, 0x0f, 0x59, 0x37, 0x0f, 0x67, 0xae, 0x73, 0x27, + 0x64, 0x03, 0x0d, 0xad, 0xc8, 0xf5, 0x11, 0x93, 0x78, 0x0f, 0xa0, 0x18, + 0x3c, 0x11, 0x8f, 0x45, 0x77, 0xc3, 0xe5, 0xd4, 0xbe, 0x7d, 0xe6, 0xee, + 0xe2, 0x2c, 0x3a, 0x70, 0x8a, 0x0f, 0x97, 0x16, 0x9d, 0x02, 0xc0, 0x19, + 0xa6, 0x35, 0xb8, 0xc1, 0x64, 0x0c, 0x29, 0x7f, 0x74, 0x28, 0xab, 0x57, + 0xd7, 0xb4, 0x1f, 0x39, 0x70, 0x82, 0x9e, 0xef, 0xc2, 0x18, 0xfe, 0x4b, + 0x77, 0xa5, 0xbc, 0x55, 0x0f, 0x36, 0xbe, 0x61, 0x58, 0xb8, 0x2e, 0x42, + 0x75, 0x6e, 0x58, 0x8f, 0xf2, 0x8a, 0xbd, 0x88, 0x31, 0xc0, 0xc9, 0xc8, + 0x36, 0x68, 0xb6, 0x1f, 0xd0, 0x56, 0xd7, 0x29, 0xb7, 0xa2, 0x47, 0xd6, + 0x42, 0x18, 0xc3, 0x09, 0x48, 0xb6, 0x95, 0x8e, 0x5d, 0x09, 0x60, 0xbe, + 0x81, 0x03, 0xe4, 0x43, 0x6e, 0x93, 0xf2, 0x7b, 0xae, 0x7a, 0xd2, 0xcd, + 0x9d, 0x19, 0x73, 0x09, 0x1d, 0xe2, 0xff, 0x55, 0x9c, 0x0e, 0x75, 0xe7, + 0xe1, 0x64, 0x99, 0x90, 0x72, 0x09, 0xd3, 0xad, 0x7c, 0x1b, 0x03, 0xe2, + 0x4f, 0xc2, 0xb0, 0x58, 0x48, 0xcc, 0x44, 0x80, 0x75, 0xe3, 0xba, 0x9f, + 0x3b, 0x81, 0x76, 0xa5, 0x3f, 0xe9, 0xde, 0xe9, 0x99, 0xf0, 0x78, 0x09, + 0x16, 0xce, 0x10, 0x10, 0xc8, 0x01, 0x24, 0x8c, 0xe0, 0x80, 0x89, 0x06, + 0xbd, 0x98, 0xa0, 0x52, 0x10, 0xbe, 0x33, 0x91, 0x54, 0xa4, 0x8f, 0x8e, + 0xf9, 0x5e, 0x51, 0x8c, 0x07, 0xd6, 0x6b, 0x1c, 0x04, 0x9c, 0x1e, 0xbb, + 0x78, 0x39, 0xfb, 0xf8, 0xbc, 0x7a, 0xc6, 0x4a, 0x8e, 0xd1, 0xd8, 0x03, + 0xe4, 0x97, 0x9c, 0x0b, 0x8c, 0x29, 0xeb, 0xe4, 0x11, 0x62, 0xa4, 0xcf, + 0x0a, 0x3a, 0x47, 0x5b, 0x22, 0x31, 0xef, 0xad, 0x3b, 0x41, 0xc8, 0x95, + 0xbe, 0xab, 0x5e, 0x65, 0x32, 0x1d, 0xc7, 0x2e, 0xaa, 0xaa, 0x97, 0xa8, + 0x9b, 0x86, 0xd7, 0x18, 0x06, 0x23, 0xf8, 0xe8, 0x3d, 0x43, 0x58, 0x4b, + 0x9d, 0xcf, 0x60, 0x8b, 0xd6, 0xdd, 0xc2, 0xa5, 0x7a, 0x52, 0xcf, 0x27, + 0xca, 0x36, 0x05, 0xc5, 0xee, 0x16, 0x87, 0xcf, 0xde, 0x18, 0x50, 0xbc, + 0x0f, 0x7b, 0x78, 0x60, 0xc8, 0xf8, 0xaa, 0x19, 0x20, 0x0b, 0xa3, 0x21, + 0xdd, 0x69, 0x23, 0x1d, 0xf0, 0x64, 0x98, 0x9d, 0x0a, 0x11, 0xcb, 0x45, + 0x12, 0x3a, 0x7a, 0x92, 0x87, 0xdf, 0x8e, 0xdd, 0x16, 0xbf, 0xeb, 0xe1, + 0x04, 0x14, 0x6f, 0x1b, 0x2a, 0xa1, 0x3f, 0xb4, 0x4d, 0x5a, 0x3f, 0xac, + 0x4f, 0x39, 0x3e, 0x88, 0x33, 0x7c, 0xaa, 0x64, 0xdc, 0xbc, 0xd7, 0x72, + 0x98, 0x0e, 0x2e, 0xfb, 0x2c, 0x75, 0x46, 0x67, 0x3e, 0x28, 0xad, 0xe0, + 0x80, 0x50, 0x15, 0xf5, 0x6e, 0x43, 0x1e, 0x8a, 0xea, 0x45, 0xff, 0x16, + 0xf6, 0xc4, 0x43, 0x9b, 0xd8, 0xbc, 0x09, 0x2d, 0xc9, 0x0e, 0x75, 0xd4, + 0x93, 0x17, 0xec, 0x5b, 0x44, 0x03, 0xcf, 0xce, 0x8b, 0xc5, 0xfa, 0x99, + 0x52, 0x0f, 0x2e, 0x2b, 0xf7, 0xb5, 0x08, 0x45, 0xd3, 0x22, 0x23, 0x1e, + 0xb8, 0x4a, 0xa8, 0x48, 0xab, 0xe8, 0xef, 0x47, 0xed, 0x52, 0x93, 0x62, + 0x0f, 0x7b, 0x88, 0x03, 0xd3, 0xdb, 0x7c, 0x58, 0xa1, 0x83, 0xce, 0x5e, + 0xf7, 0x4b, 0x47, 0x20, 0xc1, 0xae, 0xc7, 0xe6, 0x79, 0x0d, 0xd2, 0x34, + 0xa8, 0x1b, 0x83, 0x0a, 0x3b, 0x84, 0xdf, 0x7d, 0x6e, 0x88, 0x8b, 0xa1, + 0xa2, 0xd3, 0x6e, 0x4d, 0x74, 0x3e, 0x75, 0x42, 0x84, 0xfd, 0x0a, 0x60, + 0x9d, 0x2b, 0xee, 0x06, 0x83, 0x04, 0xf8, 0x00, 0xac, 0x2f, 0x83, 0x55, + 0x08, 0x3e, 0xc3, 0x6f, 0xb0, 0x3a, 0xd5, 0x24, 0x6a, 0xb4, 0x79, 0xa7, + 0x0f, 0x41, 0x98, 0x13, 0x1b, 0x96, 0xa8, 0x88, 0x27, 0xf2, 0x60, 0x87, + 0xeb, 0x81, 0x51, 0xbe, 0x84, 0x31, 0x21, 0xd1, 0x6a, 0xfb, 0xde, 0x2a, + 0x09, 0xcc, 0x7b, 0x3e, 0x93, 0xc6, 0x46, 0x1f, 0x61, 0xde, 0x07, 0x38, + 0x3b, 0x99, 0x15, 0x1d, 0x44, 0xc8, 0x9a, 0x1f, 0x8e, 0xa0, 0x22, 0x2d, + 0xc9, 0x20, 0x58, 0x89, 0x4e, 0xfc, 0xee, 0x19, 0x72, 0x7e, 0x03, 0xcd, + 0x94, 0x83, 0xe7, 0xfe, 0x10, 0xd9, 0xd4, 0xd3, 0x21, 0x9f, 0x9c, 0x26, + 0xcc, 0xf6, 0x8b, 0xd5, 0x2b, 0x92, 0x25, 0x90, 0x58, 0xab, 0x6f, 0xe6, + 0x42, 0xb6, 0x90, 0x1a, 0xab, 0x12, 0xda, 0xab, 0x60, 0xc2, 0xa3, 0x57, + 0xf2, 0xf7, 0x36, 0xfc, 0xcb, 0x22, 0x08, 0x83, 0xef, 0x88, 0x72, 0xec, + 0x81, 0x6a, 0xdb, 0xe9, 0xa0, 0x72, 0x89, 0x93, 0x0f, 0xcd, 0xab, 0x15, + 0xcc, 0x4c, 0x85, 0xe9, 0x26, 0x51, 0x4b, 0x1b, 0x89, 0x1c, 0xc3, 0xca, + 0xd5, 0x95, 0x6a, 0xab, 0x50, 0x37, 0x3c, 0x0a, 0x22, 0x8a, 0x69, 0x8e, + 0xd4, 0x8b, 0x0d, 0x27, 0x52, 0x05, 0x77, 0x28, 0x9b, 0x53, 0xe4, 0x07, + 0xdd, 0x00, 0x25, 0xea, 0x82, 0x2a, 0xcb, 0xc9, 0x80, 0x5f, 0xa2, 0x40, + 0x7d, 0x57, 0x93, 0x62, 0x1e, 0x54, 0xa7, 0xd3, 0x64, 0xea, 0x7b, 0xa9, + 0x46, 0xfc, 0x49, 0x07, 0xdf, 0x84, 0xd9, 0x0b, 0x58, 0x11, 0xeb, 0x35, + 0xf3, 0x06, 0xa0, 0x9e, 0xf9, 0xb2, 0xf3, 0x38, 0xbd, 0xca, 0xf6, 0xee, + 0xdc, 0x1f, 0x73, 0xb1, 0xa8, 0x60, 0x31, 0x7a, 0x94, 0x5c, 0xa3, 0x41, + 0x07, 0xa5, 0x4b, 0x41, 0xef, 0xd0, 0xa9, 0x47, 0x0a, 0x9b, 0x45, 0x00, + 0xb5, 0xa2, 0x6d, 0xf5, 0xca, 0xc4, 0x5c, 0x65, 0x47, 0xcd, 0x42, 0xf5, + 0xe3, 0x08, 0x74, 0x60, 0x54, 0xea, 0xcc, 0xf6, 0x32, 0xe9, 0x8e, 0xc2, + 0x24, 0x4d, 0xe0, 0x13, 0x7a, 0xe0, 0xeb, 0x3e, 0xaf, 0x78, 0xbb, 0x96, + 0xb8, 0x5d, 0x07, 0x07, 0x7b, 0x75, 0x95, 0x76, 0x61, 0xe2, 0x1a, 0xcb, + 0x34, 0x88, 0xf5, 0xae, 0x08, 0x75, 0x50, 0xa5, 0x2a, 0x48, 0xcd, 0x17, + 0xa0, 0x60, 0x95, 0xd1, 0x2a, 0xf1, 0x67, 0x4b, 0x46, 0x36, 0x9d, 0x4d, + 0x8c, 0x44, 0x47, 0x47, 0x46, 0x3d, 0x34, 0xc6, 0x9e, 0xed, 0x82, 0x8e, + 0xd2, 0x19, 0xa2, 0xb8, 0xb1, 0x70, 0x17, 0x22, 0xf7, 0xb3, 0x64, 0xc5, + 0xa8, 0xac, 0x78, 0xb4, 0x03, 0xb8, 0x96, 0x48, 0x6c, 0x69, 0xfd, 0xf5, + 0x51, 0xb0, 0xb6, 0xdd, 0x1b, 0x90, 0x7b, 0x87, 0xb6, 0xd2, 0x96, 0x03, + 0x2e, 0xe6, 0x44, 0x2a, 0x90, 0x65, 0x84, 0x6d, 0x93, 0x17, 0x5f, 0x62, + 0xe6, 0x0f, 0xbd, 0x79, 0xc1, 0xce, 0xc1, 0xd9, 0xec, 0xe0, 0x45, 0x6f, + 0x57, 0x82, 0xf6, 0x76, 0x29, 0x51, 0xfc, 0x23, 0xa7, 0xfd, 0xdd, 0x31, + 0xd2, 0xbd, 0xfb, 0x64, 0xce, 0x70, 0x79, 0x53, 0x1d, 0x07, 0x0b, 0xd2, + 0x29, 0x16, 0xea, 0xba, 0xcb, 0xb2, 0x13, 0xe1, 0xd0, 0x80, 0xf3, 0xc8, + 0x54, 0xf1, 0xa6, 0xed, 0x20, 0x7a, 0x6e, 0x6b, 0x93, 0xba, 0x6f, 0xda, + 0xba, 0xf4, 0x9c, 0xb4, 0x85, 0x75, 0xe4, 0xff, 0xb6, 0xf6, 0xb4, 0x69, + 0xf0, 0xa9, 0xac, 0x9b, 0x7d, 0xab, 0x67, 0xec, 0xb5, 0x2b, 0x4e, 0xdb, + 0xba, 0x69, 0x61, 0x06, 0x05, 0x51, 0xed, 0xbe, 0xdc, 0x2e, 0xb6, 0x17, + 0xee, 0xc0, 0xb0, 0x5d, 0x0a, 0x1f, 0x58, 0x67, 0x40, 0xfa, 0x13, 0x49, + 0x71, 0x37, 0x0a, 0x2d, 0xd5, 0x55, 0xd6, 0x14, 0xcc, 0x3c, 0x00, 0x6a, + 0xa9, 0xea, 0x38, 0x6c, 0xfd, 0xee, 0x8b, 0x19, 0xcb, 0xd0, 0x3e, 0x5b, + 0x03, 0x3f, 0xec, 0x6d, 0x0e, 0xa4, 0x02, 0xae, 0x65, 0x23, 0x63, 0x5b, + 0x0f, 0x9c, 0xdf, 0x3a, 0x80, 0x83, 0x24, 0xd1, 0xc4, 0x29, 0x60, 0x73, + 0xbc, 0xd1, 0x70, 0x2e, 0xc4, 0x64, 0xaf, 0x04, 0x45, 0xad, 0xfc, 0xf7, + 0x98, 0x76, 0x31, 0x93, 0x53, 0x08, 0x28, 0x18, 0x88, 0xe7, 0x60, 0x68, + 0x95, 0x78, 0xee, 0xe0, 0x2b, 0x01, 0xf1, 0x2d, 0x64, 0x19, 0xcf, 0xe2, + 0xd2, 0xb3, 0xdf, 0x40, 0xca, 0xe7, 0x7f, 0x96, 0xa3, 0xd3, 0xe3, 0xad, + 0x86, 0x1d, 0x28, 0x7d, 0x13, 0x73, 0xd9, 0x3a, 0xbe, 0x60, 0x20, 0x44, + 0xcf, 0x91, 0x4c, 0xad, 0x64, 0xac, 0xd1, 0x83, 0xb5, 0xb4, 0x1f, 0x78, + 0xd7, 0xd2, 0xdf, 0xc4, 0x87, 0x95, 0xcc, 0xff, 0xd7, 0x90, 0x06, 0x51, + 0xe4, 0xfd, 0x7f, 0xbf, 0x2e, 0xb5, 0x43, 0xc4, 0x7e, 0xc6, 0xbb, 0xc9, + 0x5e, 0x29, 0xdd, 0x51, 0x89, 0xd6, 0xe3, 0xcd, 0x73, 0x64, 0xc3, 0xd7, + 0xe0, 0x8e, 0xb2, 0xcd, 0xbd, 0x8c, 0xe9, 0x1a, 0xbd, 0xc4, 0x44, 0x99, + 0xa4, 0x6f, 0xc8, 0x6c, 0x1b, 0x89, 0x55, 0xee, 0xe3, 0xe6, 0x27, 0x3c, + 0xe2, 0xd7, 0xe2, 0x5d, 0x30, 0x3b, 0xef, 0x52, 0x7b, 0xbb, 0x9f, 0x91, + 0xea, 0x9c, 0x1c, 0xb9, 0x4b, 0x97, 0x00, 0xeb, 0x72, 0x90, 0x44, 0xd8, + 0xc6, 0x25, 0x11, 0x89, 0x81, 0xae, 0x8d, 0x74, 0xf3, 0x70, 0x16, 0x15, + 0x94, 0xf8, 0x00, 0x59, 0xc6, 0xbd, 0xed, 0xa7, 0xde, 0x1b, 0xeb, 0x4d, + 0x9a, 0x1b, 0xcd, 0xe5, 0x7c, 0x5a, 0xaa, 0xd0, 0x98, 0x28, 0xe0, 0x64, + 0x34, 0x79, 0xd9, 0x99, 0xe5, 0x1b, 0x9c, 0x70, 0xb8, 0xdd, 0x86, 0xa6, + 0xb6, 0x9a, 0xd1, 0xd2, 0x98, 0xf6, 0x18, 0x0f, 0xd5, 0x47, 0xb6, 0x36, + 0xb1, 0x27, 0xf6, 0xc7, 0x51, 0xe7, 0xa8, 0xf9, 0x39, 0x31, 0xf2, 0x6f, + 0x07, 0x3a, 0x77, 0xdb, 0x63, 0xe3, 0xca, 0x71, 0x74, 0x24, 0x57, 0x0b, + 0x44, 0x43, 0xd2, 0xec, 0x29, 0xbe, 0xae, 0x4d, 0x06, 0x70, 0xc5, 0xf8, + 0xe9, 0x90, 0x72, 0x32, 0xbc, 0x50, 0x3e, 0x70, 0xba, 0xbb, 0xb4, 0x8d, + 0xb0, 0x76, 0xfe, 0x41, 0x83, 0x6f, 0xa4, 0xef, 0xbf, 0x5c, 0x42, 0xb1, + 0x98, 0xc7, 0x7d, 0x41, 0x9d, 0x34, 0xea, 0x29, 0x38, 0xbb, 0xc0, 0xf7, + 0xba, 0x05, 0x0c, 0xc4, 0xd1, 0x5f, 0x6d, 0xb4, 0x96, 0x17, 0x4e, 0xcc, + 0x1e, 0x8c, 0x47, 0x10, 0x05, 0x4d, 0xd3, 0xd1, 0x41, 0x6b, 0xa5, 0xed, + 0xbd, 0x3d, 0xc1, 0x6d, 0xcc, 0x8f, 0x33, 0xd1, 0x59, 0xe5, 0x1e, 0x49, + 0xcd, 0xe9, 0x60, 0x86, 0x32, 0x49, 0xce, 0xc6, 0xaa, 0x37, 0x27, 0x97, + 0x68, 0x3a, 0xc3, 0xaf, 0xd1, 0x8f, 0xc3, 0x75, 0xa4, 0x26, 0x62, 0x7f, + 0xbb, 0xd4, 0xd5, 0x23, 0xa1, 0x3f, 0x13, 0x20, 0x66, 0x6e, 0x23, 0x30, + 0xdb, 0xa8, 0x55, 0x21, 0xf8, 0x74, 0x69, 0xf2, 0xbd, 0x79, 0x4a, 0xe8, + 0xc9, 0xc7, 0x8f, 0x34, 0xa2, 0x3d, 0xf4, 0xa7, 0x5a, 0x56, 0x50, 0xde, + 0xf4, 0xe9, 0x5c, 0x07, 0x37, 0xaf, 0xd9, 0x2c, 0xe6, 0x4f, 0xba, 0x63, + 0x4f, 0x48, 0xe1, 0x8d, 0x1e, 0x5d, 0x4b, 0xba, 0x1d, 0x2a, 0xc0, 0x45, + 0xf6, 0x7b, 0x83, 0x3e, 0x49, 0x83, 0x85, 0xd1, 0x6b, 0x39, 0xed, 0x9d, + 0x05, 0x4d, 0x01, 0x88, 0x5a, 0x2e, 0xb0, 0x30, 0x5c, 0x72, 0x79, 0xfc, + 0x9e, 0x51, 0xa1, 0x5d, 0xbf, 0x2f, 0xc0, 0xa9, 0xc2, 0x55, 0x37, 0x53, + 0x27, 0xf2, 0xa8, 0xf9, 0x7d, 0xca, 0x55, 0x82, 0x81, 0x04, 0x25, 0xfd, + 0x98, 0xe9, 0x0e, 0xa0, 0x36, 0x88, 0xb3, 0x5c, 0x3b, 0xa6, 0xf9, 0x5c, + 0x49, 0x60, 0x19, 0x20, 0x72, 0xb2, 0x51, 0xa4, 0xa4, 0x7b, 0x1b, 0xbd, + 0x98, 0x10, 0xcd, 0x82, 0xa9, 0x2e, 0x5d, 0xe2, 0xcc, 0xc2, 0xec, 0x19, + 0x29, 0x4b, 0x4c, 0x66, 0x2b, 0x62, 0x99, 0xb2, 0x38, 0x42, 0xd6, 0x3a, + 0x82, 0xd3, 0x9d, 0xc2, 0x12, 0xd9, 0xc4, 0x8f, 0x77, 0xe6, 0xad, 0x06, + 0x83, 0x25, 0xa0, 0x24, 0xcc, 0x91, 0x30, 0xb1, 0xa5, 0x72, 0x1c, 0x82, + 0x21, 0x0b, 0x3e, 0xe6, 0x0b, 0x0c, 0xae, 0xf2, 0xf7, 0x75, 0x75, 0x47, + 0x9b, 0x70, 0x57, 0xf6, 0xfd, 0x78, 0x77, 0x12, 0x0a, 0x7a, 0xd9, 0xe5, + 0x04, 0x0a, 0xda, 0xc5, 0xdd, 0xfb, 0xd6, 0x86, 0x88, 0xed, 0xfc, 0xf3, + 0xf4, 0x1d, 0xb3, 0xd6, 0x85, 0x5c, 0xa0, 0xc6, 0x9a, 0xec, 0xfa, 0xa5, + 0xdd, 0x83, 0x08, 0x22, 0xcf, 0x00, 0x36, 0xa9, 0x97, 0xf8, 0xf0, 0x6e, + 0xd3, 0x6b, 0xfd, 0x2c, 0x71, 0xe1, 0x93, 0x8c, 0x3d, 0x5d, 0xd4, 0x7c, + 0x6b, 0x75, 0x83, 0x1a, 0x7f, 0x22, 0x20, 0x85, 0x59, 0x67, 0x20, 0xdc, + 0xcf, 0x07, 0x41, 0xbd, 0x8b, 0xeb, 0xe1, 0x89, 0x6a, 0x2b, 0xb0, 0xfe, + 0x2d, 0x2b, 0xa0, 0x53, 0x72, 0x3d, 0x84, 0xbf, 0xa5, 0x0b, 0x2d, 0x8f, + 0xe9, 0x71, 0x97, 0x8b, 0xb3, 0x9c, 0x79, 0x45, 0x1a, 0xb1, 0x55, 0xc3, + 0xf7, 0xb7, 0x22, 0x05, 0x2f, 0x82, 0x0f, 0x4a, 0x11, 0xda, 0xd4, 0x4a, + 0xfa, 0xde, 0xa2, 0xa9, 0xda, 0xa9, 0x63, 0xde, 0xba, 0x0f, 0x6d, 0x23, + 0x2b, 0x3f, 0x7f, 0x1f, 0x6f, 0x26, 0xe8, 0x55, 0x22, 0xe0, 0x4c, 0x7f, + 0x35, 0x3e, 0x01, 0x73, 0xf7, 0x1c, 0xca, 0x03, 0x40, 0xe5, 0x4e, 0x5f, + 0x31, 0x4e, 0xff, 0xa1, 0x1c, 0x41, 0xa9, 0xc3, 0x9a, 0xc2, 0x3c, 0x90, + 0xd6, 0xa8, 0x53, 0x83, 0x37, 0x65, 0x71, 0x81, 0xca, 0x3f, 0x1f, 0xc3, + 0x3c, 0x8a, 0x6a, 0xc2, 0xd9, 0xf8, 0x20, 0x7f, 0x43, 0xf1, 0x35, 0x43, + 0xf7, 0x27, 0x3b, 0xb7, 0xbc, 0xa0, 0x47, 0x28, 0x0a, 0xa5, 0xdb, 0x1c, + 0xb7, 0x3d, 0x7d, 0x17, 0x11, 0x44, 0x63, 0x27, 0x88, 0xd2, 0xda, 0xbc, + 0xee, 0xbb, 0x1b, 0xb1, 0x30, 0xfe, 0xba, 0xa6, 0x7f, 0x78, 0xd8, 0x1e, + 0xe7, 0xfa, 0xfa, 0x5c, 0xa6, 0x27, 0x1c, 0x9a, 0xa7, 0x4f, 0x40, 0xff, + 0xa3, 0xf4, 0x92, 0x97, 0xc1, 0xde, 0x0a, 0x85, 0x65, 0x08, 0x89, 0xc0, + 0xd8, 0xee, 0xd2, 0x0d, 0x2c, 0x22, 0xcc, 0xa2, 0x10, 0xc2, 0x62, 0x3a, + 0x11, 0xcd, 0x19, 0x3e, 0x7f, 0xf5, 0x68, 0xa1, 0xfa, 0x24, 0x4a, 0x30, + 0x21, 0x77, 0x39, 0x59, 0xa9, 0x8b, 0x8f, 0x7a, 0xbc, 0x06, 0x42, 0x5f, + 0xea, 0xe0, 0x58, 0x32, 0xad, 0x7b, 0x43, 0x49, 0xfb, 0x10, 0x22, 0xce, + 0xbb, 0xb8, 0xc1, 0x43, 0x26, 0x88, 0xe8, 0xea, 0x4e, 0x1b, 0x82, 0xe8, + 0x5f, 0xf2, 0x73, 0xf8, 0x58, 0x7b, 0x58, 0x6b, 0x1f, 0x82, 0xfe, 0x6b, + 0x8d, 0x2d, 0x7b, 0x0f, 0x32, 0x45, 0xe3, 0xda, 0xdd, 0x4e, 0x65, 0x37, + 0xdf, 0x76, 0x2a, 0x05, 0x5d, 0xb5, 0x79, 0x48, 0xff, 0x85, 0x84, 0x66, + 0xcd, 0x30, 0xd2, 0x66, 0xbe, 0xae, 0xf5, 0xf3, 0x16, 0xa7, 0xdc, 0x09, + 0x60, 0xc0, 0x59, 0xd3, 0x71, 0x81, 0x53, 0xe7, 0x22, 0xf5, 0x48, 0xef, + 0xb4, 0xcf, 0x6d, 0xd2, 0x03, 0xbb, 0xca, 0xcc, 0x30, 0x3f, 0x23, 0x56, + 0x4d, 0x1e, 0x18, 0x74, 0x0e, 0x8b, 0x4a, 0xf8, 0xbd, 0xe5, 0xdf, 0x25, + 0xdb, 0x0b, 0xf7, 0x90, 0xe9, 0x73, 0xde, 0x5c, 0x90, 0x30, 0x1b, 0x11, + 0x1f, 0xec, 0x1d, 0x50, 0xdd, 0x54, 0x82, 0x81, 0x87, 0xdf, 0x84, 0xea, + 0xb4, 0x09, 0x74, 0x17, 0xd3, 0x68, 0x6e, 0xa5, 0x80, 0x42, 0x62, 0x47, + 0xfb, 0x93, 0x3e, 0xf6, 0x9d, 0xbc, 0x7e, 0xe2, 0x3c, 0xef, 0x2c, 0xa0, + 0xcf, 0x91, 0x49, 0xf2, 0xf0, 0xed, 0x3f, 0x24, 0x8b, 0xc4, 0xb4, 0x90, + 0x35, 0xeb, 0x82, 0xae, 0x38, 0xed, 0xfc, 0xbd, 0x48, 0x58, 0x87, 0x2e, + 0x20, 0x3d, 0xf7, 0x68, 0xa2, 0xdd, 0xce, 0xff, 0xf8, 0x4b, 0x76, 0x7c, + 0xf1, 0x68, 0x93, 0x1a, 0xe2, 0x65, 0x85, 0xa9, 0x4d, 0xf3, 0x36, 0x40, + 0x66, 0xfc, 0x29, 0x5d, 0x72, 0x77, 0xe7, 0x82, 0x75, 0x09, 0x3e, 0xea, + 0x4c, 0xd4, 0x5f, 0xf3, 0xec, 0xd9, 0x47, 0xcc, 0x38, 0x8f, 0xac, 0xc4, + 0x12, 0x60, 0xa2, 0x84, 0x39, 0x5f, 0xfb, 0xb8, 0x8d, 0x18, 0xcd, 0x2d, + 0x8f, 0x72, 0xd6, 0xfc, 0x0e, 0x82, 0x4f, 0x45, 0xa2, 0x2e, 0x38, 0xc8, + 0xb5, 0xb2, 0x7f, 0xed, 0x74, 0x4b, 0xc2, 0xc0, 0x24, 0xf8, 0x4f, 0xc0, + 0x25, 0xf2, 0x2f, 0xec, 0xef, 0xaa, 0x26, 0x44, 0xbe, 0x8d, 0x9e, 0xa2, + 0x7d, 0x92, 0xe1, 0xa1, 0x54, 0xb9, 0x4d, 0xf3, 0x82, 0x2e, 0xe0, 0x65, + 0x54, 0x44, 0xa1, 0x82, 0x80, 0x97, 0x63, 0x56, 0xac, 0xc4, 0x34, 0x74, + 0x63, 0x8b, 0xb0, 0xa8, 0x49, 0xf4, 0x79, 0x23, 0xa2, 0x7d, 0xf4, 0x68, + 0x6f, 0x1f, 0x40, 0x82, 0xe8, 0x73, 0x3a, 0x64, 0x7c, 0x83, 0xd9, 0x69, + 0xb4, 0x5b, 0xe2, 0xde, 0xc3, 0xb0, 0xe5, 0x0c, 0x68, 0x55, 0x90, 0xf5, + 0x07, 0xfc, 0x0f, 0xc5, 0xc3, 0xe2, 0x6e, 0x6c, 0xb1, 0xa4, 0x60, 0xc8, + 0xe3, 0x8b, 0x77, 0x63, 0xea, 0x08, 0x95, 0x97, 0x60, 0x6e, 0x3d, 0x5e, + 0xb3, 0xf7, 0xe4, 0xf2, 0x31, 0x3c, 0xf8, 0xcd, 0x5e, 0xad, 0xa7, 0x56, + 0xdb, 0x91, 0x75, 0xc1, 0x78, 0xf7, 0x7f, 0x45, 0x63, 0x03, 0xa4, 0xfc, + 0xf5, 0x66, 0x52, 0xaf, 0xca, 0xce, 0x9c, 0x5e, 0x52, 0x7c, 0xdc, 0x3b, + 0xcb, 0x2e, 0x4a, 0xe6, 0x50, 0x0f, 0x89, 0x40, 0x20, 0x91, 0x61, 0xa1, + 0xda, 0xcd, 0x2d, 0x49, 0x1a, 0x9f, 0xbd, 0x4d, 0x8c, 0xd0, 0xc7, 0xe9, + 0x38, 0x3f, 0xbc, 0x37, 0xab, 0xd1, 0x80, 0x10, 0xb7, 0x17, 0x50, 0x61, + 0xe0, 0xc8, 0x79, 0x7a, 0x91, 0x66, 0xe3, 0x97, 0x6f, 0x48, 0xac, 0x14, + 0xea, 0x84, 0x5c, 0x71, 0xd4, 0xbd, 0x26, 0x0a, 0x73, 0x69, 0x11, 0x20, + 0x58, 0x53, 0x80, 0xee, 0x1c, 0xc5, 0x92, 0x09, 0x84, 0x7d, 0x52, 0xb1, + 0x84, 0x3c, 0x32, 0x95, 0xe0, 0x1e, 0x07, 0x24, 0xf7, 0xfd, 0xdd, 0xa4, + 0xe2, 0xfa, 0x72, 0xca, 0xeb, 0x7f, 0x5c, 0xef, 0x2c, 0xfa, 0xdc, 0xa3, + 0x14, 0x55, 0x41, 0xdf, 0x47, 0xdf, 0xb1, 0xe8, 0x8b, 0x47, 0x3a, 0xbd, + 0x57, 0x1c, 0x3a, 0x8a, 0x22, 0x89, 0x64, 0xe8, 0xe6, 0xb8, 0x87, 0xf8, + 0x99, 0x0e, 0x8c, 0xe5, 0xfc, 0x29, 0x97, 0xf7, 0xc8, 0xbb, 0x33, 0x45, + 0x80, 0x98, 0x37, 0x2c, 0xf9, 0xc9, 0x5c, 0x00, 0xa5, 0xc3, 0xbc, 0x01, + 0x87, 0x98, 0x32, 0x18, 0xb0, 0x1e, 0x6d, 0x62, 0x0c, 0x79, 0x70, 0xb4, + 0xfc, 0x27, 0xd4, 0x57, 0x15, 0x05, 0xf4, 0x0e, 0xe7, 0x25, 0x90, 0xbf, + 0xd9, 0x77, 0x71, 0x61, 0xfd, 0x5c, 0xab, 0xde, 0xe6, 0x0b, 0x31, 0x05, + 0x82, 0xaf, 0x48, 0xe3, 0xdc, 0xa5, 0x10, 0x0f, 0x64, 0x71, 0xf0, 0x4e, + 0x7f, 0xa8, 0xc1, 0x8d, 0xe7, 0x53, 0x9f, 0x9e, 0xb3, 0x97, 0x79, 0x11, + 0x4f, 0x17, 0xec, 0x9a, 0x55, 0xf0, 0x02, 0x47, 0x05, 0xa7, 0x31, 0xb7, + 0x2a, 0x17, 0xd8, 0x82, 0xc3, 0x28, 0x65, 0x14, 0x7a, 0x39, 0x84, 0x35, + 0xaa, 0x5a, 0x00, 0x4f, 0x0a, 0xf6, 0x9e, 0x02, 0xad, 0x8d, 0xaa, 0x66, + 0x6e, 0x9d, 0x33, 0x7b, 0x2b, 0x99, 0x09, 0x19, 0xad, 0x91, 0xbd, 0xf3, + 0x65, 0x20, 0xfe, 0x0b, 0xc8, 0xe6, 0x0f, 0xf9, 0x63, 0xa7, 0xd6, 0xc0, + 0x82, 0xfc, 0x2c, 0xdc, 0xc4, 0xb4, 0x4d, 0x78, 0xa1, 0x06, 0xeb, 0x6f, + 0x1e, 0x50, 0xbc, 0x97, 0xaa, 0xfc, 0x47, 0x2c, 0xed, 0xf7, 0x20, 0x01, + 0x61, 0x8d, 0x36, 0xa8, 0x25, 0x69, 0xcc, 0x84, 0x3c, 0xa8, 0x4b, 0x96, + 0x9a, 0xdd, 0x61, 0xa7, 0x07, 0x8e, 0xde, 0x49, 0xab, 0xae, 0xa1, 0xe3, + 0xf7, 0x4f, 0x43, 0x87, 0xa6, 0x4e, 0xce, 0x3b, 0x30, 0xcd, 0xe5, 0x7a, + 0x05, 0xa9, 0x73, 0x85, 0xc8, 0x62, 0x8c, 0x85, 0x99, 0xf2, 0x24, 0x22, + 0x89, 0xfc, 0x4f, 0x9f, 0xfc, 0x6d, 0x75, 0x85, 0x97, 0x10, 0x72, 0xc9, + 0x37, 0x8d, 0x41, 0xa7, 0x95, 0x20, 0xf9, 0x9f, 0xaa, 0xfb, 0xc0, 0x9a, + 0xbe, 0x35, 0x30, 0xd5, 0x27, 0x8c, 0x4a, 0x57, 0x02, 0x6c, 0xd8, 0xa3, + 0xc2, 0xf4, 0xa6, 0xaa, 0x68, 0x91, 0x04, 0xc2, 0x02, 0x0b, 0x5a, 0xbb, + 0xf2, 0xc1, 0x8f, 0xd9, 0xbb, 0x5d, 0xe3, 0x23, 0x86, 0xbf, 0xde, 0xdb, + 0x87, 0x1e, 0xbc, 0xb1, 0x3a, 0xc0, 0x7c, 0xd2, 0xaf, 0x5a, 0xcf, 0x0f, + 0x51, 0x03, 0x65, 0x08, 0x57, 0xa7, 0x54, 0x4f, 0xe2, 0x32, 0x86, 0x8c, + 0x01, 0x6a, 0x0a, 0xd6, 0x5a, 0x07, 0x77, 0x6c, 0xb0, 0x2f, 0x48, 0x96, + 0xe2, 0xdd, 0x95, 0xc4, 0xb9, 0x28, 0x4f, 0x39, 0x22, 0x11, 0x0f, 0x29, + 0xb6, 0x1a, 0xae, 0x75, 0xad, 0x23, 0xac, 0x23, 0xf5, 0xe0, 0xec, 0x32, + 0x8b, 0x4e, 0x4e, 0x38, 0x77, 0xe0, 0x5b, 0xa4, 0x8d, 0x28, 0xac, 0xa9, + 0xa4, 0xc1, 0xa1, 0x11, 0xdd, 0x2d, 0x46, 0x5f, 0xd9, 0x46, 0x1c, 0xb4, + 0xa3, 0xe2, 0xbd, 0xd4, 0xe4, 0x08, 0xba, 0xe2, 0xf8, 0xad, 0x4f, 0x03, + 0xd6, 0x47, 0x87, 0x61, 0xbf, 0x29, 0x8a, 0x3d, 0x8f, 0x8f, 0x69, 0x0a, + 0x3d, 0xed, 0xd2, 0xe1, 0xa0, 0x94, 0xf0, 0x00, 0x12, 0xf0, 0x2f, 0x5e, + 0x80, 0x2b, 0xa4, 0x5a, 0xd2, 0x49, 0x8c, 0xd5, 0x87, 0xa6, 0xab, 0x40, + 0x40, 0x08, 0xfe, 0x13, 0x84, 0x36, 0xd6, 0x26, 0x7d, 0xf1, 0x61, 0xeb, + 0xb3, 0x7a, 0xcb, 0x91, 0x85, 0xe0, 0x39, 0x45, 0xf4, 0x05, 0x2b, 0x7f, + 0x88, 0x6d, 0xb7, 0xf6, 0xd8, 0x1f, 0xc0, 0x04, 0x80, 0x6a, 0x0d, 0x41, + 0x78, 0x92, 0x29, 0x03, 0xc0, 0x24, 0xe4, 0x03, 0x0a, 0xa7, 0xd5, 0xb0, + 0x6e, 0x08, 0xad, 0x7c, 0xd8, 0x3d, 0xf8, 0x55, 0xa7, 0xc4, 0x1f, 0xe1, + 0x69, 0xf8, 0x8d, 0xb4, 0x68, 0x0c, 0x2a, 0xba, 0xdd, 0x0f, 0xb8, 0x48, + 0xce, 0x01, 0x18, 0x74, 0xe4, 0x60, 0xdb, 0x20, 0x43, 0x35, 0xd9, 0x0e, + 0x55, 0x52, 0x3d, 0x81, 0xb4, 0x8d, 0x60, 0x39, 0x3f, 0x1c, 0x28, 0x64, + 0xca, 0x36, 0x42, 0x0d, 0xbc, 0xc4, 0x75, 0x01, 0x57, 0x67, 0x28, 0x39, + 0x45, 0xa0, 0xbb, 0x83, 0x89, 0x26, 0xc8, 0x2f, 0x6d, 0x27, 0x33, 0x74, + 0xef, 0x97, 0x71, 0x39, 0xce, 0x74, 0x94, 0x1d, 0xe6, 0xd7, 0x15, 0xab, + 0xae, 0x55, 0x03, 0xeb, 0xb0, 0xf4, 0x79, 0x09, 0x2d, 0xf8, 0x9b, 0x2b, + 0x25, 0x1b, 0x52, 0xe3, 0x67, 0xa5, 0xf3, 0x8e, 0x72, 0x3d, 0xcf, 0x92, + 0x6a, 0xef, 0x35, 0x71, 0xcb, 0x93, 0x09, 0x4a, 0x26, 0x92, 0x91, 0x16, + 0x6b, 0x7d, 0xa8, 0xd0, 0x0a, 0x9c, 0xa6, 0xb0, 0x82, 0x1e, 0x19, 0xe9, + 0x1d, 0x6d, 0x85, 0x98, 0xba, 0xe9, 0xc4, 0xcd, 0x39, 0xc2, 0x6a, 0x5e, + 0x28, 0xec, 0xa7, 0x0b, 0x50, 0x76, 0x5d, 0xde, 0x80, 0x7f, 0x57, 0x91, + 0x19, 0xdd, 0xbb, 0x5e, 0xac, 0x82, 0x91, 0xc7, 0x95, 0xad, 0xd5, 0x9d, + 0xd7, 0xf3, 0x44, 0x88, 0x4e, 0xa3, 0x38, 0xd8, 0xf5, 0xc1, 0x9f, 0x5b, + 0x26, 0xcb, 0xbd, 0xe9, 0x35, 0x64, 0xa9, 0xa8, 0xbf, 0x3e, 0xf5, 0x98, + 0x7d, 0xb4, 0xfb, 0x1e, 0x22, 0xd9, 0xec, 0xa9, 0xce, 0x73, 0xe6, 0x13, + 0xc3, 0x1a, 0x9e, 0x67, 0xb3, 0x7f, 0x78, 0x5b, 0xb8, 0x53, 0xa4, 0xda, + 0x9c, 0x82, 0xff, 0x69, 0x17, 0x49, 0x5e, 0x16, 0x0a, 0x88, 0xe4, 0x13, + 0xd7, 0x56, 0x23, 0x07, 0x09, 0x60, 0x85, 0x7d, 0x68, 0xe9, 0x1a, 0x6d, + 0xeb, 0x1d, 0x6e, 0x2f, 0x81, 0xf8, 0x42, 0x81, 0x95, 0x96, 0x38, 0x10, + 0x3f, 0x6d, 0x88, 0x43, 0x91, 0xda, 0x16, 0x03, 0x2b, 0x3e, 0xc9, 0xb9, + 0xed, 0xc6, 0x24, 0xa7, 0xc4, 0xf5, 0x1a, 0x27, 0x37, 0xd9, 0xab, 0x8b, + 0xad, 0xa4, 0xae, 0xf5, 0x23, 0x87, 0x9a, 0x3e, 0x98, 0xca, 0x31, 0x5e, + 0x96, 0x0e, 0xa4, 0xd3, 0xff, 0x13, 0x4e, 0x14, 0x8e, 0xd1, 0x60, 0xac, + 0x4c, 0x10, 0x10, 0x21, 0xff, 0x90, 0x4c, 0x13, 0xea, 0xfa, 0x54, 0x98, + 0x4b, 0xe2, 0xaf, 0xf4, 0x3c, 0xc4, 0xda, 0x84, 0xac, 0x8a, 0xdf, 0xb3, + 0x7b, 0xda, 0x52, 0x90, 0xde, 0x3d, 0xd2, 0xc3, 0x00, 0x47, 0x85, 0x5b, + 0xd6, 0x14, 0x07, 0xd4, 0x96, 0xc9, 0x0e, 0x69, 0xcf, 0xef, 0x75, 0x39, + 0x6f, 0xf6, 0xb6, 0x07, 0x8d, 0x4f, 0xf1, 0x2e, 0xc0, 0x1e, 0x0d, 0x5e, + 0xd6, 0x37, 0xfd, 0xae, 0x1b, 0x10, 0x19, 0xb7, 0xa8, 0x3b, 0x4b, 0x35, + 0xbb, 0x2c, 0xe6, 0x20, 0x15, 0x59, 0x19, 0xab, 0x9b, 0x7f, 0x4e, 0x04, + 0x45, 0x87, 0x5f, 0xbe, 0x92, 0xf9, 0x10, 0x04, 0x50, 0xc6, 0x22, 0x9e, + 0xd5, 0x26, 0xb0, 0x98, 0x8d, 0x13, 0x53, 0x3a, 0x95, 0x95, 0x4a, 0xa7, + 0x2b, 0xfe, 0x6b, 0xd7, 0xf3, 0xd1, 0x22, 0xa2, 0x91, 0xa1, 0x08, 0xd2, + 0x06, 0x51, 0x8e, 0x2f, 0xd2, 0xcb, 0x88, 0x28, 0x69, 0xb3, 0x4f, 0x95, + 0x51, 0x34, 0xa9, 0x38, 0x0f, 0x12, 0x76, 0x81, 0x19, 0xed, 0xf9, 0xe5, + 0xed, 0x34, 0x9b, 0xd3, 0xf7, 0x51, 0xaf, 0x27, 0x39, 0x97, 0xc8, 0x91, + 0x8d, 0x86, 0x99, 0x04, 0x3b, 0xa3, 0x3e, 0xcf, 0xb7, 0xc4, 0x30, 0x1e, + 0x70, 0xd8, 0x60, 0x0a, 0x08, 0xe8, 0x53, 0xd2, 0xa2, 0x8b, 0x63, 0x07, + 0x67, 0x75, 0xf5, 0xf5, 0xa2, 0xb0, 0x45, 0xa0, 0xc9, 0xed, 0x7f, 0xfe, + 0x26, 0x49, 0x7d, 0x71, 0xc9, 0x08, 0xa4, 0xac, 0xc3, 0xe2, 0xde, 0x0f, + 0x8b, 0x3f, 0x41, 0x94, 0xec, 0x06, 0xca, 0x79, 0x1b, 0x29, 0x54, 0xa8, + 0x4e, 0x51, 0x05, 0x92, 0x95, 0x9a, 0x9c, 0xc0, 0x59, 0x00, 0x66, 0x3f, + 0x9f, 0xfd, 0xc7, 0x7e, 0xc4, 0x23, 0xcf, 0x46, 0x80, 0xe4, 0x9a, 0x48, + 0x26, 0x3a, 0xc1, 0x0d, 0xde, 0x2b, 0x20, 0xc7, 0xf8, 0x06, 0xfe, 0xfc, + 0x75, 0x6b, 0xef, 0xdc, 0x6e, 0x7d, 0xca, 0x2d, 0x6c, 0xfd, 0xdd, 0xa6, + 0x5f, 0xf9, 0x14, 0x54, 0x18, 0xfb, 0x2e, 0x32, 0x7e, 0xc6, 0xef, 0x7f, + 0x0a, 0x65, 0xe1, 0x96, 0xa4, 0xff, 0x84, 0xee, 0x83, 0x77, 0x0d, 0x92, + 0xfe, 0x2d, 0x30, 0xbf, 0x67, 0x0e, 0xa1, 0x21, 0xa0, 0x7a, 0x8a, 0x0d, + 0xe8, 0x0b, 0x52, 0x6b, 0x99, 0x95, 0xe3, 0xe4, 0x41, 0x74, 0xc4, 0x96, + 0x94, 0x02, 0x17, 0xb7, 0xd8, 0x46, 0x5f, 0xc5, 0x92, 0x50, 0x2a, 0x28, + 0xa9, 0xc9, 0x0b, 0x05, 0xe4, 0x1f, 0xa0, 0x59, 0x51, 0x7c, 0x97, 0x3a, + 0x29, 0x89, 0x65, 0x28, 0x7c, 0x4c, 0xb6, 0xd6, 0x84, 0x81, 0x68, 0x4a, + 0x94, 0x6c, 0x10, 0x44, 0x8b, 0xb3, 0x52, 0xa8, 0x93, 0x80, 0xd0, 0x63, + 0x20, 0xf5, 0xd9, 0x25, 0xf8, 0xfa, 0x17, 0xd4, 0xc2, 0x28, 0xb5, 0x1d, + 0xc8, 0x03, 0xed, 0x87, 0x51, 0xe0, 0x14, 0x45, 0xcc, 0x51, 0x1d, 0xc7, + 0xe1, 0x05, 0xd6, 0x53, 0x22, 0x58, 0x3e, 0x8c, 0x06, 0xee, 0xa6, 0x45, + 0xf0, 0xb3, 0xe9, 0x0f, 0xf5, 0x4a, 0x0b, 0xe6, 0xec, 0x34, 0xc4, 0x9f, + 0x76, 0x3f, 0x12, 0xf1, 0xdc, 0xc1, 0x86, 0xdf, 0x4d, 0x91, 0x6b, 0xd2, + 0x07, 0x12, 0x60, 0x8d, 0x83, 0x19, 0x67, 0x3a, 0xfd, 0x0a, 0x35, 0xfb, + 0xb9, 0x8a, 0x98, 0x0d, 0x39, 0x1e, 0xdd, 0xcf, 0xec, 0xea, 0x52, 0xe3, + 0xc9, 0x4d, 0x54, 0xfe, 0xb9, 0xbb, 0x98, 0x52, 0x09, 0x00, 0x0a, 0x68, + 0xc4, 0xde, 0x9c, 0x78, 0x69, 0x99, 0x66, 0x47, 0x33, 0x55, 0x9f, 0x72, + 0x63, 0xf5, 0x95, 0x93, 0xcd, 0xf4, 0x50, 0x9a, 0xdb, 0xbd, 0x01, 0xe9, + 0xc5, 0x5a, 0x53, 0xa8, 0x48, 0x68, 0xe9, 0xf9, 0xff, 0xdd, 0x3c, 0x4f, + 0xc8, 0xe2, 0xfc, 0x5b, 0xd0, 0x1b, 0x52, 0x20, 0xe4, 0x1f, 0xd5, 0x22, + 0x61, 0x8b, 0x3c, 0x1d, 0x13, 0xc6, 0xcd, 0x78, 0x54, 0xf4, 0x7b, 0x8d, + 0x86, 0x4b, 0xbf, 0xd3, 0xb2, 0xfc, 0xef, 0x1e, 0x80, 0x06, 0x24, 0x0a, + 0xd3, 0xf1, 0x5f, 0xb2, 0x5f, 0x72, 0x2b, 0xb1, 0x18, 0x35, 0xae, 0x0b, + 0x6f, 0x1b, 0x53, 0xd5, 0x42, 0x55, 0xca, 0x52, 0xd9, 0xbc, 0x29, 0x5d, + 0x2b, 0x7d, 0x44, 0x53, 0xd9, 0x5b, 0x8a, 0x19, 0xbf, 0x7f, 0x39, 0xca, + 0xd1, 0xfb, 0xb6, 0xc9, 0x2b, 0x27, 0x81, 0x33, 0x23, 0xb9, 0xdb, 0x16, + 0x6f, 0x48, 0x91, 0x01, 0xc9, 0x0e, 0x5c, 0x28, 0x29, 0xd5, 0x41, 0xff, + 0xc5, 0xe0, 0x67, 0x02, 0x4f, 0xcc, 0x0a, 0x38, 0xf4, 0x3a, 0xa0, 0x70, + 0x18, 0x2f, 0xdd, 0x7b, 0xdc, 0x98, 0xfb, 0x1c, 0x9f, 0x58, 0xf1, 0x77, + 0x11, 0x2a, 0x26, 0xfc, 0x10, 0xe0, 0x55, 0xe3, 0x0d, 0x97, 0xe1, 0x16, + 0xd8, 0x79, 0x2a, 0x98, 0x87, 0xe7, 0x43, 0x23, 0x58, 0x5a, 0x59, 0xb8, + 0xd9, 0xc9, 0x0f, 0x3a, 0x2a, 0x28, 0xfc, 0xbb, 0x5f, 0x58, 0x73, 0x3f, + 0x25, 0xcc, 0xb0, 0x08, 0x22, 0x27, 0x6b, 0xe4, 0x3e, 0xea, 0x80, 0x0b, + 0xee, 0x43, 0x10, 0x1c, 0x06, 0xe6, 0xb8, 0x07, 0x8d, 0x9e, 0x98, 0x87, + 0xec, 0x8e, 0x30, 0x51, 0x49, 0x1e, 0xfc, 0x63, 0x7a, 0x36, 0x64, 0xdf, + 0xc2, 0x47, 0xd7, 0x49, 0x09, 0x21, 0xd8, 0x03, 0x51, 0x52, 0xe6, 0x31, + 0x6c, 0x9d, 0xf7, 0xc2, 0x80, 0x8e, 0xdc, 0x2f, 0x2c, 0xd6, 0x57, 0x20, + 0xcf, 0x25, 0x95, 0xc4, 0xdc, 0xde, 0x2e, 0x54, 0xe0, 0x3e, 0x71, 0xf2, + 0xc4, 0x01, 0x09, 0x70, 0x26, 0xd7, 0x22, 0x08, 0x33, 0x0a, 0xb6, 0xff, + 0x08, 0x77, 0xc1, 0xe7, 0xcd, 0x8d, 0x53, 0xac, 0xba, 0xed, 0xab, 0xf1, + 0x6a, 0xbf, 0x5f, 0x13, 0x1d, 0x1e, 0x4b, 0x17, 0xe8, 0x29, 0x93, 0x0a, + 0x44, 0x09, 0x47, 0xd1, 0x03, 0x34, 0xca, 0x08, 0xe0, 0x32, 0xd0, 0xce, + 0xbf, 0xf9, 0x5f, 0x77, 0x21, 0x96, 0xe1, 0x00, 0x15, 0x7e, 0x73, 0xdc, + 0x66, 0x4d, 0xf0, 0xea, 0x47, 0xa3, 0x2c, 0x2d, 0x98, 0xd5, 0x6a, 0xbe, + 0xfe, 0xb2, 0x73, 0x4d, 0x8e, 0xea, 0xfc, 0x80, 0xf1, 0x00, 0x85, 0xed, + 0x5c, 0x55, 0x61, 0xc3, 0xfa, 0x60, 0xc0, 0x36, 0xda, 0x7b, 0x7a, 0x91, + 0x24, 0x28, 0x2b, 0xd4, 0xe7, 0x11, 0xad, 0x2d, 0xff, 0x33, 0x6c, 0xa3, + 0x78, 0xa0, 0x3a, 0x60, 0x55, 0x35, 0x78, 0x5c, 0x6d, 0xf1, 0xcb, 0x6e, + 0xcf, 0x13, 0x91, 0x4c, 0x84, 0x21, 0xf3, 0xae, 0xca, 0x6b, 0xe2, 0xbf, + 0x4f, 0xd5, 0xef, 0xb5, 0xb2, 0xb3, 0x37, 0xc7, 0x3f, 0xb1, 0xff, 0x13, + 0x2f, 0x9d, 0xb6, 0x14, 0x83, 0xb1, 0x47, 0x99, 0xb2, 0x3d, 0x94, 0xd6, + 0x02, 0x79, 0x6d, 0x28, 0xe7, 0xa5, 0x51, 0x6d, 0x69, 0xc1, 0xbf, 0x2f, + 0xe1, 0xbb, 0x4f, 0x61, 0x56, 0x59, 0xcb, 0xe6, 0x86, 0xfb, 0x03, 0xdb, + 0x10, 0xf7, 0xe3, 0x62, 0xeb, 0xfd, 0x06, 0xed, 0xdf, 0xab, 0x52, 0xaf, + 0xed, 0xdb, 0x16, 0x9b, 0x66, 0x4c, 0x5d, 0x43, 0x0f, 0xda, 0xa2, 0xba, + 0x9c, 0x07, 0x9d, 0xe0, 0x95, 0x8a, 0x45, 0xa8, 0x7a, 0xeb, 0xfe, 0x85, + 0xf0, 0xfd, 0x14, 0xd7, 0x73, 0x40, 0x20, 0x94, 0x01, 0x58, 0x11, 0x61, + 0x70, 0x73, 0xcf, 0xc5, 0x80, 0x0b, 0x5d, 0x3c, 0xe5, 0x65, 0x3e, 0x45, + 0x00, 0xb8, 0x4b, 0xf5, 0x92, 0x5f, 0xba, 0xb7, 0x80, 0x4b, 0xbb, 0x08, + 0x8a, 0x25, 0x11, 0x80, 0x17, 0x4d, 0x3d, 0x99, 0x77, 0x89, 0x8a, 0xb9, + 0x0e, 0xcf, 0x95, 0x89, 0xc9, 0x5b, 0xa1, 0x23, 0xe5, 0x38, 0xf8, 0x86, + 0x27, 0x86, 0x04, 0x39, 0xc8, 0x9e, 0x75, 0x59, 0x67, 0x2f, 0x02, 0x3d, + 0xe8, 0xb0, 0x58, 0x9c, 0xcf, 0x98, 0x3c, 0xf7, 0xbb, 0x80, 0xaf, 0xbf, + 0xce, 0xfd, 0xcd, 0x64, 0x53, 0x80, 0xdd, 0x2d, 0xc4, 0x49, 0xd0, 0x8f, + 0x42, 0xc7, 0xfd, 0x86, 0x03, 0xa8, 0x1f, 0x93, 0x2a, 0xd5, 0x30, 0x41, + 0x69, 0x39, 0x71, 0x36, 0xc0, 0xee, 0x48, 0x6a, 0x0c, 0x05, 0xbd, 0x97, + 0xd7, 0x89, 0x17, 0xbc, 0x37, 0xb5, 0x81, 0xd7, 0x66, 0x64, 0xf3, 0x6d, + 0xa7, 0x13, 0xe4, 0x93, 0x8c, 0x6d, 0xc9, 0xe3, 0x12, 0x2e, 0xc6, 0x64, + 0xfe, 0x25, 0x1e, 0x5a, 0xc4, 0x44, 0xcd, 0x8d, 0x93, 0xd7, 0x3c, 0x0a, + 0xde, 0x64, 0xdd, 0xda, 0x01, 0xbd, 0x05, 0x9d, 0x45, 0x5a, 0x3d, 0x52, + 0x18, 0x7e, 0x7f, 0x92, 0x9b, 0x94, 0x2e, 0xa9, 0xa6, 0x28, 0x10, 0x30, + 0x48, 0x89, 0x85, 0x1f, 0x9d, 0x54, 0xec, 0x21, 0x33, 0xdc, 0xa9, 0x46, + 0x97, 0x34, 0xf0, 0x64, 0xbd, 0xcf, 0x22, 0xdf, 0x6c, 0xaa, 0xe6, 0x63, + 0x58, 0x92, 0x8c, 0x69, 0x02, 0xa5, 0x53, 0x7d, 0x4b, 0x13, 0xc0, 0x37, + 0x32, 0xd7, 0xb5, 0x41, 0xf5, 0xbd, 0x79, 0xb0, 0x6a, 0x13, 0x2c, 0xfd, + 0x11, 0x13, 0xe9, 0x0b, 0xef, 0x46, 0xf6, 0xbe, 0x46, 0xf7, 0x54, 0xf0, + 0x96, 0x92, 0xe0, 0xab, 0xe9, 0xfa, 0x8d, 0xc9, 0x55, 0x62, 0x46, 0xc4, + 0x50, 0xc4, 0x2e, 0xea, 0xff, 0x85, 0x7e, 0xe9, 0x16, 0xc7, 0x82, 0x30, + 0x52, 0xfc, 0x19, 0x61, 0xd2, 0x13, 0xf7, 0x6f, 0x3d, 0xb7, 0x46, 0x85, + 0x13, 0x81, 0x65, 0x85, 0xc4, 0x75, 0x71, 0xf6, 0x83, 0x53, 0x6f, 0x09, + 0x44, 0x28, 0x99, 0x0d, 0x43, 0xb6, 0xde, 0xbc, 0xa4, 0xf7, 0xe4, 0xa4, + 0x33, 0x22, 0x78, 0x3e, 0x10, 0x1f, 0x7d, 0xe8, 0x07, 0xb5, 0xba, 0x02, + 0xf1, 0x0b, 0x26, 0xe3, 0x3c, 0x15, 0x7c, 0x6f, 0x93, 0xdd, 0xb5, 0x85, + 0x17, 0x29, 0xea, 0xed, 0x70, 0xe2, 0x1b, 0x47, 0xcc, 0xa2, 0x45, 0x14, + 0xfc, 0x86, 0xef, 0x01, 0xf6, 0xf9, 0xd1, 0xdd, 0x5b, 0x14, 0x33, 0xf3, + 0x35, 0x9f, 0x0c, 0xe4, 0xe9, 0xfb, 0x6c, 0x64, 0x76, 0x8a, 0xc1, 0xf9, + 0x15, 0xdf, 0xea, 0xdd, 0xdc, 0x96, 0xe3, 0x36, 0xe2, 0x55, 0xee, 0x8d, + 0x73, 0x6e, 0x04, 0x01, 0x69, 0xb7, 0x07, 0x01, 0xd4, 0x22, 0xf0, 0xfc, + 0x8a, 0x2d, 0x50, 0xd9, 0xb0, 0xae, 0x89, 0x03, 0xe9, 0xae, 0x1c, 0x73, + 0x8e, 0x06, 0xa1, 0x29, 0x5d, 0x3f, 0xc3, 0x62, 0x30, 0x23, 0x25, 0x36, + 0xde, 0xfc, 0xb7, 0x21, 0x24, 0xd4, 0xb4, 0x73, 0x04, 0xd2, 0x60, 0x56, + 0x4f, 0xb7, 0xb3, 0x3e, 0x51, 0xc8, 0xa0, 0x28, 0xf4, 0xe1, 0x15, 0x94, + 0x46, 0x46, 0x18, 0xa6, 0xf4, 0x9e, 0x33, 0x6e, 0x2e, 0xb9, 0x6d, 0xa3, + 0x66, 0x1c, 0x30, 0x15, 0x45, 0x5e, 0x06, 0xa5, 0xaf, 0x4d, 0x41, 0xf4, + 0xee, 0x4b, 0x3d, 0x99, 0x02, 0x6c, 0x1f, 0x98, 0xc7, 0x48, 0xe8, 0xd1, + 0xd6, 0x20, 0x9a, 0xbd, 0xad, 0x33, 0xd1, 0xcc, 0xae, 0xa3, 0xdf, 0xf6, + 0xee, 0xfe, 0x07, 0xe6, 0x21, 0x21, 0xb4, 0x2a, 0x4b, 0x6d, 0xd9, 0x79, + 0x48, 0x81, 0xc2, 0xe9, 0x07, 0x74, 0x61, 0xf6, 0xe5, 0x4e, 0xea, 0x58, + 0xbf, 0xc6, 0xe3, 0x4b, 0x34, 0x0b, 0x89, 0x79, 0x16, 0xbf, 0x27, 0xd7, + 0x88, 0x65, 0x32, 0x0b, 0x7d, 0xdc, 0xf6, 0xc4, 0x7a, 0x45, 0xb0, 0xb4, + 0x25, 0x30, 0x9b, 0xcb, 0xbe, 0x5b, 0xdc, 0xa9, 0x6f, 0x05, 0x48, 0xb6, + 0x91, 0x68, 0x5e, 0x43, 0x83, 0x25, 0x30, 0xf8, 0x8b, 0x59, 0x4b, 0x6f, + 0x00, 0xc7, 0x40, 0xa9, 0xe8, 0x17, 0xeb, 0x8c, 0x24, 0xeb, 0xcd, 0x3e, + 0x1a, 0xef, 0x02, 0x26, 0xe9, 0x73, 0xa6, 0xf9, 0xb8, 0xc4, 0x19, 0x97, + 0x1c, 0x6b, 0x97, 0x84, 0x2d, 0xb2, 0x00, 0x9e, 0xb2, 0x17, 0x38, 0xe2, + 0x65, 0x54, 0x57, 0x6e, 0xce, 0x7a, 0x34, 0xae, 0x40, 0x17, 0x5e, 0x73, + 0x3c, 0x86, 0x9d, 0x37, 0xe7, 0x7e, 0x0a, 0x89, 0x72, 0x17, 0x4d, 0x8e, + 0x2b, 0x63, 0x24, 0x4a, 0x4f, 0xc2, 0xc5, 0x99, 0xb6, 0x69, 0x0c, 0x07, + 0x2f, 0x7d, 0x3a, 0xc9, 0x95, 0x53, 0x5e, 0xb1, 0x4d, 0x1c, 0xf2, 0x2d, + 0xfb, 0xd9, 0xfa, 0x00, 0xca, 0xb2, 0xad, 0x19, 0x67, 0xf5, 0xc8, 0xa2, + 0x02, 0x16, 0xbc, 0xa9, 0x2e, 0x3e, 0x11, 0x46, 0x77, 0xf1, 0x45, 0xa2, + 0x39, 0xd9, 0xa5, 0x52, 0x9e, 0x22, 0xe7, 0xb1, 0x65, 0x58, 0x72, 0x45, + 0xf1, 0xb1, 0x6c, 0x8f, 0xe3, 0xd4, 0xda, 0x07, 0x6a, 0x0b, 0xd1, 0x56, + 0xc0, 0x89, 0xbf, 0x1c, 0x93, 0xe8, 0xfb, 0xa4, 0x29, 0x15, 0xad, 0x17, + 0x7b, 0x4f, 0xcb, 0x0f, 0xdf, 0x28, 0xea, 0xe8, 0xbe, 0xb7, 0x6b, 0xf1, + 0xf7, 0xc5, 0xdc, 0x53, 0x73, 0x07, 0x02, 0x55, 0x47, 0x47, 0x6e, 0xd2, + 0xac, 0x4f, 0xae, 0x77, 0x63, 0xd5, 0x65, 0x79, 0x97, 0xa9, 0x69, 0x7f, + 0x53, 0xb7, 0x2f, 0x5e, 0x01, 0xd8, 0xd8, 0x2d, 0xe1, 0xe8, 0x5f, 0x53, + 0x5c, 0x18, 0x25, 0x17, 0x58, 0x81, 0x42, 0x00, 0xc1, 0x89, 0x4c, 0x20, + 0xe1, 0x9a, 0x29, 0x67, 0xfa, 0xdd, 0x70, 0xf3, 0xfd, 0xe9, 0xee, 0x9a, + 0x8b, 0x93, 0xbf, 0x1f, 0xa8, 0xab, 0x96, 0x56, 0x08, 0x50, 0x35, 0x33, + 0x13, 0xca, 0xeb, 0xa4, 0xc8, 0x2d, 0x74, 0xb3, 0xd0, 0x1c, 0x6b, 0x1d, + 0x0a, 0x4d, 0xbd, 0x0c, 0x8e, 0xc6, 0xfc, 0x4e, 0x3c, 0x5d, 0x13, 0x8e, + 0x2a, 0xc9, 0x74, 0x72, 0x79, 0x77, 0x3a, 0x8c, 0x77, 0x91, 0x0f, 0x87, + 0xc9, 0x3b, 0xa8, 0xd4, 0x8c, 0xf3, 0xa1, 0xbd, 0xa5, 0xd6, 0xc0, 0x0f, + 0x3c, 0xeb, 0x13, 0x06, 0xdd, 0xaf, 0x94, 0x6f, 0x4d, 0x78, 0x7e, 0x09, + 0x38, 0xc3, 0xa1, 0xe9, 0xe4, 0x1d, 0x75, 0xfb, 0x2a, 0x81, 0x2a, 0x2c, + 0xd5, 0xac, 0x07, 0xcf, 0x17, 0x4f, 0xff, 0xc9, 0xd2, 0x77, 0x35, 0x24, + 0xea, 0x20, 0xb5, 0xd0, 0xee, 0x45, 0xb2, 0x12, 0x9f, 0x59, 0x42, 0x6e, + 0xc1, 0x3f, 0x54, 0xb1, 0xfb, 0x97, 0xc6, 0x2f, 0xee, 0xc4, 0xa6, 0x29, + 0x4e, 0xba, 0x9b, 0x42, 0x04, 0xa5, 0x4a, 0x36, 0x11, 0x12, 0xcb, 0xa9, + 0x4a, 0x0b, 0x75, 0xf2, 0x69, 0x37, 0x94, 0x72, 0x76, 0x7c, 0x9e, 0x4b, + 0x3b, 0xec, 0x64, 0x22, 0x53, 0xfe, 0x7e, 0xe9, 0x22, 0x85, 0x1e, 0xd3, + 0xad, 0xf0, 0xd1, 0x30, 0x80, 0xe6, 0x2b, 0x64, 0x96, 0x6c, 0xa9, 0x0a, + 0x8d, 0x63, 0x58, 0x0c, 0xda, 0x08, 0x40, 0x9f, 0x82, 0xd1, 0xff, 0x51, + 0xac, 0xdb, 0x7f, 0xaf, 0xa4, 0xc3, 0x92, 0xc5, 0xde, 0xba, 0x17, 0x02, + 0xfb, 0x35, 0x37, 0x68, 0x81, 0xe3, 0x82, 0x9d, 0xda, 0x13, 0x49, 0xe7, + 0x82, 0x0c, 0x29, 0x9e, 0xc7, 0xd1, 0xa1, 0x86, 0x2e, 0x7a, 0x3f, 0xb1, + 0xe8, 0x32, 0x84, 0xb8, 0xd4, 0x0a, 0x96, 0x95, 0x38, 0x6c, 0x62, 0x27, + 0xa6, 0x23, 0x46, 0xa0, 0x42, 0xd9, 0xf6, 0xdc, 0xc6, 0x71, 0x8c, 0x5d, + 0xc8, 0xc6, 0x7e, 0xba, 0xaf, 0x0b, 0xae, 0xd8, 0xb7, 0x76, 0x39, 0x9e, + 0x5d, 0x7a, 0x5a, 0xb5, 0x8e, 0x57, 0x94, 0xc3, 0x61, 0x6e, 0x92, 0xd8, + 0x52, 0x18, 0xb1, 0x6c, 0x72, 0xa1, 0x82, 0xfc, 0xcd, 0x5e, 0xdb, 0xfb, + 0x4a, 0x5c, 0xed, 0x9a, 0x45, 0x11, 0xf8, 0xff, 0x0d, 0xfd, 0xef, 0xff, + 0x92, 0x61, 0xf5, 0x6c, 0xa8, 0x9a, 0x18, 0x8c, 0x51, 0x96, 0x12, 0xa7, + 0x1f, 0x05, 0x20, 0x8a, 0x07, 0x00, 0xec, 0x84, 0xca, 0x2f, 0x46, 0xca, + 0x26, 0xf5, 0x75, 0x50, 0x30, 0x65, 0x64, 0x7a, 0xff, 0xbd, 0x4c, 0xe7, + 0xca, 0x34, 0x65, 0x8d, 0xb1, 0xde, 0x2f, 0x60, 0xa5, 0xcb, 0x1f, 0x11, + 0x38, 0x03, 0xcc, 0xdf, 0x4a, 0x5b, 0x8d, 0x9a, 0x19, 0x17, 0xbf, 0xa9, + 0xce, 0x53, 0x78, 0x45, 0xfd, 0xe3, 0x71, 0xe4, 0x29, 0x02, 0x41, 0x6c, + 0x0d, 0xc2, 0x9c, 0xa0, 0x8a, 0x9a, 0x1d, 0xb4, 0xdd, 0xa0, 0x2b, 0x91, + 0x8d, 0xc5, 0xa5, 0x7e, 0x68, 0x4b, 0xf0, 0x75, 0x0d, 0xa2, 0x53, 0x93, + 0x29, 0x05, 0x0f, 0x81, 0x8b, 0x04, 0x0c, 0x18, 0xd3, 0x8a, 0x89, 0x60, + 0xd3, 0x07, 0x7e, 0x13, 0x2b, 0x4f, 0x0f, 0x7b, 0x2c, 0xf5, 0x43, 0xc7, + 0xc3, 0x04, 0xba, 0x22, 0xb9, 0xe2, 0xdc, 0x3e, 0xee, 0xb1, 0x2c, 0x98, + 0x5f, 0x5e, 0xfa, 0x56, 0xbe, 0x2f, 0x24, 0xd9, 0xa6, 0xe6, 0xde, 0x17, + 0x60, 0x20, 0x3a, 0x04, 0xf0, 0x77, 0xbb, 0xc9, 0x33, 0x82, 0x28, 0x5f, + 0xa8, 0xe8, 0xa5, 0x22, 0xd9, 0x6f, 0x2e, 0x0f, 0x8b, 0x36, 0xd5, 0xda, + 0x1e, 0x75, 0x67, 0x3e, 0x36, 0x3a, 0xcf, 0x89, 0xb0, 0x38, 0x97, 0xde, + 0x2d, 0x43, 0x52, 0xd1, 0x06, 0x00, 0x22, 0x04, 0x5e, 0x21, 0xe8, 0x92, + 0x2d, 0xac, 0x60, 0xc2, 0xc8, 0x84, 0x3d, 0xf6, 0xa8, 0xb5, 0x5c, 0x72, + 0xb3, 0x01, 0x9f, 0x26, 0x55, 0xbd, 0xce, 0xbe, 0xd5, 0x93, 0xc3, 0xee, + 0x18, 0x84, 0x79, 0x08, 0xac, 0x6b, 0xa8, 0x2b, 0x7f, 0x97, 0x3e, 0x88, + 0x3e, 0xd3, 0x5a, 0xc3, 0xa1, 0xc6, 0x55, 0xad, 0xba, 0x39, 0x85, 0x73, + 0x6d, 0x0f, 0xdc, 0x5c, 0x7e, 0x82, 0xba, 0x61, 0x98, 0x4d, 0x58, 0x2b, + 0x92, 0x50, 0xee, 0xda, 0x09, 0xaa, 0x2b, 0x62, 0xe1, 0x2d, 0xa0, 0xfd, + 0xcd, 0x54, 0x38, 0x6b, 0x8a, 0x38, 0xf5, 0xb9, 0xfd, 0x5b, 0x6d, 0x8d, + 0x48, 0x88, 0x2a, 0x0e, 0xd9, 0x6e, 0x18, 0xec, 0x7d, 0xde, 0x08, 0x78, + 0x69, 0x1b, 0xd1, 0xfa, 0x33, 0x5a, 0x9b, 0x23, 0x79, 0x83, 0x92, 0x40, + 0xb2, 0x36, 0x28, 0xbe, 0x84, 0x62, 0x35, 0x88, 0xef, 0xca, 0xe9, 0xbd, + 0x77, 0x73, 0xed, 0x8f, 0xa0, 0xdd, 0xb3, 0xef, 0xb0, 0x04, 0x36, 0x8c, + 0xa0, 0xe8, 0xa8, 0x0c, 0xa4, 0x84, 0xee, 0xf0, 0x3d, 0x3f, 0x5a, 0x18, + 0x49, 0xef, 0x78, 0x77, 0xf6, 0x4b, 0x48, 0x89, 0xa9, 0x8e, 0x46, 0x3f, + 0xd9, 0xd7, 0x97, 0xfb, 0x14, 0xce, 0x88, 0xb6, 0x80, 0xe0, 0xf0, 0xd9, + 0x38, 0x0b, 0xfb, 0x7e, 0x17, 0x22, 0x3d, 0x55, 0xff, 0x24, 0x8f, 0xb3, + 0xbc, 0x76, 0xee, 0x4f, 0xc3, 0xbe, 0x96, 0xf6, 0x48, 0x6f, 0x64, 0xd5, + 0x9c, 0x98, 0x20, 0x26, 0x3b, 0xa8, 0xa3, 0x08, 0xb5, 0x1e, 0x84, 0x44, + 0x60, 0xae, 0xd1, 0x9f, 0x85, 0x63, 0xfb, 0x43, 0xed, 0xd9, 0xae, 0x83, + 0xf3, 0x17, 0x16, 0x39, 0x80, 0x85, 0x00, 0xae, 0x57, 0x5f, 0x8c, 0x1a, + 0x17, 0x2d, 0xb2, 0xae, 0xe4, 0x12, 0x51, 0xcc, 0x81, 0x97, 0xcb, 0x20, + 0x69, 0x76, 0x35, 0xb1, 0xc9, 0x92, 0x26, 0xf3, 0x03, 0x5e, 0x64, 0x44, + 0xaf, 0xe9, 0x28, 0x0e, 0x72, 0x6c, 0x92, 0xe3, 0x86, 0x03, 0x94, 0xe2, + 0xc8, 0x03, 0xbf, 0xbd, 0xee, 0xb2, 0xcd, 0x4c, 0xf0, 0x85, 0x68, 0xcd, + 0x3c, 0x77, 0xfa, 0x7e, 0x60, 0x97, 0x4e, 0x35, 0xc8, 0x14, 0xc1, 0xf7, + 0xf3, 0xb0, 0x1b, 0x29, 0x36, 0xe4, 0x49, 0x44, 0x19, 0xda, 0x32, 0x1b, + 0x4e, 0x7e, 0xd6, 0xa1, 0x94, 0x04, 0x38, 0x53, 0xa3, 0x1b, 0x5c, 0xdc, + 0xb2, 0x3e, 0xd6, 0xaf, 0x30, 0x4a, 0xdc, 0xd2, 0xfc, 0xce, 0xb3, 0x20, + 0x63, 0xb3, 0x67, 0xad, 0x74, 0xff, 0x1b, 0xb8, 0xb5, 0x02, 0x16, 0x11, + 0x2c, 0x0f, 0x55, 0x37, 0xff, 0x01, 0x3c, 0x55, 0x45, 0xf0, 0x86, 0x11, + 0x7d, 0xdd, 0x57, 0xe9, 0x62, 0x5b, 0xe7, 0xd3, 0x2a, 0x67, 0x62, 0x05, + 0x16, 0xb1, 0x1f, 0xf8, 0x41, 0xce, 0x56, 0x20, 0xa4, 0x11, 0xdc, 0xb7, + 0xc5, 0xff, 0x2f, 0x4f, 0xa8, 0xff, 0x97, 0x37, 0xea, 0xaf, 0xfe, 0x33, + 0xd2, 0x68, 0x49, 0xca, 0xd0, 0x10, 0xb1, 0x27, 0x85, 0x9e, 0xe9, 0xab, + 0x74, 0x6d, 0xcd, 0x0f, 0x4d, 0xa6, 0xb2, 0xd4, 0x4a, 0x47, 0xa2, 0xd4, + 0xac, 0x50, 0x17, 0x90, 0x47, 0xbf, 0x1b, 0x6f, 0x25, 0x91, 0x1b, 0x4b, + 0xb9, 0x5c, 0x57, 0xc5, 0x2f, 0xe5, 0x71, 0xe1, 0x30, 0xce, 0x4d, 0x72, + 0xdd, 0x97, 0x23, 0x41, 0x36, 0xc1, 0xdf, 0x74, 0xa9, 0x5b, 0xa8, 0xee, + 0x1a, 0x15, 0x29, 0x0e, 0x24, 0x11, 0xef, 0xbc, 0x43, 0x59, 0x5c, 0x2d, + 0x13, 0xed, 0x80, 0x4b, 0xf6, 0x76, 0xf6, 0x01, 0x6c, 0xfe, 0xcb, 0xad, + 0x9f, 0x3f, 0xad, 0x14, 0xd0, 0x5d, 0xe0, 0x36, 0x73, 0x2d, 0x38, 0x19, + 0x40, 0xe3, 0x27, 0x63, 0x9b, 0x34, 0x47, 0x02, 0x1e, 0x69, 0x7f, 0xd6, + 0xc5, 0xd0, 0x75, 0x70, 0xd2, 0xe6, 0x3c, 0x7c, 0xdb, 0xec, 0x41, 0xa5, + 0x69, 0x44, 0x8b, 0xed, 0x9d, 0x16, 0x30, 0x92, 0x4e, 0x94, 0xd5, 0x31, + 0x49, 0x0f, 0x1a, 0x9e, 0x90, 0x16, 0x09, 0xff, 0xce, 0xa2, 0xd4, 0x4c, + 0x64, 0x39, 0xb6, 0x8e, 0x3a, 0x12, 0x35, 0x53, 0x06, 0x52, 0xf1, 0x2a, + 0xa2, 0x49, 0x1b, 0xff, 0xb6, 0x29, 0xf1, 0x01, 0x4a, 0x8c, 0xf8, 0x9d, + 0xcf, 0x70, 0xd5, 0x13, 0xae, 0x72, 0x01, 0x6b, 0x3d, 0x60, 0x41, 0x87, + 0x8b, 0x1a, 0x17, 0x27, 0xc7, 0x51, 0xba, 0x27, 0x21, 0xdf, 0xb2, 0x8a, + 0x76, 0x65, 0x52, 0xe5, 0x64, 0x8a, 0x2e, 0x65, 0x2b, 0xb4, 0xf8, 0x3d, + 0x18, 0x39, 0xfa, 0xf1, 0x7b, 0xee, 0x2e, 0xaa, 0x7d, 0xed, 0xab, 0x27, + 0x98, 0x58, 0x41, 0x2d, 0xd0, 0x4b, 0xe5, 0x75, 0xa1, 0x88, 0xc0, 0x88, + 0x8f, 0x7c, 0xa4, 0x16, 0x95, 0x9d, 0x65, 0xef, 0x95, 0xd2, 0xd6, 0x6f, + 0x03, 0xc2, 0x45, 0x35, 0xbc, 0x64, 0xd5, 0x53, 0xb6, 0xa5, 0xd6, 0x95, + 0x50, 0x63, 0xe6, 0x44, 0x23, 0xa9, 0x59, 0x1c, 0x7d, 0x63, 0xa3, 0x27, + 0x9b, 0xca, 0x5d, 0x4a, 0x7e, 0xf8, 0x93, 0xaa, 0x7f, 0x6a, 0x46, 0x4b, + 0x8e, 0x6a, 0x9a, 0xc9, 0x69, 0xeb, 0x32, 0x12, 0xac, 0x25, 0x56, 0x67, + 0x9e, 0xb9, 0x0f, 0x9c, 0xb5, 0xca, 0x0b, 0xd9, 0x72, 0x8c, 0x26, 0x9b, + 0x6d, 0xbf, 0xd0, 0x33, 0xd9, 0xa5, 0x53, 0xed, 0xea, 0x66, 0xab, 0x4f, + 0x20, 0x6e, 0x8c, 0xc2, 0xac, 0x20, 0x97, 0x0e, 0xcc, 0xad, 0x17, 0x8e, + 0xe2, 0xdc, 0xda, 0xef, 0x2e, 0x43, 0x62, 0xb0, 0xc0, 0x7f, 0x82, 0x20, + 0xe2, 0x80, 0xbe, 0xb6, 0x28, 0xdb, 0x1f, 0x1a, 0x60, 0xe7, 0x0d, 0xba, + 0x6e, 0xc0, 0xfa, 0x36, 0x16, 0x3b, 0xc4, 0xac, 0x5f, 0xf4, 0xa2, 0x6a, + 0x3f, 0x0f, 0x50, 0xf9, 0xe6, 0xb5, 0x89, 0x80, 0x66, 0xda, 0x1a, 0x76, + 0xe2, 0xd3, 0x65, 0x77, 0x8a, 0xf2, 0xc7, 0xe8, 0x46, 0x9f, 0xa8, 0x36, + 0x7b, 0x80, 0xa6, 0xd4, 0xb5, 0xe3, 0x89, 0x15, 0x83, 0x12, 0xf8, 0xeb, + 0x02, 0x93, 0xa5, 0x44, 0x2c, 0xf0, 0x7d, 0xb7, 0x47, 0x95, 0x7a, 0x4c, + 0x70, 0x6f, 0xba, 0xf8, 0x43, 0x62, 0x50, 0x2e, 0x6e, 0xf3, 0x2e, 0x1d, + 0xff, 0xd8, 0xc5, 0xf5, 0x02, 0xcb, 0x55, 0xf3, 0x72, 0xff, 0x16, 0x94, + 0x95, 0x28, 0x5a, 0x8c, 0xe1, 0xad, 0x63, 0x9e, 0xfd, 0x8e, 0xcb, 0x3d, + 0x6a, 0x3e, 0x6a, 0xe4, 0x23, 0x5c, 0x79, 0x2e, 0x18, 0x01, 0x14, 0x4f, + 0xb8, 0x83, 0xed, 0xfe, 0x07, 0x84, 0xf6, 0x39, 0x15, 0xdb, 0xe3, 0xfe, + 0x9c, 0xc9, 0x74, 0xc9, 0xb5, 0x8f, 0x02, 0x32, 0x03, 0x25, 0x93, 0x36, + 0x20, 0x4f, 0xcf, 0x80, 0xc4, 0x1c, 0x44, 0x5c, 0xb2, 0xbb, 0x01, 0x96, + 0x33, 0xd1, 0x16, 0x07, 0x81, 0x7c, 0x67, 0x1b, 0x65, 0x5e, 0x08, 0x9a, + 0xb8, 0xd0, 0xb3, 0xf2, 0x6d, 0x16, 0x2a, 0x1e, 0xe5, 0x44, 0x1b, 0xcd, + 0x1f, 0xf7, 0x14, 0x2e, 0xdb, 0x08, 0xc4, 0xd4, 0xb5, 0x8c, 0x40, 0x42, + 0xdb, 0x27, 0x20, 0x6d, 0x45, 0x65, 0xbf, 0x17, 0x45, 0x84, 0xa1, 0xc0, + 0x52, 0xc6, 0x52, 0x98, 0x5e, 0x3b, 0x6a, 0xeb, 0xd4, 0x64, 0xae, 0x52, + 0x52, 0xe1, 0x95, 0xa6, 0x0e, 0xb8, 0x62, 0x92, 0x1a, 0x18, 0x52, 0x29, + 0xa3, 0x78, 0x60, 0xd7, 0x97, 0x9d, 0x5c, 0x26, 0xd9, 0x56, 0x2a, 0x3e, + 0xf3, 0xaf, 0x79, 0x7f, 0xfe, 0xc6, 0xb9, 0xc1, 0x7f, 0x83, 0xab, 0x2b, + 0xed, 0x92, 0xe0, 0x30, 0x98, 0xf5, 0xda, 0xf6, 0x0b, 0x6e, 0x85, 0xed, + 0x38, 0x21, 0x64, 0x30, 0x0c, 0x04, 0xfd, 0x29, 0x4c, 0x7e, 0x32, 0x3f, + 0x91, 0x54, 0x97, 0x64, 0x19, 0x38, 0x8b, 0xda, 0xef, 0x65, 0xe2, 0xe4, + 0xec, 0xe5, 0xed, 0x42, 0x20, 0x9c, 0x3a, 0xa5, 0x55, 0xd8, 0x0b, 0x94, + 0xe2, 0xff, 0xe7, 0x71, 0x23, 0xb2, 0xfb, 0xe7, 0xfc, 0x4d, 0x93, 0x7d, + 0x00, 0xf9, 0xbe, 0x00, 0xaa, 0x99, 0x44, 0x3b, 0xd4, 0x1e, 0xcd, 0xb1, + 0x20, 0x65, 0xde, 0x6c, 0xb4, 0x10, 0x71, 0xed, 0x89, 0xff, 0x74, 0x70, + 0x64, 0x69, 0x75, 0xd1, 0xe7, 0x9e, 0x90, 0x80, 0x37, 0xa0, 0x3c, 0xcc, + 0x60, 0x19, 0x87, 0xed, 0x96, 0xfe, 0x06, 0xa5, 0x3e, 0xa4, 0x9c, 0x82, + 0xaf, 0xf1, 0x5d, 0xa4, 0x80, 0xcc, 0x8d, 0xb7, 0x84, 0x2e, 0xc1, 0x88, + 0xc7, 0x14, 0x59, 0x86, 0x23, 0xb7, 0x96, 0x62, 0xf3, 0xb6, 0x66, 0x77, + 0xdb, 0x72, 0x30, 0xd5, 0x98, 0xea, 0x71, 0xf9, 0x56, 0xe5, 0x2f, 0xb2, + 0x06, 0x59, 0x52, 0xc8, 0x3c, 0x06, 0x01, 0x91, 0x32, 0xd1, 0x52, 0x4d, + 0x00, 0x1e, 0x96, 0x8f, 0x05, 0xf0, 0x04, 0x71, 0x0f, 0xc0, 0x43, 0xa1, + 0xa8, 0x04, 0x7d, 0xb8, 0xfb, 0x3c, 0xd3, 0xfa, 0x2e, 0x03, 0x3e, 0x1b, + 0xd8, 0x97, 0xad, 0xd1, 0x5a, 0xbf, 0xb0, 0x1b, 0x88, 0xa8, 0x41, 0xa7, + 0xc0, 0x79, 0x66, 0x71, 0xea, 0xbc, 0x56, 0xf2, 0xb6, 0x4f, 0x61, 0x25, + 0xbe, 0xd0, 0x49, 0x63, 0x15, 0x06, 0x8e, 0x78, 0xad, 0x18, 0x64, 0xe9, + 0x7d, 0x87, 0x38, 0x07, 0xb7, 0xad, 0x44, 0xbc, 0xc4, 0x32, 0xdd, 0xb6, + 0xf3, 0x00, 0xf0, 0xb3, 0x5e, 0xc5, 0xc2, 0x7b, 0xba, 0x93, 0xdf, 0xcf, + 0xa4, 0xce, 0xd9, 0x01, 0x67, 0x0e, 0xd5, 0xa2, 0x1b, 0xf0, 0x16, 0x49, + 0x9f, 0xde, 0xbd, 0x3a, 0x88, 0xed, 0x4f, 0x40, 0xe5, 0xb6, 0x09, 0xee, + 0x75, 0xa1, 0x73, 0x8c, 0x71, 0x14, 0xc3, 0x7e, 0x0c, 0x0a, 0x54, 0xd7, + 0xcf, 0x9c, 0xa6, 0x56, 0x53, 0x27, 0xcb, 0x26, 0x26, 0x3f, 0x2f, 0x93, + 0xcf, 0xdc, 0xe7, 0x65, 0x21, 0xd0, 0x3a, 0x28, 0xa8, 0x7c, 0xa9, 0xde, + 0xce, 0xdb, 0xbc, 0x4b, 0x02, 0xa4, 0x3e, 0xc3, 0xa6, 0xca, 0x30, 0xf4, + 0x71, 0xee, 0x21, 0xd6, 0x65, 0x8a, 0x89, 0xda, 0x51, 0x6d, 0xbd, 0x56, + 0x30, 0xa5, 0x8c, 0x01, 0x7f, 0x2a, 0xa7, 0x33, 0x55, 0x43, 0xa6, 0xde, + 0xe6, 0x10, 0xab, 0x90, 0x84, 0xa2, 0x85, 0x5e, 0x0d, 0xc6, 0x3d, 0x8b, + 0x20, 0x38, 0x49, 0xb5, 0xc8, 0x20, 0xaa, 0xee, 0x47, 0x47, 0x09, 0x9e, + 0x6d, 0xd6, 0xec, 0xf7, 0xbb, 0x2f, 0xd3, 0x4e, 0x4f, 0xb0, 0x6f, 0xa0, + 0x6f, 0xa2, 0xb1, 0xc3, 0x49, 0x23, 0xb8, 0x75, 0xec, 0x84, 0xc7, 0xa3, + 0x7a, 0xe9, 0x7f, 0x87, 0xcb, 0xe5, 0x4a, 0x2e, 0x0f, 0x08, 0x68, 0xe1, + 0x6e, 0x7f, 0x0f, 0x19, 0x37, 0xe7, 0xc5, 0xd5, 0x30, 0x20, 0x29, 0x64, + 0x18, 0x61, 0xfe, 0x2b, 0x47, 0xa5, 0x54, 0x80, 0x2e, 0x73, 0xa8, 0x14, + 0xf6, 0xff, 0xd8, 0xcb, 0xd6, 0x03, 0x0c, 0xb9, 0x7b, 0x36, 0x6b, 0x16, + 0xc8, 0x23, 0x6b, 0x55, 0x07, 0xd4, 0x6e, 0xe4, 0x7f, 0x19, 0x63, 0xe3, + 0xf3, 0x4e, 0x68, 0x8d, 0x37, 0x69, 0x29, 0xcd, 0x92, 0x3b, 0xa1, 0xa5, + 0xea, 0x20, 0x69, 0xcd, 0xbe, 0x87, 0x8f, 0xeb, 0x99, 0xa3, 0x58, 0x4a, + 0xb9, 0x9a, 0x9b, 0x5c, 0x33, 0x92, 0x51, 0x98, 0xb1, 0x5d, 0x58, 0x81, + 0x71, 0x5b, 0xfa, 0x3c, 0x7e, 0x77, 0x64, 0x6d, 0x8c, 0xef, 0xfa, 0x0e, + 0xa4, 0x6e, 0x69, 0x74, 0xe7, 0x76, 0xcb, 0xed, 0x6d, 0x3a, 0x2c, 0xdf, + 0xc3, 0x60, 0xc0, 0xd3, 0x94, 0xc8, 0x6f, 0xcf, 0x46, 0xda, 0x37, 0x73, + 0x07, 0x21, 0x7b, 0x79, 0x18, 0x19, 0x44, 0x77, 0x2d, 0xce, 0x01, 0xc9, + 0xb6, 0x98, 0xc2, 0x73, 0xa3, 0xe4, 0x8d, 0xf6, 0xa5, 0xd4, 0x75, 0x80, + 0xcd, 0xf1, 0x92, 0x37, 0x48, 0xb1, 0x7d, 0x58, 0x84, 0x62, 0xa0, 0x69, + 0x7b, 0xd1, 0x9a, 0xf9, 0x60, 0xc5, 0xdd, 0xb8, 0x15, 0x41, 0x72, 0x0e, + 0xed, 0x93, 0xf2, 0x33, 0xf5, 0x22, 0x16, 0x6f, 0x87, 0x32, 0xd0, 0xca, + 0x21, 0x5f, 0x67, 0xa5, 0x5b, 0x2f, 0x06, 0x2a, 0xf6, 0xf7, 0xd3, 0x22, + 0x30, 0x7c, 0xaf, 0x71, 0x09, 0xed, 0x42, 0x80, 0x0b, 0x10, 0x4d, 0xae, + 0xc0, 0x07, 0x88, 0x75, 0xb3, 0x67, 0x8d, 0x1b, 0x4b, 0xbf, 0x60, 0x82, + 0xae, 0xb1, 0x74, 0xb7, 0x4d, 0x03, 0xab, 0xf4, 0x86, 0x97, 0x6d, 0xd9, + 0x44, 0x8f, 0xc5, 0x4a, 0x4a, 0x4b, 0x48, 0x6c, 0xc9, 0x57, 0xf6, 0x57, + 0x1e, 0x65, 0x02, 0xa8, 0x8d, 0xdc, 0x4a, 0x33, 0x0c, 0x39, 0xa3, 0xcb, + 0x67, 0x7f, 0x95, 0x51, 0x83, 0x46, 0x77, 0x32, 0xf2, 0x4c, 0x4e, 0x1f, + 0x92, 0x95, 0xe4, 0x32, 0xb0, 0x63, 0xea, 0xa5, 0x27, 0x4c, 0x50, 0x42, + 0x3f, 0x32, 0xa5, 0xb6, 0x0e, 0x39, 0x59, 0x53, 0x3a, 0x25, 0xad, 0x18, + 0x10, 0x03, 0xdd, 0x6e, 0x8a, 0x1f, 0x45, 0xdb, 0x5a, 0x60, 0x68, 0x98, + 0x33, 0x42, 0xe5, 0xc4, 0x71, 0xa9, 0xb3, 0x34, 0xd1, 0x89, 0x42, 0xe1, + 0xee, 0xba, 0x9b, 0x8d, 0x6b, 0x0c, 0xbe, 0x97, 0x88, 0x4f, 0x14, 0xb2, + 0x4c, 0x53, 0x0e, 0x7f, 0xcc, 0xc4, 0x8a, 0xb5, 0xd8, 0x93, 0x95, 0x92, + 0xf3, 0x7b, 0xd6, 0xdb, 0x75, 0xd7, 0x8b, 0xe5, 0x25, 0x93, 0x1f, 0x2c, + 0xbb, 0x62, 0x7b, 0x7d, 0x00, 0x1f, 0x93, 0xca, 0xd7, 0xe3, 0x03, 0x0a, + 0xee, 0xbd, 0x41, 0x67, 0x9b, 0x8f, 0xf2, 0xc2, 0xaf, 0xa6, 0x3c, 0xac, + 0x45, 0xb6, 0xb5, 0x07, 0xef, 0x49, 0xba, 0xf7, 0xd6, 0x5d, 0x06, 0x38, + 0x6a, 0x29, 0x3c, 0xdd, 0x0a, 0x2c, 0x94, 0x94, 0x8f, 0x01, 0x0f, 0xdb, + 0xc5, 0xaa, 0x86, 0xb3, 0x9b, 0x2f, 0x9f, 0xeb, 0x22, 0x68, 0xf5, 0x0d, + 0xe3, 0x04, 0x50, 0x3a, 0xdd, 0x98, 0xb1, 0x9d, 0x50, 0x8f, 0x33, 0xe4, + 0x85, 0x3b, 0xf2, 0xb4, 0x33, 0xa2, 0x61, 0x5c, 0x67, 0x50, 0x48, 0x86, + 0xc0, 0x94, 0xec, 0xa5, 0xea, 0xeb, 0x76, 0x65, 0xac, 0xed, 0xa9, 0x5b, + 0xdd, 0x0d, 0x13, 0x0f, 0x83, 0x65, 0x38, 0xee, 0xb0, 0x51, 0x8b, 0x78, + 0x1b, 0xdb, 0xeb, 0x57, 0x1a, 0x38, 0x93, 0xc4, 0x5d, 0x07, 0xc1, 0x2d, + 0x98, 0xab, 0xb6, 0x8f, 0x09, 0x64, 0xd0, 0x95, 0xed, 0x82, 0x21, 0xca, + 0xb1, 0xe6, 0x78, 0xa9, 0x9e, 0x16, 0x21, 0x89, 0x2a, 0x1a, 0x66, 0xa0, + 0x00, 0xaf, 0xe6, 0x36, 0x68, 0x5f, 0x15, 0xb4, 0xa8, 0x62, 0xf1, 0x5b, + 0x34, 0x40, 0xa6, 0x62, 0xf1, 0xc0, 0xbe, 0x5c, 0xfc, 0x25, 0x4a, 0xbd, + 0x6a, 0x52, 0x5c, 0x5f, 0x23, 0x33, 0xaa, 0xe3, 0x0a, 0x27, 0xf0, 0x18, + 0xe5, 0x6b, 0x1c, 0x11, 0xd3, 0x1e, 0x83, 0x19, 0xb9, 0xab, 0xee, 0x23, + 0xf9, 0x12, 0xa2, 0x82, 0x65, 0x0f, 0xf6, 0x41, 0x59, 0xc2, 0x1f, 0xd4, + 0x6e, 0x08, 0x52, 0xad, 0x5d, 0xda, 0x0c, 0x75, 0xcc, 0x3c, 0x96, 0xf2, + 0x26, 0xfb, 0xb2, 0x48, 0xcf, 0x41, 0x72, 0x36, 0x26, 0x86, 0xfc, 0x60, + 0xaa, 0xc1, 0x42, 0xb9, 0x08, 0x88, 0xe4, 0x23, 0x4f, 0x31, 0x54, 0xaf, + 0xe1, 0xa0, 0x26, 0x9a, 0x02, 0xbd, 0xdc, 0x1b, 0x38, 0x3a, 0xee, 0x17, + 0x67, 0xf5, 0x7e, 0xaf, 0x09, 0xe9, 0x25, 0xe3, 0xe1, 0xea, 0x50, 0xf7, + 0xaa, 0xf6, 0xda, 0xcc, 0x3a, 0x80, 0xa6, 0x38, 0x3a, 0x5f, 0x34, 0x5a, + 0x59, 0x56, 0x15, 0xb2, 0xc6, 0x39, 0x50, 0x4c, 0x0a, 0x29, 0xa0, 0x81, + 0xf4, 0xaf, 0x77, 0x7b, 0x5f, 0xc9, 0x71, 0x91, 0x0c, 0xe6, 0x5c, 0xe1, + 0x2e, 0xab, 0x31, 0x64, 0x29, 0x06, 0xd1, 0x70, 0x67, 0x26, 0x60, 0x05, + 0x10, 0x14, 0xc3, 0xb9, 0xf1, 0xa1, 0x64, 0x9d, 0x51, 0x85, 0x25, 0x11, + 0x8e, 0xf8, 0x39, 0x0d, 0x3a, 0xa0, 0x03, 0x11, 0x4d, 0xf0, 0x8c, 0x84, + 0xc0, 0x07, 0xb1, 0xf7, 0x8b, 0x7b, 0x79, 0x93, 0x79, 0x7e, 0x82, 0x0e, + 0x78, 0x3f, 0x97, 0x6b, 0x6a, 0x2a, 0x3a, 0xea, 0x65, 0x05, 0x60, 0xb6, + 0xa2, 0xf4, 0x32, 0xdb, 0xa3, 0x58, 0x65, 0xbf, 0xe1, 0x1b, 0x1e, 0x17, + 0x13, 0x28, 0x40, 0x44, 0xf5, 0x8b, 0xf3, 0x42, 0xef, 0xbe, 0x9e, 0xa7, + 0x40, 0x97, 0xb6, 0x77, 0x43, 0x79, 0x71, 0x0f, 0x62, 0x78, 0x21, 0xc5, + 0xab, 0x6f, 0x9b, 0xcd, 0x66, 0x82, 0x99, 0xa3, 0x61, 0xaf, 0xaf, 0x59, + 0xd0, 0xce, 0x29, 0x37, 0xb7, 0x73, 0x0c, 0xd5, 0x6e, 0x7a, 0x6a, 0xdf, + 0xb6, 0xd8, 0x71, 0x90, 0xbb, 0xba, 0x41, 0x72, 0xab, 0x13, 0x8b, 0x5e, + 0x91, 0x4a, 0xbd, 0x2e, 0x78, 0xbd, 0xc4, 0xa3, 0xd9, 0x60, 0x22, 0x5b, + 0x8c, 0xac, 0xa8, 0xf7, 0x26, 0x11, 0x23, 0x9d, 0x42, 0x6b, 0x3a, 0x78, + 0x6e, 0x66, 0x1f, 0xa1, 0x28, 0x78, 0x4d, 0x12, 0x8e, 0xc9, 0x55, 0x54, + 0xfc, 0x4b, 0x8c, 0xa8, 0x6f, 0x6a, 0x95, 0x47, 0xce, 0xc5, 0x73, 0xc4, + 0x05, 0x43, 0xf3, 0xd2, 0x3a, 0xc5, 0x86, 0x9f, 0x3d, 0x0a, 0x13, 0x96, + 0x2c, 0x96, 0x4b, 0x52, 0xc5, 0x71, 0x1f, 0x90, 0x15, 0xb6, 0x79, 0xa3, + 0xd9, 0x30, 0x8a, 0xb9, 0xcf, 0x24, 0x3d, 0x9a, 0x81, 0x2f, 0xf0, 0x46, + 0xbe, 0x49, 0x00, 0xfd, 0xc8, 0x4c, 0x1f, 0xec, 0x40, 0x71, 0x8b, 0x49, + 0x2b, 0x93, 0x07, 0x3f, 0x9d, 0xc1, 0x4c, 0x02, 0x76, 0x4d, 0x0d, 0x45, + 0x07, 0x33, 0x0a, 0x98, 0xd8, 0xa4, 0xe4, 0x8e, 0x22, 0xb8, 0x5e, 0x69, + 0x23, 0xf4, 0xa5, 0x3f, 0x0d, 0x3c, 0x02, 0x84, 0x0d, 0x58, 0xdc, 0x43, + 0xc4, 0xfd, 0xb5, 0xe7, 0xbc, 0xbb, 0x9c, 0xc8, 0x5e, 0xa8, 0xd7, 0xab, + 0xf0, 0xe4, 0xa9, 0xc3, 0x27, 0x6d, 0x11, 0xd7, 0x8c, 0xcf, 0x6e, 0x9b, + 0x89, 0x19, 0x27, 0xd8, 0x29, 0x69, 0x49, 0x66, 0xe0, 0xc3, 0xc0, 0xca, + 0x8c, 0x56, 0x52, 0x26, 0x8e, 0x52, 0x0f, 0xf0, 0xd5, 0x05, 0x27, 0x70, + 0x69, 0x51, 0xc2, 0x3a, 0x81, 0xf7, 0x7c, 0x76, 0x13, 0xf3, 0x82, 0x76, + 0xbb, 0x74, 0x53, 0xba, 0x00, 0x98, 0x81, 0x1e, 0x75, 0x68, 0xe1, 0x68, + 0x3c, 0x05, 0xd1, 0x3f, 0x6d, 0x21, 0xe2, 0xdc, 0x30, 0x4b, 0xa3, 0x3f, + 0xa7, 0x3b, 0x66, 0xb7, 0xb9, 0x08, 0x6f, 0x84, 0x62, 0xbd, 0x60, 0xee, + 0x82, 0xf7, 0x7f, 0x98, 0x52, 0x09, 0x99, 0x12, 0x4b, 0x6f, 0x52, 0x18, + 0x04, 0x5b, 0xd2, 0x37, 0xd6, 0x04, 0xab, 0x97, 0x4b, 0xd1, 0x18, 0xa9, + 0xab, 0x5d, 0x2c, 0x7a, 0x4e, 0x32, 0xef, 0xd9, 0x0e, 0x2f, 0x72, 0x04, + 0x8d, 0xff, 0x49, 0xbb, 0x8e, 0x47, 0xae, 0xe8, 0x7d, 0x7b, 0xe3, 0xd9, + 0x9c, 0x0e, 0xc0, 0xe0, 0x6b, 0x4b, 0xe1, 0xf7, 0x44, 0xc7, 0xb6, 0xf1, + 0xbd, 0xaa, 0x06, 0x03, 0x84, 0x1b, 0x6c, 0xf1, 0x0b, 0x62, 0x83, 0xd4, + 0x1c, 0x69, 0xf8, 0x06, 0x9d, 0x11, 0xa7, 0x99, 0x73, 0x0f, 0x24, 0xd1, + 0xdd, 0xd5, 0x21, 0xab, 0x5b, 0x24, 0x37, 0xde, 0xe2, 0x0c, 0xfc, 0x83, + 0xb5, 0x26, 0xa0, 0x4e, 0x78, 0xbd, 0xb2, 0x03, 0x7c, 0xb1, 0xa9, 0x34, + 0xe9, 0x1a, 0x6f, 0x0e, 0x84, 0x37, 0x6e, 0x8e, 0x17, 0xde, 0xca, 0xc4, + 0x6f, 0x8c, 0xcd, 0x32, 0x99, 0x9a, 0x20, 0xde, 0x94, 0x88, 0xd9, 0x85, + 0x66, 0x27, 0x90, 0x30, 0x4a, 0xf9, 0xb6, 0x54, 0x23, 0xb4, 0x51, 0xd8, + 0xbc, 0x3e, 0x70, 0xae, 0xda, 0x0f, 0x1e, 0x46, 0x09, 0x93, 0xa6, 0x14, + 0x7d, 0x15, 0xca, 0x0d, 0xf7, 0x0e, 0x14, 0x1f, 0xa6, 0x8c, 0xc6, 0xcd, + 0x03, 0xb4, 0x46, 0x14, 0x4c, 0x2c, 0x62, 0x7e, 0x0b, 0xed, 0x5f, 0xe6, + 0x90, 0x29, 0x2a, 0x35, 0xa1, 0x41, 0x47, 0x08, 0x65, 0x4d, 0x3a, 0x01, + 0xe3, 0xbc, 0xe6, 0x85, 0x26, 0x6a, 0x2f, 0x6e, 0x5a, 0xe2, 0xf6, 0x8b, + 0xcc, 0x73, 0x56, 0x2e, 0xb3, 0xbf, 0xa3, 0x10, 0x22, 0xcd, 0xf7, 0x64, + 0xb6, 0xdf, 0xd7, 0xaa, 0x01, 0x30, 0x8d, 0x5c, 0x99, 0xfd, 0x1f, 0x11, + 0xd5, 0x01, 0x91, 0x4a, 0xf6, 0x3f, 0x0d, 0x9d, 0xc4, 0xb7, 0xe1, 0xd7, + 0xc7, 0x0b, 0xaa, 0x26, 0xc2, 0xc4, 0x71, 0x0c, 0x14, 0xfd, 0x1c, 0x7b, + 0x96, 0x7c, 0x34, 0xaa, 0xf3, 0xd8, 0xcf, 0xb6, 0x16, 0xeb, 0xa3, 0xf7, + 0xf2, 0x7e, 0x9e, 0xe7, 0xfe, 0xce, 0x43, 0x12, 0x72, 0xe3, 0x36, 0x4c, + 0xee, 0x28, 0xc8, 0xb9, 0x99, 0x25, 0x74, 0xfb, 0xa6, 0x73, 0x3b, 0x5e, + 0x56, 0x2c, 0x7e, 0x9e, 0x43, 0x8b, 0xc8, 0x92, 0x83, 0xae, 0x10, 0xd6, + 0x85, 0xb9, 0xec, 0x26, 0x69, 0x15, 0x43, 0xb4, 0x06, 0xf7, 0xcd, 0xc4, + 0xd7, 0x6e, 0x14, 0x55, 0x68, 0x01, 0x0a, 0xda, 0xe7, 0x75, 0x75, 0x21, + 0x55, 0xbf, 0x08, 0x6a, 0x9f, 0x8b, 0x7c, 0x3e, 0x59, 0xbf, 0xe1, 0x29, + 0x4c, 0x04, 0x3e, 0xd9, 0x64, 0xfe, 0x79, 0x4e, 0x92, 0xbf, 0x80, 0xf4, + 0x65, 0xab, 0x13, 0xf7, 0x7c, 0x49, 0x75, 0x92, 0x51, 0x6f, 0x8b, 0x9d, + 0x9c, 0xe8, 0x6b, 0x3e, 0xa9, 0xf1, 0xab, 0x49, 0x79, 0xe0, 0xb9, 0x15, + 0x60, 0xe1, 0x08, 0xac, 0xfd, 0x96, 0xa0, 0x9d, 0x79, 0xd3, 0x84, 0xdb, + 0xc2, 0xc0, 0xa2, 0x3f, 0x8d, 0x20, 0x9a, 0xbc, 0x3b, 0x52, 0xc2, 0xbb, + 0x13, 0x9d, 0x7a, 0xd7, 0x4c, 0x46, 0xc7, 0xda, 0xa2, 0x47, 0xe8, 0xd8, + 0x2a, 0x2c, 0x1c, 0x07, 0xd4, 0x68, 0x14, 0x23, 0xcf, 0x15, 0x98, 0x22, + 0xfb, 0xfa, 0x0a, 0x2a, 0x65, 0x24, 0xf0, 0x0c, 0xe3, 0xb7, 0xa5, 0xf4, + 0x2c, 0x2f, 0xb7, 0xc8, 0xcf, 0x1a, 0x98, 0x62, 0x66, 0x13, 0x8e, 0x44, + 0xde, 0x30, 0x7e, 0xe0, 0x23, 0xac, 0x68, 0xf8, 0x3d, 0xf4, 0xb0, 0x7a, + 0x41, 0x17, 0xef, 0x19, 0xb9, 0xfb, 0x55, 0xe6, 0x29, 0xf4, 0x5f, 0xc2, + 0xe0, 0x47, 0x65, 0x44, 0x2d, 0x46, 0xe1, 0x0f, 0x25, 0x9b, 0xaa, 0xb0, + 0xc2, 0xbb, 0x1d, 0x2f, 0x78, 0x8e, 0x3e, 0xfa, 0xe1, 0xdc, 0x67, 0xe6, + 0xbf, 0x0d, 0x19, 0x50, 0x76, 0x49, 0x28, 0xd3, 0xeb, 0xe1, 0x7a, 0xbf, + 0xbc, 0x8d, 0x5c, 0x99, 0x27, 0xae, 0x99, 0xa9, 0x89, 0x2c, 0x08, 0x3c, + 0xc5, 0x46, 0x77, 0x1f, 0x3c, 0x00, 0x8c, 0xbe, 0xcf, 0x1a, 0x3c, 0x18, + 0x0f, 0x40, 0xff, 0x57, 0x9c, 0x8d, 0xe5, 0xae, 0x6c, 0x31, 0x14, 0xd7, + 0x87, 0x21, 0x3e, 0x66, 0x31, 0x07, 0x69, 0x33, 0x9c, 0x16, 0xca, 0x8e, + 0x2f, 0xb9, 0xc3, 0xfa, 0xd2, 0x98, 0xc0, 0x48, 0x84, 0x6c, 0x9c, 0x4f, + 0xff, 0x2a, 0x44, 0xd4, 0x34, 0x11, 0x69, 0x06, 0x17, 0xbd, 0x63, 0x1f, + 0x0d, 0xe7, 0x27, 0x2f, 0xd4, 0x4a, 0x2b, 0x9f, 0xf0, 0x8a, 0xa8, 0xa5, + 0x13, 0x9b, 0x46, 0x3f, 0x01, 0x76, 0x09, 0xcf, 0x34, 0x5e, 0xa7, 0xd5, + 0x8c, 0x72, 0x40, 0x62, 0xa9, 0x24, 0xf9, 0x4b, 0x7e, 0xa0, 0x0e, 0x15, + 0x9a, 0x3b, 0xaf, 0xab, 0x22, 0xfc, 0x58, 0x76, 0x39, 0x93, 0x9c, 0x5c, + 0x90, 0x5d, 0x7f, 0x65, 0xbf, 0xba, 0x36, 0x28, 0xa8, 0x68, 0x66, 0x00, + 0x83, 0x2c, 0x51, 0x10, 0x7a, 0xe4, 0xd7, 0xa2, 0xd4, 0xce, 0x32, 0xca, + 0xc5, 0x89, 0xb8, 0x68, 0xcb, 0xca, 0xf9, 0xd7, 0xc0, 0xef, 0xc2, 0x8d, + 0xb4, 0xd0, 0xdf, 0x74, 0xbe, 0x7c, 0xa8, 0x56, 0x5b, 0xce, 0xb9, 0xde, + 0xec, 0x48, 0xcf, 0xed, 0xcb, 0xcb, 0xd1, 0x7d, 0x25, 0x59, 0x7d, 0x8b, + 0x32, 0x2c, 0x5d, 0x1d, 0xc9, 0x41, 0xe7, 0x39, 0x45, 0xa2, 0x4f, 0xeb, + 0x30, 0x45, 0xad, 0xbc, 0xf2, 0x24, 0xe9, 0xed, 0xd9, 0xc1, 0x6a, 0x59, + 0x68, 0xee, 0x46, 0x4f, 0xd7, 0x95, 0x13, 0x70, 0x01, 0xcb, 0xaa, 0x0c, + 0x52, 0x17, 0x37, 0xba, 0x23, 0xb9, 0xd5, 0x9b, 0x9e, 0x24, 0xe9, 0x7f, + 0xd4, 0x9f, 0xf8, 0x56, 0x1c, 0x37, 0x54, 0x21, 0xcf, 0x7f, 0x8a, 0x88, + 0x49, 0x1f, 0x78, 0xca, 0xf9, 0x5c, 0x6e, 0xf8, 0x7b, 0x4f, 0x70, 0x2c, + 0x0f, 0x1c, 0xd1, 0x48, 0xa2, 0x87, 0x2e, 0x47, 0x53, 0xf5, 0xf3, 0x68, + 0x97, 0xcc, 0x9f, 0x26, 0x8b, 0x28, 0xef, 0x21, 0xbc, 0x9f, 0x33, 0x38, + 0xd2, 0xae, 0x81, 0x5b, 0x45, 0x81, 0xca, 0x55, 0xfb, 0x30, 0x73, 0x79, + 0x12, 0x87, 0xd8, 0xe9, 0x88, 0x98, 0x3b, 0x22, 0xc3, 0x28, 0x8b, 0x17, + 0x6a, 0xa9, 0x57, 0xbd, 0x21, 0x91, 0x16, 0xc8, 0x97, 0x77, 0xe8, 0xba, + 0x1c, 0x67, 0x96, 0xa1, 0x4a, 0xab, 0x11, 0x29, 0x76, 0x13, 0x20, 0x42, + 0x6f, 0x92, 0xa6, 0xa4, 0xc3, 0x7f, 0x96, 0x29, 0x4a, 0x48, 0x1b, 0x17, + 0x4f, 0xc1, 0xe3, 0x8b, 0x5d, 0x3a, 0x9a, 0x3b, 0x9a, 0x35, 0x62, 0x54, + 0x23, 0xac, 0x52, 0xca, 0x7a, 0xb3, 0x3e, 0x25, 0x91, 0x83, 0x73, 0x3f, + 0xfb, 0x1c, 0x62, 0xab, 0xe9, 0xc4, 0x71, 0xc9, 0x7b, 0xb3, 0x8c, 0x2c, + 0x37, 0x90, 0x79, 0xd1, 0x66, 0x20, 0xab, 0xe2, 0x82, 0x5e, 0x53, 0x9f, + 0x46, 0xde, 0x53, 0x0b, 0x1f, 0x4e, 0x58, 0xdc, 0x83, 0xd0, 0xca, 0xd7, + 0xa3, 0x99, 0x80, 0xcc, 0x5c, 0xc5, 0x1f, 0x8b, 0x34, 0x9b, 0x01, 0xf9, + 0xfb, 0x35, 0x38, 0x43, 0x43, 0xe9, 0x5e, 0xdf, 0xc7, 0x30, 0x08, 0x2f, + 0x92, 0x31, 0x8b, 0x81, 0xd1, 0x61, 0xf0, 0x36, 0xfb, 0xbd, 0x8e, 0x54, + 0x2e, 0x5b, 0xa2, 0x1b, 0xde, 0x29, 0xec, 0x0d, 0xf8, 0x75, 0x98, 0x80, + 0x62, 0x53, 0xdd, 0xe1, 0xd1, 0x80, 0x27, 0xee, 0x55, 0xb4, 0x50, 0x0d, + 0xb1, 0xa3, 0xb2, 0x95, 0xc7, 0x37, 0x94, 0x2c, 0x11, 0x31, 0x9c, 0xac, + 0x44, 0xda, 0xdc, 0x56, 0xbb, 0x2e, 0xf9, 0xf2, 0x28, 0x58, 0x4e, 0x85, + 0x18, 0x29, 0x54, 0xfd, 0x22, 0x66, 0xa6, 0x0c, 0x0c, 0x25, 0x27, 0xd1, + 0xe7, 0x15, 0xa3, 0x88, 0x74, 0x00, 0x5b, 0xbd, 0x3c, 0x21, 0x6f, 0xd9, + 0x04, 0x45, 0x8c, 0x59, 0x4e, 0x08, 0x2d, 0xee, 0xe2, 0xc8, 0xe0, 0xfe, + 0x53, 0x62, 0x26, 0x0e, 0xe4, 0x97, 0x3f, 0x84, 0xd0, 0x2a, 0x71, 0xbe, + 0xd1, 0x96, 0x50, 0x34, 0xd0, 0x77, 0x5e, 0xb9, 0xb2, 0x73, 0x59, 0xfc, + 0xa1, 0x7f, 0xf5, 0xf8, 0xee, 0x6b, 0x22, 0xff, 0xe6, 0x31, 0xed, 0x9a, + 0x65, 0x7f, 0x72, 0x67, 0x8b, 0x70, 0xb9, 0xca, 0x02, 0x14, 0x0f, 0xb3, + 0xc8, 0x19, 0x26, 0x11, 0x1b, 0x3a, 0xb4, 0xc1, 0x47, 0x24, 0x7a, 0x37, + 0x47, 0x22, 0x67, 0x40, 0x19, 0xe1, 0x43, 0x22, 0xa1, 0x08, 0x7e, 0x75, + 0xbd, 0x2f, 0x7c, 0x52, 0xa3, 0x63, 0x59, 0x1c, 0xe2, 0x45, 0x7e, 0x0f, + 0xf4, 0xf7, 0xe1, 0x7d, 0xaf, 0xae, 0x1f, 0x85, 0x10, 0x24, 0x6d, 0x05, + 0x44, 0x6c, 0xf9, 0x4a, 0x8f, 0xed, 0x70, 0x2c, 0x4e, 0xf3, 0xcb, 0xca, + 0x0a, 0xa7, 0x72, 0x5e, 0x5e, 0x4d, 0x03, 0xc7, 0xeb, 0x8a, 0x26, 0xe3, + 0x99, 0xed, 0x01, 0xc7, 0x99, 0xb6, 0x0a, 0xb9, 0x94, 0x28, 0x1a, 0x09, + 0xed, 0x5c, 0xf6, 0x9a, 0xae, 0x99, 0xe7, 0x8b, 0xcd, 0x31, 0x88, 0xff, + 0x79, 0x84, 0x4b, 0x8c, 0x22, 0x04, 0x00, 0xcc, 0x80, 0x0e, 0x3c, 0xdb, + 0x73, 0xeb, 0x3f, 0xc4, 0xfa, 0x69, 0xe8, 0x12, 0x36, 0x81, 0xb9, 0x1a, + 0xf5, 0x54, 0xec, 0x10, 0xd9, 0xc9, 0xa7, 0xe5, 0xe4, 0x6b, 0x4d, 0x38, + 0x62, 0xaa, 0x0d, 0x67, 0xda, 0x7a, 0xba, 0x7e, 0xc6, 0x96, 0x1b, 0xb6, + 0x68, 0x2b, 0xe3, 0x5c, 0x3f, 0x36, 0x4e, 0x69, 0xba, 0xe8, 0xb4, 0x1c, + 0xa9, 0xe0, 0x44, 0x84, 0x80, 0x50, 0x5c, 0x85, 0x28, 0x61, 0x11, 0x52, + 0xc8, 0xbf, 0x8f, 0x3e, 0x26, 0xa7, 0xf9, 0xca, 0xba, 0xc5, 0x52, 0xab, + 0xe6, 0x9a, 0xab, 0x70, 0xd5, 0xfe, 0x3e, 0xa4, 0x6a, 0x9e, 0x54, 0xb2, + 0x7d, 0xbd, 0x7e, 0x4b, 0x88, 0xb2, 0x28, 0x19, 0xf8, 0xab, 0x62, 0xcc, + 0x2e, 0xb3, 0x47, 0xc1, 0x28, 0x90, 0x86, 0xdc, 0x19, 0x94, 0xcc, 0x9a, + 0xde, 0xf4, 0xae, 0x9c, 0x09, 0xcf, 0xcb, 0xe4, 0x2b, 0x53, 0x82, 0xfe, + 0x94, 0xa9, 0x1b, 0x08, 0x39, 0x3d, 0x5f, 0x36, 0x8d, 0x18, 0x6c, 0x86, + 0x3a, 0xd5, 0x9c, 0x1f, 0x81, 0xd4, 0xad, 0xb4, 0xdc, 0xf6, 0x76, 0x33, + 0x4d, 0xb4, 0x17, 0x7f, 0xbf, 0xf5, 0x14, 0xa1, 0x04, 0xd3, 0x05, 0x6a, + 0x8b, 0xee, 0xd7, 0x4d, 0xa6, 0x53, 0x10, 0x27, 0x8a, 0x2b, 0x65, 0xeb, + 0x86, 0x87, 0xf6, 0x42, 0x27, 0xc1, 0x1a, 0x7a, 0xf7, 0xb2, 0xaf, 0xf7, + 0xbf, 0x47, 0xc2, 0xe1, 0x21, 0xfe, 0x66, 0xd3, 0xdf, 0x94, 0x38, 0xa3, + 0x7a, 0xf5, 0xaa, 0xb5, 0x50, 0xe8, 0xcc, 0x26, 0x17, 0x58, 0x91, 0xca, + 0x5f, 0xa1, 0x2d, 0x2d, 0x83, 0x77, 0x4f, 0x87, 0x0b, 0xa4, 0xe3, 0x72, + 0x40, 0x94, 0x3d, 0xcf, 0x49, 0x35, 0x88, 0xad, 0xe1, 0x64, 0x8b, 0x0e, + 0x5c, 0xdb, 0x51, 0x21, 0xc3, 0x02, 0xc9, 0xf6, 0x8d, 0xce, 0xe0, 0x04, + 0x2c, 0x3b, 0x23, 0x55, 0xf2, 0x99, 0x14, 0xc3, 0x82, 0xb7, 0x84, 0xfb, + 0x83, 0x3d, 0x17, 0xbd, 0xb4, 0x32, 0xca, 0x8d, 0x1c, 0x20, 0xbc, 0x30, + 0xb2, 0xb3, 0x38, 0x4e, 0xe1, 0x25, 0x6c, 0xe9, 0xe3, 0x7d, 0x5e, 0xf7, + 0x23, 0x05, 0xb2, 0x75, 0x7f, 0x8c, 0x05, 0x47, 0x3d, 0xe2, 0xd6, 0x86, + 0xd0, 0x7d, 0x4a, 0x6a, 0x46, 0x2d, 0x10, 0xf4, 0x68, 0xd7, 0x60, 0xe2, + 0x74, 0xc5, 0x74, 0x70, 0x40, 0x1d, 0xe6, 0x64, 0x23, 0x36, 0x6b, 0x2a, + 0x4f, 0x2c, 0x2f, 0x7e, 0xdf, 0x83, 0x10, 0x5b, 0x1a, 0x17, 0xaa, 0x38, + 0x6a, 0x55, 0xcc, 0xa0, 0x48, 0xb0, 0xe7, 0xa4, 0x40, 0xe5, 0xbf, 0x31, + 0xd9, 0xfb, 0x7a, 0x6a, 0xde, 0x64, 0x7f, 0xeb, 0x55, 0xd2, 0x7a, 0x99, + 0x11, 0x9d, 0xae, 0x21, 0xcd, 0x35, 0x32, 0xb6, 0xf2, 0x65, 0xce, 0xcc, + 0x09, 0x69, 0x69, 0x2b, 0x87, 0x17, 0x6f, 0x56, 0x51, 0xa9, 0x7a, 0x5c, + 0x57, 0xbc, 0xa9, 0x8b, 0x9e, 0x69, 0xb5, 0xcd, 0xbb, 0x2a, 0x8a, 0x83, + 0x4d, 0x17, 0x8f, 0x94, 0x13, 0x6c, 0xde, 0xbd, 0x96, 0x9e, 0x48, 0xbe, + 0xae, 0x81, 0x18, 0x96, 0xdf, 0x47, 0xc3, 0x2f, 0xc7, 0x75, 0x28, 0x5f, + 0x0c, 0x53, 0xc8, 0xdf, 0xa4, 0x97, 0x16, 0x8a, 0x54, 0x4f, 0x97, 0x2c, + 0xa2, 0x8d, 0xea, 0xad, 0xba, 0x7f, 0x40, 0x58, 0x62, 0x4d, 0x92, 0x20, + 0x51, 0x2b, 0x0a, 0x9c, 0xe2, 0x5d, 0x45, 0x09, 0xab, 0x22, 0xb1, 0xcf, + 0x57, 0xeb, 0xd3, 0x2c, 0x2f, 0x2e, 0xbf, 0x82, 0xbb, 0x04, 0xd4, 0xce, + 0xb9, 0x3f, 0x3b, 0x78, 0xfd, 0x9d, 0x2d, 0x98, 0x22, 0xea, 0x17, 0x06, + 0x6e, 0xa7, 0xad, 0x5f, 0x79, 0x89, 0x14, 0x26, 0x18, 0x7e, 0xef, 0xeb, + 0xd3, 0xf3, 0x3c, 0x97, 0x22, 0x84, 0xac, 0x98, 0x0b, 0x2a, 0xd9, 0x52, + 0xb8, 0x5b, 0x9c, 0x5f, 0x47, 0x98, 0x22, 0xe1, 0x5c, 0xcc, 0x24, 0x60, + 0x19, 0x4d, 0xa4, 0xd0, 0x55, 0x45, 0x5d, 0x1a, 0x07, 0xd5, 0x6c, 0x6d, + 0xad, 0xaa, 0x90, 0x42, 0x48, 0x82, 0x36, 0x7e, 0xd8, 0xa6, 0xe9, 0xf3, + 0x7b, 0xb0, 0x20, 0xe6, 0x68, 0xcd, 0xb2, 0x7f, 0xc7, 0x22, 0x86, 0x09, + 0x40, 0xcb, 0x6f, 0x44, 0x36, 0x41, 0x34, 0x99, 0xf8, 0x34, 0x29, 0x7f, + 0x5c, 0x2f, 0x41, 0x0e, 0x19, 0xb9, 0xc1, 0xf1, 0x0d, 0xb6, 0xcd, 0xb2, + 0x00, 0x2c, 0xca, 0xa6, 0x15, 0xa2, 0x73, 0x2a, 0x7b, 0xa2, 0xae, 0x78, + 0xa3, 0xed, 0x0e, 0x56, 0x8a, 0xa4, 0x25, 0xd8, 0xa7, 0xb9, 0x95, 0x7d, + 0x07, 0xff, 0xe9, 0x26, 0x3c, 0x53, 0x09, 0xdd, 0x10, 0x60, 0xf2, 0x7b, + 0xa5, 0xfb, 0xa5, 0xe1, 0x8f, 0x93, 0xec, 0xf5, 0x0d, 0xf9, 0x43, 0x97, + 0xac, 0xe7, 0x40, 0x74, 0x6a, 0x73, 0x78, 0x1f, 0xcc, 0x1a, 0xc3, 0x83, + 0x09, 0xf8, 0xd0, 0x03, 0x86, 0xe7, 0x9f, 0xde, 0xee, 0x3a, 0x59, 0xf7, + 0x93, 0x79, 0xeb, 0x83, 0xdc, 0x41, 0xdd, 0x71, 0x64, 0x61, 0x4d, 0xbe, + 0x3d, 0x0d, 0x7a, 0x9c, 0xed, 0x08, 0xe7, 0xa2, 0x4c, 0x78, 0xe1, 0xd6, + 0xaa, 0x03, 0xa7, 0x14, 0x5f, 0xd1, 0x29, 0x61, 0x03, 0x74, 0xc7, 0x93, + 0x52, 0x06, 0x80, 0x48, 0xf5, 0xa5, 0x04, 0x0a, 0x03, 0x86, 0x05, 0x8b, + 0x3a, 0x1f, 0xf5, 0x38, 0x9e, 0x8e, 0x12, 0x62, 0x53, 0xa7, 0x4d, 0xa9, + 0xbc, 0x85, 0x33, 0x6a, 0xb5, 0x3e, 0xb4, 0x8e, 0x81, 0xdb, 0xb5, 0xdf, + 0x22, 0x13, 0xd1, 0x04, 0x42, 0x67, 0xfb, 0x89, 0x9b, 0x83, 0x80, 0x78, + 0x9a, 0x25, 0xca, 0x0d, 0x70, 0xe4, 0x8a, 0x7c, 0x57, 0x1b, 0x03, 0x53, + 0x0a, 0xee, 0xbd, 0x98, 0xad, 0xc0, 0xf0, 0xff, 0x11, 0x39, 0x84, 0x95, + 0x45, 0x02, 0xba, 0x5e, 0xe9, 0xd8, 0xbe, 0x07, 0x8f, 0x3a, 0xfc, 0x16, + 0x4e, 0xab, 0x67, 0xcb, 0x76, 0x70, 0xd7, 0x14, 0xa9, 0xfa, 0x2d, 0xe5, + 0x47, 0xaa, 0x58, 0xd4, 0xbd, 0x7b, 0x7a, 0x73, 0x05, 0xc5, 0x83, 0x63, + 0x27, 0x55, 0x26, 0xe7, 0x82, 0xd6, 0x07, 0x0b, 0xa8, 0xc5, 0x56, 0xa0, + 0x7c, 0x21, 0x67, 0x3c, 0xf5, 0x25, 0x32, 0x8e, 0xc1, 0xcd, 0x1f, 0x41, + 0x04, 0x97, 0x3b, 0x2e, 0x17, 0xf5, 0xa9, 0xe1, 0xca, 0xb8, 0x30, 0xf6, + 0x0b, 0x06, 0x38, 0xa8, 0x26, 0xb0, 0xab, 0xee, 0xc5, 0xeb, 0x30, 0x28, + 0x7d, 0xed, 0x1b, 0xde, 0xfb, 0x77, 0xf8, 0xca, 0xff, 0xc3, 0xc8, 0x49, + 0x7c, 0x67, 0x21, 0x6c, 0x8e, 0xd4, 0x26, 0x67, 0x88, 0xd7, 0xcd, 0x0a, + 0xf8, 0x64, 0x25, 0x77, 0x6b, 0xf1, 0x97, 0x52, 0x8e, 0xa6, 0xc1, 0xc5, + 0x24, 0x1c, 0xde, 0xbb, 0xba, 0x03, 0x8c, 0x07, 0x88, 0xf2, 0x96, 0xfe, + 0x85, 0x00, 0xde, 0xda, 0xf0, 0xb2, 0x7f, 0x71, 0x27, 0x6e, 0x7d, 0xdb, + 0xf1, 0x49, 0x78, 0x7a, 0x57, 0x1c, 0x90, 0xf6, 0x6f, 0xcd, 0xcb, 0xb7, + 0xf7, 0x1a, 0xc8, 0xab, 0xad, 0xa7, 0x89, 0x14, 0xd3, 0x52, 0x64, 0x20, + 0xfd, 0x31, 0xed, 0x6b, 0x65, 0x21, 0x40, 0x16, 0xf4, 0x69, 0xac, 0x1e, + 0x78, 0x26, 0x78, 0x06, 0x44, 0x83, 0xb0, 0x57, 0x00, 0x6a, 0xa2, 0x96, + 0xbd, 0xd2, 0xcf, 0x53, 0xf0, 0xdf, 0xde, 0x9d, 0xb6, 0xb3, 0xc3, 0x46, + 0xc5, 0xe5, 0xe5, 0x3e, 0xc4, 0xc7, 0xa7, 0x59, 0x2e, 0xe0, 0x0b, 0x14, + 0x57, 0xd7, 0x36, 0x38, 0x5d, 0xb4, 0xe2, 0x14, 0x2d, 0x1b, 0xc3, 0x4d, + 0xd6, 0x04, 0x03, 0x56, 0x80, 0x05, 0x17, 0xbd, 0x02, 0x2c, 0x8f, 0xd8, + 0x29, 0x8e, 0x1b, 0x78, 0xee, 0xcc, 0x61, 0x4b, 0xce, 0x36, 0x1f, 0x50, + 0xf0, 0xc0, 0x30, 0x9a, 0xa0, 0xe7, 0x4b, 0xfb, 0x1a, 0x3e, 0xb4, 0x8a, + 0xf5, 0x60, 0x05, 0xd2, 0x16, 0x8d, 0xaa, 0x9e, 0x33, 0xc0, 0xe5, 0xbd, + 0xff, 0x1b, 0x33, 0xce, 0x80, 0x9e, 0xc9, 0xd5, 0xbc, 0x36, 0x75, 0xe1, + 0x75, 0xc2, 0x6c, 0xb6, 0xee, 0x85, 0xaf, 0x5f, 0xf7, 0x77, 0xa0, 0x86, + 0x08, 0x84, 0xee, 0x06, 0x6c, 0xe0, 0x7e, 0x85, 0xef, 0x1a, 0x0a, 0xae, + 0x17, 0xde, 0x24, 0xac, 0x2c, 0xb6, 0x38, 0x08, 0x76, 0x0b, 0x9f, 0x2b, + 0xba, 0x84, 0x91, 0xd2, 0xdb, 0x64, 0xf7, 0x06, 0x19, 0x9b, 0x6f, 0xa5, + 0x1d, 0x0f, 0x7d, 0xa6, 0x89, 0x12, 0x66, 0xfa, 0xdd, 0x11, 0xce, 0x1c, + 0x68, 0x62, 0x27, 0xe5, 0x27, 0x5f, 0xa2, 0x0e, 0xca, 0x37, 0x3a, 0xee, + 0x32, 0x2d, 0x91, 0xf6, 0x36, 0x9f, 0x09, 0x0c, 0x57, 0x4d, 0x42, 0x9f, + 0x37, 0xce, 0xd1, 0x91, 0x19, 0x0a, 0xcc, 0x6b, 0x3c, 0xd1, 0xae, 0xf5, + 0x09, 0xdd, 0x86, 0xdb, 0x38, 0xad, 0x39, 0x4f, 0x2f, 0x07, 0x56, 0xea, + 0x20, 0x57, 0x2f, 0xd8, 0x64, 0xd2, 0xd8, 0xa2, 0xdc, 0x3a, 0xb1, 0xdb, + 0x52, 0x6c, 0x0c, 0x3a, 0x0c, 0xb6, 0xc7, 0xab, 0xea, 0x6b, 0xd3, 0x92, + 0x40, 0x40, 0x4a, 0xf1, 0x2f, 0x71, 0x79, 0xfc, 0xe8, 0xc0, 0x51, 0x28, + 0x6e, 0xf3, 0xe0, 0x4f, 0xb4, 0xd2, 0x23, 0x6d, 0xa2, 0xf5, 0x34, 0x1f, + 0x75, 0x21, 0xa7, 0xaf, 0xa8, 0x29, 0x98, 0x1a, 0x30, 0x5a, 0x4b, 0x52, + 0x9e, 0x00, 0x6f, 0xed, 0x45, 0xb5, 0x0d, 0x2c, 0x1a, 0x16, 0x2b, 0x1f, + 0xdf, 0x28, 0x79, 0xfb, 0xde, 0x69, 0xd1, 0xcd, 0xaf, 0x42, 0xe0, 0x08, + 0xea, 0x3b, 0x98, 0x93, 0xf6, 0x70, 0x6f, 0xd2, 0x2a, 0x75, 0xdc, 0x98, + 0xb0, 0x78, 0x08, 0x74, 0x4f, 0x2b, 0x5d, 0x81, 0x4b, 0x32, 0x21, 0x53, + 0x98, 0xb4, 0xa2, 0x2d, 0x46, 0x5c, 0x04, 0x4b, 0x7f, 0x91, 0x62, 0x3b, + 0x5f, 0x18, 0x65, 0x16, 0x17, 0x52, 0xe1, 0x52, 0xc5, 0x12, 0xa8, 0x9d, + 0xd1, 0x5c, 0xec, 0xb8, 0x03, 0xf0, 0x6a, 0xe0, 0x7d, 0x81, 0xd4, 0x5e, + 0x28, 0xd8, 0x75, 0x64, 0xd9, 0x94, 0x32, 0x46, 0xa1, 0xba, 0xc6, 0x31, + 0x74, 0xc9, 0x6b, 0xad, 0xa5, 0xe5, 0x75, 0x8d, 0xd6, 0xb7, 0xd8, 0x76, + 0x77, 0x67, 0x52, 0xf8, 0x19, 0xee, 0x98, 0xe8, 0xea, 0xa7, 0x23, 0xe0, + 0x09, 0x60, 0x5e, 0x52, 0xcb, 0xfd, 0x1a, 0x1c, 0x2e, 0x8e, 0x56, 0x8c, + 0x6e, 0xbf, 0x32, 0xc9, 0x02, 0x94, 0xef, 0x40, 0x20, 0x1b, 0x12, 0xf6, + 0x1c, 0x23, 0x8d, 0x20, 0xdf, 0xe5, 0x49, 0x7c, 0x24, 0xda, 0x7f, 0xdb, + 0xf7, 0x7f, 0x93, 0x57, 0xda, 0x50, 0x2a, 0x58, 0xc6, 0xc9, 0x03, 0xcd, + 0xd6, 0x3e, 0xd2, 0x6b, 0xfc, 0x65, 0x3b, 0x06, 0xaa, 0x1e, 0x97, 0x8c, + 0x0a, 0xe7, 0x6d, 0xf8, 0xb6, 0x06, 0x4c, 0x6a, 0xf2, 0x89, 0x4a, 0x7b, + 0x0e, 0x7a, 0x4d, 0xbf, 0x1a, 0x4d, 0x44, 0x37, 0x54, 0x7f, 0xeb, 0x4b, + 0x76, 0x76, 0x8e, 0x99, 0x86, 0xec, 0x53, 0x91, 0x6a, 0x6b, 0xfe, 0xd2, + 0x2e, 0x69, 0x6a, 0x81, 0x24, 0x2b, 0x84, 0xe8, 0x95, 0x4f, 0x61, 0xd6, + 0x36, 0x17, 0xac, 0xe3, 0xd0, 0x10, 0x32, 0xf4, 0x93, 0xf5, 0xe1, 0x42, + 0xbe, 0x06, 0x48, 0x2d, 0x90, 0x4e, 0x0e, 0xbf, 0xa7, 0x20, 0x5d, 0xee, + 0x96, 0x69, 0x4a, 0xbd, 0xfd, 0x46, 0xed, 0xfb, 0x71, 0xfb, 0x02, 0x41, + 0x20, 0x1b, 0x0d, 0x66, 0xbe, 0x99, 0xdf, 0x19, 0x84, 0x59, 0xf5, 0x01, + 0x5c, 0x79, 0x0e, 0xd3, 0xfb, 0x2e, 0xd5, 0xf8, 0xf1, 0xbf, 0xaf, 0x90, + 0x79, 0x7b, 0x7c, 0x71, 0x36, 0x0d, 0xdb, 0x72, 0x9c, 0xeb, 0x73, 0x74, + 0xf2, 0x8a, 0x7f, 0x93, 0xa9, 0xb4, 0x6b, 0xea, 0x66, 0xc8, 0xd9, 0xac, + 0x9c, 0x61, 0xfc, 0x78, 0xdf, 0xb5, 0xa3, 0xd9, 0xdd, 0x38, 0x84, 0x5d, + 0x6c, 0xb6, 0x4a, 0x6e, 0x2b, 0xcb, 0x28, 0xd8, 0xc5, 0x39, 0xcc, 0xcd, + 0xeb, 0xd9, 0x96, 0xd7, 0x2c, 0xb4, 0x20, 0x49, 0xd3, 0x17, 0x70, 0xc7, + 0x5c, 0x6a, 0x04, 0xfe, 0x70, 0xb5, 0x9f, 0x2d, 0x35, 0xe0, 0x28, 0x5c, + 0x64, 0x65, 0x14, 0x56, 0x36, 0x8f, 0xbf, 0x94, 0xb1, 0x64, 0x11, 0x01, + 0x9f, 0xb2, 0xeb, 0x60, 0xbe, 0x36, 0xe3, 0xdb, 0xf5, 0x68, 0xb0, 0xa1, + 0x18, 0xbd, 0x91, 0x38, 0xf0, 0xfb, 0x68, 0x2d, 0x8e, 0xb4, 0x96, 0x77, + 0x24, 0xd5, 0x0d, 0xd0, 0xca, 0x5a, 0xb4, 0x4d, 0x09, 0x73, 0x1c, 0xa2, + 0xd5, 0x08, 0x77, 0x48, 0x75, 0xea, 0x4d, 0xb7, 0xf8, 0x52, 0x8a, 0x7f, + 0x58, 0x8a, 0x4d, 0x3e, 0x87, 0x17, 0xf6, 0x08, 0xa4, 0x01, 0x4f, 0xb8, + 0x55, 0x1e, 0xa1, 0xb3, 0xc3, 0xb2, 0x2c, 0xcd, 0xf4, 0x9b, 0xe8, 0xa8, + 0xea, 0xa6, 0xc0, 0x6a, 0x53, 0x86, 0x6d, 0xdd, 0xc7, 0x3f, 0x85, 0x9e, + 0x50, 0xef, 0x4c, 0x21, 0x1c, 0x7b, 0xd3, 0x12, 0x70, 0x53, 0x9d, 0xf3, + 0xb8, 0xd0, 0x26, 0x00, 0x74, 0x57, 0x67, 0xcf, 0x6f, 0x28, 0x8b, 0x64, + 0x79, 0x76, 0x84, 0x4a, 0x15, 0x5a, 0x9a, 0x89, 0x84, 0xb4, 0x16, 0x07, + 0x59, 0x63, 0xbe, 0xcc, 0xc9, 0xd3, 0x80, 0x31, 0xa6, 0x2c, 0x8e, 0x6a, + 0x5b, 0x19, 0x65, 0x70, 0xa2, 0x26, 0xa5, 0x47, 0x8c, 0xe0, 0xba, 0xc7, + 0xa4, 0x34, 0xf5, 0x3f, 0xc6, 0x15, 0x8b, 0xff, 0x4d, 0x49, 0xd5, 0xc1, + 0x86, 0x2a, 0xc6, 0x9f, 0xff, 0x42, 0x25, 0x23, 0x18, 0xdb, 0xcc, 0x7a, + 0x9d, 0xfe, 0xff, 0x20, 0x7c, 0x6b, 0xad, 0xf0, 0x01, 0xca, 0x39, 0xfa, + 0x8a, 0x6e, 0xbb, 0xfa, 0x79, 0xc8, 0x12, 0xcb, 0xc7, 0xa5, 0x90, 0x71, + 0x13, 0x84, 0xa6, 0x6a, 0xee, 0x48, 0x96, 0x57, 0xf0, 0x62, 0x98, 0x22, + 0xa6, 0x61, 0x51, 0x2a, 0x58, 0x79, 0x40, 0x8e, 0x5e, 0x21, 0x64, 0xe2, + 0x3e, 0x15, 0xec, 0x36, 0x72, 0x15, 0xb7, 0x21, 0x29, 0x2a, 0xa1, 0x1f, + 0x89, 0x55, 0xe5, 0x8e, 0xa7, 0xef, 0x58, 0x68, 0xf0, 0x1f, 0x80, 0x7a, + 0x98, 0xbc, 0xbc, 0x33, 0x82, 0x33, 0x5f, 0x35, 0x95, 0x19, 0x60, 0xbd, + 0xed, 0x26, 0xed, 0xc5, 0xa2, 0xee, 0xf8, 0x7b, 0xca, 0x5a, 0x64, 0xcb, + 0x80, 0xe5, 0x36, 0xce, 0x2f, 0x13, 0xa2, 0x34, 0xd2, 0xed, 0x45, 0x9a, + 0x39, 0x31, 0x48, 0x4d, 0xf6, 0x3b, 0xd1, 0x6d, 0xe6, 0xc1, 0x33, 0x45, + 0x15, 0x4a, 0x68, 0x5d, 0xac, 0x33, 0xf0, 0xaa, 0x5f, 0xf0, 0x46, 0x71, + 0x4f, 0x51, 0xd5, 0x20, 0x4a, 0xcd, 0x8c, 0xec, 0x11, 0x4a, 0x5d, 0xfc, + 0xd3, 0x99, 0x7c, 0x5a, 0xd3, 0xd5, 0x2f, 0x7b, 0xca, 0xcd, 0x6a, 0x9c, + 0xfa, 0x0e, 0x1d, 0xde, 0x3d, 0xd2, 0x48, 0x97, 0xac, 0x36, 0x59, 0xf4, + 0xbe, 0x99, 0xe9, 0xc1, 0x87, 0x9c, 0xdf, 0xb7, 0xf0, 0x00, 0x5e, 0x68, + 0xe9, 0x40, 0xbc, 0x72, 0x35, 0x71, 0x66, 0x52, 0xf5, 0x0e, 0x02, 0xb2, + 0xae, 0x15, 0x79, 0xf7, 0x65, 0x1e, 0x3c, 0x73, 0x04, 0x2b, 0x7f, 0x23, + 0xa0, 0x35, 0x99, 0x56, 0x57, 0x52, 0xbf, 0x52, 0xe0, 0x98, 0x6f, 0x13, + 0xde, 0xde, 0x84, 0x6b, 0x86, 0xfe, 0x91, 0x5e, 0x17, 0xac, 0xa1, 0x06, + 0x49, 0x7f, 0xa1, 0xc4, 0xcf, 0x4f, 0xd2, 0xd8, 0x94, 0x6d, 0x58, 0x77, + 0x13, 0x7b, 0x6f, 0x79, 0xb5, 0x88, 0xb9, 0xd0, 0xcc, 0xa0, 0x77, 0x79, + 0xe4, 0xe0, 0x96, 0x02, 0xf7, 0x2f, 0xb7, 0x28, 0x23, 0xec, 0x6e, 0x6e, + 0x72, 0x7c, 0x36, 0xa9, 0x51, 0xa6, 0x34, 0x1c, 0x3e, 0x83, 0x0b, 0xc2, + 0x25, 0xc7, 0x58, 0xd5, 0xd3, 0xab, 0x35, 0x77, 0xed, 0x23, 0x49, 0xf2, + 0xac, 0x88, 0x10, 0xec, 0xf4, 0xd8, 0xf6, 0x51, 0xe2, 0x07, 0x31, 0xcb, + 0x41, 0x80, 0x00, 0x30, 0x8b, 0x2a, 0x0a, 0xa6, 0x09, 0x16, 0x79, 0x09, + 0x16, 0x24, 0x9d, 0xc6, 0x43, 0x1c, 0x76, 0x8a, 0xd5, 0x17, 0xd3, 0x95, + 0xb0, 0x97, 0x55, 0xa8, 0xa8, 0xea, 0x8a, 0x78, 0x9c, 0x17, 0x27, 0x05, + 0xb6, 0x18, 0xf9, 0xb7, 0x0c, 0xe1, 0x73, 0x0a, 0xc2, 0xf8, 0x46, 0xaa, + 0x62, 0x27, 0x6e, 0x65, 0xa8, 0xbe, 0x6a, 0x23, 0x8a, 0x7b, 0x77, 0x14, + 0x5a, 0xe6, 0xb7, 0xff, 0x84, 0xb8, 0x03, 0xdc, 0x37, 0x20, 0xed, 0xf2, + 0x27, 0xbc, 0xd8, 0x46, 0xd6, 0xef, 0xdf, 0x3d, 0xc5, 0x55, 0x55, 0x56, + 0x58, 0x62, 0x33, 0xc7, 0x1b, 0x3c, 0x9e, 0x88, 0x74, 0x9d, 0x94, 0x59, + 0x98, 0x5e, 0x39, 0x44, 0x05, 0xde, 0x8d, 0xda, 0x4c, 0x94, 0xdb, 0x86, + 0xd5, 0x10, 0x5a, 0xf0, 0x8b, 0x63, 0x65, 0xc7, 0xd4, 0xff, 0xba, 0xc8, + 0x5f, 0x94, 0x24, 0x31, 0xe3, 0xc2, 0x48, 0x72, 0x69, 0x43, 0x9d, 0xa3, + 0x31, 0x4f, 0xa3, 0x30, 0x56, 0xa2, 0x58, 0x81, 0x5e, 0xa5, 0x14, 0x7a, + 0x69, 0x8a, 0x8b, 0x93, 0x50, 0x19, 0x08, 0x77, 0x4b, 0xd4, 0x9c, 0x7b, + 0x0a, 0x6e, 0x7b, 0xf0, 0x17, 0x02, 0x4b, 0x5b, 0x79, 0x64, 0x3b, 0x4b, + 0x91, 0x33, 0x6b, 0xfb, 0x2a, 0x0e, 0x32, 0x8d, 0xd3, 0x1e, 0x62, 0x66, + 0x10, 0xfd, 0xf6, 0xdc, 0xf7, 0x90, 0xbf, 0x61, 0xa0, 0xff, 0xb6, 0x02, + 0x8b, 0xa6, 0x1e, 0x43, 0x49, 0x91, 0xd6, 0xd5, 0xf7, 0x9f, 0x8b, 0x31, + 0x04, 0x5d, 0x65, 0x6d, 0x45, 0x42, 0x55, 0x89, 0x2b, 0x2d, 0xd0, 0x9f, + 0xe2, 0xdc, 0xe9, 0x6e, 0x46, 0x0a, 0x33, 0xb4, 0x99, 0x81, 0xbe, 0x9c, + 0x6c, 0xc1, 0x7f, 0x42, 0xa6, 0x42, 0xe9, 0xbc, 0x49, 0xcf, 0xcd, 0xcc, + 0x79, 0xd8, 0xab, 0xac, 0x6d, 0x60, 0x82, 0x3f, 0x8e, 0x4b, 0x7c, 0xef, + 0x6c, 0xd6, 0x2f, 0x1c, 0xaa, 0x65, 0x68, 0x9d, 0x88, 0x83, 0x98, 0x53, + 0x7c, 0x52, 0xaf, 0x80, 0x96, 0x63, 0x9b, 0x0e, 0x40, 0x84, 0x01, 0x4b, + 0x4e, 0x8e, 0xaf, 0x72, 0xd1, 0xf0, 0xeb, 0x7c, 0x37, 0x96, 0xbc, 0x97, + 0x65, 0x8a, 0x82, 0x9e, 0x77, 0x44, 0x66, 0x6c, 0x5e, 0x38, 0x93, 0x7b, + 0x7e, 0x27, 0x10, 0xb7, 0xf2, 0x9f, 0xe4, 0xeb, 0xd5, 0xd5, 0x38, 0xc4, + 0x54, 0x76, 0x18, 0x15, 0x75, 0xf9, 0x18, 0x7c, 0x6f, 0x71, 0xae, 0x36, + 0x31, 0x62, 0xc8, 0xc7, 0x8e, 0xb8, 0xa6, 0xb6, 0xb9, 0xb4, 0x84, 0x8f, + 0x9d, 0x87, 0xc7, 0x5f, 0x20, 0xb7, 0xcb, 0x22, 0x02, 0x13, 0x92, 0x4f, + 0xcd, 0x7c, 0xd8, 0x10, 0xbc, 0xc1, 0xd5, 0x24, 0x63, 0x1e, 0xc0, 0xb8, + 0x77, 0x6b, 0xc1, 0x35, 0xf5, 0x5a, 0xd4, 0x76, 0x04, 0x85, 0x55, 0x1b, + 0xd6, 0x14, 0x55, 0xb9, 0x55, 0x09, 0x4f, 0x29, 0x23, 0xc3, 0xd1, 0x05, + 0x39, 0x0d, 0xf4, 0xc4, 0x2c, 0x87, 0x28, 0xb8, 0xe3, 0x1d, 0x90, 0x7c, + 0x8a, 0x28, 0xbb, 0xea, 0x08, 0x11, 0xee, 0x0f, 0xa5, 0x88, 0xb5, 0x3f, + 0x14, 0xac, 0x13, 0x28, 0x0b, 0xdc, 0x0b, 0xb6, 0x1a, 0x9d, 0x44, 0x12, + 0x45, 0x13, 0xa6, 0xfd, 0x78, 0x07, 0x18, 0x80, 0xf9, 0x64, 0xa1, 0x4f, + 0xa8, 0x6b, 0x4c, 0x98, 0xab, 0x6c, 0x33, 0x66, 0x29, 0x1b, 0x80, 0x8d, + 0x3e, 0xaf, 0x7a, 0xb9, 0x99, 0xc1, 0x05, 0x38, 0xdd, 0x1a, 0xba, 0xf3, + 0x4e, 0x4e, 0xf3, 0x77, 0x9a, 0x87, 0xe5, 0x7f, 0x89, 0x84, 0x4c, 0xdb, + 0x6d, 0xf4, 0x55, 0x9a, 0x2a, 0x56, 0xed, 0x2a, 0x02, 0x0d, 0xc7, 0x4d, + 0xf7, 0xab, 0xc3, 0xbe, 0x76, 0x97, 0x29, 0xe4, 0x77, 0x1d, 0x47, 0x37, + 0xe6, 0x65, 0x47, 0x3d, 0x90, 0xb9, 0x69, 0xbd, 0x5d, 0x17, 0x72, 0x6d, + 0x1b, 0xe4, 0xfb, 0x60, 0x8b, 0xaa, 0x4c, 0x98, 0xd5, 0x80, 0x1d, 0xb9, + 0xe7, 0x0b, 0x15, 0xb1, 0x7f, 0xac, 0x11, 0x30, 0x38, 0xbe, 0xe1, 0xbc, + 0xfe, 0xd8, 0x1b, 0x0b, 0xa1, 0xfb, 0x2a, 0x20, 0x44, 0xd9, 0xbe, 0xec, + 0x30, 0xb1, 0xb7, 0x4b, 0xf6, 0x62, 0xd4, 0x99, 0xd1, 0x0a, 0x43, 0xca, + 0xe6, 0xe8, 0x77, 0xc6, 0xd9, 0xd0, 0xc5, 0xe9, 0x33, 0x9d, 0x16, 0x0c, + 0x8e, 0xa0, 0xd9, 0x96, 0xb1, 0x93, 0x78, 0x57, 0x2c, 0xc8, 0x41, 0x8e, + 0x78, 0x42, 0x49, 0x13, 0x9b, 0xe6, 0x81, 0xf0, 0x45, 0x1e, 0x8b, 0x31, + 0x9d, 0x86, 0x6e, 0x79, 0xf2, 0xcd, 0xfc, 0x00, 0x54, 0x56, 0xb3, 0x43, + 0xfb, 0x10, 0x7b, 0x57, 0xed, 0xb1, 0x0a, 0x84, 0x81, 0x29, 0xdd, 0x28, + 0x96, 0xc4, 0x2a, 0x28, 0xf5, 0x5e, 0x9a, 0xad, 0x35, 0x23, 0x4a, 0x20, + 0x6f, 0x49, 0xfa, 0x82, 0x34, 0xaa, 0xa1, 0xe3, 0x8a, 0x76, 0x20, 0xa5, + 0x40, 0x4a, 0xb7, 0x7c, 0xf6, 0xbb, 0x67, 0xfc, 0x36, 0x34, 0x66, 0xd5, + 0xcc, 0x80, 0x87, 0xa4, 0x0a, 0x77, 0x02, 0x97, 0x8f, 0xee, 0x29, 0xa0, + 0xc7, 0xc6, 0x68, 0x70, 0x2d, 0x41, 0xa2, 0x2f, 0xe6, 0x46, 0x50, 0x02, + 0xcc, 0x75, 0x60, 0xaf, 0x5f, 0x12, 0xa9, 0xfa, 0x98, 0x66, 0x88, 0xe3, + 0x14, 0x70, 0x79, 0xd9, 0xa3, 0xc0, 0x35, 0xf8, 0xab, 0x9b, 0x5b, 0xa4, + 0xe3, 0x7a, 0x12, 0xfb, 0x34, 0xe5, 0x26, 0xdc, 0x7f, 0x4d, 0xc7, 0xba, + 0x4a, 0x8e, 0x10, 0x4f, 0x9f, 0x6b, 0xad, 0x98, 0xe2, 0x32, 0x58, 0x23, + 0x35, 0xd7, 0x63, 0xc4, 0x23, 0x4a, 0xd4, 0xb9, 0xd3, 0xec, 0x48, 0x46, + 0xa6, 0x58, 0xbc, 0xeb, 0xdd, 0x31, 0x42, 0x54, 0xc9, 0xcd, 0x74, 0x61, + 0xa9, 0x18, 0x66, 0x85, 0x40, 0x66, 0x4e, 0x20, 0x19, 0x0d, 0x14, 0x21, + 0x74, 0x77, 0xaf, 0xeb, 0x38, 0xbd, 0x84, 0xd2, 0x8e, 0xa2, 0x04, 0x99, + 0x03, 0xbd, 0x4e, 0x44, 0x97, 0x44, 0x9c, 0x9b, 0xab, 0x8f, 0x14, 0x09, + 0x26, 0x22, 0x60, 0x23, 0x3a, 0x08, 0x4c, 0x2c, 0xc0, 0xf2, 0x03, 0x87, + 0x7f, 0xd1, 0x19, 0xd8, 0x5b, 0x67, 0x7d, 0x39, 0xc3, 0x22, 0x47, 0x22, + 0x15, 0x1d, 0x66, 0x03, 0xe8, 0x14, 0xc0, 0x17, 0x1a, 0xbb, 0x09, 0xa9, + 0x5f, 0xea, 0x09, 0x74, 0x9e, 0x16, 0x97, 0x7d, 0x0e, 0x03, 0x73, 0x93, + 0x45, 0x6c, 0xe2, 0x4e, 0xd4, 0x57, 0x19, 0x36, 0xaa, 0x75, 0xf2, 0x0b, + 0x7b, 0x51, 0xe3, 0x1a, 0xa4, 0xbd, 0xd4, 0xde, 0x55, 0xe9, 0x0a, 0x16, + 0x0e, 0xf7, 0x0a, 0xee, 0x05, 0xbb, 0x1e, 0x8d, 0x38, 0xb5, 0x5f, 0xb8, + 0x27, 0x53, 0xcd, 0xf6, 0x08, 0x5e, 0x6a, 0x72, 0x82, 0xb5, 0x4e, 0xd5, + 0x44, 0x16, 0x2d, 0x79, 0x6e, 0x55, 0xa8, 0xc9, 0x69, 0x47, 0x82, 0xc3, + 0x55, 0x66, 0x9f, 0x81, 0xe0, 0x0d, 0xa8, 0xfa, 0x73, 0x17, 0xa5, 0xde, + 0xa3, 0x0d, 0xfb, 0x38, 0x60, 0x23, 0x85, 0x5c, 0x51, 0xf9, 0x47, 0x5b, + 0x9e, 0x1f, 0xaf, 0x07, 0x1d, 0x0a, 0x42, 0xcb, 0x7d, 0x3e, 0x30, 0x31, + 0xfc, 0x75, 0xfd, 0xdb, 0x22, 0x61, 0xa9, 0x04, 0x9c, 0x98, 0x8e, 0x77, + 0x52, 0x3b, 0x3e, 0xd7, 0xef, 0xca, 0xc5, 0xe8, 0x91, 0x7a, 0x59, 0x18, + 0x26, 0xfc, 0x09, 0x03, 0x21, 0xe8, 0xd5, 0x6f, 0x3b, 0x2e, 0xfa, 0xe7, + 0x6d, 0x3f, 0xcd, 0x03, 0xfc, 0xa9, 0xbf, 0x58, 0xa0, 0x5f, 0x91, 0x83, + 0x77, 0xe4, 0xbd, 0x2d, 0x7f, 0x15, 0x87, 0x48, 0x29, 0xae, 0xa8, 0x92, + 0x16, 0xd5, 0x18, 0x00, 0x10, 0xcd, 0xd8, 0x23, 0x87, 0x1f, 0x66, 0x60, + 0x1c, 0x78, 0x73, 0xad, 0x3a, 0xe5, 0xd5, 0x3f, 0x78, 0x02, 0x8d, 0xc7, + 0x1d, 0x27, 0x4d, 0xf1, 0x08, 0x88, 0xb9, 0xd6, 0x7d, 0xfd, 0xdb, 0x4c, + 0xf0, 0xf6, 0xd0, 0x03, 0x6c, 0x31, 0x2b, 0xd0, 0xea, 0x9b, 0xf2, 0xf8, + 0x0d, 0x4b, 0x6d, 0xa0, 0x7b, 0x21, 0x9b, 0xf1, 0x80, 0x93, 0xe3, 0x88, + 0xd9, 0x30, 0xa2, 0x7d, 0xf6, 0x88, 0x99, 0x83, 0xa5, 0xf9, 0xb0, 0xfd, + 0xe0, 0x7d, 0xed, 0x85, 0xea, 0x41, 0x06, 0x8b, 0x00, 0x77, 0xda, 0xb2, + 0x65, 0x63, 0x82, 0x9f, 0x0a, 0x6c, 0x8b, 0x3f, 0x59, 0xc0, 0xe1, 0x12, + 0x43, 0xe5, 0xf0, 0x93, 0x1f, 0xf0, 0x69, 0x1d, 0x95, 0xb7, 0xaf, 0xc1, + 0x83, 0xb2, 0x88, 0x18, 0xd1, 0x4a, 0xcf, 0x23, 0xb7, 0x4e, 0xc7, 0x4f, + 0xf7, 0xe0, 0x81, 0xc1, 0x55, 0x76, 0x1b, 0x8c, 0xbf, 0x68, 0x34, 0x63, + 0xd1, 0x86, 0xc3, 0xaa, 0x81, 0xce, 0x0d, 0x6c, 0xed, 0x73, 0x48, 0xfe, + 0xe2, 0xce, 0xb3, 0x73, 0x71, 0x7b, 0xb7, 0xd4, 0x76, 0x7b, 0xd8, 0xa4, + 0xc4, 0x70, 0x38, 0xdc, 0xd9, 0xb3, 0x9c, 0x92, 0xe2, 0x15, 0xd3, 0x93, + 0x43, 0x0b, 0xd6, 0xfb, 0xe8, 0x03, 0x22, 0x9b, 0x8c, 0x21, 0x41, 0x00, + 0x79, 0x54, 0xe7, 0x6e, 0x1a, 0xb3, 0xfc, 0xb2, 0xfd, 0xec, 0xce, 0x59, + 0xc1, 0x56, 0x30, 0x04, 0xca, 0xc1, 0xd9, 0x3e, 0x28, 0x18, 0xd0, 0xb5, + 0x4a, 0x87, 0x61, 0x37, 0xf7, 0xd3, 0x2d, 0x95, 0x88, 0xf3, 0xfc, 0xe5, + 0x78, 0x36, 0x40, 0x67, 0x40, 0xbf, 0x79, 0xee, 0xa5, 0x23, 0x78, 0xcd, + 0x25, 0x02, 0x2b, 0xf0, 0xe7, 0x77, 0x7d, 0xea, 0xa9, 0x45, 0xa7, 0xf9, + 0x14, 0x75, 0xbb, 0x72, 0x3c, 0x1a, 0x25, 0x53, 0x50, 0xf4, 0x3e, 0xe8, + 0xb5, 0xc4, 0x92, 0x94, 0x43, 0xa2, 0x44, 0x29, 0x7e, 0x10, 0x3f, 0xe3, + 0x08, 0x78, 0xcf, 0x03, 0xf5, 0xe5, 0xbf, 0x86, 0x77, 0x3f, 0xaa, 0xce, + 0xa7, 0x10, 0xea, 0x4d, 0x3a, 0x2f, 0x18, 0x18, 0x64, 0x83, 0xd6, 0x96, + 0x8f, 0xf6, 0xf5, 0xc6, 0xd6, 0x12, 0x1b, 0x10, 0x00, 0x1f, 0x2c, 0xc1, + 0x0f, 0x61, 0xcd, 0x6a, 0x24, 0x86, 0xa3, 0x9a, 0x2c, 0x89, 0x1b, 0xe8, + 0x37, 0xf8, 0x4d, 0xa0, 0x12, 0xfa, 0x16, 0xa2, 0xd6, 0xc1, 0x79, 0x29, + 0xb5, 0x61, 0x64, 0xdf, 0xad, 0x57, 0x5b, 0xb8, 0x8b, 0x66, 0xc0, 0x58, + 0x20, 0x8c, 0x9b, 0x8d, 0xce, 0xd1, 0x89, 0x49, 0xa0, 0x1e, 0xac, 0x72, + 0x01, 0x37, 0x51, 0x68, 0xb1, 0x5a, 0x90, 0x8d, 0xd7, 0x66, 0x61, 0x26, + 0x7c, 0xc0, 0xb7, 0x66, 0x00, 0x83, 0x09, 0xcb, 0x5a, 0x9d, 0x35, 0xef, + 0x6d, 0x7b, 0xb2, 0xdc, 0xcf, 0x34, 0x32, 0xd0, 0x4a, 0x21, 0x6b, 0xbd, + 0x26, 0xfd, 0xa4, 0xad, 0x86, 0x82, 0x48, 0xa0, 0x04, 0x38, 0x74, 0x63, + 0xf4, 0x47, 0x19, 0xf1, 0xaf, 0x34, 0xbd, 0x4b, 0x40, 0x79, 0x5d, 0xc9, + 0xbf, 0x69, 0x2a, 0xd1, 0x57, 0x8b, 0x30, 0xc0, 0x6f, 0x1a, 0x14, 0xb9, + 0xdf, 0xe0, 0x86, 0xe0, 0x92, 0xfb, 0xdd, 0xc7, 0xf7, 0xeb, 0x67, 0x49, + 0xcb, 0x5b, 0x06, 0x24, 0x90, 0x4e, 0x96, 0x6f, 0x86, 0x79, 0x33, 0x66, + 0x25, 0x72, 0x29, 0x82, 0x84, 0x7a, 0x2b, 0xd8, 0x1a, 0xa0, 0x86, 0x0e, + 0x6d, 0x36, 0x71, 0xce, 0x2e, 0x1d, 0x8d, 0x2f, 0x30, 0x4e, 0x28, 0x9c, + 0xa6, 0xaf, 0x4d, 0x38, 0x3b, 0x00, 0xdf, 0xde, 0x7d, 0x1f, 0x10, 0x0e, + 0xf0, 0xc4, 0x88, 0x95, 0xa8, 0x10, 0x22, 0xa1, 0xa6, 0x74, 0xd1, 0xbf, + 0x53, 0x20, 0x8a, 0x15, 0x4e, 0x22, 0x95, 0x9e, 0xce, 0x11, 0xa2, 0xf2, + 0xb7, 0x44, 0xb3, 0x78, 0xbd, 0x6c, 0xf3, 0x0f, 0x02, 0xfc, 0xe0, 0x51, + 0x4d, 0x2b, 0x9a, 0x2f, 0xed, 0x3d, 0x36, 0x83, 0x4e, 0xb1, 0xf9, 0xfb, + 0x15, 0xdc, 0x7d, 0x92, 0x3f, 0xd4, 0x3e, 0xe8, 0x53, 0xb8, 0x73, 0x5f, + 0xd6, 0xc5, 0x29, 0xfd, 0xc9, 0xa5, 0x2b, 0x40, 0xd0, 0x55, 0x1c, 0x87, + 0x77, 0x08, 0x90, 0xa0, 0x32, 0x4b, 0xe6, 0xbf, 0xa0, 0x0a, 0x41, 0xe4, + 0xa8, 0x19, 0x22, 0x50, 0x0b, 0x47, 0x17, 0xd8, 0x06, 0x4d, 0xe9, 0xd1, + 0x34, 0xfd, 0x19, 0x2f, 0x37, 0x39, 0x75, 0xe9, 0xaf, 0x7d, 0x03, 0x2b, + 0x83, 0xc5, 0xd6, 0x55, 0xe5, 0xe5, 0x00, 0x6c, 0x65, 0x70, 0x6f, 0x7d, + 0x4f, 0xa7, 0x3a, 0xd6, 0x41, 0xc7, 0xf6, 0x3c, 0x6f, 0x69, 0xdc, 0x43, + 0x13, 0x58, 0xce, 0xa1, 0x2b, 0x45, 0x4e, 0x2a, 0x26, 0x30, 0x4e, 0x9c, + 0x97, 0xbb, 0xf7, 0xdd, 0xd8, 0x92, 0x47, 0xc0, 0x70, 0x9a, 0xf8, 0xee, + 0xd4, 0xde, 0xab, 0x7f, 0x82, 0xe6, 0xbe, 0xb2, 0x3c, 0x95, 0x95, 0x3c, + 0x26, 0x59, 0x01, 0xd7, 0xf9, 0x4f, 0xb8, 0xbb, 0x8a, 0x8d, 0xc4, 0x98, + 0x11, 0xc5, 0xf1, 0x7d, 0x62, 0x19, 0x1f, 0x27, 0x0e, 0x57, 0x4f, 0xe2, + 0xf7, 0xd6, 0xa0, 0xea, 0x25, 0xa2, 0x59, 0xb1, 0xfc, 0x21, 0x73, 0x8e, + 0xc9, 0x9b, 0x69, 0x07, 0x02, 0xed, 0x49, 0xb9, 0x11, 0x61, 0xf3, 0xcd, + 0x12, 0x40, 0x40, 0xbf, 0x92, 0x89, 0x2e, 0x0c, 0xa7, 0xd9, 0x06, 0xfd, + 0x45, 0xa0, 0xdb, 0x7f, 0xba, 0xee, 0xd7, 0x8b, 0x3b, 0xa7, 0x2f, 0xc4, + 0xef, 0xe9, 0xa2, 0x38, 0x26, 0xbf, 0x87, 0xa8, 0xc2, 0x76, 0x01, 0xa6, + 0x97, 0x01, 0x13, 0x02, 0x7d, 0x77, 0x15, 0x43, 0x28, 0xbf, 0x57, 0x61, + 0xfc, 0x13, 0x01, 0xa6, 0xee, 0x53, 0x32, 0x4f, 0x92, 0x76, 0xb7, 0x0f, + 0x85, 0x3b, 0xf0, 0x28, 0x47, 0x41, 0x5c, 0x25, 0x48, 0x5d, 0x76, 0x1a, + 0x41, 0x59, 0x12, 0x01, 0x45, 0xe5, 0x6d, 0xb5, 0x84, 0x3e, 0xb1, 0x95, + 0x90, 0xab, 0xf7, 0xe9, 0xed, 0x84, 0xf6, 0x7e, 0x0f, 0xaf, 0xa5, 0x0c, + 0xa8, 0x6d, 0xfa, 0xfa, 0x83, 0x2a, 0xdb, 0xc5, 0xc9, 0xb8, 0xd7, 0x28, + 0x2f, 0x29, 0x91, 0x18, 0xee, 0xe9, 0xcb, 0x37, 0xf8, 0x58, 0x45, 0x9e, + 0xd1, 0x1f, 0xcd, 0xd9, 0x19, 0xaf, 0x21, 0x59, 0x51, 0x10, 0x81, 0x4d, + 0x08, 0xc6, 0x5a, 0x55, 0xe8, 0x8b, 0x77, 0x88, 0xa3, 0xf2, 0x2c, 0x5b, + 0x63, 0x61, 0x4f, 0x7a, 0x55, 0xef, 0x11, 0xdb, 0x9f, 0x87, 0xc1, 0x2a, + 0x6e, 0x41, 0x3d, 0x66, 0xec, 0x24, 0x09, 0xa6, 0x54, 0xa8, 0x4d, 0xa2, + 0x4f, 0x06, 0xa2, 0x6e, 0x5a, 0xc7, 0x2d, 0x9a, 0xbb, 0xfa, 0x01, 0x1f, + 0x55, 0x6f, 0xe2, 0xd3, 0x0c, 0x3f, 0x8d, 0x07, 0xa7, 0x34, 0xea, 0xd8, + 0xbc, 0x15, 0xda, 0x7d, 0xc3, 0x2d, 0xc6, 0xfb, 0xfc, 0x94, 0xed, 0x2b, + 0x27, 0x7d, 0x6b, 0x05, 0x14, 0x86, 0x3a, 0x6f, 0x53, 0x1f, 0xf2, 0x59, + 0xf7, 0xf9, 0xbe, 0xde, 0xef, 0x48, 0xea, 0xda, 0x55, 0xce, 0x79, 0xd9, + 0x73, 0xc0, 0x10, 0x9a, 0x77, 0x8a, 0x44, 0x19, 0x98, 0x70, 0x2d, 0xe4, + 0x1f, 0x9c, 0xea, 0xd6, 0xa5, 0x8a, 0x53, 0xf2, 0x17, 0x98, 0x8b, 0x53, + 0xc4, 0xd1, 0x63, 0xc8, 0xdd, 0xba, 0xb5, 0x17, 0x91, 0xb9, 0xc0, 0x7e, + 0xfa, 0x13, 0x12, 0xe7, 0xc9, 0x10, 0x9f, 0x77, 0x95, 0x29, 0x35, 0x72, + 0xaf, 0xba, 0xe5, 0x24, 0x4b, 0x6a, 0xb8, 0x7b, 0xb0, 0xff, 0x6c, 0x59, + 0xf6, 0x1e, 0xfe, 0x6f, 0x8e, 0x95, 0x0b, 0x0a, 0x5a, 0x8a, 0xaf, 0x32, + 0x4d, 0x38, 0xeb, 0x4b, 0xaf, 0xf7, 0xa0, 0xbc, 0x10, 0x9b, 0x25, 0xa6, + 0x8a, 0xa3, 0x25, 0xf4, 0x65, 0x97, 0x7b, 0x49, 0x78, 0xa4, 0xa0, 0x03, + 0x38, 0x00, 0xf1, 0xe6, 0x2a, 0x04, 0x4c, 0x3e, 0xce, 0xeb, 0x9d, 0x9e, + 0x5f, 0xeb, 0xc8, 0x5e, 0x38, 0xba, 0x8c, 0x6d, 0xab, 0x5b, 0x21, 0xa3, + 0xa6, 0x58, 0xa8, 0x6e, 0x45, 0xe1, 0xc7, 0xca, 0xa3, 0x25, 0xb5, 0xa5, + 0xb9, 0x9f, 0xbd, 0x3e, 0x13, 0xb1, 0x02, 0xa3, 0x6f, 0x39, 0x41, 0x0e, + 0x6f, 0x6a, 0x92, 0x35, 0x82, 0x07, 0xa9, 0x6e, 0xf1, 0xda, 0x15, 0xd0, + 0xe6, 0x69, 0x20, 0x18, 0x89, 0x1a, 0x3d, 0xc1, 0x3f, 0x0b, 0x4a, 0x15, + 0xd6, 0x30, 0xdf, 0x9c, 0xf3, 0xe0, 0xdf, 0x24, 0x18, 0x31, 0x66, 0xcd, + 0x2b, 0x56, 0xef, 0x51, 0x65, 0x42, 0xc1, 0x6d, 0xc6, 0xa8, 0x43, 0xaf, + 0xb9, 0xea, 0xa5, 0xb2, 0xce, 0x0b, 0xe7, 0x9d, 0x07, 0xd4, 0x8d, 0xa3, + 0x89, 0x7b, 0xd8, 0xa1, 0xee, 0x0c, 0x84, 0xf1, 0xa2, 0xc4, 0xe4, 0x5e, + 0xa5, 0x7c, 0x38, 0xb3, 0x2b, 0x37, 0x81, 0xed, 0x07, 0x65, 0x02, 0xe6, + 0x14, 0x40, 0xd2, 0x2c, 0xcc, 0xcd, 0xdc, 0xf8, 0x3c, 0xab, 0x7f, 0xeb, + 0x03, 0x02, 0xc6, 0x08, 0x57, 0x78, 0x79, 0x38, 0x82, 0xbc, 0x16, 0x22, + 0x1e, 0x67, 0xe7, 0xed, 0xa4, 0xe7, 0x95, 0x5d, 0x2e, 0x2d, 0x01, 0x28, + 0xf0, 0xc1, 0x2b, 0x17, 0xa7, 0xf4, 0xbf, 0xb2, 0x1d, 0x51, 0x9a, 0xcf, + 0x52, 0x0b, 0xbb, 0x77, 0xc9, 0x73, 0xfb, 0x58, 0xa0, 0x5c, 0xb5, 0x6d, + 0x74, 0xeb, 0x8c, 0xcd, 0xd8, 0x05, 0x72, 0xe6, 0xa9, 0xf6, 0x29, 0x1a, + 0x6d, 0x73, 0xe9, 0xa7, 0xfd, 0xea, 0xca, 0x2b, 0xc0, 0xb2, 0x47, 0xa0, + 0x80, 0x29, 0x11, 0x49, 0xad, 0xd8, 0xa5, 0x22, 0x1c, 0xa3, 0x43, 0x09, + 0xd1, 0x09, 0x81, 0x1b, 0xeb, 0x99, 0xdf, 0x4c, 0x25, 0xab, 0xd9, 0x87, + 0x68, 0x70, 0xf4, 0xe0, 0x7a, 0xbd, 0x79, 0xc3, 0x9e, 0xe5, 0xbb, 0xe1, + 0x60, 0xa0, 0x10, 0x40, 0x1d, 0x5d, 0x21, 0x08, 0x5d, 0x02, 0xe9, 0x25, + 0x5e, 0x1b, 0xa6, 0x85, 0x64, 0x3c, 0xfb, 0xe4, 0x03, 0x3c, 0xb9, 0x40, + 0xce, 0x7a, 0xe7, 0xf5, 0x5a, 0xe2, 0x33, 0xf7, 0x9c, 0x51, 0xe0, 0x7d, + 0xf3, 0x2f, 0x5d, 0xab, 0xa6, 0x97, 0xc9, 0x4b, 0x5e, 0x14, 0x03, 0x71, + 0x3f, 0xa3, 0x76, 0xd8, 0xcc, 0xc4, 0x3c, 0x39, 0xbc, 0xed, 0x51, 0x68, + 0xe8, 0x91, 0x2f, 0xaa, 0xd8, 0x97, 0xca, 0x8d, 0x2f, 0x1b, 0x0b, 0x41, + 0xe8, 0x27, 0xa2, 0x7a, 0x2e, 0x28, 0xa9, 0x64, 0xde, 0xec, 0x5e, 0x57, + 0xc1, 0x40, 0x22, 0x4f, 0xbc, 0x81, 0xa0, 0x73, 0xc9, 0x0c, 0x9c, 0x6e, + 0xe4, 0xe3, 0x63, 0x21, 0xcd, 0xda, 0xb6, 0x88, 0x20, 0x0b, 0xd4, 0xeb, + 0x4b, 0xb6, 0x38, 0x0d, 0xc3, 0x1f, 0x3a, 0x53, 0x1e, 0x5e, 0xbc, 0x99, + 0x5d, 0x6b, 0x93, 0x8d, 0xe5, 0x2f, 0x03, 0xb5, 0x56, 0x35, 0xb8, 0xf8, + 0x6a, 0x1a, 0x1a, 0x2a, 0xd8, 0xaa, 0x47, 0x71, 0xba, 0x5b, 0x36, 0x5c, + 0x4f, 0x42, 0xbc, 0x7e, 0x1a, 0xd0, 0xc0, 0x2e, 0x35, 0x11, 0x40, 0xfc, + 0xc0, 0x22, 0x69, 0xb6, 0xbb, 0x1e, 0x84, 0x7a, 0x3c, 0x20, 0x35, 0xff, + 0x2e, 0x7f, 0x20, 0x11, 0x4c, 0x76, 0x20, 0x68, 0xb2, 0xbb, 0x39, 0xf2, + 0x1d, 0x1a, 0x79, 0xfc, 0x3e, 0x63, 0xb0, 0xdc, 0xab, 0xf1, 0x0f, 0x2a, + 0xc0, 0xbf, 0x30, 0x12, 0x90, 0xec, 0xeb, 0x5a, 0xb1, 0x28, 0x0b, 0xbc, + 0x6a, 0xf1, 0x30, 0xf9, 0x22, 0xf1, 0x52, 0x9c, 0xd9, 0x92, 0x50, 0x2d, + 0x60, 0x5b, 0xd8, 0x87, 0x9c, 0x6c, 0x05, 0x99, 0xb9, 0x8e, 0xd1, 0x14, + 0x20, 0xa8, 0xf8, 0xef, 0x35, 0x1c, 0xd3, 0x19, 0x0e, 0x81, 0x04, 0x0b, + 0xf1, 0xf8, 0x64, 0xa8, 0xbe, 0xa7, 0x6d, 0x58, 0xb5, 0x96, 0x20, 0xf0, + 0x51, 0x9c, 0x57, 0x57, 0x27, 0x6b, 0xd1, 0xec, 0xf2, 0x2a, 0x61, 0xfe, + 0x1c, 0x87, 0x0b, 0xe6, 0x75, 0xd6, 0xac, 0x4a, 0x8d, 0xdb, 0x5b, 0x16, + 0x82, 0x2d, 0x8a, 0x23, 0xc5, 0xdb, 0x30, 0xbf, 0xa8, 0x42, 0xd6, 0xd5, + 0xf0, 0xe5, 0x5c, 0x8b, 0xce, 0xc8, 0x6e, 0x94, 0x9f, 0x44, 0x2e, 0x7f, + 0xc9, 0x9f, 0xbf, 0xba, 0x5b, 0xa0, 0x2e, 0xc7, 0x83, 0xe7, 0xb5, 0x74, + 0x05, 0xed, 0x27, 0xdf, 0x04, 0x01, 0x3e, 0xa2, 0x1a, 0x7d, 0x91, 0x9e, + 0xdd, 0x34, 0x49, 0x74, 0x85, 0xca, 0xcd, 0x24, 0xc5, 0x4a, 0x84, 0xab, + 0x3c, 0x7e, 0xee, 0xa5, 0xec, 0x82, 0x63, 0x8f, 0x72, 0x43, 0x16, 0x75, + 0x35, 0xac, 0xd5, 0x43, 0x10, 0x74, 0xbc, 0xe9, 0x3a, 0x24, 0xa4, 0x27, + 0xb3, 0xfd, 0xf4, 0x2a, 0xbf, 0x39, 0xed, 0x6a, 0x03, 0x98, 0xc2, 0x35, + 0x9a, 0x25, 0x4a, 0xa6, 0xeb, 0x6d, 0xa1, 0xdb, 0xb5, 0x6a, 0xb7, 0x49, + 0xd5, 0xc7, 0x84, 0x8c, 0x5d, 0x43, 0xea, 0x76, 0xca, 0xe4, 0xca, 0x06, + 0x56, 0xed, 0xd5, 0x5c, 0xba, 0x24, 0xd4, 0x5d, 0x99, 0x19, 0x9f, 0xcd, + 0x81, 0xb8, 0x8f, 0xa6, 0x85, 0x85, 0x53, 0xe2, 0x15, 0x1e, 0x86, 0x83, + 0x38, 0x84, 0x79, 0x1d, 0x35, 0xd9, 0x8d, 0xce, 0x3b, 0x70, 0x13, 0xa7, + 0xaa, 0x54, 0x5e, 0x88, 0x26, 0x7e, 0x17, 0x00, 0xe9, 0x86, 0xae, 0x4a, + 0xf9, 0x58, 0x49, 0xe7, 0x5e, 0x92, 0xeb, 0x96, 0xc2, 0xa8, 0x74, 0x49, + 0xa1, 0xb2, 0x2d, 0x4e, 0x2e, 0x8e, 0xe2, 0xc7, 0x08, 0xef, 0x2a, 0x98, + 0x9d, 0x6a, 0xfa, 0xfd, 0x92, 0xfb, 0x79, 0x60, 0x8f, 0xea, 0x95, 0xd3, + 0x07, 0x2b, 0x90, 0x8f, 0x0a, 0x31, 0x83, 0x49, 0x56, 0x05, 0xca, 0xa4, + 0x67, 0x83, 0xe1, 0x76, 0x37, 0x55, 0x22, 0x6b, 0x0c, 0xca, 0x0c, 0xe5, + 0x6f, 0x1c, 0x54, 0x2d, 0xe4, 0x30, 0xaf, 0x8d, 0xbe, 0x32, 0x95, 0x4f, + 0xe8, 0xab, 0x64, 0x66, 0x3a, 0x4e, 0x6b, 0xef, 0x0c, 0x1f, 0x70, 0x1d, + 0x85, 0x03, 0xbc, 0x27, 0x88, 0x60, 0x07, 0xd0, 0x8c, 0x43, 0xe4, 0xb6, + 0xe8, 0x58, 0xe3, 0xcf, 0x8b, 0x3f, 0x47, 0xfd, 0xc8, 0x20, 0x60, 0xb1, + 0xaa, 0x06, 0x94, 0xcd, 0x76, 0x27, 0x26, 0x13, 0x51, 0x32, 0x27, 0xab, + 0xf8, 0x53, 0x81, 0x76, 0xdf, 0x6a, 0x7e, 0xf8, 0xdd, 0x33, 0x7e, 0x68, + 0x0a, 0xc8, 0x7c, 0x30, 0x98, 0xb2, 0xf4, 0x9e, 0x77, 0x40, 0x4f, 0x4e, + 0xd1, 0x39, 0x94, 0x07, 0x1b, 0x04, 0xfe, 0x11, 0xa2, 0xf8, 0x44, 0x5d, + 0x4d, 0x28, 0x81, 0xe2, 0xd3, 0xe2, 0x39, 0x46, 0x1c, 0x2e, 0x7c, 0xc4, + 0xda, 0x39, 0xd2, 0x02, 0x19, 0x58, 0x12, 0x1a, 0xee, 0xde, 0x38, 0x8b, + 0x0c, 0x6d, 0x29, 0xa9, 0x86, 0xb0, 0xba, 0x11, 0xa4, 0x2f, 0x63, 0x67, + 0xa7, 0xb5, 0xf0, 0xeb, 0x9e, 0x74, 0x15, 0xf6, 0xec, 0xd9, 0x16, 0x91, + 0x55, 0x71, 0xcc, 0x31, 0x26, 0x01, 0x0b, 0x03, 0xc6, 0x2a, 0xb9, 0x18, + 0x26, 0x48, 0xf8, 0x7f, 0x59, 0x4e, 0xbb, 0xf2, 0x30, 0x0a, 0xb8, 0x37, + 0x56, 0xb6, 0x04, 0x4a, 0x0f, 0x44, 0xb1, 0x94, 0x23, 0xaf, 0x23, 0x7e, + 0xef, 0x83, 0xe1, 0xf2, 0x78, 0x0e, 0x2c, 0x35, 0xa0, 0x95, 0xb7, 0xb9, + 0x09, 0x1b, 0x1c, 0x46, 0x41, 0x95, 0x8d, 0x7c, 0x55, 0x52, 0x24, 0x34, + 0x9d, 0xd7, 0xff, 0xa1, 0x59, 0xef, 0x7a, 0xd8, 0x9d, 0x58, 0x00, 0x40, + 0xc7, 0xae, 0xe0, 0x8f, 0x78, 0xea, 0x57, 0xff, 0x68, 0xbc, 0xc4, 0x53, + 0x3c, 0xf7, 0xc9, 0xa6, 0xc3, 0x4f, 0xe1, 0x25, 0xfd, 0x83, 0x27, 0xb9, + 0x04, 0x3e, 0x31, 0x4c, 0xf3, 0x4e, 0xb3, 0x32, 0x51, 0x44, 0x92, 0x8c, + 0x46, 0x2e, 0x74, 0xa7, 0x1f, 0xfe, 0x3f, 0xf5, 0xde, 0xa8, 0xbc, 0xc8, + 0xfd, 0x93, 0xcb, 0x24, 0x80, 0xa5, 0x5e, 0x21, 0x95, 0x8c, 0x17, 0xdd, + 0xbb, 0x84, 0x16, 0x38, 0x69, 0x64, 0x48, 0xa7, 0xf2, 0x0a, 0x33, 0x46, + 0xc2, 0xd9, 0xca, 0x24, 0x00, 0x3c, 0xaa, 0xae, 0xf3, 0xe6, 0x5b, 0x17, + 0x8b, 0xe7, 0x43, 0xbd, 0xef, 0x8f, 0xb1, 0x61, 0x23, 0x03, 0x89, 0x65, + 0xdc, 0x76, 0xe1, 0x09, 0xf6, 0x4d, 0xc1, 0x87, 0x4b, 0x8c, 0xf8, 0x47, + 0x7a, 0x48, 0x82, 0x29, 0xff, 0xaf, 0x3f, 0x4a, 0xf8, 0xff, 0x98, 0x8d, + 0x77, 0xe6, 0x7d, 0xb4, 0xd7, 0x7d, 0x14, 0x62, 0x37, 0xf2, 0x28, 0x87, + 0x84, 0x6c, 0xce, 0x3c, 0x67, 0x6c, 0x14, 0xf8, 0xce, 0x2f, 0x47, 0x0e, + 0x8f, 0xb7, 0x77, 0xa0, 0x6e, 0x7a, 0x3e, 0x4c, 0x69, 0x22, 0x00, 0xe5, + 0x29, 0xf0, 0xa7, 0xc2, 0x21, 0x0b, 0xfc, 0x9c, 0x2a, 0x9c, 0x4a, 0x40, + 0xe3, 0x4d, 0x67, 0xca, 0xd7, 0x30, 0x2e, 0x11, 0xc7, 0xae, 0x9d, 0xe8, + 0x71, 0x48, 0x5f, 0x5f, 0xaf, 0x80, 0x86, 0xd0, 0x8d, 0x79, 0xcc, 0x49, + 0x99, 0x58, 0x56, 0x59, 0xc8, 0xe9, 0xb1, 0x43, 0x49, 0xf6, 0xac, 0x02, + 0x3a, 0x79, 0x46, 0xe3, 0x8a, 0x06, 0x40, 0x4d, 0xd5, 0x49, 0x51, 0xee, + 0x88, 0x84, 0xe1, 0xc4, 0x13, 0xac, 0xf9, 0x7b, 0x1b, 0xc0, 0xe5, 0x9e, + 0xa5, 0xe6, 0xda, 0xbd, 0x4b, 0xfe, 0xbd, 0x77, 0x43, 0xd7, 0xe6, 0x8e, + 0x70, 0x34, 0x4f, 0x23, 0x98, 0xea, 0x59, 0x50, 0x63, 0xd2, 0xb4, 0xbb, + 0x3b, 0x66, 0x7a, 0xd4, 0x66, 0xce, 0x10, 0xdb, 0xdd, 0x14, 0x23, 0x93, + 0xa1, 0x95, 0x09, 0x91, 0x04, 0x03, 0xeb, 0x6f, 0xe4, 0xe9, 0x37, 0x56, + 0x03, 0x04, 0x0b, 0xb8, 0x3d, 0xb4, 0x20, 0xcd, 0xf0, 0x45, 0x1d, 0x08, + 0x22, 0x70, 0x84, 0xf1, 0xa9, 0x88, 0x01, 0xd1, 0xd5, 0xa0, 0x3f, 0xa1, + 0x00, 0xba, 0xc5, 0x2c, 0x50, 0x9f, 0x49, 0x80, 0xd6, 0x1b, 0x16, 0xcf, + 0x5c, 0x3b, 0x3c, 0xd1, 0xe6, 0xf5, 0xc7, 0x4b, 0xdc, 0x55, 0xe1, 0x27, + 0xde, 0x29, 0xca, 0x6e, 0xaa, 0x70, 0x42, 0x6d, 0x79, 0xcf, 0xee, 0x79, + 0xf2, 0xe2, 0x86, 0x98, 0x80, 0xd8, 0xf5, 0x68, 0xc8, 0x32, 0xa9, 0x0d, + 0xf0, 0x2e, 0x2a, 0x02, 0x50, 0x71, 0x4e, 0x53, 0x4f, 0x32, 0xab, 0x72, + 0xfd, 0x6d, 0x61, 0x30, 0xb3, 0x3f, 0xcb, 0xdb, 0x24, 0x18, 0xc0, 0xa2, + 0xdb, 0x77, 0x38, 0x1d, 0xa9, 0xdc, 0x39, 0xd2, 0x88, 0x97, 0xf9, 0xb7, + 0xcc, 0x4d, 0xcd, 0x81, 0x5d, 0xc8, 0xd5, 0xc0, 0xaa, 0x3d, 0xb5, 0x7f, + 0x17, 0xf6, 0xf8, 0x50, 0x23, 0xfa, 0xf2, 0xe6, 0xba, 0x8a, 0xd5, 0xb8, + 0x22, 0x90, 0xf3, 0x71, 0x4c, 0xdb, 0x36, 0x16, 0x50, 0xbc, 0x71, 0x96, + 0xa8, 0x8c, 0xa6, 0x10, 0xe7, 0x9c, 0x7b, 0xdd, 0xc5, 0xbd, 0x51, 0xd0, + 0xfa, 0xf4, 0x9e, 0x8e, 0x9b, 0xac, 0xd5, 0xbd, 0x3e, 0xfd, 0x98, 0x44, + 0xb3, 0x83, 0xab, 0x50, 0xa7, 0x24, 0xf4, 0x1f, 0xc5, 0x52, 0x0f, 0xf5, + 0xed, 0xa2, 0x4a, 0xb3, 0x2d, 0xdb, 0x87, 0x27, 0x4a, 0x7f, 0x34, 0xbf, + 0x35, 0x65, 0x19, 0xed, 0x05, 0x97, 0x8b, 0xe3, 0xe8, 0x92, 0x98, 0xbc, + 0x8e, 0x7a, 0x88, 0x0b, 0xa2, 0x0a, 0x18, 0xf2, 0xe1, 0xbb, 0xf7, 0x57, + 0xea, 0xa9, 0x74, 0x32, 0xb3, 0xb3, 0xb6, 0x02, 0x6a, 0x66, 0xca, 0xfe, + 0x78, 0x79, 0x1e, 0x54, 0x2b, 0x8d, 0xa5, 0x5c, 0x4b, 0x37, 0xa0, 0xe1, + 0xe1, 0x3b, 0xa2, 0x8a, 0xc3, 0xe8, 0x47, 0xd2, 0x2c, 0x7c, 0xe0, 0x2f, + 0x68, 0x33, 0xe6, 0x44, 0x0a, 0xbf, 0x98, 0x7e, 0xcb, 0x1b, 0xe3, 0x9c, + 0xae, 0x23, 0xee, 0xe0, 0x2c, 0x10, 0x27, 0x2f, 0xa4, 0x53, 0xa1, 0xd4, + 0xa5, 0x71, 0xfb, 0x64, 0xae, 0xac, 0xf1, 0x58, 0xb7, 0xab, 0xbf, 0x1c, + 0x56, 0xce, 0xf9, 0x7c, 0xdd, 0x9a, 0x04, 0xde, 0xe3, 0x80, 0x69, 0x5d, + 0x49, 0x06, 0x43, 0x8e, 0x5a, 0x16, 0x77, 0x6f, 0x97, 0xd3, 0xc5, 0xac, + 0xbd, 0x64, 0xf7, 0x97, 0x25, 0x10, 0xc5, 0x77, 0x60, 0x49, 0x31, 0x75, + 0x96, 0x5f, 0x35, 0xce, 0xcf, 0xcc, 0xf3, 0x84, 0xd6, 0x50, 0xf6, 0x7e, + 0x43, 0xe0, 0xd2, 0x81, 0xa0, 0x4e, 0xda, 0x14, 0x9f, 0xe2, 0xb3, 0x04, + 0xc8, 0x7a, 0x62, 0x0c, 0x4a, 0x37, 0x30, 0x4c, 0x6b, 0x38, 0x6d, 0xa4, + 0x20, 0x8b, 0x1e, 0x72, 0x6f, 0xd7, 0x79, 0x60, 0x06, 0x96, 0xb0, 0x6b, + 0x73, 0x60, 0xc0, 0x1a, 0x72, 0x8a, 0x69, 0xa6, 0x4a, 0x6f, 0x66, 0xc6, + 0x1e, 0x87, 0x46, 0x24, 0x93, 0x48, 0x5e, 0x5d, 0x65, 0xe2, 0x40, 0x8e, + 0xb3, 0x4e, 0xbb, 0xae, 0x4f, 0xbe, 0xb8, 0x78, 0x4f, 0x6c, 0x4d, 0xf0, + 0xb1, 0x58, 0xcc, 0x27, 0x74, 0x0a, 0xb0, 0xed, 0xd8, 0xed, 0x09, 0x51, + 0x3c, 0xd0, 0x17, 0xc2, 0xfa, 0xfc, 0xd5, 0x32, 0xd6, 0x3b, 0x8a, 0x3b, + 0x3a, 0x46, 0x55, 0x35, 0x54, 0x21, 0x84, 0x8c, 0x94, 0x0d, 0xc2, 0xb6, + 0xce, 0x86, 0x1e, 0x94, 0x73, 0x12, 0xf2, 0x3f, 0x47, 0x5f, 0x56, 0x03, + 0x22, 0x52, 0x4e, 0xc2, 0x0b, 0x8d, 0x46, 0xc3, 0xaa, 0xbe, 0xe0, 0x74, + 0x1c, 0x21, 0xba, 0x28, 0x50, 0x77, 0x2c, 0xc5, 0x8a, 0x87, 0xf8, 0x71, + 0xc6, 0x8b, 0x28, 0xc7, 0x46, 0x8b, 0x4e, 0xf5, 0x7d, 0x3c, 0xfd, 0xf9, + 0x7f, 0x33, 0xec, 0x22, 0x1c, 0xed, 0x99, 0x1c, 0x75, 0x93, 0x6a, 0x28, + 0x5f, 0xaf, 0x8b, 0x8c, 0xcb, 0xfa, 0x9b, 0x27, 0x87, 0x0a, 0x7a, 0x53, + 0xba, 0x05, 0xcd, 0x24, 0x8a, 0x1e, 0xd3, 0x71, 0xf1, 0x97, 0xd8, 0x28, + 0xea, 0x9d, 0x8c, 0xbc, 0x2e, 0x6e, 0xb8, 0xea, 0x14, 0xce, 0xd7, 0x70, + 0x7f, 0xb6, 0x52, 0xa3, 0xa7, 0xf5, 0xbd, 0x23, 0x59, 0x9e, 0xc6, 0xe0, + 0xf4, 0xef, 0x2f, 0xac, 0x15, 0x5a, 0xa3, 0xcd, 0xbe, 0xaa, 0x9e, 0x15, + 0x7c, 0xad, 0xca, 0x7a, 0xbe, 0xb3, 0x28, 0xb4, 0xe8, 0x39, 0xdb, 0x9a, + 0xe9, 0x64, 0xb3, 0xa2, 0x89, 0xa2, 0x5d, 0xfb, 0x19, 0x6c, 0x4e, 0xcc, + 0xc8, 0xa1, 0x93, 0x73, 0x91, 0xc2, 0xfc, 0xf5, 0x3a, 0x58, 0x37, 0x1e, + 0xa6, 0x00, 0xb1, 0x61, 0x1c, 0xd8, 0xe8, 0x7b, 0x0f, 0xe1, 0x10, 0x66, + 0xf8, 0x7c, 0x66, 0x02, 0x9e, 0x85, 0xdb, 0xcc, 0x11, 0x2c, 0x38, 0xef, + 0xc0, 0x14, 0x07, 0xea, 0xb4, 0xdb, 0xc5, 0x5b, 0x76, 0xd7, 0xb8, 0xa2, + 0x73, 0xa7, 0xc5, 0x90, 0x06, 0x3a, 0x75, 0x5d, 0xae, 0x7f, 0x43, 0x9d, + 0xd4, 0x25, 0xf4, 0x6e, 0x06, 0x6f, 0x12, 0x4f, 0xa5, 0x81, 0x09, 0x62, + 0x12, 0x06, 0x79, 0xf8, 0x16, 0xcc, 0x5a, 0x27, 0x52, 0xd9, 0x57, 0x3d, + 0xd0, 0x61, 0xce, 0xfd, 0xc8, 0x8e, 0xcd, 0xfb, 0xe4, 0xfb, 0x3e, 0xd1, + 0x88, 0xd0, 0xb9, 0x77, 0x53, 0x03, 0xed, 0x31, 0x3a, 0xd7, 0x0f, 0x5f, + 0x82, 0xfd, 0x38, 0x82, 0xb9, 0x6e, 0xf0, 0xcf, 0x8a, 0x19, 0x7a, 0x69, + 0x33, 0xe3, 0x5b, 0x91, 0x52, 0x68, 0x85, 0x10, 0xdc, 0xa8, 0x13, 0xa6, + 0xbe, 0x2b, 0xbc, 0xe3, 0x57, 0x3f, 0x1c, 0x93, 0x48, 0x98, 0x2c, 0x3c, + 0x6a, 0x8c, 0x61, 0x75, 0x5b, 0x88, 0xeb, 0xa2, 0x39, 0x7e, 0xde, 0xf3, + 0x77, 0x6f, 0xfe, 0x61, 0xc7, 0xbe, 0xde, 0xb7, 0x13, 0x41, 0x9a, 0xa1, + 0xd8, 0x1a, 0x06, 0xb2, 0xe7, 0xdb, 0xdc, 0x9b, 0xcf, 0x1e, 0x93, 0x25, + 0x1f, 0xba, 0xe6, 0x1b, 0xe5, 0xc7, 0x7e, 0x9f, 0x2e, 0x4b, 0x54, 0x8f, + 0xf2, 0x32, 0xb0, 0xed, 0x29, 0xe6, 0xa2, 0xd5, 0xc8, 0xe1, 0x39, 0xb7, + 0x9c, 0x93, 0xde, 0x45, 0x68, 0x35, 0x75, 0xd3, 0xb1, 0xed, 0xc3, 0x4b, + 0xd1, 0xd8, 0x7f, 0x1f, 0xc4, 0xa0, 0x40, 0xc7, 0xbc, 0x32, 0x0e, 0xb1, + 0x19, 0xe3, 0x61, 0x4b, 0xb9, 0x15, 0x51, 0x1b, 0xfb, 0x5e, 0x88, 0x9f, + 0xb2, 0xa5, 0xd9, 0x52, 0x0d, 0x4f, 0xb7, 0x7d, 0x20, 0x0a, 0x25, 0xcf, + 0xff, 0x5e, 0x2d, 0xca, 0xe9, 0x95, 0x9e, 0x37, 0x9d, 0xfb, 0x1e, 0x2b, + 0x8f, 0x04, 0x03, 0x83, 0x9c, 0x59, 0xf8, 0xfd, 0x9f, 0xfe, 0x03, 0x3b, + 0x2e, 0xdc, 0x82, 0x67, 0xce, 0xf8, 0xa9, 0x68, 0x5e, 0x83, 0x55, 0xba, + 0x60, 0xd3, 0x88, 0x9a, 0x69, 0x6b, 0x2e, 0xae, 0xab, 0x31, 0x33, 0xa7, + 0x36, 0xa4, 0x10, 0x8a, 0xab, 0x8c, 0xa4, 0x68, 0xdd, 0xa4, 0x7c, 0xb8, + 0x7d, 0xf8, 0x51, 0xcc, 0xd7, 0x1b, 0x35, 0x0d, 0x80, 0xb4, 0x3b, 0x9f, + 0xad, 0xdf, 0x8d, 0xfa, 0xb6, 0x2a, 0xb9, 0x3e, 0x2e, 0x0a, 0x69, 0x93, + 0xf3, 0xe6, 0x18, 0xd3, 0x61, 0x96, 0x01, 0x78, 0xb4, 0xe9, 0xbf, 0x26, + 0xd7, 0xab, 0xc5, 0x6a, 0x73, 0x97, 0xcb, 0xe3, 0x22, 0xaf, 0xb2, 0x65, + 0x2c, 0xc3, 0xde, 0xbe, 0x9f, 0x53, 0xfa, 0x9b, 0xe7, 0xb7, 0x53, 0xa7, + 0x81, 0xb4, 0xc1, 0xc1, 0x84, 0xd4, 0xeb, 0x80, 0xfc, 0x72, 0xfe, 0x76, + 0xfa, 0xa3, 0x63, 0x1b, 0xd8, 0xb2, 0xc1, 0xca, 0x50, 0xc7, 0x9b, 0x03, + 0xdb, 0x6e, 0xb6, 0xd9, 0x7c, 0x91, 0x9b, 0xd8, 0xf5, 0x4a, 0x9c, 0xb4, + 0x2e, 0xdd, 0xd4, 0xdb, 0x32, 0xf3, 0x47, 0x14, 0x56, 0xd2, 0xee, 0x13, + 0xab, 0x5c, 0x6c, 0x00, 0x14, 0x23, 0xc2, 0xbe, 0xd5, 0x91, 0xf0, 0x8b, + 0xfa, 0xbe, 0x3b, 0xb5, 0x1e, 0x18, 0x8d, 0xae, 0x82, 0xbb, 0x94, 0x88, + 0xcf, 0xf7, 0x27, 0xa8, 0x81, 0xea, 0x2d, 0x94, 0x51, 0xcf, 0xf7, 0x8c, + 0x92, 0x46, 0x63, 0xda, 0xc9, 0x9f, 0x59, 0xa7, 0xe3, 0x2e, 0xd3, 0x08, + 0x6b, 0x06, 0xb3, 0x96, 0x00, 0xbb, 0x31, 0xe8, 0x97, 0xaf, 0x81, 0x8f, + 0xe2, 0x77, 0xf5, 0xb7, 0x70, 0xf9, 0xda, 0xc9, 0xb1, 0x0a, 0x9b, 0x8d, + 0xf2, 0xd0, 0x9d, 0x29, 0x0f, 0x2d, 0x33, 0x4c, 0x0e, 0xa9, 0x7d, 0x6e, + 0x68, 0x64, 0x14, 0x11, 0x88, 0xc9, 0x35, 0x36, 0x51, 0xfc, 0x0b, 0x84, + 0xff, 0xd6, 0xb8, 0xa9, 0x2d, 0xd5, 0xa2, 0xfb, 0x8a, 0x15, 0x26, 0x77, + 0x83, 0xaa, 0x3c, 0xbf, 0x40, 0x4b, 0x22, 0x94, 0x95, 0x71, 0xb8, 0x27, + 0x93, 0x54, 0x04, 0x27, 0x37, 0x84, 0x6d, 0xf6, 0x07, 0x80, 0xf5, 0x05, + 0x14, 0xfb, 0x9d, 0xfe, 0xfe, 0x72, 0x53, 0x15, 0x8f, 0xa3, 0xf5, 0x25, + 0x7a, 0x99, 0x48, 0x3b, 0xe0, 0xa6, 0x0f, 0x12, 0xa5, 0x29, 0x44, 0x0e, + 0x47, 0x54, 0x02, 0x4a, 0xa8, 0x66, 0xe9, 0xa8, 0xf4, 0xdf, 0xb8, 0x71, + 0x26, 0x70, 0xb3, 0xef, 0x3f, 0x87, 0x90, 0x73, 0x3e, 0xdc, 0x0e, 0xf8, + 0xc7, 0x3a, 0xbe, 0x5d, 0x0f, 0xad, 0x49, 0x8d, 0xfb, 0x49, 0x17, 0xa5, + 0x7f, 0xe2, 0x5a, 0x64, 0xd3, 0x79, 0xc1, 0xfa, 0xe8, 0x56, 0xc6, 0x07, + 0x4b, 0x60, 0x97, 0x9a, 0x78, 0xb4, 0x10, 0xd6, 0xb9, 0x6d, 0x22, 0x75, + 0xc9, 0x21, 0x22, 0xe1, 0x14, 0xcc, 0x6e, 0x5f, 0x01, 0x42, 0x5b, 0x10, + 0x4c, 0x6d, 0x21, 0x0b, 0x72, 0xb8, 0xeb, 0xb9, 0x7b, 0x96, 0x39, 0xbf, + 0x3a, 0x45, 0x48, 0xa8, 0xe6, 0xef, 0xa6, 0x52, 0xf0, 0x7e, 0x9e, 0x61, + 0xd6, 0x77, 0xb6, 0xbd, 0x52, 0xa1, 0xf9, 0x9c, 0x3c, 0xd3, 0xf7, 0x39, + 0x71, 0xa1, 0xfc, 0xa8, 0xf2, 0x85, 0x30, 0xa5, 0x8a, 0xed, 0x3d, 0x74, + 0x83, 0xd7, 0x39, 0xa8, 0xf9, 0x2f, 0xbf, 0xa5, 0x09, 0x72, 0x1f, 0x42, + 0x9f, 0xe4, 0x85, 0x75, 0x85, 0xad, 0xe1, 0x04, 0x34, 0xd2, 0xb2, 0x70, + 0x06, 0x71, 0xcb, 0x5e, 0x91, 0x97, 0x8a, 0x2c, 0xab, 0x6c, 0xd0, 0x9d, + 0xef, 0x32, 0x77, 0x49, 0xbe, 0x58, 0x66, 0xc9, 0x39, 0xc5, 0x17, 0xba, + 0x9c, 0xac, 0x58, 0xd5, 0x36, 0x11, 0xe5, 0x9d, 0x44, 0x6b, 0xfa, 0x72, + 0x2f, 0x05, 0xf8, 0x87, 0x6a, 0x36, 0x1c, 0xe8, 0xc8, 0xa6, 0x70, 0x42, + 0x7b, 0x69, 0x6b, 0x19, 0xa6, 0xb7, 0x23, 0x32, 0xa1, 0xc2, 0xd1, 0xfa, + 0x46, 0x66, 0x36, 0x5f, 0x85, 0x3b, 0x3c, 0xe3, 0x21, 0xa4, 0xcb, 0xf3, + 0xc9, 0xf2, 0xa8, 0x7e, 0xdc, 0x00, 0xc5, 0x0d, 0x94, 0xf0, 0xb5, 0x14, + 0x00, 0xe8, 0x15, 0xd1, 0xf2, 0x26, 0xa6, 0x1b, 0x38, 0x17, 0x70, 0xbd, + 0xde, 0x70, 0x68, 0x9e, 0xf6, 0x01, 0xb6, 0x72, 0xda, 0x6e, 0xde, 0x56, + 0xdf, 0xb4, 0x7d, 0xc2, 0x63, 0x2e, 0x05, 0xac, 0x62, 0xf6, 0x3b, 0x01, + 0x8e, 0x64, 0x64, 0x81, 0x70, 0x1a, 0x61, 0xd3, 0x5f, 0xdc, 0xf0, 0x4b, + 0xe8, 0xb5, 0x57, 0xb4, 0xaa, 0x97, 0x08, 0xa2, 0xea, 0x67, 0x5f, 0xc9, + 0x0e, 0x2b, 0x1f, 0x57, 0x75, 0xb6, 0xbd, 0xde, 0xfa, 0x6a, 0xef, 0x64, + 0x71, 0x75, 0x8d, 0x41, 0x7b, 0x26, 0x74, 0x4a, 0x4d, 0x3d, 0x5f, 0xbe, + 0x0b, 0x27, 0x3a, 0x3c, 0xa2, 0x1f, 0x2a, 0x68, 0x52, 0x09, 0xf4, 0x8f, + 0x44, 0x6a, 0x3d, 0x91, 0x70, 0x1c, 0x43, 0x76, 0x76, 0x0c, 0xf5, 0xe2, + 0xe1, 0x31, 0xa1, 0x7d, 0xd6, 0x8a, 0xfa, 0x4f, 0xbc, 0xd3, 0xcd, 0x0c, + 0xa4, 0x3d, 0x83, 0xaa, 0xb5, 0xee, 0x23, 0xa8, 0xa9, 0x8d, 0xce, 0x92, + 0x1a, 0xac, 0x86, 0x22, 0x9e, 0x4e, 0x8b, 0xa0, 0x84, 0xcd, 0xf5, 0xb0, + 0xec, 0x2c, 0x25, 0xbc, 0x26, 0xb6, 0x35, 0x44, 0x2d, 0x32, 0x7a, 0x69, + 0xa5, 0x70, 0x15, 0x11, 0x43, 0x4e, 0xd4, 0x4e, 0xe0, 0x81, 0x21, 0x33, + 0xa9, 0xc2, 0x42, 0xc4, 0x66, 0xe9, 0xf4, 0xce, 0xb1, 0xc3, 0x10, 0x85, + 0x2f, 0xcf, 0xd1, 0x34, 0x48, 0x03, 0x78, 0xf2, 0xdf, 0xde, 0x37, 0x22, + 0x0b, 0x65, 0x9a, 0xb5, 0x54, 0xe7, 0x03, 0x2c, 0x2d, 0x80, 0x11, 0x98, + 0x18, 0xf7, 0x85, 0x4a, 0xde, 0x17, 0xfd, 0xec, 0x3f, 0x71, 0x79, 0xfe, + 0x35, 0x82, 0x66, 0x4c, 0x94, 0xcd, 0x23, 0x21, 0x81, 0x61, 0x33, 0x44, + 0x34, 0xdd, 0x2a, 0x82, 0x6c, 0x8f, 0xfa, 0x5f, 0x61, 0x9e, 0x18, 0x80, + 0x49, 0xce, 0x09, 0xd0, 0xf6, 0x2d, 0xf0, 0x8a, 0xcf, 0x07, 0xc8, 0x45, + 0x30, 0xa8, 0x61, 0x0f, 0x65, 0x72, 0x32, 0x2a, 0x13, 0x00, 0x55, 0x73, + 0x81, 0xc5, 0x3b, 0x5a, 0x20, 0x1f, 0xe0, 0x39, 0x26, 0xb1, 0xb5, 0x2f, + 0xa7, 0x96, 0x82, 0x60, 0x6b, 0xed, 0xda, 0x99, 0x25, 0xdb, 0x37, 0x92, + 0x63, 0xba, 0x71, 0x69, 0x3e, 0x3e, 0xc0, 0x15, 0xf4, 0x56, 0xb9, 0xcc, + 0x15, 0xf1, 0x1d, 0x5a, 0x6e, 0xda, 0xde, 0x69, 0xf3, 0x6f, 0x60, 0x5a, + 0xd6, 0xa3, 0x29, 0x1d, 0x6c, 0x00, 0x56, 0x28, 0x2a, 0x8e, 0xac, 0x47, + 0x1e, 0xd1, 0x1a, 0xef, 0x4a, 0xb1, 0x23, 0x2f, 0x97, 0x87, 0x1a, 0xe0, + 0x9c, 0x8a, 0x05, 0xad, 0xfc, 0xd7, 0x5f, 0xc5, 0xc4, 0xe8, 0xe3, 0xf5, + 0x9f, 0x77, 0x1e, 0x98, 0x9f, 0x22, 0x40, 0x29, 0x96, 0xb3, 0x04, 0xc0, + 0x33, 0x18, 0x5a, 0x6f, 0xae, 0xd7, 0xeb, 0xca, 0x05, 0x49, 0xa9, 0xc6, + 0x5e, 0xe8, 0x11, 0x6e, 0x09, 0x14, 0xc4, 0xd0, 0x67, 0x21, 0x7d, 0x88, + 0xcd, 0xd9, 0x8b, 0x7a, 0x09, 0xa1, 0x6e, 0x49, 0x9d, 0x79, 0xa1, 0xe0, + 0x61, 0xbe, 0xfd, 0x83, 0x90, 0xec, 0x49, 0x9e, 0x86, 0xb8, 0xa1, 0xfe, + 0x23, 0x3c, 0x52, 0x6b, 0x58, 0xfc, 0xcb, 0x34, 0x47, 0xbe, 0x23, 0x3f, + 0x47, 0xb6, 0x59, 0x98, 0x96, 0x7f, 0x26, 0x4b, 0x1d, 0x93, 0x9e, 0x59, + 0x9e, 0x3f, 0x9f, 0x1d, 0x7a, 0xf2, 0x6c, 0xd3, 0xdf, 0x75, 0xb6, 0xc9, + 0xee, 0x8a, 0x70, 0x71, 0x7e, 0x51, 0xb4, 0xc3, 0x2a, 0xf0, 0x7b, 0x2b, + 0xcb, 0x5f, 0x80, 0xdd, 0xce, 0x98, 0x3b, 0x6b, 0x09, 0x08, 0xd6, 0x89, + 0x9a, 0x85, 0xac, 0xa1, 0x58, 0xe2, 0xad, 0x19, 0x33, 0x44, 0xab, 0xf1, + 0x41, 0xb0, 0x51, 0x90, 0x49, 0x36, 0x6e, 0x7f, 0xe3, 0x94, 0x7d, 0xcc, + 0x94, 0x08, 0x8d, 0x6e, 0xde, 0x4e, 0x67, 0x70, 0x66, 0xfd, 0x93, 0x4c, + 0xc7, 0xd7, 0x20, 0xcd, 0x95, 0x20, 0x11, 0xb4, 0x36, 0xdd, 0x26, 0x5b, + 0xaf, 0xa3, 0xb2, 0x72, 0xb2, 0x9e, 0xd0, 0xb4, 0xb5, 0x63, 0x5a, 0x0c, + 0xf8, 0x71, 0xd6, 0x2a, 0x55, 0xea, 0xc5, 0x39, 0xf8, 0xcb, 0xe2, 0xe1, + 0x8a, 0x0f, 0x24, 0xe1, 0x83, 0xd1, 0xee, 0xcd, 0x80, 0xa4, 0x52, 0xb0, + 0xc7, 0x66, 0x53, 0x47, 0xb6, 0xd4, 0x79, 0x07, 0x11, 0xa6, 0x33, 0x3f, + 0x8b, 0xc6, 0x91, 0xdd, 0x01, 0x1d, 0x71, 0x32, 0xed, 0xf3, 0xa7, 0xa4, + 0x4d, 0xcf, 0xa7, 0x6d, 0x2a, 0xde, 0x7b, 0x2c, 0x8d, 0x89, 0x8c, 0xd7, + 0x14, 0x0d, 0x3d, 0x9d, 0x67, 0x62, 0x3f, 0x6b, 0xd8, 0xa2, 0x3d, 0xb3, + 0x7a, 0xab, 0x0c, 0x7a, 0xb0, 0x7f, 0x5b, 0x35, 0x39, 0xf3, 0xbe, 0x46, + 0x84, 0xd8, 0x5b, 0x44, 0x7b, 0x3a, 0xcc, 0x0c, 0xcc, 0x60, 0xb9, 0x7b, + 0x23, 0x1e, 0x0c, 0x73, 0xe2, 0x07, 0x79, 0xd0, 0xfd, 0x9d, 0x66, 0xf6, + 0xe5, 0xfe, 0x3f, 0x75, 0x4f, 0x03, 0x2c, 0xcd, 0x6c, 0xbe, 0xf3, 0x59, + 0xbf, 0xf2, 0x35, 0xfa, 0xfa, 0xd6, 0xeb, 0x50, 0x7c, 0x26, 0x71, 0xb4, + 0xe3, 0xb7, 0xc0, 0x23, 0xba, 0x49, 0x00, 0x7a, 0xf4, 0x41, 0xfa, 0xb6, + 0xb6, 0x44, 0x52, 0xfb, 0x20, 0x0c, 0x38, 0xe3, 0xa9, 0xb0, 0x32, 0x98, + 0xc0, 0x78, 0xaa, 0xc1, 0xba, 0xa8, 0x75, 0xcd, 0xf2, 0x3d, 0xc6, 0xd8, + 0xd6, 0x3d, 0x1f, 0xe1, 0x23, 0x2e, 0x32, 0x35, 0x05, 0xbd, 0x99, 0x21, + 0xaf, 0x88, 0xd0, 0xe2, 0xba, 0x85, 0x32, 0x25, 0x4b, 0xf3, 0x6d, 0x4b, + 0x84, 0xb5, 0xe6, 0x69, 0x20, 0x33, 0x35, 0xcb, 0xe8, 0xa7, 0x97, 0x17, + 0xac, 0xc5, 0xd1, 0xb0, 0xed, 0xd9, 0x37, 0xec, 0x9a, 0x46, 0xdd, 0x33, + 0x92, 0x6f, 0xc8, 0xd9, 0xc7, 0x64, 0x8a, 0xb5, 0x38, 0x77, 0x10, 0x0d, + 0xfe, 0xfb, 0x83, 0x86, 0xab, 0x3b, 0xd2, 0x9e, 0x4b, 0xed, 0x7c, 0x03, + 0x0c, 0xf0, 0x5f, 0xa9, 0xa7, 0x20, 0x88, 0xc5, 0x42, 0x00, 0xb4, 0x81, + 0x6a, 0x01, 0x7f, 0x75, 0x60, 0x79, 0xd9, 0x2e, 0x7b, 0xb3, 0x86, 0xf3, + 0xd6, 0x89, 0x4b, 0x60, 0x7c, 0x84, 0xf0, 0x76, 0x97, 0x8d, 0x78, 0x76, + 0x18, 0xae, 0xda, 0x7d, 0x80, 0xc3, 0x83, 0x53, 0x7d, 0x5b, 0x70, 0x02, + 0xc6, 0xeb, 0x9e, 0x6b, 0x51, 0xf7, 0x7b, 0xc0, 0x49, 0xb5, 0xa3, 0xf8, + 0xad, 0xcf, 0x44, 0x7e, 0x9f, 0x80, 0x41, 0xb6, 0x6c, 0xb4, 0x4c, 0x58, + 0x22, 0x7f, 0xf7, 0x23, 0x15, 0xaa, 0x34, 0x8c, 0xbc, 0x69, 0x55, 0xae, + 0x8e, 0xf5, 0xf6, 0xb7, 0x6c, 0x33, 0x64, 0xba, 0xce, 0x7d, 0xb0, 0xf0, + 0x1d, 0x0b, 0x65, 0xb8, 0xfc, 0xe5, 0x00, 0xb8, 0x4b, 0x0e, 0x39, 0x48, + 0xdd, 0x7b, 0xc8, 0xfd, 0x89, 0xed, 0x8e, 0x5b, 0xa7, 0x58, 0x8a, 0x52, + 0x2d, 0x55, 0xc2, 0x61, 0xc1, 0xf9, 0x95, 0xb2, 0x07, 0xff, 0x0b, 0x6a, + 0x56, 0x65, 0x58, 0x70, 0x59, 0xde, 0x53, 0x3a, 0xeb, 0x1e, 0xb8, 0xda, + 0x09, 0x48, 0x4d, 0x5f, 0xba, 0x85, 0x43, 0x01, 0xf1, 0x2e, 0x0f, 0x17, + 0x0c, 0xbc, 0x36, 0x31, 0x73, 0xc4, 0x29, 0xbc, 0xff, 0x1a, 0xf2, 0x88, + 0x67, 0x5d, 0x32, 0x68, 0x4f, 0xcd, 0xfe, 0xb7, 0x65, 0xef, 0xb6, 0xf6, + 0xf6, 0xe6, 0x11, 0x3e, 0x9f, 0x40, 0x46, 0x7a, 0xb0, 0xa7, 0xb4, 0x6e, + 0x52, 0x1b, 0x86, 0x17, 0x1a, 0x49, 0x19, 0xf4, 0x6f, 0xa4, 0x2a, 0x0c, + 0x0d, 0xb0, 0xcf, 0x95, 0x46, 0x30, 0x38, 0x43, 0x2c, 0x29, 0x33, 0xb3, + 0x46, 0xd4, 0xe8, 0x78, 0x12, 0x0c, 0xc0, 0x89, 0x8d, 0x25, 0x04, 0xd9, + 0x5b, 0xef, 0xcf, 0x0f, 0xe1, 0x5e, 0x94, 0x74, 0x68, 0x7c, 0xda, 0x7e, + 0x6a, 0x75, 0x34, 0x27, 0x66, 0xed, 0xab, 0x9f, 0x81, 0xad, 0xa7, 0x03, + 0xcf, 0x36, 0x88, 0x9d, 0xe9, 0xe5, 0x56, 0xaf, 0x47, 0x83, 0x5c, 0xd0, + 0xb8, 0x45, 0xcc, 0x77, 0x20, 0xdb, 0x81, 0x91, 0x67, 0x8c, 0x3d, 0x1e, + 0x36, 0xd3, 0x01, 0x26, 0xdc, 0x03, 0x0d, 0xca, 0xbb, 0x2f, 0xc7, 0x1a, + 0x08, 0xa4, 0x1f, 0x8f, 0xee, 0xf7, 0xbe, 0x23, 0x0b, 0xaa, 0xb9, 0xe8, + 0x2e, 0x3f, 0xaf, 0x39, 0x94, 0x87, 0x22, 0xde, 0x6f, 0x79, 0xf4, 0x1f, + 0x3c, 0xe0, 0xbc, 0xa1, 0xdc, 0x20, 0x01, 0xfe, 0x72, 0x3e, 0x63, 0x0d, + 0xb2, 0x70, 0xda, 0x2f, 0x11, 0x46, 0x4a, 0x80, 0x67, 0x63, 0x10, 0x46, + 0xa6, 0xd4, 0xda, 0xec, 0x2a, 0xfe, 0x09, 0x13, 0x06, 0x95, 0x3d, 0x2b, + 0x6f, 0x3d, 0xa5, 0x56, 0x00, 0x3d, 0x27, 0x30, 0x5f, 0x90, 0xe1, 0xac, + 0xb8, 0x02, 0xc1, 0x62, 0x27, 0x7b, 0xfa, 0xcd, 0x3c, 0x12, 0x0e, 0xc3, + 0x85, 0x48, 0xb8, 0x5f, 0xac, 0x97, 0xfe, 0xbb, 0x23, 0x19, 0xd7, 0xe0, + 0x10, 0x3f, 0xad, 0xb5, 0x5d, 0x6a, 0x6f, 0x0f, 0x37, 0x74, 0x32, 0x30, + 0x4b, 0x36, 0xa3, 0x63, 0x44, 0xb0, 0x50, 0xc8, 0xc3, 0x95, 0xd0, 0x94, + 0x14, 0xdc, 0xd9, 0x85, 0x46, 0x3a, 0x41, 0x8b, 0x94, 0x12, 0x2e, 0x88, + 0x31, 0xe2, 0xfe, 0xbd, 0x1e, 0xd5, 0x17, 0xee, 0x4d, 0x12, 0x68, 0xb4, + 0x6b, 0xe4, 0x01, 0x20, 0xab, 0x7d, 0x77, 0x24, 0x31, 0x86, 0xe9, 0x9f, + 0xc4, 0x73, 0x09, 0xd4, 0x39, 0x89, 0x72, 0xbc, 0xd3, 0x69, 0x3d, 0xec, + 0x4d, 0x07, 0x3f, 0x80, 0x6a, 0x75, 0x08, 0xa9, 0x28, 0x95, 0xcf, 0xab, + 0xdf, 0x1e, 0x61, 0x77, 0x5b, 0x2d, 0x27, 0x62, 0xc6, 0xa8, 0x79, 0x9e, + 0xe3, 0x6f, 0x4d, 0x8d, 0x3d, 0x37, 0x84, 0x91, 0x19, 0x84, 0x04, 0xf4, + 0xf4, 0xc9, 0xf0, 0xf3, 0xbc, 0x4c, 0x94, 0x1c, 0x12, 0xad, 0x32, 0xc3, + 0xa3, 0x05, 0x26, 0x7a, 0xea, 0x66, 0x8d, 0x6f, 0x31, 0x8b, 0x03, 0x73, + 0x64, 0x2a, 0x69, 0x0d, 0x29, 0x97, 0x5a, 0xad, 0x36, 0xcf, 0xfc, 0xfc, + 0x6e, 0x09, 0xc2, 0x37, 0x85, 0x35, 0xea, 0x6a, 0x10, 0x49, 0x97, 0x84, + 0x23, 0xe5, 0x08, 0x59, 0x73, 0xd1, 0x6c, 0x15, 0x3a, 0x4a, 0xc8, 0xbf, + 0xfc, 0x79, 0x43, 0x07, 0x62, 0xf6, 0x75, 0x0c, 0xbd, 0x5f, 0x3c, 0x31, + 0x98, 0x20, 0x3d, 0x90, 0x3c, 0x7e, 0x23, 0xfe, 0x4b, 0xc7, 0x6d, 0xce, + 0x3c, 0x3f, 0x4b, 0x1f, 0xbe, 0x1d, 0xbb, 0x85, 0xab, 0xcb, 0xdb, 0x9e, + 0xa5, 0x7a, 0x8c, 0x61, 0xf2, 0x7b, 0xa5, 0xd1, 0x7e, 0x86, 0xd4, 0xa9, + 0x57, 0xbf, 0x58, 0xdb, 0x7e, 0x56, 0x3b, 0x4a, 0xf7, 0x7f, 0x0c, 0x21, + 0x31, 0x2a, 0xfa, 0xca, 0x0a, 0x98, 0xb4, 0xde, 0x52, 0xfc, 0x02, 0x6a, + 0x26, 0xfd, 0xb5, 0x75, 0x61, 0x79, 0xd8, 0xde, 0x45, 0xf7, 0xde, 0xcd, + 0x54, 0xd6, 0x66, 0xe8, 0x27, 0xcd, 0xd6, 0xcf, 0xa7, 0x80, 0x63, 0x04, + 0x5a, 0x25, 0x6c, 0x4d, 0x69, 0x2c, 0x32, 0xea, 0x0c, 0x34, 0x47, 0xc4, + 0xa6, 0x03, 0x35, 0x6f, 0xa4, 0x91, 0x00, 0xc1, 0xe4, 0xfb, 0x0c, 0x38, + 0xf6, 0x1f, 0x86, 0x00, 0xa8, 0x16, 0x24, 0xa9, 0xb4, 0xe9, 0x2c, 0xd3, + 0x2e, 0x83, 0x11, 0xbc, 0xcb, 0xcf, 0x33, 0x44, 0x02, 0xee, 0xc8, 0x2d, + 0x38, 0x63, 0x23, 0xe6, 0x41, 0x9c, 0xfc, 0xa3, 0xbd, 0xba, 0x86, 0x4b, + 0x80, 0x97, 0xdf, 0x9a, 0xf9, 0xef, 0x62, 0x53, 0x36, 0x68, 0x44, 0x4b, + 0x40, 0x9b, 0x1d, 0xfe, 0x84, 0xa8, 0x45, 0xde, 0x3a, 0x6c, 0xda, 0x74, + 0x53, 0x17, 0x9b, 0x72, 0x34, 0x3a, 0xf2, 0xcd, 0x29, 0x55, 0x3f, 0x39, + 0x03, 0xc1, 0x6b, 0x2d, 0xf0, 0x00, 0xfe, 0xbf, 0x4a, 0xe1, 0x9a, 0xf1, + 0x7a, 0x60, 0x5e, 0x9c, 0xfd, 0xdf, 0x7d, 0x9c, 0x75, 0x57, 0x6f, 0x2e, + 0x50, 0x98, 0xe0, 0x5a, 0xe8, 0xf6, 0xf6, 0xfc, 0x13, 0x11, 0xdd, 0x61, + 0xa9, 0x45, 0x02, 0x1f, 0xc1, 0xd3, 0x22, 0xdc, 0xbb, 0x0e, 0x0b, 0x80, + 0x35, 0xa6, 0xdc, 0xfe, 0x2a, 0x27, 0x1d, 0x70, 0xec, 0x29, 0x89, 0x8b, + 0x8a, 0xe8, 0xaf, 0xa6, 0x4b, 0x70, 0xa5, 0x1e, 0x5b, 0xbf, 0xfc, 0xf7, + 0x63, 0x57, 0x18, 0x47, 0x41, 0xd8, 0xe2, 0x16, 0x58, 0xb2, 0x9c, 0xa0, + 0x21, 0x4e, 0xd9, 0xb7, 0x23, 0x3a, 0x0a, 0x86, 0x51, 0x45, 0x59, 0x12, + 0xfb, 0xef, 0xf8, 0x93, 0xd3, 0x92, 0xdc, 0x24, 0xd0, 0xf3, 0x3b, 0xb6, + 0xab, 0xd0, 0x8a, 0xd7, 0x48, 0x46, 0xb3, 0x0c, 0x4c, 0xf3, 0x23, 0x6c, + 0x4b, 0x33, 0x54, 0x2e, 0x31, 0x0e, 0x19, 0xbb, 0x8d, 0x5d, 0x26, 0xe9, + 0xb4, 0x82, 0x96, 0xae, 0xd3, 0xb3, 0x0e, 0xb5, 0x5d, 0xb3, 0x7d, 0x39, + 0x25, 0xa1, 0x17, 0xe6, 0xf9, 0x4d, 0x4b, 0x76, 0x6f, 0x79, 0x81, 0x9f, + 0xd0, 0x2c, 0x03, 0x8c, 0x09, 0xc9, 0xe7, 0x8a, 0x98, 0x33, 0x83, 0xfc, + 0x60, 0xf7, 0x16, 0x82, 0x5a, 0x3a, 0x84, 0xba, 0xb0, 0xfb, 0x3c, 0xcb, + 0xe0, 0x4e, 0x46, 0x4d, 0xc3, 0x25, 0x63, 0x43, 0x11, 0x3d, 0xa9, 0xf4, + 0x03, 0xf8, 0x99, 0xdf, 0xd8, 0x66, 0xac, 0xbe, 0xf3, 0xd9, 0xbe, 0x99, + 0x4e, 0x5c, 0x11, 0x18, 0x14, 0xe9, 0x1f, 0xa5, 0x82, 0x0b, 0x22, 0x6f, + 0xc4, 0xca, 0x26, 0xe4, 0x86, 0xba, 0x55, 0x06, 0x50, 0xf4, 0x2a, 0xff, + 0xda, 0x1b, 0xba, 0xae, 0xd6, 0xb8, 0x8d, 0xcb, 0x5f, 0x57, 0xc1, 0x84, + 0x23, 0x91, 0x22, 0xec, 0x47, 0x35, 0xe7, 0x65, 0x07, 0x94, 0x6c, 0x4f, + 0xad, 0xa2, 0x0d, 0xfd, 0x57, 0x0b, 0xff, 0x8c, 0xb0, 0x4a, 0x51, 0x9a, + 0xbb, 0x5c, 0x92, 0x5e, 0xcb, 0xb8, 0xca, 0x1e, 0xce, 0x8c, 0xb6, 0xb0, + 0x0b, 0xa2, 0x29, 0x8b, 0xea, 0x4e, 0xf4, 0x3a, 0x35, 0x3c, 0xcb, 0xfa, + 0xde, 0x1d, 0xd5, 0x3b, 0x2f, 0xfd, 0x52, 0x8a, 0x29, 0x86, 0xfe, 0xee, + 0x0a, 0xa0, 0x26, 0x7b, 0x06, 0x10, 0x19, 0xef, 0x02, 0xce, 0x4c, 0xdc, + 0x70, 0x22, 0x3e, 0xab, 0xd9, 0xa8, 0xd2, 0x76, 0x5a, 0x9e, 0x9b, 0x36, + 0x8c, 0xfc, 0xe4, 0xc9, 0xf1, 0x79, 0x6e, 0x97, 0x9f, 0x83, 0x7c, 0x7e, + 0xa9, 0xb4, 0x9e, 0x16, 0xa7, 0x5d, 0x11, 0x47, 0xf1, 0xd3, 0x36, 0x34, + 0x18, 0x93, 0xa4, 0xe8, 0x45, 0x76, 0xc0, 0x03, 0x39, 0x79, 0x3c, 0x1a, + 0x2e, 0x66, 0xc9, 0x05, 0x8c, 0x23, 0x2f, 0xda, 0x16, 0x15, 0xc3, 0xbc, + 0xde, 0xc7, 0x81, 0x47, 0x8b, 0x69, 0x4a, 0xbd, 0x6d, 0xf6, 0x11, 0x96, + 0x8b, 0xee, 0x2e, 0xf2, 0x7a, 0x44, 0x4c, 0x36, 0x8a, 0x3e, 0xd0, 0xf9, + 0x09, 0x39, 0xf3, 0x69, 0x0c, 0x06, 0x35, 0x44, 0x31, 0x37, 0x46, 0x54, + 0x37, 0xd9, 0x44, 0x20, 0x3f, 0xc2, 0x82, 0xf6, 0x23, 0xb6, 0xb1, 0x2f, + 0xba, 0x70, 0x4f, 0x89, 0x96, 0xa3, 0xbf, 0x5e, 0x76, 0x2a, 0x50, 0xbd, + 0xf2, 0x0d, 0x65, 0x65, 0x27, 0x03, 0x2d, 0x1c, 0xb8, 0x85, 0xb4, 0x1d, + 0x99, 0x6d, 0xcf, 0xa3, 0x63, 0x93, 0x43, 0x47, 0x35, 0x00, 0x23, 0xdc, + 0xf4, 0xcb, 0x75, 0x98, 0xc7, 0x2f, 0xa4, 0x59, 0x59, 0xca, 0xf8, 0x96, + 0xa0, 0x32, 0xa2, 0x79, 0x95, 0xf4, 0xe8, 0x0a, 0x40, 0xc3, 0xd8, 0x7f, + 0xa4, 0x99, 0x80, 0x46, 0x71, 0xd2, 0xd7, 0x69, 0xc6, 0x85, 0x5f, 0x6a, + 0x35, 0x69, 0x45, 0xc4, 0xbb, 0x9d, 0xe1, 0x60, 0x2c, 0xf0, 0x8e, 0xb9, + 0xcb, 0x27, 0x51, 0xc0, 0xf1, 0x3c, 0x63, 0xf3, 0x31, 0x2b, 0xd2, 0x53, + 0x5e, 0x77, 0x99, 0x86, 0x13, 0x5f, 0x98, 0xbc, 0x03, 0xde, 0xf6, 0xbf, + 0xa8, 0x22, 0xe3, 0xa1, 0xe7, 0x61, 0x13, 0x6e, 0xda, 0x81, 0x21, 0x2f, + 0xfd, 0xf2, 0x9a, 0x0b, 0xe5, 0xbe, 0xa8, 0x0e, 0xbe, 0x08, 0x8a, 0x1c, + 0xe8, 0x5d, 0x7f, 0xd4, 0xbd, 0x0c, 0x92, 0xb9, 0x8e, 0x0c, 0x0d, 0xe4, + 0x0f, 0x6f, 0xfd, 0xc1, 0xf2, 0x4b, 0x0e, 0x1d, 0x03, 0x6d, 0xbe, 0xa3, + 0xe2, 0x13, 0x34, 0xe7, 0x98, 0x84, 0x77, 0x8c, 0x38, 0x73, 0xb2, 0xd1, + 0x5d, 0xbf, 0x57, 0x9b, 0x86, 0x6a, 0x26, 0x10, 0x4e, 0xfe, 0x49, 0x30, + 0xd0, 0x22, 0xa2, 0xfe, 0xd2, 0x1c, 0x43, 0xae, 0xba, 0x62, 0x3a, 0x61, + 0xc1, 0x83, 0xfb, 0x23, 0x6d, 0x45, 0xf6, 0x68, 0x24, 0xef, 0xba, 0xcc, + 0xa2, 0x4f, 0xff, 0x4f, 0x79, 0x2c, 0x11, 0xa7, 0x5c, 0x4c, 0xb4, 0x69, + 0x83, 0xba, 0xaf, 0x69, 0xab, 0xde, 0x06, 0x04, 0xf4, 0x1c, 0x40, 0xa0, + 0xb9, 0xb9, 0x56, 0x65, 0x86, 0x3d, 0x85, 0x79, 0xc0, 0x15, 0xbb, 0x10, + 0xa9, 0xff, 0xee, 0xff, 0x0c, 0xc0, 0x03, 0x8c, 0xec, 0x24, 0x5a, 0x78, + 0x83, 0x34, 0x8f, 0xa3, 0x45, 0x18, 0x49, 0x98, 0x6b, 0x27, 0xd1, 0xb5, + 0x40, 0x9d, 0x90, 0xc0, 0xa7, 0x02, 0x27, 0xef, 0x5f, 0x1d, 0xe5, 0x9a, + 0x15, 0x66, 0xd0, 0xb5, 0xbe, 0x20, 0xaf, 0x90, 0xb8, 0x58, 0x65, 0xe0, + 0x54, 0x72, 0x54, 0x4c, 0x35, 0x50, 0x56, 0x93, 0x61, 0x18, 0xf8, 0x0b, + 0x51, 0xa7, 0xea, 0x83, 0xaa, 0x60, 0x6c, 0x0a, 0xbe, 0x79, 0xd8, 0x81, + 0x8a, 0x17, 0xd8, 0x9c, 0x56, 0x06, 0x7c, 0x15, 0x44, 0xaa, 0x62, 0x48, + 0x67, 0xe6, 0x5c, 0xa0, 0x09, 0x75, 0xbd, 0xf0, 0x61, 0x15, 0x9f, 0xac, + 0x57, 0xbc, 0xfb, 0xa9, 0xa2, 0x97, 0x84, 0x6d, 0xfa, 0xbc, 0xcc, 0x53, + 0x96, 0x46, 0x01, 0x6d, 0x98, 0x43, 0x83, 0xc2, 0xcf, 0x38, 0x92, 0x99, + 0x3f, 0xdb, 0x93, 0x99, 0xab, 0x56, 0x6c, 0x02, 0x78, 0x04, 0x66, 0x1a, + 0xec, 0x8e, 0xd3, 0x70, 0x48, 0xd0, 0x7c, 0x1f, 0x54, 0x0c, 0x2f, 0x30, + 0xac, 0x75, 0x08, 0x1f, 0x57, 0x6d, 0xe2, 0x20, 0xb2, 0xd0, 0x19, 0xff, + 0xe3, 0x56, 0xc5, 0x6f, 0x1f, 0x9c, 0x29, 0x79, 0x0a, 0x59, 0xe6, 0x81, + 0x5c, 0xe2, 0x98, 0xef, 0xb0, 0xba, 0x93, 0xe6, 0x2f, 0xdc, 0x92, 0x68, + 0x0c, 0xb4, 0xff, 0x7e, 0x71, 0xda, 0xad, 0x10, 0x99, 0x0f, 0x6e, 0xc4, + 0xaa, 0x0e, 0x69, 0x45, 0x4a, 0xf3, 0x5d, 0x5a, 0x34, 0x64, 0x55, 0x8a, + 0x16, 0x1e, 0xdb, 0x3b, 0xdf, 0xa3, 0x3f, 0x5e, 0xbd, 0x47, 0xf6, 0x65, + 0xb0, 0x44, 0x4f, 0xf7, 0xda, 0x29, 0x42, 0x6c, 0x6c, 0x83, 0xc3, 0xb4, + 0x6d, 0x1a, 0x8a, 0x39, 0x6d, 0x28, 0xa9, 0x71, 0xf1, 0x0e, 0x29, 0xb2, + 0xe0, 0x2d, 0x06, 0xcf, 0x6b, 0x60, 0xbb, 0xf8, 0x0b, 0x29, 0x78, 0x43, + 0x3f, 0x29, 0xf6, 0x33, 0xd3, 0xac, 0x3b, 0xd6, 0xce, 0x98, 0x6e, 0x30, + 0x6a, 0x0d, 0x64, 0x4c, 0x96, 0xfc, 0x4c, 0x74, 0x3c, 0xb1, 0xc4, 0xd4, + 0x80, 0x5a, 0x13, 0xc5, 0xb2, 0xbe, 0xc5, 0xe8, 0x19, 0x43, 0x50, 0x3f, + 0x74, 0xd9, 0x1b, 0x89, 0x75, 0x24, 0x4d, 0x5a, 0x9c, 0x1c, 0x0a, 0xae, + 0x29, 0xf2, 0x64, 0x41, 0x7a, 0x6d, 0x71, 0xde, 0x67, 0x2b, 0xc0, 0x60, + 0x48, 0x94, 0x5f, 0x74, 0x85, 0xfd, 0x8f, 0xf6, 0xaa, 0xf9, 0x48, 0x6d, + 0x9d, 0x63, 0x5b, 0xc9, 0x87, 0xb8, 0x4a, 0x6f, 0xaa, 0x3e, 0x34, 0x56, + 0x50, 0xa9, 0x46, 0x14, 0x4b, 0xe7, 0x80, 0x64, 0xd4, 0xf8, 0xaf, 0xd4, + 0x97, 0xd3, 0xe6, 0x3a, 0xa0, 0x3a, 0x3b, 0x88, 0xe5, 0xcc, 0x7a, 0xc7, + 0xb8, 0x38, 0x8a, 0x9f, 0xf8, 0x7f, 0x67, 0xe5, 0xdf, 0x39, 0x08, 0x2a, + 0xe9, 0x82, 0x4f, 0xec, 0x80, 0xb5, 0xce, 0x3d, 0xee, 0x1c, 0xe8, 0x89, + 0xb5, 0x42, 0xb1, 0xfd, 0x03, 0x62, 0xac, 0x6c, 0xfe, 0x19, 0xaa, 0x0f, + 0xe8, 0x07, 0x98, 0x2d, 0x13, 0x27, 0xc2, 0xcc, 0x62, 0xe1, 0x22, 0xda, + 0x1c, 0xde, 0x22, 0x00, 0x2f, 0x1b, 0x41, 0xa7, 0x0d, 0xa9, 0x16, 0x87, + 0x40, 0xa5, 0xb9, 0x4a, 0x87, 0x70, 0x9d, 0x9a, 0x38, 0x1f, 0x65, 0xe7, + 0xcd, 0xa0, 0xa2, 0xea, 0x93, 0x22, 0xcb, 0xd5, 0x57, 0x34, 0xd5, 0xb9, + 0xc6, 0x4a, 0x58, 0xe4, 0xcb, 0x92, 0x35, 0x3c, 0x63, 0x07, 0xf7, 0x6a, + 0x5d, 0x44, 0xef, 0xed, 0x0c, 0xea, 0xa1, 0xa2, 0xbd, 0x88, 0xe0, 0xc6, + 0xff, 0x2f, 0x0d, 0xcf, 0x7c, 0x25, 0xce, 0x31, 0x1b, 0xb7, 0x6b, 0x70, + 0xfa, 0x4e, 0xc7, 0xa3, 0x9b, 0x9e, 0xfb, 0x3f, 0x16, 0x37, 0x8d, 0x3c, + 0x50, 0xce, 0xb3, 0x33, 0x86, 0x4a, 0x41, 0xa8, 0x61, 0x4e, 0x93, 0xb3, + 0xbd, 0xda, 0x0e, 0xf6, 0xee, 0x4f, 0x75, 0x50, 0x8f, 0x87, 0xbb, 0xc7, + 0xd9, 0xf9, 0xe3, 0x2c, 0xd0, 0x04, 0x04, 0xf5, 0x61, 0xdf, 0x09, 0xef, + 0xc0, 0x65, 0x01, 0xc2, 0x5e, 0x6f, 0x93, 0x92, 0x0d, 0xa7, 0xb4, 0xd6, + 0x40, 0xcc, 0x5c, 0x2a, 0x3c, 0x6b, 0xd7, 0xe0, 0xbb, 0xdc, 0x3d, 0x28, + 0x91, 0x34, 0xb0, 0xf6, 0xb2, 0x15, 0x34, 0x13, 0x75, 0x5e, 0xa2, 0x15, + 0x2f, 0x1b, 0x03, 0xf7, 0x4d, 0x39, 0xae, 0x8b, 0xc8, 0x85, 0x48, 0xb3, + 0xa5, 0x1e, 0xf7, 0xf4, 0x25, 0x8e, 0x5c, 0x5e, 0xa7, 0x9d, 0x4a, 0x71, + 0xd6, 0x02, 0xb6, 0x32, 0x36, 0xe9, 0xa6, 0x9e, 0xff, 0xff, 0x66, 0xe1, + 0xee, 0xa4, 0x97, 0x25, 0xe5, 0xad, 0x10, 0xfb, 0x6a, 0xbb, 0xe3, 0x7a, + 0x97, 0xdf, 0xe9, 0x43, 0xfe, 0x5e, 0x2a, 0x7e, 0xf9, 0xf7, 0x1f, 0x47, + 0xdf, 0xe0, 0x9b, 0xc1, 0x71, 0xf5, 0xfc, 0x1e, 0x17, 0x2c, 0x8b, 0x75, + 0x91, 0x93, 0xe5, 0x05, 0xed, 0x60, 0x84, 0xf0, 0x03, 0xb5, 0x3c, 0xfc, + 0x2b, 0x2c, 0x28, 0x83, 0x47, 0x23, 0xaa, 0xd3, 0x37, 0xd5, 0xbe, 0x86, + 0xac, 0x37, 0xe2, 0xf2, 0x5e, 0xb8, 0x42, 0x2b, 0x31, 0xb2, 0xcb, 0x34, + 0x63, 0xce, 0xaf, 0x75, 0x4b, 0xd0, 0x4a, 0x9e, 0x16, 0xd8, 0xd9, 0xa0, + 0xca, 0x07, 0xd7, 0x2e, 0x4a, 0xbe, 0x5f, 0x4a, 0x69, 0xf1, 0x6a, 0x23, + 0x05, 0xe7, 0xab, 0x1c, 0xd6, 0xf0, 0x17, 0x06, 0x84, 0xf7, 0x9b, 0x71, + 0x24, 0xa4, 0x87, 0x3c, 0x89, 0x2f, 0xf0, 0x41, 0xe3, 0x1b, 0x11, 0x02, + 0x02, 0xe3, 0xb3, 0x8c, 0x7f, 0xda, 0xba, 0x57, 0xe0, 0xa7, 0xbc, 0x84, + 0xf9, 0xeb, 0xb7, 0x7c, 0x35, 0x01, 0x4d, 0x88, 0x75, 0x89, 0x3e, 0xc0, + 0xca, 0x06, 0x9e, 0xf9, 0x12, 0x10, 0x31, 0xae, 0xe8, 0xd4, 0x6f, 0xea, + 0x88, 0xec, 0xb3, 0xfd, 0x5b, 0xdc, 0xae, 0xb2, 0x10, 0x9d, 0x03, 0x9d, + 0xbb, 0x9b, 0x6d, 0x3a, 0x30, 0x56, 0xb1, 0xea, 0xd8, 0xf4, 0xb7, 0x48, + 0x36, 0x76, 0x3b, 0x49, 0x02, 0x68, 0xa9, 0x52, 0x0c, 0xed, 0xee, 0x6b, + 0x22, 0x21, 0x0d, 0x76, 0x09, 0x63, 0x41, 0x13, 0x0f, 0x12, 0x02, 0x36, + 0x4f, 0x1d, 0x3d, 0xea, 0x95, 0x45, 0x70, 0x59, 0x8d, 0x58, 0x31, 0x85, + 0x8d, 0x9b, 0xe4, 0xb3, 0xa4, 0xa4, 0x96, 0x26, 0xd8, 0xee, 0xd7, 0xd6, + 0xa7, 0x0d, 0x81, 0xad, 0xd2, 0x33, 0x0b, 0xb8, 0x19, 0x30, 0xbf, 0xa9, + 0x00, 0xe9, 0xc8, 0xb8, 0x0b, 0x66, 0x5b, 0x50, 0x25, 0x05, 0x81, 0x4b, + 0x86, 0x40, 0x07, 0x88, 0x1c, 0x65, 0x8a, 0x14, 0xbd, 0xba, 0xc5, 0xee, + 0x2d, 0xba, 0xf8, 0x67, 0x92, 0x06, 0xe2, 0x42, 0x36, 0x17, 0x8d, 0x89, + 0xc0, 0x06, 0xdf, 0xa1, 0xa5, 0x8a, 0xb7, 0x34, 0xa9, 0x2f, 0x1f, 0x6b, + 0x4a, 0x87, 0xf9, 0xc2, 0xc3, 0x25, 0xf9, 0x9f, 0x1b, 0x0d, 0x81, 0x55, + 0xc1, 0x6d, 0x7e, 0x53, 0x56, 0xce, 0xfc, 0xaf, 0xae, 0xa9, 0x88, 0x05, + 0x8c, 0xcb, 0x36, 0x95, 0x50, 0x12, 0x13, 0x85, 0xba, 0xd6, 0x79, 0xe0, + 0xb7, 0x63, 0x4b, 0x93, 0xa1, 0xcd, 0x88, 0x6c, 0x69, 0xeb, 0x14, 0xcc, + 0xb0, 0x9e, 0x72, 0x92, 0x61, 0xb0, 0xfc, 0x59, 0x4f, 0x63, 0x3f, 0xe3, + 0x73, 0xb9, 0xfd, 0x45, 0x20, 0x04, 0xfe, 0x0f, 0x87, 0xcb, 0x14, 0xed, + 0xc2, 0x2f, 0xbc, 0x51, 0x78, 0xcc, 0x8e, 0x6f, 0x90, 0x89, 0x2d, 0x9a, + 0xdb, 0xde, 0x31, 0xcf, 0x21, 0x8b, 0x72, 0x18, 0x77, 0x30, 0x8a, 0x02, + 0x98, 0x8e, 0x6e, 0x26, 0x4f, 0xdc, 0xcb, 0x13, 0x89, 0x1c, 0xc9, 0x27, + 0x8b, 0x54, 0xff, 0xba, 0x4c, 0x01, 0x2d, 0xbb, 0x13, 0xe4, 0x3d, 0x83, + 0xde, 0x75, 0x5f, 0xfd, 0xe2, 0xfc, 0x3d, 0x25, 0x3a, 0x77, 0x6d, 0x79, + 0x09, 0xbb, 0x83, 0x9a, 0x20, 0x25, 0xf7, 0x60, 0x0f, 0xbb, 0xb3, 0x1a, + 0xbd, 0x25, 0x21, 0xd6, 0xfb, 0x56, 0xfc, 0xaf, 0x12, 0x95, 0x00, 0xc8, + 0xf6, 0xcc, 0x09, 0xed, 0xee, 0xff, 0x78, 0x23, 0xf5, 0x74, 0x11, 0xc3, + 0x5e, 0xf8, 0xee, 0x3e, 0x5f, 0x8b, 0xcd, 0x5a, 0x0c, 0xd1, 0xd1, 0x86, + 0xae, 0x4a, 0x70, 0x4a, 0xeb, 0x36, 0x37, 0x61, 0x95, 0xfe, 0x9c, 0x95, + 0x4b, 0xfa, 0xfd, 0x13, 0xfd, 0x08, 0x8f, 0x54, 0x67, 0x83, 0xfe, 0x3c, + 0xe4, 0xd7, 0xa5, 0x04, 0x5e, 0xbd, 0xc4, 0x82, 0x4e, 0xa6, 0xd4, 0x52, + 0xfe, 0xb0, 0xe8, 0x1e, 0xb6, 0x87, 0x94, 0x48, 0x09, 0x8c, 0x91, 0x34, + 0x56, 0x65, 0x3a, 0x7d, 0xed, 0x11, 0x93, 0x48, 0xb0, 0xd7, 0xdc, 0x5e, + 0xca, 0xca, 0x83, 0xc1, 0x1a, 0x7e, 0x4c, 0xb2, 0xcb, 0xef, 0x83, 0x10, + 0xe7, 0x1f, 0xac, 0x27, 0xc5, 0xb9, 0xe9, 0x36, 0xec, 0xf5, 0x51, 0xe3, + 0xf6, 0x3a, 0xfd, 0x84, 0xb0, 0x48, 0x48, 0xf2, 0x31, 0x81, 0xa8, 0x24, + 0xb0, 0x01, 0xca, 0xa7, 0xd5, 0x1f, 0x79, 0x08, 0xa1, 0x75, 0x48, 0x99, + 0x50, 0x56, 0xf9, 0xca, 0x26, 0xb3, 0x1c, 0x50, 0x48, 0x2b, 0xeb, 0x91, + 0x5f, 0x78, 0xe9, 0x21, 0x79, 0x88, 0x99, 0xb5, 0x36, 0xfe, 0x00, 0x30, + 0x51, 0x1b, 0xde, 0x7c, 0x95, 0x45, 0x40, 0x18, 0x28, 0x15, 0xaa, 0xe4, + 0x07, 0x70, 0x4b, 0xd4, 0xcd, 0xcb, 0xda, 0x33, 0xda, 0xa6, 0xb7, 0xf7, + 0xb1, 0x4c, 0xc7, 0x91, 0x4f, 0x34, 0x83, 0xd0, 0xe1, 0x6b, 0x19, 0x3b, + 0x48, 0x26, 0xe8, 0xf6, 0x40, 0xb5, 0x62, 0xfb, 0x13, 0x3d, 0x46, 0x38, + 0xca, 0x61, 0x42, 0xd4, 0xb9, 0x21, 0xb0, 0x40, 0x56, 0x97, 0x0f, 0xe4, + 0xcb, 0x4f, 0x24, 0x4c, 0x9a, 0x76, 0xc5, 0xd3, 0x3d, 0x4b, 0xb0, 0xaa, + 0x0d, 0x6b, 0x8e, 0xe3, 0xa1, 0x1f, 0xf3, 0x87, 0xd1, 0xed, 0xae, 0x6d, + 0x8e, 0xcc, 0x21, 0x0a, 0xf3, 0x13, 0xa6, 0xf5, 0x64, 0x42, 0x19, 0xd6, + 0xa6, 0xdb, 0xa7, 0xc5, 0xd5, 0xdc, 0xd6, 0x9e, 0xf2, 0xf1, 0xec, 0x8a, + 0x97, 0xdc, 0x73, 0xd5, 0x3d, 0x48, 0x94, 0x7b, 0x70, 0x8b, 0x58, 0xcf, + 0x10, 0xb7, 0x63, 0xf6, 0x2a, 0xee, 0x22, 0xf4, 0x4d, 0x0b, 0x07, 0x90, + 0xda, 0xf8, 0xb2, 0x93, 0xff, 0x67, 0xb9, 0xbd, 0xf5, 0xf9, 0xe9, 0x51, + 0x2f, 0xbf, 0xf5, 0xf1, 0x2d, 0x2b, 0x66, 0x4b, 0x34, 0x53, 0x9b, 0xb3, + 0x6e, 0xbe, 0x4c, 0x52, 0xda, 0x87, 0x69, 0xd2, 0x3b, 0x6f, 0xe0, 0x4b, + 0x06, 0xf0, 0xb2, 0x3e, 0x28, 0x9d, 0x20, 0x9d, 0x5a, 0x9a, 0xb2, 0x17, + 0x42, 0xcf, 0x67, 0x12, 0x77, 0x1b, 0x63, 0xdc, 0x25, 0xcb, 0xa9, 0x67, + 0xc5, 0x94, 0xbe, 0x01, 0x85, 0x8c, 0xc3, 0x74, 0xb9, 0x31, 0xbb, 0x8e, + 0x0f, 0x42, 0x60, 0xe2, 0x7f, 0xb4, 0x05, 0x17, 0xa3, 0x2a, 0xfb, 0xc6, + 0xc1, 0xd4, 0xc2, 0x63, 0x15, 0x71, 0x7b, 0x8b, 0x91, 0x72, 0x5e, 0xee, + 0xa7, 0x41, 0x58, 0x91, 0x0a, 0x09, 0xd2, 0x1b, 0x59, 0xf7, 0x3d, 0x16, + 0x09, 0x53, 0x40, 0x35, 0xe9, 0x50, 0x0b, 0x77, 0xac, 0xa8, 0x57, 0x45, + 0x58, 0x76, 0xf8, 0x06, 0x6c, 0x5c, 0xa4, 0x3e, 0x82, 0xdb, 0xf7, 0x81, + 0x20, 0x1a, 0xd0, 0x23, 0xa1, 0x1e, 0x3d, 0x66, 0xfe, 0xe5, 0xbb, 0xee, + 0xdb, 0x16, 0x74, 0x9a, 0xc4, 0xba, 0xc5, 0x60, 0xd4, 0x28, 0xde, 0x03, + 0xa9, 0x93, 0x94, 0x95, 0xb4, 0xcc, 0x9a, 0x11, 0x4d, 0x88, 0xf1, 0x96, + 0xce, 0x31, 0xef, 0xe6, 0xc4, 0x54, 0xa4, 0xcc, 0x43, 0x6b, 0x6b, 0x6b, + 0xc7, 0x4c, 0x75, 0x12, 0xa5, 0xb6, 0x8f, 0x38, 0xde, 0x12, 0x72, 0xf9, + 0xab, 0x70, 0x38, 0x93, 0x06, 0x68, 0xda, 0x2f, 0xed, 0x28, 0xd6, 0x2d, + 0xaf, 0x19, 0xd0, 0xbe, 0x9f, 0x8d, 0xae, 0xb7, 0x19, 0x14, 0x24, 0xbb, + 0xf8, 0x0c, 0xa1, 0x5e, 0xe5, 0x55, 0x40, 0x92, 0x2f, 0x18, 0x34, 0x54, + 0x2f, 0x89, 0x6f, 0x4f, 0x47, 0x88, 0x3e, 0x61, 0x48, 0xe4, 0x1f, 0x34, + 0xf5, 0x85, 0x8a, 0x2e, 0xfe, 0xb8, 0x14, 0xe5, 0x10, 0x82, 0x64, 0x66, + 0xd4, 0x9b, 0x21, 0x8a, 0x16, 0x27, 0xbb, 0x92, 0xda, 0xd9, 0x13, 0x8f, + 0xd0, 0xe6, 0x35, 0xe2, 0xba, 0xf6, 0xb2, 0xe0, 0x49, 0x2d, 0x50, 0x9f, + 0x8a, 0xf1, 0xbd, 0x14, 0x25, 0xdf, 0xcf, 0x7e, 0x4d, 0x31, 0x4e, 0x4f, + 0x84, 0x12, 0xce, 0x2d, 0x2c, 0x3b, 0xde, 0x79, 0x34, 0x70, 0x28, 0xee, + 0xf4, 0x59, 0x08, 0x9b, 0xc9, 0x06, 0xc7, 0x92, 0xb3, 0xb5, 0x70, 0x0c, + 0xc8, 0x4f, 0x73, 0xa6, 0x07, 0x18, 0x58, 0xdb, 0x16, 0xd8, 0x56, 0xb8, + 0x42, 0x85, 0x02, 0xbd, 0xf9, 0x6a, 0x60, 0x21, 0x25, 0xd0, 0x4a, 0x75, + 0x44, 0x56, 0x25, 0x94, 0x76, 0x39, 0x6f, 0xf5, 0xc9, 0xf0, 0xe4, 0x0f, + 0xe8, 0xeb, 0xfd, 0xc8, 0xc8, 0x7c, 0x9d, 0x54, 0x25, 0x51, 0x98, 0xb3, + 0x17, 0x74, 0x24, 0x7a, 0x99, 0xf1, 0x68, 0xc5, 0x57, 0x22, 0xca, 0x94, + 0x5f, 0x99, 0x7b, 0x60, 0x74, 0x86, 0xc2, 0x4a, 0x76, 0xbb, 0x03, 0x87, + 0x57, 0x83, 0x6b, 0x0b, 0x72, 0xec, 0x3f, 0x75, 0xc6, 0x2d, 0xc6, 0x1f, + 0xbe, 0x12, 0x48, 0xb9, 0xea, 0xa7, 0x5e, 0x0c, 0x7d, 0xc6, 0xa5, 0x22, + 0x16, 0xa7, 0x80, 0xa1, 0xcd, 0x7c, 0x09, 0xcf, 0x84, 0x8b, 0xd2, 0x70, + 0x56, 0xc1, 0x7c, 0x59, 0xc9, 0x05, 0x36, 0x57, 0xb9, 0x02, 0x46, 0xfe, + 0xbb, 0xb2, 0xe9, 0x19, 0x09, 0x7a, 0xf8, 0xb5, 0x97, 0x4d, 0x10, 0xd0, + 0x5a, 0x5d, 0x37, 0x69, 0xc4, 0x91, 0x7b, 0xd3, 0xe2, 0xdb, 0x1c, 0x7f, + 0xc9, 0x5d, 0xb2, 0x48, 0x97, 0x34, 0x66, 0x37, 0xaa, 0xdc, 0xbf, 0xce, + 0xe1, 0x37, 0xbf, 0x09, 0x4a, 0xb9, 0x1f, 0x06, 0x38, 0x64, 0x69, 0x67, + 0x6c, 0x87, 0xb4, 0x2c, 0x5c, 0x95, 0x04, 0xaf, 0xbb, 0x11, 0x25, 0x7d, + 0x46, 0x91, 0xfc, 0x8b, 0x34, 0x99, 0xae, 0xd2, 0x94, 0x03, 0x7a, 0x81, + 0x92, 0x7c, 0xbe, 0x36, 0x75, 0xa7, 0xc9, 0xe0, 0x54, 0xfe, 0xab, 0x12, + 0x28, 0x26, 0xa3, 0xb8, 0xc6, 0xee, 0xa8, 0xfa, 0x97, 0x0a, 0x35, 0x18, + 0xb9, 0xfd, 0x5b, 0x01, 0xa8, 0x67, 0xc0, 0x5b, 0x82, 0xec, 0xa0, 0x6f, + 0xfb, 0x48, 0x6f, 0xd3, 0x81, 0x95, 0x29, 0xc1, 0xa5, 0x8a, 0xd4, 0xfb, + 0x90, 0xf7, 0xb2, 0x47, 0x5f, 0x4c, 0x7b, 0x0c, 0x53, 0xbb, 0xdd, 0x75, + 0x8d, 0x6c, 0x6c, 0xb8, 0x57, 0x50, 0x41, 0x38, 0xf7, 0x02, 0x7a, 0x52, + 0x42, 0xed, 0x25, 0x9a, 0xe5, 0x94, 0x13, 0x59, 0xc2, 0x2f, 0xc7, 0xf2, + 0x9c, 0xd9, 0xa8, 0x3a, 0x77, 0x80, 0x95, 0xee, 0x28, 0x62, 0x85, 0xd8, + 0x1b, 0x0a, 0xa5, 0xcd, 0x12, 0xa1, 0xcb, 0xdf, 0x35, 0x89, 0x9c, 0xd0, + 0x0a, 0x5a, 0x68, 0x77, 0xf8, 0x98, 0x3d, 0x21, 0x6a, 0xb3, 0x8b, 0xb0, + 0x77, 0xfb, 0x63, 0xee, 0x10, 0x62, 0x1c, 0xd5, 0x86, 0x3b, 0xb5, 0x4d, + 0xe5, 0xeb, 0x5b, 0x6a, 0x23, 0x65, 0x0f, 0x16, 0x64, 0xba, 0xb7, 0x89, + 0x2f, 0x61, 0x31, 0x0b, 0x59, 0xde, 0xdc, 0x27, 0xf1, 0x2c, 0xd6, 0x16, + 0xd4, 0x94, 0x14, 0x2f, 0xbd, 0x51, 0xa4, 0xc4, 0xbb, 0x60, 0x61, 0xb6, + 0x19, 0x47, 0xe3, 0x19, 0x57, 0x63, 0x00, 0x05, 0xb3, 0xfa, 0xc0, 0x2c, + 0x46, 0x77, 0x95, 0xc5, 0x19, 0xd6, 0xad, 0x92, 0xd5, 0x25, 0x7f, 0xc7, + 0x86, 0xd5, 0xac, 0x57, 0x07, 0xdf, 0x84, 0x20, 0xac, 0x56, 0xa4, 0xb4, + 0xdf, 0x72, 0xc4, 0x86, 0x75, 0xca, 0x4b, 0xfc, 0xfc, 0x7f, 0x1a, 0x14, + 0x27, 0xe1, 0xbe, 0xa0, 0xd1, 0x6c, 0xcc, 0x6e, 0x1b, 0x2c, 0x40, 0xc1, + 0x10, 0xf5, 0x02, 0x84, 0x1e, 0x31, 0x99, 0xd4, 0xcf, 0xbb, 0xe9, 0xa9, + 0xde, 0x20, 0x5f, 0x61, 0x5f, 0x7b, 0xfe, 0x73, 0x2f, 0x12, 0x44, 0x8d, + 0x17, 0x23, 0x06, 0x82, 0xe6, 0x83, 0x15, 0x8d, 0xd2, 0x16, 0x46, 0x44, + 0x26, 0x43, 0x86, 0x80, 0x45, 0x01, 0xf3, 0x13, 0xfe, 0x8f, 0x78, 0x9c, + 0x49, 0x36, 0x81, 0x7a, 0xc0, 0x4e, 0x4a, 0xc2, 0xc7, 0x57, 0xac, 0x10, + 0xf7, 0x29, 0x92, 0xfc, 0x9a, 0xe6, 0x10, 0x8f, 0xed, 0x14, 0x9d, 0xcd, + 0xd7, 0x8f, 0xa2, 0xbb, 0x48, 0x0e, 0x78, 0x50, 0x18, 0x98, 0x04, 0x1d, + 0xeb, 0x52, 0x85, 0x18, 0x4a, 0x73, 0x01, 0x3b, 0x6d, 0xf8, 0x72, 0x7f, + 0xe8, 0x59, 0xf0, 0x8b, 0x0c, 0x42, 0x30, 0xba, 0xf5, 0xa5, 0x79, 0x36, + 0xe3, 0x92, 0x93, 0x86, 0xc9, 0x9c, 0xbe, 0x61, 0x35, 0xa0, 0x5a, 0x7c, + 0x6a, 0xbe, 0x8c, 0x73, 0xb5, 0x7e, 0x52, 0x4c, 0x4c, 0x42, 0x75, 0xd8, + 0x3a, 0x5d, 0x7b, 0x4a, 0x26, 0x03, 0x82, 0xd0, 0xd4, 0x19, 0xaa, 0x2d, + 0xde, 0x81, 0x44, 0xfd, 0xc7, 0x49, 0xfe, 0x23, 0x38, 0x63, 0xe8, 0xf8, + 0x65, 0xb5, 0x40, 0x6e, 0x09, 0xc0, 0xfe, 0x60, 0x9c, 0x7a, 0xcc, 0x01, + 0xb2, 0x31, 0xe6, 0x59, 0x84, 0x7e, 0x8d, 0x33, 0x8f, 0xab, 0xa4, 0x83, + 0x7c, 0x76, 0x90, 0x4e, 0xdf, 0x7b, 0x73, 0x34, 0x46, 0x41, 0x1e, 0x18, + 0xc1, 0xc8, 0xd7, 0xa7, 0xe8, 0x49, 0x23, 0xf7, 0xd4, 0x9c, 0x63, 0xda, + 0x1a, 0xd1, 0x9f, 0x20, 0x4a, 0x75, 0x45, 0x91, 0xd5, 0x4b, 0xf1, 0x41, + 0x6a, 0x7f, 0xc3, 0x16, 0x04, 0x58, 0x21, 0x34, 0x50, 0xc0, 0x92, 0xad, + 0x7b, 0x2e, 0xc9, 0x0e, 0x3a, 0x80, 0xfa, 0x48, 0xe0, 0xb4, 0x9b, 0xdb, + 0x8c, 0x50, 0xcd, 0x9e, 0xe3, 0xa4, 0x00, 0x1a, 0xef, 0xb5, 0xbc, 0x97, + 0xb1, 0x63, 0x5d, 0x2b, 0xcb, 0x78, 0x6f, 0x6b, 0x86, 0x27, 0x75, 0x03, + 0x69, 0xcf, 0xc8, 0xa1, 0xe2, 0xd6, 0x45, 0xa7, 0xf0, 0xf7, 0xb6, 0x75, + 0xeb, 0xcb, 0xa9, 0x6a, 0x87, 0xed, 0x60, 0x20, 0xd7, 0x81, 0xf9, 0xbe, + 0x64, 0xa5, 0xde, 0x14, 0xdd, 0x10, 0x9c, 0xfa, 0xfe, 0xe9, 0xbc, 0xbd, + 0x8e, 0xef, 0x6d, 0x26, 0x1b, 0xe8, 0x60, 0x22, 0x71, 0x88, 0xdb, 0x46, + 0x58, 0xcd, 0x25, 0x6d, 0xd3, 0x99, 0xb2, 0x3e, 0x90, 0x92, 0x1c, 0x01, + 0xd6, 0x58, 0x82, 0x9c, 0xc4, 0x62, 0xbb, 0x47, 0xb7, 0x6d, 0x3a, 0xea, + 0x6b, 0x20, 0xa8, 0x17, 0x92, 0xb0, 0x62, 0xd6, 0x1d, 0x56, 0x2f, 0x4c, + 0xcc, 0x5b, 0x35, 0xf8, 0x56, 0x8c, 0x95, 0x5a, 0x84, 0x32, 0x2e, 0x41, + 0x65, 0x8c, 0x51, 0x14, 0xe8, 0x19, 0xaa, 0x94, 0x08, 0x87, 0x5f, 0x37, + 0x3b, 0x4c, 0x7f, 0x28, 0x1f, 0x95, 0xe9, 0x00, 0x3b, 0x67, 0x66, 0x18, + 0x26, 0xc6, 0x32, 0x89, 0x79, 0xe9, 0xe7, 0x04, 0xec, 0x27, 0xe9, 0x90, + 0x6d, 0x49, 0xc8, 0x1a, 0x48, 0x66, 0x29, 0xe4, 0xa5, 0xc4, 0x18, 0xfd, + 0x8d, 0x51, 0x22, 0xe0, 0x99, 0xd7, 0x97, 0x7d, 0x21, 0xa2, 0xca, 0x6b, + 0x90, 0x37, 0x82, 0x32, 0xd5, 0x87, 0xf5, 0x0c, 0x1f, 0x17, 0x37, 0xd2, + 0x8b, 0x22, 0x77, 0xe2, 0x80, 0x95, 0x6d, 0xf6, 0xb5, 0x2b, 0x1c, 0xfd, + 0x41, 0xdf, 0xf9, 0x7f, 0x3e, 0x26, 0xd1, 0x71, 0x25, 0x81, 0x54, 0x63, + 0x21, 0xa5, 0xa6, 0x45, 0xe2, 0xba, 0xa7, 0x58, 0x46, 0x48, 0x75, 0x28, + 0xf5, 0x48, 0x75, 0xf6, 0xd8, 0x81, 0x21, 0xda, 0x95, 0x4a, 0xc3, 0x7e, + 0x1f, 0xa6, 0x22, 0xb9, 0xdb, 0xe5, 0x5d, 0x1d, 0x09, 0xc5, 0x98, 0x76, + 0x89, 0x49, 0xa3, 0xa8, 0x05, 0xda, 0x2c, 0x9b, 0x01, 0x42, 0xa0, 0x42, + 0xe6, 0x4a, 0x4d, 0x83, 0x3b, 0x65, 0x18, 0x04, 0x44, 0xbf, 0x3f, 0x6b, + 0x72, 0xa8, 0xd1, 0x8e, 0x52, 0xf8, 0x05, 0xca, 0x93, 0xc4, 0x7e, 0x23, + 0xea, 0xe6, 0x65, 0x94, 0x27, 0x03, 0x97, 0x86, 0x7b, 0x70, 0x37, 0x70, + 0x78, 0xae, 0xea, 0xf9, 0x63, 0xd4, 0xc2, 0x4c, 0x38, 0x14, 0x5d, 0x57, + 0x9a, 0x3d, 0x31, 0xe7, 0xce, 0x02, 0x8a, 0x5c, 0x6f, 0xf9, 0xcb, 0xc5, + 0x92, 0x84, 0x4b, 0x84, 0xd0, 0x5c, 0x07, 0xff, 0xf9, 0xfb, 0x9b, 0x03, + 0x0c, 0x41, 0xc0, 0x0f, 0xe5, 0xa3, 0x3d, 0x1b, 0x3c, 0x00, 0x96, 0x35, + 0xca, 0x28, 0xe7, 0xa5, 0x6f, 0x98, 0x56, 0xf1, 0xa6, 0x2d, 0xfe, 0x2a, + 0x8e, 0xf7, 0xb8, 0xed, 0x93, 0x5b, 0x03, 0x39, 0xb9, 0xb1, 0x77, 0x0a, + 0x92, 0x67, 0xdb, 0xee, 0xfc, 0x75, 0xae, 0x75, 0xf8, 0x48, 0x5d, 0xd3, + 0xc8, 0x68, 0x53, 0x79, 0x49, 0xab, 0x44, 0x47, 0xb4, 0xa2, 0x5a, 0x30, + 0x72, 0x4e, 0xf9, 0xfa, 0x4a, 0x78, 0x37, 0x03, 0xa6, 0x3a, 0xfe, 0x44, + 0xeb, 0xae, 0x23, 0x20, 0x91, 0xfc, 0x9b, 0x1e, 0x1e, 0x00, 0xf1, 0x03, + 0x52, 0x7f, 0xc6, 0x51, 0xd3, 0x80, 0xda, 0xc7, 0x62, 0xaa, 0x5b, 0xc3, + 0xdb, 0x21, 0xb6, 0x7c, 0x63, 0xa6, 0xe4, 0x78, 0x32, 0xe4, 0xd9, 0xe9, + 0xdb, 0xa7, 0x0f, 0xa1, 0xd7, 0x50, 0x12, 0x32, 0xd5, 0xa2, 0xf5, 0x9c, + 0x2d, 0x60, 0x97, 0x30, 0xb6, 0x84, 0xfd, 0x5d, 0x91, 0x96, 0x69, 0xa9, + 0xd0, 0x16, 0x55, 0xba, 0x5a, 0xce, 0x1e, 0x1b, 0x9b, 0x82, 0xaf, 0x53, + 0x06, 0x61, 0xdb, 0x43, 0x42, 0x1a, 0x3c, 0xef, 0xda, 0xea, 0x62, 0x0a, + 0xe2, 0x71, 0x7c, 0x5b, 0xe8, 0xce, 0xf8, 0xcb, 0xf3, 0x19, 0x9a, 0x6b, + 0x90, 0xd0, 0x43, 0x4c, 0xd3, 0x0a, 0x9b, 0x64, 0x64, 0x70, 0x52, 0x07, + 0x2a, 0x17, 0xec, 0x20, 0x3f, 0x15, 0x5e, 0xd3, 0xd8, 0x3a, 0x6e, 0x40, + 0x2f, 0xa5, 0x1a, 0xcd, 0x45, 0x1f, 0x4c, 0x48, 0x54, 0x40, 0x60, 0xef, + 0x32, 0xe3, 0x8c, 0xa7, 0xe0, 0x86, 0xe5, 0xf9, 0x25, 0xe0, 0xb6, 0x89, + 0x1d, 0x5c, 0x9a, 0xe0, 0x2d, 0xb7, 0xba, 0x88, 0x99, 0x15, 0x3b, 0x15, + 0xb6, 0x0f, 0x54, 0xb7, 0x1f, 0xdc, 0x89, 0x24, 0xe2, 0xfd, 0x69, 0xef, + 0x50, 0x4b, 0x42, 0xf9, 0x48, 0xf3, 0x02, 0x8d, 0xe0, 0x0d, 0x8e, 0x99, + 0x2c, 0x68, 0xb7, 0x95, 0x57, 0x26, 0x88, 0x61, 0x84, 0x72, 0x22, 0x6f, + 0xc0, 0xd9, 0x17, 0x3a, 0x53, 0x8c, 0xe1, 0x4a, 0x80, 0x75, 0xa8, 0xb2, + 0x25, 0xaa, 0xce, 0x67, 0x3a, 0x0f, 0x40, 0x87, 0xeb, 0x4f, 0xe4, 0x2c, + 0xc5, 0x74, 0xa9, 0xb0, 0x5b, 0x6c, 0x9b, 0xe3, 0x9b, 0x8b, 0x80, 0x30, + 0x7c, 0x96, 0x95, 0xa1, 0xde, 0xef, 0xdd, 0x00, 0x77, 0x2a, 0x3c, 0x61, + 0x8d, 0x6e, 0x74, 0xff, 0x7c, 0x90, 0xe0, 0x1e, 0x6b, 0x4e, 0x79, 0x8e, + 0xbc, 0x8d, 0x2e, 0x8b, 0x43, 0x0f, 0xe6, 0xbd, 0xd5, 0xf0, 0x36, 0x0a, + 0xca, 0xfc, 0x89, 0xf1, 0x59, 0x49, 0x2b, 0x72, 0xcb, 0x57, 0xa1, 0x90, + 0x46, 0x21, 0x2c, 0x05, 0x93, 0xb5, 0xe7, 0x7e, 0x6d, 0xda, 0xd7, 0xdb, + 0x04, 0x9a, 0x33, 0x6d, 0x36, 0xd5, 0xaf, 0x7f, 0xa8, 0x7c, 0xed, 0x06, + 0x6a, 0xe7, 0xd9, 0xbc, 0xf2, 0x07, 0x0b, 0x73, 0xee, 0x87, 0xd7, 0x90, + 0xb0, 0xda, 0xa8, 0xe9, 0xd4, 0x38, 0x2d, 0xb4, 0xb9, 0x33, 0x06, 0xa6, + 0x65, 0xe0, 0xc0, 0x60, 0xd5, 0x66, 0x87, 0x7b, 0x63, 0xc5, 0x08, 0x91, + 0x22, 0x7c, 0xe0, 0x24, 0xb7, 0x3b, 0xc2, 0xb2, 0xc1, 0xb6, 0xa5, 0x50, + 0x40, 0x7a, 0xd0, 0xc4, 0x38, 0x61, 0xc6, 0x73, 0x70, 0x29, 0xe5, 0x53, + 0x7c, 0xf3, 0xbf, 0x29, 0x36, 0xc1, 0xad, 0xa4, 0xf5, 0x13, 0xd8, 0xd3, + 0x54, 0x66, 0xb6, 0xd5, 0xd4, 0x09, 0xed, 0xf8, 0x30, 0x96, 0xfc, 0x2d, + 0x9a, 0x17, 0x98, 0xe3, 0x62, 0x25, 0x25, 0xc9, 0xad, 0x1e, 0x76, 0xc5, + 0x14, 0x8e, 0x38, 0x6c, 0xca, 0x18, 0x96, 0x04, 0x84, 0x9f, 0xa3, 0x2e, + 0xcc, 0x0c, 0x4b, 0x13, 0x7a, 0x1c, 0xe3, 0x7e, 0x43, 0xa2, 0x51, 0x67, + 0x04, 0x09, 0x9b, 0xd1, 0x92, 0x7c, 0x89, 0x25, 0x05, 0xe4, 0x33, 0x18, + 0x59, 0x64, 0xba, 0xdb, 0x9d, 0xc4, 0x63, 0xb5, 0xb5, 0x47, 0xdf, 0xde, + 0x6e, 0x33, 0xae, 0x14, 0x7f, 0x1b, 0x94, 0x80, 0xa8, 0xb5, 0x92, 0x3c, + 0x16, 0xb3, 0x0a, 0x52, 0xa6, 0x44, 0xe7, 0x2e, 0xce, 0x1b, 0x47, 0xa4, + 0x5f, 0x8e, 0x80, 0xb1, 0x98, 0xe1, 0x23, 0x23, 0x16, 0xdc, 0xc7, 0x28, + 0xe8, 0xf6, 0xc6, 0x21, 0x42, 0x70, 0xd3, 0xd8, 0xe6, 0x31, 0xbb, 0x43, + 0x23, 0x8f, 0x62, 0x32, 0xdd, 0xf6, 0x82, 0x22, 0x81, 0xd9, 0x2b, 0x93, + 0x28, 0xfe, 0xe5, 0xec, 0x41, 0x09, 0x56, 0x7f, 0xe8, 0x40, 0x78, 0x42, + 0xa2, 0x77, 0x6f, 0x2b, 0xe6, 0xfd, 0x25, 0xe8, 0xaa, 0x75, 0x7f, 0xdf, + 0xa1, 0x2a, 0xdc, 0xa5, 0xe9, 0xa2, 0x2e, 0x9e, 0x85, 0x77, 0x80, 0xf8, + 0x68, 0x60, 0xde, 0xed, 0xe1, 0xec, 0xa2, 0x1f, 0x5c, 0xf7, 0xad, 0xf3, + 0x42, 0x21, 0xef, 0x7e, 0x2c, 0x3f, 0x75, 0x58, 0xad, 0x97, 0x1e, 0x8e, + 0x08, 0xb2, 0x46, 0x41, 0x10, 0x4c, 0xe0, 0x2f, 0x6d, 0x38, 0xc1, 0xc0, + 0xaf, 0xd5, 0x90, 0x0a, 0x5c, 0x2b, 0x89, 0x64, 0x92, 0x26, 0xd4, 0xf5, + 0xbe, 0xc1, 0xbf, 0xbe, 0xe5, 0xd3, 0x13, 0x82, 0x77, 0xb2, 0x4d, 0xa8, + 0x32, 0x0d, 0x39, 0xd8, 0xfa, 0x08, 0x8e, 0xb8, 0x27, 0x9d, 0x78, 0xd2, + 0x6a, 0x4f, 0x19, 0xee, 0x85, 0xc7, 0x63, 0x10, 0x48, 0xbf, 0xb9, 0xab, + 0x23, 0xae, 0xde, 0xa3, 0xb6, 0x68, 0xfd, 0x12, 0x31, 0xd3, 0x99, 0xcf, + 0xc1, 0x8b, 0x30, 0xf8, 0x2b, 0x65, 0xc8, 0x6c, 0xb0, 0xd7, 0xcd, 0xaa, + 0x15, 0x02, 0xa4, 0x43, 0x05, 0x79, 0x73, 0xb1, 0xbc, 0xd8, 0x53, 0x53, + 0x39, 0xc6, 0xe5, 0x9b, 0xaa, 0x9d, 0x58, 0xce, 0xe2, 0x0a, 0xb3, 0x46, + 0x48, 0x60, 0x96, 0x04, 0x33, 0x93, 0x6c, 0xf7, 0xfa, 0x27, 0xee, 0x14, + 0xdb, 0xad, 0xa0, 0x40, 0xc9, 0x3c, 0x64, 0x04, 0x28, 0x8e, 0xcc, 0x75, + 0x10, 0x89, 0x04, 0x6c, 0x4a, 0xc8, 0x9f, 0xf0, 0xb9, 0xfc, 0xe2, 0xc5, + 0xd1, 0x4e, 0x3e, 0x88, 0xb2, 0x89, 0x93, 0x1e, 0x63, 0x4e, 0x64, 0xb5, + 0x39, 0xbd, 0x9b, 0x02, 0xa2, 0xcf, 0xe2, 0xbc, 0xdf, 0x83, 0xd7, 0x38, + 0x68, 0xf2, 0xf1, 0x50, 0x10, 0xa5, 0x37, 0xb0, 0x2f, 0x1e, 0x56, 0x5f, + 0x4e, 0xaf, 0xb4, 0x41, 0x62, 0x8e, 0x6c, 0xd7, 0xcd, 0xc6, 0x42, 0x60, + 0x17, 0xa7, 0x15, 0xc6, 0xf8, 0x70, 0x2c, 0xd4, 0x4d, 0x6e, 0xaf, 0x23, + 0xf0, 0xdd, 0x5b, 0x49, 0x9f, 0x2c, 0xfd, 0xa8, 0xb7, 0xd3, 0xec, 0x06, + 0x8e, 0x7f, 0xcb, 0x33, 0x44, 0xc1, 0x5b, 0x6d, 0x7e, 0x8d, 0xd1, 0xd5, + 0x37, 0x67, 0x15, 0x94, 0xa7, 0x37, 0x1f, 0x2a, 0x53, 0xa6, 0x41, 0xb4, + 0x62, 0x8a, 0x51, 0xce, 0x0f, 0x8c, 0xaf, 0xc7, 0x17, 0x42, 0x6b, 0x9d, + 0x4f, 0x2b, 0x4d, 0x94, 0x75, 0x73, 0xa0, 0x59, 0x1b, 0xc4, 0x84, 0x70, + 0xf0, 0x2d, 0x95, 0xed, 0x12, 0x80, 0x5e, 0xfd, 0x1d, 0x3d, 0x23, 0xa9, + 0x58, 0x6f, 0xb4, 0xe8, 0x68, 0x4f, 0x99, 0x27, 0xce, 0x73, 0x74, 0xe0, + 0xfb, 0x97, 0xcc, 0xa0, 0x4d, 0xa6, 0x13, 0x0f, 0x0e, 0x5c, 0x36, 0xac, + 0xa6, 0xd8, 0x7f, 0x7d, 0xca, 0x98, 0xf6, 0xa1, 0xb3, 0x11, 0x9a, 0x61, + 0x65, 0xcc, 0x23, 0xb7, 0xce, 0xb3, 0x1a, 0x98, 0xa2, 0x5d, 0x96, 0x40, + 0x22, 0x83, 0x07, 0x6d, 0xf1, 0x14, 0x18, 0x37, 0x97, 0x1b, 0x98, 0x5f, + 0xd3, 0x47, 0xfd, 0x9b, 0xcc, 0xbc, 0xb4, 0x7e, 0x1a, 0xc5, 0x3d, 0x00, + 0x58, 0xe1, 0x96, 0x77, 0xd1, 0x88, 0x4a, 0x3c, 0xd5, 0xe7, 0xe8, 0xba, + 0xf0, 0xe7, 0x1c, 0x65, 0x83, 0x17, 0x50, 0xee, 0xee, 0x95, 0xe6, 0xb7, + 0x73, 0x68, 0x1d, 0xe7, 0xbf, 0x98, 0x44, 0xc6, 0x01, 0xf3, 0x0b, 0xbc, + 0xf8, 0x69, 0xe3, 0x30, 0xb7, 0x0a, 0x70, 0x61, 0x57, 0x79, 0x06, 0x4d, + 0x4c, 0x09, 0x97, 0xa9, 0x84, 0x20, 0x9f, 0xea, 0x92, 0x43, 0xb8, 0x6a, + 0x0a, 0x21, 0x8f, 0xd3, 0x1f, 0xa9, 0x2d, 0x5e, 0xae, 0x28, 0x54, 0xce, + 0x34, 0x77, 0xd8, 0xda, 0xaf, 0x31, 0xdf, 0xee, 0x44, 0xf8, 0x99, 0x76, + 0x7d, 0xe2, 0x72, 0x81, 0xd7, 0xa5, 0x32, 0xa6, 0xe2, 0x42, 0xe8, 0xf8, + 0x60, 0x72, 0xa8, 0x56, 0x7e, 0x03, 0x4c, 0x4f, 0xd3, 0xae, 0x80, 0xc0, + 0xcf, 0xd1, 0xfb, 0xfa, 0x67, 0x5a, 0x9b, 0x2c, 0x75, 0x8c, 0x19, 0x7c, + 0xe4, 0x18, 0x1b, 0xc0, 0xda, 0x14, 0x3f, 0x66, 0x56, 0xa6, 0x6c, 0xd6, + 0x38, 0x84, 0x8c, 0xa7, 0xed, 0x7d, 0xd0, 0x56, 0x70, 0x17, 0x08, 0x7c, + 0x31, 0xcd, 0x35, 0x40, 0x89, 0xe9, 0x0a, 0xb8, 0x8e, 0x79, 0xb8, 0x7b, + 0xa0, 0x9d, 0x26, 0x8c, 0x05, 0x27, 0xee, 0x18, 0x0e, 0x92, 0x3c, 0xa6, + 0x43, 0x0f, 0xe6, 0xa9, 0x1f, 0xa0, 0x1b, 0x95, 0x90, 0x8e, 0x60, 0xd8, + 0xae, 0x45, 0xf0, 0xc5, 0xca, 0x86, 0x52, 0x18, 0x6a, 0x2f, 0x9e, 0x1e, + 0x14, 0x73, 0x68, 0x77, 0xbe, 0x95, 0x66, 0xd6, 0x6e, 0xc7, 0x6d, 0x9c, + 0xa6, 0x9a, 0x78, 0xf2, 0x0c, 0xb9, 0x73, 0x6c, 0x96, 0xae, 0x89, 0x5d, + 0xd2, 0x30, 0xbf, 0x29, 0x69, 0x66, 0x1a, 0x7b, 0xa4, 0x9b, 0x99, 0x20, + 0x97, 0x2b, 0x81, 0xdb, 0xa8, 0x5d, 0xde, 0xa6, 0x5b, 0x3e, 0x88, 0x2b, + 0xbf, 0x98, 0x92, 0x07, 0x8c, 0xf9, 0x37, 0x8a, 0xc0, 0x61, 0x57, 0x1e, + 0x75, 0xd2, 0xbb, 0x02, 0x4c, 0xde, 0x14, 0x71, 0x35, 0x4c, 0x08, 0x4e, + 0x16, 0x06, 0x79, 0x0f, 0x8e, 0xda, 0xb4, 0xde, 0x23, 0x3b, 0x1a, 0xd2, + 0x19, 0xe9, 0x76, 0x7a, 0x47, 0xd9, 0x54, 0xe1, 0x8e, 0x5f, 0x1f, 0x49, + 0x07, 0xd5, 0xf0, 0x9f, 0xff, 0x75, 0xa1, 0x1c, 0xa3, 0xdd, 0x11, 0x17, + 0x30, 0x64, 0x17, 0xc4, 0x7c, 0x46, 0xfe, 0xf2, 0x39, 0x91, 0xfa, 0x25, + 0x14, 0xda, 0x51, 0x94, 0xf0, 0xe4, 0xe8, 0x76, 0x1f, 0x4e, 0x8d, 0x29, + 0x93, 0x98, 0x1e, 0x98, 0xe5, 0x92, 0xbe, 0x34, 0x8c, 0x14, 0x3d, 0xc7, + 0x5b, 0x1e, 0xdf, 0x89, 0x3b, 0xb8, 0x66, 0x2d, 0x93, 0x38, 0xc7, 0xe1, + 0x8c, 0xab, 0x88, 0xf6, 0x7e, 0x5d, 0xc2, 0x0a, 0xf6, 0x26, 0xbb, 0xef, + 0x5e, 0xf8, 0x65, 0x1c, 0xd8, 0xa4, 0xf4, 0xc6, 0xfb, 0x2e, 0x40, 0xc9, + 0x21, 0x20, 0x1e, 0x4a, 0x05, 0x0a, 0x40, 0xb8, 0x8d, 0xaa, 0x9a, 0x7d, + 0x7a, 0xc3, 0x67, 0xde, 0x68, 0x06, 0x83, 0xc0, 0xae, 0xc3, 0x61, 0x1a, + 0x1d, 0x85, 0xe6, 0x15, 0xdd, 0x6a, 0x54, 0xae, 0xda, 0x1b, 0x4f, 0x28, + 0xd2, 0xbb, 0xd2, 0x6f, 0x20, 0x83, 0x55, 0x32, 0xf9, 0x65, 0xac, 0xde, + 0x30, 0xb9, 0x2a, 0x5e, 0x66, 0x2d, 0x1e, 0x02, 0x7b, 0x7d, 0x30, 0x2f, + 0x5a, 0x6a, 0xd0, 0x14, 0xe1, 0xef, 0x21, 0x70, 0x03, 0x92, 0x37, 0x76, + 0x8d, 0x27, 0x84, 0xdc, 0x23, 0x36, 0x46, 0x56, 0x0a, 0x52, 0x94, 0xad, + 0x1f, 0x5d, 0x9f, 0x6e, 0x67, 0x50, 0x6d, 0xad, 0x66, 0x8f, 0x23, 0x9a, + 0xa8, 0xc2, 0xa5, 0xec, 0xb4, 0xaf, 0x43, 0xc1, 0x05, 0xf1, 0x49, 0x86, + 0xb6, 0xbb, 0x79, 0xbb, 0x66, 0xa3, 0xa1, 0xc4, 0x90, 0xd1, 0x1b, 0xb1, + 0x13, 0xeb, 0x79, 0x63, 0x42, 0xc0, 0xc6, 0x0d, 0xd6, 0xea, 0xab, 0x97, + 0xeb, 0xf3, 0x66, 0xc4, 0xd9, 0xc0, 0xd2, 0x1e, 0x7d, 0x68, 0xcc, 0xf0, + 0x7d, 0x9d, 0x47, 0x41, 0xdc, 0x7f, 0x9c, 0x86, 0x55, 0x53, 0xf2, 0x9e, + 0x19, 0x92, 0x85, 0xbb, 0x0d, 0x49, 0x99, 0xa9, 0x80, 0x98, 0x6a, 0x65, + 0x32, 0x43, 0xaf, 0xbe, 0x61, 0x19, 0x15, 0x5b, 0x1c, 0x77, 0x9a, 0x9f, + 0x59, 0x91, 0x97, 0x31, 0x46, 0x70, 0x79, 0x35, 0xa1, 0xf4, 0x13, 0x6b, + 0xf9, 0xac, 0x65, 0x81, 0xfc, 0x8a, 0xef, 0xf7, 0x0b, 0x5a, 0x89, 0x20, + 0x1a, 0x3a, 0x91, 0x1e, 0x67, 0x55, 0x4d, 0xe3, 0xb1, 0xef, 0x8d, 0x72, + 0x8c, 0x6e, 0x83, 0xe0, 0xcc, 0xcc, 0x98, 0xf5, 0x49, 0xe3, 0x17, 0xca, + 0xab, 0xef, 0x69, 0x12, 0x8b, 0xd0, 0x36, 0xfd, 0xa5, 0xbf, 0x0b, 0x3b, + 0x67, 0x3f, 0x2f, 0xa6, 0x47, 0xba, 0xb1, 0xa8, 0x14, 0xdb, 0x43, 0xe7, + 0x89, 0x08, 0xee, 0x16, 0x43, 0xb3, 0x52, 0x6e, 0xed, 0x30, 0x60, 0xfa, + 0xf7, 0x15, 0x5b, 0xf7, 0x3d, 0xd5, 0x74, 0x92, 0x62, 0x42, 0xa3, 0xa2, + 0xb9, 0xa4, 0x16, 0xca, 0x3d, 0xfc, 0x37, 0x3f, 0x6f, 0xe3, 0x30, 0x91, + 0xe5, 0x23, 0xf8, 0x7d, 0x81, 0xda, 0x0b, 0x86, 0xcd, 0x5a, 0x35, 0x4f, + 0x55, 0x30, 0xeb, 0x0b, 0x73, 0xb7, 0xdc, 0xe9, 0x4f, 0x8f, 0x12, 0x26, + 0x55, 0x2e, 0xdb, 0x7d, 0xa6, 0x62, 0xea, 0xc3, 0xd7, 0xb4, 0x5b, 0xeb, + 0x0d, 0x19, 0x91, 0x4b, 0xeb, 0x4c, 0x79, 0x18, 0x56, 0x5a, 0xc4, 0x90, + 0xba, 0x91, 0xd5, 0x77, 0x31, 0x90, 0x48, 0x7c, 0x4a, 0xd5, 0xa0, 0x45, + 0x5a, 0x2c, 0x27, 0xc7, 0x79, 0x12, 0x1e, 0x4f, 0xb2, 0x2e, 0xf8, 0x1b, + 0x3a, 0xb4, 0x3d, 0x8f, 0x0f, 0xc6, 0xef, 0xd6, 0xb7, 0xdd, 0x80, 0x06, + 0xd3, 0x28, 0x78, 0x3e, 0x33, 0xf3, 0x21, 0xbf, 0xdb, 0xf7, 0xce, 0x79, + 0xbe, 0x83, 0x60, 0xa9, 0x26, 0x41, 0xea, 0x77, 0xfa, 0x22, 0x52, 0x14, + 0xc5, 0x8d, 0x68, 0xe9, 0x57, 0x78, 0xc7, 0xca, 0xda, 0xf2, 0xbb, 0xd5, + 0xd5, 0xea, 0x82, 0x64, 0xcd, 0xa4, 0xb0, 0xd4, 0x75, 0xce, 0xbe, 0x33, + 0xdb, 0x5d, 0x5f, 0x9d, 0x3b, 0x9e, 0xeb, 0x05, 0x7a, 0x72, 0x90, 0x71, + 0x88, 0x26, 0xb0, 0x25, 0xc3, 0xfa, 0xf9, 0xfd, 0x03, 0x8d, 0x48, 0x02, + 0x8a, 0xc9, 0x82, 0x18, 0x99, 0x49, 0xa3, 0x96, 0xea, 0xa5, 0x71, 0xd9, + 0x2b, 0xc6, 0x38, 0x96, 0x87, 0x3c, 0xbf, 0x2b, 0xf5, 0x62, 0xe7, 0xb7, + 0xbd, 0x8f, 0xf7, 0xdf, 0x10, 0x26, 0x41, 0xcc, 0xbf, 0xd8, 0xe5, 0xfe, + 0xbd, 0x87, 0xdb, 0x24, 0xbc, 0x71, 0x6c, 0xcb, 0x3d, 0xf5, 0x5b, 0x5a, + 0x53, 0xd8, 0xa7, 0xb0, 0x4c, 0x70, 0x7e, 0xf0, 0x64, 0x4f, 0x7a, 0xa8, + 0x22, 0x82, 0x34, 0xeb, 0xe1, 0x2b, 0xbd, 0xbf, 0x85, 0x8a, 0xb6, 0xa9, + 0x0a, 0xc5, 0x6d, 0x5f, 0xf0, 0xf6, 0x1b, 0xa8, 0x0f, 0x51, 0x3c, 0x1b, + 0xf7, 0x46, 0x45, 0xdd, 0x45, 0x55, 0x20, 0xe6, 0xc3, 0x55, 0x70, 0x13, + 0x8a, 0x24, 0xa7, 0x27, 0x38, 0x53, 0xd8, 0xb6, 0xe4, 0x71, 0xd6, 0xe4, + 0x8b, 0x14, 0xbe, 0x8f, 0xc9, 0x4c, 0x1e, 0x38, 0xef, 0x23, 0x53, 0xc0, + 0xd4, 0x24, 0x6b, 0x50, 0xa3, 0x2a, 0xf5, 0xa9, 0x9f, 0xcc, 0x72, 0x03, + 0x4b, 0x47, 0xe4, 0xa3, 0xfa, 0xe1, 0x65, 0xde, 0x85, 0xba, 0x28, 0xe5, + 0x0b, 0xc6, 0x1a, 0x3f, 0xcc, 0x21, 0xfd, 0x3e, 0xcd, 0x3c, 0xc4, 0xc5, + 0x5b, 0xd3, 0xef, 0x85, 0x1e, 0x3c, 0x06, 0x2b, 0xcf, 0x26, 0xa1, 0x7a, + 0xa0, 0x7a, 0xfe, 0x51, 0xe1, 0x05, 0xb1, 0x42, 0xa0, 0xdb, 0x6a, 0x0f, + 0xa4, 0x45, 0xee, 0x54, 0x6a, 0xc6, 0xf6, 0x1f, 0x80, 0xd7, 0x07, 0x31, + 0x96, 0xfc, 0xf3, 0x47, 0x8c, 0x66, 0xe9, 0x41, 0x39, 0x37, 0x89, 0x05, + 0x85, 0x3d, 0xb0, 0x0a, 0x2e, 0x19, 0x97, 0x20, 0xf2, 0x85, 0x36, 0x6f, + 0x2b, 0xd4, 0x1c, 0x47, 0x1f, 0xdd, 0xd7, 0xff, 0xc5, 0xaf, 0x8d, 0xfc, + 0x5e, 0xd4, 0x63, 0x6e, 0xb0, 0x8e, 0xb8, 0x58, 0x13, 0xe7, 0xf3, 0x7d, + 0x38, 0xfc, 0x4b, 0x51, 0x85, 0xc8, 0x2f, 0xc9, 0x6f, 0x41, 0x91, 0xbf, + 0x34, 0xae, 0xeb, 0x40, 0x14, 0x1b, 0x37, 0xd1, 0x6f, 0x70, 0x6e, 0x5f, + 0x13, 0xa7, 0x3d, 0x63, 0x93, 0x60, 0x6e, 0x18, 0xd2, 0xcb, 0x68, 0x71, + 0xa4, 0xca, 0xeb, 0xf7, 0x80, 0x0c, 0x63, 0x0c, 0x70, 0xcd, 0x72, 0x4e, + 0xcb, 0x23, 0x5b, 0x96, 0x14, 0x32, 0x77, 0xc4, 0x82, 0xb8, 0xb1, 0x24, + 0x68, 0x2a, 0x72, 0x73, 0x85, 0xe6, 0x02, 0x29, 0x0c, 0x8f, 0xae, 0x88, + 0x24, 0xaa, 0x13, 0x6f, 0xaf, 0xd2, 0xaa, 0xa9, 0x77, 0x57, 0xbe, 0xbd, + 0x16, 0x83, 0xea, 0xc0, 0xad, 0x01, 0x27, 0xf1, 0x4f, 0x87, 0x49, 0x32, + 0x02, 0x9a, 0x19, 0xe6, 0x97, 0xb1, 0x6d, 0x4a, 0x85, 0x5f, 0x19, 0x07, + 0x88, 0xe2, 0xc2, 0x9f, 0x1d, 0xfd, 0xb0, 0x10, 0xf2, 0x66, 0xb1, 0x04, + 0xfd, 0x54, 0x75, 0x92, 0x35, 0xa2, 0x3b, 0x0e, 0x93, 0xde, 0x53, 0x62, + 0x66, 0x99, 0xe0, 0x97, 0x04, 0x46, 0x07, 0x9d, 0xff, 0xaa, 0x30, 0x53, + 0xe1, 0xfc, 0xb4, 0x87, 0x68, 0x59, 0x5b, 0x23, 0x35, 0x40, 0xbc, 0xd4, + 0xd9, 0x21, 0xa7, 0xff, 0xee, 0x38, 0x32, 0x45, 0xcc, 0xa9, 0x82, 0xff, + 0x5c, 0x8f, 0x67, 0x3c, 0xfd, 0x52, 0x4a, 0xf6, 0x4e, 0x2b, 0x8f, 0x48, + 0xe9, 0xa5, 0x2a, 0x21, 0xb7, 0x6a, 0x08, 0xe5, 0x25, 0x3c, 0xf7, 0xf9, + 0xd8, 0xb0, 0x12, 0x99, 0xea, 0x21, 0x84, 0xc8, 0x2b, 0x96, 0xdb, 0x52, + 0x59, 0x55, 0xa7, 0x36, 0x57, 0x1b, 0xd7, 0x82, 0x13, 0xd9, 0x77, 0xdf, + 0xa6, 0xba, 0xb8, 0xbc, 0x5a, 0x56, 0x5b, 0xcb, 0x90, 0xb5, 0x99, 0x9c, + 0xa6, 0x67, 0x64, 0x41, 0xfe, 0x38, 0x25, 0x8e, 0x61, 0x8c, 0x38, 0x4d, + 0xf1, 0x21, 0x37, 0x6d, 0xa2, 0x0f, 0xfe, 0x93, 0xee, 0xf0, 0x33, 0x78, + 0x80, 0xfd, 0xc1, 0xc8, 0xfa, 0x51, 0x9f, 0x8b, 0x78, 0xd1, 0x2e, 0x10, + 0xe8, 0xb4, 0xae, 0xe7, 0xe1, 0x10, 0xbd, 0x9c, 0x07, 0x8f, 0x0d, 0xcf, + 0x2b, 0x3d, 0xfb, 0x26, 0x83, 0xc7, 0xbe, 0x83, 0x02, 0xb6, 0x4f, 0xfe, + 0x8a, 0x0a, 0x9e, 0x4d, 0x76, 0x32, 0x2a, 0xa1, 0x12, 0xf4, 0xf7, 0x08, + 0xad, 0x71, 0xf9, 0x82, 0xe1, 0x8d, 0x40, 0x00, 0x3c, 0x6f, 0xa7, 0xdf, + 0x8b, 0x65, 0x46, 0x38, 0xfa, 0xa1, 0xf9, 0x93, 0x6c, 0x6e, 0xcf, 0xde, + 0x3f, 0x16, 0x2d, 0xee, 0x4b, 0xf6, 0xda, 0xf1, 0xee, 0xab, 0xfa, 0xc3, + 0x2d, 0xd1, 0xd7, 0x08, 0x08, 0x62, 0xba, 0xdc, 0xd8, 0xd3, 0x53, 0xd1, + 0xcd, 0xaa, 0x77, 0x9d, 0x8a, 0xa2, 0xd9, 0x14, 0x24, 0x7a, 0x9e, 0x4b, + 0x08, 0x1c, 0xc0, 0xe4, 0x37, 0x5e, 0x78, 0x4f, 0x7d, 0x06, 0x2e, 0x6e, + 0x70, 0xbf, 0x50, 0x5b, 0xcf, 0xf8, 0x06, 0x32, 0xad, 0xc8, 0x5e, 0xc5, + 0x8c, 0xcf, 0x87, 0x28, 0x97, 0xb8, 0x21, 0xda, 0x16, 0x91, 0xdf, 0x37, + 0x45, 0x3c, 0xe6, 0xc0, 0x56, 0xae, 0x89, 0x4d, 0xfb, 0x07, 0xe7, 0xb1, + 0x1a, 0xd2, 0x46, 0x3c, 0xd8, 0xbf, 0x37, 0x4b, 0xbf, 0x5e, 0xdd, 0xea, + 0xe2, 0x22, 0xe8, 0x76, 0xcc, 0x01, 0x4e, 0xeb, 0x5b, 0xa9, 0xcd, 0xcb, + 0xa9, 0xbc, 0xef, 0x1e, 0xea, 0x26, 0x42, 0x71, 0x6c, 0xe5, 0xae, 0x4b, + 0x47, 0x7c, 0xae, 0x4e, 0x39, 0x69, 0x83, 0x00, 0xe9, 0x11, 0x61, 0x82, + 0xd9, 0x70, 0x99, 0x8b, 0xa9, 0x95, 0xd4, 0x16, 0x68, 0x6a, 0x79, 0xb3, + 0xb1, 0xe9, 0x17, 0xb5, 0xff, 0xed, 0xed, 0x00, 0xdc, 0xdf, 0x04, 0x13, + 0x7d, 0xc8, 0x95, 0xb2, 0xe2, 0xc6, 0xbe, 0xa8, 0x8b, 0x7d, 0x1f, 0x2b, + 0x21, 0x68, 0x2f, 0x55, 0x65, 0x45, 0x46, 0x05, 0x85, 0xf0, 0x27, 0x5d, + 0x88, 0x70, 0x73, 0x57, 0x4e, 0x67, 0xc2, 0xad, 0x3b, 0x0c, 0x1f, 0x8a, + 0x92, 0xbb, 0x49, 0xe6, 0x43, 0xee, 0x4c, 0x2e, 0x6d, 0x3c, 0xa6, 0xe4, + 0xb2, 0x88, 0xa8, 0x47, 0xcf, 0x50, 0x4f, 0x5a, 0x45, 0x55, 0xc4, 0xb1, + 0x67, 0x2f, 0xff, 0x45, 0x00, 0xf3, 0xfb, 0xec, 0x56, 0x3a, 0x2b, 0x30, + 0xd3, 0x08, 0x29, 0x4e, 0x7a, 0x27, 0xc3, 0xe7, 0xf8, 0x59, 0x92, 0x7b, + 0xdd, 0xbd, 0x6d, 0xe3, 0x4e, 0x31, 0x8e, 0x9b, 0xcd, 0xba, 0x98, 0xb2, + 0x21, 0x75, 0xaf, 0x42, 0xf7, 0x95, 0xc8, 0xb2, 0x44, 0x13, 0x40, 0x79, + 0xcd, 0xfb, 0xbb, 0x58, 0x60, 0xbf, 0xa6, 0x0b, 0x4c, 0xfb, 0x5b, 0x0f, + 0xd8, 0xe2, 0xb9, 0x38, 0x6f, 0x35, 0x21, 0x7e, 0xae, 0x7b, 0x1c, 0x43, + 0x96, 0xa7, 0x0b, 0x7d, 0xb7, 0x68, 0xb4, 0x8e, 0x86, 0xa4, 0xe5, 0xd6, + 0x97, 0x7e, 0x8c, 0xe2, 0x14, 0x24, 0xf5, 0xd8, 0xb2, 0xf2, 0x0b, 0x23, + 0x18, 0xd0, 0xb4, 0xb4, 0x90, 0x01, 0x2f, 0x56, 0xd6, 0x70, 0xd0, 0xf2, + 0x2b, 0x3c, 0x9a, 0x20, 0xa7, 0x71, 0xdc, 0x3a, 0x45, 0x42, 0x40, 0xe6, + 0x72, 0x0c, 0x3c, 0x56, 0xe2, 0x06, 0x4f, 0xc0, 0x2d, 0x94, 0xe0, 0xe4, + 0x2e, 0xae, 0x33, 0x21, 0x01, 0xab, 0x6f, 0x87, 0x4f, 0x9a, 0x73, 0x55, + 0x98, 0x0b, 0x48, 0x89, 0xff, 0x19, 0x41, 0x33, 0x12, 0xcd, 0xb7, 0x3b, + 0x49, 0x8d, 0x5f, 0xcd, 0xc1, 0x90, 0x71, 0xc4, 0xec, 0x96, 0x7f, 0xa1, + 0xe5, 0xc9, 0x84, 0x43, 0x49, 0x9a, 0xb0, 0x02, 0xd9, 0xb1, 0x52, 0x80, + 0x0c, 0xe0, 0xc8, 0x63, 0x7a, 0x2c, 0x31, 0x57, 0xb7, 0x5e, 0x75, 0xb2, + 0x82, 0x8c, 0x43, 0x0d, 0x65, 0xd4, 0x29, 0x24, 0x8c, 0x3c, 0x83, 0xf8, + 0xc8, 0x7b, 0x37, 0x60, 0xae, 0x70, 0x22, 0x14, 0xe5, 0x4b, 0x7a, 0xfa, + 0xe3, 0xc1, 0xd5, 0x97, 0xe6, 0xce, 0x33, 0xdc, 0xe2, 0x86, 0xd4, 0xb2, + 0x23, 0x6f, 0x3e, 0xd7, 0x9c, 0x45, 0xdc, 0x16, 0x9d, 0x88, 0x46, 0xad, + 0xfe, 0x05, 0x14, 0xb6, 0x0f, 0xaf, 0xd2, 0xca, 0x4b, 0xc8, 0x3f, 0x5d, + 0x06, 0x98, 0xc1, 0xdf, 0x1d, 0xd8, 0xa9, 0x45, 0xb4, 0x8e, 0x37, 0xef, + 0x37, 0x04, 0xa3, 0x28, 0xbd, 0xf3, 0xd0, 0x68, 0x29, 0xb1, 0x85, 0x21, + 0x0b, 0xfa, 0x08, 0xc8, 0x6b, 0xa4, 0xc5, 0x52, 0xdf, 0x08, 0xa7, 0x56, + 0x52, 0x31, 0x8f, 0xed, 0x5a, 0x48, 0x8d, 0xe5, 0xf2, 0x62, 0x98, 0x5b, + 0x05, 0x5c, 0x7b, 0xb3, 0xe0, 0x2a, 0xc9, 0x24, 0x18, 0x7d, 0xa5, 0x5f, + 0x3d, 0xa5, 0x0e, 0x9b, 0xb5, 0xb2, 0x37, 0x09, 0x09, 0x35, 0x20, 0x7d, + 0x09, 0x5b, 0x1c, 0x23, 0xbf, 0x7e, 0x29, 0x78, 0x25, 0xc8, 0xa8, 0x0a, + 0xef, 0x7b, 0x63, 0x2a, 0x66, 0xb9, 0xd6, 0x12, 0xe8, 0xbd, 0x06, 0x3f, + 0xc2, 0x39, 0x30, 0xd1, 0xfd, 0xfd, 0x95, 0x0a, 0xe8, 0x34, 0xe3, 0xbc, + 0x9f, 0x95, 0x2b, 0x73, 0x29, 0x43, 0xa1, 0xe6, 0x5e, 0xaf, 0xa8, 0xca, + 0x1c, 0x7d, 0xf0, 0xda, 0xf0, 0x5d, 0x54, 0x60, 0x6f, 0x9c, 0x05, 0xc8, + 0xc0, 0xbf, 0x33, 0x3c, 0x9f, 0xd0, 0xd6, 0xd7, 0x57, 0xe1, 0x3e, 0xc9, + 0x97, 0x7c, 0x93, 0xf3, 0x0f, 0xdc, 0x68, 0xc6, 0x60, 0x3e, 0xfa, 0x50, + 0xc7, 0xf2, 0x62, 0x0e, 0x3f, 0xbe, 0x06, 0x2b, 0xb1, 0x96, 0x9c, 0x26, + 0x7c, 0xcb, 0x1f, 0xc0, 0xef, 0x6a, 0x64, 0xad, 0x70, 0x58, 0x8e, 0x06, + 0x88, 0xd9, 0x78, 0x0d, 0x5b, 0x70, 0x3d, 0x8f, 0xeb, 0x02, 0xf7, 0xbe, + 0x9e, 0x42, 0xac, 0x17, 0xb4, 0xab, 0xc1, 0x9b, 0x70, 0x15, 0x0e, 0xa5, + 0xd5, 0x19, 0x88, 0x73, 0xfa, 0x8d, 0xa4, 0xd0, 0xee, 0x5a, 0x5e, 0x6f, + 0x63, 0x1d, 0xe0, 0xa6, 0x84, 0x6d, 0xa4, 0xd8, 0xa6, 0xeb, 0xc5, 0xa7, + 0x6b, 0x73, 0xd0, 0x65, 0x94, 0xb3, 0x48, 0x8b, 0xd8, 0xf6, 0x15, 0xbe, + 0x1a, 0x14, 0x48, 0xdf, 0x7a, 0x6e, 0x4b, 0x1c, 0x09, 0x8e, 0x71, 0xf8, + 0x18, 0x04, 0x26, 0xfd, 0xd9, 0x76, 0xda, 0x13, 0xc6, 0x78, 0xd9, 0xe9, + 0x5a, 0xfe, 0x4c, 0x5f, 0xac, 0xbd, 0xe5, 0x49, 0xc7, 0xbc, 0x58, 0xb5, + 0xdd, 0xbc, 0x88, 0x89, 0x1b, 0xe1, 0x60, 0xcd, 0xf2, 0xb3, 0x97, 0x23, + 0xda, 0xc0, 0x5c, 0x3a, 0x68, 0x63, 0x5b, 0xb7, 0x55, 0xd4, 0xf4, 0x71, + 0x69, 0x5d, 0x99, 0xf9, 0x73, 0x5a, 0x47, 0x78, 0x38, 0xf0, 0x54, 0x19, + 0x26, 0x20, 0x28, 0x62, 0xa6, 0x02, 0xaf, 0x7b, 0x58, 0x30, 0x36, 0xbf, + 0x90, 0xdf, 0xe8, 0xb7, 0xf1, 0xd6, 0x80, 0x54, 0x39, 0xaa, 0xe4, 0xb4, + 0x26, 0x9d, 0xea, 0xba, 0x5c, 0x68, 0x85, 0xe8, 0x66, 0xd7, 0x34, 0x69, + 0xd8, 0x46, 0x8a, 0x4e, 0x46, 0xf8, 0x88, 0xbe, 0xe0, 0xd1, 0xfe, 0x20, + 0x76, 0xac, 0x51, 0x17, 0x30, 0x33, 0x35, 0x29, 0x54, 0x88, 0x52, 0xde, + 0xb6, 0x86, 0xc4, 0x92, 0x4e, 0x3c, 0xe8, 0x62, 0xc7, 0x20, 0xf0, 0x63, + 0xb9, 0xd3, 0xb0, 0x1a, 0x3e, 0xe7, 0x9c, 0x88, 0x5f, 0x0b, 0x53, 0x7f, + 0x56, 0x03, 0xc2, 0xbf, 0x43, 0xb8, 0x4e, 0x47, 0xb6, 0x52, 0x30, 0xfa, + 0x46, 0xb7, 0x2b, 0xf3, 0xc4, 0x8d, 0x6f, 0xa2, 0xc5, 0xec, 0xa1, 0x53, + 0x55, 0x3d, 0x61, 0xb9, 0xa9, 0x8a, 0xe5, 0x19, 0x31, 0x5b, 0xef, 0x39, + 0x84, 0x56, 0x1a, 0x7b, 0xd9, 0x25, 0xa3, 0xd6, 0x56, 0x88, 0x55, 0xe7, + 0x66, 0xf0, 0xb6, 0xcf, 0x27, 0x2d, 0x15, 0x3c, 0xe8, 0x1b, 0x2c, 0x46, + 0x73, 0xb5, 0x66, 0x8f, 0xff, 0x2f, 0x68, 0x7c, 0x86, 0x68, 0xae, 0x13, + 0x39, 0x0f, 0xcb, 0xa7, 0xd8, 0x93, 0xeb, 0xb2, 0x8f, 0x20, 0x6a, 0xda, + 0x1d, 0xdc, 0xdf, 0x5e, 0x4b, 0xce, 0x89, 0xad, 0xd6, 0xf4, 0xc7, 0xe0, + 0xe3, 0x30, 0xa9, 0xc3, 0xf7, 0x65, 0x92, 0x29, 0x11, 0xb6, 0x43, 0x5c, + 0x44, 0xb1, 0x36, 0xf4, 0xb6, 0xbd, 0x8f, 0x42, 0x9a, 0x4b, 0xb2, 0xc4, + 0xb6, 0x0f, 0x06, 0x5b, 0x70, 0x2d, 0x8a, 0x89, 0x13, 0xf9, 0xeb, 0xfe, + 0xb9, 0x33, 0x58, 0xe3, 0x3b, 0x19, 0x57, 0x4c, 0xb3, 0xea, 0x06, 0x1d, + 0xc0, 0xba, 0xe9, 0x98, 0xb4, 0x4b, 0x9c, 0xe2, 0x0f, 0xfd, 0xd2, 0x22, + 0x40, 0x0d, 0xf3, 0xf8, 0x68, 0xe1, 0xd3, 0x2b, 0x0f, 0xf7, 0xf1, 0x14, + 0x14, 0x7c, 0xbb, 0xc4, 0xad, 0xf0, 0xd5, 0xd7, 0x53, 0xaf, 0x3d, 0x0a, + 0x82, 0xa5, 0xa3, 0xbf, 0xd0, 0xdb, 0x63, 0x06, 0x6d, 0x05, 0x1d, 0x38, + 0xfc, 0x8e, 0x2e, 0x49, 0x44, 0x3a, 0x28, 0x82, 0xc7, 0x4c, 0x35, 0xe1, + 0x0f, 0x90, 0xa9, 0x41, 0x7d, 0x6e, 0x31, 0xbf, 0xc1, 0x51, 0x9b, 0xc0, + 0x2e, 0x00, 0x37, 0x9b, 0xe3, 0x6f, 0xa3, 0x29, 0x51, 0x63, 0x99, 0xd7, + 0x73, 0x12, 0x29, 0xde, 0x03, 0xde, 0x79, 0x1a, 0xd3, 0xa2, 0x39, 0xbd, + 0x03, 0xae, 0xaa, 0x39, 0x9b, 0xd7, 0xf1, 0x5b, 0x75, 0xc8, 0x6a, 0x3e, + 0xbb, 0xc1, 0xa8, 0x64, 0x46, 0x19, 0x79, 0x31, 0x3d, 0x1e, 0x36, 0xde, + 0x03, 0x95, 0x60, 0x73, 0xb4, 0x9c, 0x6e, 0xc3, 0x2f, 0x90, 0x4a, 0x7a, + 0x8b, 0xd1, 0x01, 0x66, 0xb6, 0x5a, 0x86, 0x7c, 0x35, 0xd7, 0x10, 0x99, + 0x42, 0xe7, 0x6f, 0xe6, 0xc8, 0x73, 0xb4, 0xbe, 0x0c, 0x27, 0x4f, 0xfe, + 0xe2, 0x72, 0xc0, 0x48, 0xdb, 0x9f, 0x78, 0xb5, 0x88, 0xa3, 0xa3, 0x17, + 0x5f, 0x4b, 0x7e, 0xc0, 0x3e, 0x38, 0x88, 0xad, 0x2b, 0xf1, 0xdb, 0x84, + 0x76, 0xd6, 0xb6, 0x87, 0xf9, 0x60, 0x7a, 0x15, 0x96, 0x5e, 0x29, 0xaf, + 0x66, 0x57, 0x04, 0x54, 0x1d, 0x74, 0xe4, 0x22, 0xbf, 0xc6, 0xc9, 0xfd, + 0x6d, 0x71, 0x8f, 0x28, 0x7b, 0x1d, 0x53, 0xc4, 0xe2, 0xbc, 0xc2, 0x26, + 0x29, 0x67, 0xe5, 0x4f, 0x4b, 0xe1, 0x16, 0x57, 0x21, 0x99, 0xe8, 0xa0, + 0xe2, 0xb2, 0x38, 0xbe, 0x11, 0xc5, 0xf8, 0xc3, 0xdc, 0x27, 0x83, 0x77, + 0xee, 0x39, 0x16, 0x00, 0x0b, 0x12, 0xe3, 0xe6, 0x6b, 0x4f, 0x99, 0xe2, + 0xd5, 0x2c, 0x7d, 0x42, 0xf4, 0xd4, 0x0b, 0x17, 0x42, 0x75, 0x3d, 0xf9, + 0xda, 0xe2, 0x36, 0xb5, 0xce, 0x04, 0x33, 0xb7, 0xc6, 0xd8, 0x01, 0xfa, + 0xf5, 0x2a, 0x5c, 0x41, 0xd0, 0x7c, 0x6c, 0x77, 0x95, 0x01, 0x3d, 0x34, + 0x9e, 0x3d, 0xe9, 0x0b, 0x37, 0xfe, 0xb7, 0xc9, 0x34, 0xb7, 0xe3, 0xc6, + 0xda, 0x89, 0x07, 0x29, 0xca, 0xba, 0x1d, 0xa3, 0x11, 0x9a, 0x47, 0x0e, + 0xe2, 0x6f, 0xd4, 0x24, 0x71, 0x87, 0x36, 0x83, 0x57, 0xdd, 0x78, 0xde, + 0xcb, 0xba, 0x24, 0xe3, 0x2e, 0xf7, 0x73, 0xd8, 0xf0, 0xeb, 0x52, 0x9c, + 0x65, 0x89, 0x87, 0xa8, 0x63, 0xf5, 0x9f, 0xb0, 0x7c, 0x17, 0xc1, 0xd2, + 0x47, 0x60, 0xbb, 0xdf, 0x9d, 0xff, 0x22, 0xa0, 0xf1, 0x41, 0x8c, 0x33, + 0x67, 0x3b, 0x41, 0x4b, 0x18, 0xce, 0x40, 0x7d, 0x85, 0xb2, 0x86, 0x46, + 0x3c, 0x8e, 0x7d, 0x7a, 0xc2, 0x96, 0xe6, 0xa7, 0x45, 0x87, 0xd6, 0x63, + 0x9a, 0x54, 0x2b, 0x31, 0x26, 0xfd, 0x9b, 0xf1, 0x4c, 0x2f, 0x43, 0xd0, + 0x5f, 0x68, 0x02, 0x7e, 0xa4, 0x92, 0x01, 0x79, 0xa5, 0xc9, 0xc7, 0xe2, + 0x6c, 0xf0, 0xf4, 0xe5, 0x75, 0xc4, 0xaa, 0xed, 0x56, 0x37, 0xd4, 0x76, + 0x55, 0x3c, 0xb8, 0x57, 0xb0, 0xdf, 0x8d, 0x30, 0xdc, 0x71, 0x4f, 0x5b, + 0xe9, 0xe2, 0xa8, 0x2c, 0xb7, 0x80, 0x1c, 0x50, 0x76, 0x61, 0x90, 0x74, + 0x4c, 0x1d, 0x7c, 0x39, 0x2f, 0x51, 0xb0, 0xc7, 0xfc, 0x09, 0xa9, 0xcf, + 0x80, 0xd6, 0xf1, 0x0b, 0xb3, 0x76, 0xf7, 0x57, 0xd3, 0x07, 0xd7, 0xe1, + 0x38, 0x76, 0x56, 0x8d, 0x31, 0xb9, 0x84, 0x5c, 0x18, 0xc5, 0xeb, 0x95, + 0x7c, 0xf5, 0xa6, 0xfe, 0x6c, 0x3b, 0x4c, 0x49, 0x82, 0xf0, 0xbd, 0x05, + 0x82, 0x7a, 0x0b, 0xc7, 0xb0, 0x9f, 0xcb, 0x53, 0xbf, 0xad, 0xb4, 0xc1, + 0x2c, 0xc8, 0x68, 0x9c, 0x66, 0x92, 0x41, 0xfa, 0x2d, 0x5f, 0xe8, 0xe4, + 0x6b, 0xce, 0xe3, 0xc3, 0xc8, 0x85, 0x38, 0x37, 0x47, 0x85, 0xd7, 0xa3, + 0x0a, 0x35, 0xd8, 0x68, 0x62, 0xcb, 0x3e, 0x66, 0xde, 0x10, 0x85, 0xfb, + 0xe7, 0x68, 0xd8, 0x32, 0x78, 0xce, 0x19, 0xb3, 0xe8, 0xde, 0x79, 0xf0, + 0xef, 0x27, 0x1e, 0x4a, 0xd1, 0xe2, 0x2e, 0xd0, 0x9f, 0x20, 0x81, 0x1f, + 0xcb, 0xf3, 0xf5, 0x19, 0xf5, 0x68, 0xc7, 0x8b, 0x16, 0xc6, 0xb5, 0x7e, + 0x0a, 0x89, 0x6e, 0xb9, 0xf8, 0xfc, 0x8c, 0x28, 0xf1, 0x5b, 0x38, 0x40, + 0x81, 0x0a, 0x87, 0x0b, 0x27, 0x75, 0x47, 0x67, 0xd1, 0x62, 0x4f, 0xff, + 0xda, 0xae, 0xec, 0xe3, 0xd0, 0xf2, 0x34, 0xdc, 0x77, 0xf4, 0xa4, 0xce, + 0xc9, 0x46, 0x66, 0xfe, 0x16, 0x6f, 0xbd, 0xc6, 0x4d, 0xb4, 0x90, 0x83, + 0x9a, 0x9c, 0x08, 0xa1, 0xf4, 0x9f, 0x3a, 0xee, 0xc1, 0xde, 0xf3, 0x23, + 0x77, 0xee, 0x42, 0x30, 0xd5, 0x8f, 0x5a, 0x53, 0x0a, 0x9a, 0xe6, 0xb6, + 0x1f, 0xe6, 0xa8, 0x34, 0x63, 0x5d, 0x8d, 0x87, 0x32, 0x72, 0x8a, 0x5b, + 0x72, 0x16, 0xc4, 0x1c, 0x5e, 0x99, 0x7c, 0xaa, 0xe6, 0x8e, 0x90, 0xc1, + 0x65, 0x9f, 0x49, 0x9c, 0x64, 0xdb, 0x2e, 0x61, 0x71, 0x0a, 0xd4, 0x47, + 0xdb, 0x2e, 0x4b, 0xb9, 0xd4, 0x02, 0xad, 0xef, 0xc4, 0xfb, 0xf2, 0x9d, + 0x23, 0xe9, 0x07, 0x33, 0x25, 0x7d, 0x8b, 0x3c, 0x0b, 0x3c, 0x36, 0xc0, + 0x31, 0xb9, 0x14, 0xec, 0x7d, 0xfe, 0x4e, 0x2a, 0xb3, 0x7b, 0x7c, 0x24, + 0x1e, 0xba, 0xc1, 0x5d, 0x9a, 0x3b, 0x29, 0x39, 0xd9, 0x9b, 0xea, 0x27, + 0x8f, 0xd2, 0xbc, 0xc0, 0x16, 0x93, 0x90, 0x5e, 0x20, 0x6e, 0x42, 0x6f, + 0x1b, 0x89, 0xe4, 0x2b, 0x1b, 0xe2, 0x6d, 0xe8, 0x82, 0xdb, 0x67, 0x03, + 0x5f, 0x5b, 0xb4, 0x51, 0xf5, 0xb5, 0x53, 0x1e, 0xaa, 0x29, 0x6e, 0x54, + 0xd1, 0xc0, 0xff, 0x28, 0x98, 0xbe, 0x9c, 0x9d, 0x35, 0x9c, 0x0e, 0xee, + 0x78, 0xdd, 0x19, 0x05, 0x30, 0x7d, 0x2c, 0x19, 0xf2, 0xb0, 0xb4, 0x56, + 0xab, 0x78, 0x09, 0x65, 0xb1, 0x01, 0x3a, 0xbd, 0x60, 0xd9, 0x0e, 0x5b, + 0x7d, 0x1a, 0xc0, 0x47, 0x7d, 0x12, 0x1b, 0xc0, 0x6f, 0x9f, 0xac, 0xb7, + 0xf2, 0x23, 0x4e, 0x15, 0x67, 0x70, 0x8a, 0xc5, 0xf4, 0x8f, 0x0e, 0x7f, + 0xe2, 0xc9, 0xa7, 0xbb, 0x2c, 0x89, 0x73, 0x00, 0xfc, 0xf2, 0x72, 0xd5, + 0xd6, 0x5f, 0x38, 0xbe, 0x4c, 0xed, 0x53, 0xfc, 0xa3, 0x0b, 0x09, 0x19, + 0x75, 0x01, 0x81, 0x89, 0xc9, 0x57, 0x46, 0x3c, 0x3e, 0xc6, 0x19, 0xd3, + 0x16, 0xcc, 0x75, 0x5f, 0x8a, 0xcb, 0xde, 0x74, 0xec, 0x1c, 0x3f, 0xb1, + 0xee, 0xc1, 0xe6, 0x56, 0x13, 0xce, 0x7d, 0xf6, 0x53, 0xbc, 0x59, 0x6c, + 0xb5, 0xd0, 0x88, 0x10, 0x6d, 0x72, 0xf9, 0xae, 0x16, 0x29, 0x04, 0x82, + 0xdf, 0x7f, 0x6d, 0x95, 0x2b, 0x86, 0x96, 0xba, 0xd1, 0x82, 0x2d, 0xdc, + 0xb9, 0x6a, 0xf8, 0xd6, 0x00, 0x97, 0x5c, 0x03, 0xe8, 0x6b, 0xd4, 0xf6, + 0x8f, 0x44, 0xe7, 0x96, 0xb4, 0xa0, 0x85, 0xa8, 0x30, 0x77, 0x37, 0x0d, + 0x4c, 0x1c, 0x1d, 0xe4, 0x81, 0x6e, 0x10, 0xd9, 0x8d, 0x84, 0xca, 0x97, + 0x7f, 0x53, 0xaf, 0x63, 0x14, 0xfd, 0x85, 0x53, 0x75, 0x35, 0x0b, 0xc5, + 0xfc, 0x53, 0xc7, 0x28, 0xc4, 0x4a, 0xe9, 0xaa, 0x8b, 0xe7, 0xc0, 0x64, + 0x2b, 0x7a, 0x57, 0x8c, 0xfd, 0x66, 0xdd, 0x9c, 0x72, 0x1f, 0x04, 0x47, + 0x27, 0xee, 0x75, 0x0a, 0x42, 0xc6, 0xf8, 0x57, 0x05, 0x77, 0x4b, 0x03, + 0x38, 0xcc, 0x7f, 0xfc, 0x6a, 0xad, 0xe6, 0xce, 0xf5, 0x7c, 0xc5, 0x02, + 0x99, 0x62, 0x3f, 0xfa, 0xb6, 0xba, 0xe1, 0x17, 0xe9, 0xd0, 0x4a, 0xff, + 0x4d, 0x39, 0x3a, 0x45, 0xd1, 0x3f, 0xf9, 0x5e, 0x79, 0xbb, 0xb0, 0xe7, + 0xed, 0x53, 0x22, 0xcb, 0x9a, 0x4d, 0xa7, 0x01, 0x3b, 0x50, 0xa6, 0x05, + 0xd4, 0xcf, 0x21, 0x9e, 0x9d, 0xa0, 0xc9, 0x08, 0x02, 0xd9, 0x8f, 0xbd, + 0x03, 0xed, 0x47, 0x3c, 0x71, 0x58, 0x1c, 0x03, 0x6a, 0xfe, 0x5c, 0x92, + 0x38, 0x01, 0xb7, 0xab, 0x74, 0x4b, 0x4d, 0x2f, 0xad, 0x66, 0xfc, 0x18, + 0xf0, 0xe6, 0x80, 0xb1, 0x59, 0x8f, 0xe7, 0xb2, 0x74, 0x99, 0x55, 0xfe, + 0x91, 0x45, 0xed, 0x32, 0x11, 0xa1, 0x97, 0x70, 0x50, 0xc3, 0x81, 0x31, + 0x70, 0xc0, 0x9e, 0xdc, 0x4c, 0x49, 0x0d, 0xdd, 0x99, 0x06, 0x2f, 0xa4, + 0x3d, 0x86, 0x07, 0x82, 0x3a, 0x61, 0x46, 0x06, 0x57, 0xeb, 0xde, 0xd4, + 0x15, 0x35, 0x19, 0x89, 0xd3, 0x97, 0xda, 0xce, 0x7d, 0xf0, 0xea, 0x79, + 0x7e, 0x09, 0x62, 0x00, 0x63, 0x32, 0x05, 0x42, 0x36, 0x36, 0x8d, 0x43, + 0x2e, 0xfb, 0xe7, 0xde, 0xd6, 0xcd, 0x73, 0xb8, 0xc8, 0x10, 0x2a, 0xe3, + 0x28, 0x7d, 0x74, 0x79, 0x04, 0x52, 0x70, 0x79, 0xa8, 0x25, 0x80, 0x04, + 0xcf, 0x93, 0x0f, 0x07, 0x0c, 0xbe, 0xda, 0x16, 0xb7, 0xf0, 0x1d, 0x4d, + 0x21, 0x0e, 0x63, 0xa7, 0x27, 0x9b, 0xe6, 0xb0, 0xcf, 0x11, 0x0f, 0x8e, + 0xfb, 0x0f, 0xb3, 0x60, 0x69, 0xa5, 0x8a, 0xc2, 0x57, 0xbf, 0x03, 0x5d, + 0x8e, 0x58, 0xbb, 0x47, 0xe7, 0xf3, 0xdd, 0x6e, 0x43, 0x1a, 0xe1, 0xdd, + 0x32, 0x96, 0x08, 0xba, 0xdb, 0x31, 0xf6, 0xd7, 0xcd, 0x28, 0xe3, 0x33, + 0x73, 0x15, 0x48, 0x9f, 0x69, 0x03, 0x35, 0x4f, 0xd8, 0xe4, 0xe0, 0x29, + 0x0c, 0xb0, 0x24, 0x7b, 0xbc, 0x62, 0xcd, 0xe3, 0x20, 0x03, 0x28, 0xd8, + 0xcd, 0x19, 0xee, 0xf3, 0xd9, 0x85, 0xd9, 0xf7, 0xe2, 0x5e, 0x5f, 0x27, + 0x7a, 0xac, 0xe6, 0x40, 0xcb, 0xaf, 0x3e, 0x3d, 0x7a, 0x51, 0x3e, 0xf6, + 0xf0, 0xfd, 0x7e, 0x9c, 0xf6, 0xa4, 0x2a, 0x9c, 0xa5, 0x87, 0x2e, 0xb1, + 0xb8, 0x1b, 0xc0, 0xfc, 0x74, 0x16, 0xc7, 0x00, 0x4d, 0x22, 0xba, 0xac, + 0x63, 0xea, 0xbd, 0x6c, 0xe8, 0xee, 0x3b, 0x0c, 0x0e, 0x71, 0x27, 0x9f, + 0xe3, 0xba, 0xf5, 0x80, 0x6c, 0x2e, 0x92, 0xe5, 0xc9, 0xe0, 0xe6, 0xce, + 0x89, 0x51, 0xad, 0xd6, 0x99, 0x71, 0xd9, 0x98, 0x4f, 0x34, 0x72, 0xb7, + 0xd5, 0x3e, 0xc9, 0x8b, 0xa1, 0x1c, 0xbe, 0xc8, 0x15, 0xa9, 0x4d, 0xe7, + 0x0c, 0x29, 0x82, 0xb3, 0x56, 0x47, 0x4b, 0x45, 0xa2, 0xa2, 0x6f, 0x0a, + 0xab, 0xae, 0x03, 0xa3, 0x54, 0x74, 0xb0, 0xd6, 0xcd, 0xce, 0xb0, 0xcd, + 0xb0, 0x7c, 0xfc, 0x77, 0xaf, 0xc7, 0x7c, 0xf8, 0xd3, 0xf5, 0xa7, 0x6e, + 0x04, 0x4c, 0x11, 0x4e, 0x1c, 0x12, 0x77, 0xdd, 0x28, 0xf4, 0x3f, 0xf2, + 0x9d, 0x06, 0xaa, 0x91, 0x9f, 0x4d, 0x31, 0xa2, 0xe1, 0x72, 0x50, 0x28, + 0x3c, 0x58, 0x9e, 0x34, 0x2a, 0x4c, 0x9d, 0xc0, 0x27, 0x55, 0x29, 0xa4, + 0xc7, 0xf4, 0x5e, 0x7b, 0x7c, 0x5d, 0x31, 0x02, 0xb8, 0xfb, 0x43, 0x75, + 0x68, 0xaa, 0xc5, 0x3d, 0x9f, 0x90, 0x92, 0x12, 0xb9, 0x2d, 0x5f, 0x6e, + 0xe5, 0xc6, 0x1a, 0x7a, 0x65, 0xd2, 0x5b, 0x23, 0x03, 0x94, 0xd6, 0xd4, + 0x22, 0xce, 0x80, 0x7e, 0xbd, 0x4f, 0x38, 0x87, 0xd4, 0xc4, 0xcf, 0x81, + 0xb8, 0x93, 0xb5, 0xfe, 0xdf, 0x0b, 0x71, 0x61, 0xa4, 0xe8, 0xcd, 0x18, + 0xa6, 0x21, 0x3f, 0x45, 0x22, 0xd6, 0x0c, 0xe1, 0x14, 0x74, 0xbb, 0x5e, + 0x2b, 0x09, 0x3d, 0x8d, 0x7b, 0x13, 0xdd, 0x58, 0xad, 0x5c, 0x94, 0x91, + 0x9c, 0x79, 0xc2, 0x84, 0x88, 0x21, 0x04, 0xd2, 0x1f, 0x85, 0xb5, 0xdf, + 0x84, 0xe0, 0xd3, 0x35, 0x93, 0x16, 0x16, 0x3a, 0xb0, 0x81, 0xab, 0x33, + 0x5e, 0x5c, 0xad, 0x54, 0x2a, 0x4d, 0xcc, 0xa8, 0x95, 0xf0, 0x61, 0x78, + 0x9b, 0x07, 0xb5, 0xc6, 0x6a, 0xdf, 0x0e, 0xa6, 0x61, 0xe7, 0x0b, 0x23, + 0x0d, 0x0f, 0xfb, 0xee, 0x0c, 0x8b, 0x12, 0xcc, 0x6a, 0x87, 0x36, 0xc7, + 0x3a, 0xe3, 0xda, 0xf0, 0xdc, 0x52, 0x4b, 0x11, 0x2e, 0x48, 0x08, 0x94, + 0x1b, 0xdd, 0x83, 0x65, 0x6a, 0xed, 0x6f, 0xbc, 0x0c, 0x48, 0x7f, 0xfa, + 0xd0, 0xe4, 0x9b, 0x38, 0xd9, 0x91, 0x30, 0x44, 0xa8, 0x91, 0x5e, 0x2d, + 0x16, 0xaf, 0xc7, 0xe2, 0xb7, 0x14, 0x1a, 0xdf, 0xf3, 0xf7, 0x18, 0xce, + 0x64, 0x51, 0x92, 0xd2, 0xc1, 0xa5, 0x05, 0x9f, 0x63, 0xe5, 0x0e, 0x20, + 0x7c, 0x64, 0xdb, 0x60, 0x76, 0x8d, 0x9d, 0xe0, 0x45, 0xfd, 0x26, 0xea, + 0x15, 0x0e, 0x92, 0x92, 0x9c, 0xed, 0x54, 0x65, 0x05, 0xaf, 0x0b, 0x3d, + 0xf7, 0xba, 0x3d, 0x02, 0xbe, 0x1d, 0x1f, 0x53, 0x11, 0xe6, 0x19, 0x96, + 0x89, 0xad, 0xc9, 0x04, 0x0e, 0x94, 0x05, 0xdc, 0x76, 0xa3, 0x6c, 0x5e, + 0xcc, 0x64, 0xf6, 0xb3, 0x10, 0xd1, 0xe7, 0xa5, 0xa1, 0xe3, 0xaa, 0xd8, + 0x19, 0x14, 0x30, 0x52, 0x3b, 0xcb, 0xc0, 0x54, 0x4f, 0xf9, 0x01, 0xaa, + 0x99, 0x41, 0x6d, 0xce, 0xaf, 0x1b, 0xf8, 0x59, 0x14, 0x1c, 0x9b, 0x9f, + 0x63, 0xba, 0x29, 0x81, 0x5b, 0x07, 0xb4, 0xdd, 0xa2, 0xd4, 0x02, 0xa6, + 0xbf, 0xe6, 0xed, 0x4b, 0xf9, 0x35, 0x71, 0xa2, 0xa4, 0x49, 0xc3, 0x2c, + 0x78, 0xab, 0xc4, 0x10, 0x54, 0x55, 0xcb, 0xd7, 0x7d, 0x83, 0x65, 0x0f, + 0xe6, 0xa5, 0x6f, 0xee, 0x15, 0x9e, 0x40, 0xcc, 0x8c, 0x5f, 0x8e, 0x4d, + 0x3c, 0x28, 0x13, 0x49, 0x28, 0x4c, 0xda, 0x20, 0xca, 0x1f, 0x1d, 0xd0, + 0x54, 0x49, 0xa1, 0x28, 0x72, 0x0e, 0xe0, 0xc4, 0x3c, 0x96, 0xbe, 0x05, + 0xa2, 0xc9, 0xfa, 0x84, 0x51, 0x70, 0x2d, 0x09, 0xc4, 0x2f, 0x70, 0x8d, + 0x11, 0x01, 0x58, 0xdf, 0x2b, 0x36, 0x92, 0x3d, 0x6f, 0x33, 0x42, 0x1b, + 0x87, 0x15, 0x00, 0x07, 0x22, 0xdf, 0xc4, 0x4f, 0x07, 0x48, 0x48, 0x18, + 0x67, 0x9c, 0xcf, 0x1f, 0x44, 0x99, 0x2a, 0x6a, 0x9d, 0x47, 0x77, 0x6a, + 0xad, 0x38, 0x7c, 0x9a, 0x95, 0x04, 0xfc, 0x42, 0xd3, 0xcf, 0x22, 0x1e, + 0x29, 0x58, 0x5f, 0x8d, 0x90, 0x0d, 0x0a, 0x27, 0x5f, 0xff, 0xcf, 0xa2, + 0xfa, 0x60, 0xc1, 0x36, 0x79, 0xd5, 0xd1, 0x15, 0x13, 0x9e, 0xdb, 0x53, + 0xbf, 0x89, 0xca, 0x8d, 0xc4, 0x0b, 0xa9, 0x40, 0xa7, 0xcb, 0x46, 0x21, + 0x95, 0x6b, 0xb4, 0xd1, 0x98, 0x97, 0x9e, 0xbb, 0x48, 0x83, 0xc1, 0x1e, + 0xa8, 0xc5, 0xce, 0x1b, 0xce, 0xc1, 0x51, 0xd5, 0xea, 0xc0, 0xa5, 0xd4, + 0x86, 0x74, 0x1b, 0x73, 0x34, 0x45, 0xa9, 0x24, 0xbd, 0xfc, 0x3b, 0xfb, + 0x55, 0x36, 0x9b, 0x72, 0xae, 0x78, 0x66, 0xb4, 0xb2, 0x5d, 0x70, 0xec, + 0x14, 0xdb, 0x23, 0x0a, 0x91, 0x8d, 0xa1, 0x6f, 0xcf, 0x3e, 0x49, 0x05, + 0xd2, 0xed, 0x4c, 0xdb, 0x51, 0x47, 0x2a, 0xf9, 0xc8, 0xcb, 0x1a, 0x1b, + 0x0f, 0x71, 0x8b, 0xaf, 0xe9, 0xb1, 0xb9, 0x71, 0x3b, 0xb8, 0x2e, 0x32, + 0xc5, 0xd4, 0xec, 0xcd, 0x79, 0x72, 0x2c, 0x9d, 0x1a, 0x8f, 0xdc, 0x04, + 0x79, 0x6e, 0xbb, 0x5a, 0x55, 0x20, 0x12, 0xda, 0x0d, 0x3c, 0xae, 0x42, + 0x21, 0xd8, 0x41, 0x80, 0xfc, 0x7a, 0x38, 0x74, 0x61, 0x7a, 0xac, 0xc3, + 0xe7, 0x82, 0xf5, 0xbb, 0xbe, 0xfa, 0xee, 0x97, 0x74, 0x94, 0xbe, 0x12, + 0xec, 0x85, 0x05, 0x50, 0x6d, 0xf1, 0x35, 0x22, 0x94, 0x76, 0x75, 0xfa, + 0x0a, 0x7a, 0xf0, 0x57, 0xee, 0x9b, 0x7f, 0xfc, 0xdd, 0x14, 0x54, 0x29, + 0x05, 0xb2, 0x58, 0x98, 0xda, 0xf9, 0x69, 0xab, 0x6b, 0xff, 0x01, 0x15, + 0x58, 0x9b, 0xcd, 0x60, 0xeb, 0x5c, 0x8d, 0x11, 0xc6, 0x34, 0x88, 0xfe, + 0xdd, 0xda, 0x8b, 0x91, 0x09, 0xd9, 0xb0, 0xad, 0x01, 0x11, 0x37, 0x14, + 0x65, 0x35, 0x74, 0xc0, 0x8e, 0x55, 0x5b, 0x7f, 0x15, 0x20, 0xeb, 0xeb, + 0x5b, 0xb7, 0x30, 0x4e, 0xf6, 0x1d, 0xdc, 0x74, 0x51, 0x14, 0xe3, 0x4a, + 0x20, 0x52, 0xd3, 0xba, 0xfe, 0x88, 0x08, 0x92, 0x77, 0x36, 0xf8, 0xe0, + 0x08, 0x99, 0x47, 0xc8, 0x58, 0xd2, 0xd6, 0xbc, 0x89, 0xe9, 0x62, 0xe9, + 0xca, 0x3a, 0x21, 0x52, 0x1c, 0xfa, 0x78, 0x75, 0xac, 0xb3, 0x76, 0xaa, + 0xe9, 0x03, 0x83, 0x66, 0x3a, 0xcf, 0x34, 0xeb, 0x2d, 0x3d, 0x66, 0xd7, + 0x4d, 0xff, 0x3f, 0xaf, 0x26, 0x7c, 0x1b, 0xfa, 0x71, 0xc4, 0xca, 0xc5, + 0x1f, 0xc1, 0x1c, 0xe2, 0xd4, 0x09, 0x5b, 0x57, 0xc4, 0x62, 0x8c, 0x8c, + 0x1e, 0xaf, 0xe8, 0x7d, 0x4b, 0xca, 0x1c, 0x89, 0xe3, 0x5d, 0xfd, 0x19, + 0x79, 0x1a, 0x0a, 0x41, 0x69, 0x4a, 0xc1, 0xbb, 0xb1, 0xf7, 0x18, 0x2c, + 0xa2, 0x8f, 0x82, 0xc4, 0x06, 0xa4, 0x95, 0xaf, 0xe8, 0x0e, 0x09, 0xa5, + 0xe6, 0xa9, 0x6e, 0x02, 0x3f, 0x86, 0xad, 0x98, 0x1f, 0xb4, 0xbb, 0x4f, + 0x19, 0x7b, 0x8e, 0x65, 0x0c, 0xbb, 0x01, 0x75, 0xa4, 0xcc, 0x0e, 0x59, + 0xef, 0xf6, 0xf2, 0x0b, 0x45, 0xa2, 0xdc, 0x5e, 0x2c, 0x29, 0xbb, 0x56, + 0x5a, 0xef, 0x6e, 0x42, 0x83, 0xa0, 0x38, 0x41, 0x41, 0x09, 0x13, 0xf9, + 0x77, 0x8f, 0x79, 0xb0, 0x7e, 0x0d, 0x9d, 0x24, 0xed, 0x5a, 0x19, 0x3d, + 0x58, 0xf7, 0x0d, 0x4a, 0x96, 0x6e, 0x98, 0x33, 0x62, 0xe9, 0x99, 0x29, + 0x35, 0xd0, 0x81, 0xb2, 0x21, 0x73, 0x5e, 0x54, 0x61, 0x29, 0xad, 0x12, + 0xd5, 0x82, 0xc8, 0x5e, 0xfc, 0x71, 0xf8, 0x22, 0x53, 0x70, 0x0f, 0xf1, + 0xa8, 0x3b, 0x15, 0xac, 0x22, 0xdf, 0xa9, 0x2c, 0x76, 0xf9, 0x7a, 0xd2, + 0x46, 0xcf, 0x31, 0x0e, 0x5c, 0x3f, 0xcd, 0x05, 0x92, 0x33, 0xb8, 0x5d, + 0xc0, 0x86, 0xa4, 0xbe, 0x35, 0x94, 0x09, 0x50, 0x77, 0x58, 0x28, 0x91, + 0x7d, 0x19, 0x04, 0x9c, 0xa4, 0xfa, 0xb6, 0x19, 0x9b, 0xa9, 0xb5, 0xe5, + 0xb6, 0x31, 0x10, 0xb3, 0x61, 0x93, 0x72, 0x6c, 0xe7, 0x68, 0xeb, 0x7f, + 0xed, 0xbe, 0x33, 0x93, 0xf7, 0x53, 0x8a, 0x74, 0x2e, 0x33, 0x31, 0x27, + 0x93, 0x97, 0xbb, 0xa4, 0x0e, 0x2b, 0x78, 0x79, 0x3c, 0x0a, 0xd3, 0x66, + 0xf7, 0x42, 0x9f, 0x42, 0x10, 0x96, 0x5c, 0x0c, 0x58, 0xf9, 0x43, 0xef, + 0x22, 0xd1, 0xde, 0xd5, 0x73, 0x14, 0xdc, 0x9c, 0xc5, 0xd7, 0xe3, 0x0c, + 0xa4, 0xba, 0x99, 0x9c, 0xd6, 0xec, 0xa4, 0x9b, 0xca, 0x3a, 0x79, 0xb9, + 0xa1, 0xdb, 0xb2, 0x5b, 0x7b, 0x71, 0xc6, 0x49, 0xd5, 0x05, 0x9d, 0x3f, + 0x1a, 0x8c, 0x6e, 0xb6, 0xbf, 0xc8, 0xf9, 0xde, 0x21, 0x79, 0xa7, 0x65, + 0xe2, 0x37, 0xb5, 0xb3, 0x9b, 0x61, 0x56, 0x8c, 0x1b, 0xc0, 0xfb, 0x6b, + 0x92, 0x6e, 0x79, 0x4c, 0x4a, 0x0a, 0xbb, 0x47, 0x4c, 0xa1, 0x77, 0xa5, + 0x13, 0x46, 0x37, 0xc9, 0x7d, 0x71, 0x0e, 0xbb, 0x5c, 0xf1, 0xfd, 0x43, + 0x48, 0x58, 0xb1, 0xf6, 0x67, 0x5c, 0x42, 0xd7, 0x85, 0x8d, 0x71, 0x41, + 0x1a, 0x68, 0x45, 0x18, 0x38, 0x5f, 0x5e, 0x63, 0x96, 0xfa, 0xa4, 0x55, + 0xca, 0xf0, 0x8a, 0x0c, 0x3c, 0x4d, 0x32, 0x76, 0x95, 0x4c, 0x70, 0xf0, + 0xbc, 0xce, 0x21, 0x00, 0x5f, 0xc1, 0xc8, 0x19, 0x97, 0x26, 0xb9, 0xaa, + 0x87, 0xf2, 0x4a, 0x7d, 0xbb, 0x4b, 0x76, 0xef, 0x63, 0x68, 0x54, 0xb6, + 0xde, 0x82, 0x50, 0xa7, 0x08, 0x53, 0x0b, 0x64, 0x31, 0x5a, 0x89, 0xbf, + 0x03, 0xe7, 0x86, 0x9e, 0x22, 0xfe, 0x0c, 0x89, 0xf5, 0x36, 0x2a, 0xfd, + 0x91, 0x2c, 0x29, 0x86, 0xaf, 0x3f, 0xa5, 0x48, 0xa7, 0xec, 0xbf, 0xfc, + 0x52, 0xc8, 0x61, 0xe0, 0xc2, 0x59, 0x3e, 0x35, 0x93, 0x5c, 0xd2, 0xdd, + 0x39, 0x39, 0xf1, 0x16, 0x71, 0x3e, 0x5c, 0xec, 0x9c, 0x2c, 0x95, 0xc4, + 0x50, 0xa1, 0xba, 0x1d, 0x9b, 0x01, 0x79, 0xbd, 0xdb, 0x43, 0x70, 0xcf, + 0xe0, 0x73, 0x03, 0x59, 0x38, 0x33, 0x7e, 0xb3, 0xc0, 0xc9, 0xaf, 0x66, + 0x9f, 0x46, 0x41, 0x34, 0x31, 0x0c, 0xab, 0xdf, 0x77, 0x5e, 0xe4, 0x41, + 0xfc, 0xa9, 0x4f, 0xbc, 0x27, 0x2b, 0x81, 0xbe, 0xad, 0xca, 0xb4, 0x81, + 0x5a, 0x4a, 0x1a, 0xc5, 0xf8, 0xe3, 0x96, 0xe4, 0x7d, 0xe2, 0x01, 0x54, + 0x88, 0x22, 0x96, 0x44, 0x55, 0x74, 0x01, 0x6a, 0xfb, 0xb0, 0x23, 0xc2, + 0x0f, 0x89, 0x95, 0xdf, 0xbf, 0x1d, 0xfa, 0x40, 0xf8, 0xad, 0xef, 0xe4, + 0xe6, 0x91, 0xe3, 0xbe, 0x92, 0xa1, 0xd9, 0x4c, 0x27, 0x60, 0x12, 0x90, + 0x6d, 0xbd, 0xf8, 0xba, 0x82, 0x55, 0x82, 0x66, 0x48, 0x9e, 0xc0, 0x96, + 0xdb, 0xd4, 0xb7, 0xe9, 0xb5, 0xe6, 0x9f, 0x7d, 0x60, 0x93, 0x98, 0x38, + 0x8f, 0x08, 0x82, 0xd1, 0xad, 0xea, 0x5b, 0xb2, 0xa3, 0xd1, 0x8b, 0x16, + 0x58, 0xa7, 0x46, 0xaf, 0x57, 0x80, 0xf4, 0x6c, 0x8d, 0x76, 0x25, 0xa3, + 0x37, 0xfc, 0xf4, 0x6d, 0x92, 0x49, 0xc0, 0x80, 0x98, 0xfd, 0x7e, 0x26, + 0x33, 0x7d, 0xee, 0x26, 0xa8, 0x6d, 0x18, 0x2c, 0xac, 0xdf, 0x28, 0xee, + 0x71, 0x26, 0x5f, 0xe9, 0x3a, 0x0c, 0xec, 0xad, 0x1c, 0x37, 0xf5, 0x71, + 0xf5, 0xb3, 0xb0, 0x70, 0xe2, 0xbb, 0x0f, 0xcb, 0x97, 0x40, 0x5d, 0x63, + 0x46, 0x28, 0x04, 0x61, 0x35, 0x4b, 0x23, 0x06, 0x6b, 0x69, 0xbc, 0x28, + 0xa3, 0xe7, 0xf1, 0x6c, 0x00, 0x0a, 0x0c, 0xb5, 0x31, 0xb2, 0x8e, 0x3b, + 0xfe, 0xe7, 0xcc, 0x26, 0x60, 0x5c, 0x45, 0xad, 0xd8, 0x25, 0x2e, 0xf8, + 0x7c, 0xb5, 0x19, 0x49, 0x42, 0xc4, 0x12, 0x14, 0xd4, 0xe2, 0x82, 0xa4, + 0xb4, 0x26, 0x67, 0x34, 0x1d, 0x8c, 0x8e, 0xab, 0xab, 0xe5, 0xc7, 0x8e, + 0xbe, 0x11, 0x73, 0x84, 0xff, 0x07, 0x72, 0x5b, 0xfe, 0x33, 0x6a, 0xf6, + 0xd1, 0x4a, 0xf1, 0xb6, 0x08, 0xe1, 0x3d, 0xf7, 0x26, 0x49, 0x46, 0x8d, + 0xf7, 0x71, 0xdc, 0x33, 0x04, 0x90, 0x30, 0x7c, 0x83, 0xf5, 0xe3, 0xca, + 0xac, 0x8e, 0x03, 0x7a, 0x4b, 0xe1, 0xf8, 0x70, 0xbd, 0x33, 0x42, 0xc1, + 0x9d, 0x6a, 0x66, 0xe4, 0xcf, 0x9d, 0xfa, 0xc9, 0xd7, 0x5f, 0xce, 0xba, + 0x7d, 0xa1, 0x90, 0x04, 0x2d, 0xbb, 0xaf, 0x65, 0x47, 0x6d, 0x54, 0x21, + 0xad, 0xb4, 0x98, 0x5b, 0x39, 0xb1, 0x92, 0x9f, 0x1b, 0x1d, 0x2d, 0x3a, + 0xad, 0x5a, 0x41, 0x91, 0x3f, 0x69, 0xd3, 0xf5, 0x7e, 0x41, 0xb5, 0xfb, + 0x23, 0xfb, 0xfd, 0x0c, 0x6b, 0x05, 0x43, 0xbd, 0x77, 0xf7, 0x2a, 0x71, + 0x7e, 0x7e, 0x45, 0x02, 0xc0, 0x68, 0x50, 0xc6, 0x93, 0xfe, 0xde, 0x29, + 0x62, 0x37, 0xf0, 0x3d, 0xab, 0x65, 0x7e, 0xae, 0x5f, 0x89, 0x03, 0x2a, + 0xb8, 0x88, 0x36, 0xcf, 0x3a, 0xe4, 0xb0, 0xd9, 0x44, 0x4f, 0xf7, 0xc1, + 0x65, 0xe6, 0x68, 0x08, 0x1b, 0x22, 0xa2, 0xc3, 0x3f, 0x72, 0x20, 0x83, + 0x57, 0x61, 0xe4, 0x4f, 0x1d, 0x0f, 0x2f, 0x89, 0xe2, 0x8b, 0x83, 0x6b, + 0x9f, 0x03, 0xfc, 0xc8, 0xcd, 0x89, 0x18, 0x0e, 0xa8, 0x75, 0x5a, 0x37, + 0x2c, 0x7c, 0x1f, 0xc1, 0xea, 0xbe, 0x10, 0xad, 0x28, 0x00, 0xf6, 0x10, + 0x5d, 0x50, 0xcb, 0x9f, 0x44, 0xae, 0xef, 0xe9, 0xc1, 0xfc, 0x7d, 0xf1, + 0x59, 0x7c, 0x20, 0xde, 0x08, 0x32, 0xbd, 0x52, 0x00, 0xe7, 0x0e, 0x66, + 0x35, 0xff, 0x3b, 0xd2, 0x57, 0x09, 0xbb, 0x79, 0x4c, 0x37, 0xcc, 0x27, + 0xb9, 0xdb, 0xe7, 0x0d, 0xe5, 0x38, 0x4f, 0x60, 0xfe, 0x18, 0x6f, 0x17, + 0x97, 0x64, 0x58, 0x6a, 0xc3, 0x7c, 0x7a, 0x81, 0x30, 0x67, 0xa5, 0xc1, + 0xfb, 0x4d, 0x07, 0xa2, 0x33, 0xd0, 0x5d, 0x4d, 0x6a, 0x18, 0xdf, 0x35, + 0x6a, 0x32, 0x29, 0xe7, 0x57, 0x46, 0x52, 0x3e, 0x0f, 0xc1, 0x5b, 0xe1, + 0x9f, 0x83, 0x75, 0x56, 0x78, 0x25, 0x4f, 0xba, 0xb5, 0xf1, 0xde, 0xf9, + 0xeb, 0x37, 0x61, 0x73, 0xba, 0xec, 0x47, 0xb4, 0x04, 0xb1, 0x20, 0xb3, + 0xc1, 0x54, 0x4d, 0x71, 0x89, 0xc4, 0x0d, 0xcc, 0xb9, 0x69, 0xe7, 0x96, + 0x3d, 0x42, 0xea, 0x04, 0x8a, 0xf1, 0xf5, 0x88, 0xa4, 0xed, 0xbc, 0x17, + 0x4e, 0x8d, 0x1e, 0x2c, 0xb1, 0x32, 0x61, 0x80, 0xfd, 0xa2, 0x04, 0x59, + 0xf9, 0xbd, 0x74, 0x36, 0x3a, 0x8a, 0x08, 0x62, 0xc2, 0x62, 0xa2, 0xad, + 0x57, 0xbf, 0x5b, 0x44, 0xda, 0xd3, 0xcd, 0x3c, 0xa3, 0xab, 0xec, 0x88, + 0xa3, 0xac, 0xae, 0xb3, 0xa8, 0x98, 0x1d, 0x47, 0x80, 0x05, 0xf3, 0x4d, + 0x0d, 0xa6, 0xef, 0x15, 0x14, 0x84, 0x19, 0x23, 0xdd, 0x1c, 0x30, 0x6f, + 0x6e, 0xc5, 0xe3, 0xac, 0xb3, 0xb7, 0xef, 0xbe, 0x35, 0x54, 0xea, 0x6e, + 0xab, 0x46, 0xde, 0x50, 0xb2, 0x04, 0xf9, 0x8c, 0xab, 0x92, 0x3e, 0x38, + 0x9b, 0x68, 0xd9, 0xe0, 0x75, 0x2c, 0xd4, 0xf9, 0xa4, 0x17, 0xf9, 0x09, + 0x62, 0x6b, 0x7c, 0x22, 0x3c, 0x28, 0x38, 0xcf, 0x58, 0xfc, 0x54, 0x85, + 0x51, 0xf6, 0x89, 0x72, 0x2f, 0x91, 0x89, 0xa7, 0xa9, 0x6f, 0x54, 0x7e, + 0xc9, 0x89, 0x57, 0xfc, 0xff, 0x85, 0x74, 0x36, 0x76, 0x72, 0xeb, 0xb4, + 0x71, 0x81, 0x4c, 0xe5, 0xaa, 0xca, 0xa1, 0x9a, 0x43, 0x9d, 0xfb, 0xd6, + 0xd3, 0x20, 0xd1, 0xd0, 0x2c, 0x40, 0xed, 0x44, 0x73, 0x76, 0x6c, 0x10, + 0xc7, 0x3c, 0xf5, 0xb0, 0xb4, 0x26, 0x1b, 0x62, 0x1d, 0xed, 0xfb, 0xcc, + 0x67, 0x79, 0x63, 0xed, 0xb0, 0xb2, 0x9f, 0xea, 0xcb, 0xa5, 0x81, 0xd0, + 0x3a, 0x7d, 0x99, 0xda, 0xb3, 0x6d, 0x2d, 0xe9, 0x32, 0x0f, 0x71, 0xd8, + 0x9e, 0x06, 0xfd, 0xd3, 0x6d, 0x92, 0xaa, 0x7f, 0xfa, 0x00, 0x2c, 0xef, + 0xea, 0x81, 0x2c, 0x4c, 0x1e, 0x67, 0xe9, 0x9c, 0xc0, 0x71, 0x98, 0xe9, + 0x74, 0x5f, 0xdd, 0x7a, 0x5e, 0x44, 0x47, 0xe4, 0x7f, 0xcc, 0x57, 0x97, + 0x2b, 0x49, 0xda, 0x92, 0xc3, 0x1b, 0x0e, 0x01, 0x53, 0x12, 0xbe, 0xb6, + 0x63, 0x82, 0xec, 0xe1, 0xb6, 0xe7, 0x64, 0x59, 0xe6, 0xa5, 0xa0, 0xeb, + 0xe6, 0xd8, 0x11, 0x30, 0xf1, 0xae, 0x0d, 0xc0, 0x53, 0x48, 0x5c, 0x31, + 0x83, 0xc4, 0x33, 0x9e, 0xf4, 0x3a, 0x24, 0xb2, 0xa2, 0x3f, 0x8d, 0x29, + 0x9f, 0x49, 0x06, 0x03, 0x7e, 0xcd, 0x33, 0xca, 0x2e, 0xa5, 0x2e, 0xb0, + 0x0f, 0x06, 0xf4, 0x52, 0xc3, 0xe3, 0x85, 0x28, 0x73, 0xb4, 0x19, 0xf6, + 0xfa, 0xe8, 0x83, 0xcd, 0xb7, 0xf6, 0x9a, 0x71, 0x0e, 0x99, 0x45, 0x51, + 0x7e, 0x46, 0x3a, 0xdb, 0xb0, 0x6c, 0xf1, 0x75, 0x46, 0xbf, 0x2b, 0x40, + 0x2c, 0x47, 0x03, 0xf5, 0x50, 0xe6, 0xed, 0xbc, 0x3d, 0x15, 0x1d, 0x00, + 0xe4, 0x5b, 0xb8, 0xd1, 0x76, 0xfa, 0xd0, 0x04, 0x25, 0x6f, 0x4b, 0x33, + 0x4b, 0x40, 0x98, 0x0a, 0xc8, 0x7e, 0x9a, 0xf0, 0x0f, 0x98, 0xb6, 0x0a, + 0x61, 0x07, 0x5a, 0xb2, 0xb9, 0x17, 0x54, 0x83, 0x23, 0x14, 0xb9, 0xe8, + 0x25, 0x55, 0xbb, 0x20, 0x13, 0x85, 0xd2, 0xbb, 0x9d, 0x06, 0x18, 0x39, + 0xe0, 0xe6, 0x24, 0x2a, 0x45, 0xc0, 0x08, 0x9e, 0x8f, 0xfd, 0xac, 0xf8, + 0xc5, 0xae, 0xdc, 0xe3, 0x0a, 0xb4, 0xe0, 0x38, 0x38, 0x53, 0xf8, 0x5b, + 0x3a, 0x71, 0x9b, 0x0c, 0x03, 0x5b, 0x0b, 0xe5, 0xab, 0x00, 0x8e, 0xec, + 0x75, 0xf4, 0x83, 0x7a, 0x1b, 0x0a, 0x15, 0x1e, 0xaf, 0xbb, 0x1d, 0x5a, + 0x07, 0x43, 0xe2, 0x9f, 0xd7, 0x87, 0x25, 0x88, 0x5d, 0x3f, 0x94, 0x68, + 0x49, 0x02, 0x3e, 0x4e, 0x8c, 0xc2, 0xbc, 0x37, 0x7f, 0xfd, 0x30, 0x59, + 0x47, 0x41, 0xfc, 0xa6, 0xc9, 0x01, 0x97, 0x6c, 0x72, 0x7d, 0x9c, 0x92, + 0x85, 0x4c, 0x7f, 0x86, 0x04, 0x73, 0x04, 0xa2, 0x70, 0xaf, 0x9f, 0x5a, + 0x5a, 0x07, 0x8e, 0xa8, 0x5b, 0x24, 0x38, 0xbe, 0x2e, 0x4c, 0x39, 0xfb, + 0x1a, 0x2f, 0x41, 0x13, 0xcc, 0x06, 0x7c, 0x92, 0x1f, 0x9c, 0x47, 0x8b, + 0x9f, 0xf3, 0xce, 0xe0, 0xa9, 0xb3, 0x3e, 0xa9, 0x97, 0xd7, 0x1f, 0x4d, + 0x2f, 0x66, 0xc8, 0xf1, 0x36, 0xe9, 0x11, 0xd5, 0x4e, 0x72, 0x9a, 0x85, + 0x7d, 0x62, 0xf3, 0x0c, 0x0b, 0xe4, 0xc7, 0x48, 0x6d, 0x7a, 0x7e, 0x2a, + 0xc6, 0x7d, 0x25, 0x58, 0xac, 0x57, 0xdf, 0x0d, 0x0a, 0x0d, 0x9e, 0x27, + 0xd1, 0xe9, 0x9f, 0x9d, 0x92, 0x31, 0xb7, 0x3d, 0x04, 0x1b, 0xbf, 0xb1, + 0x1c, 0x4b, 0x84, 0x4d, 0x09, 0x24, 0xad, 0x8d, 0x4e, 0xe0, 0xb4, 0xc7, + 0x98, 0x26, 0xc3, 0xfc, 0xc4, 0x61, 0x2f, 0xc8, 0x26, 0x24, 0x88, 0x98, + 0x96, 0x7f, 0x44, 0x73, 0xb9, 0x92, 0xa4, 0xee, 0x8d, 0x3a, 0xf3, 0x67, + 0xce, 0xd0, 0xad, 0xac, 0x7f, 0x47, 0xbd, 0x44, 0x2a, 0xc2, 0xed, 0x82, + 0xd2, 0x95, 0xaa, 0x8e, 0xd8, 0x75, 0xd7, 0xfe, 0x28, 0x40, 0x22, 0x0f, + 0x05, 0x91, 0xdf, 0xd1, 0x6b, 0x8d, 0x87, 0x06, 0x7d, 0x3b, 0x4b, 0x59, + 0x10, 0x46, 0x0d, 0x99, 0xe7, 0x36, 0xff, 0x99, 0xe7, 0xb0, 0x56, 0x92, + 0x97, 0xb4, 0xf3, 0xe3, 0x09, 0x7a, 0x4b, 0x08, 0xff, 0xf5, 0xbe, 0xf0, + 0xf1, 0xbd, 0x00, 0x02, 0xb3, 0x51, 0x9d, 0x8c, 0xfd, 0x23, 0x4d, 0x52, + 0x16, 0x6e, 0xf4, 0x82, 0x65, 0x55, 0xcb, 0x5e, 0x47, 0x5f, 0x6f, 0x57, + 0xef, 0x7d, 0x59, 0xef, 0x64, 0xae, 0xbc, 0x08, 0x8f, 0x19, 0x14, 0xec, + 0x92, 0x50, 0x0a, 0xbe, 0x13, 0x13, 0x59, 0x5f, 0x62, 0x4a, 0x54, 0xde, + 0x73, 0x3b, 0xbe, 0x20, 0x9f, 0x35, 0x22, 0xdb, 0xf8, 0x4f, 0xe4, 0x66, + 0x9f, 0x31, 0xb4, 0x81, 0x2f, 0x19, 0x21, 0x33, 0xa6, 0x7f, 0x37, 0x68, + 0xe0, 0x61, 0xb8, 0x9f, 0x00, 0x3e, 0x8a, 0x1d, 0xdc, 0x34, 0xda, 0x48, + 0xd8, 0x4a, 0x3f, 0xfb, 0xfd, 0x5f, 0x05, 0xf8, 0xce, 0x7a, 0xa9, 0x32, + 0xd8, 0xb2, 0xee, 0x73, 0x0d, 0x9f, 0x69, 0x41, 0xe0, 0x9d, 0x36, 0xac, + 0x34, 0x9f, 0x8c, 0x72, 0xf2, 0x5b, 0xec, 0x48, 0x77, 0xf0, 0x9f, 0xa8, + 0x34, 0x46, 0x25, 0x70, 0xd7, 0xfe, 0x01, 0x1a, 0xc9, 0x5f, 0x98, 0x9c, + 0x82, 0xbb, 0x78, 0x4d, 0x47, 0x9f, 0xf3, 0xd0, 0xb0, 0x3c, 0xb7, 0x82, + 0xae, 0x05, 0xc5, 0x2c, 0x69, 0xb8, 0x09, 0xc7, 0x49, 0x57, 0xe5, 0x31, + 0x8e, 0x1e, 0x94, 0x59, 0x99, 0x9f, 0x38, 0x5c, 0xee, 0xe3, 0x12, 0x0f, + 0x83, 0x3d, 0x1e, 0xe5, 0xf5, 0x50, 0xec, 0xd9, 0x61, 0xa1, 0xd2, 0xd4, + 0x15, 0x86, 0xee, 0x88, 0xb7, 0x59, 0xc5, 0x97, 0xbd, 0xcb, 0xda, 0xf7, + 0x5a, 0xde, 0x26, 0x83, 0xa5, 0x3c, 0xea, 0x57, 0xbf, 0xdb, 0x06, 0xb1, + 0xf6, 0x2f, 0xb5, 0x9e, 0xce, 0x8d, 0xdf, 0x08, 0x68, 0x47, 0x46, 0xfa, + 0x58, 0x63, 0xd4, 0xb2, 0xc0, 0xe1, 0x95, 0x2e, 0xd2, 0x73, 0xc5, 0x4f, + 0x5d, 0x02, 0xe9, 0xfc, 0xec, 0x59, 0x2b, 0x36, 0xa9, 0x78, 0x9f, 0x0a, + 0x00, 0xa3, 0xe4, 0xab, 0x4d, 0x1d, 0x14, 0xf3, 0xd5, 0x5f, 0xda, 0xa3, + 0x11, 0xfd, 0x30, 0xc5, 0x15, 0x70, 0x73, 0xe6, 0xd9, 0x42, 0x5b, 0x2e, + 0xd7, 0xf9, 0x2b, 0x74, 0xf4, 0x53, 0xbf, 0xe2, 0x8d, 0xb8, 0x25, 0x3e, + 0xce, 0xc0, 0xf9, 0x77, 0x60, 0xb9, 0x57, 0xe6, 0x23, 0xf5, 0xf5, 0x26, + 0xb1, 0x97, 0x32, 0x4b, 0xe5, 0x65, 0xa3, 0xe4, 0x79, 0x81, 0x72, 0xb8, + 0x34, 0x2d, 0xb2, 0xb1, 0x42, 0x28, 0x62, 0xc7, 0x21, 0x9b, 0x71, 0x76, + 0x74, 0xbf, 0x43, 0x42, 0x70, 0x99, 0xbb, 0x0c, 0x93, 0x0a, 0xde, 0xce, + 0x8a, 0x2c, 0x4c, 0x2c, 0xcd, 0x13, 0x5e, 0xba, 0x07, 0xe0, 0x04, 0xd4, + 0x5a, 0xa4, 0x4f, 0x81, 0x04, 0x8b, 0xa3, 0x5b, 0x05, 0x1d, 0xbc, 0xc0, + 0xad, 0xc0, 0x48, 0x91, 0xaa, 0x6f, 0x36, 0x1a, 0xc2, 0xbe, 0x80, 0x19, + 0x98, 0x28, 0xf9, 0x64, 0x72, 0x5b, 0xbf, 0x2c, 0x77, 0xad, 0x48, 0x23, + 0x5f, 0x1c, 0xb9, 0x35, 0xab, 0x22, 0xd9, 0x9f, 0x9d, 0x34, 0xca, 0x06, + 0xa3, 0x20, 0xb4, 0xe4, 0x38, 0x7c, 0xd4, 0xdd, 0xa0, 0x59, 0xae, 0x48, + 0x51, 0xde, 0xcc, 0xb6, 0xa1, 0x07, 0x66, 0xd6, 0xbc, 0xee, 0x71, 0x38, + 0x97, 0xf8, 0x16, 0x47, 0xa4, 0x51, 0x16, 0x77, 0xf9, 0x38, 0x5d, 0x51, + 0x62, 0x83, 0x01, 0x40, 0x4b, 0xb1, 0x56, 0xc7, 0x80, 0xa7, 0x2f, 0x34, + 0x77, 0xa8, 0xaa, 0x7b, 0xc3, 0x5a, 0x9d, 0xaf, 0x87, 0x38, 0x8a, 0x37, + 0x63, 0x73, 0x2d, 0x09, 0x67, 0xe9, 0x69, 0xda, 0xf7, 0x31, 0x27, 0x43, + 0x62, 0x8e, 0xc6, 0xb8, 0x7f, 0xfe, 0x61, 0x1f, 0x34, 0x9b, 0xff, 0x53, + 0x24, 0xb5, 0x8c, 0x6d, 0x8d, 0x9a, 0x4c, 0x20, 0xc6, 0x29, 0x97, 0xb3, + 0xea, 0xb7, 0x5a, 0xb0, 0x88, 0x4a, 0xe0, 0x44, 0xb0, 0x22, 0x34, 0xe5, + 0xce, 0x9b, 0xa2, 0x71, 0x61, 0xdb, 0x3e, 0x50, 0xfa, 0x8c, 0x56, 0xff, + 0x8a, 0x4c, 0x17, 0x0d, 0x25, 0xe1, 0x95, 0xce, 0x10, 0x0c, 0x53, 0x1d, + 0x3a, 0xd4, 0x4a, 0xe6, 0xbe, 0x1d, 0xfe, 0x01, 0x20, 0xb2, 0xab, 0x10, + 0x4c, 0x77, 0xbf, 0xb8, 0x20, 0x82, 0xe2, 0x9c, 0x81, 0x2a, 0xa3, 0x59, + 0xde, 0x71, 0x23, 0xc5, 0x3e, 0x57, 0x80, 0xd3, 0x60, 0x87, 0x7c, 0x24, + 0x6a, 0x96, 0x85, 0x28, 0xd3, 0x86, 0x02, 0xd7, 0x69, 0x86, 0xf0, 0xb3, + 0xee, 0x2f, 0x19, 0x26, 0x7c, 0x89, 0xbd, 0xa9, 0x1a, 0x83, 0xfa, 0x33, + 0xda, 0xce, 0xd4, 0x87, 0x34, 0x7e, 0x1c, 0x9c, 0x08, 0x64, 0xfd, 0xc0, + 0x54, 0x90, 0x19, 0xd4, 0xd1, 0x65, 0xea, 0x3f, 0xad, 0xa6, 0xf8, 0xe3, + 0x35, 0xd9, 0x74, 0x5b, 0xcf, 0x0f, 0x3b, 0x41, 0x7d, 0x6e, 0x55, 0xee, + 0x63, 0x4d, 0xc3, 0xc0, 0xa6, 0xb7, 0x64, 0x76, 0xc8, 0x9a, 0xa5, 0x58, + 0xf1, 0x8a, 0x1c, 0xa2, 0xfb, 0xc3, 0x6a, 0xef, 0x04, 0xf0, 0xc0, 0x83, + 0xfc, 0x21, 0x54, 0x83, 0xf4, 0x71, 0x21, 0x94, 0x47, 0x8a, 0x87, 0x95, + 0x8d, 0x82, 0x5f, 0xab, 0x08, 0x09, 0x7e, 0xfa, 0xa6, 0x2e, 0x35, 0x5c, + 0x6b, 0x59, 0x77, 0xdf, 0xa6, 0x28, 0xe6, 0x45, 0xc1, 0xaf, 0x18, 0xa2, + 0x73, 0x6b, 0xc9, 0x44, 0x5b, 0x79, 0x40, 0x52, 0x18, 0x2d, 0x04, 0x85, + 0xca, 0x26, 0x7f, 0x61, 0x17, 0x29, 0xde, 0x8a, 0xe6, 0xd5, 0x35, 0xac, + 0xf8, 0x50, 0x62, 0x77, 0xd5, 0xb4, 0x1d, 0xfb, 0xa0, 0x9e, 0xc6, 0x1e, + 0x04, 0x49, 0x3f, 0x4c, 0x5e, 0xed, 0x03, 0xfd, 0xc6, 0xde, 0xc0, 0xfa, + 0x9c, 0x8d, 0x83, 0x50, 0xfc, 0x83, 0xc8, 0x5d, 0x32, 0x57, 0xce, 0x9d, + 0x14, 0x68, 0x19, 0x08, 0x8b, 0x3a, 0x0e, 0xd9, 0x33, 0x3e, 0x89, 0x34, + 0x7b, 0x71, 0xd9, 0xa2, 0x53, 0x2d, 0x08, 0x23, 0x48, 0xc5, 0x7f, 0xc7, + 0x0b, 0xc8, 0x99, 0x87, 0x80, 0xa2, 0xca, 0xe5, 0xb8, 0xb8, 0x99, 0x82, + 0x0e, 0xae, 0x4f, 0x0b, 0xd9, 0x80, 0xab, 0x0a, 0xbe, 0xd1, 0x42, 0xe7, + 0x10, 0xf6, 0x3d, 0x9a, 0xc1, 0x71, 0x31, 0xcb, 0xb9, 0x93, 0x74, 0xe5, + 0x9e, 0xfb, 0xfa, 0xbe, 0x9c, 0xc9, 0x27, 0xb3, 0xe1, 0xc4, 0xa1, 0xfe, + 0xb5, 0x35, 0x9e, 0xeb, 0xbc, 0x1a, 0x2b, 0xae, 0x12, 0xa3, 0x5e, 0x95, + 0xce, 0x8c, 0x53, 0xbb, 0x71, 0x7e, 0x95, 0x3a, 0x68, 0x43, 0x16, 0x5d, + 0x5d, 0xd2, 0x48, 0x04, 0xc2, 0x5b, 0x04, 0xb8, 0x0f, 0x2a, 0x85, 0x5e, + 0x93, 0x64, 0x55, 0xf5, 0x86, 0x68, 0x1f, 0xa0, 0x38, 0x43, 0x03, 0x5e, + 0x4b, 0x86, 0x51, 0xfd, 0x83, 0x8e, 0xbb, 0x0d, 0x15, 0xd0, 0xfd, 0x0c, + 0x04, 0x3b, 0x85, 0xe0, 0xd3, 0x4c, 0x81, 0x39, 0x4d, 0x91, 0x5e, 0xb2, + 0x3f, 0x29, 0x30, 0xe2, 0x22, 0xb8, 0xc8, 0xf6, 0x15, 0xe1, 0x74, 0xdb, + 0x2f, 0x84, 0x5c, 0xb3, 0xe3, 0xd9, 0xd7, 0x13, 0x7b, 0xab, 0xda, 0x6a, + 0xf5, 0x8a, 0x09, 0xbd, 0xb4, 0xb8, 0x28, 0x4a, 0x25, 0x49, 0xf5, 0x4d, + 0x66, 0xe6, 0xf5, 0xb0, 0x65, 0xca, 0x8f, 0x47, 0xff, 0x9d, 0xfc, 0xab, + 0xb4, 0x52, 0x97, 0xda, 0xe2, 0x87, 0xf2, 0xae, 0x8e, 0x0b, 0xb0, 0x67, + 0x6b, 0x7f, 0x3e, 0x44, 0x9f, 0xfd, 0x1c, 0x21, 0xdd, 0x7e, 0xb0, 0xeb, + 0x46, 0xeb, 0xc5, 0xac, 0xc2, 0x5f, 0xcc, 0x64, 0x02, 0xb6, 0xed, 0x74, + 0x31, 0x62, 0xcf, 0xa2, 0x8e, 0x1f, 0x66, 0x74, 0xea, 0x87, 0x6c, 0xb0, + 0xff, 0xde, 0xaf, 0x58, 0xfa, 0x34, 0x0a, 0xcd, 0xbb, 0x5c, 0x96, 0x82, + 0x88, 0xda, 0xe5, 0x5a, 0xc7, 0x40, 0xcf, 0x49, 0xf9, 0x55, 0x23, 0x16, + 0xce, 0x20, 0x29, 0x36, 0xb1, 0x53, 0xfc, 0xc1, 0xfb, 0xa9, 0x4d, 0x89, + 0x78, 0x8c, 0xf3, 0xda, 0xd9, 0x81, 0x0d, 0xdb, 0x98, 0x32, 0x98, 0x22, + 0x48, 0xe8, 0x7e, 0x47, 0x6a, 0xa9, 0xbf, 0xc3, 0xea, 0x57, 0x3f, 0x75, + 0xbb, 0x4e, 0x7a, 0x1d, 0x29, 0x43, 0x91, 0x60, 0xc3, 0x3f, 0x9d, 0x19, + 0x06, 0xcf, 0x64, 0x06, 0x7e, 0xc8, 0x30, 0xc1, 0x2e, 0x6d, 0x9c, 0x7b, + 0x35, 0xc6, 0x5c, 0x0f, 0x2f, 0x6c, 0x98, 0xc2, 0x17, 0xd1, 0xe1, 0x55, + 0xe8, 0x7c, 0x5d, 0x0a, 0x07, 0x18, 0xf7, 0xa5, 0x4a, 0xa1, 0x67, 0x4b, + 0x29, 0x55, 0x85, 0xe3, 0xaf, 0x80, 0xc6, 0x57, 0x01, 0x0c, 0x8b, 0x2f, + 0xe0, 0x2e, 0x52, 0x2f, 0x43, 0xbe, 0x77, 0xd4, 0x01, 0x65, 0xee, 0x01, + 0x92, 0x8e, 0xd2, 0xee, 0x27, 0x62, 0x2d, 0xf1, 0x69, 0x47, 0x95, 0xa4, + 0x02, 0xb2, 0x07, 0x7d, 0x44, 0xe9, 0xac, 0xdb, 0x13, 0x15, 0x3a, 0x9e, + 0x5f, 0x6a, 0xf9, 0x45, 0x93, 0xed, 0xfc, 0x9f, 0xbf, 0xbf, 0x17, 0xd2, + 0x8b, 0xbb, 0x4e, 0x1a, 0x4a, 0x6d, 0x2f, 0x73, 0xef, 0x44, 0x18, 0x6f, + 0xfe, 0xa2, 0x40, 0xcf, 0xc7, 0x5b, 0x4d, 0xf8, 0x59, 0xbc, 0x4e, 0xf1, + 0xc8, 0x3b, 0x3d, 0xbd, 0x43, 0xd4, 0x69, 0x41, 0x10, 0x33, 0x5f, 0xa7, + 0x0f, 0xbc, 0x2e, 0x51, 0x7c, 0xfd, 0x69, 0x7e, 0x9c, 0x1f, 0x4c, 0x70, + 0xde, 0xd4, 0x2c, 0x0d, 0xeb, 0xfa, 0x61, 0xa6, 0x87, 0x42, 0x80, 0x85, + 0x72, 0xd4, 0xf1, 0xa7, 0x8b, 0xb9, 0x84, 0xb2, 0x53, 0x45, 0xfe, 0xf2, + 0x27, 0x3a, 0xfc, 0x97, 0xb2, 0x21, 0x2b, 0x79, 0xc8, 0xdb, 0xc7, 0xa4, + 0x15, 0x4b, 0xf6, 0x64, 0x7e, 0x5e, 0x74, 0x6a, 0x0d, 0x28, 0x20, 0x75, + 0xb1, 0xa6, 0x1e, 0xda, 0x0c, 0x79, 0x8d, 0x56, 0x97, 0x57, 0xe6, 0xb2, + 0xc3, 0xf1, 0x7f, 0xda, 0xa0, 0x8e, 0x72, 0xb0, 0xb2, 0xc1, 0x90, 0x8d, + 0x03, 0x03, 0x34, 0x57, 0xaf, 0x00, 0x2d, 0x5c, 0x06, 0x0c, 0x13, 0xf7, + 0x8e, 0xc6, 0x93, 0x04, 0x5d, 0xc4, 0xa8, 0x8d, 0x0e, 0x11, 0xfb, 0x3f, + 0x83, 0xcb, 0xaa, 0x2b, 0xcd, 0xea, 0xfa, 0x66, 0xb7, 0xe2, 0x6b, 0xb6, + 0x80, 0xf9, 0xf0, 0xe6, 0xc0, 0xea, 0xc9, 0x01, 0x24, 0x09, 0x45, 0xf9, + 0xac, 0x68, 0x9d, 0xd2, 0xc6, 0xe5, 0xe8, 0x85, 0x08, 0xab, 0xf8, 0x61, + 0x85, 0xd4, 0xb2, 0x20, 0xa9, 0xa4, 0x45, 0xeb, 0xe2, 0x26, 0xf7, 0xf7, + 0x3a, 0x1d, 0x9b, 0xf9, 0xe0, 0x7e, 0xe2, 0x38, 0xa0, 0xc0, 0x0f, 0x3e, + 0x12, 0xd9, 0x1d, 0x27, 0x3b, 0x3a, 0xd1, 0x4c, 0xbf, 0x80, 0x33, 0xae, + 0xc8, 0xe3, 0x06, 0xa6, 0xb1, 0xe1, 0x0e, 0x09, 0x27, 0xc4, 0x3e, 0x77, + 0x36, 0x51, 0x41, 0xaa, 0x9c, 0xb3, 0x8d, 0x12, 0x15, 0x51, 0x98, 0x1d, + 0x5c, 0x40, 0x3d, 0x95, 0x26, 0xbe, 0xaf, 0x21, 0xbc, 0x5a, 0x11, 0x3e, + 0xaf, 0x6c, 0x0d, 0x52, 0xf8, 0xf0, 0x04, 0xeb, 0x36, 0xd3, 0x57, 0xb8, + 0x3e, 0x8a, 0xf4, 0x94, 0xe2, 0xc5, 0x97, 0xf2, 0x6b, 0x9e, 0x31, 0x7e, + 0x22, 0xc8, 0x45, 0x96, 0x79, 0x30, 0x15, 0x77, 0x02, 0x39, 0xd1, 0x46, + 0x12, 0x41, 0xf5, 0x35, 0xfc, 0xe6, 0x62, 0x0e, 0x13, 0xfa, 0x40, 0x99, + 0xb0, 0x54, 0x96, 0xab, 0x75, 0x7c, 0x40, 0xc9, 0x43, 0x26, 0x89, 0xc8, + 0x60, 0x22, 0xa7, 0xd2, 0x94, 0x42, 0xc3, 0x5f, 0x84, 0x72, 0xc4, 0x05, + 0x61, 0xe2, 0x69, 0x43, 0xd9, 0xf6, 0x23, 0x4c, 0x03, 0xf8, 0x3a, 0x91, + 0x8b, 0x38, 0xd9, 0x3f, 0xb5, 0xf2, 0x56, 0x78, 0x81, 0x50, 0xe4, 0xaf, + 0x76, 0x88, 0xe1, 0xc2, 0xb1, 0xa3, 0xe9, 0xed, 0xe2, 0xc4, 0x64, 0xf3, + 0x21, 0x8d, 0xf6, 0x6b, 0xa4, 0xf5, 0x31, 0x0f, 0xa6, 0xd9, 0xb5, 0x99, + 0x09, 0x83, 0xc5, 0x42, 0xf2, 0xaf, 0x59, 0x51, 0xac, 0x34, 0x2d, 0x60, + 0x3f, 0x0d, 0x0f, 0x36, 0x6f, 0x0d, 0x31, 0xbb, 0x25, 0x23, 0x7a, 0xca, + 0x69, 0xeb, 0x77, 0xe5, 0xbf, 0x0e, 0x91, 0xd4, 0x75, 0xdb, 0x01, 0xe8, + 0x37, 0x79, 0x76, 0x21, 0x36, 0xad, 0xc1, 0x40, 0x86, 0x7a, 0x21, 0x42, + 0xe6, 0x83, 0x0b, 0xe4, 0x5d, 0x0b, 0x28, 0xbe, 0x11, 0xba, 0xf6, 0x25, + 0x6e, 0x10, 0x46, 0xfc, 0x6e, 0xd1, 0xe7, 0x92, 0x6f, 0x8d, 0x8f, 0x1c, + 0x36, 0x07, 0x64, 0x11, 0xb1, 0xff, 0x97, 0xce, 0xfe, 0x5a, 0x29, 0xa9, + 0xf2, 0xab, 0x37, 0x22, 0x0e, 0xf1, 0x5b, 0x38, 0x58, 0x74, 0x7f, 0x32, + 0x6f, 0x16, 0xec, 0x01, 0x77, 0x6b, 0x7b, 0x5e, 0xe4, 0x37, 0x72, 0x67, + 0x1e, 0x43, 0xb7, 0x41, 0xc4, 0xb0, 0xa0, 0xb2, 0x73, 0xd6, 0xb3, 0x19, + 0x04, 0xe1, 0xb2, 0xf8, 0x5a, 0xf3, 0xbb, 0x7e, 0xbe, 0x6d, 0x00, 0xc8, + 0x9d, 0x7b, 0x04, 0xcc, 0x54, 0xa7, 0xc4, 0xf4, 0x01, 0x4e, 0xf6, 0xd5, + 0x60, 0xcc, 0xdc, 0x56, 0xf6, 0x3d, 0x81, 0x6a, 0x65, 0xc6, 0x02, 0xbf, + 0xa5, 0xc5, 0xcc, 0xde, 0x4d, 0x4d, 0xfe, 0x40, 0x7b, 0xd1, 0xbf, 0xe5, + 0xb7, 0xe9, 0x38, 0xbe, 0x70, 0x8b, 0x94, 0x83, 0xfd, 0xbf, 0xd7, 0x0e, + 0x01, 0xe0, 0x3c, 0xf9, 0x51, 0xab, 0x5e, 0x1e, 0xde, 0x19, 0x6a, 0x62, + 0x94, 0xb0, 0x93, 0x40, 0xcf, 0xa0, 0x8b, 0x08, 0x99, 0xb3, 0xbc, 0x86, + 0xce, 0xb7, 0x8a, 0xe9, 0x24, 0xe0, 0x89, 0x93, 0x64, 0x4f, 0x4e, 0x5d, + 0xd0, 0x3a, 0x08, 0x53, 0xea, 0x89, 0x14, 0xa9, 0x4c, 0xcb, 0xed, 0x74, + 0x95, 0x94, 0xba, 0x09, 0x91, 0xb0, 0x88, 0x30, 0xc6, 0x59, 0x64, 0xcd, + 0x40, 0xe0, 0xa5, 0x6f, 0xea, 0x59, 0x99, 0x71, 0x95, 0xf7, 0xb8, 0x01, + 0xbd, 0x78, 0x02, 0x42, 0x30, 0xca, 0xee, 0x2a, 0x2e, 0x0e, 0xaa, 0xa9, + 0x02, 0x0d, 0xb3, 0x97, 0xb3, 0xb9, 0xe0, 0x2d, 0x75, 0xc3, 0x44, 0xc8, + 0x9b, 0x3a, 0xf1, 0xb9, 0x0f, 0x3c, 0x77, 0x34, 0x68, 0x42, 0xb2, 0x2b, + 0x89, 0x77, 0xe6, 0xed, 0x7d, 0x94, 0x3f, 0xcc, 0x60, 0xae, 0x79, 0x7c, + 0xef, 0x59, 0x60, 0xa0, 0x74, 0xa9, 0x4f, 0x7a, 0x24, 0x0a, 0x07, 0x20, + 0x34, 0xaf, 0x87, 0xb0, 0x78, 0x1d, 0x76, 0x81, 0x9d, 0x67, 0x3f, 0x32, + 0xc0, 0x7a, 0x92, 0x52, 0x13, 0x67, 0xae, 0xb6, 0x41, 0x05, 0x5c, 0x9d, + 0xaf, 0xe0, 0xbb, 0x35, 0x04, 0x28, 0xe0, 0xc9, 0x59, 0xe0, 0xe6, 0x62, + 0x21, 0x59, 0x5d, 0x11, 0xba, 0xa3, 0x0d, 0x2b, 0x82, 0x17, 0xd3, 0xdc, + 0x0b, 0x85, 0x17, 0xe9, 0xf9, 0xe3, 0x94, 0x31, 0xf8, 0x8f, 0x50, 0xb3, + 0xed, 0xa3, 0xbe, 0x82, 0x4a, 0x52, 0xc0, 0x46, 0xeb, 0xe2, 0xad, 0xa1, + 0xb2, 0x82, 0x1e, 0x22, 0x7f, 0xed, 0x4b, 0x51, 0x22, 0x52, 0x53, 0xa4, + 0xe7, 0x20, 0x52, 0xae, 0xb8, 0x66, 0xc4, 0x71, 0xaf, 0x46, 0x33, 0xdc, + 0xb0, 0xc0, 0xc1, 0x72, 0x82, 0x55, 0x53, 0xc5, 0xe1, 0x20, 0xed, 0x38, + 0x3b, 0x4b, 0xca, 0xf1, 0xfd, 0x30, 0xd9, 0x81, 0x16, 0x1b, 0x1e, 0x64, + 0x9f, 0xf2, 0x0a, 0xeb, 0x3c, 0xfd, 0xc1, 0x32, 0xd2, 0xdc, 0x3d, 0x95, + 0x2e, 0x2d, 0xfa, 0x3e, 0x5f, 0x55, 0x84, 0xd1, 0xcb, 0x02, 0x5d, 0xb9, + 0x77, 0x91, 0x8d, 0x3a, 0x71, 0xb5, 0x43, 0x09, 0x38, 0x39, 0xe6, 0xbc, + 0x44, 0xc6, 0x49, 0xe6, 0xd0, 0x60, 0x8f, 0xaf, 0x68, 0x72, 0x16, 0x82, + 0xcc, 0xb2, 0xea, 0x67, 0xad, 0x26, 0x4d, 0x9d, 0x94, 0x36, 0x48, 0x8c, + 0x8a, 0x9f, 0x8c, 0x46, 0x9e, 0x8a, 0xb7, 0xf1, 0xe4, 0x39, 0xc6, 0x95, + 0xcc, 0x3e, 0x75, 0x31, 0xd9, 0x05, 0x38, 0xfb, 0x1f, 0x56, 0x02, 0x14, + 0x5d, 0x4e, 0x5e, 0xed, 0xcd, 0x96, 0x07, 0x61, 0x07, 0x0d, 0xf0, 0x5d, + 0xe0, 0x85, 0xb2, 0x36, 0x7d, 0x5b, 0x9c, 0xa0, 0x70, 0xa0, 0x3c, 0xee, + 0x2f, 0xbb, 0xac, 0xaa, 0x2a, 0xba, 0x6a, 0x53, 0xd4, 0xd8, 0x0d, 0x9f, + 0xec, 0x7f, 0x00, 0x2e, 0xdb, 0x73, 0x84, 0xb9, 0x97, 0xfa, 0xf1, 0x22, + 0x8a, 0x5f, 0xa3, 0xd5, 0xcf, 0x61, 0x23, 0xeb, 0x91, 0xc9, 0x08, 0xa5, + 0x96, 0x76, 0xb0, 0xb6, 0xad, 0x11, 0xfd, 0xe0, 0x31, 0x0e, 0xfa, 0x50, + 0xd1, 0xfa, 0x55, 0xe8, 0xc6, 0xde, 0x2f, 0xdd, 0x72, 0xa0, 0x6b, 0x41, + 0x56, 0x8d, 0x3c, 0x6c, 0x92, 0x8b, 0xe2, 0x95, 0x40, 0x9b, 0xc8, 0x9a, + 0xb5, 0xef, 0x99, 0xd0, 0xc7, 0x0e, 0x8b, 0xb5, 0xd1, 0xe1, 0x80, 0x32, + 0x22, 0x71, 0x76, 0xc8, 0xf7, 0x3b, 0xa8, 0xce, 0x14, 0x2b, 0xee, 0x14, + 0x95, 0x3a, 0x1f, 0xdf, 0x6c, 0x56, 0x4b, 0x90, 0xe9, 0xc0, 0x0f, 0x5e, + 0xfa, 0x95, 0x68, 0x8d, 0x98, 0xc7, 0x88, 0xdf, 0xe4, 0x6b, 0xa7, 0x73, + 0x11, 0x3e, 0x5b, 0xad, 0xdd, 0x7e, 0x8e, 0x8d, 0x65, 0x8d, 0x19, 0xd5, + 0xa8, 0x2d, 0x6f, 0x25 +}; +static const unsigned int random_bin_len = 256000; +static uint32_t value; + +/* + * This file has one purpose which is to make the firmware big! + */ +int +read_random_data(void) +{ + int i; + + for (value = 0, i = 0; i < random_bin_len; i++) { + value += random_bin[i]; + } + + return value; +} diff --git a/bootloader/mcuboot/testplan/mynewt/apps/slinky/syscfg.yml b/bootloader/mcuboot/testplan/mynewt/apps/slinky/syscfg.yml new file mode 100644 index 0000000..1005edc --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/apps/slinky/syscfg.yml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +syscfg.defs: + BLINKY_TICKS_PER_SEC: + value: 1 + +syscfg.vals: + SHELL_TASK: 1 + STATS_NAMES: 1 + REBOOT_LOG_FCB: 1 + LOG_FCB: 1 + CONFIG_FCB: 1 + STATS_CLI: 1 + LOG_CLI: 1 + CONFIG_CLI: 1 + STATS_MGMT: 1 + LOG_MGMT: 1 + CONFIG_MGMT: 1 diff --git a/bootloader/mcuboot/testplan/mynewt/key_ec.pem b/bootloader/mcuboot/testplan/mynewt/key_ec.pem new file mode 100644 index 0000000..7307800 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/key_ec.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MGgCAQEEHKDCA+0fUA9P6MkTZvGlO4IN8kQfMbD4xyt619WgBwYFK4EEACGhPAM6 +AASgjFrWmCAa1bnE/X+l0wjKAJFexpJJzhjFZBftv2PQzbj3/yklNVp6IDDJJpWy +V9FGDWkYE8l9sw== +-----END EC PRIVATE KEY----- diff --git a/bootloader/mcuboot/testplan/mynewt/key_ec256.pem b/bootloader/mcuboot/testplan/mynewt/key_ec256.pem new file mode 100644 index 0000000..0f9264f --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/key_ec256.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEINtq7Uc92lGcI9pcfwU7IEWfjtYKMpG3iSLy8vdn3p0poAoGCCqGSM49 +AwEHoUQDQgAEbmhhf9fiyT20CixtsNDO/lS4lq38YIeJQektIcg+HV7Dd/1iX7v7 +2hCnWiOq/AG13HM9N8FFj5A7Zv0rlWacyQ== +-----END EC PRIVATE KEY----- diff --git a/bootloader/mcuboot/testplan/mynewt/key_ec256_2.pem b/bootloader/mcuboot/testplan/mynewt/key_ec256_2.pem new file mode 100644 index 0000000..da05302 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/key_ec256_2.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICDt9+UM3LnshnbYzo73AGTWav9ZmRYDydJAarzx4Og8oAoGCCqGSM49 +AwEHoUQDQgAEDv6jJOUwhCpzEH3T7yWlAOZRTLIlC0q/JMHyJR0PkPMS+eyq7fEp +hOnAXbCNx5PLyVLR2NSnOU1A6QiLHW1j0A== +-----END EC PRIVATE KEY----- diff --git a/bootloader/mcuboot/testplan/mynewt/key_ec_2.pem b/bootloader/mcuboot/testplan/mynewt/key_ec_2.pem new file mode 100644 index 0000000..82dff43 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/key_ec_2.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MGgCAQEEHCN8/JmSnXMbwvimYU61t5J3BqSog3UD9rszWEugBwYFK4EEACGhPAM6 +AATihCbugkqIXekQOh7iZZZPghfS/bQID5ad8FRY1xnL5rBd2nR7gFqTXxV9M9tF +EJFA+MwFnIbgng== +-----END EC PRIVATE KEY----- diff --git a/bootloader/mcuboot/testplan/mynewt/key_rsa.pem b/bootloader/mcuboot/testplan/mynewt/key_rsa.pem new file mode 100644 index 0000000..c43da3b --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/key_rsa.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA4khfMQhQLk9tv83QCfFc1gjphM91LIgHzA75U65KmpO0G1VD +t34QqFDZFJAFgBWgjNvcGwNW1keURKHoFfR3SWBFNKQXmoGo/LbZIfZo+noT5SXN +pQt3Cl/2C500j8P0kferKBJS3LuykoCY4hMz58MqHCeY/P8H210W0lDBjhL6lZhR +kbv57sNlpALgv7axxiDHn/5jYWpo9gjKVTLAUN2NUnMDer7XMtlhmRn/DeaCyBw9 +BxrpqHutdSwmhz1nK1rEf3CGcIp+7ekvvirVtDN7zInNVwi7ct8AjuqKKqigHLiK +Ya8yPUHp9rGic8L/0TJVH9oJI8GDUeFP1WU+lwIDAQABAoIBAQDBV+8mGSFRgIKY +2UVByZ52LyVAWnaW9yAaZkz5CDose7nvhNoYZbnb50Ckhi588327/XvDBQZkjsKM +Jf8FC14FLyHSycZ1OQZn79/1WfL22eo36CYfOH2dOsMjx04K7PcC5aiz03xDqIj7 +DrASsy+tfp9zcQ4SVeKjt5VxXJkVR1wMTUM08N7GXTf71YpTLSb0drbovWS8QUYq +JIUh36EMhNOIqNyXVBBxCHdbUEvN0p4185mciK+P42JJvTm1i9ooyuYQUaF3U25y +yDyhMGxVxPwgREaIhA/xfmU2Dsqrmq0pitiVZ9qJqbuWIuxZJTd2l9tkeEr4hdHB +zCgFqyKhAoGBAP/iDrMUdf+KB53U7le2xIk+MJwNDSblVF/O2t93hvMk39Y0bT9y +YvGaVh3Ryqoy4/g81nwbvQs36IMb3XGvbn3N4a5XGBlysOLOzwF0piYNX1ZyRgxn +2wxw1O24uE1qN4Eh0UrLDvN07nKRJ6V9az+zSW3EAxwwUDWBIFL0pM8xAoGBAOJi +2cbIfHXEpt46aDEN7BwBog+8KL5Mima13zN8Melu6WvhB69JGG8IlUstMPPAq6uL +Wd72vVkN0Vy7kr6dHCa+/IzdW47ukUFIB4IaMAHYTByWaY7Hh7mnIUyNUIojou9e +o49DKdpV/xOksJWljwhtiHy6QCivbka3prTBikhHAoGBAMFQn5pbkuoD9c0f2REb +W0/0U/URRyZji2L2fBTn0GRRL2o9IWwVlvrAht7waBQ9bk1UaRZKPoADNP4YRyxk +RS12JVH5KpPPOiOf6nRHFF8bKzO8EX+91peHhtYx/8s8u8IrMls3HYyAgsS7NSCp +qCTv5kGvHEpnlbFWZH1HpluBAoGBAJT3wSA6SxPfzIJNYsRsyeJ//JloElNu4F/H +69DgN6PI1Rak5D6m1coylrL6UM0FCrH+J6w9JsnT+uGPmHePwLeKU3uKKbZ7K+AO +OsqU2uRL5YGmRF2s1JYI8TODhezwmEX1O6GI66B9mDTf2UcPw3gjQ333vUJIdkNY +k/07off/AoGBAPx9NTR13dbDhlG6hyxEtCrTyxKKXs3vmTfjmaQnicvM/oceRYed +h8+q/YN2ePvxeOYiOgluFWJz+pOrWKYm1sFYEa6+GtCnBwyv5fmxLxY/99ULIMYA +Jyudi8CusEM1q5TetFMoNcRSj7iPLUyFO1thUoRpCTJ/bUcD0Dl9PL20 +-----END RSA PRIVATE KEY----- diff --git a/bootloader/mcuboot/testplan/mynewt/key_rsa_2.pem b/bootloader/mcuboot/testplan/mynewt/key_rsa_2.pem new file mode 100644 index 0000000..c0dba0a --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/key_rsa_2.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArKyBv+rzsk2frv0lqcYNJ0o2ahMwS/lpXXvfkd9cGvgX+Xf+ +Zdv54VHO6cpvp1CExOoeVwhK+78ETYTXuOqIzJB2uv+MtcvlwN/OH14g0TmMb14Z +n3iaV9I9/VLEt2BxI0gAp+megUAz5CW66BMw/cfxX0jvWYhHr8qFRWSbZkExivwy +b10azCw+g6xk+NlOddd9x0Jw1UjIZcSKoVdLcuhctBTqteMqhFUIsR9LjzEj0VQk +nXtHFABIXGsc4J20WvLMueLIyd5AW1J4EKLHLcpvBuNFQTil3U8T5mmdfarVaqVr +7AHINsv3ROIw6sWzA1S9baB8Ok9cDzkZLK0XgwIDAQABAoIBAQCUUXT9ya3b2Qsx +fuYsBx9jQT6uLJ9OkwSrNOzb2rS9wZ6uPSC6k5H2tZN6g9UWLXZtwf+fmFL4HKJw +h4vYnTQ2Klyh0UInIeXOny7pCKw9qyXyvyxZK3m/t6phfwfTz7Y+rOlLUcNBmEk6 +TiJWl618P5MX2oklKYcR+24wJfPJhwpclnrxw5UzqUbDSFfSL/zDjlnPEeQ8tQ1Z +WQISqUim6bMk3J62AiOUwH9QsOj7seEQG7zCqVc+MWQ5OloI1wlPyACLDAvnBBT5 +9VaP8ixEaJbarpMXH9j9qnRRsEeLa8BGqJ6qZIlf9zyWEQrkzLAi75oTdiRL5cm1 +VSiXVAhJAoGBAN7s7537JJuPFMHwurAUbYd02rqv6pWztwmVJZzsILOPjVBOyb9E +QJXCnJ1OfL1lhtegndP36Sb/mGwdK6IWDLH2+E/eLcWzcUXNvI0H02pbvzq/Vbh+ +Q0VxFblhvwGhvi0+SydC9MowTzcv/5wAV7zp2AK6+fBjenE3aCPNZCyNAoGBAMZK +7sgIOuvLS0qVd7YXJpcBcjZRNpRc14T8Qh+AXRLv5qbFjMjwc3usJo3E4+EIE7eq +IopMw54emT7qDfRoZXXjPPDawixTRMFDBEFHbXb1KB3sDp8xyghZ1f8296Y00NJ9 +3pNba/UBTgVXNspZ5gTois8HsLrAnbqjNuA5NrhPAoGBAMaZ63ehUKHNvL5zSr6n +1FSDRIJhSuqHqx+8YkAFFbUixNCxAIeHtMo3EPQMApFxK8paa4F2MZ7uwso+yqqi +XjkGP35YAAtLrDR17+7s8+qjRiB+aU3uHtx0vNflPxejExyXjLizrAWdOFWAS4ad +v4ysACeekCEbXvASXpLW3tHZAoGAemvWwb57CgpnwHNJBi2C0KW+6pP3O1+aW2sW +M7afP8rGvt2mDoSM96SP5OTSv6Kp8bFjQ7ki6GMBv5rm5KbzRPX3MMgOKyl5gEus +u9SqW0/95YNQf65QihlUig1Ylc9zwRCesqE1pHyau6ddl04rOYqL8EdSL+otNwX/ +Ii2Qf/cCgYBi5WOv0cFyff+uQNUGEv1l896+8yhyml2ogE8PTdrBX16e4/N89vG4 +5vXEmPmqk9qYxmKIr5MLM9229gWCCGt7wWdH5Xt0AT7ZUvxwyKnaS/QimOVU2X1t +3lpGMTJkRq5bxMUUbC1n9e6o/GFrlmtLjCTYgRLOBKNy36bxxZnOZw== +-----END RSA PRIVATE KEY----- diff --git a/bootloader/mcuboot/testplan/mynewt/keys/ec256/pkg.yml b/bootloader/mcuboot/testplan/mynewt/keys/ec256/pkg.yml new file mode 100644 index 0000000..2467ce4 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/keys/ec256/pkg.yml @@ -0,0 +1,3 @@ +pkg.name: keys/ec256 +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" diff --git a/bootloader/mcuboot/testplan/mynewt/keys/ec256/src/keys.c b/bootloader/mcuboot/testplan/mynewt/keys/ec256/src/keys.c new file mode 100644 index 0000000..ece45c3 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/keys/ec256/src/keys.c @@ -0,0 +1,19 @@ +#include +static unsigned char key[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x6e, 0x68, 0x61, 0x7f, 0xd7, 0xe2, 0xc9, 0x3d, 0xb4, + 0x0a, 0x2c, 0x6d, 0xb0, 0xd0, 0xce, 0xfe, 0x54, 0xb8, 0x96, 0xad, 0xfc, + 0x60, 0x87, 0x89, 0x41, 0xe9, 0x2d, 0x21, 0xc8, 0x3e, 0x1d, 0x5e, 0xc3, + 0x77, 0xfd, 0x62, 0x5f, 0xbb, 0xfb, 0xda, 0x10, 0xa7, 0x5a, 0x23, 0xaa, + 0xfc, 0x01, 0xb5, 0xdc, 0x73, 0x3d, 0x37, 0xc1, 0x45, 0x8f, 0x90, 0x3b, + 0x66, 0xfd, 0x2b, 0x95, 0x66, 0x9c, 0xc9 +}; +static unsigned int key_len = 91; +const struct bootutil_key bootutil_keys[] = { + [0] = { + .key = key, + .len = &key_len, + }, +}; +const int bootutil_key_cnt = 1; diff --git a/bootloader/mcuboot/testplan/mynewt/keys/pkg.yml b/bootloader/mcuboot/testplan/mynewt/keys/pkg.yml new file mode 100644 index 0000000..82d0e84 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/keys/pkg.yml @@ -0,0 +1,9 @@ +pkg.name: keys +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" + +pkg.deps.BOOTUTIL_SIGN_RSA: + - keys/rsa + +pkg.deps.BOOTUTIL_SIGN_EC256: + - keys/ec256 diff --git a/bootloader/mcuboot/testplan/mynewt/keys/rsa/pkg.yml b/bootloader/mcuboot/testplan/mynewt/keys/rsa/pkg.yml new file mode 100644 index 0000000..eaadda1 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/keys/rsa/pkg.yml @@ -0,0 +1,3 @@ +pkg.name: keys/rsa +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" diff --git a/bootloader/mcuboot/testplan/mynewt/keys/rsa/src/keys.c b/bootloader/mcuboot/testplan/mynewt/keys/rsa/src/keys.c new file mode 100644 index 0000000..bc59322 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/keys/rsa/src/keys.c @@ -0,0 +1,34 @@ +#include +static unsigned char key[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe2, 0x48, 0x5f, + 0x31, 0x08, 0x50, 0x2e, 0x4f, 0x6d, 0xbf, 0xcd, 0xd0, 0x09, 0xf1, 0x5c, + 0xd6, 0x08, 0xe9, 0x84, 0xcf, 0x75, 0x2c, 0x88, 0x07, 0xcc, 0x0e, 0xf9, + 0x53, 0xae, 0x4a, 0x9a, 0x93, 0xb4, 0x1b, 0x55, 0x43, 0xb7, 0x7e, 0x10, + 0xa8, 0x50, 0xd9, 0x14, 0x90, 0x05, 0x80, 0x15, 0xa0, 0x8c, 0xdb, 0xdc, + 0x1b, 0x03, 0x56, 0xd6, 0x47, 0x94, 0x44, 0xa1, 0xe8, 0x15, 0xf4, 0x77, + 0x49, 0x60, 0x45, 0x34, 0xa4, 0x17, 0x9a, 0x81, 0xa8, 0xfc, 0xb6, 0xd9, + 0x21, 0xf6, 0x68, 0xfa, 0x7a, 0x13, 0xe5, 0x25, 0xcd, 0xa5, 0x0b, 0x77, + 0x0a, 0x5f, 0xf6, 0x0b, 0x9d, 0x34, 0x8f, 0xc3, 0xf4, 0x91, 0xf7, 0xab, + 0x28, 0x12, 0x52, 0xdc, 0xbb, 0xb2, 0x92, 0x80, 0x98, 0xe2, 0x13, 0x33, + 0xe7, 0xc3, 0x2a, 0x1c, 0x27, 0x98, 0xfc, 0xff, 0x07, 0xdb, 0x5d, 0x16, + 0xd2, 0x50, 0xc1, 0x8e, 0x12, 0xfa, 0x95, 0x98, 0x51, 0x91, 0xbb, 0xf9, + 0xee, 0xc3, 0x65, 0xa4, 0x02, 0xe0, 0xbf, 0xb6, 0xb1, 0xc6, 0x20, 0xc7, + 0x9f, 0xfe, 0x63, 0x61, 0x6a, 0x68, 0xf6, 0x08, 0xca, 0x55, 0x32, 0xc0, + 0x50, 0xdd, 0x8d, 0x52, 0x73, 0x03, 0x7a, 0xbe, 0xd7, 0x32, 0xd9, 0x61, + 0x99, 0x19, 0xff, 0x0d, 0xe6, 0x82, 0xc8, 0x1c, 0x3d, 0x07, 0x1a, 0xe9, + 0xa8, 0x7b, 0xad, 0x75, 0x2c, 0x26, 0x87, 0x3d, 0x67, 0x2b, 0x5a, 0xc4, + 0x7f, 0x70, 0x86, 0x70, 0x8a, 0x7e, 0xed, 0xe9, 0x2f, 0xbe, 0x2a, 0xd5, + 0xb4, 0x33, 0x7b, 0xcc, 0x89, 0xcd, 0x57, 0x08, 0xbb, 0x72, 0xdf, 0x00, + 0x8e, 0xea, 0x8a, 0x2a, 0xa8, 0xa0, 0x1c, 0xb8, 0x8a, 0x61, 0xaf, 0x32, + 0x3d, 0x41, 0xe9, 0xf6, 0xb1, 0xa2, 0x73, 0xc2, 0xff, 0xd1, 0x32, 0x55, + 0x1f, 0xda, 0x09, 0x23, 0xc1, 0x83, 0x51, 0xe1, 0x4f, 0xd5, 0x65, 0x3e, + 0x97, 0x02, 0x03, 0x01, 0x00, 0x01 +}; +static unsigned int key_len = 270; +const struct bootutil_key bootutil_keys[] = { + [0] = { + .key = key, + .len = &key_len, + }, +}; +const int bootutil_key_cnt = 1; diff --git a/bootloader/mcuboot/testplan/mynewt/project.yml b/bootloader/mcuboot/testplan/mynewt/project.yml new file mode 100644 index 0000000..76c0e24 --- /dev/null +++ b/bootloader/mcuboot/testplan/mynewt/project.yml @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +project.name: "mcuboot-test" + +project.repositories: + - apache-mynewt-core + - mcuboot + +repository.apache-mynewt-core: + type: github + vers: 0-dev + user: apache + repo: mynewt-core + +repository.mcuboot: + type: github + vers: 0-dev + user: mcu-tools + repo: mcuboot diff --git a/bootloader/mcuboot/zephyr/module.yml b/bootloader/mcuboot/zephyr/module.yml new file mode 100644 index 0000000..9360dbf --- /dev/null +++ b/bootloader/mcuboot/zephyr/module.yml @@ -0,0 +1,6 @@ +samples: + - boot/zephyr +build: + cmake-ext: True + kconfig-ext: True + sysbuild-cmake: boot/zephyr/sysbuild diff --git a/docs/BLE.xlsx b/docs/BLE.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3a11617dc973784dc200b3f87a5c602fdb18a901 GIT binary patch literal 9852 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

    H~QxDhG`U5OpyN32c;9mU22B(e1@8U5_E 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

    !Td5Hv3l8RsD9 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

    HhHV{4`-N_X>_GGlA->8zm2?`4$ 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 }{gq7#rpDqQ2MFa1tX9;)}mS0UYmo#Fnq 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 a{(C5+rig*;K>+}Se9a*Hby*Lj1O5lNYF}&s literal 0 HcmV?d00001 diff --git a/docs/interface.xlsx b/docs/interface.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..dbdfd10fc312440e29df7672214da9f2e7f1f339 GIT binary patch literal 9630 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 E0d)-awg3PC literal 0 HcmV?d00001 diff --git a/docs/shotData/shotDataExamples.md b/docs/shotData/shotDataExamples.md new file mode 100644 index 0000000..659da06 --- /dev/null +++ b/docs/shotData/shotDataExamples.md @@ -0,0 +1,19 @@ +11 sample rates: 4006 Hz + +208 (gyro sample rate) +416 (accel sample rate) +Gx Gy Gz ; # GS0 +Ax Ay Az ; # AS0 +Ax Ay Az ; # AS1 +Gx Gy Gz ; # GS1 +Ax Ay Az ; # AS2 +Ax Ay Az ; # AS3 + +416 (gyro sample rate) +208 (accel sample rate) +Gx Gy Gz ; # GS0 +Gx Gy Gz ; # GS1 +Ax Ay Az ; # AS0 +Gx Gy Gz ; # GS2 +Gx Gy Gz ; # GS3 +Ax Ay Az ; # AS1 diff --git a/docs/shotData/shotDataFormat.docx b/docs/shotData/shotDataFormat.docx new file mode 100644 index 0000000000000000000000000000000000000000..a3c825b1e33efe35f809a43605eddaccfed2fd61 GIT binary patch literal 80942 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 W(gY%X>pip install cryptography + - cbor package + >> pip install cbor + +The key generator is provided within the Nordic SDK/bootloader/mcuboot/scripts/imgtool.py +Run the following to generate key: + >>python /booloader/mcuboot/scripts/imgtool.py keygen -t ecdsa-p256 -k .pem + +NOTE: in application, sysbuild.conf needs to be updated to contain path of generated keys: + SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="C:/cyber/scope/sw/keys/cyber_pk.pem" \ No newline at end of file diff --git a/keys/test_pk.pem b/keys/test_pk.pem new file mode 100644 index 0000000..8501c59 --- /dev/null +++ b/keys/test_pk.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs5jc/9scXf5KxYgU +2enoeOqMZPaEEfmtuqvaIMxWyBmhRANCAAQnjE3ae+5AQTmT7o1y1sxECTqD4MRO +ukTHeDUcJD1C9qbXLK6phyMFGB57S9LMlBiYnpuVS57rElDcHgWmm76Q +-----END PRIVATE KEY----- diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000..be2b428 --- /dev/null +++ b/prj.conf @@ -0,0 +1,192 @@ +#--------------------------------------------------------------------------------------------------- +# BOOTLOADER +#--------------------------------------------------------------------------------------------------- +# Enable FOTA over BLE +CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=y +CONFIG_NCS_BOOT_BANNER=n +CONFIG_BOOT_BANNER=n + + +#--------------------------------------------------------------------------------------------------- +# MCU +#--------------------------------------------------------------------------------------------------- +# Stack size +# 2048 does not work for fs_init () when we have a lot of files. +CONFIG_MAIN_STACK_SIZE=16384 + +CONFIG_REBOOT=y +CONFIG_POWEROFF=y + + +# DEBUG CONFIGS +CONFIG_DBG_STATS=n +CONFIG_ASSERT=y +CONFIG_ASSERT_LEVEL=2 +# Memory/etc configs +CONFIG_DEBUG=n +CONFIG_DEBUG_OPTIMIZATIONS=n + + +#--------------------------------------------------------------------------------------------------- +# PRINTING AND LOGGING +#--------------------------------------------------------------------------------------------------- + +# For printing floating point +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y +CONFIG_CBPRINTF_FP_SUPPORT=y + + +#--------------------------------------------------------------------------------------------------- +# PERIPHERALS +#--------------------------------------------------------------------------------------------------- +# RTC +CONFIG_RTC=y +CONFIG_COUNTER=y + +CONFIG_CLOCK_CONTROL_NRF=y + +# GPIO +CONFIG_GPIO=y + +# POWER MANAGEMENT +CONFIG_PM=y +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y + +# CRC (using our own in FS. Enabling for mcuboot) +CONFIG_CRC=y + +# ADC +CONFIG_ADC=y + +# SPI +CONFIG_SPI=y + +# I2C +CONFIG_I2C=y +CONFIG_I2C_TARGET=y +CONFIG_I2C_NRFX=y +#CONFIG_I2C_NRFX_TWIM=y +CONFIG_NRFX_TWIM1=y + +# UART +CONFIG_SERIAL=y +CONFIG_UART_ASYNC_API=y + +# PWM +CONFIG_PWM=y + +# Watchdog +CONFIG_WATCHDOG=y +CONFIG_WDT_DISABLE_AT_BOOT=y + +# Bluetooth +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_RX_STACK_SIZE=4096 +# Bonding / pairing +CONFIG_BT_SETTINGS=y +CONFIG_BT_DEVICE_NAME="Cyber Scope" +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=16 +CONFIG_BT_SMP=y +CONFIG_BT_BONDABLE=y +CONFIG_BT_MAX_PAIRED=5 +CONFIG_BT_KEYS_OVERWRITE_OLDEST=y +CONFIG_BT_FIXED_PASSKEY=n +CONFIG_BT_GATT_CACHING=n +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_L2CAP_TX_MTU=515 +CONFIG_BT_BUF_ACL_RX_SIZE=519 + +# Zephyr's BLE stack automatically persist service changed (SC) and client characteristic configuration (CCC). +# This means every subscription on the phone side will trigger a change, and therefore, a flash write. +# Use lazy loading (won't write unless actually accessed) and only write when bt_gatt_store_ccc () is called explicitly to prevent flash wear. +CONFIG_BT_SETTINGS_CCC_LAZY_LOADING=y +CONFIG_BT_SETTINGS_USE_PRINTK=n +CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE=n + + + +CONFIG_BT_SMP_APP_PAIRING_ACCEPT=y +CONFIG_BT_SMP_SC_ONLY=n +CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY=n +CONFIG_BT_SMP_SC_PAIR_ONLY=n +CONFIG_BT_SMP_ENFORCE_MITM=n +CONFIG_BT_SMP_DISABLE_LEGACY_JW_PASSKEY=n +CONFIG_BT_CTLR_LE_ENC=y +CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE=y + +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 + +# Settings subsystem (required for bond storage) +CONFIG_SETTINGS=y +CONFIG_SETTINGS_CUSTOM=y + +# TODO: review. +# CONFIG_HW_STACK_PROTECTION=n + +# For use with ws2812 LEDs +CONFIG_LED_STRIP=y + +# Enable Timing ralated functions +CONFIG_TIMING_FUNCTIONS=y + +# Thread Analyze Options +# Enable the thread analyzer module +#CONFIG_THREAD_ANALYZER=y +# Allow the analyzer to print stats to the console/log output +#CONFIG_THREAD_ANALYZER_USE_PRINTK=y +#Run periodic thread analysis in a thread +#CONFIG_THREAD_ANALYZER_AUTO=y +#Thread analysis interval +#CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=30 +# Give threads names for easier identification +#CONFIG_THREAD_NAME=y +# Also track stack space usage, which is very useful for debugging +#CONFIG_THREAD_STACK_INFO=y + + +#--------------------------------------------------------------------------------------------------- +# LITTLE FS +#--------------------------------------------------------------------------------------------------- +# CONFIG_SPI_NOR=y +# CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 + +# CONFIG_FLASH=y +# CONFIG_FLASH_MAP=y + +# CONFIG_FILE_SYSTEM=y +# CONFIG_FILE_SYSTEM_LITTLEFS=y + + +#--------------------------------------------------------------------------------------------------- +# TODO : EXPLORE +#--------------------------------------------------------------------------------------------------- +##### PRINTING | LOGGING ##### +# Print configs +# CONFIG_PRINTK=n +# CONFIG_USE_SEGGER_RTT=y +# CONFIG_RTT_CONSOLE=n +# CONFIG_CONSOLE=y +# CONFIG_UART_CONSOLE=y +# Logging +# CONFIG_LOG=n +# CONFIG_LOG_MAX_LEVEL=4 +# CONFIG_LOG_BACKEND_RTT=y +# CONFIG_LOG_BUFFER_SIZE=1024 +# CONFIG_LOG_STRDUP_BUF_COUNT=128 + +# Clock Configs +# CONFIG_CLOCK_CONTROL=y +# CONFIG_CLOCK_CONTROL_NRF=y +# CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y +# CONFIG_NRF_RTC_TIMER=y + +# # Power +# CONFIG_SYS_POWER_MANAGEMENT=y +# CONFIG_DEVICE_POWER_MANAGEMENT=y +# CONFIG_DEVICE_IDLE_PM=y +# CONFIG_BOARD_ENABLE_DCDC=y + diff --git a/releases/20251216_v1_0_0.zip b/releases/20251216_v1_0_0.zip new file mode 100644 index 0000000000000000000000000000000000000000..31d6a74ed30884c0a7360cc3e953d5ed83509214 GIT binary patch literal 354619 zcmeFZ3v|;}_CI>Qk38CjHf;ez%Ohz3O9Pq~R0KxSqzUS<7&OZC>v-dvx?34L0&qLD-Xkyj2f9X}xK6 z@gt8t^x#d)iXZ#M2OT@3usk94MT%(hr(co(|MUN83Xt)f{-t)Fy!0JUs_^^M=cjzY z(ob{_ma}yBwRH1Up1fld3H$v|&zhfKRo5PVsDjQ&#oncGdMh*Y#{~?$y_N zpS$`(^LIY^L)I=YtN1MUV4D>IPDplBAJIE=xv9=vGD+#VMG~f}rVf+21eK3759@T5 z^B(qlbS7s;3k=G$PA1uWEAdpw#Aa^F4`hq^0h?lbK#`#K)CBm1F`l~vL~~Do*OUhy zTv{G@Xep7ubma%`zWa3Wo_jv_Ly4|Ws;vC z;4uVvsA|6m&2J-vTJ@aHq2ot zV@S_y&TF#Nm-x_7&9RV%=Xy1=p_dP7T9Lz_^&AW7H5}gcy_&v@p6q~ztFe5Qvfsm@ zw+S|L%gTT#j+rs?yk<)LMKLr~Jb@PVy`qdd9hQJV483B_}tO3%7ZS0Xos`^8;;HlI2Vo6!m0ALm=)W%Dh1Mo3!J+ZqrOMtg{X z=-J5cUaIg}$eiqSPd2#5+%mMwCC>10U9LoP3#0Wcc_2Hx)5(G2#N5K#Pl>j}`WWen zlXz)jXhVUAoA3Y6$aPP~YsLpej z%*7uFMa08hr2N4y%j4q72LgA+KbkECl}c;i50E-nqqtAYHo&V#n^S`qNy+{&Pw3G{U zG<0jiKRrYb3THdPp+wZrxCOkNdYzXQ1S@rrpB3$_O+Z==fYt_2Il;MCtwAOCtAH&Y zD=Kix$=Zl=IqHe1r(m3ZyTs6E__Zf|x#$?b^J)|o4yd;zfq1tEE`{1@F`TR(ejr=PX3wVd`54_;-=Gt5wy+YVM=OnxSm_9?Lrntj!))K0>|=EmKCG2n zM@AuHf2f=NlXV8FN8idgXUqRJPrX481QjCsk7w(T$zvi{Ly`-V^JUjPvjWM9K-q>Ir6BF%~PB&J3yim2o#=o*w6AHn{7n@BX`fd`rf-uK><<@_-cMCfxGb=mG zRyPKhEr>szANbg4gY{wy%wK4Pti_nv+RDW|n0X&)Xuh6cG*G|q3Jk-T%N7-%(1eTW_tIBI6fO%NgLFV+V@Qis_e&fGb~0;`IfmhNHozCBsr z>+1VmNUdoIYIp)`=PeHj9-F_ws2U#9E{mJqbST zjU@&z!&LM#NVP{TIdf|QT*63b5@#;7dcFu7Bpvi-^N|!Ht7dpeSCoE?2Y#R?+FLdp zwb5b`Cs}zVqo$FyWa)GPbMS~Dg4Up#joA?o!0KVT-zM+0aT4R8kw55v1{(`l%wN<# z0A)e1L2KZF0M;f}2ebZc@?o3WQx|eAtm`7ehrOHH)zj*N#};ZYz+&qw$|3j$*YM1? z1lP3Ng8UD>1}hNu;b8gvz&vItTrUq?bTWJ+nvntTg3+)Pd<(Zhn$+T1=}m#{&1o#W zj*ZVS+6!EZuoASkKtbZZFqf$TonY%G+~Ju+vID0|mk>*UBg;bNe1FEMvyDHSQ&1W- zM~!)E&m6WQU>OwX%cmXloD4+-OW@diXD;I~Mh|YEEJWK0xiPKB%T`J)^069_KZhi;{OLW=wHj`V zUvq7=Iu9Fftk(=EohK9DXMGqs8SI_meFL*2^VQN;r19hr1s`b=IG>eI@UgmrFAr2w zOX?r${kZl9Q*2HCXF3;q_Xh=@QF;!aAF$|e1D#z9ZGpr@TR@x4XuQ!n)w6_HL%-&s z2Q?e5V?9e!*vJ}#1qow4b-@DAZzHcRQ($@0MrSD-DY~`IZ-g^MH^MBV>7EtV`>pKy;YC&KjG#6Wqi#!cMfmC}w zyq9 z1aEEucB7)Y=8BnYy>gV)r6yxHe^nR6@3$!to%!g|k3R@;>hF9QDaU9{nzmiJsG8Jv zQH8PI#~*ql%ooJ&I2h*R{NW;PTX&NS1dS@^X=PAMzei z*Aw^?Cbfm~pR12_=SLWfI;2-uUx|>&htFLpSA+IjNYmCUyY8zC3k$d%bzzaa9DY!L zIsAy0-&Pk^Ew~(Z@Lz==9EM-T<#6#hVe5gg>M&B{FNc@k`&IanxUa$v6VgjAhf7w7 zH3!0K?N{OA`FOKm4nGD;KVrap!B^qsg?M*=6@H9UYMaNLmfwi7 z+x`_U$wk?H--RE0%jl z%BMm^RTd)ZH6cirP-I#G`~gdJ^RgkmP&-KAQR0zcp`N{1@e{1S4H#Z!xm>{1wjqB0G4x6HiP;gsKQ7H zMnYnr4vKLHv)$SIvm3KZP|%ByN2A4o*=vVB_Sf*4bRLyA)>f9c=5^mf|{rdA6!50^|Mut zRa=8Kxi8ZFVx;8?Nr-gUMud9EfQ@WbvGsI)y)7L0Iehsw_^1C3{^xtn*dO+se00zG zbN$KoY;4ar>}28(d%mP|Ki%^s!q#J&lB!%5!<-xP{_Nm8TbCu<{$gi2#MAF@2w!c# z*uYID89hERq!m`=2vjpMHf>5Xckp2DmwsL%#@O-S`in-3?_O-Ux3{9BKHPql4-Y41 zGaxl)sm-q*PW0qk(kjBqrGnm1d3rPD^yk-ZrbO?McCTHs_8ltf-=Vyjm-G5`?tdBt zeWbg7a5gYDR)11kN46*pkxH9W^J-~^d0{hW;0|)Nk?!`0zB#{BC}nkaMvRW3`JdDI zF4bf*$tbG?qg~_CW6wk6q%awO9~Gy^xh>MJT<%ukrfwgRpAHM7V=m|&x||_yk}h7* z+c`a74{ljU^u7MUzGq{zssRqSHDTWv;%XGqoEYw4q`N7iYhEeG5ux7Pk}>bqwf42f zr@ve)WD4du`9eQ8TE4*TGOk&+MldJm=w*SBNnDK(=0dQNfRC$>+cTv3xb@O$T~6H^ zuI9M=i)TjUTwXIGCox zS8sXu+2MEIFngk%$l;#hd1UPIHj?wQeSDfhN*`54Gc2+4BLkdqMd{n6sm2wnEbXb2 zSFGcWrqyGN!F6h*>GAJ7v{kDP6Zzi$NzKa+r%OKnN_iOh%&(XJdX1jN7YJcA)i#mz z89u|j?U+x&G{t+rn`GSPwlrlqR%kh@_J2jrTVu;*2ZZH|(yRXrD9jKGqv;5s+dvd;COxlah4m zO^1EjIY83CyD(LXmG2BcOHCwRek8mTzXpky%fgZFX9oo1vuH;zU5fT~KH8VINqueH z3Lh^&6W)#*vGVM2oP4OC=rJy$ZDD*r->u3Ma0;`Kim4VnwbmXV^Dr=)GCj2@K}obt zDz+vpQ<`}>CalO)%w25POWGEOYxpNTy!AZ!8LTP_oAMuFKZ(LBqV0}EyKnaX*doJA z+jPB5BR@Lu-^-T$TN$6nJ5uay1jI81?QnUi4&AjhpQm$>sXBPvoVl!QQd_re!d;@I z8kIi#HoG8)diZpOr1FiOqX)IqBk7XBR%(TqKGiq5Ol8vf?!da5Ps^G+%Cgo#@&$OK zLwbi2tjAyvNx$G-Xi$TbD*6?Gdj>NY@McP^tw$pDUF*uZQ>zT$`APa8z1v|iZl`>W zMIv%K;6Czx5V|Ab55C!Wx8wa|Uz1;*{*jN5Vv-bT_D==Asyt9UGy7^}71m6WVh&0B zxQ(QZF&uB>lh}B1T&#Zva5K|2XSu0yc}F{}Nkkq)7Sc4cu@@eK=uRw>uJl@_vN9o$ zm45GM^tfJ^ldwZ_%yFbh!ZAzdrnD4$wya%c?Tjp2Y`@+IFY$mWa0thpxvamRzN>)) zH&q$!TL`WfSPy#~KlaZ0;qr9RwklCqi+yWtL?h?+2$(5R65P9T*=)12xN5#yZaUr( zFTT=%b3fseXO1uCZ^_6p<-FLC+`GPmn{KUc40m@VhgG&c4ZE%4+*(f>P9Cx>$#{=* z$*_TmD-xMITA8745+t=Gp3U#{l@7xkx%*<~Zng5~EbVoPSN)02f_sEr-C}a5I!M~_ zHqMYTD#b3EyZpV8NV&EtF^>eZ#gww54UOUU4k64NxoIgm4PDNa{F-zRF{b2Hlr@Aq z{ z??{qW9PiEV+*R7)U+P$FPs`!$$J>O&-S+znwJjXOi!65b_*px!6SK!besHbva=+%o z5C>18rF~YkJzL*as&su2QYWzSE8xh?JYC4S~VVSmG! z=BlK^QT5swhrU#;X>}Raslxo^)%K`f^IXF(_ttquWt{0DuV7-hJrc#OD}1_*%i3*a z<7_JJ@{falS5VFL(Gs8bl5`JEgBNv?y9PU^b!8ufA_+J9TvV)i!8*h9Sp#P*82PPF zaF2KL<1JSs#rM$h&fgy4?&v)!8jGAnkKUiVhfZw8nO%a?`wA8J9h)V3UZH&JQ#qPY zWCr|=3E!Q+9r{mojKv?>2;WVPVZ8NNw&ZdhFg_P4Mh~k<2&ekaG+%N|@6DM!&iKKIq=+#RtSDdK% zM)SV1rm);W!pRE4OQPHq~+MQ$hIq$n;jllxIzHd+D2!_#cM?7YWyHagar zXy01&J=oLIpY)_fPpg`-ma*R0+|wp)6j~)+hn@v!g_P+1rrNk+VL&M-W<1eS7J|n$gk)$aWR1!Wt>HxiH$oj@|`QjKKn#UKp7xRW`&J z%t$YZ429(eXc8wMpi}L-=5#y9bjKDkXQF+oW1*BS#iYf`B$DpPLn>2>mgdLG-5(A1 z?QDvdM6@&~qJUp2R-P3R^D=Y>N#_`x$-N&8!a;UA;^c=vVEB&7pcrAY6n}Ob5wg-` zL1@J4ope%n=kMJ6R9f!?h8T=_MsLKFAF%S`xya<*)`OlD>5BiEj1N4|qR(Dy)eeumB+ zTDH(Z44*bsT7|Q3NBa|dbrP4UC`*Th-0mfYbxv+<(uN#oj$>Wn$A#O}B&6pQqYRWc zy=0w5#k`?8h}JRM3I*p>%mZt8QX949mANDhEF4kE%VE_i_vh}L@=ik%jN0)25e}>U z9rYW9{z5UN7v@TaQDP{a7%9JGmo=M;ri^HdMTJ}|x%sj;VyttLn{*z2eBFrDUkfI| z$KO2C!`*tdp>711mhyxW5h-HuStT)!rAg*hjymV3*f$)cqS=B~L@S74Xj-9jpDtET z?P=eyXv8U zpN%Owk4qy4W-;xyAEzYgI8_Vy9d?|G<5LP5nRvKaCsW4rfecbUe70g2MLc6WFHmYOm(KnyBcoT11T)AYq zE|myorOP)V8#)jFlCz&EeNsnR>0f-OI@z4!B@v9c}nF+847it+2#rn8&T) zOw1qsKyL+Q`tU$D>-SN*gI1cSyNRKSR%%7$q(`dgv#wWH#$9Vu0XjTyPHyH=mJ<09 z7v8(DEE#K|3Z;xXjc!l`8z)e*1~kFhKc}vx#RRl+#mYw_Bu$XWHvOErk_B}76TQ_P zwQg;Z?*5N>;{`vt`KtFcb(ziBy^?epHtH0})qnZ61HVhk;~hlHor!cm6)7q?O{?{( zWynd+Xg)+Y>66_Ju*R0eEZekI6?3L;pjTv|fLm&tNCn*56rG&%gUDxONAfq1F5rF@s`abF5o% z-`H$vUzy0-eKWEX<%-Zm%_jU-(Rxc2or1@W=N3E-cp5Dwl@qHbuJ{C0TE6VPBJfyP zAs?dKdrF8xPU^ES2Osvg7PItrdH{J-`b;Zo>3f-RZiT!6d$3~tUIPc-boKoTa$F(r z>^Bw%A1NySgdQ?)r%#zzH>a&!<~~F}(jKDK;C-d<5Pe)5mvexAWaf;?_62u|U9Qz) zSM^gm$Ni2h*y!rXN36r8Zr^FD&CHwo2`x6Crb9Cq+F8FZ=xeFNzCSdM<0c5_YUvj3 zxCwS&Exk9(;6o9ztLN_1-NP}tmz&B`Bn4-vqnDJ z&+@j@?e;@7$0gz{MlfjPt-bNhqS@78nT4}xtm5tF2YOw!iZ;8d=xo;>_vyAf9zi{v zm2xqkwCNtHqK=wa`SF24ZX7+wD$SNed3}TfcX#y@qshL){x*83Hm`6Qt4-J?9Wa|p z>sH3OA*=0*=zdH;jmyxSHBQiYYw4|-$yl|CzCEfvXSk@8f6;H8pui|^HPq5VbF$?- zzi^YtN%>fjB8{5 z1gRbLSu>NN&ufit$WN^nxK}`HRGIg<$@nT-W#{B`VJ%u1m%YiKDY1R}>b`$(W8)8W ztD+mt7s4=P&_ zleTQrcIq}Ksf088SDwU<@i$np&zxm4n$t5G{iGK%8v8twQ0g1cu7%Ii zN@A=c<09Q(hPV0eEF+V)c#WIU_Iy@qtWwC!shGZ3E5bq(X(jTGGe6zDmku$T zngv&dbKgo1lJ&Ws78e&H7?SjW?CpeIp?(o{mh-tc)*;2piWz=w({|>Hv zb!BVRmO@NuKR8Cq^winj{VNlz55O|nT9z`Gt?CBzRwuKnX7~?doUJR_n138k62?Q) z-|$)5I}GQ(j+@QN3B4r!w|MV2_`bHZ$7O!@wL&%`?sP=|U;UW3=$xp`xA~c!m=1Y- z2c9W-Hn$P7;X6Wl@Y@eNGA>=+B%ZUhH)Sdzp-zUmyvbBc)GdF#S3#Ie+gLA3!z8|T zgH%I{>*M6Teaxn;=fJ~}?R1M-s8!}v$((SwK~a0pk?piIQ!tJ9344Z^jL@-nXXVWr z10C9zd4gWatSdf5|7tiv|CM=&TC&>7_HM2qN5?8+&RIAC&Vr@ciHGS;->9*&MxzdTY7a*6#+P~&8 zt1+S=rn|jEo)SUNLF*QXD2ro#>A z8s;H?8#I3UKEY%G{{JetCnrP1_MFC8FdlASP>4N^F=R^cXoh2ZhsczkrnKM$ zrQEF4Ole>75czxhRt`p8z~E-(e4Bc~X37CQ3TCA$!oq8u@dWMTDrqcqOK0Zi^zr>n zg1iuwAgN?Hw}RZp@q4!W)8T`tA0WY4#S#C$;!J6Xe4Gxp+81s0c*-wrXZ>W{1wV&a z`Z#&_fJ@r9JX5md43XcZnG(Z@VRMkG-ef(h4=Uu3dQ^Kz?di4`xJzwnqgtNUUlH}G zKG}Pk=48FToLLv6a}=_ScbfQmXuGOBg>)G$`JHQ5=jOa>u7@wKZu1+#IF&hbW{QCO z7OY0BY>n0rr=*!1ySA49nTll5NlW`5vd!SH{k?U|85J`noQ24RKf!DXWq2{4?ybET zS?22uu9TQ8@@5ymhnR@G0X9o9QOE~-?*Ij9>GnI=ykAW{P0g!Z(j7Uh^l*wo-qp)6 zB<=p&)WG)ra7XuT*RW;dU~`ALFqkv>u#i}{`KOqF-NP9fzKrKyFJ9T%xq394tIVq@ z=do*j1(JBQ1bStar+z+y*#yjUb)v0$)d~7(VI|eBhrI{u`R|yS4-yYh%eK>P)y7wY zF)<0f8+tyT#rX46594Uv`cH8JqRSHYD07CGPSBytFhY5!i;Z?WMhQ;Dc;9ioTFiO} zzdQew5+~o+?`k0FM1IvZbdTWHxj6mXtr!t^Th2B=;0$$umN|?saZkzvHb%o;Imd0g z7_BnRlesXN{OV>#BR;I2%mr{ox(~nBUT6y z-2TvZAvI-sbe6~5t9KJMcr(b0rz%OQW@=I34lalyL+d41r-$wGbisSy#7gJ=ianK7orM(?wXPUlm_L}EUk-=- zqh`nzRjONa<5YMsydZ)M{4<<9yV84 zkp(JwPp=;IjLX95H6+3p*rmj!=`${Qb=AyYyvusYI{`i1+b=@me-7PfijLPo87>OM zr*Ve!t426?ud=plSEa0rbiW5*1S{k6&aPs64wi;Cl-~)9dFLJLtr?zOYl)tD9n>wX zjQJcyNjZVEpK!5rPI2ucPyME%g?O<7yw_I1oRiQjSR=ErewZ%Ih9)VliIcgW>v~h_ zi>??*t<&_=0+*!BnJsxBx2DiZDYM~0m`blp*+Cg=N~sGy&L3`Apguia-Sw$sZ`Nwt z80fvmFzv8UzF)mRxKQ}5l|L1m^P-hIr;$JEXBt8yH(=LyUAs)p+p`wD%NN0~@L?j^ z#$m1^-3fhD-In%07n=3`*#C>5ZD*#4b4-=I3Nox(e}F2}i17e)btR3-ih3O?1fvU3 z)Gx}@!Y4514u4g#7c2F1`iyI@wc7J-$}Yq@?DCLtsm;QQy^xusuqw<1V=ax7XAh`y zc)d!NBHyNt3w-@V70MM7Z%CI^a!rIZ-<)G7B2GGF%(dO%`iy~oo2Q0Xi1n-0zowJG7<944(jBFca61$S;xNmwQGu?pvNhST&k z>^UA%=`=ao_WTB{53>vWbhPb-4M%Cts#rOh>Oqm%Qbv(^*oWQJuay54QO#j1DGPJ6 zdmtU>2;Uce2Pw3rH0Y1|F+Ih*If8xby6Ze7H>oslbiHLAns+yL)Iy-fIOg1q@H$$r zzLn-mVdtbIZMb2$G-m8`0i|MDK%wAn(Rwcl?=;K`+^hT=zufJ!0t*lWx)4!McZ)$E zR)zj6!~pdATO%`Z7Ra3@W?R%?V1m$m#cH(=ulzLNDy+*pp+thWSe3I94b+)kAirq104xYH) z7DTZ87I0&v7R0XjU2h|8l8ozGu%ZWbVhbe3!`8 zIK4KIdh=&Kw!Vw5m9aB4u2A+HH`H|2IWzpnbnyswa_D7=U{X5&yd z#mUQih+GJH`Yle~_w;dcGv(x#2q*uIs`FCQ@;eWdE_S`-Qsyb=>~@i~5%%-z7q3sL zSp2veesI>tfmHa=_*5e1WnZ1Q0H?fZsc9*XN4kBG2dr#TTO@nRop0mR)I!ZOV`Ygd zaen-I*z`;#ooc>0@8RdPvOXczG0O8+bup~?#oE*~wFkdgF-!HSZuqUV`s!V|?>_VW zGfjv|L@x{L3Wqln`T4L?dg=&~pAD;|hZ`3)3UBHq#P`^-w{*hd$4N_b&)e0@tBcFH zt-|C9Wn`<6wWnblJEi3i#X|8lC2!jC;d7d)aLM`z%xem1s6@X>TPDWrHvh%%gruSP6{M!vhp;yz3{?6k{G!nvfe zdYsV9EvzK3N8PL$dleRFto#(u%PO3M@eGVEw>4ED{zM_goE1%qb6gtVD|95!O|~k0 zNn=x5Czv?zbW@Kny}BM}(_JTvVUIsatDCt!wKUzp!|%Dg`K_`FI>8Wumo(krK>36) zqoTQmAKUBKjHT#XPo|1o^{b&&fvpYAS!?zFDCg(2&xTJ>Jd3yi{r#@pE@y61)X&9u zmEpPv*xFo!xUxLN0F_zGs>-mV6mW}*6J=iHe2F;4`>>fz>h%(n+A~zbDb3h%kfbs4 zMMU6yk9m%f|AAD%3tgyx`SDAS?^^%0pO}Kaq0%TR0Q?volAB018=Z>k+&uVzug3}b z?ro6y7aF$FdOgYL@FmOP1lh~&s-e^1ft@xs2wi=V*+wEUb^B`QDf1qc(iefIn3l;X zaiI6SpPzFY(H#G)_YC@3^ASfNBg?+OxeB|>Ard%lT)M<$Lp^x#9Y~qIJsSO9{F02& z1?=DTTUFos&MRl0enpA%0)FZllJT`qr@DN)sBDBP@w8#hmu0PMtN|{qkbPTUU4uJ% z2K`!mXFqTCo*k3%*|}wFxoOIbKcC&dR>=6{*<|eMc^}WD^Vu)gav2|;O>Ex(jF9nr zlxVrMUytx|8Hapnzu_|6XWh+-`O{5-IF$$wro26(0YT99>|dctTQpYcy$sw(F6muu?Qu*jCQwfMH-L^1#5Sx*CJ zmfQev0xRbTxSPQq1nm6A;ZX@6CqFP? z28D|wi1Ko_KF-qb4ixE)dZ-pgFUOU(9)^q5y%BAls&Uhz9G>x7L|As88i=iTwj}9U{M1+QVen;ww90KQTjsV_r)0#OTaRz$IdLv= zXzhEdYon`*tv1H{EXHZK{YRM~l+}e|`NWL99)2!X#^cxF)J-f7sfG%28vG^v+}((9 zVe4{2KOyQS&;a)${@Ba>+8n%Ld~-F*v`umIn4gEw)T=E+_os3 zYEG=Y87a}JxK`uYBZF-|4)|yAl;B}LbRIVa2K!RYBXWG?G*$D6t3v#n6Pi^=8O}qq z5MzV>82nKFNBGlJ1K5Hv&PeckC+e8{*pBNu{4up+4rgS$E&)+e8<*;A)ps)+NgM6^ zR2xCxEG^*7=4c1eTL`=5IGBg?19=Hm&n3nz%8w?;XKtkmE2y7nfyjhu^J|K9z`5L3p{@iC`RjTm9V+_EyrhC@qFBnnOvuuaZ8U zDS~eAJLBZ_lwm$4Qwtlx{X33W9O^kj^**{hvxeGXC6?f~5WkORlJ-Jvocti-2L1>* z5&Mb?nx@ANWYzA<^OY%8&fHgkk0NjPruA0N^V3o`c2rDosb9X@Z@(^J*6}zhxt{>VdAXQ?O&8OUknn>p6cIWWILfp*WqE-&YKZ+y3S`T zXC@k z3-RH^$^UFnB1U9LP~Z}&eg-jxe5l9kvzF&5gOXs%Nus)ED(L>wBznU$Np$$c3R+iM zK@S)>s-MSAeS5^whDdq^eJT5-FXo4NVCT}+*olygd~e3wZS>Vludar^lxV=7%T=0= zHN2h*@LVP4aFe&v{RLk;x6-O8{L)I7!Ju?SUY(=TG&R9Wg44x zbNH@HkfNIg9$KT2XDVjN7R5uQLl8N_#(yDl8L{>WW+OCFyjgrpG#10sgiStQCC}{J z3ms&1?u9-o!uKe1ol^}BBN*(u{_bab{|-IFcqlW5$%4gY>#fgvQgyX-E;qO?S=mqX ztv|_ELU!bL7M7;CD(G=T1wCP?qkl-OpvMf(Tvm_ELmp%#OT!P9yI$HL-5~7(7pxyp zBkoHn3EoKeKBS9$lMxfBG8LXxPty6um#rSDy2-)O>5oO3EoNqXXII0|) zMZhB*F3e6a^FK`D@IP>ikx{PRUs{J#O!fHu%Z)w4?bO&smA>QLk?EMB#v@7 z?*dP2qmf#CcwCMaXMTE1C3}h3&Eq}Yiou$7g96P+UsB^aC%4pM9{Iiyri0~ZK|Qiv&Xu#%M5OI z|G^fCMp;PA?iie}?);nQ?UstlDy%Nd+r8(E;0kL2&Po(LZtzFu<>pzqQN!KB&cSrP zURCTFrGX2pv&EoVyFlW)s!G@6r1yz|BdRH92J_}fJaS3~Cbr%zU51bK)k2)w#gR#^ zUPN5g)`I(AY9`yxyNRsn!z$zqIozIgbU*&FMmv%0UPtRbCYp8h0Eczw-cEOAaR@d47INd@9H^o^9dj;4w~bm28O_QxK1|G{s|vTRuhUW) zg;^Kn`)S;uet-uElpdr7MGT%wQX-;n82H5BgMGC-cJ>6xT1L9|I#qHiV$Ep_i>mn3*XU_(B4hVatEHYw7VI*gm}ff_ZtvWOx4TE9dFzXla~M`fah7Sh`T=nJ(3~jQHn}jHwGkEMuG!sMJkWi4K-*kN z-z&J}XST?Dndkicgn#(z>9Q;@U7EJ8m_;nII%{AbmLLBUC*;8{(WW}$+@xBU>&&f$ zErlBeJ;bcb8JaF$)P~9vgbS7iXYNqU#Y*`%#F@Jf>q*sAZX6O~*0u_}wrBhH_NdGb zckJBxWfrRq+I?ucI$BqSy3w0hVV%_LAF8Lg2k{%y4LyAXn8DVy!SP&Un^I4jIO zwKO$lk#$iv>H1=7#!=lGd6OyLlwqgS~PAv<)@KH40khi3|&N|@~ z_ZG{Wftw&Z%-k-5Xb-G9L#$lV%k4@-w5ecL;jYtQO;fKZ>6+IXOu*rme*R#rd~;aX zQ%8SafPUux+0Tvr)GtbZ@K2OZ_y_kFyMBGtHfYPRcOh@ruU#@vj&@iuGlTo@a`sgl1Avo`*U+eDW3?8Hy} z1glgnr?J~bopq%x{@A?pzh3W7*_d9keB*9- z3mcQRiQ&G)z;J)NZ>~LV748*Pt~xA?inCiCDefffwh`fE7aSw(oL$6CBO#CNX%>~K zbE=COehQu{`kC1Db<1cMpGfLuf)8?8{WWoqTzr)zf)2Asvo1P zA?Y*mj|xw~k5k@R^4_w;#aIW`SZ~gzr{e&V%@15O>Ug@V=p?@f^!GY z)D||+j9*Sfc_vxn*QUAiJ13PgzS$Cvg)N}9a1u8=(*5_37>qc(23X-tVa`Nzth~RO ztrynD&F^zW+Y~DaYmKOXbF}_SMD0iG&sG@`oBq*Rz_A_>nTT_qSp1g?Yxk{ayQw&f zOHHb%6e`yjR;)i}7piW@93NOM%spOdt2|bCd_7;t`kKwn<^)MT)JX5fzGb$hk>=Px z-_l5}^S8tAJrs9f8tFWg+^fC#-1EqZ#k-`fqgMS^{BuX5^g6!BOK(`~P_GUpV|;J1 zlYq2FI(L2}osaewIA1|~7VT@OJr1?+vh02CL!@U&@1mB)!uwve5?_W}{!qZ~KBJjS zYxT>~`_T7*25}qrjE}IexSp)(5}c!v@f#d2V^_JtrRw1GLqRqzBK25gbWq zK{}NqkEU?sd!*ywoBRanGT7-`0RJBHGVz=N{5|*`g0dF@W5bh)X9w~`c609`c7HGB zA(pz{=LPh|+W8p2ccQ)`PdRt@L&~`+594einmYdjY~s5dc>@oF&wGa>8|yhTE(2aT zXan6@E@Q+wews03IOG^Frwki6cI@O~w+tJ5d(P}(!|ohA_K`;(eSFxDAJ>MONCuJD zyDaSknFUx;-}LZhHV2o%!xx~>8{@cx=hou=TjgcRV6i3s~moh6OMs|2? zjw?9da>5^#`1^QcTyZkr>LirZFjnjVz zEcH??%}+Z;=O+!~unl)Z9^{FBTKaD63l>twhu7!q-}BgfbT`d%+*B+|@RB@*$Wd+x zJOXCiPvl{{&Kls}G()~VIACG7dF64DLfmxYS4FxH^Zrc%JHvN4iBY6gGcqJq^d`b- zMBaUZn+T_|$`{&CV`bMOc2{oU4r;LCZV4agI=EWc4e!z0!e4Ue3z{7I{78{LTXMm# z9?5jGVxF#qkd^U>Z}?~Ck>(ouc_uvh9BfL^B!>B3(tErkoaY?J z=+H!QvhbH<^zYm;dOk5?T#p*eK4bN#Hd!6p5HZ56>gn!elfs(_y}R9`)^j+Q*hWtx z(w=dH7xYO7&-uY~69xv;)ktR`9asG-eKZTD?JMDPaFL|!5*;{o-q;d}p9tlbJcHix^E zcc();cY~vjeukjXS@wKdi0Wnl#cZ$3S;-e{9|kP~$ok04I{#7)`PT{~BQCwyuzbzt~Zaos6-17esB- zP)R%^{5si<+e$^+7TXGLvaKjqw)A-0h8TBSyH2ruc4L?6^ceY_mM?(QRQP*Yq!MSs z`H`2!?(R~}LVVIG^Gm7`r`cIoY=0%aM4I4ZJV5_xZo*#Y0Btoh2%F3!qU4nRyejd` z4RgfMX%Zu^M-=`k^97aeG;1pv5Nhu|28%x2JsYus(-4u>=KI!?Ba;Wfp@t{e;017m65SSoi4X+^iz%t4Z2h=J?^3^c!JzVr`RoyYIro?wI=z7 zIyt@EqQhV6s)B!rzmMK}^q*9;|8CaNj$HCW_C(w@5T#asleHB8rN^Hf>LhwU?qnWE zE0ZmQt^Cf)`I4O%{I6Np;oo<;$;r-t`LUnEy2k%V^SI;dEi%joEshQ7+(O=nRg3H? z&QB2)GYA8pY;?EB;NgQ_%f`UcliE(%V&wxAdre-26X!_x_Qyhq9a4Njdt(nW(N-n#{!Th7i&Xkms4cQ}4k_uQ~Bux?gm{XuFr98HO@N9Mv z&sb^wwR>Jd^q!Xuce5&RFN=*M6XW1#4~M5vDgP1UW$)|j`L~5OfyB+W{kqn@P^mo= zixXNOV%^-hd0@5s=#(_Yob`5_i%-+wmO!NY=sT}#mLY1njN=eP3-KglCf9maN%DO z2K$-XbbNFCo!sQ(Rc}8dN+vh{-xB9O@dL#8_T$)jly0YM%(&HJVB9*@qLy#{py-)o z_m`V@TRTq@Lx-Q?%FdsRYw&*yUgH|xr0~@}lZtlcp`8G3?XXoF>HZ6JAA@x^O^Cv( zqOg$CS7&5T0%z^$-`C8I8?1RVYW}UCOq%9xEmp}-;LO_Wh1C-_E@vxd5B_(+;P|pQ zk;W|HLj}T>m~C{vJ|=p<|B_daTaz(zE+QZPfgW0mIcOq=75)?a>agrT`HgGRmJ1=5 zZ9y?NT_GoM3#ie&D_0?X>}Qxdf$5VTw%=j6s&F>rEf$(%nsJxhle{J=S~ICwn9geH zav2QsHwY7|>J_-l`n0!(mYEe3E;R5yYxUn%_A|uvD<5~`=k!X=OEk%T7$>|1RGnT8 zKkQI1{}$H8|EBHD7G)SZoo}Kp$kje!dOKmP18`CLRnl8P*aLdSY{># z9EzY#SrnDQLeYVCaDeIziVG>$u_CszIO?dAu*|gJ!nhD=W}LJXa=Rt(_ql0-nRkAF zynnp#xxFX%o_m((Jm);mdCs%2!9eQ(Gxf zHSrQ3g>lnh9mgAb@`7}~NKX}=3we5@_J0$i=Yr*CUX4@@jF}Q-A#rPl-Fk|<2)_^a zJN+zV{3xGMr;l*&ZeCfHnrt)K@>gzKq*9*KHow^H+gR&5X41GJ*^zhKjMnRA!A%W! zRfCd!-w53U#_Kg$$jbrN#clPfE9bch&&c=yn3Qfkq9z#=r_c(Jq^lscT7h}zERRtde;T}90aza}3PtSC_c15CPr}Uzzk@&@2$GyYgQ`)$Han9sL$^S0^T2*OaeG-hDAp}+?Ca@zS*jD0x4$g?PF#e)FM)43qTM3x z7j;#H53tH(7aheafwR8}p54-YBD8KeW2V+~PMRm4f<4;X(!G%6*Hvzjo)*>Vd?&9E zI=69vl|ks-%}#Znf{oE($dQE3r-5-#_Zxw6_1T1R?R|`nO85ryTaA=-ZzS>U5#=6E58?bbgF}=t+0?dY?RV9_!mJeLUFyj}6Ia{|9L0j-D*LQYVaG zE>6`X%t4cy7MJaYYJZNCvK+^MyH__5C*sm$*lLLLC^*YXybd>2#R}lAM3hi$$-OrrQ}k?3>A^A9H;%yiw_C~+ z%g|@Fe;@4RIe%Oauh*{6ssOe2zkrshnt!xGD^Dey2iFu*Lf7~bep8JGxyGC+85vVD zriA}xM57)p>M^;9wy6C}Ladg`wue6S3Y`n_WC1v)`!(HOrQG&+;M;+BDD^#%U5%H= z3U)#3S?t^-Bj;a$5qvHGs`n;6D%hsU0(QWQVV;rIh63|jqGli7sE2OytGz~G{_q~^ z!yA%qn+CLu^Jv#|egnpkgAEo7ij{Yq@D28=+P3APK<*&j(Lf#+rY=km>wl@2C`(bl z!xg3xah@T%a0Z+MQLOL^KSaG&y-zgcpP(TH@~pYLm+AoBE8^Fa-qV8SMEF?}V0qQl z!}~HjMv7BN@HEf}|kdg-FZvx|Zf&Qm=jm>GSny176~#sa#pMw$vfh zms1%m!&?Q&MFjaT@g#(AFKEMaDW)IJqoAX%eB$Vsi;XNGx1Idssc|x-e)8y_#z4k2 zfzA3^8-tOlk?v>Ie&qn29Zqrw14nxn9(!cn7Wgm(C*cSN9_v*=P9*e2C!~3tpCRWp>rxLt^^K9Lg$^V%0lVoI4c;1ld*Az zlg%->r_I$@C14gWhV}KiE5nA7UVR;SZdYe$MucZ;v%$_Af&r=L$Xa<1p62yN`X7;y zQ(psKo9F*^menabI9RoW0;Brut|0uQ5`-5>F}FaN;EPpq(%*qyU&mJs;!DPOmT`?` znFmFd`P&E2?6~*dd!axa#{{s>z=uf)`fa$MLO<*n)3!eSpe1klwu>t5c}5>UCv633 z-iP#8*D5+ARd!2XfDi6(W`6gINpi1%&$H=}0!L!Do`U}7zugU$SJ$4C;B&i7u#(+h zoinT-LHM{to=-ly@|sAZm{fi`7gOS|o~N^$qzdQNzC}k!;EJpu-X$>?)>>C zsaA8@8-6n^16n{@dcxxG8O?>k=OZ(|^f2R?v=dkEE~GcRHRlH3#Ap1~!)UL0R{_om zfH4bQ3?FfcU7uEDi@kwRNQeChyGP-FGQ{rTwQB$90o*IeN|ASH!Ee}m*}cjl9a3Kn z3oE&588`Hhi5!sA z>XA_Roi(`C1g|k?FkeHg#g>wjp4XBi3ZOsX^I`|ie_Onn;KNy*GeVyqZQO`;j@nB8 ziN|V3rEB18Dyl6eJ*GW2FCsk>w2ARYMMOkI;eUM8{!Ts5W$D|oE+3~AoWZ$Ag#XoJ z@p3kQx~p;8$Fk>2IH$^^-eOd&MirQpCTz zsjS4A)SgPTC*B&dp0n(c6WT0|VdI-2S<+yKt!t1z7D*=c8NLp~ zCAv8NiJi@P%{BHT(n8Iq=Xb&~xIb|4g9OY;_4!(qSi`6W{jmXp@);_%zrYUZ6=WNR zxUeNljO9KL{FVEk?>97OV08~$RjBp4`El zC8jIjO}@^jxiv31RDRTYlYLYc+A0c`=(d=w==KKK7Dr^Lfp@GwLZwng;J+q9pEVx4 zB+YRh6fR_30oMKUJ#8lIo}Ag`p}x-^C`T)w7j>B(kYs-+>adsDj63!FI|K1)n{ZFY ziarpi?`!j>Xa3DY@*pPbjC;>ax7P{03p7=i&`Gw@-w6K=V^!16(_EA>$5YzOc@Z8l$DUk~38v$U7NvwAwEYG}s|0ol9Tr5OXt4?irc0++t=V;k|eeeuI=5Z2RQQ zsh-I)L?~a4sLJG8INj36s6_p?uT@je>9;LZF`fIJdsPYNzq5Z`88exz5##Z9zf*<3 zzjqq2;yl#Pqn!+5T-{r=XQqDJ1?57yt|h_`Cr@*aZ&OX4>W;xh?Uryts=YLFGW?FF zacTM|QqW8KGTaJdJ0i`!%8TraeStp*+4?Nk7+A6&u(NxDf#1VUfvDy1$rn6H^0+m* zwaZ7M_jEeD>@9`m@8027%zNuqT%F#7821`cnTfbDfbab{&sdJ@=eQp6h9)aL2kp6_ z2RpFSEa4skW}4^4dDUr6xIUHF4O$bo0Vk^A3-*D@EK@O&W#W;Zf~#i|EAxY|l)No5 z&REL{-xElU!6Lj z-^O>hmgi#CuYn~rlT{3Fb93AWpkupJ+J$|n;2Etj13#+qi1$G7r&SSL7f?($L#!?S6 zc{VT;5Q2Vw5&Wy9XCIZvFoX2tY?~!f%WBC_!NUW$Ut+aRX_uxykllX-`wXNFEI4Uu zHLd6x#lIS(C&jlB9x+|_$#&Jz>^<`TICj)vihe^87AsY|iaVcei4fi1I+d}r&Iv0N z)RQ$pBSvy#7Bls%;T3z6)SCT*bgG)oeFZ7<6v6m^sO}5AKA?dVo1Pu815WTEfp{QY zv72^EH3k)|dwtay2LqR7>=HJ)q*HU*X>T-=%rMJ*KzcWOr*s-}X2u_?X7vZ{jqt`1 zWj-jqpRL>0V82(nxaBCU=Z{Js#5212i48~-WU`tGb~Og&bQV5R8|=_(!zuI!@sVc0 zN~Cw6F$ULB(2$|0AB(j;ZVi+1AU@oq zh!Y7Bx_~M%ZE)=-{3R+h1C%lgJvREsZ=&)uv#-kdMs~*xY`ksg}aG|#ctGXF~E?^63O2JJ2nwEsNP+af=`K6xMf1&+3` zlV$`$Z?I_tKV(_1VQ$d8*n)MPom6!__y)l#!4(C)9tr6kSc9v&uOd|=r+#|l-A)ev zQ#$bE&%l?e?jPPz!$VdXq~$ZZzj=dJtTEX^Ja2!aQF;$Nd)4)GNVUc%Y~O2Y>Hgx4 z#mVelZ#33Ji=6B>SEj%A}c}yt8pwBa_K}Rf{%9Sq|E1 zuW};(%^z5q=h_K;tm^i^0lpv5{$)?#hlgB#<+O9zs%hsMaX+e>hFyIL_ErhrJX(jC zqtd5hjfp+0)1CW)$xwWSRY*h6cABo8!Rm$dDWs#$(75fwuM^@te0R!FYx=BBt$XhW zTJ@N$Z#+MF6`3d7@7B3~7^8dZhr4ud{Gitz{vlKM>JRiKaa%LG=e(JX)-8II(Z-9H zzmJ|YO4Cu!jP6H)3$eiBJj_($MW$;th@ZHo>CxHDIClKxibJ-E z&WVq$dW`n9C$kUR6Sv{)JQTiLa6c-&r|ocsd9fr<WWRpl&tE02x1HguSS&gR9Zi$-1^% z01vXE;Kbq3!eIRMJxX}(gI2DUzw@M|DTIg1N8lIpUOL~Ryi^A4)o_2RM=j@6qeOVm z)FKxywU!V2%jC$Lruz8MH!;QPri983u4pTKX|l*Sg6vXkE>^$5p{6!Zt%cr7f`csJ zsFwSi=Q=dd&<%gj8mZn$YjWiZS0v@uF8Y7t3w8t zg1!}68`a16RQ7u{uzVhB-;i!HLFH-mljNgj(<8-Z3=6qgW$%*lNZut8@Qors+;=C_|a#E1fhJ$55!aQ|t*O zg~nJ~)3V<2d$mUWO<(RhUYz^jiB zl|K?+P=AeGPs`sN2qu)k-z+qZIGhj(xf*NyoYBjZmy;!_!mm*zm(g3RD~;^RmXky) zp^S2>a~;P435nOrb(@DuG4taT{7*&8x z8eO5l7I*?i4&b&LxJ};=ja6RLaLDRUNz6356j-r;8Pdpk*-4a}2d|=bZ*9&yPAy}m zMOxv*XIj9^Yw>G@Y8HOU{%3#kZqnWbI8A^|VW{lwzzG>Yopp}n3dhVnqZtwP!6qwT zBv0x(tW5Fj-S{dxxU!ke{3ks288whRmr_uwI+LG_bY z?Vr>K$cP1+1LeX z&(}ek6HhZtNBklnri1?6Gd|6m=!5%u1cvdS#;Lag_veFjF7j8o%g-ZiOFJuxx!lC) zmXlYN<+${^?x4xk!RtSiwoS_etf(vv^r%7s9juAq+bHyDSuixx?fX zbZ=V&Ov@4ct-Z!(!T))0dhLsMe@U;+ zHBL98D5Mqq5A~yM1vD$vlYtMDHOB?`-a%Xp59Nce0Ng8k)h?s%GCaO4hrI)NfKV3Y z>FDbj^4Iuw4!)gpDU3(4h%pLSCBjf-B<3Q)oNTg;Oc>WWYun0tU@^gu+89w>yP{_0 zF5q};G9qTcTXyoQi>sW;eAm?k@(m^1%^)}B4Ub&lnEvzdvdTHU^Hp=hR@Em{$%uF3Pc*Bss`T1^lG`3G zA0DMMHcKT8m6L*Uevr#4gk^I~F?=T`82yZ+pnJ<2#8@>zClYIOqT-A^Hv;fvtbjCb zM#T?az;(^#LEH(?Edf5A=DHNcXQZ{a>G4(%X#!E66Znmpg0Y`z<#djwTURWzybkrx zkp0BX5d3YB(wvj-ybbLrKZU#CukBeVKhfiCiYQhA?uaqUhuxU3i+60FG@O19%k-O1Eb*O@u=hMGQDjC`dm_&ZlM{y zYv9O=amk-I$IoJYGgZLKia$vU8RlBA;|Oe6MYD9AO}`93qv29I2RM|Wuzt1d5!*$a zwP!G4yrupzfC1sJ)O2SFc`H+4rq1zbz!B>-p}?C1x=L2x;HWcAvzB&Plw-^W&pXmX z2AT)_wca%}%l?&oV8xW;DRT_g81qMx@!k{Cp=sr-3c4A6`La^{1rIV!U&&LnmrrGC%W-gzi33i$8nW z^f;8I(`W#R&FEW$19r_>$J`lrFGHHQAv+WdCrN25BKzK6#^Y0-qt{7U*GKQ!@6Lr=_C8(~=pN5m!lJDDX-My7axn zt;1(6T4nN^mwrez=Sr-3$+Fa6Ypj%?)7|<7p5)^waD8e-Xb#F&gyQ)1xlFR7MzS&i-!YKbA+C zks_++|GJ0lMdf~?ETYynj4JUkM%2XU!fS!NbI|DqpAq%5r4c=naR#%q2z*Wzzu3e> z3w7wZx+w-871|xJbI>L^Ho^J^95STZG1&3b46E%^Vr380<`cNHmpJ4I(H9Y(}2v zj1&!t2mDv3snbUIqvAGL;ddW69qB3r=S}US9ZWPX4X*GGP~lJHHHhuN;taOXFDh1A zAFTw0(!MP}3z#>QV@x1rFyhMB`~*G8oxS6K{cly-`SM@xnYrTeJkxJ`-jvxk(>flz6+l+|Z6)QX8elJl0z!}(f6Qx!l@||vwBAywj{>%Idv`RE6hx z=)(Tk%)>_6-8==jF?VV^JK}t@O5slM!wrx-q1*AR7*9s=$*V#>n{h_EXPgpTx!^u| z)wFn%NkKU+H(_^Y29`5<;#ZM zm?K#0HjnV9BmT}-zsP>~%MY&_;|-QCy*}l|^ZH*EVK+b;u4CYX`@7};SoxgkBIe!L z{H1J}9iYurlHDWJ=NH z%_T~aXm-r(M&9tdFb#QJrXlo(X`y#{P4FH^-)bn%jAGjF>{>H&u3@kLDVy(OhHETu z_g(D{1)4(>&7wK)IDJF13pH%SVY3&C0I#-{tF_CeU;k4s*Qd-)O-{#GUHIyykisCE zNsd9^;WWE+T!{+yzC`&Le}km2JZ7r4#Cyss51V2=yL&jpQB$PnJJ{l@JPq3pL9cdq z4{VO{F|Cb!dTiS_(mk!vSmjfp6`O;&IgEd+w8@g^x=Z_*$XzmaJ)Ex;r9eJ- zeAr7L2?e(IF0gR02wsSsdD6QmbALsHbAsa$#~&Ps;#Bjw7n^=jy^#3d6#HUm&TJ>y zF4CmIJd{pZelxxJmUR6s>5I3f5o?6L)x6D@jI)1lf7zmW-v*e6%S(jJglYLv2fj!b zH;ZIq2^KSASGKCoHx{)lOeJ3?=9bUzIoT-SY+5<{WT(IhS!Wudx5{pVZ2iYX-hWSU zB6R4wQ3iDRpP=$IW3c~$_COzzSv9ZE9^) zdNN$3w-5h3Z7d>|7>>t~4TT)fk;;n(Ea7_z7VfErJtqDFXl)4RB%2_McuFHr0jLtBknns|35tig$D)_?&e0S(8<8U{lO3UeFJx$@yuU;=A*{#c z!1sy$?)|U`UTGs886)x!$+{NV95d-RbYx1zYQ~>6V5op$j)#;CON zeZv^yRq@uy8q~+tmVWOH=&3QxxNc2T*!EjlT)TlzPHNZSjwmlG|AaoW{2ppw9%9v9 z2_bGkD53=YEi}4Rej|LlXO#*1@76$Ta&6vm9w}CT8l2R1Z@8b~vax^MK+>hocxva{ z5^4YjQB6^xMtPxI>%rc0!%&<;Dq~Gk%IuXJ%bR~(4lOW7<-gu*aD^?vJ2oV{$d;U> z&8)6t4W(%$)SxtKJ$yzWQmfJcUxmh(8_VHkgnls-uX~UOzCk{cqVrYIn!!#2vhbRa zS`X=BZkyb08|o!_WFD|ohm)M;ZYmepPyr6L?0!V!pc=|6vs~q{czeVVg&oi%wmGy< zllSR@mXmWpuP8*dO(^=C6kWYr($4)?qspJ(PZE^hXbBdRrJ1hpHqefgps4cy7T)f2 z%jm3nq+BOa&1mg+8w8>pU5B;b1J}3rw%({|>O>CTg#yK)**MeQf?d&f8z@FkC@=wI zH3PAC{rPJ6KOXr$j~GWx`nQ-HOL70&^R1VppJ9BX{z7hEa%m98X5^6En&Sk23D&K1 zU0kJ*scI{zG&Qr+$7XSDn=7?hs&-mM)?H$edS zLb;IK!gl>5%qc(`S9Es6n#0=~s#X3#kJ>fC@y9iQ(l}gG&+z>DZ&Va<=V!sv?ze2% z^98diU#%=(%jmu>JFr%_R>+zoYdtIY>9ed1tN*SH9)jSFZ;l&2XnVd}P7#atm}Yto zuZPcnK%*wquyLg;t8y{$Z5TA*c)wTr6?WmwB*y=W^VAguA0FZ8ac2kXzbMy#eV_z; zLPor_W(zO@dQ#a5HpHw8EDH|iACCOvzTkV#yH$bnoc=wp$@(s?cd#cK$o>WNo$n$H zC?I?g^zW0zp@7w0u+g{*+HaSiloHc;|Kc9aQM_4z%LI-s2YgBH3F%I7xO2j9@Xbl= z-NIkj*}|p0ww}qX1#c+mcdAm)m3IH4;v-Z*uIEH0-hGf5*rkE0;Q^XAZu(zmr2usfmkT#YI7<&3Wl^kP1 zYoI5zE+bCm;Mz#|NoA4@oX&T0z}Y8b?L7bsF{~d0z~1BKG7ihOvuLdaewFpe@xhIE zyhU@Asod6_yq?V*)3ykIQ`+k6v9p%l@vLcNTO0h6?J-SkL7PSLix%ynd=YX!W!nY2 zAm?M-_SoTr6FHccJD*}1QudYYb?pgQ0Z=1O6yOoT{|feC%g`R8?G!)xe8mc2LSC>& zN}sEOXX<}CNjpujSbpt%wqa+DG~U4D3=Fc&uBP}aUWL8}{t}&d%HT9J!%%jt0(1%7 zGvw7qoEw;NH{)J}dlBv>xR=CVu&Mm5;HlWGI1lUZ>W8NYR=R{wJ&f z`BTqkP|KiC49#3Bn@Rtho5$=wi}lGD|EyOz`1CLG(~sf*MXIPmdE*TVc?}E1S9hX$V=MOnOd*^L~f-CV_k$PsxD<CDOFULowodvxe2krMQTQCAfr-UermPqK)ztbnCZ$+D-d!e7QbSqjYT423q z`yQTvMlk=tUmf$cx91*~0bjA6aMuppR9KVOZ!&Fefd-?`$be7Kgz=i*X_^o@Qi!xf zdEle6LTxj+IK;#p#%i%{j*`7q|H>Z&{*m$)T#uOFdKLcq`uMBHNSsBk;rein;#+`O zf_F>sj)T0skLzl^I%gaoThPJkqx%GZL@bBXTts$HWqk9+MQo#wk1gpqyK0q>6-qm{ zq?UH<7}n^Ebh-J zG+I&Gkx;@Q3MXoRD7G(^Veo8mOl%(?_mJ4y z7-iEqDB3%sv5Q0LUq!Q!)lHcFs>#wee$Ir_!nneY4;6ptQw(ptTAKZG6zfyutNMn= z|Dlha_e@lNhmb$EMCgkbs3o?jXuttpy-KJZ$wg>Mp#o2gXbFQTAtmBaOPbJy!fG5F3kqyD*^3$<`KAIEXFg4D!)ElG2i+E|fRR_MH7oYiv7Iz}(zq+=3vBsjmj4_=gck_-L!`&{{b zYhSH2{>FRQ)%{;r%Xy>(Lw?Jjdv7U2?$C9N6kPsUo z>oDVP))M~GJc_o)%u2y(H@pRLVBw*!6*9vjeIhh|Ac@nfiuW>+@Pf-`nIs{F@nB8Y zOWs3T(2*mI(dc4~jzPXRqBzpZq@9t35xB}GyB@H!*$?3SR+!iAU2G@L6tW*2lhb$f zeFWqS!nD?8(O(5wKFd#cA!R&rjfvad!U0}kO>VRi`PEIsD0dpg7zMq@*^)ZVB&XGB z8r`nmgI$On*cFi}d=*h-?An>l`BVEK*TISP1)D5+!%47H8`d|VKCgifE~10+bk4aH z?{1u$Qoa~aiAA!o+r>ARo#wA9q8+U|!Du;w5eHnrL;X=cujR5=Va5h}@N6sOunEK` zC_NMWRWs@2!3gkT=uf`hftfHu&@FZF#c0)iC^z@*8}mq( z8wG2!xQ%~L|Qf6mW*1A@}LuZ^PsW?g2b)elS;()-~J-Y&TRm% zs0O!Olq;ZISOhYmz$El1u_fB7YXl_`5X)JcG*1m5GCBiW%hrD?%i9Pa5isJR(PM5doc;UxIa4zzG;R0Fc!QGEKqt<9;f~66uEyTJ)~Qai+CI2EbJ9J zH3Hu&bmKgs06krs4?g6%Wge;MUS`~DCah6~*?>JJYBmt0@0B54IgsuWN1w)u3;(nJn0|IDKk+*c z$?gShjw`%35Q-Pr+1v%7UPCaZdh(idCFPHYvTk?lD>q|D&1LCUPx6eRl?|(c8YhtY z8>3AtN+M>PkXy$`AabNRJJ*GLxu`dn6cC%iU0QI9lwDGfE{LdY3-3zi?9okZxBK4hk-;ryHja-ca~Zd zd}c18PY}uzS$ObO=RC4-><0AISu4fVD1ICFY4q!ugT-`4GZ(XJb>HjOjj4M~f|=>s zR{c8@a}^fX6Ysqk40wXg&^h01QM+F${reT17s~5PBmP?5O8+~(Y`wB3{Olb(A&a{ z>RD*3|HFwmVp)qkZ2ir07r0lal>5}Ykhun{jstLUIj3)AvXu3t|C?;dwfK_jOvArq(L(ewhkB=O zq1yl8R_^sB+kG7OoM|=cd+7{8qu$LRl0kwj?G-x9%)+&Ru-uz#VRT!)>5$+emiIuZ zpNV=`E~g298UMG=$(GLne}eY0-UU`2YK%F{r0g+mJUiQpXqsI|oqJ5it~O_DXPJq+ z7U0f~V#H7&CP+FGAGr%lsb(61N8JQbiaU98ED%k^Ns(EX1gT#}|1Rf>F?O_9iD&=B zzQE*eUS5hFI|EvVHE5cm{x^sXh`^pWX;Ie>tyG_1iC*~ml}J3jt4951+!vX%<4(tY z5qFv*?rtR?(1U&E>QAIT;?iD4?d3YV=X2L;baT9%2<;T|G+JhvQ3jK6XTL`3}t8H-g5lkrk9Wgp^iGsRpF}DnEM(smIIc*p z(cJt^>Bfzihx~N&<|7YQij#`m&sr}xE@Q|h;~D2R>o=0Pi^+P}t?*ffseGnLoTMk# zNvVjMastBI*u05}N7# z(svG-6~016gzq$`@)fD)+8=<|MOOC!_E@QiL^{-(%Z=2U(hjrmA-tB;nT4^dP;U91 z!T;}NeD!~{|6JR=8}0w%|7iaUgY9RsP9io%0U$Em^^FvLpwur$Fj=KTB_nc?B{88Y z43|S~De$GB&UksyQzyY=eicFE1@Ig2Oi_;`Mt9j;gCqAJ?VE9Z;alMr&gfq7*3B{d?>7D!QMTEgL*vHorM6PN zfAGpU&4m9Eo>$@=fbePS5x}Mze1R_?GC(mpQR0pK&SizWESn^zb&cD~ek1v7l$Lsn z=F;j7kdU}dPq3NyyQ6#pul5!2&q|DOag~_$vgJWHsD{tXM97|yc>UA%Y4!?xf=?A| zLL9=!3M;VU0YCVQXJA!OzZs?T^`#vJDd|b{#clSZu!~XeUv2-;eo>BGSVv#*nzIE@ z(7E9nNacAq;*!d^rSLa|UPGi!*6_5c`vO~+W(IyLhC5z;qHzPYY^RCZrfFYNB~JX_ z{Rs1!mqR?iXAsegHEcEQzrfYkq5jd9_aMy*m&n$qI{u0^fZ#;%A=qq&pZ5`#F_wLj z0d(`6q_daX3zID1L|-ud8WhQN_8V!tQ*r6R!e9F$_~~a)U16H(bPf@NxZ7p@x;de3 z$4D0g9^nD4ll<8$-2UX{$(?+Zv6Eqj`J$qhZa^x6&HU2682xPbDEvPQ9ri`?tYNe1 z%!U8@wprny5Mm7)d^gK?X??iflV=z)JmUu2J#&O5)iSJd%HuCc5v|*fF#7KhYnaLX zF7;+@u$q^TBLXWHnCG3j$z6p6)cn+95M(B*@S9Lbnoc3C%+%v@UVU4Ht-hnZvwH{+W6Vf1l>g;c%bIyM*QCem) z;8n5ai;%xq#sIrnHT-kM0>eDO)yUen;>MXkpMayO> z_0p26Yo23CS>{MHqB&gp0AE8+(x2F}-F&w>u|?r4iB>>DHO;JU*(|Y6w3o*_X|(f@ zW5K#`-M8R%KF#&w**`52CvO6VJ1vkxsf?bP)+iUP918#ap~D;5onls`yMT6#-)cI! zXy68axo|bG-f&i&9O*WJ!wCg`1Y{AXTYPgZSwHxz&tJKfn;YxN+E{Q*07YEgqwwDm zBHm+K@2fJ`Ac}|9!qyYACgL5!U#n5#U7@E;&9GGtKT-H!0oN1CC8~QGWoLn2|IJM_ zb{4D@vevr2cO)r&=7d2Qwh|1>*rgT$zI2~WB$&Kp(yW_b74Y1Vl!-O{#Y%sph)D6q z#3UJRMn-{{uxUs;rO~a2!ErM<9TjO0%q zCTR?9Qli*~*hMu(d@k*z>l3+a-z?qVN9*n`UHA$8eaW;I^VjL`1Ez%g0iICWLh>na z1|sZ4uQ;n`i?EJ3ymsYAM)$FV6~0vw=dC4+;n0|=jda6f*a4MqCLf7)?Qy34 z2{wcAA96DFm^%k}Uxae9xwK=}sHv6WL;)VXLV>x#EvA5nA_=fEmx^NBbC%-!@PMqr2WA6q%3!Kx;cfYhcT9$rO`*2dU*_wSj}1BUuLQ{OWYArzhPZSf4G9Fx0*+rS7Oz% z4e~1giMJ}Z!oM7P!$W&*!n+0;k7Ff;|8F=`K^#6QjK`1&0+Yj7{DybB+y+DC;;LK+ zXA!gsmiJ^V{GBOY#zI%+QOBopYtJC3iJOkL6=1H;T?ee=)GZFjqIHjehYSYVLxkU_ zyhIg-d-$K3I~ie=Inwv%$O{=9h3e& zRyWnjRsZ(qkWrG89jH2g?ux?ohl`WZLr4+cn~?r zH)jk{Kh`&gHA%U1IbaK`i(I$FmUlc67EnXwk4;y%MWRH;SCD|nzZhjUlf^pVn0fOV zW@og9*27lEO!|(_o*)x~m#yM{eHELVDC=5lkGjoBYu{rV{{=l0hCf;netM_;G}5)ju1bYR zId!COTk7lhMNe+Mzl7gr%Xa*o)JR{KcCT^B>nV6JP!3^D((1+al{pKhnj0MV%kS69 z@543a%QY$dd;5v935ScJtqY8vYxR~S@|-poZ-HR*?v(PE?$%pH8Q>A7AF#y z^%bD82Y!9HXevc__Gj>~l4TJPbL_W$dtCH37-8r#<~w$C4-(cNqjcGBoBtyb&#TA`k# z<4o6Gn_ihSULN6$xdcH*f3QS>-6zWgrASCl09W<`{C6ph*|@4VQo$J!$IW@5*B z0Neq116guTlY_Bfa@k6^(gqZ8P{{)is_sHPJTR>VTn8x3M}tYAdbNzPk6ipeNE*v z7+V@AG5y2F0#Kb#l7_|yeo!%HL~)3h7~Vpo2Y%wnhIQ{z&2zxsU=zGqg~zU{ zd;D8tF<-~G?vo~5>Ml%_c~4m9J5`{wgmH_jo2l}B2)Lz z1^3GP)P(0dcS+p-_hkO{*-}<%Na>=Hm{bHOrK0sHUO$hKNi`nr{tB2XD-`n|Z?Ys$o43O-v?hqlez`&{a4l z*RSx+N+5g!*Ece`iqBl(iSW(h{K(aq&M>4tk=rot=KA++c=fC5 zUij8=6A#Hc?J|?y|GB!cZSf`8?w50H?~piGB@#Xy1D&*NpC5 z_}?&g{V80tSDe)8UR>31&+q?3)*{bsjI*QZsUxcN7}+0Kb~OHt7GfX9IfOI6ZuyYx zQVC7I#nvU@&1m%kr^L*cat%hipjBdBb-=^df)YJ;Kt=9zS8m;BG5#%17ChtAeK<8( zOQp;LUvz7Zd)V-R9XVz+X*w4+Ns%QHV*O20fAAiEPf9#_Y^3NUdrk zon}Mjatr+P<$${9f(OWjUJJr`>cQQ3z&pu08SS1ti&_rOc`B}H zA70@K(F^!5&NUR-6*qd3Td4mmAfEoPueOS7%F&)7A*j8fP7 zO1n?UDSw~Ld(zmDJWL)V9W<=O%%_e_bBhGQgs}9NPQJ1|qvwzoh|+gLsj&PFr4Iz+ zLb*>cy2VNJ^I4KvaEXZ~AB)HfQJip?*~Py$03V3^5GfXXS3+FoZrLx${KNw6q!l9C zrw;~-dTF$bU9VM8+y01F9+p!F`)F;D+l00S1L@6qO=+-oVdTC447VOSp)W}Btpvq~ zd$;%d0Gl-Tn3B?tl2pZ|a(P!l(uNekt3Zyoxbul>Q&(FoR$IaMg+P51mm3pz+Rxaj zJ*QIX?*S9-?*+({@{ZHaFzDq&%tkH4!_ytZ(~yO(Bg08cFvL8DZ_CP zhQyO?w~nrS`iMA*tzrBz)!^=S;o7tpI{MUJrKJtp>q(F^(w>#Cjph>mNFExH{7cXuNkHP}E~rYa5iiDh}CG64RIC+qsE}#uU;+Ac}o&8_E|y zmJ%6p;x=i_ku%_)PB`bYUs^Iv3C@+j@Cs}3$U`As((yg^%r+gn^#iUwZ@PgOQ1{B1_iS@}G_1q9Hd#DcD zMZNE3%@W>c^@eF7;Su5d7f{ZoIk_KcqK|bYY2hZ=y*J)i zv9Hf$h*~_!V!uC%_+)xx^tU_Lfghi#nqCDTNp4{}2hUcgoq~TCL2oBda6gCM7w|cj+bj=NHJPq~;*NGlnlnH{!FCd7 ziW>zvsoq>)`I2doo1pl=sIl>SgLja1BN%p zYP0+;^!lD(Be##$L6*Kh8T=y~b-R~FW=40;TQpiD+d*ZoFOc&K{yPWg*ZuOZ*g-m+ zNp$%7a`{<`7pHma{wco+_vrRpdh~n8Z$YQ`;S6Y&Ohv>A^Q#s)s>q^B#>C6g5Km5g zIMHtUN3^^An9`!bj(i}X?8gev;{>&qe@!@?0q>FnfmQu{UBaw}a>XpA^;0|7eQ9Yz!C!3jglQQV+(!sZuL2$vbc<{>)1f(_hxS;v({!sI z6KOHY`c9Xi7XjU+|B-+G|IFVS&L8%;(m#c?aQmkqYBTOPwWJ11*M##*O(}D7D=8w> zD9g%YjK8e6Kk#&Lly#krnWTmfr*z9yXxaP?+ClH*Itc#*t?=l4q&XOvKOp$u?}Y__ zA2TufqJSvv%OYN8giagI!pqF0J%gww*?JrJK1aGmA__IF*K3Cj(31}^^If<2IP!3Sgru(R4&D@R2!JAh>MFNSS`Hn^K37F z)tJN0%7>1PVEjxeg&mJGDCfhy+oX^lr)UP6 z8?5_Ti8WyNzG?M4jQ&v@thBRXXHM5+<86098&7eGe2?TdXDz>|6cGo8fwkl{SiuXp ze=Bq337$HXaj_94IeF})ohEoDfjzuXjnb+=0WP36CzI6z@8#0-cwc~L*{Jh5L_ma9 zypT(!Ccs;Rke%Y9Rq!vK6X2_=`Y3%G@|d;MKf)#2$D*H(rjeEls_jQcTb{CPw-`GQ zI!|t(NIIMGmo*gNFK6KKmp7bzoFZ#(#$Sb@0DqMR9)DGalZc9-Hf+Y<2txt>MjCkh zjWV1>%;RXo=1LRpF$P|afQqSsAptSiDr}9mt2TOu_&A!c zCAIi9Ai&sHS^i*o6FI&F@AM_!e2F(-;tf4J1iVb4SwH_jJu{4&omj$Xpn#OXb`dK^_*Ltqh~*D@dpI=KF>6`=nrm{GLY)7uNlNw%c2h zAC3acwA_3a`9Ge;{*Pyx|9B>#JY_oXE=au@SN3L^G5?V_T(|l^o~dD@)&jr%oOPi+ z?6F#XoW7^$o=~1kz;k7u2D%^Ei-@d9a2|U9;eWi}{{MRa4B};y9nnJk{cXsIzlTCX zvBJuvt6LEH2iA?8mEs66Ue*|`Al+Zer#^~x#ku+&4J>DdY-|o*GcM(Uhp@Jeht9D! zvVd_LTSUY_2>;hN3pea+7_4~e<6;XeEP-b`O^&ANXP`5Db-L?hrznWfGu&&`7VI(B z8AnFD`mXmY`%;IG6UO##3i8og8wH=n7~L1!y~U(#7n5sDq@m1;uOntX?_=|!6CRIm z{K`HJ;;XRvOkZqpM`3A4bcwS4O&=TkVj=SDSe-N3*b&V*j<3P}F{-S|?a<2>wqwnXHZPHc>2q+sT-f8mQ@+l`;T_}SqgmJ`#JE3Qa^fC# zVubr8tmoTtaT9r@IixuQbc8ee-OnX*W<+2AoWu1TBC>0aTgPx_7V%2hQbc68?v$S~ zE!7{R<+x|K%kW*BQ!z%rG;(J->rj3?KUo zRcXiW*kVuC#G>|C*xr|RER^Nh2RhY?RLiw1+|R%Nk^A{Fv_8?##-$_r5AU8QjhkD5 z3;6}~hU0x(a0<1B?RDKVhMOo-?-koEG3J*n=)D!ax4s_ky>QT^H*3xKMiJqTtW0*W zlN9da%0&)tQfkYym30m}>EA->kox+-U|;Wbqo2OLaz6u}IUTX%_)K$$V7|M3#Mn30qb5Our#yMp$hB242k7go-X!2V>B;0ZF*?M31V`K*_>Gi~xQQ9SzdU1i!1z9*S9db` z-GS--MpGGf8F|M6g_*<~z?1t+BP?qzN%A?|3FlT5SpXEa7rRb$iagWH^-l&T^@6dW zBRYDwv9x2voI{Q8_XQ<=bZlSZXX-wt2X6}Y7N)oR64lU85zg!_%X9_N)Eousm7K#cjXb~3_Sz4?w6-wD$aG6O2 zoU)A_6*80oZa402(>DL_xhd+r%kT4tPkNvG+-HBzbDr~@bDncPY~%Q`xhYAmR;^=1 z?I{Oiolvl%06lzhx^N+|SZE7uIN(ZV;d6G--FuYMOsJd6)5?xk-nA+3VbqQC?5b}! zp>CANP~E!!s9P&h-S0W?{<-e=ey;ncWLGQWI2@oJn|k=RR~;1&>NW2Q*I66xfpKUg z)d{~)Io>=~57%iqvt*!N??Am{a<^U4vDI}0IbxAs!u=xGo6hKP8`D+az8S8UwdI~m zrm{8!3WOg6iv$VJ?db1+1J*|Fcjq9+M6qkFVn`-0u_ z$euO*5y3>eAyOBN?m^#ORe4;Gw%u{&N18*7Bi?Ba87sz7&zED=yJh6aMEZWL|D2ce z#-3S3X)%W{htu-rTpC5_Hf-8Ws*xRVva zNfMLTEcf)0_3+8OgAp|{yMq&7>s9Zi`%(t)u?_=Y=E?US^Dq|Y^8Dgs9c(;ltZ}wP z?2M{(emM(z!C1e}3ol8z z-|sDPAA!W{Fnh#(%6q&+onl7Ge?!Sf+zG2vSe0m3SrOBOn0ryzns`>sX34e;rHEDP zCaE3XqCE8jGUX;P@ zY%jw;6yeuc7rTF7%uULGt{2&^`$zBGmUQ#yrcUXbzDTRVt;0ReBs^JbYhaA2OFAUm zYo|eb-hzB%V6UkqphxUJd74$-rsGJ9%;v7xr?>8O|5$v*z1?do+U`x(FD=@>a%a(x zE3Xu7FWgzM4j#2m$j#p{ywSN^npo$b%t5nB4^ zAJ^@~4{?7cJ2S);A36F7H#f4D!M%voJOe~+^+RKgKR8*jZ&39At{S!e$oZ??`1 zgpiNQehYaiOlh`T?@+?jCXaP;K<|#iZJJCxIaqbn)f;hJd0xM9#}pJ^l;U3i)FsM*_~K)7QWV5ap)&m^`Pp}gEU5bM@d`YcPbt~EpuEp7>~;_CKj_s z9C#yaBDM?%ya&4i3@p>Ssg3thtV%d7*A#%J57JvrVo3TLH$Xh7#o#{;??!B<7Edpp zJf1I6JX+yFt!q%L7qz}*|8bFN(V|6fEuvPv*`sUd&<;OEV{lTgfE>tx-fzTHi{~ah zc|1uzWbolTgKzL&xCtY*#r|V~sbEpTTLn0EHPYSmko1X++kmSh&KmJocYz=@UtMd4EYU<7LHBn9ZDnlNImcj2Ak! zEGsVTv3ac?*tD=O_RxJPNm{+16$_Bkk7t?!4IlRtY}}B3dkFa(Zb<)bf+v&)q&k2U z_!Z$7kJO3yJ%Zn#@SBG3Q$Aru2G4{0d9T64imx09KQ|-&eJJOpLpP-5C{vI0HvC?| zZv%eI@%saQMaWx)bfrl1cZ3fi?$3CJ4&0C|$ouvoxqjoY$lS#wub909)}un1(E@H7 zv+(F8Y^mdYyI`rlQ+hb~5x%o$c9XX?iwZKf3q9ALeB3kMBR9hOkoQwBU;eW5(~fBP zT#i=p-~LU>H9h!{197~eVVo-B)LYJoZ;w6mb`qEQQ_?mke}S=Rb+-n-7J`qt5| zjipv=gU>(nXzelVhS7XN=Ftb%I<+T{%qtn*DJ9|NTG^9i?V1@C1Pv+HZ}GlUcf2E7 zbKd)*lW9wUe>}$2Tp~99uKfHn&o8_^mzyvl_iC~!H`2SS?n9@yDa!L;InGa=k3=N6 z_1?23mjiNrsrOhp0~<6^!iKu&g0E_iVmxxn6Ea>*G~|{(P+eE)bd`{8m=B7mb^Ci% zeim&x)hCqPS^$mc5_oWe{#h?p73^iVC1|#LE#=kVCFlf{q>l2LYu_LC!!zl*A?e+2 z+A~bTQwuxcFFh4lo%&JcJ?yQnJC+nash4_J)F1c0iq=v)xV8i?&X-gES$Ww5>A9Po z(dP(yVSi;#r&QSo=w%qPcxEEYEe!lFvGBX(u2+{I$Nn6xd7S2vLrBdF3HDJ9DJt$X zV@fP_H`Y0ac{OJ)JhLI0n>a6dBkUO^2wTgeYD*WsjamNp`LoXqE#)#MLNC&o8wg;A z)_zsH&N<}dG4Go4@VtDQ7};?A-pAdMxob*}KVZ&1GS5;=b2J0?BFaj@dnqMP=!AvK zyhqW$DyPQhEVnd-b1Rm!ux^h%qw{i^hTM42H5_0htNv!{2%oEbYQv}HcQnvw5AXXF z;~Xw85i}$3jrZJy5I)yX;DKcy9`@|54TmoS#|-CD-h#s9LVa#mKsyHQ7gUf#|GQ`q zX7KObOyQ1Ncvwz(3Agdm8d#kCJ_P(31$_%_TYzqN&PgiFON=XgBUp4tl3wS4($h8)X$^ z#`{Wm32ldd#Blh|=#>7{?S>8B2=fTw$ejKR==Vjy5<^Iu+Ru#T&oQa&IkFZ;s}2wW zZ!!T4X=sI0bzy8S zc8@X>yuPWgtWjkM1%-)#VwXH+-Qe75WeiGSO|W%nx)xx@z^{1|^fs*_GpNMG$Y&Hpn zR9<^JF*1`$8T*t)j(5Z-@+rff;$X{dA?AR=eSr+@ck4WW{ub8yFSNtnXs<6T{;{D9 zB@FQ$f0i3N*w0%_YSlv{Coq<@r%d4I9z`h$Pcc`@xX8?Tg?5ynF9?^xr+_PuGI)#$ z>u1)%>HkH@P zCV~eId>*csRp1;2rRU`*C#t%vu2ix@w|v`8J9f&xS^-}pLFpNp>H1wxPCCB3YlC8& z3Eq%i1n-lquFPy&4P2Jk@UGd8nzCiGv=W4+l~_>+%6uAg8`BMH{3;_c6|ycPdEZ`H zld%`nK&5ktf3JMdT!Zk*q#F2kICa@;H3`QrueEkd-}G;UM+uU-e&|stS}5>t+E_*D z%x{Cn_*Y=BtWwIUR`SwYdmx;$M`}I9iQ_}3P?9G}IfJDZLC=6Y>uA?3+8I~|D!9xE zP8Yu}hvKx0wgt8?G1Tr2c67iqg7G6+V~H9w!Oy~9fyVNY?970FhB@Rxf&FM z?>*?I+`DK@06sArRM-dLQ-k`Ou`nsQ4icRUAIVRe;&a)lWXmu<$i=@cAF{V%rwB@) z^oK_mbittt^g#uF_@`zS+Tp=lbKom;|E0gd+6v((im8zu8A*^<4h6>PlKv1h!TMH5 ztliWB`kJ+16wJqEkv%T|i$%M*w`CQF8~hwb!h~_=*eRH0>{Le39x9;%UXn=r>&FJ) zWntbjG3;xUZlhwYfCBVg&b^-#w|AeqEM%OsFHC;;iiS&0dKkJfOxm@xCP9`a_Oq}+ zD=U!tcR*@~)Y?yw`T-ynHi4#1o3dm48}G`}(0<6D*srqsqzO13INImJTM^CNTu2MW z1{UMZiOaf~wsRUvw-@w|Jt^$3k%za(gZ*mqDmx0&tVNV^PS3>Juoq%Z%>RTL)fqT- zITv;YZnw(t!zep?q*og`ah~G&f=NgJ(@)8#>}2QQO6{ChmQV6&&F{e48w(4Mr!L=a zgci>ghDRA}sdHV9;ak?K>hi!#tuCGXy&aLs59WqNufSqXJ6cUv(aBb2j4F3XG4YDY;s~p zo}9iJJSLfF2#Zm`eqjmXj7ApTSW(;OxG@8k$WogU1rNMn5QeG%J^>8DvTB~6;Fq`M z_&G7T@4sV-N$0}&;VIlzQJrbA4yIQt z>nHI-cxBIl);sxijZp9fVUJ(qjHtF~4f17N0oGtohgYF0j(H9Dk}TzQw4!~`_-psE zsS#Mys*FZR9?8c|xW<2C1>czakpj!+T6T;bSpLnTw3g3anp7)ETYE0Pv^UswLlYdN zF$tKf-*BQu9+x;B+j95?YQBMW8d$QjQ!zwf~>6C8`2X! z6TsV#1YJu|LVKLCzDo-Bk@w`~vI87qPJB%Eh%CI5lRZhu4PZ|)3p5TZJ|7Zmi5A)v z(lyZ8m%`g|OP!u?!CsxW0oI^=ChraDogR~S40`qzX6CXl=f*d&w_#+y=yjTY3`Cm# z86errUipE7z4C)|Jc8XGgA(?`vkmM(h_}g{xdfPl6(`7d=cP8?Eqq*;JU*)_d310* zIBKJx2b`*fU3JOh5H~6~j_jpQ_2A2>kGkGoodrufZ$C-!yanlo3D0@@JO@3umymRa zxco2dMNi&myo`7N5+EjZg`cb1Am5+POa&&wjBs}HH@aCft7$9A!-X>Je#8eNx%dWn zU#r&er?w)@*dFv<@g`5Lu4}O^XdY9(t?TU-)pfb;GmK@Xs253m#3$m?*>e+sPf1jjMD?*QX8|66rzr2-beZ26lY)@~*y|fR! z2k<@>9C<)D?wqDJ@%T0t-;DOu^6{{o$^7!<4Ar(8=yK+YQ3vwDx45$ZCOPZTv z$&{2Efut1pk4@g;ZfAQ!uFmY&i*x*-DIlV}%&1xHwFq!m6f z{UTP)1f}Ey4Qu4dSX~oJ*2t3Pk20>I3|Lt;-9S#_z4Z6Z zL9M`7&5MRf(t2p?g1$Ftk)GNZH?ZE|{{ySi(U6LieDe~d{IE9>rDiFqOgO3O>KUxH zTGSBPWXF2eHPSi_Nqxwdg?wR*b3@vQ{)cmsy!TpPB4%n;FwB2>3ArVueL9?Gh-N0= z$k8b(_uXdjtxV_L3sM$6UA@lSA*J+D{uQvZ90|x-0;_NFY9 zUom{mR9DK6+qb(jy|9-IWOze5(^~^=AUa8}hIDpMUyWM~aHG1E@mMCVW*~v>yCHps z8Km!TB7C^F0Ii{YjOd}ALGrwQL#iK`W7vTbv}DBw`n{D3C&>uDKi0G9hSVYJ8cYQ> zo?mi*5lRKVG6NED-%6SH-@vX!moFrh;MwMnG<6QlOnzT#11^aoC9sN;(oHFmLJmmE zojUXsxMOSITf=yvqJiDaw!N+`kz4Iqg~o!cmByk}ugSAsZY|K@)WwX4O<;I)B@aNH zoioK3S>21>U9c4XN#jVI#&g>%K%)*JyugLmJLlact4t33)iAqcJ)`%qJ9dFS-31*I z8kev%gCJxwP9-$z^D) z2=Bm}kZBJz(`pP!yBgz6ZGm{4K#vr2Sc|M`ZmTMi7dd{oZ(b`OndD=}!m65?fi#DK zzXjgJE80k$k?_eb>=2}fQYeJqh+B6gMEhzi_&UU!zT8959FG~M-tF!+p?CWf-mKL# zu>UGBzO8pg!&X2B`ktos!S5gFvk?1OZFrBZ1eN}69lV{8L#xRf-c-o# z{tZmqk)sx-7c<2yR?sbqeV5<)Sh1$}TJa0O=^8&JC>_)JT2pIx@+dg?}|^1xK# z`^~`-tZi&F?Ta7wE-mJ5V`2TcPe*=XZ}U^^HjG)ipS@KFo+YOd2BoUkawB{n9s~p)JgKpZ^Qs)^B{D!j8@{zsmMklneW% zfQN%hnT&c?qfZ@_8V9nhLSMDHY*N60Jwe4u~-1<3T(vf^~K z%d}M2#M}a_PV#3%Kdu`&eq_&!-Nt6sxM&|gF3$J6TB_%d!TGh3;5Vo&aV<`ha#yyD zAYF^K$Bv6s`gkkKy5`4Qlg6U9n3qxy8~U5EW=u=-;ZLC-cAA;gi$3lScAN@*%uwQf z3(3QGfF8mmFLclDbin2^V<#Ip9O$nE;BMO8*^RI2ipjv6S&cN_=it|q@B)SX7$ez#6=NX|)HAR9X1O=(D}?4E)kyX9^b=IA0JiRu z?&!7^tAxe1@Cww%vMe;)7<-pg(Vtjbh*4<>)if}pX+LBNV6!#2*O}1dv1SUppxS@`veQ+6DZL@?eC={hVrZ&P+RS^5ifYzUJBmjM!g zz!$~Co>`VW_@Zn4^;*9LeWw*mE8^e)M}IQ-$_9^0*i^UJ8;?DSRx_=fSY;d@@v>FU zc4N`@x_sB5m)0-Ai70lR^UBFrm#km%zBA}&MzK>3xku%X=Ijf$b-*t%tfTF>p9c=c zUV`>i^?GZ$v9Zm#$IBn@aHu@+X6%uV=U8hi(YV(gw&1V(`2VdSA0UpD$uf?Xdq38|T11xW*ubFaxtYUsd8K!XCEHLHB^R z;d@BB8iJHXvBsSpOZi42A8h9yGzf7PUgm8+Il^=V7|;1Md4aRnKfjmw?Kh;W-JX5R zI`^&^mdm|w!j8f)jF{7tSUcYQ97^FrJa*|v0Bx|6X|9wXHSLpsZzs%I2-qSF*?|*A zF3$7d$UrMnhi?%KV_Mvy#ao1ZaYqbnve+9Ftv2^5ynl)n1Su!d{R^Y(g2&C5O-Z%S zD|LO{ZvqZ{8E^$2FwPWef0dRy#lwR|*e@e#-D<%@-9S{RxURQEaNt?qTLw731bTD^ zoNzx;KKm7jOvnLgTFO`d0j4qtey1Tn3SEc3dD?p z9_^ghxlGI9SeH@3t0Y~D-pPM`i;I7n;^$vM%zh8>lL{^w-7Y% zoOhrBqOob{ACq z1@?`gUMm&4m$gOvHp&GiP}lYNloT+5Vj?V?SNJN@yehx!v10d(5ytvonV5wfomhQ$!k0ZPLj+5p+XlZ}LGp5v z8~T0WjD4@6o=whCzSp1|$)-g%y#`OnuK@yLof)`2V2(+|mqmC>@ZW>8tQ4yOlw9wl zFAlNJ^YX-R0z7zFJocDP&W)fCSkvAHnwdesxHFM=3C<;XR60D!Qt6Y^Uy~oT4{ha% zDz-A#0|yoZDyZ(E{t@tD+9k~jQt2c8J;jR)6QRj8)NI7*dpuqs0rKwEu!m z4I^d;7Z>i3@8$NvgDq$TBRgeY(guQ_qP5V`Nku8OCOUOQ^fm{`dvFx2G3nudo^4LE zh`8gcGL^~qTi~Y<&)--^Vf`Erl*wPu`6|E-?0Vt#Lw<+VJ`3=|q9R(cWBRO03w4bl ztE&i7pd!A&g%$5wncM&@R10h4z&TygxlafSI31D2iNy~U=Q?H-7dlkATZ_HQ^Fr|# z%5(Ti-O7b4XRf4g`3`IDNoSZR03JQ4X!f=fb)hk2k@kTHOW}vin0vUc6YIK5dMv0* z)%v=mwRrM)ZV4(Hs=&nbb)gbV_^V4=d4TX6r2pNC%I+ddxxl6HuQ7n--|H?YNUROZ z(Zjf$&1I}!+yaGWljw-RNd%fb@vbiLZhV(je9B$o*HrH_N>DluK;_w zL7%x1{u{z{cH#wH;r!o2WC9M;6P zh0;&dwbU)CWhdyycKAszT5bE6f3sOYZE?L3NhdI84DP2Mu9F)Lh3@*ppcxFnN+WrK zAC+)ydET%Wh7UsNj2IJ4E&6d)y)!YlWqB8LvEB70t+6@fb)T+M3H<2NRu*Tp4?^rT z1~$0$iG#(ErlPLPpW_&1sRi$wgPiVI$8p>pZ?3x^5+^OJhj7rA8v!plOI#-5NQX8j z?9aKf$=xV}H))=;bVLfU*Zs{*+WkJyvw#Fx`WtB)+Oko$8p)#tY<0(vvVgPR#syd|Fi69)WB(axb|2l=Oa%na^t>WSGzhGm&s*w@vw z`pUO;@^D6<mjpeJF4W^%aKps}E-;GUkGrmI(- zakS6Nq27MlncRk|lh?%ojRu`yNQtW!A%`w#G zd6mw@=6bYf(6|4+HSWf$ksGKVIl#ot50B8M@y%PD?6+??FM|7WJ>Y_DlV4>3&J5Sb zz|Op(&{dFMSXKZ$imzpb`3RMj8$9q~1x}lxzz1#|#rfpHcyr^{(xoOt;W30LH(L~; z&p~-5cpkx%U583V){_qL{}H#UjHI#qtoeA79$5!^|EnQG+w z?Z(EkvPPWl*fM9?u`<%gIJPeDC6%z%$*fAnt-Fx)TxfCmPH+Nj!X{^yB8|xcwp0tA zR|bR5Bk$3-fy8Yq>ydf0(nqblOt(MmP1GT z5%{n?LgIZ)VlMnY@E(LSl`wG86{C^X4^dDrgR1%3v|auJc_?PP{8v*YSUGG)8U7NC zISV()K3F72kz0( zYH$`3pWav>FX~i!zf08d@Oxwbs<>26Oow-3SgsCBH^P>%YUO(|0;&BU$(C4>R@{Qn z93zEBBVxqZ@Fb*z7mYJ!%Ca=(KB>cRUD)o};tq-7mSF zt7a9=kvB&6NuPncT4!F2{>+tAAptS_xfI(5=0g}<+9(ppOqCQXy+g|Ex66wqu9K0vvu`xX_x2U-;U%cz9}n4Mak zR86$#@=B-nOscg6QgxzqkM-L@>9FnhE4nOC_bo2eE3_oe6{Fh%20`;5CrO_I-Ogu{ zmNV9`u)<~KJoicgQDDsY8UA$8O(Dq!UMcmBWLzO>a?@jlAK~_29Vp_r(3%r2lF$=X zyiRFJDO!?>mOQ7lgz|HJ%L+jaIp?@OmmlP;E00}q#gf-%{z@6Ykao_$oZy-%z3D7DX{Jk$C}{siCIec&loIEC8=R-CE7s4!Jv+w)?( zq!GRDLJc?#N#GtlFMrKEFTc!$c~<0qEexG!G{jZlPgN=HnHk!wVNX|^4)Gq@guOr!8kG#32gGWoEAwWwsmby@X! zg`}w)4<331`d-~fZ!S^Ae~KrKHpxxHb8JOycrG{Kv~v>zi_H0G?VSFYfz}#vf{Ai% zhkslBOUcRV4BXs0$ztbqU{7E%>kM{MyL{?)+9O@7@{3&MUwJ@awS{0Em+O*#9FQCv1eG#N1czv!DGZ4QA>n_S zI~qHKU{t;USF}ZNW9;+b+r))mDehXu;hxqzUKUob7+|7y$cf#xKNf@c>Ft!(1;cT* zJ*4|`i+LP+xu*|fZN4Er^$F~C!~avM9p_xkP=)*nNfUUoURWW2Xi5~^((_-(;SJ@+|2@YMNM_5?RP4=<6kUFm}pE!24n=8F6fPswEwq3U@E@QPNhj)lB=} zP4L0k$-?*1zl(EQxs=CN;&1Mqt{SJ|%)eKDu+rJ8vW#7YzfLyDb=EkUX56EEj<39D z!~@O;a^8O?Ybm#$-Owjx^kyyXld^mHYPO2oyi10En2orB27aT>_!oI>4eojG#hvbo zh)NCY&EGEk(tGOpCj9n4-0=LBRRdC0@Q+@Ue`CK=c;Hdn+V|uKu}8*|Wcd{)#fm#` zE-%(Ms^P#ybzG*;;?z#OBnLqJbeh(l`5cc%D+Hs*ANmOFi>xLg}?Iu3kM zt?yhh^@!wfD%3z2nXw)w>4DYdrZZ#-evo;(oo-Jt(R7U=V~}lA;Rb4d8h_zIrAA{f zv^T8B+^!-XCxNik?^tN@%7=@?H?kKO#lVy8wqo9Ou5PpQN4KuYx)S^oSU6=fA1+cg z{ouALT8p?N4BC+uJdn(lLu=xT;=2%96Vk6V6jvw32h|<3R>I>%M|Bbt9E7uV7V&+Q z)XPKp#rk4s#z8xd5f6i^ceGzeUmWcZWX^XHwz#J-Ix7LIfN)zxYPMo|OFeDcw3ul_ zrcE2IN>hUehdUHKM8*77k-fy=t{3=n*fq&3Tyo=Z$QLP1Qx7xln(txIO+VPJqKPlX z^OC}Wv*Jw8jKa<~hteJDSxkILa4C8J80_<%lBRpHLc2~d{}sN`Vv5o+Tg!vL-n$&_ zKjl#DAA(81ps!#aZ+=Zr(+qG9=}a4vUioA&d{5m>)xZ46+pSUPDr0N4N%J!BMp667ipWTli(l8O%K>c22cQg)C_ z)%w}nVj&5Y{9Wd$sPSZvYXtEGh})+EXJ3}ud!P-w0y?9jDJd+~r`SLbcRR&1?E(AE zZ@T(kX{gdRS-Rt(Cy9Z?A*2flfiKM45_=^%j1DLs5pme%6;F@Ar= zZ~q}#67Zab-`)7V)F?||;`t1qPnIAH1soD5X+7*&L<&8U9r_{vGCv0GQ_?}(zJ~k& zMhy4?OswUL4YmI^lP4KxA}?VEkJ;*;=?X>r?DhBdDiFJB7Fpya(#g;V{`!eyC@ z3$7Ge7x6e1kx#@?@R+@&pBwch{2!8+Mxr^tfXB+9^nLFwD24nX{%<8+DSTfk>(pgv z?ZWcgKbIdTJGn}!W{lZh@HYJZ*_|hfnsK{BmJWx&VHHLTEp^XhZv(Fqn3bLI0(mop z?<*RMDD+`hh`9ywgt`};s%G7|$TP~lxE5J@FGT(#*~yGe^bthKjbm#G$Iux`mfmdw zYZkIfnll;a6ISrnVq_dIK}&PAeFZ#LLpQ0J)_MZYub_NDA*R^7Nduda@Yll7{W7Y5 zg`&GQ!-sqYE5N&++02Z~_0j151Nti27~LS|%liLgbi;lmN5hXKa7M2K5|u^;1}5}! z<45?xVfk-m!2agw(aZm@QT@BpR`-A01SGwuZmFh03;$)^(rYsOmvLAzD;V<+@Lm?y zoY2*G58$J2>7M;0fk?zkg;fez*a2733{T!}4pt^evP9!Rs~2C(V1LKLxyh$8#RR3( zSXZwe3QCoSNGFPBMOpXwrp4O78CISQ?lbJQ|?8Yr)vSAKOHV+)4bYIY3 zn~J`O(gTp0I>h)A#!O{o2Vt+zs@hC(AA2+=CFg9txc(63M&21LWYeNF5&3V!t}IGZ zl>8x2IR8lHt=gkWVOmJK#Ed=c6D3Rc7^Ga$P4y)P;R85T^zmYBZR5ul4N*LmJ zTck=aKGYw6uc7Ij1qgw6E6J=yk*0eY;20!6mDaCY!h?){!gGbxT8sOcRgcCRNLZNDyE1E!7kAXjhq%dZWjz#GaCA^ulp}@lc;B?TLZ3e^kbK#H8@12?1k@%9I|^rwlfY4@9I4GT8#Ulmt^M^m$Ffs6f1FQy{_gT40cA zV_>jpL)f;-?7F-6-TA%zbt!&tmG8|gHrhri)&K-%8aI@?g&EJ>%RR}~F|mx1J;(f= z{f230cd^eh`3!7>V@Gj_D@#cql|{SE{d)0wpYedTP@icnAS>W+yY1qB`DEZMG~=v= z`>xE3@rdg@dhsXhtqyTf=_XlYgtSPEfu}lADI?UU7r#Nd=Y18xrBUL6p1;HM)<62F z%u@lfOv;S=tAAbox|xZE*I|cFl=gOiUZ-jC&SEFV73;+ly@}(Ze64ki^%Ligx}ie* zNyeH4?PzwQUOa$rZGLL$*QljRY5hB;t7gT+*8n3fErq78yC$$gsbho9$$hfKT1ewp>0AxpQHGZASkBdLhJU!O67pYG<;=bz?9p3u19xmcd6|Jn z5rzQ{wXQaYtZHD+#XZ2%x8OGucGBFikye58H501)v<{w|yY=F5rQUrdm<>Bg5Meb# z{I3!Ye-D(9wer|K`N9<7q8PMmXfK6dZ)t|??Yi`C4<{I~|Ji0-CN}( z@YoagZk2~t@RiJ{9d+OtOq_*PrWa2K_rUt}u}i;Rm)|X7OiyB6()xTH&pS)h%xT2A zm9=^}XM7xE`WEjD6Nxpe!!OHmaA3q(99qI@fwVAOJHccKCKI( zPi0D+UaNB`3e!Tb#W@+mD?e3ZX$87!}V zvMZtA%1M{TE1?DQl}k1ybg%r(r7=oqw*1R#TirQxceuVd)KS5Lv)85;5TLBcXqE@}p#C2Hm)3bdqOyt$9j z+PD>K;vk;1R!%Bw<8aO?Jjddl)(x$hq=6EK&SaDlj29DU5w4{2^%n=Dg^^+)^wPz6 zCG-&=`!u-abJl&n>s) z!|vKc{1ug<9C2x~S%H%4(uwYT!HJk^`MQ)DoGWl-E&UIvLZoyZP(N z)l2=d3b=7!q2_~yg)>1tZJN%c-gWN6WsPenEX}FPpkX^zwSJV+A_p+%bt$WV)NFMH z&Y)9{!po|GpLEhPliPG%`l;80wq2J_K{Jr(qSW5&64$>2l4RV~*<2G;-OB{o+&HBq zlI~$_`aeJkDivhPC)Jzl3O75AC;3fGMM5E{9bu+WGu_b;rm&ZZ91}jR-oQC9lIc+B zDdx+nsnqcsm~U(+rqBs`|H4V*u%6BbR-6bYjuM#}^ILh2^eh>1n`g}|v&iIbbe0RH z0=IE+v(ew&P?I;bd4&H!!*lstoVdB^Va9pd>!97sn8Uo_iaf44+RxZl`BZHV*rW9% z+}o;Gt@5pyp?sg6A+q}$U>#L=cG;|oJi{5H2i^cJ$r-FI6n+t5#xT6)_xP%5W^9tb zzRfec&{r+Qx6E!$o%dFIC?g?l(uJdK6F6%Rxa-hBs&cmaNn7|x8)qQDm3EveCW$%y z}pjzms??zBE*Wmke-WSavvj6LzZYjx)z_e`G2n3e~aaZNX# z*n<(~d02aUEI@fa^pj7%a4p7AL7*aUE!yU3H?B#WNnfBVBaN5u4cvlpXjf{veZ?*a!Pmss67br0Ld0465|X1AVM!A^~=TnIZ}oKjz9 zU{L<1Ja9xke4A>;CY-Q!XSO&E;9NA_7`{iNK-@=xEzTP!BQU>%&M@OT&l^F%Zh4CF z-u~0%=b*m4IuyE|-4LZ=LZNII-m|f==$G>_Xpw7Dd++^9Da50>hMS4^DQ^T(WcTix zRMo#ApZq05&ZE3<=%*3BOF8{slTP+p1RGMlsKmma=*(-7%R;j+M$c2cZ6% zw4y&pDgT=Egc6IBBYO?}Br22g&`4dAKImmqXiQB?o3<%m!}+UwBdZLsmKN_yXv)Z+ zh#sJAv=(5mLIrt;suCUvc2esX3iAZSqx6!2Hclij@YY{y8TYGp74h-KYFJo!wt*dz9jTHBXom-@x36O`*^$j=$zd01g<7t>K&eYtnap%ftyB zx1Os`9<;q3Baj;b|Io)e;`N{9&k|SPSCyC7=@_KDa6G>%&(Tp;5>d@&MpVAjz-pSp_~#aPr=*1BlE5`^KH&6UcU4Cg(7 zdNfKs;c?!l)PuTF=dPer$Vd7`ze>Ly>7%XJq~G+RKFqO(4X?o2eNQQ)9ERE%eJhl= zYtmnWVR+2$D;kJdGY~VW?}>q!rw3xj^*ufivwR>XrEkeV%o4=VXxxP1A-`%ZVNIgQGx|y8cq}F zU4Wgq96qtb<8n)o;QyLr{nhvkM_v`i!Vh>3w=FzZCzSOO9^a@SeWAGum#aoSlaxM% z*V4XTYMTme3)j=p^UJ(m?HNDN`xr^|>#zA3b-X za@GsqWV1QL-!FL5pMBTpSLWV#-|6ho&SmF5{^Pl9K9m0ai1h-S@1I}G?&GJNr)L+t zFrnqvX@>~!fd}Dwc@xj9{Mf*JF`wuN!hJf&4utpP7aq;!$_UU%{_Wyycqv+P={2SA z;qiPFZNqN+^LQ;BNKJTUD|qs&J6U+9if~z5w%&=o7upI#&s66da(S2D_kvPR7%ND@ zGUcu1r|?Kj9vF!tl<^PW<-jYmDzQ8N(?e^!KXZ1cCuvp>-*Yzi%MShA@)_IOL+QF4 zrd7{>iS>S@ErFe-q+oloeHep zTnj6L;d4lM-98?iK2X~p$mwVwSsct8h}kg^^FYu!5VLh4W^T|i5cAqV%#2^PZ0*l2 z3%3-!M(hy`OR}hNn^r3A3%C2eUb0pc_48O+2iDz^U-e-T`apQ;AtjD6jRsEqO*eZ* zfA2?Ko{Cit{WPcXz_hXd7`5lI z`(v{!8xIYB{K)UK!yCz`*`cp09L$7eH}{QD7{-LArAlJ_;gKb)aP})Gy$-5@{ z#wol0o}RJ!Z#d~&)Y%keG>5e~+CzW-1Xd|I^!2h#-$t}h2?k-(l#E3yYiA3zwz>O06y__In?-M2+r*fNvyo_l zOZ>Jdr}Mwkqi5uZBiSR+>I%6gW!%g$Ux>)Z8Qt<-;O6-JvOmso?Y%t|qIdenFcr7c z*bzOvb?->r5=^YzoFBbmcws4>A|RLfOCQ#Ru8dYjghd##A->pVo9Y|e9Az8btizA7 z-Rfhkk(EQ<$Sk(omit!}^WyjXeA{AwWHnovxRa@TVHWrL(49lxpCe?5f9`=!v05ce z^l8Q=`J=0$Q8Q^o0YTX07L)BPwBWx9jI({`XHnuk!-iDe13rcy=@{F;eZvY`aoeUn zkXSXPcA`0H$0Tt`U*cve_3K{tj-gemaT-N3ID|J@hX5-jm7BHf>6s!kGrBsyHo9s^ zb$mf2O8VUA5F$X24p&ZcNLR+SXF9E&o_LJ*f}aB4%a5=BC(aPeOq^h{&Ig86KQL^_ zhK1-I>^j++eYj85!dRJGzPYUav#N4PwZ2ks8L~-J{BE0_KT{VmCgscnpztP%--EX{ z&Ij+|TQh_*ffpW4V|{sP@&4frsw&R4(Z`G$vgKiv{BW@?ZJBRe+6qYRn8>%<>}4|4(!QtIlR`^rEk zAU_{ri#x+wKO1QI1I4V}o+kU=YD?#JHm1FG4Yj!pREb)!Phr|vKF(L2%Un@M+629M zabCRNmZ-DAH=|meJM_F}v);0iM9<8Tl4^`&RK^5N0Fgz%adrc{>7@?y$$) zEZLscP3>fLW3q3o-eaGl-7uso3cve@4S8)|G5QvLs4XHU>rB+T1m(lsS?gpD)QR+=57kDyfE07cRr?i z#Y`3+(@1I-#;1RjPX(R;MS7gS=W-kYNJ}E9EZ=Dfr+$F6mRKI+R0W91)itznnM(Od4@Q-FHuQv z-@}@zzFj@)3SRIvkU!c{fswZ83lnVrQ0$u?ZOi8>9W-}enp`=*s6@xFRHzz4078xY(u5b8QA+d^j^gvIP(!-|RVVGOn9aWuJ zNpxfp)+ko>g@mdw6khKMkKK;l%y``e&E^cHylKI3?AG1Ptvb{)MTyN+YRT^0xLZ>d zF88&dR;g#*?qh{fm=*QrV+E7M*_hQ?XQQg`Y1Q!#@qp(sxWw9^txjIT+8iD4dbFEa3$?!=YeQQxNlZgYIOi()R8^FdNh|hI?#G+= zwLQ2xuleSh53kz0E-@+WsNV(>H$R}X_-qf&^z;5Hm;Qyb0M{D|eI+=6RidhtHl;ay zuYo3??^jhjg{K?#!w#H7Eca~4-<+4-Y2Lj)A9S9F&gbAA4IO#uFUnbcU)!l=gU=+w zB0;j>J8L~sM((EX9^r+2Wkp!7O6`3Hfp^iGUEQhLy*Dr->a(_`8o*6@t;3ra0MF~U zzJyjo%thY_aIt17eve-AJ+hDDY%SZtsan<=zhGHw!~$Bu-v<7B%$bW@Y7<%`;MYu5 zMRiUTwF8_MJ$qH^3x=@;W2^Tczc{E>7tg!ct_j~V;1pK|{YNKtCb?tEj{XG%%kfi!1L0CTiylDI-jHsNDpE=8b zF&|VpG2hV2aaGO>mvw(|fFHRNn2dUx0eK4IfXAlcl%9_g*8~m1=$UD=hC0>*2i|+e zP-V`q!hKmicDd}%+jo607c00h47I#Wu#f#NL>M?-42B5vri)VO^hL57!uyH;nB9rp z_s+Pxi0h=Az@*d#jCbMYd>|&DT2Ac_^+r`w%Nbh@Jc#S^wgt3zhEtEi4ce+eC=>&J z-1XIjOBrU)ZDG1y;md(v_vSNrZ{pD>RrTE{W3Qi9#~=mDtDNsEkn-T^&TGUI}lgqJMxpNI)2}vYJ%Mn z&0%hM`1{aMg`$UbRmuplsaZ{|>8dnzpkL5K!<@ub#Dn(}_lH4M+P3c&znMf(lL4QH zH_ww-rKfv|uXI&9qMS)z4}5buF7IL>%rCnt{iDAz@Mv0g=gggX_$%yWae{hIIVZh3 zv1Vf^^nLcki+d)9`IU!5c+UyP|1F#7{^^gszLz*m;AiAn-zXDSHyBNJQ2?}mC8sc^ zhPU!t&2NErc|Qmnynogfd|q%4nqfl=7t}sbI19ekDjn+z=3{OnLKM&7Cb(@xLz5LF ztp>izEKt9>G5=r_Ysn}$;2P?;z&c0Fiui)hfw8RM`u*^HO7%SrFXfwCUdq>1>M~QR z;tO<}hA6c8i-_SfyKn*|o>e6Hlab(`((byj-y_VawN$az_<|Hr_bFA+<;NF1hx9Fh z1nftHupiCGnL5&%0X?M8JM{PS70mcmST*0scY46>IhKtYdvb)@g~(&6W+JCas1b?Gr^|t_~M1V zOkT8QP5RA+%L$7|Hb*XDV%MPx5a}GFV*=?YmJ|Cc+j^X(Uet$*WH~2A(oK3XU z)MyREreGE-qc;M#gL}Ifa~o&fh2BJ0b_LAU6jL8y)8{cC2MA_$RY?U6ZP78ynwhbG z^f9+iy09%@3z~R<;+!C>zqxFjFuV1ZgdwN52NZeGc3Y6_^k=MO?*Mkig zE*k-*D$MEE$~RjJ*+Xgz{1dwM_{KuR574_2ygnnVgO?)Eh7*4bFrv2P8Rwe(=kG#U zH)~a({i^gF(yG{$SO&B=r(?!C#ETxr=JUr@qn;-^sSB#XK8d5ljacrP-@t6K~99+oV*K0`SeJkzu?N#RPAVT z3Ew7ZHt4H%T;X*mlk}B_YQsJ{i^DEODKi$5veGf&z0%r$wx6$pewz~hrjOsOQ9?|g zp*lGkFe9!nHB_Z5afSVS1#6}}+xudG+o`lQrAg2cm_lZkSWKc*jjmM@LDiruuN(Fow^?TB+u9Jy<26d2Q+o zJSaFS`+3w%y;EN>170cS8Cb!JQ|kFsDZj~#J$^gJ&(prF6q?t;MyeY!d%l1zr35^ zB4W#PBPgE(F(L5IRDiBWyWc^Ye@i<<(5_xVkQQ6XjGgKyxO`LjvPC%;B0pfvh#X`m z&>{Y&1aD^AkHQeV53$*uKkQ~wMhRw|hD|m9a1kwNR{9s$%Zy{h_dQCleLM1aUyc&5 z=^djyx!$UlX~LaC?f*yEl?OysegF4nXCK7{k#d+pMR7qv!`#MgVi-g%D=RBft8uBu zt*9&p&@!_kwZiS2sg=2cMv16cSy|b)nQt4Wezg)t%Iccvyjhs}ea?M@V)gU;$9Z>o zcfaRu=bn3xrz~AH^_~mGMw|?9Hx>|E|mN4-d7>T_c!k= zl1v*_iRU1*D02|inS7!p@h%!q7#}F*{Zu#8Y}`4X^Y-I)lm9t*z2ZtXjuY4~7z29o zZq)xb?>Ew2xQo+cEs<+_RHAf0#5zN*@iAO-Nj602?h|=gCbTI_Ae1?X%0Ok=2%cRP zos1PGw-vL49%cP4vR9;;pf4}Q=aj!`6ffh6Hh+bHqv}HoG#BM0OX9H149Rta<<`?# zScyt_sSco2SH^DmQu@yL{v*BJclK*tLolv2*)$g`exOl5!-YDHliN`fS&5$U{qFr8 zFF8=IFx@Bo=Dmg2h1My}`ApS0$T381Y`S$@ao7IFII5MIl{(LPpTk|BCbDz=40H}^ z>DE+NLq^jD?bL6Ss|mZKp;>+zPT|J2;tk!fCKKJF>-LW%VMFIL(Ap=YnFS#Q(7{YN zE$>Cx$c=xi#p}El>rNCmf3$NQdW&up&UphUH{teEScz%>T)lIIrMuv$i35`zIi5v)#MFkb?BI!CdSvs&U8SB5Yd<IR_id|D)19PpElljBEQ8|)s|QzUu-B&9P0Wg3DfXb*rMTI#BjHui zu&_bsamh2X`I#M>H7QwgZIj1VjE!Egs6lCI_Rw6F!Dp(d6FpB;DX-(M;;rQRF|-f+yq6@yqZYFq3L?;wmCU;>s56$b|z;<1mScoeH_hwgQnJ+pD$ zDxA-Z(n{i5-P))%f#fx#%@@9KDpz9Bx3efttI)cTP!gv)s|@7Ri~mnnw8mXd3cm1s+@A7@_p{>4XsaFo`Mkk z&<>vVJye4aeM0{me^BDIORi@Fh+RI2?ZIhRd7_uQ3lkH1;1%PLgzMY`Pp6}w?C1Fmc57ZAVnW*t-DeR;(pVpoahFTX8tS;MLFlhJPH{AmIEghv z?>A0-hns*OPeiXpn(J;Ece|y(>q<*5^kK%uc}q}EV9^Sid1KrI*ep=&sIA242Ru}o zDD1(iJpAz6 zwoL-BCuHW4E>T@|6zhn$wyB1`?4+MGHEFF1aHRQyQS5!cP@{vabx3CcuNL@x_ha6; zQ!S(b^pIw^v{Fco$;YiYzrX&nNjdP#CebLY(Asp?3D99|u=HU=d>X64qQmJyZ|(#A zI@muOn*5?*o(5SHJe^+oy>efybeaYmQ!P=f22!OvXU4j^TMhPTRsrd)@s*IT$MfG6 zB>VNb(d;|l#M*AT`beDDA;~g@hxk|UCy&B1*krKJ1gBvlTI&b*Nojzg6Hdn8)}OPG zzTSlVgu~p7eAnT1=~zh>lj@|@W06igFKsk?Y=1Owi}}I#JrhSjek#h#u;k~%Tm@JM zfXZ*d0dqem6+LjO4Y<2%hi^&l`c2 zD~iPghUX3EHceny#3sTvjQa)sLnAgXY(u%#I- zHW7IogDr>KzVY7>v3&*G4czZ@|KN!2W7r0BzeD~(5nC;6gSg*&{_KdY3bt(Sx80u= zvAqpj7WdnT7lnHBfV_d+wHCNbZBFe^?fzv*6sR9|#43c-Fvle*gflP?v{MM@U@kN& zgl3on(iMV4?!F2^xJxZ`9IW7d<{qLD^l&d6sSrBAoI7427+@Z|T_L2weBn-o&tW*e7VBYt- zLMVhe{7r>0114k?+g744XV)m$$3Z2l#+&Ntl7iLxH0&6c@u*?Gy0G__(C;3qudX^i zFWw3XiS`o2ieiajNTD_8ST|oT#$*)7gP!zCnRNn|Uv$wZRdH-hsin4R#nU;++fiLw zNvDwKor?5W%Ot!+7T9U$Rvua%c!!%}U<)@}{BLv9=YNZv7yWN?^SpmEH-GbQ;%1Zo zU)*f)ujl5E{&n0u88MIf*K+r_{x#fm`&V=GOMeA7Kl8uF&5!-Ba`OZKDwrhO)?toj z`}W1zU&d<{$r}q=&ZDlExLpIg!0MgLxho6~1A+bMT*h4zcWrV$%U$Q;deJ=t?+zvJ zMeOa$aF?46PTo>mDfnrl*c={unbV1OJnVTYznGVy*#9&)pY(4inqFiqdeSPC(_A>; zU--D`@t=x{BJSCK!xKM0zVY#e5%+Ze0^5STg*@)ve%1MQiuv4rJM5{7x!gXM<{iJgh}Z^k?$$LS9s*CdL)gD{`EhF$)j>QBx~j{J|q|JC^a zzN?Q%ceh$d155!d2kZs70A~ThJ>VAxOaUwcYzMdi5+MCvwQzWM|(V%5r z!kIM*c&8|@*aly{M*}=cz!=KJ7)q+4`b|X}ECR^$KtjISvKSKaGBs~m4q`2QZ(Qto zeXQ!dUX?HDWAmlhlzd6oCtp$x``vA@AH|!JO-r{!P7&9j#^+&uwH~r^Q%LHn0eZ7r zNU{=WmZMfd5|;+jwCqkuBeHY@U&s->S{2h2tK!agR*9>=lmzE`X*^zgbT-Q`$GWh( zUD?rq|hT{G^Szr{^$6Zif$aVvS9 z<@z3w>YZV4L3q7%eI{OwWOmpBZ2)lH4p+xaBV1;rt87!B^-?)nqtoTq$O&fh){ym2 z|2=I3QtNs-?JjUlzMOXZzoq@cRcS~6kF@^`t(TH6r~NBjlP;%y2HKadF88vl(moMr zEw{dUaV7QmQMYO9B(^9X3S#7pmVpP z9Q;fbt?vrEuw8*fY7x@Z5s91nm$ei-&E&k}sWAN{B6+z(hA!?H=WBKR>WvtvnB4oI9 zKGcOBzh)YzQM#-1H?_(Cz%}E3h`4{9L%LoyhpfM5{Ehz*Kl!ToPhB&9=|9Bpa#j4h zuNi;ZKg3VED*o_m#vlC;@vplo{&m-kZ~TY&DObgB@yY#9^LmN}`%=hOp>fiRTTPeU zG>UEmE|I(r3M-~6SwE?D{=a%mDcLAV#F)a|uLO<=e;rpJ`dY_TYgz&ep{(*+a&=mG za~8Z-TI>EHt#!QA^0;a{)&;i9f3370ZIhP5(2i}A__cp?{Qx` z2e*dXaU~pLLVh!w7YV~dz&)#tTY!5ScSl2>h7v1x6dQ3s!Oa^FoW;Cw6yT^fTh~?6 zzs!~qc_VEja}VLhq9YT-I`$Fvg!djFmOl(9VchrN^qdTx3^%ztXLfMq;QSoLBuQ60 zc)T5}u^7ZTI8%|)-IbL;Ah)4V^3L=X&P%~wuWxP?>qitcx8Bq{m(tO(OW0`-chRf0 zDAo+g)#=u>@nru!NNF7p$Y~vO$75alCJ=Z24lyM+i=~IU@?0U+GW`ozS04M@;3uB= zoeGN{XYv6kp%tf6Q+x)IYH{m4|#hXs})9h{Eow19)Zj)?CMZ9}Xt6 zez;p?HObulK(Lc7g@@{FRah{`V=m1!N3lEiceu)Z^L~Y;9k1v3Af=gb)!#9otLSgT z-W26tIve{*)F~5Z%|m!2wFu^Zz)}303{wYK1rTjXMMTe`Fgevz_J}yyb%owav&+m& z)+IQSn;nBAxY;f^oSSjMVcd)glEkDNxm~$`2>MO(-iCfVwq!Ww{*94#fePEHicP@DL*OqG%zzoDx8;dU>i71PvZ>Njh}yX zQf}3C+@?hQ<-C+(Z)_dqOgm9+U1yGO>jTF<+0)d*L_iT>Gr$GVO;-yBn1nNh%W!r; z8`7Si+V2GH-vAguhcM{?!ZGTH)_p$Vm%h56wUm#=SphAJM>#qk*Fi>wklyQfI;dDG zquxZL=XRVpi>f*g%-~ck9UBxDYDvR~5g0({H$5=KTC2i{i2GG3>WE64-sC39qR|-* z^+`5yO>!ZH@7yN*fZC3{etiSviOzw8 zo@n?T#u!=!yedMSh*pw@dR4+r_V?xH zCVwApzV6T9=2}pY9OhouXd_|gs(n4VZ@Iq*H<$an+j>~L7I8`g4^g&{?4+kNxPNxh z{ERQ$TZ@F=N8E+>c9#C^JO4+!qcalglk+-T_4XKtj*UlaRH8K;W_kx3#isN49k~5& zZl|}fQS5fmu3GVjaH<~d+YD&MOT$BGGr$)35iU}j?WO;b_9Xc+)TUSPuAg7S{)5~J z?+)V@>uS8~3J#~Xc;{>RJKn|ZyMlLRNa=s!-NhOi@9>s_ip|mJnN@sE# zyd!_YyHUWqw99zcnd4mt;N8m0cy|hEkBs2mu@(pLPJ#F4_o7V*^9FFtBdHXP9P?z1 zd)Oyq+$^7raS!-pjGN}`&2j4g*ip>D?d`Zdxuz}NRib^40$TA-f}QYg15Cm@YBR#S z*7hX%Le!?%KTUWu|E+>kQpHBV3P9;w6W$*GN~Or|@{2j~>7BseuV?9Ef3b@gC;lPa zEPf~K5XyxQh1-OY&|aJkrUwP{Lcgi0FxHgiSdEv?LdMOmHMo~t4fz&pMhk4-V!>!E zG}@noUslnKfyTn#MH!B-lR;rH4Rr{q=1X6iGuTU^!=4b%*GrMYNl=x--uU1W*uVEI z$xD13IwCII6_q%*%+}6AtH~lR-D2zFr)|a7oc8 zcON>OwiZHunwBkvG&#C6eI!pXB|7S1E4Xy8aV4Z~j9ok<>j>O&=cz_d1ha}_!O^2$ zeCPEsy@fxAKXdAAdzLE>5_^efaQPIY2#H^dAB&4byLhWewFM<7COYFVWOm~IWid%y zq(tpqwpdZ5n4!o~3{Z4clq=MVe<@x=8i-j*ZC86fI$`9cLWJzf-d4V< zd`Y=P`Jr;JGD_)JG%3DTe59Z}s2wTAe4gSg<#grE%2CQps6R_kh7~Inixle>IW`f} zw6rWS*fvHMk1<*?SYcFL68{j7i3N&}#Hor~75ha6`kHj_6oix(;-&@7wG1y+hu-Ud zo)I^Nr%X&0SJM3j^q>u`hjh`fHz{;IT06><1dn46Y16Lee9)uNfa}BNs_-f&+gYSvPr5O81uy%h8I1K0m z9R3vM2Y^oinQ$*hm>G|#g~c!xO!84Jer=I!VP<7zL_c1#I0`McXQ!C;hUa69Vc-rl zOo~QR;YYbTQ#(tRL&HcfpqXXhZ8`X{uv0d(Zi|If(V%7q&PmXIG1<-B{%e_m^p z$~5d5tXyVxp^R`#mM!HiQ?gIF@4uX)LB-tI51ZLWCrQq60?)g)XxQ);qn$>=G-EJ%^3JdYj+$OeWXTWiv<-uA8er3f_&0ak-ZzD(x1mn74RnWI*f_b;cnDH3wpZT2Adn?94(h~%-BWvJj3`} zV*DxM)36Har zf4@ge#6yTY9#aY1uf7qlu5&PsBAvFrFwZ6CE`VKA~T&sMW;QH!}YKsE0Tuw?|D<`l;lcrdHg`85t zj}NxDy0`hLEh`XuFAx2ae?4flG!N|BE@qieQwmGq*C`?yY2>9%@CWV*3fA4f4Dr_S zc=LIfWbMf2mD>{{|2yK2scX@4{(m|zMX{C2(1n+w_;ML$qYOj+k0I=%JnVR02IA3q zXuCM{*lygTqfU!`Bva>p9%2|mbbwTMGuu&mhlT!*@jZyYxADJy@wXlRj%*%}_({AD zCLTkZkqimG$94;;6sMQ(PQ)3;<0SGpRPQX*Pc$Cet;guh^G!mC3?4#(5cg5}S5Yaruj|kDeO$zkiBMrfS=sm23B>4d3{9d}S zO@yRiS~&vfdB1l?IFpBHs)eT4FopODA+9?O?Oi@{9fzwYuVWQYpIW6`sW$a1XjAoL zt{B<;`8F}W98oB6s+c%YHQ=jyZ-vVBDYVlRu#Gy`47`DtEbO)k`Q_2YmzHgV4sGwJm|6T@#=l*{G&X35pkM=}DHbgoD}8Fi{-Cmq?-y3K&{|D< zhp=}x%A;gQf=6mp6-v-!%FtumBqe)4D3(vGspYu#n?I?xhTG1fErz%S6YX4}yR)_$ zyF(p|37&>d&*J9v?P2fW+S4_)c)>UF(tvF-=S66TqE;FmqLCH$_Nt}ZVIBK3Ag(nP z()wNRd5fpf6bO4K*2*#O*)En}iJKM@*G@h|x57I1T|j|22R%YB@Rd7{;Co9+)x{&l zSa)G|f@y=93v)rqio8VojI6{$c}4{$>=Rjspu8?Qc%N?AzRYICTs`tq?;Rm%N1-~r zx?Rm@(XjXDebizC3kIj)m2Bq_ea-wk6W=>Py(|k)v1r(<;jlNMmSju4jCPNMB)Z4K zt$BX1kNlUwKLP%;FZ);To5Sn#FmCNP^A)Jw_LLTL$TL)zZm3H<>Y~0}mkGR%iSXCM-xq4F z`vi_q9s46s0L{etY|>Gvdo(Z=BSh=mSWdg~Zj-&`o(J z+zoqjTC;oEx=KM$T(}AHJw&!H1Q|R? z`AD#{O@Kt%5bg*|8_B0G_;jOS1eT(v;PHfh!#n7fh?RxhSo@|RZV@P{WD7$!E=Ck= zA#ZxP+M{GO7#(;;yG8h}kb|_gPVvRYj zVZUR~cjNKi`5B-k=|D@;LcFNVgVhsMK`r~PMaQM#cISBYn~x;p8gjL)LpZng`pa=n z26Ok2+%g?I;rq$+5cmdkpl|g;Y7;R7A9JI=;O6|c{uOXrwX>DsOX(zn!Ua0Eug*i% zQ5`$zdk<9LR(ZNQ_FhdK$Lrm`y`W*y9%KvVQrg!Oj}P@B^?CX{^!YnV;SByA0;u4g z3?O}jH9nHB|4VnTfoi!KH@9Uu?*ilZuayMZ;6hXAcK$0Za(5K z(Xp|Dd`^WJ`Q>94)auQ|90nf<7#{CbFtchjxBY-vkjIB{ zN)*3-?4(4!sC;;f=0{HR8_auh=H8t}LcasC(9(DZ|x!k&31Tc<)u zsy@&=uN(5i+=d))K)jzpPoldN^uP2i@Obe8&|UyX0TRHl5IiP;rGR$8>FWg>CX`|qEkudk@%|w??z~#0liPZK+C!d3yBR-~J* zm3~uEJfu(13#al|m%O+2>BsJ${BS`|MmN$Itj0_zu>Ifw(Qw=QuxYt%w{xf+@6Vv2 z%KYa$&UjP$Y5J2`L)4<~5$aEGR^D<5x>D7dUH4E5N_;8yAFg=JWas!FN`J-WRA0E9 z>eI&(kdlaSf@x^scL+-*_>a50!xga;U))f;`O^CXSB`%0SPVi(n?99#69`B7<{~9(%aP6VcEupAha>rob+y(& zbBt&t>PQXl@yy5?T6mWyj=QNvZ}m{!x;rL$XD8`XkNOLQt*P50BuLqK#cZmGSukAXZyT&^3t-tL$ zaOXZlnqX*2Q((U=7*Za^ScMJ+A$=C+?Kn`KVPFyNGL2 z%ko0BKi1++Cvi<`vEL?cNi7=@B5p}7yD>!El3F%6MBI{EHZVlol3La;MBI{E);shH zH+zIuanl%D$<6CSE4Y~)dYPM@LUcd&61VFQ(v8|d+^Cs~re~DYIm3JP%b#@HU4{4SvWocsZ(1I+74i9=%A#dQ4@`$#)=n+hZ+ncy8ymc`fTaw{hD;Ew6b@=ub7;FS%xzP0h=h0r&Kxt~EI}4R`1BR@~`J zs>!lxxlbG%;Et>m`;6BUXlWL3MJ~wio68Kza zi{(ByaGxUhB-!G)PZsxi1n1i6c*z;h{boFdwF7V%a1LN7!X62*0I&*h1P}zIJgydo z0SW<5z-GW<0P(vMpEHe_bl^L!FPSj=AT1hS`7k%Yy%#VHb|-}am;t$fBY+ctW`GX= zlK^xFI~DE)fH`nG07U>NU@4#+K==IXV5)hY)TmQ&^W8g@wgg@)p>8w&QJLtjqO5W_ z`bgM?{tqhn{jAkZ^oHTsvyXQZ^i$$xxt3QxY&j(zsswBRln?4A zq+!j|87#kebA5Ndd}4NNpK+2Zp~-E+8+g1S7^)jg zTDw{`caiQY(eOR;zeri5_b>^ra;Md zxab}S;~x|>1?vc(cU)47m^JP~`TVK65HD)R@}4fZDQ3*otT zLt#vLA?(kFtL%k^CE%`Z@|J`*6mH4?0sJJ;90~X1;U6TG-cYCo?{t%QUib~$50Xwl zrBG$5fO}SWeO`It`n+~_alnyY?FwVoK>lpW8XIIgonK?y^XuFMCwsZ1!WvU#tE$MY z$Wz+e+gFZ0#KxD9mZKPl`OgOnh1A|m;aicb#C;CU*kek{Vei84D{d<3WzR%D+2JPp zFW#ZjOnZsN0e7EpubIDiv!r+IG3AwTr-f(2oguw5Q)NNfg(lp{&z$v(*C;j3j4ekE z;L|?*4t%;uy=KM1M+cv%a1(s=lEbzz50b&{c+CVFT>FyP)j7Zjm2@1f(BzduajW&^ zIhH287d(!(X!8C6p7M6(2Kf9Lw&F!wE^NPrQ}O*N)Q8pMm0L{30Ct-{W_5hI4!)=O zzi;sGbNqvpF!KO*lm8AYv=CJ^d0pY#t-s)1CQ){qyr1C~W6`7%iynVIf`$i5`~6UX z6=!z5({1wBg!2)4RwVRp9$IB-htQ-Cf3;b}?&UhSQQ*SloPJXE$pu zT3RVxXWeoil{XYlww}F@%Ha!lw53l=adhU=!!_(8@L^>04?BCG7V1u5OdJMe1D`=Z zISOSpUH%RH?*n%_fN-AhJqI8N*<+3;Sj}m$yAj3($iaBXk01+S`c5cm1smkn(E52s|+>>SAbzVz*Bq<$&mvNX()aS?B7@-D)gdYu@Do_Gv4 zjTj5tJlIqs^~yuADa07~+3_|x9=kL_>?V(Y-zSaN%j4fSvoWST#i~d6$I%k$g_O&J zaA&K%klN%Ce#6z|-O-Y0-BL*H^*~sUmZS9V3&*TIdtaP23HH0fv3cp!AkD!l?{*gY z?(!VLTtV$K3?{WrA+G>*!m)8a5PTcmTNfG_GklvuiMpxA50*cjs!L3x>w` zv(7s~agjr(A+*5OI;ZeY@vyUsO{`^Eb9;l)KZ zs59a_^4;dAZgDkXG_6GsxfR^r1p9+-CAa?!`+X$A4Pj2geybZ&@L>NQ_6cqcx4U7t zy0zT?1?(g6ZkNJ*4110{irb0LX`nlr+iSu(wK3fGZdhL%%Wb>DXKUiPZ7UBMcU~1s zZz!(+%j<{}I47A3sg^`>l3(j-*@nHzdD_pTx0RjM3dbS!Lc3D8GHT_go{sQUao=6= z#ZC1}<;n_BnW&$hk*tty6Swj+PZ0grfVX0V_n;{bC)}nz58-$DBJ5xAKM7_Izz#^e z4YVehFMW7%Slcs&yp~84GA5joDl3N-oszb1xJyc#NHgmxX*0C#Gg zdCgc_C-GVo6gQDm+~}M|a}A9V=$%K{Cdl?tTARFYwb1)h`bT3Z^IhF|U4Fus35SV7 zsLj*bgzmI|>R3o8f=uY62aODE-a7x#TqNQwM<+S5x2F9FPrHbx-8xUTJ>zNq-!oo1 z_!zI8@zR;Swi0K&^Yni3BeVxG^AnU>(cFyS}%Gx;JrH}<^4Fb#MIiN>d41H$Q+v);2@mV(0YkX6Id@#fq2g!*oj0uy$cO`)F8Pm`+F<$Obal&qBYQ9P(a-6vBn@c|YQF7ks>-4e;3+@tFdj z-$SS2^Jc_nGJG0BhM8x*viu5#VG?{!gbLxaG7@Gye7+8i=Xrh=+JHaLME;D%pHD)k z;qz3)X9Ro>gbcIJV&0a^H55MYh6>>`JK}Q#e71!)z-L;-CmTK+L#N?$2XALNW`Fp; zb}-i7ALoXP&{Cl73XO@(D{b;psJ_j`NRNo7+?xu z4qy?W0`M-t1vm%LA&oRZA;3;1(xEws)@WL!V{=rn-36e zUBfmUptLAmN~bL^D)|#_{eSTUUi5y8v}K;ai@0efo(XUP9HoDHe-YY#DdI^83!S3u z*HBK4612`ja3(_Y5?V5Pua1}HbG(552DtiXWIg#1+~^cSi8s(f9qE4|JutYxfe#dW zvxDK*yhI*<>wfy5Sv|(O1p6(Ff28*)_Bb>Ka$rtAK0JQ}c1j7@SE<=cVJBn?-0r%O z@2VcgY1-yic)*b@WUiMKdT?eNV#Xwn(Po^i zUoVN^+B*hQ3G-W+YM5@AvK}o(#uxP`q1Z%7ZSwZQ&i63n4`{i|2-gAkE7;E@?7mSP0n4NzO&7^L#WVc&P3&sh_5xhB2Cts|>f@HOb!p}MC9S8y)x z)t)(M6Y2wc*G~Pg9B$(Arv5kqyS^0nRb-~e)Mvh{9k0#Tfm)hTXmV(6dPkQ+T4m++ z-Cw%^>5yI?(yuiK_F;&t#QR=-5wrp4k>+;CP4th#XnV!fV{cI15vyV+S8f&cB~dFd z{%eUh7(C0hv9>OAp+yszqh>qsZWTI{ZCxA}JsR6;2i=hA%tRZ&{Eu_{4CFNtKy{${ zQ2C>KbwM8b>JAnYa_Q`y$^Rn}ht?;;+niP22}6?3t$cb1)as?rD(USrczdDbydJ z)z`qK6q0r$jQ@bnhVYYH|F!4;F#OcwQnSQ;v~{ zHf>usUGL%r~?Dmh8bf+RT0^nPYzwsWp28{A4t`bW{E&>*l=8HbbG#p?4$|Dxr^b zqjlqW8Y3(GZ}745qJIN7m-%1k<}+N1fd=n>h#s$DPjl%78ukR2UZBC-d7{T_*gP)1 zK*Jv9(hD?f7METCeOp|5frd@<6FpwT?%`4lG>}D0^mq;E7(|cPu$%ovkJqpQe>pev z{6v%2FpEFh_#(HB^1r~%;r>!?4)K?86Zpo>0sdlc_T@4PG_03j-g9>M%X`jLzr5$f zdqCskJYFnvJEY0BUwkuc_d~lEPU7R}&rd$0wnJy5k8XxF><1sw+%f)rwCm)wKeL8? zRdYA@{lZ5ycMbd0H<)gM`9AjpAJN<~{(V`Xl;|^vvpkV)R_sx96y8znRP0q8P<*KP z6t0fKQH7hE?S$_X#0!J@-)Ae3ZR>qmMcNFyk4R*ni(iS~fS38d;?JT^I4h3jVVC>n z6^-GxXMLl&S?1ewInLK&y?9dmN%V;45GPY{0}nCJ_h(TKw>{*OeP;Ty9@l0(#PjJ2 zB6D{|Z$*Daw!$t9QFK@Wn)YAjPZ#{~u*=L~8lC}N955BI2(STg7;qL4`z+Qqz+}KW zz<$69KoF3!6#GWNRKOy@I=~?S1Ef7i>7O1s73OllVZd2H3f?-M!`PxVJqRpp@1TKr$(i?M9QJGVCdzxQoOsc7~ZIX$Y|&c6Kxw#vDQ`z66oV6Qm) z_BS&>#`+wL@k^1h{%6U<_BA||numIzw1T@roC5Mh=_}mj<*rcaO73FZ)xNBpyNDxM zVAq$u$X!2i*NtV*bJub1$}4-0yS{;|xcTQD&lG4)dmLf!vjHXOcgJ=>gCTeW`mnDg z+J6au+DeP_p298v1&5lAL!6P#pYC|!w5jNkj0D^aJ={Ciy|L)#n#c0#?n^WzJ07zc zF)tyy`7`roSz`=|j+uGjjK@9ZYYp>@ zW`bYdzKiC+VsJD+Y`>M4_(@*=aV58K*8=XkwPYf9J;YsmN(#8^0q**!#Kv9sa@SWS zR_>a@T|bvtxN8!3JyJTByYk>FZoXs3sDjZGw5A+;mSX^RRi9%&L%NcweXN(;XJn*( zYD)%NbL{icI`47%U(SvpR+YimK%)q8!^0YB9I2uI?*>o{$#y6_urZt4YQudS2Xfnc z;q=A<+*TDfHPY)ZHF(;uZ=^l6n!O#)YV6K!8^iq?)3|MYxL2c*+bY6ojj7zWGTg1P z8@Ig}PHq(H2z!AVVfYX3x0NwMdSzh?N23lEwJ7HU61-F^Qmd4k(ZD@E@BdsTDHW=+h zH@s?`|8HodwM5POpq-X+TTiqT-QwQrngfjZe&@8JX}yb^zurlAsm0A-?R++Wsck7* zy{>U751$a8-S7;zMTZ|~Si)`U@O=$s+$MylHqhFmW`WS{4Yca0*`?6U4Ycm4+4<1; z23m2{>|7|ffz})~^MuAU5QRg{ehQ6fpw&jrPKJgwJXTONArm-~gS{SPh+%B}wsQf> zp=SRMc^av7Z@CNe@3Y-!RT}0t&MBBPK{S2c_z=gn4jfywm>bNHt2)1E8s>o?o${K0 z()qA`1`kDAuLah#WEyu7$FIOFCHHdI1@4+uayNI87E(OyEV+}r8n~;lWD0lvm%A30 z$m8gH?phETM=rREn@?>Ui+LbxZrEGqBRv0hn>-I>urES158PBgf3Cc`g}w7?h7{a5 zL1B8B_el>IjaGf6Phul|qT6ZAH`z>9jp1TLzk+@fl&0Go`xNxCRid?MXKX;OBbz_j zLEJ)Wc0=e;Ls~)i2`bZA-fG{v6Y~>o9dKUwuHpKEZh4|fZ{NemqKe1cy5lDgQ7tTI zIu~@Fpf>dat`pAvfB4uIe{-<*F&#Km++47O_zjAi^LE^le@otEtIja7 zp`c*G1dZv{hWvu@c}mk1PhNq|mN{4MiMxmr(5&8-$9rO4OSo}N!I(UiX`Dv^JotL6 z-7chNFjsJR-iKHXgS>w}apNt%ZMaoy&@|joki+{Y3+bs@Hc#!pJHPh~C{Xit zwrL{NqutcGuun*O~I6k9DNb6zQ2wsN`mN#(kZ371zHu1h6u!R4mzTYJOcV-k#-=i=od5iZ-Rc>2?cRw1o zO8)D}DEhUE+Hz?mo|^p}&L~Q%IUvPuI3UHF=U34?A;MGTHW>>alMIw^p)dmd)PqiI zYL*xvK3+9T2u!z4&zoTt`pJ?T)Jzkg^O~9|0yXEg33O`ef+s1ENr2b9u|g~aAhZ|n z|{)ybI_pAHskfN6D|HAc&@&h`b zso9r)oAqXDPtdHbR%4r%J+!X}WCSR|M@2j1l#(&LM`FVhtNQjUZVv36UNjH$w|A#J zk6+k{_Yy>LwVd?C+9AIJy>|(efVnQ>&N8#_oYRW#@BN^Q>@Mf@q6c9fby8e2JK_W{ z0;IMK=w)V-b24t|&8*o;FM^1R)Lg1E$|Kc;`!Z@(?0G;};v-d+fHH5!9CGw|oUZ_e z~Cw<|@)!eV%=a1}czxG{he2)A5+s7K8=eEy$zcvz0M9mKS8XL=b={uqHqo{5Z zUjSeB1#dj0!Bg$}mShwS%6JlQ_%oqlWjp$7p_|@*Q*X^I!Pw%X2a=*pON>1|)W>*b zx%0nxzlt}AE!eACzGw`4^)1+;T0UtU*elyjeLldwAd<#SC8{=QIE`wLgO}&uIBga; zAKXE$Z9)lV12;1>?Cv*10oo4Qu4p(vWEjzSmoufgVxGcl??&E^@widdR zBrVvq8@b>u;PEy%iJ$O-_a^Sz7Wrr9t{u*P{2yNIAkAJo=+)=JAkJEr_Kk@}*%^1Z zCl%%Ny3M@`?R|^8b%ed-ywctpv;j02U6zw^1i0S^xSj$y1iK5c7_bzu98eBe2RIEl z3y=WyfK>=z0cag#@4~(gumP|cum!LkPzl%z*bg`ikoj`Pxs<7D-iJ!diN+zAXFl3~ zOVMDMb=#@z#m%+bd!Too-*ctpT9lkM7~q?=rxgbkrUQ*B@L73zP8UthjS93%R(s*eiP~Ew-MRtbg7SEO$J_l(OGecX0+Zmh!2AJ*^^2vlPKH}4%g+h`HA*oD& zEO{s7g_TyS*;MS+dmo2xLZiUM@tL-NV&k4@Z9VV)=RO zw*>5e=v|L65Hh=XYUwB&&Ee5k?Y3Iulttq39UbCO{t5pWL(T_svk@XU;KWYv&~Dv< z%xZcsT2{HIN^KF}p*QXSl2n%R8nMFT?G=o+dc6HwqVRnTJ?inMx1<+Bsv_=qsIO_) zn}A&}l|={^TON4e0rSgW-8d3^qMJ%dF5ngcvcBv!;(9Aau*cid;;Om{)VyylxK zc)Z^SM9!1q@&3DI>J1xeNQcGV^Xh~@+^3-X;_1n)DfHs`xG>%?xHFK`XC6rE=GqcG z-jCrs%ssWIHlXw7T$gIe9UJymqa39u$7=lB=PtYfQp$eS3SsX%k@CF&|DDkLGzno& z`L&j*&{C&Nc{Rg~%IWU(8QCD!Yc)Xv%hyHvOpqPh&U&}p0Pn7gd zyqyy^eDJ^n&zRo@o#sds@s~J8SHhka4c;}p)heq@+5<_**l9lGD!>|~Vx^ozfzFYc zW)*u9d;@u?)dlaDz<^2aT=cAfl_%_74#`dy#Aprqz`-#*{Wqu^M4A% z0IP}(2ob*N{r&SYZJBr%rQ@=EWkWUxJ!GR4&;ZuOhk5AJcq zZ?*r>#(oTTd2Z>4xx;ik5jU2aG>q%fU7#~233JFWoGtd&#OI0NB@{+OQZd?TApGn# z(U<*bpOKE8vGy;siBLE54u!A~Py#rCyxPtwA7M@*zjinmevbJ;Ovf3{qQ%KX?MknD zgef93iD1&v+F_y+saP}a?pjZX7~TAY6fHDi&q!&-;5=9gPAYwMY$PQE-ebl%C^h{7 zHQw+U9H=qP3y3Ml!Mr|!R_6KcQ&LXVTHHztX>Z{C(F5m?o)+aEt1T~Yr6g=faae6D zz(dx-ky4Zd%BxSJu=o0x`S0~V=R338X}JX_UhLtyzt+;VirS@7(pZLBPf4tD81Bkc zct1UqW1|FIC3kahAEds-*4=68ZISWx*C6SukbSav7*N#v$nH$;{vk-TS{3^)NZM%R zKk3}(%b?I-c6aCQkAp%#*=@99HJL!MYWIujG45C{`R#%?##dfN*sb8$9R<096y_aT zdnR<@_D%a~&-W)z{8)>tdRsbNPU+SCVee_2;hH_v_Jm`ETZB_5fP1ZP;Rs*P!Yu)G zFX4^^kPaZc&Ve}@K)9{7)m^yjrCZJl-NXT7&I;{?-jbVq6uD_^7jR|oNbtxeLiT!f z$AXR%I*nffX}F{}lXPCCThdrVm_ldS>HC8={u?@9cQwvR6;@HNT#|D63A?NI%(ufF zKY6rBb#eF{WIC%*-vL49smJ{FUzDBl$m{@K3zUn_&8MUdkk7PX#TM8$?|!Y4UN6i! z{k9Dfm7Fwl-RByL?+q?7EpeN$Djhk}+M6#8Etz*BkP8qXvy_w=tHvqAQOQ~nAj zaByc%XxX$X;+Euz&PD#!W06!Ljf_-1^wESt~2iw7udg#BBaJs9#HFVUgUrOnao!jSh z?8>;80il5pKcAd=5s4t9|vx=#ypkx)VI5rJ@wz+-|U_UztzEZYv>HEVuJ(f zH50gPKtQu*Jh$};M6aQ1Gz=-;ee-$aBH(excu!kS$97 zXZ_==*w_AS&$}2Kgu7<|i-5;J!Tb>* zt-xL!|4zbQ3G+0}Loj1kVxJDP2)Moh<~l$HU<+U~U=^SoupF=yuo&P3ECS3xoC>V> zn=w9)0L}qY%CY7E76CQ@>H)DYgWe681E>HT1qdil4qz&v9B>%01#kkO;I*0KFU4HG z0B$YtZ4BmeC1xJM(4~;*SqeVi%X2r?mFm&~;S%?#(yyz}tOjpxUZO2-YS*b{m>nu!=Q>byc({RB&o(J7b20MyiUP49h#kzovZ{bb2S+@+i!BtSh^zEEave zP#v4+exhVF_BnN~c_Pt=ghY%<72AuHh;sS&a#A`8NaMpQCDLe*G~)8wJt?Q5pRY*e zcm3do>&Vktb{MIs! ziNx=H&GAT8{!XqjFAt?z7xutl2#&=5tXQdu4`FXd7W5k@0u8R7cQ$3VgqbXX(gu;-F355AV z-4#60wzehkfR_ZVs9d1qX<4pW7HZ3_RTRIqErbZQZw%tgxpx1D6k6-zsHV}pxbi}k z20KlmJnnqF(!l$0Gxqb9H3xk0LZdgcCB~+xZ?qTvN8LBOdLIV33C^Bl530it^oS_gYD4NeOYT6&v% z6H3bgzDdyF*>K$34?C`6FJu3I=+8#y!hEs2@Cv1u5UJ_@Agh_1ze+M<#q8sHq(JOG zh~Jsu9;o&qdH1a7ZP5WgoP;^%_Gehl~m7%Je(CnPQY4H({-n41)$DSM_?QgxN3;a`P!IU>JoW>>kEL zy~#t(aQ%!>r!g-kQYb-ACxeGr#Y5cUGCirRGRzlDitY$8h~_*>r3+*SQkhEJ13`r~ zL;taGtKMjc%v#Yrh2rM`Q~!^E}~0*9|=8m7j(Hq?6<)l zg)<+~(k8-girDjZscn`j-r{xh z|9D^0=>0G-7nqzF6i0@4EBY(T=hmN+_P&vfTgOK4y8-2DWqG4l6`X}I)zGggON@N$ zat^f9q+MNhy@m&J}B>AE{?jdg4$a{rGN~dauaHL}_DDS|!rD zJK(hRFZ@_il~cWL!wJ37`*vUy?3-~yZ}i?8+=9`nCCbT5gW(<@sZWKQ!jA~_hu=$q zOnl8K2WSsdXjFeo^pYw6@iaouWKaBint%I8O1jPciwAp0UQ?9NOeu8Y$q z^AlcZ1B4gKzu?6dzl;|Wcn%xA@1Pui#S1z~sF;H1P$6mm5igVxyeRRv;)M(P3uU}` z4D=wvi%`7HTZ8y??BF;aiF@i`pCES z(UDl6W8DAjrWHw8MXN8Rbck~y)Vq6Qj{Vl9;W-&B?TvdDQvcoV|H9M1fYyYsUH!(3 zY4n~;$Zl^mS+?Up#kXLk`k2q-AG(x1@%J&4r4L`*>F*8r`yT(hhvsjs$)YpPSzy3U zTLqrl*+p9ytsI?Gmx#G<6QAWbxS})uRnONZtV-J!MDsmH2c)u+r&8|XCD2A2e_bH2 z5*qwl!vB@JI5l}bwNb`G+6ox*B=qkKq(?5fpJ6)0x%1Y)i0rko!x>u(-tTn8o>uf28If#7;*UK9u&> zVm;OiucR`AhrlgN}voGs8>h&Tq}m1*p01z`ilY5nVw z@KT27(waxPluqH1$0+1+l;$AeJA6gq>qz~7=H=~^x1_RtkzhiKnL_V8S1(Z7=0P?e zQpfQt!=gw2D)v4oe?#18msBdPuqp$41i7!bO7>cE)9HqKce0dDbvq=i%4g=-R)54g zq{5y0-WsZtj?P0$5%&^Gc73R#1{#)memrgDDW7njLY^OUZjLj&+&|(QE&P|aGnKt9 z&wCVbP$T|L|0U#yr9q$N6`uDrHlnVuhhTUIYcxrs&cQm}HXYji{zE#`D4kcXN+-F_ z)m<@`*1;bdRgFRpRQmol=7O>Gn&mY|hEAhRzl)nz&YlX>_^Tq zuf)1boE1u(|LGQ3$-d)t|G&!pKjR$pO7=15n1_xP&M~iK2mDL<{~FFI zPx@$xQ(noc{KO}(gf0%^lUK5L{L8udrl0PUmFx{a@kEkrD!OS_vel7vUhyx1o8BBa z%+RN~FVfz;)q=Z`QhuJhf+dCAwV1mumh|msW_Op)22X^U-BG#? z^P!pDTB?9PmU!Hns9|;rs>aONUBL51|AQW5W_wE%6%F2x`P+MiA>WGr_bcJEKjLGB zPtd1=&z^|SIQU%fso}FV;xigPzxXupc_ZR86h1%twD76$$5@7-b*{Adw>5&1IQ4q5 zf&Ofpb2ps@>r3jI-v$4MI|x2r%RVz#)LH0{n=8 z3cv|~ZZ-H60gC}A0DabgW((L0NLmXT4q!Rp5I|T5H((v09uSN5Z3ZBJ73jMFqCx$H zaczg4cr9o?C7LhMfD19c$^okYM*$}Q=Kw(f?YlQ~9C{x&StFZ&*}0LsD&Z2CqU3e% zB29+^3pm$v*GBHzUb2R})^gXIkn};AxQe?zD0zju=yr_oVkLJy!(FU|xFybe<@^7p zk{7v4zW*0XpXV<5{@-8n9Cyk0|Mt>nxQp)p!4X@!n7gKOSHIE{?z)w`dX^S*mwf-P zDP6=}^8J6f$|A&Ovl7-wgkh_kS%;T;M?)tcdcq-0&(^@jC9_F@g z@Gowz{6Eg#1U#xD>l?pYFG)H{X8{5Up_2wo(g7M0Ff0yDr*pF*VN+3XY(brlYY-iS zIxigpisAx99X3a{aabh_aSSmgh>rVAcK{7U#wdZt^r*KC87{|}e64qoB?XJbT;a~0(K z{JfIxHKe04nTq?5%L?y?K9K#61iR1Q)u*q!OF8Aa3wKM{aU@|hx@2g~z49LP*vsIk ziZd>iW6XT(I)n4AC%`SXzC&YZP1o<QX^fOqmIeDUn3*t>MJ4mtonlO3U-+yiqY!p+p;viS$&QOb&Ebp`T9#fA z^K@99+8FcrjZ~E86Qo&=IhO7Xe*|3q6s`sCTe!<`5}bgsrh`j>8w-~Smj!2o%Y$1C zw+!xKxF_J8a2w!u!tH~jaY5tdJ@|=-J2Y+_7!M4SYzf-zwpgCU`;d&&(4K?%yZaDc zsZg`g;84u&+IQp~jJ%@*QgQ`Qcwt{BCe(1o)cw3a%3hC>Dk~bRn z6flN`vXQyyze^^Vzm?x~F^EwqQg@)dy5?%nnt86UmOq)IT|Fi(tX9It>EQwY)PBrE z;>{Oppy^TLLafKMje_Bd>krUO{)>k<{Nx$Rp)Hvvt1ITL!<6cBAupA2 zcGsAr^V%)fgoPE>n_UrS=UmgmTEv>JoWr5UC{MU4#x)A@IK&G(I-}ikWe#)T@P#Qz9bm^mo&z!mmYQm$I2_crGsPT1<1Rv=^BN4hxI#9 z@78wS`2D32<9Cp88t}9ID-h;xCvL!f&>~CYDMflZpqKhEo&txOst5d0{gC4m8{5^! zkFUolMhqHpKPxd74nTfSe5ZXV{ayGvfUn;vU)S{Gq(OY^`qx50#RcBMZYOi)Rp%(v zD%XQ&Gfj(K4zyPQj6XUw;Lqv51!>k_Pcu1ivYMM20e$1OhuA&nHHm4|lyb}$u(iw& zKFLom@Ko?Q^#0$^PJDmZFJw2|fv5N0%4>(tV)cQk7%D9@{_2ki!*T$~mn7oP?LvsXE-c99Th3wk z$V+d?adoWGR6;q@de@CrZwT{GwDl6o)Gk&rucM#n4mh2(=0VCw$V-F zI7{D@vtU1zW|hs(WeH0QmloB?%&w*L*v*`e4f)I)VrywKUH~k&6?5YDzDC7bFzBMw;b%g zgdh&LH9i`rv=K)=N=b4=Th_APWgmN*V7R&oF&L?+!!Qy2$E|(Ykhu%^7cz# zMBX~bUWch_Ywn1Yt&KR{H2-qe=+NeC^Wu`X<_5LQSgbc>8mPZN!u&z`6HbL9|7UPs z#6rr9Fmn-hkEDr?hrV1Yi7;^}HKk=!WJpi^0dg3bE)_83Jy--MJ%4ZY(Ov*{A^K=9 zz)64ZqrCtpRrJxGf|FkDdtP~N?0ZgmKHo=s15PUMTcoy7Qh+^40NO=*KzUN%6ydP8E-*x{HZ6e<42Tm4k;&8S{9L{4- zY8NN{pu8P%CX0`dtO7LFagybIo#P>eia~}WCCf$9(7+MZ1jPcl&;L9^8L7-UVIt|8 zd=l~yv^yzv*_AYjnav--U^^fYCUpT+uj(lc9Q1)9XRZ_oC z($@b}a{2=DO{cMXpf7n!8h?FIMrb}{gzm@MXs#k7ROZTsWD&`k`uw-ZLvlpFL!4EJ zGh`jwO zuKEMLrcxo1xYIh}WEF)^hrXIFga)M*b;3hRD3=5+HdqA&V^W@!!sWRSExfP#OOFTT zUxv$4JIjp6*gbcfm#fH{c|N7AA{vrKu?6!=c=mEaU~vR|*Bsf^Hki zi{!N^KQ^TVW9@DipEAs~M8S&xfcr}Ldb#Iz<=Ng7#IQES>fRzH?A-Bi$3kF;1Z{&B zMbfMdd*W55^VkzJrgQFHxIL8ud+-l9u;awclYp2b0wm*Pf%a}Q-t@}b5?8kJ7NWd; z;3m5wjL88Tm(;SC!9Bdk&4BZB5!OtZlsnxsmDUlg77evdHK|SKAnSq>q+TtGB++(7 zlIYJ0*1raA2m zy$1Iw9N`q{$|2j0WJmAU{X_dx!o{pwE!G1%tOl|gSlB3KQh3PWno0~J^{X1a%QXIo z{-n8kRB2k39x-t)77#FYP&Q`nq3;6fA5#|$6QGwW7(Pvb$^96M%bXREBQV|HK` zVgGcK{wDaloC}M(7%OUJ_+nH|@0HGatNK(;uV2>m#$`Bd_w;O#G_1zw%++UTuIm55 zu(}?;lSx12W_(UdldPToFAr%VBD+A)oJ!|3W$Yo6uEE(X^vzp@be4t-jL;NYS%vuat(D_Q}(*7*mu=!9K zpN-1+e1A~l^;vg*K464f|D5ELsYDvR7u=S4Q%k2pcRuZ{IjLRw!a(kkNjcbo+-Se$X|YbGlG z2xX`L$D@ZGGF$iZShR3DQ3?mqEH5 zGtE``L^bQv@_bj{sz<~lUOwC*hRZC@QzM`QGMH1D{Gb>e9RKnWvB)DxAN4=H2m4;Q zV{k2S%-$exC=-6_LmGSO@YDFS;W-!XKDb$Mi{TjPGbxhZ((y+2Ae-b6UxthrNBYdb z8&^TIkYIq;ASRy6?%=ZHvV#&JWGNo~4~Rhz63SC#Uyb3=*vpuCXzUftt(wR)EM!gD zgAUmKc^Uk`PU!8WRAc1e_g_wv)CAKw7wIc&$IM+@X)I-q8kM<73mwFj4X_mD<1u;E`l~n{ zp;@EJk{FM=J5cv~p&^fSByVa^EA@LdKqH?OI+fZ<9?F~QCDo1_%pY7-7dVt-1aVX| z?l2g`#ThHvB+BwBO=y5rQ;E4PFN#cRu8TeNsQ8S% zNK$J@x0ynVBueF-eyO7_vt`+6{)EkeX(bG(H{kysUE`@CqSbq)S4*zjTqb11r@Gd_s^3 zTO!5|YS0Fh!~6*2!@2+`f)U!1Yw2M5m#ZI5pjtoHp;8^W%G~{NXPBy~GfZ<^^9Jjp z7QktGQAfxOUD&(TV|rec|5kUtZg-Awc5K_+9K$L4f;nlyeCQcwLjwLz;N%roIC7!8 z6EnnO+={`SYtXqa_j3oNvw!rOHPfpR2_) z^p1Ho#qDM$cw9H~xj8VD676F?^hmWwt~&27uhfY`N;Ga#GSW(ZUs zh4svAaQ;8X4M#cw_y@G9kXG?g19XbRVrRZvc}5S=DGn<|4A9LzRtg=UQydmDM07uv zl{m!?0V@sk(=9%<>u$PPAX!-HYQwF#8%wiCZclI}_mwhJe6GwCpSWplZ*bfMdNQ=i zze&0Uep=bLIe?Gmo201&>#AYDLXX*Czex)8<9UGOh#Cj5&jKB1r4RdqWl9bIqtxek zKk1rarFZ)S{$>5o48}Uz|8zdq3(>!=;Xc&xNu`F@x`%cn&kpWbN(Xl%PaJ&&y5A)K zMXZa~bUgyD${B@ccGh#NgHB;2e+#~3yM|`R$4xU_cPZgtcGK+mfYRm_N}KK1+x%I7 zu${Xc3F1v?0}ttE4U2n-I4{?jv708?;7cuLF7AK|Egm(HGsstFrM!M7y&O8`wB|hJ zhA}J6?tjKZGwg}$CBXhM@=#;%bd$8DpF6O&n(krYE?9p?zDnUwv(ngpX2P1Tdyej_ zhLYm0rAO~i4EKfb+~JI(n4a}lr^Qbh87s3hrz>!d=s#?|=Fan^DsB6{(ze^sw);&B zm9`aKFL~*2+P1h~J#sSI67Uxe47*;kr;kcbJ~}c#161I*y>b>d?dg8Ul6~Nf!R?1@ zffN3Uy%gMHxM$(^!_oSd=FpuO$0Un?7|)O3{sY%Ve(>2^@RZ?ZD>Y}kgSUb=yc6W! zuN&mvJKq_?$KE~xFvlFwpLxB$uPXKZ{GENUfbpa$46BhWlNPIy49v$`gGs@-KlRmJ zqB~BK|6j7cCPndmE*olSFCh6pmQ!lzzLVr{0@pW|5Lnp%%T=Duy(EdwO8@N5QJ$aok~})C*rDlCI$0k) z<(cWu#=11iU5+s@)qM+AnJ?GDHZAJAqLocn z%}ND0_W@V-!EMOJL8s!A%m+dk10EHMo)q6{C9UI=XoX%kG0y<~;-pt4vm{+i{|%Hv>QfcysCrYBCkrVxf>wXe_+LTi z+VRnO<R^)sGr;Qy>cSQF?L54{)af~Zn9RRw7vRUWj;OJxluxxC_gwt?dB zMBEKvVbvp7Y$GnH<`*8W2I9g}Hh6v_4hLif`T~#!% zho;83!c(JM+SJEgMSy-(e^EgUYz(Pd=H=nL<)g)SCPWR|0?EzGpUX*1zX&b_Gyd(x zX>}q}$bHIsquiF42>AH=vO!O8l2$2_V5?k~xwCI2>nf=tW4K9jbhFd0RYSjdRp`V{ z_Y9;y+ZPx=y>LwFq?+jkQkt%6S{=v{R_ zHd2DF2n!@&JKYrP7ysA&I@mFpti-k&tJS_H~cTQ|g$_NxR=4Q8W`H z`*jzWalYDscw75&s)o;F#)2!5$w{00E>yp1mC;|Ew6SkysV;wH9!X^~$)7KtN&Vkf z>KRrzJYVI_QSsjOd`TjWj?X-5afjz4!1*V@e=*?yDg1;(wrcPo@!o=W>KnqD%XpXZ zuG^10@_3$xBb=jtdmoN?_NS`4abqDCx?ZEei%d2RYXNrXBInxVbiLqxiDf{+*$nOD zrji0XMx1ggbK{N)@kqL8jjixVevKC;m{Zb9L&04rY0%||&ttDTAB};OIGY7IBEea` zR4Y`ivcR5IHkFh#_)vEzI6j8*_kJ?hM&gX6W@Xl)Umn7}l-D}q9@XBh(4)8HWy*7h z%q8CiOAsv1v+h*R&RJ=*yd)ndMW9+Q$+zbdkBI!w$%_X$9IWJ&7sAhhHnGyv@_hJt z#s7pn4}L-MKPu0KU#0jRasm8m#lKw6gI}ZgmqB&_v`q2eF5BP_Rs8d1EBs-Kzd*LY zuT%Us`FHS#EB@cfv*3?V{8{o0_(|s?=0G_MexjSKlrB$$KT7eBmowpyR{REe3j8sO zf0S&7KUVR_$`j!qruZY|bohrWeyyAa{|Lp;$tm#1!4Lbnf${KxOJ74wTW|?qmPIhpI*12@+Ih2HaNy4 z7h|1zzdXM_+!x3Q_a(Ay+eT}cD@JA~1=6~)Lyu#Qh-8VW(t?{9xdsR08^hON_kKhS zZyA$}yx4$$rM#rh=oph>wBLvc=;E~U2sm8POeg66I?yB9L*wo&$+ z7T0%jdzo6aiOe{zjh)(H%>;+W3@Y^3UNb1nRX0XHul4dVr{od(Q*sOw@V|v~qqzEC zs*#Unb;4)WyRFI1dlH-Do2&B@*(pT1LHSuaNo8k6vsy13rS&rU_Ubh0mH_T~T3*Jz z5a-fKVq6>g9UO>o)P9UAHl2Xij8oJZ;zHY!!M<3p4h z^WcYaOywc3TAg8wS-$8&)Cp|Z4BMjRj4z(gwpna6X5(QYyd9K$rW4cdM1ekfj2y~gJ*D-AFGtMrfc=l;RO_1Z>PJUj~~_GWJGAAkVCM>{ov6dh^Tye zKJtr4Ma@6PLpOK4E~A8WbwfwW0DE(PHIs7DC5Q__57jtVVj4R^d!+H8Ki6?!fA!`{ z+_$zcN7EHfn{AL+5%B+^f51NztHR~2du}y|>2}Mtfzn6C4V8qc132j*s4jvO^bKsy zzmBaY2k#rhSaNE`6npzwCM6fRmA3_MPZ4hUa&0EZ)|^|K32P0kMciZ?@LwKKV>~4A zL3kmYnT8Xyd_c`2U3PvW&ouNFZ@2!SKoIkRI@E+Xx=^orI3xY+WHW#(X0o~x`H!9W zy#bBSJg9@C0l&b1VAxRm$U;4xbXGf2qliy%raa63U}1!Jo{snGMipHPtU4#hSdPn4 zn?9FQ%;(V8pUAPU5NIA58GocYB#fQhG1?&>lqc%l+Fjlg?91T32wLjJz1FJUtGzP)&yF|Fu&8kHv3J9e2bPvQv)R<4eB*tS}!Atjd{vZSm1r zuqsQC--td6+L9xD{PYs))z<6K6|Ayxpei4W%a+rLl3JkDb(cO?dQ3)$N~ngeohvkN z0&jh?eoM6jIWQW~vuEQ%eOfivdE>~Q&AZjWg4=3)uU)O_z1qoUtY3d}y+KR^e>V)U zq?N7)tu5*(WI;#gKa_B%O#`}U5I=wUgdAhm?AR?w?NWKUDe)O6{gxU-}`X zBPUeOf_?h8sK=O9l!F=bHS;L>7|hm8F{C%b?6e-c4{=!LUc3im#V|`ji>N<;-IwK+ zm2y<2_+S{u*pX`Q=m*7C?H=z*jyd|en61b~J>?>f?rK*nb6NaR!CL{^9f~nR24I^U zc1*#Uw7VV>!@$pq=_&ICad+EqXHo4};D-*`ApA<)`yl3%kUC#r_D6Ecrg^RfB6@q5 zMO=s;JKr1X6WBaCbkhsfGe;9_iIW=Wan=(?JngVQm~EQ_sHdU)V|`AsscyxC#^uw( z*lF)p^WGe;+N+*j)S)&oBiJYv1*lY*eQe%Y&oi zW3h&ysy#?>)Ber#vFL@5*Y3fuO8eFz>f5Kg>6b6{jSqsiKb5xb00dHF`l{0^5E-`9G%ig7EAjiGLo_DdHXQY~*TIG4P=a}+5 z)KjB8|JGBjJook-Ri1ARK6msSQr`dEb69y+48FhGb5MET*h5@$MtZ*IZ_2a0=WXS= zriboCFw!$U`;_NX${hvq#vkC)!m;mj{@f$abkL&E_hn;MO?3WxZ^2sXnyuDQ}}4u36{}4hM)b5m3PQN zA6=RMfKrpL?)#OrQa6=UhV{owWe?2*zZYv#TE9(J>i#{fp_)p6sIIVHX?YCtK`WHp zCZL6k)akrec^i+nHC<6he_v>K1lJ79%L2IHMlzddahV+_*mNEMiTCbXJrR!EaSHsi zKTLscu4}RKCMa)^S$*dQv{it%6YwTz2fDFFxT2YY->c2q%i@0Thg^B}>*Dm$Y0CWn zf^rMjB5BJ?9VSq`fS*x%VOZIdRqQPfi8JSc)@2gs99;8B!M3gHK5X^7rzuc)r2Atr zaA|zO0?&=|KkiNwd8PaWalG_F_nzzhy%3bi0vWKB!5*I1{XOR2*rVXv!j-^ng=>Oq zgA*#j--ENk+2J<99ftc9?lRoEcMunD3HClj?`iKvYfwAh1A#G&6Hi7O!|cMd36AW@ z&pm>?UIdR3PbVCWz5RHeh9kbZlk&u~4eyYP2kiT>-=(K4CyS(U1pS9yFXpSq$^R2} zy+nJ+?~(Hg^As&1jP$+y#3ixt0mo?QMs&Dx=F+NKz&?Xk0)MsMqwIfv_7gYqnmbwi zos#b-GN`y=mf}AF{|H6P2qV?Ym=z49WdujF2#+@eVSiLkevq__V1pjzZiHh$HIrS0`&S!9Zy(%Z;Ep6~^gB*WpIW%NZina1Ed>J?R z7F&yv7gYTC}13z*V6%Z%sH z{5-Y_eGiuhcORS+ZWrA9a4m3MaC-22`jDm_ee)8YDfEQ1!@n3$xwa^A16I6Q;&AZ? zKS>~FL5Ac9|KAlW40I#m2mfubl&Ln3mCCTY2nXi>;NOq_G~t=pJytRYe!yLA8T>xB zFix?BZidWf#4!fi2S{VHsV)v0%}_YIjo&(6nlIl7 zfsH=ogF9{DqH!s4?jSE+Cq{s?|1eHm%1aCD?sLomckgFq);7CmqkMhC?EIghZIDTk zT({)0xNAngKljYSuUGlK+%psYi^}g0kTcU;rsMtmar!;ilZD@Jl;6`mCe&XAyG-9* z=A|)XQzvGOBJGtw`!Dy#)s0o|h3Ff|Cfv{d?|RLUGa=u(-jw>$ip6m~>}BaKsmSl> zaq^|WcTn+-hwrb7ZybEP6rT~k9f~g*zBi8R6kF!{hA~R6M^U0yv7fGQNKiuF#@zXe zn^$axlY9p8_6c63Lwy(e{C&7CIDHLp1a2;zgZv01yvn3F;PE|+=Q_9&xI8#5;w;8< z8Jq*|CAin%f-+&tp!F=yF-9_U>+49G8}eb`#F1PW$%=J>9()^0eU^kB0Fyq`rAe9A z5|f(QqE4IAqB9v>5vGam__>VNsRPe2qMUg}))*|;BDhy%wf-GhZ-}!AUME-1EL?rO zs%^Sys)`?ULmERebLO<$UHJw1b4OYBSgkG|+(031iYunelB{v< zxosIWau4(ucLd1(fdl+Q}0d4k1mKt+@Fv(s+YLi&?iOjNE_%Gkq@2mcsq}; zTyHGC271)=j;~S=Zg?;p1Aa53XKe2R-+{B4$AH)Hzf=pnhkq8HrFb5LJ54xEethqO zYr;1zp4zz~`5}4Q+GNQ4?_R;-KB)#Jecpq&d!eIJs_$VMDesTY2lLnd9peQ3_kVS@ zq4P4Kr#Ix*+vKr~Ix|t)5Xh>UmF`zg80mzBxZN~Y z*q>Y=Pi2x;kID}CKgW8c1f?3EyyUs7h&Y8s9VbIH-jlqw%b9s@!yR%6GjerNM~*fb zv1<_fPPcaA&c^wLi{$YZ@b<8uJ;~i6XE0H#%N;IR-}IN_YzH$@bj5ZxMXrWq+~wuf zzE2;^h40hl)mNR73tXeXSJ-HYu`YEbbK_6{u27IFA2(VmbM5tm`SG|fz4}E<$TnY9 z$hNcg{n%-LD%>GwG0FFqJ6emu%|Q)ASe$`E;(tS(;mHjiYgc7-oVd{g&1i-gYh2?x zsp&qqrxhC6zJttdJfu!TnLjGKg;n^)xq~MuKe-?o81pnR==<&@^y|O7N7Fkn=wIE5 z`J)Qr@m7Yf&E4_%`mgTc^p3Ah-EpK>3vV0o^(4Y6EP~$gwW>QNKdeB9x2N#+ShpTu ztGYGxj<0*Vhx)VcJ@Bd-M!g9*GlZY92y?(4BR7bPS@?b*Tu6$kMXpV}6LUoA@SX+c=^So>VisB%luo7*8l0J!#QvV zY}}Bcx7U$^P38}9nfo!Hc))cCV}crDm>r93s96}`Il)lou;LU8;Rb3Q2Pv5SxhgTQZWq=7%LY;u`+HIu)k?9pq3qi^ zW#7(YNk3S(?Ddt*(FpO+vbjm;V^u#@j}*P5Nq&}1Aql}5Ellc6^vAPKs>7vohREOyZ#iy!|Io7wbKX#oJTSl+mw@>e5IaR z)N@eQubgU1yvs0##s-IZnV|R!cnefl(ogcHoML}dPA>uV84E}%c0zDo5tv_hEneu+ zTp4eJP6#tQquMgcGG$JhqM3y9=s_Ns*K^lF8kG%A04I*wMS3mXl+DO5bG}C0Aa2GE zF*VRs_s959jG5bQ>@BsHV;1CRr`YY)>6Y=3G)&K?bOquZti+*50+Pjd!|n*?^5=v3 zCx|FP8`N`wbk4z2cA*ql`;HJtppdEs^h#@WjD_o9g`K!J zn$*FuVcE#vJe0=_aJos7Zv0?r7>lH@W$X|{s4f-1fy&7NOC7=Fe}f3EbH0=9PLg^s z)vHBw&G1_zaX;fE=ATxKWFhAA5*D*#ckiYuKAmFu-1x?+ydSFAx**Vu^76{M()}c?{C*EhI9f&5K(Vu8kd(Bas`T=b6PsDY>_$>hF%3jzd*s zzrrkT&4m@y{65EhMt!nb47QO;vRwPhk|doQ2<{JP4vJ7_q7eLc50-aJHOEj{+WgSc z&^odx7@=z&jQ1o{jaExu(0FtallE-m`QAzHlY)BeqgzuzlF|;`9o+ z>qerLf_biEsh0OH=9e*Z?PU%~Z5OF2T+ewIa45^HZ+IWJh3O6X;H_{2-rFe6?@{mU zh~0Sm!E+sUTxj#c4Xp8;1-DQ~(Yo?O-xx07U(mSFu~5sVu`bT&^OWS8YlHPiT{vU& zq7Cf0rCK&)X?#sjdFM%-b3-;r_pPj-f4=Tx+K)s;;6@F#H>ZPFeQf_y*6Y(7H`_Nm zgXN|-y6tYKxnU97!kT(q+_)AG{rH&TOv@^T!EFL8&Z>^fx zGpRUPqg#N`!``@h!}XXO&S{d)>|Qz;b3?19<1~$!<$=8l-laG0zH|3dgIGK&4WX&m zL-*dfcj-@~ZsfVVky7Pe|NgY&=?&oFdco>Kc=%v=ZS#$iJ`2ttbDnbqv5Cd0RG2sj zX%2E$np=whT9ppk&+CF1`BB7(Q~#C)XO(wJWYXDx%R0-fd*AoLe2(j{)}P!TI!^iw zbZ(ou(-Lb5byI7lvJYfVi63(I16ggk2MkSF{itRJ)q75+;8#&FzdKc-;@~pPQ-U!`GIR>{8>(NC{qAUUb)JE;E zQ#dHaXeGt`!4&uAXo4y1!4wJDS49*=)` zYy5I_f~{RkXNm!Tc0jH|416on&_^b;)a83?9ho1fXx;d#aZe8(C=&YqX3aYR_^9kJr? zp)GAJ#s{P>(G+O0kJ+<|mHq=R0SkRnJiV_ZexCU_ zcv-*$!i~A`V~;vXu6?jsNZSBBuv~fF**xmY(J#jl1jb1k)r9yF6N);{XjY%a9h^z& zjT%62h8U!gW7S2tWm44foLcSEs0I5+KXR+mp^*b?Xz$m2z4b$%fh+3xT0OUA3#_yY zK0TO_@AR)?j>l78g7+V6QHPZwj7o1_;be?PXP$G2uGr*h_OWog9VeMd=bdgGn; zJFmxBzF_&HJr1ITg7*_%f9fu*%~|d)JIN;mDcMa8TH~KRE9}VGo@aM^?%JTlaNdY9 zykU4g?DqjWJfL-3&u~C%VNW!@16tWVG5L`NdhEt}V_4|Ei@?|H9^%9FMzK<6j}ALX zf^;|1-V9y|g%PB?krvh%8(V$_>rL<SkfF+}1+=D22_y{TS|Y$Hk02`-)Q4&4XVdwO2# ze8R^Frjik-a&jn5%vm#Rl*|wrJO~J_?~9yw2@J+dY?XWgi9G&uj+g^LnFhFo+YM_-FlRH#-qh=vu8U-Otz%uUmO~PuLeW( zzY1vxQ!`kz7imO&>9)$PRhu!N(r$wZ8#F| zUu=`H$2X2k`|8KgjIVyw!9`>+u)B)5x@j9OZ-J2smq4!#+bw-0b8u5tyx>y;3a92*MJDj{SaVkksVLMT2j*;pIS z+8rHopX@uWb=4;L1|hVY>BO1kGx|f0jXIZ z%W50r#0VE-94w(JSVF?DN@$6Uz@FP|KG|teo3n%Ynaz8AbNP7*6JaGfe=XS|zg@`X z{$Kt@q?-;`{9ozs|JD0y+_g;r1WX=we2<4#bbk8PcF}LB&+{_)fFV9kOSVaNiizTz z-R3reiE0z|&auYutypiEg-s0FuPI9w7s|pRehc-J<{V&Uc&u{{cnOmYh&6*r7AA&o ziP%DV%~adjiQxWl9Ns3CanN43ri`B`xcMD4PD8iw72FQGk)|tqQ_g66$2X2&WV!=5 z6eCR#ZfE{q{^Pk|s(6n6|8F@w`hS!`Y5Mu4xNjRwzw~+xz3?cbR%*4Dy`2fBe3XHn zk4k>$3Gu#5G{#gr5;S);p6p~|ma--zS8@KwP$OefpIc~M0c;Dam|zUu!c~O(?pEUq z%e)bFNOustT-^C2ScbI{mOR$^TnvqpjQH(2+}VR(o)52J(!X?_2NxhC9%zJcWo`Vl>sk$%Q|jLaQp|ZY2OicaN8LZ(E;8Um}TkQxQ=_nJYMtK52CQ;K9O%* zp2(!zU9mVjG%GaY+PR-DCP-XlT;q8;YZw0-C<><~-7YhNIIyN`;2pw`EgH?~H@hbr zn6$(As>ZF*@KDwEAZGB}HB*wY{%poQBT$%3NJBx(4B^G>uC8}J%~^A;=qNLGq|Al| z>nZqHA#knw$cdaTHAf{EvTMzcEf#q1q82&m?aa zxh=ybE&83T4>L(@1~9+_yfB|b4+qNPHH@#XOs$Nqf0WUE+D{wL+W2p$T=*h~ktUa; z{|r=DYUR~)VS{xg$~xKnVktMd5+P=AN}D<@OlcaZgFy{TU!z(c0fhIhElwNOaz~ow z%EN2-_?BQ!!u@RQ4`aHP7g<0ZE!S9e2G+Olu8-*wW?Q5m6zU9UG!Znu>^uo=vSu?g zK1@Uzh6L;$KoLPb%my9g=Vy1FII>t4!a!@Fjs8_i3o7*CMSdqjO~9~@AD(4R=to%F z`~JwHYHMdxbZAA`mU9b$x5x!Z}PDX(8`E zci_svaKi7Trqjm?{wH)=##Jnc%6RCwK zR_*N-QuDP5UXI`0D@?X|L&BQl^1W$^8prv}T>cz0@Xlt4 zYb3Za&`SDvIgJNP=a1zRlV6r|3`tUnl{5^CY`D*+)5QVuZM(o1+RXe#$+1((u{AD# z4#f1Ig&PfXJ`LY94Vg!CPs z19-%*CCaPdOy`?x*<>1J%R!@6+h2x{F~*b-J>KKh@%M=ZV&)*G0)9G9!BgJ7rJ4cV z53Ar`d%hj>95Y03=O3c+H|bId=y2$|@uJXHQcC_KW%CAU2}WH=*SUayW<9?hbMDO0 ziq;XxXUhu8$INeEVd8hhJ3=cspTI43F-9Jx+}1ucx&zn7C?)h<6hH}xqJP?G7(d%T z;>cGx?^~ygdR6lB&VLXu8|$9Q=7Lfh@v2J&(5x0*1v%0vgpp2^aY^?gCdImXu>m8v zpw!qUWgBtgk=^CYrjw5&2xSeIlzcVG&7K>%JUp1o8?b^pluNS`Po4Y$$*qFk^Ejbd z)1^5Qx&!@g4)^8os*0$?tnvAap_r*G($=z%3sn`P#KmJ&#y?#Q-JwaHea(89Gz2C| zd=tl<`h;V?f+PO{Jjs!tTv3Pjahj>`vZnw#;}qH#xVGsazmt041oolpkMG6zk?rO9 z9o{}CO~A=)4$nag;jSZ`^c}ayH-|$%>xrrd&ufM8%R4`bd7zU6J)5E*sV2zW(^KBZ z_%1kgQYjw?+&NN~AZ0}o%^O<1(tKbxhl#@YH5VV9(OgXu2+;gzjj>L1$4Fx%lidH- zthxHY5yCQ}nf!Q^fHYIglndo+ftEoFxXWSU?=K2lEmmO@^_2y-%aJo^Wx$^f?bg3e zogkGeb)a102Gd#q{a_sKdI`bYRk&?VYxQ7Fsnjqr^?K}k2Wu0ItF-Jl@xw%ReDuY$ z;(0lC7c*Yd2Kl%!nrVQ^>cF*c4(!QE6}wuPiI8)=<~lIXMwn^oEXEkqSJv3Lw%_N> z?z-a;_-#rbQL3xJ|N6R*a69ZSTd0={L%j`Ku!4EJEUt7SM$2<$Gy^Yb+$Ih?lYqU5 zs?A7RoAW=|1>KkvuOV(( zU9jz};b8lX@ngpLdFl|2A6Vq5--g-rZ)MFK%_P>21(+{R7-{|UK!mqlwL`TH*o2)S z6M4MxGtkEgMw7A5w<5Bzgy~Ey<0eNJ?(GdRwt|;i&K-c9q&Xw31hkJYEZLZ#syKO3 zP{w9(PJSg}MmzhP_bKfYu)^HZ9#XGjzWVaZFBgDYkbu)PvP0W(DBwSKoYt99hnP2X z2N~Rm`Lo=B9_OWBFkU5$S3NI?M-US5pE$nkdVkS6>qdVC_J97X{*w2Pw~hCV^zo77 z&b{N~c%sL^_<=S4bB9&iH>*Rz*<^eR%SPe;E}N{`a=WOX7@SwOi>KQowlis`8==uG z`R~=Rb6e5zuKGDPqUn2Wu(yK`Q+LPR?vPnql+A13ta_0?zCVyNWv2hHhg% zi!11;c-ra>4b6`$z;98jrO$O-aFZ_tdCSQea-#+9kJss1u?EH^o`gx1snC+Zne+a6pO6m=}rVJ4ljx0joC zI__{U7i;yZxutQK>D4>pL?5HxIFe>}%=KJJ`^03*ljgVC{#OoTb^{fp*1X+LH9F)C z4clwmed%DKY8&Cs(hweV<%=cvCG(TH-k5D?E^!q@vrE!`ZU@1G(n024c>4TV+jh*6 zU*tUTf8cu9dlVBhZRV$q+eP8qGs;T;vEWKy8Hv}<4ftjBUWhjoD~3$Zf3KN*F6yO# ze?`3+`uA;m;%!cZFGo)~KjflT-Tv*3_5|l&MVIp7dF?@JPwkx29)S_MDi4}_vrjJL zC7w@mv!<}dd}Fb3UecpUx<)bi0nGn4P)D1;eh|q zx<%rWQlt3OMb|cA*I!?U1pLSA%dmQQ#Z!Y><;r_a zzDS1fM{66xpm`W;hF#|`1#yPbvp#TrOJ@7Oe@C^MFsH-=tVjLIF6|Z%U8InJ|HAP} z`2qi@$7uuv{I}uOBF&Eku{%Ax#nnUfSs$e4tW>JDOeA{$lK5MCjujTWA}vf*yP1i> zxqurFF6?poz4Z=!h7dsUDGmIC=7k=hY{$FEy>_`a3;ZR zthRMFWn%sP+E<7X@W;J}`so7xwe`XGNynMSG3&hx-pAUU>x0%bV|i!PhQ`jvUKNax z9sQ1YaKz&)uM`d}z^b3!JmB~DEBrq%ard;a2KGy5RJjG|qLp;Rr5|OI;Cio{%Nrn@ zNYZ1yeI6ZXptkn6F?nfWFY&xoqI08O((0hy0+eMz88{dmmwE^^L1-E#ZmC}EYL(+k zm`P1nesC@r!=_rUXv%~%i}XU70Wo1~(!Qno5ac8na4%+*StSEUZ+F)~f@3~fs};o$ zMP|Z$z_UoX^O)$GDi*X+zjCg|{3W6*7xh|aNnShHf;`ax`v62IMwXHPm!5j$mn>jrxTdWk0PcA3TL;(S?H z%0R9b_f2aii!(*5xCGqMiQ>7p{I@3&Ovs zlSR5ca|k|I`%IPHNW~(YnFGliO7%ju9x?@-_Lygmlss4>r-i=hR2^1ndd>nUA1r^+ z!%QGfNTpaUj`XA#q~}e@-(Uo#9Z1j12aUOB;HJC{nBNx+q~?z;7zdvIhGKg6XmbAQ$c#1s%R| zh z#~j1hq%)e>_aUPU*N*o-I6GVx+{17JWYX5*8I>Q6{ENDdq1?`XJ<2_Xa@+eO@^#3+ zsH+;`p8imTS0lWoUzM-UQ={B_QEqELNt@k^a=+}SJgH2|edwFtggEo!{+MvX0~X{D^enc#yi^1I{<8{giD`?HT%Sk;n7ZEAnm#=>{+920^lY5>`B21PZH|; z5oCbmhS-8(dBc=;ywVq|V9$$vkxDzB?F+|gxPGu5YY`XrlJBx>tCA$48*)1hOtRGu znYdsmWHe~(63iE)4m6*$g}j~41)rt&+MHr$LY`|}fob*a%WAATakk4?uyEOSYnJvG;h3grT275~Ry#v2a)RF3$T^wH(zdEdh>iw96FKCCQ*jz=q zF^VN_20QMO+>%nr>W$j7KNfkK{*6Lyis$fde)im20VJv}y+Y+o8|dS-+x~+7hqy!ctAjnH9we z#Vd+4YHoy{Q-RBybm7bc#j(@=o4${cMhEUj^cA*^2FL7{p8suZl5D4~cx`2- zf=x!H9dD|I)6972j#8vQvy~Q(Qog7}m+He#=B9b4zmqTT#%Zwq_Nqi_5}==p-E|Q9 zr)72Gn$|>VY`4D3xSBKl(KW8Pp^BtaJ`1QtOFcm*Sk4a3{io?|F~(3f#*pn-W9Wh- zc{Q8CrCqQubD1Ek z4L4>c1E3nT8uWsb{N??R756%3IQG^p&}h$O6=RG!MrrOp6Y&2rpib1a);m%#E~pfB zD`T9wve&`1G2?OT9o96*NON%qaG`6%AgvvO6t&SifcZsT3(N^s-|Znx8!fGZ6%v-2 zOzl8DMHfLOuJM8+&5ass1fN-j)1EB2={&64B}w6O7I-C>1B$)s7lL6LnNEbVMEp(} zEhWp8hsOmxHLbb!$D@Dpzz1Ak?J=x;%0pofAAL%x!{0DtMdd{gV#Ze78=yJhwQiEG zpjl~s_XWoy56M@ADVX(qcc>Dhyqi}r>q*GQ6aEk3&k#)?n!8GBnWXu0)-0?Kin1O69r=1Ff$`r4|V zyFR0qG!W(m>$0Okz{&#hzSqZOha92Q=#m^9lP&VE$7Cx)slMOHLu2w&c?hOMupELd z$uHZA!6|dy7%SV={FxxNlAx6bdI$RGzV<`zRllM?t*XhVv!T^19#n}j&2uHKp03bP zqM(rq1!asE>^DZ7;Ws0Ww1NFo8AA!uLCCI?G#+6a^&^d}1j#ed42t=!=ZRvh{4~EE zUs-@w4ekbJxyX0AH)-{ODkE+j7hs-ZEn4XgME?hTHQLwyH4dmSXv zt4i!p>Z#nSQ#}t1Uawl`(F22{1~K?%S)Vr@?az>hFnFv?_e+A>Vx> z?%x0HjdlJIjE7*JjhT`l5~SeqYL@1#+<^aqx^A{pyr?FB9@dZG@AUyP4Es zt@>l@pwk`FrxvYkmo=mXQnePy6*Eb;b&j!)eWLz-doHbwf_7$TRsMfZci;b-F6RFu z>`maKs?PrLbLY;MjZ8>FCJW@wOn@vPLjX+$WtfCZ76cS+DQF!Q>rJpFfVBbJG9;)G zutEYFf@oaJ;*udm4G) z4!(GG@ZQ7iu$66c8olQwO+CRhA3j)w7H+3)Nv24?E_GOm^aSOjyXs#rMYPZel{b4Y zYNv2N8(EN5pv8PR2`h~^o%?keaj}k_nym-zx!p0gB^}QM>yRuHJz*FLg|Z=AZ24V+FM?fY}oCi3T%sCQb$&h9!W75 zY*R8#xzLjD?|E68RPeDMmg~u<0Ho~eL^pEq1(0+QUn;G2PVZPMy?mKP?w=VergpH= zki|U@>k4-I=9`&t(kA_^;4aZzFh+i}2Pv_)gaUsK-Ba+|WnDxT*7x^btimTeIMe*u z8QnotjNS{&&0bYj7PGXX%|Yw*2`a~2Pz;zh_B2R^1r07rD|^4k3W{Q1;(rXypiZA+(fS-xlNopv!;8uz*6=!?tWD6Bj~KcIzTthfd8KB2%% zp+=7re4j$lZzSKW6GyestU%kVPALpB%9t}JhPO*0&Ja5Cl zUIr(D=3poMAF#|0+y-;Q!!dtStuuV)`@nC2| zTfM~9ERt9)2fJNtbv9CR@I8MA^wkA*&Z@dOGoFz8B%;elU``!^kD*-L`=}!bEm@-H z3Da(Jnwrx@?hK?9TjRxK$e)gpUjZ+Xbgak7AHI7ug6v}O&|s;$MfwkmZurmI z?uMRAL91%-eC~SjsYc>8oUUY$l{-_Jgt^FMU!}&b#g<`^(F_3|v{4K_hj= zT7TOWYk8pTfv*P)xF?9i+gW^x1Ej-IcD!l6qr`E)!+psBdBLX?0pCoR!G9g7 zLh9j2eRP0V-;fr0Or}|mUpO{hGHB*IsFbG^i+%kiO;jlGmRdep0o;r7? zR5u_d9m)|mKur0j3_@&1+OdFPyjo&1Vlt5{8MUV%S1NLw8-Lo*nJ9ULnmPrk<5Y-K z0C6gyU|cc!nDS5=kD|9VQBNsZ$PoUQOi$9ju%jVx(YEb(!8KqK~UTTh__PRfH)2HjJg40%2x%5O}1H%T;N2) z2Tv&z0ErYleNii%zNnAay}%AuIADKmOT@u74#NxDI{ntvgO36uXm$=iXqw`5>~m^s z)^+#3F&8&m(OYn@vPHW$zQTvq&o{)cdEj0=8|PQ}u=e>a^VhiOx4V{N&)3qob_PEs zgV=yC#d-!tu>2P585n`E!BWBoM=r97#@zj3Y=Bmn&kwe!9dq4<88q)J;$~(n?J{VY zfbp^aHoWdLXsu&S*cmX4fdeM6Fx#(zIUWdOj(HDsB^6+;(9?k#NY5D*UuOO3TAw1D~!yyyNeE@tNIb|oMD;$n7gse^E7>cJ9+ zVTZnUI54WAMq6*PWgJWv?{*YY9*uTeDB$TgSz}-+jPB`M`U#_B$9mcXY)(((;FIWO zE%ws;RH|Z;cLb=G1=diYp+8G}Z5dgP`U}co;6sH1&-B0akOg}n=5)VR{Kuoid(x+} zoYx@>{e-RR(hNxLfyN$o;rH6k*l2C%DE4s#B2geg*=ug zEbA}j^UuuOpiHo9J8xnU-hZt7IAS<*L~&!&F!9MI@5O}N`OsY*2j0}IhxiAa(9du_Fzb+MeDawG zM*&XbRP3^$+hL*gvr&*`&Y!vYk|r7w7VTGk;kKG?15WEVjfX`({r4R?&_u}+364;J z>wf?>gTEjUCGa6^w;FJ7{*=>HF!E%s7$puXpu9iyG27B*w-wmZVf~pwLU}^+@+xQL zB~Fv#3JlhPyI0ikb_W~(x%X3lxDNQJ?pyj$PQlV;pMb{715IOUibjiyA2MZ+@^P>_ zyJS=}q_5BhhCZ8)p8l~$v71U9Rq!df#Tj4TrIt?IjWIYKFTfav0?mD7c?I$y%vf*) zRwC%zKGb6?a9g#o#aRW3$rFB~n1{YJHj^z=*t~{+&yR4fDtHff9v)+JKD36U=?3qx z;#y~-mr1GepMqf?F0Y=}=YP*|gqft*JtffdxEmE{MvM^QtXAkMY=u4KmOs$3zLm^ph`NkjRDsgRcee8-_-07z> z-z|ZS>o_LNMqg05Eb~@xxa4ta$ywe}D0wWsPtWYQ72bLs*ABwt?r3jv;ZU0_Vyc|f zV{U>SZnYL0xZWwQg|2$nSy#$pL7EGwmby~qfA=lLN&HrxAQB|9-3x2}DE*92fX#Y+ ziBZkn=0|>bxBKEJ@I4IpZe{FNf)jq%!1;IB60E7PX+@TlwMANt>B|_?ccD*9ZF&Xi z-bLUtIp2?6x~dd;St}xHpbSMncIeEJYz7N zzJEF5^f{++W;MiTaOdfnQBq$v9P+%BDgo&Q^(`^Or`Md?Sypf0Pp8gVEv4#`P9-B{ z4frh!%dFk(u7yhyi)+i6C0`ElwM(*~vkiW1)=6)DnN@ac>X-bF>K~2Cd41I_O=V~F zU!RVIr4Hgw+!r4Vd>x7`3X41u@@;d)F5QqYQRW%1qa`xDxdY$I$5-E74+lw`KOqKi0PyS%n6 zRjWO1_SBZyx>M892*=v{-scx*)}L8lzu_jZ>j?ap_B?7fR`!jbG?R=^Dsb$3?b85HXK=jyS@xEDcLvsFvq&r&Ar{Xvkca_r{8^&d}YCZfL#wsu{XR_8|+^IThiXQ z6I5b2=8EqHKfZHJfL{Fd^nTwqf5c8(cWAW@@{SR;ulU)RO@7KV1}nA0_Za2TaB5k< zyI$5OzBOuD9LmyOFYA5ZZ~gH_CRNMsrb)p-?LZuQamxVB)0-v+L1UAa9o^++BX$FB z$>|&2S!W}5)c}p@LztgKbJdM^x?B4O&vtq*Sy<|XRKUW;3rk5_WZ`1WJmmSY%jo{G zD`Gv@yb3Mu9^kMaz2asUh+3d(|vwPeE!F3iL5O+xX!e!SGVeI z+%R^^j;t{3J&Q03kJfbu(#g|w@81|^*7LF<#5ULS2BWyv~ zhERv_9D>_|c!X+%%?Mi%wjtCZJZD+*_Rzc;T7&E#?0jU&PN~Ytc}L3se2?%W-Fxaw zNT;dWhXQ^5 zT&n|C$PMST#WNx}8`*iRayW5@L~isA#YLaXkI)xUj4+9_Ryt-VHg#;Nm1;jCovy?g zK`G`dRslM&y-YTN62QDdTwe1@gH}vDwJFVz6C=iVEaJ}XccEO|;&i$k?CN5NTVa#( zTBC{??4xN4;vWHpV*594K*%Q70$VVk4;sO#*W0XOFu)*DvA_}xTeX3Cu%1N!TD!|NG<#BHQ{O20E(OxSN{T$9j~!u* z)`sZ-x5KHVrD^croEBB&RD@|3ndA7KPWbY0Dhbo(%I=3>g6^WqrtS1w_@oqQTnNHM=t~ej}Kolh9oPetI`*nrfM!93g}T`H^#(tpmK$-L_hmEmqT#e3NDSnSxkq` zkX>2zZU^WS#-b95S36oGJd3!F zOO`RkV+(&)PI5nEih_Y_usAmnbK^=M$tWFI84O(LgSPC?V0|Jo#>p z?QQO4b2Ba0kOgoHPH~M@Npt`Apn>u7oV-fp&5TX+*M8z{_T;jRO9T{@E zHX*-2Hfd*IPJRHa_kr>pEO&gMG?=10`RE5{#(00FjKhs)z$@h7_Rp~ZR_;IRFxo!% z-)=jF-3Bd%R;j^O>x}w}31}BW1Ea58E`6_ecAuj={D|U6_3ru!&`YymV<8K@o8L!! z&l8}SCinecZ8g0g<{!Q@``w~bC^1H&5_dTtYh+hutZ(bO|7m7D25kL?r${=6nxiD@ z<#@H1qbD9i|3ad38kb9&1bdywlm`1{-|78dD|MCpZb%i)3kEL1DjjCmde{dt@y|8P z*>%U)iW@21FCo-$TiMu1@jY?MdDtRIXY!xj*zaQ>^ntq{FXT9`{L4-K%&uH=|EH&m zaY{X{Y-#_b%P@{kTuWx&)?ty;xw!n}fN%yhB?%fX_qg($>BRoyfQcImdE;T_GwH^O z>No{C*62%mc=}+m1>X11GVxNlRpx6#4%rlR)2D_ z86J}yS6)D`e$|5+DvZIrKGJ2d1_P&ihEojs_k$h_EFZ+;S1elmQBOKr`~hy@@f&S^ z2Y1>k-E{;qiL~A!yw8|1jxi0GF;Q`}=7-k*Cs+>;BRq#-WG2ptbVjDdGm4)0rqSH4 z1}(cC-*3nF+mYsWq;(*Vf>4NhXuqJn;%FzE&tG-hFw<6WC+XZAws)}!aQ+T=@1O}G zA+QNhF6$|GbHXRc`y2x8jpn1U!wik=@LmzNrEo9qKb2;dod~-NQFDihvqwE;=Fi7u zMtNU%MtG+JzuTa17@4V6xY(C3>-kQfna)|ZK0j+)pys8Uf!MF+k221gJ?c|4c=)lA znY?e36eC~e zI1I9%e_b66TvVd5Bm5OR!dWE}$licopWypfiV^*FRw1j0pCA@ex@`^UBkC*avjceV zM#zM`ZJ1k;BBvgvv$mnw)I7KQJjytv=01~>3=h{BrM7^{uA>5z(*_b1Pc1>qso-9@zS7zXJ5)9m>WT5=n z2-Idx$z-dhkE!#6@>Z=i6i>EreY~|s$*>P!o;t~$VuPGuV_%m{V?b%Z*;$*@k}oZ9 zahCFxM_k6%$f8I{AudEc;~`tI5Hm4dfe&X$+%Cd58~R`oz9pis7Yx=*Fg%2S{eSLb zjXF^h6GVP%oj6jSGw{_#_8=P%d;M-*XIca(=Zy~ZP*|$J5GiItwt>k<-1PoHU$bI` zXAu_rus3J(qM1`V$d||R4HP$BP4~Qe;~-VN9le&ddED|}mZUzD`98N6NzoZk_Y;TIz9rDZl4jSt>k{r%Pxr zq+(r*>zeqTyPS1xBgOa6r^q@LqI!JW?WZ1E?H?(ZDwdKe*oC^}e|ar#Ff}|p_u)SBaurLF z|IvRDQXol%iN!zI`UAzG_`)A7{efwr5wOrZLY4RE4@?gwLXNyYFf|khNG1Z3aiL+L z7fg^=>JQjMv1;tN5Xs#12S$Zxt{k89uVY&<&NSXM-(JD<`)N$5#*<`j{#yp*{jw+F z$)Ni^$TDS4^G1s+n{;$`xn2Sb0&e(C~FX-pb@D*=pSNlLhk*Sd6 z%TKQE$GscLm!DkOA2={zERMz51g%H4*G3Sy!fUn>P?y90UA7eRB5z zUlfJdc>_ANPn>ES2v&lh;QqhHiR7ZlORL>G>JF_qc?=sXh|45?E%_znSIuwPqC~on zy7Ya7T!&nPYOrQG zR?@05C!g(O<4*d%@#_Jp0b}tU#v&NhVJyDGSo{#A`89;c2u@?O*`rwt?`ErvtwT7t z4|y&?-hXa)AM%_E{xm%=3Ldivsezn6pRpCZtDv!YvB&P=VcjJB^-f^7sI}ars8vSM z2*2;N56tZzfl}dr9i@&yse6K~ZQc$2btCRQig9^4=(ut;PQc397Cek}-w&oWHfMUO zs+YG^Nl^tn>WKgrjV#W=m~6zkHwW|c4?);xEYuZ`!#8)3FC2$&tFE_!%2E(CQoiJt zs$G%bYZH#nYvx6*)CjGmv(>vK?d50&opa5y)ZwXHoqWgUf|n7KhaMvQd^<{>kCxny zl4qhF#^d2}r?ROWCN(MA^G%AreV0Tw-e#u0epwg2WM+ARzT+7x>3r+=d5#Pjc8#x> zmor%2$p0$Oq{4L)vQL%OuUw8EMx|1jOJ-_uqowmgq9K}Kd+}~lt5$6{L8nE#3A$_j zH^OFAVXK!a;l)PRQD5yyi7PO>cDRSe$Z_Q!^!#`IbaJ@|GakIHwsAMK&DdO7z0s*Z zNiDo9#aK}9He&=JyBMN75{jc3?a8}RIK(9*Eq&_KfCdn%o`k6t(2;0S!jUlmcI^kc2u?P2;mD1tY`S@l69?0!J0cXm$5K z&p8ASU*cBJAm&*#rVR^-j&tWzDmI$vWI^SpDb4YB#LsDVCUnG|`2DDLsgI<#8Q(Ut z40b*Zo@KRDd91txPM!BKB;`218`QKiXn(l}efx805@7tH7turGkG5zb4`M>7MqkYv z?624Hgx6+?6H7d1i91g{v+&7{PK}pkD&P~4o`5$KetDjRU&J#GX&&_3VRMo3p7L4w zSX)L=d}xD8=NU^5GR+(DeJk;Xbt2~aMpXzeL;Gm+ARGNoBYkiA^n#&qxpN@ljpv9L0fXOF6l zhgT<^*Ese?+*h03EMxKciP*7Wx2lIEl^8V5$|VQ19XX3_<3A;~75)XPwHC7+9eM{c z%aDKQ+b7O~ejNA&d`{&eC3=D}cY$cA!5zy0+1%wtLp{6$Tax9Oif-pSkRbk0neFZn zf6)<97YfXT9NjW6^p0_#cPHL|Mt>#x_fZfGw?WrlQ?>29{DCA`fna(Y6P&Adq7p za1sy7$OEoSvSI8t%hy7mE5-)++|toEv-}cjbI2RrnMh-n+d{hYd&HQrC;Sm(kuxIP zM+d&HsZzxWClcd{`DxH-Q+IZOCTikUFmRsLY z9MHCP*iHNRk4>&S;Q=92JXcmXtMHJ%j&$I7j56sGUwo5|Z4vkQ-%(~WXuw12L-8y? z&yioonEE{;^ro`w;Z-m?ie%@e!#YuixCD?J0Qs-b&a2;P#&&%dQxB;uw3uaYfPCK` zksCYDpHZX%RcMyagJ&_l`?qiX4D^^;-md<7@U3uD{uUY;he#xW-j`ut8e#r~3 z+iFZXeqB)g@R(%>-oJu2s795@Hp_3n=S7{T5UB1jjlUio&ss<{n&o78X1UwT?SwSv zr&+h}zB|)3*{R?R0yG3rwQt5eT zx6dNw!e5+C{t%`8+h_Yj)S*)sH4_Wba$^%$4{K$-?P1`VN%1Z!H(|0_-UG_q=c78V z^)+~g_zKKIU+zOG@A=5j*a{Y=?N`Xw>uCTd zZLq8rf88$$!v)zi6~62 z2nLLOg)NldBI}eefJ+~I}m>~u)|0raneWj%_cANY{ zp9^yX9KIf@iNkXv+Wre@0*9c56d^#@ymef0ob(UZRtaUs=Sz1ZB^#AaCLhj%U$m@bOZxU zO3u2eE!m~#{E@;3{$POgf{*w$yO}Hncem_PjKZWp);oVV8QHN*i4a!f{ri*Jj#ed7 z=-LQRtS3{2eSRHE|ID8vyoPbQ?cJ_tsL$+J{qW`Zld*#A3T*k)`217JET`wgkaXoC z<4q$(PmOR1Wf*3|cZAAMSOdD@Rz(lG;TB~LI12rNES&4V#5#9;5T+enc>X7S8PZ$T z^yx~Onm+FTNG~^UZ;akIOcX3SSg+?`y+^ZkIc##;aa-_0ugSv+iI7*-PSJ|7>JGHH z-vpdR-|~yJD!$Fc%DU>+gFjCAHh{JMgZH)~gBq8DIKP+Qt=q%oX_!sR&LvxQdom$q zInH60$x~E^chX=vTKw{PIm8m4AdiRRi;QY1ROT@++gSyRD48NO$>PlNe55|;9Wz+Z zcSzCX{jkfS!T@?I^L4LK6rtwsK-{Z{bE|Q;Anti@N|9cT`y1k(^%@3Y&qCZ5Z^B@{ z!-#tl`P$WdsferehWmi;r}jqyQxmMa`*vcS{(!#UfUphWIfUH^jR=Pkjv;JDC~wx2 zjI8ykJs~8 zo4L%YMxg_A5Z1`wdg#U+JP^nm8nB=KPiQwJaT>5L=!Sg0$CcU$kF|~nnosnFo66<< z*ue?(I~2G$5SHVtB*)hCF%_EPi@+M#u*~>;# z-87bld5~WWxv}5Ej%yE>sTF5pg&6Fb95;IQ7O#8eT+nkI=%*=XHap*yY-gR2i-T1g z>|URE--Vt5hhGQr>p1iiX~%P*@7uia_M*ZQz|48q%hZ@R@V?*6H7u{CVWt zgHoKTX795D=loP3XZ#3~Gj}PrdB@Rv_YQc`{~ZYbLXg!ye6WN1jmBdvq?KYic@BFr zTEl}D%MKaDKOTC`c_;djx5H2*XywU(-vX_JP~ac^+@ALR;0K}RX}*c69(Y#e>axY6 zhtXB5HwyH)7&cHmam=chHevPR3k z!E@v`Ewe)oz)`)V!d6k$7$d@ugph2BD9(h=Y(r=%v=ZW5!Wg%`GnvyCXPQUJ+d|rc z@HgX!;3*;SdG7tmne}ZNjH$rFeDrbn&cTAi11+r01%5l7lPCRFA z^8t|d^s_^pDA+W6smD_K8>vqn;Er!$6R>*RMymx@w{7soCzkA(In1lQtj$0um^~8` zO9C%M5v5S28GIT*ZRfexsG2SsuYTuWp}@h2xWin z3)@DB@oFdSDEQWIM7yhe+x=Rh+Fy+_;Bl8#`E|m2zg}49Hwb_9)4nQ$53#B7dC4K! ziga7GHhrzN1Sc~qH=5grIenM0_9<|kwRX)uXr$~Rzr1n_Y)y+A>}_0~_T~`yre^HF zp}^0B-{ToS7iYuXaaPPxOP*+zw}goDz!`Gd6wHpO!MRG$y@PXZIC#?ZJHkp-dMFUt zOZzqL196yp0``53|E#y+xy1OE2>)G1H@8i|=;|=OR@oJLtT?^qrkV}K88ulobmtLK z6IoMPY(_{y7>Qs-7>6(sVN#9Gnv4}aswQf$P!qX#bWOzG?3$Rp6KV`r?ajG0v3sG7 zymfMoUF7!hWAs+@p44ZbQpz1qDH{;#5MD=UM>yrMJ(Ky&_-8(a#yMmOdJ3_^HGA?z zBfPak4j-fAkSpDg-!5kpe&Ky!j&A2yO2(E+F3rv+7oExOgFe$rslH{C^XJ|-qpbft33Jn4k|kL-@P;&Nu+${E2iWC)ADeK(TTn6%QnzOG-4WUgb8-G3_o06!&Y4Nj zuN|uG4M{tNEMCypbI5y#&j1_~h9pV+*iTk)V|IK1N%XrQs~p1~hc1j&UO5nn_rG-t z8hb$zYS=o^ByCc$iv#eE?8zuOd6C0iy-pnNWj51ZSfd1_u>5ZB z*1(4XQuPNf4??*nRDZq9F6dmH@eeD>6n`lt$qxb2b=`~K+U+O2H3Pv6+(Pr{)LQVd zLE{Pdoq<63Ea5TugvI=#IYu-^HLw~uPqs2urwaWG%>=b!g&m$@0%v~|E@JOarp_XSLS9BepUg&bBn;kV}M zM)*wxb;9i>kB<5p@>->{=9G-PLkFK+xEfP^YVq5#w=2d&410!FoZm9KC=&a4k{pj# z-mH!X!9cq{zdIB#K}TLwug!*y3yd$Vqz5pQE@P*qRJ23TliQhG@tdrF`+k|WsA5IV zMIUT|PG6Sut*@lw0oXPe2A{%7@&)Y2pLuzdktFZH``>(j^&3GU((lvw{r+H?XV9<5 z(3hVeoJZ(DFu~tSY>{@d(Tbf(eh(*egJ5cPT-yi!agw|lxvzQQKTVB&3%`E!YC%&c z$?Ncb5vLFn`t*C0(~V%neI>h#*))a8a%l+nc8KfHK;^sn$wu(-tPW{J5&8AvMxz(4 z3B@l+{8sKwU5(bh9A__nk9NEM8QAhs8d|isKxeKf8yF)=et5v#LUTC+IA@~zXv~)KBz$7x;(TCoiNn8E02E3SzSSub{q!)e54%h=-&_Ft5{a2?>5w>DWvZMfq%e; z*mH>_-`;o#_eX?7Z4rgT-~;{?S*P->UY_d70)K#X#V@VZb(%S|-73EWIr{Qe$~Vum zX*tR2MS0A2N=5g|1A&c)XfHK2A1zyoTW2OeqC~-iQnI|Z50W6vEN7!JlfS*`T{#4H!wbxXB+;CJADMYQ+XXvs>cMrU2@1GY6_{QWO?FYj1T(Z z=yLMjMx&&I)@8E1yw4=-Vki2z-DbIKKnI-#YA?~J!$UuV#4g;qEU%lzW8dR;pKIB- zobD=#?<=6?Iep*z&GL6@&r$xK0S>za%j)*!z>*2+?I**p3XPaqJ_()jO#FVM|08(* z3Bv*{>tzk#NYQWpp?FWDQou+91&NpPIoS9S2ZN*HEf8?O?=6dJb`-gQr?~Lx)k25cDTS!>u@^Cwl-bFgH8yQ&# zx}BZJ|7q+i{EuR1v9IN+b-Jm(c{)##2Q&;;Dri3&a@uXnGNI*w96Iu-m8c8&sh`nhl()Ma_^O^zle* zmS201#(?68NA$JQ;qHJmRJF6R@n)Z1oK%2w$Bd+0Pi_PLo8Pkc<6LJ{i2- zRi~8Nr>G_?7XFh}?foIhJGvMB8&q$u_LTEuSHAfUiqmLg2zGLUK9#g9h zP=5*DF<+SGN?S>*I28EcJ<7}B?!(v|TO2P{!84l46;)A*9^@v$26|3j8)u6uCmBkO zIuO>T0=~ z{E{P*rbY`*ThcWz^Xm6FM6H}2)QJtHBSG0u#u+%us~b(d{qkbl?`h3E1PflVG>T8d z<12U%--kXN)wZ8axCee1+qay-e8`{a^z8uDgpI80>YY=45h}c+RUUeQcdvMph%ZsY z&(q24f^>Vq+xsUKXljWnS|H;8B+%ZGW_fGSw()uAxDzN#_?ySFZYx&EmE?Nov9_J5 zFYGS`7xxV%y4)z|^wE6nP;Db>eDMhL-zX6kUGuN3_gKVZacjr2uf zMn2fTO>u$y$MGBRB)&v={_4~iPuteLWI;X*>mH!!}8lrqO=Wp)UVxNl~OslbJ_I&RT*Ad1Gf*Mcro$8@Fs4m#G zEKHKiKn;`qu;Gvi!!8a#edNW)qwvBT;09{;vW${7Pbrc182G38REctY3O|jXD)FGQ zlN@n!Pmp}E8qg-q&g_aeVCyhB`rQ6{=TnN_1j}CnxFhg6YHuy8*ae#apf2H|v&&aj z@iEf;==%}6_mOu*mOz>_1NT7}HItKNW3Of(tIfnd9*KQ?H}>&loI=Plgee>I^>5$> z;%v%^9If?(^bL2v6Zqnof9w@~m= zX5XyD*i$1nE5qQ+u1ex0CoE+kHPSJp(+Wsye;#Qmy{XlBy)62DV|nn8hTkzrV**_8 zr6_V0){2eJjg3Yhmu>TEvJ<>#J+vc`H^x%Tu{|N(9-3>fhM;TNnFy=#Fc&M=Dim+B zE>7d@kytk-hgE*^T}SoO>h=_haY#$0@S7IinoZfUDA@pt3_gr(e0h|V?@~|e^j@9s zrgz*CH*2iC!sSFuKHQ}Ha5Mflsa@e5NAc?@ew{@g6S!}tX1xXbm;&4L@^}5_mY3Ho zm8bJBuY@mj3Hw%s2Y1OE*Ho?~8|w{t=gSE{GSxp8Q(CA0Vb8eW1nFcIW3@e7cfmyi6p9Wm?vmk4eKp)+W{>wlhT=X2C zv6&{68D>D{Cd1a;|=sGPH9B1 zegZiTFUqGnXm4ZLd0x++sYycrqObL@O7hM_o%clGU5F>z7tebA@f`Bork;(&+`X z^mKoG;R|TvhyBALFJmgM!?%y&cdicKTKk8oa&5ct?S0tR+l6mU{gJAi!w!6V2T}k# z@NEz7&A%D!7wR86CmqA{FhX8)l$6s{ES+^lNySettc=FF+b9Jgi$ju2OIIhjs>SkB@w>i<3JKaBcIQ9t`Q zJT6p!{zh9%yL2OU66Ob$Hy@L16$cb<9&YAaiHFR~m;3Z$q31#ce1TbbIi=4Is<+(1 z%ZCo?Oa4)jQq8h2`n1I>F6r)BaVhbhk?y$i^42=+Z`%;^nvbft>>Sg1@=zWxA0xhO zrh@xIUhW1)pgoqCw}UP>A}wcWQovz`?IAN{HjRj>K1gShaGbfCmtVOa^C)6WfS0oX zUgUWe9!J49Q|$xsBS{-|MmM_?+Nkj25!Obf70Ju@A3Oy<<|$~cwJSRiw%fn;(;Oon zh!p?;v7eXMC^KgYI3Mw{ zq?qL+JxfcMdtNMs9K_-skn4JBFkGGY*0axD;&EUYico-qP(rezM zF$=eIWCagjdF7BoyRW4RxOs#(8|QXj{w?U%@VK?KWR0%__c%-3*0%7z2rCb6(NNsJ zmPmPe|0&4D$9on&>N6yt(%4>FyL45(Q{O>0I|$aI&>4RoEREg_SC*Z#<^-)s}-Qa6&fA{mYFR{-yenNDPl7Gis>PPSua68CszxbZbou&3~kJ`U% z+$!{GFz{OM*NSN?`Hhaa>}L7ZP0mHW*TKQ2xV^o=MS9L+d4z66X9cr2UW9Ec+>Hh) z_Q~GsxumEjqMSwnBLxG0yy5#vPz&a@8aZ9Z+3t$i=in|^a_y?!ymR??e$OVPIA(m0 z8xY9B5UiB@SJ!h_Nt>|m1_Srd7ar$g#pcF+=umvb8N~?t^dNZD(7eb;y2Dfk+BqHV zbh7MlI~TQ(UIxXe_2q}_`_LPJ*F)UM6+5o|CcZbY5?&AS7S|hC(mM}qzvr+%w+!M0 znge;wHuaR8BrC9p<8Bh}IIR?Nf+q;xFS8w23MZ!KlUUH`C#W4ROf*w84UFF(Rjup&H2MtF&%d(LgJ{a2p;^)4xfxFbo_~fe!lI`M&se(I${X5a0&C8c==#YZj|d0;;(DuLfEC#$bSi)@beqtXX%VaEn{)N2ED5j z`Htk0lMZ;nvDF#rvus?5rv|=|1(gpI44muT?>A1WbN=89_+h_~WLB^3 z#*B%;UD%LRe0a`eSDXQExO;Hk9LCtTgRW%lMy>W!(3_Y$b%XOAJc7ZwlhV)Q#^t^W zW6KoGmgEYWFDHU*yn^|XBnJnE=F5gdL-QrCnUi;fsQ*p&d02@ycnWmfU<-5D9eV3_ zWd-yNdHEJlG4njrY=UT8%`6)E(LwB}k<;3AMU+YhyqBTUI+uNUtu)`i!TAI_9fe-y z?unD&Q;6Bv)w=Q|IW#b|Cmw7en>Fg`5My);bnGY}&E+r-Z#-mb292ezjlF#lC0qsH zIgh1)qUF8DNmYkIJzYVlA{5ZmQ78KZF=?|Klx}D~_y% z4^D4o#nDwf%)n# zU^sn3pMC>IXXkI+6$G44c=>kZ_W5<#8);X#rJvq-nd=e65B!mGV$T=Y1-|jq9ze7+ z(d&?SFj-bX+5(aUZ-w~l_~%}isg>~D`bWTv0=-Ln2Aw@so0pT4!TAfq#v|_Bni%Lv z&cenz@TH)4ET~e**oMAV3bqXH0Km@oxk<*JFm(i^Wd!jBSAU>_e$&1Z!CM^SM~b#Q z7?{_qb3b0L;_NEcHW@T4!9aBDacM&JB>Nw!rB zkDw2R{@S9kIIoZepufO&n#&VjH=}!b?7kxk)7?pOY%mcce=F!GjT{K->5RU9IP#Ev zqNhS`P*|Pb;p%FO?41G`$PMBcMe}Ivr=KVqw$i05-0$jg%>bwNazA0k5xvAWBYaD9 zsGMlF>XU%$B;cw9w7d7S8m?ADJi^R;FV4jnjSandO~X*5fEw?HK7WU2m|EY79%gNa zoQcle3M$Hp{~DnZ|Fyyq$TH#{O&C>}vVJ1!exrXR>h8d(YUJ%f;sEj}&w}#Cp*+Yy zfj>^zeNWFQo6cQTK2bDbMb^N=wOMZMx!@m4orv9vW&V9IbxjX$&a5Nd_2syWMf$aB z3t#P_^8eFD;*sdZC&8CwS-^(4z5hh`)HEbrXz1}h|SY)SGM*i$OU{?{LPV1Q;x zvLd)f9b)5`d&jw$5TSBb=5~LvdV1mXVnKKAF#{aR^g{91Xvf!l7ly0^lCzFz}7 zdta#dWen)S2H=<^`Lq5;&)n`)s~3uQiYbLXfnUO^Kf!X(|AOUp=a)Wi*GAmpf7_?{ zlW@;GX0UGR8D7}}ZBmj`pWEHGx(9Z*$Qy9m%AUZOfuXf9G}lPagU*VCn`MN41j1#1 z#gp)@7td;}bD~vcc=q7neaLZ*cZ8rj0`53yCz4d)>W)gO6`NG+wC{lU&yEx2}7|TkN?Dkk99Yt^1QbXP{^7 zkiBl2OLnS5NVmbViuP4Sn8;*){dVZtCd1QV65cxO^?KGhcKss!Z+9C}KlQ==s?HFT z?^$oe+UyUULn$4CMt1O!(!HXwIIaw{Y1?>9tnFDtVcQIO9KT&TV|qqk*hc<((Q0fHcfdx zYSY*(Vr98zW#!7F<>68~)l$}V``!e1YVTaNWIw*Wi@SH|XAID&&b_8VeP7}$ETUpQ zpocN)@1sXH3~a}95qfdIo0ENzpQ8KHeQ)xRX$`->`Xp_q8u@wp%G{~ zTcPjFWN}!l`+CRt!!sZoJ@L{Ybd_&1)Vm1t&^oa{P&6>!|0UK9t)24-@B`8P@LM!< zb;wgam`8dOJvWhiuB0VAC#atK>RSt`mI~}8{ejT~3;YD}MHIqkbJbZMvwdLn9jC#le##JoF+^>F-R9`@xx@Z@*2@|;&D zAm#0YDM!6Y{fCt0s3WLFYzAU;P`9axXin(P3|K`2{FjGgtb_S9Z_?Plg%T~|ToofE z!pir$?wM~G8N4{e2bRWH;$%hY-R`*TuB zZO=|{vd5g2aZ^q5H+QzAo#$TYk+_Mta2%kOxji^Ob+|X&3Nzq5*KZs@4ze1VZN>~c zSph9zIp2ECDpn#Zx`drz6`41ap9G9cd2?kWN!I{3LNXT`Aw_wi!o#Ddy zGrVVoW$_$W^bQ{9rsw<0amPqIV}BrRK<|DG`y8_p#ZDJLJuhL4im0Qi7%M6xXim+vbI1**dge0|AG^k}94ZaKB1n+*L{DX4c$_7QZ zB@Bm>@4S+WvL>lz(P=i!kI`3bXoQC!`LY}5yYjW*G8yK_Rj;@dKY!=`9ZqfABPAo; zpmW<`FVx*AGI@3I0{9fhwv{E22hwFim!Vc!;Ti~R3c3&3K|L{f9?ptP(rnSZSzo<# z4Q>^Lrj`4uiQ~Nw{BgbgLWN7(rf3`37A_|fG)1^qFW`3A+Bk)t;gab+ub$#H zfKR89@8}`^#8}9;Y2-zH^#76)hHF9*)u*#+tUU^w zw-J!$`X%P^aph0;+UgqYj~lQzR@%=15Afhhr{urt<>cDFm;7OUHpi9R`8@$kFZnFV zy|X7UymyFyNNa}9Lj|A#hUkQLiLZ$FOv}-D$@2HV-U|h8ZaFHYR1Eh_%btR747cLI z$LtL5F*rsNgMb(NaujYtlJJbdGa1iVJX3{*e|Sn6DJ+nN6>-o6GM)zyzdcgggc)Hx zFG0pKvK_Z2NEwUqjKeb#GMD=YA{y^gw0)3~dzZEK13tdmSq%$z9l9Lznf+G_S0r{+ zRX1JjT^Dzvy}F?@l5<>pWcHK$7tf67;3nAMt@+!q2%J^q7AYr^?I9bu z4@uxXM1c>7w)EnDMQety0pj@6-O7_83w#^&2cmj~A_0^(?A1du=mID`-PFV!xr4m5 z+GN%ia~sd%{~uv1;wjvi?!SNkcO1bmD|F0H{DdK1LV5E|kW#)bXd!m`2~=Rpc|0z8M&>M{-4MTBKL-6UUH$tV{GPN){=`w=p9g|F_# zGkzcmt(E#m6eSk2?JJ;p0z;F7={%)pa#V-|jlx0xZ9>WCfNZ3@N?Ig++*CKq)VTn%1(Us* zhq55=^2=TaM#RbYk(XS~F36CqkwGY8Vx2-qU zNczk}asTl_(g}_kwed&QV^0`T%}?_E?>yo0A&&*8aP}qIiEs|}&A+gowI!6ptezy= zyWEV{C#7M9<+I%AH(IgA<_NUg>}h|J<-xzGDm%qtd{*EtBYcidoTGHn#EaPtoFF86iN7+=ooch zUpa^e9&cn>Q@k-!G&~00<0UU%qBm#lLc=Ym=13zvVV(fLc9Egy^o%rvRZ0`&{NC&- zZqZ~d1$Pd2(Z9w`?XR`ZyrCqx1bIsDJdX*|k%rD}PLOZvHMczI3YS6a{R`iNu7KYR zk6X#Ob$Dlz0{wr|gnwtE0$u+^cv_3c_e1y&c@2C&gzpnVCg{G$;Clrd`(tv;kcn?);*MW2slN!XVe_zHzhV^vDzPd(X^#>IwWz-dGDseFW@{k%2V2{RAg@Ea>>#ugV5O3vy@6vJ58+AV0n|If_fRtoVr65s znB}oSCXhU*{ko^(kY$d9HbnO19lE;aH=g5{kLyOxo>E{DF}7ktfdDN^Xt}{Y%T?cp zmp?RgB@@qu)ya3(Lqb+B)UTv*O!i*+B_yu}T5r|ZyLVF>AuOpAQ344cf%2Zcb85l6 zh^fQv{|@xcPzu&YYlHBJ3_mjPvk6X%5p>3}zTF;b-5udGO_(En{npL4HXp0lwu-pn zvV)UfIII)%=M+G$jLH7~XB68+mUEZy8O3U;cRt`94Vj#5O(J@Z{I86JcEh{4FLquT zk5wb!6j-Reiy}XOzF-Om_D4DNqFa zs`rBK)4~`YXQOlN9MA}Q0lFcOO8ZegU3>r@m(fZq^J#^=uu+-;oc|B{hafoz&JJmJ zWU1@-6(t+q4#KdU0FUf7w8l1ZJN&}N;cFK1eu%F-)vrH=YwIBHp*|fsgJ)kM6t>U~ zY4JqBZ;;W$K`KGL0|&;zJW?@D`K(A4KdC!TxVNwGEb&mPgxmluq-g}B{50ks;jPgk z$tG1orc+m>5m-hUt^dhJd0mLBBiYLFIKc1rz;a>HR>eAFt75b8Vzg(Q(|Cf*U5{TW zYn`%Ild_5|l$s91pA96<2xe0gQ6%&4*pAWLjy7w>g&r=q)|vDbH}x^K|7p*#ts)wM z<#hXsii!)1ff4?cuLQD!g}?}ZnxO3%ot_UftVtz%xPfLba2!%FlL&QG(tDM_rFJus1wbqP#{C4C0W+5d^CC! z=4i#h8o_fDa80^AG1Sq*O_-xnH90sV)4D!u(4X4eL>g@5O-l$ZXu7(_A}&wM6uaPQ z<1fKRc=wph@)}-l=i76RsQv}d_~~oU^{?G3{qebSN~H?U84jLq!(JH0j^jV!UYdu5muQ}E zQRibNFjJVv7lCn$fQ(yR3->~N(HA($7~ut;bjQ-=!vmR~SZLSL``ZKdmOQa9@Tw|n zOptY89vnkxs(uA?Ax|7#-WPZ#$PMC3+W(2}??#ZD?RWgJW>e!$m>ow{Zj-@xc_sW! zK<5G_fetHf)9jrHE=7c6AaF#T6+iQ7zzJLpA5K5>eh!}e=a6Cg9N6}AaL$MHiau9{ zv#yOKr^JoP6JH0$8ZovnaCh*BmC)cle`!s<(|jg!uK{+L%;H^&`IB5FVce%mM7w6s z8#v=Ur5Ih{bM$kQFTkFr-%I)*Kdfx>vuux7@#jH*vM+E`klT5AO+=U4$as! z>^v+-^70i%KJoM%!_rfFizLR%hkHr?OJ|pGYMl;!avjb=f9^B2a8_$M?GNRxpBiFM z=iG6{Q>{-sO%`o<=r1;mJZ-&W=<7dxkX-NMh0Fr@2~L;+MYIB`B7Nc{V7Cb_o>+{`~Oq!G<z3*A2+-}U#bi6FRkdtsGQMSh>c>nCVq&J?1);e%%R7ZMF z1WuRkx%v)Ww`)rCRhOOU&aj^NW%A#6(?xHX&ZqT2eriXQBq$M&Rw*mWbYx7xyj=Da;-{QjF{&-#<~H zb7?{%ifF6gb2KiZG9kyerHAsWvd z?u-D3gXN6=Dn8gE`#)MhqS`%f@td? zT@Z6au~O-RVVjpqMq-N;p|pGc_l&|WX0?{>j*9J`QRE!1^M0RmW)RzU-}mM78D`Gy zcYfFB_gufv7oP9%J|^WZ=xC}WW`WEv6by;IA1=bm-_=j8lFz+B++43$ZWBBjUzHZr zeJ9NW7d5~7=vhtn(X+a2J#=_Ah)1VP+36mfjb>9cyX_YY;NVxG$4)g<{82bE)vF?L z)#>1s>mj&~IE&Q#WtwKZjyyStLs7g|6q$sgBS1G^KwcNvvR-u)(t(lR$+4%t%?am6 zOS4Ue>)G^hAmQ|MqRyJ&{ZF`w!wch?3CN+7qvyv+Gvqsg>Z0lCJnN0an*T|BOJZv` zN+HbAv+Cb`=wl-bgb+35ln1@AzT#0Ko>FPYQ7N%bSK}9>(He?8ZuF}5>S}J|kQWf` z3}N)Rra?=#^OQYFsWJFp_;Zr30`Ac!)w~6;qm2jZo^njG6P+i0=CjbQ+AoB{<6=ns zP4DN>4~AP`Y)->C4D}=9f=UhVO1+tRaR@Ot%Dx#2NUV3v*gbli?@&3UW~}1LShqk# zDW~7J!OM+~b^(oa$qVVStYOq4k|N~ei)}HC@AdMf&d{2Wnxm4%TJL-Zxp#oAUT_+} zQmo;vdb7^#QO|2t3tIJN`MV!9a*DiT(HNVNZ))Tizm#h(wUm@gk5~(xixG7>QB73k zMwzR$2|X?vNe16$_<((a2!7(%yH>slx?bdr0bVyO@0Asu@75jhhxBUsBL zQw+O_Z;9xi2~Dwb_5NPsdTqiV1EqiCDsrCG&+O165*{|L!MaBB&bA3BjRjDGkLmd?slyldG!ER&$aSOQJ!T2SFeSJogLvMP%LoRG~|;R-xgG_tPr z95Z#9+z#SdM_yB&U7}yu5opK4kykSV8CjxXxeSgqjda&aWvthBhweL}uiME#2OE2YpHL39K446%zITWQ-f%( zW!i0HYHyQwvnuGZZt5PpPY>F!WQhs%K%ky`(cghHfp*mlpK%4w+|<2h-z8}L*sw$Y z$l~7d7CE=+*IrWZn?*aW{(1a=Qor82gsa1^Q2o=idiCYrIO`bU0O$kRMS#`x+GvtX zu8rYBIhKn?4gqpz~zpC z?wj!6N?_9-BRn@i*gEcDrPd zGo8lk4qt3Dj_MeBZi!acPJT17;QIaIi!H{Moyf=%CG*fP^jc?|fA4CJ1nvT;t}ugm zzR6zT0+_hum5fjd+YTcn41SM{v&k$XU~x|h?m6$JI#OL}{AgeGi@T76@djBxXO?H+ zT^d-y+k+lp%DH%5kDQrTIa;FqR|mU$CnC=fCk*cAjE6Y;Q{%@AS+d4ISuTg#qTH_< z_t8byG&%-1kx~v#eG{<;N9yr_EhE$pEYoaP`ZPcpM&7Z}YYp@R^#@`c za&6$2ev}lSW%3DL4jHMWX-0mH^oCU}=+rvPVh?d0*7+&U#qOB#Tal4ui8I5knYGp4 zN3sX(K_xAPPP;>$WOk&p^4T@RK0Wd($@CN9 zz5X3A1We78<+Y@x$njTM_o&=2mo}^=o#f=)zeRMm=0@h$xQ9>kY5-1E78-W zg>0a8g1q$#FJ59{OeD`_Hbn*{{qf0yiFC-rdXOTyk!hJX2oPO_yhv za&%HRwF8ng_KQhOienHl>00iL+Xfl8Pi*&RK)-8?7zb>%?Xc;Fx3%G8%vhPDbIft9 zb3hA*WPysVa}s)R_K>leTJL1Y)17`rLc{rputxq*nbki)-KdUI(}Y_-k!$m7J!~mG z?M^C~?c6E%M#3$c*%mi>K!&$kftx}7GCSB$mJ1&0B~kAG&(KR!OJu?FM6GX5M9?4O zV89>av7Fz*AA|af-br2xm<7Y5`l(E7sc)(Obco{0g8t+qXLu>JA`VUSyhPyG%RXn+ z23~4lg1Q@uH|!Jd9?-(RqwZp($oetpCt5Nj{tdKn0QZwzo`h_+T5djSo;W*kc8oi1 zbt*g}e?q3=ORyr|?YioghMc(0(ZKWrwv&Es$RE25I`+CJLi|h*!%SF&JtFwbdsqpE z8E*j*izM4%zMJP(isJ7ELg$I%{R28^VxC(eA|DmgT8cIuxdZeEpKr0o-VW#@-bT!V z6+SqiWi_y1Z(fWH%zcQ{K<-yJv+WV#Ar*OS5@q70U2TZ3-g9d1&zR4O{dz$B#9!A8 zy=Y1V@(A>av7mR0+uiMyDXY2eAoZZRN6v?3amu!#|CA&R3>yRFHhs7Kux z_|j4TNWnVco9xaScrb(w5_au$uwDIJ01fzu;<1f?lS*97B-4YT&L0aa;ZL8k zH0U?#Im_N<<32jeJ}_GNpWY-llah;C?3z0SpJyuPaw$yO_nzQWWS=IiqY&t@*P|DY z^y{$WtVjPf^&_?#t8pIURbv0F=y0h2CS)St7IbmdlB(?>_?>Xx(1dXSBC_p5M}{cY z14kdfritQ~egn6@ILh&HalGT>GA23BI!<`~(A^`-qPd~tQeQHgObHMV2jXYY4L6gyt4>zlr|W^c$cB6h)?Ajd6GrE84*Hhrc?iN+~`hTG7x)jhT94{oA~@k7s||4qddS3t)P z44R7Dz4V?(0`H*~^@+}drpiI_oj&4g2gSGWt(?F6U~)0Ig%no!PQL#i;C%bUTjk$a zC3!RtZ5K|&h+=tPTA2=`^Hd+@EI$z?iVycu`?m(>&Mtf#nm%_kzRzXPxEPt9u67fJ zyDRawGt8e~$F!v^VJUTv za{b;94@(o~(D6TldtuWJh(wq$hnZ~WXXP(`KDs#ph@rE2@?~Ny#e9ckoVK^|TIeck z8fpHo#((4NBK!}Zo!=NM?VYSo-|726Hwkx6z!TZ{PK_d7dqOfYS+{d7YP+l{t(W^!WjXq-aR&9L!ap&V zv+~u&iMON7ZHUtbhl6qziQ-%S?6CjCN1y>adTAs-0u9*QTkG_HFYDw$?}(PPjp%2h z!9b_rpDz8Rl-~8Z?1jE~9DLBx+kwcU8oI*P%Kx`B>BjF^3(FI29z8Es*z8Aan~*TT zVkcpzT!aSeXrDkaMabL^FFvgECA~(>q(!~7?p0XlxxJ%+kWUu83zz}(k@JAgY3UgA z;QC&o0pG%R_&n+vTyx#{P4nYxd_c42=B(Zblqd~1XXQJHCNfweJuK{(`m_*F4tVG| zp?~nlQpU`t7ZueP-N%FaJO|zKP~mDeEd_Ta@PtTEU#hXp=M7VU3|7BTnkYoxqh@}_ z=KwyRjQG$oU84^@bChq7lw+88Oz_OOzafeX7lP8+y%Y5?Da0dD`iS+J!!TP zvxN8!WVy5<*4;Fn?q|%Sh4(GohoT z$lc3x>$tpbk${;%LD@35#U46FC*?hgx`{v z${3eNHBu-E;f@)b=vbyEky|m}K}}+5AEL8Ko>Bj58i;ulKh2HSDN6?J%&w>CY~a91MT z51RIGwFvi2M=eh3RUJjE^_B?!nt57TYJuXU-1o4ZEn4@3 z%oP_o$KOo%)N10P^*&+k)F-!-XX199r-Nb6FR`zg;kJV^2`Z(qhVJ(9gPVAsgNW8rs>P3UcAkV3f z2GyrnMBiZFfprIw;#iq|ya78W@mvqD?7=+SQL0DpoEoB?^Whaeq6c_`RG(msWVlDs zaX-@l3I#+3-~kVuaobn((oU-$kz(;YqDrQz_5h`xD{-r9+3hr&oqcq+9rvjA5N{0b zH&yV^m83x$N#TXJ3RHVQTcXI1hhjE>u2x61W?{}_$1GLQS2s`=WJ0i(T&a)~%zZ9d z+cgM0WX-f~^AzN4k|kq>e_?jngaOwUnKO(Pz&GPwMo^Wvon@PG|0YYBCnvRad|y>B z3ruBgxuq&UC+`ID6DuV`K2Gl|=SXi2la^fm7U@XC!kihTH#LO$RAEm`M_j3`_w{W6 zu1%YPX8j^aMr_7BSuRp`9>$#Es0Vel@#7qSEF>y8o?nr-BKJ}BY>&A|oQl3KL0_j2 zEu%BZJz~NT3xBFz4)tCZz0@$5`oQJQl{Hg8^pseHs<$les z@?3igxDz}fjU%@okbir{dLUVATz>)1ogI?ON=GAnk7I@3_LE*w!{>5}q|+wv37LTr zh!xKC6aPme58RC27g%$d}iY_A0MI{G$*oTUTBF{%V|3r z;JMl$RT{V1Gfzx~gz^|9lz1*BV*&j|E-$hVGvFXbSz2peYX$oUucW~rh^~3IU1D}eP>N#qw5z^Q!AW9LhSlO70JI*2dh|<>ad8YjB_Vmt_V``uO`aNB$ z(1LS|t^;+8(08JTkwPxPL4?ygt1-Mo6X=2R|X;Gzb%h zf1AoQz7kd^q+yRciyfKz;czKvj^zdq10YF{iy9%Y4+b|b-kTcA8eYTc1@ zQRea(-;?^b;CS|Q$MlHZkgj_~AEb0!Pj^%gu&4g}cuHAWWfhMS$}l5bKS+}G?WnH? z^-XXWmt7Caq{!FZp0RoHx9qo%>r6O?Y~sW7^-prGpO9KLs?dA>)Q zx?Kw}-`Svs)5O(3s)LUyc_kWKipuIAHMXc^UWa%l+wk3IWY5aA4EY|}%1Y*rf=-a} zC8}bYb*B=XX7_e`KQyaGTsOX#??ft?LbE&Gkq1jMc?x}W7CV#ti~5`N7Adz-oydAh zdkX2*7>n8iz1q4UMniaOHV^`*dj9D9T=O~XhbH7P8b35T_m93xCRTXJAB$f*@$2gW zoz%7_)yYYV!uPD&TNybIz8unRlgUmnNot)uNxFSD^cML#>C@-Avmq=eB~Iof+icnU zx!lC{dx?L%V>x`T?o=~79#u1c$M<)4s2Rf4L2P;1279b$G&BVaW;tO*Ckgd~PT*kZ zQ-Jd3dy=Iy+XoeWG<2<|%ofVAuJQd|zOr%nim@qXe&`6!{j|)v-$gQ^EOCQxzf$4` zf67{P9tF4TL%#vCg|UUuuT(%1HV*Ue2E?&={7f_DB)Nph0n&+=G{wyy1N@MY?@N$1 z-2q7$PnWWjuLWHw zQEzal%glKcClHgJ@A$YB>s8I9zk+`Ly(+OahFc1KoO(L#92?d(AmZUG{QrYBtO7L2 zy3D;~^*1_A$JbBNQ3j4`k@@?pwd51_74X=M&^^{!8Ox`9!s;WWQD=-Tfqa!cMHxN7 zCJ2E~d5_oy>^)mg(KOKd-UjSlw9f_QR^s`#;#^6S7PvC@3o%RtGN1g|%e!?P#kls0 zjsbYrIqcAAuPaZhWL?RXb6ka$<*pr-AGv<0WD(s;Pbs?0FX4k7jK|T)y99jQ{m#ZX z*eyD(+mOe63lK=Q10CZuA{dns_}IUw*#f_#O6xNGud?pN|7vR&{%^KswkUe0OxRf@ z8D*kK-ft7Mr#eoVnQ=1B94*aA%p9hfQ}U*QW^(@*nyDG4nZc*u1D|G?F&|VuQ?@h-SwrHg*2KKc zXVlCxt^)tZ!hY!&^HIyU<-K01C38hBU%%`l4x7AE{|lgUVaNhc`2a5#VNOV)wEs(# z5%6Lm_J3*Qe&PDleMior5Rnyci)A*||dT9Xk{XZ6B;RLm}{>juFC#RDvYw zgb9dF0wouw0{_L>LcCgd0kFaFnSc+`ZVi0wUK5#h!s{_a7sjpbC+s z4c6oCW72zVW1SyM4aTAzL+i)Vdq&1LUsYotCHLk6E0aQa2a29a(Tx?kk^2f>-r?XK zuL0+D6M7E6z%yBq-A}=tvEqD&oa4}n^AjW^-bwP_w^(D7@tg52P_xO@A0%f(#+E&! z6=@=`SCe=Pe(h&cUOPi1wkKnN8WtX1nV*Xf3eo zYE=AA+17*XT$b3*ugdh6s1v|~f8ypRHa>CmiBV79{N%?8KNF%+#U{0x;Q@P20!INFeO#T-<=2**lKE`qm z5#TPMB5INu(^RKlyc02AOzPi&4-~G~0I|7Ov>}o|yF`@?Po0z_CEH3ex%d*qN%y63 znv(1i(lXZiKl7@94`^G)xDA=>;lU^I70+*gcHZTr$oPs48|-yH10#B}oVCSjH(8oU zpUyZRaF#T}+60}`d{q|qXO-vrQ`_y=@M8lKjt0HWqy&2V3O%P8sGwGDK5QTitdcRp zrGZ}H37dg5dI<=~Vc3PB?}VN1bl`W!g9$iCR1R-%IlVm0!DDQ;+ruI0oWB1{FOH0- zm$#uVRq#n~!?g=u{5KL#p3pRH{zEXhG{ynMFe5}aHA}a)GXD^*(x0)*} z)>S->JJdVm^%H{gQYTS?~(FzgTezdayd!$jDY^#XP3@EbB2~C$lX(<<4vZreN+)w_&zloaV0w zo(b|2X|vpuF}`WT$Bd;1Tr273#T);Fw+O9K{)CXMpco z>O?u38uSez)l#{{MHeAQ-V%8SS4Kp6h9X>*@hO?BnKmRwQ7l5bv1t>c+mIi%6}ekX z&*qH!_VY4*YUqio!mpr(q}A{@P!hj59eAil*kffRR2e%1Gs||noAg5SXPcKT0k(l( z{M+D9%gOExY>Xk&jFaSrQh=WwBjm_B2p`{%Jh?z3_7py}t-1dB-&EOV)$YH^qV6ZbJ0PF>!aKpsKch0iiBAx4<=+n-IlBz`#q%j}h_-55=#qN)a(R+rO;r)%V=C9LdZZb>oh z4(QQLneadDvRVR@{%!WM61 z(z`t&dGt;4D%Bx~5w5{+79TpdXHS*t_B(P|c04;TTKKW0G#aIW*Nf$h&UHWleeuyW z#I!s%XnI^|HkX;42QWf$YQlwI5>m@7ts)f9H11s|<#THQjfpsEuByOHf@aWUew@6G zROT*kgvs={;umf7`6I>SHhbHC)8m&w`!I&U9!cVC8&+=IYJUcCKB=v7+-7dNI|g>X zu~kc)p@<^y^Hd{iVF5CS&EvGPu5hJOU9*6b#3tZzsA?8+D>gp5VLw*^zByb!j@yJ; zAH}VQUKUwTTr)7kQ>yNF#$+>-`@k=eJT1XLqddoc966W8Ka*GI zK3l!D6S$D|`{!_{<~I14khTj?yCP~W?#S&CwYeN+J`>z3{L0TpKDrtS{x2U&>BbMG zc+<_~X((~I;@3F`P z6_2}P<-5ZZn92J+4P}z}aX;-vlBgR*E3tzTF0~}yMDNA3@zvX)&D@5l`wZ@KuZ5vE zsJ$w*_inT|T5j*1Xm6#LT7DbOwRS(X56|1EoLBJvM6T5P-|Dl>tAekcp*5e|#s%xI ztVPNq#as>B&cX=ny~hxR{thr!Rl8#mIczBD0}WZ^w)e2=ROd$E*Q()}9|0}-|H9vH zrl-)E>g*FgA6#(Yw6hNJ%S+uKNa-aXNbyE>)eH75t%tkx>chzIR%0K3l8_K2al(+P zPzRsTdtFiaZ#dKB7#HO2X4E0!B=z9m`DV=AX3XF>oXY&24t-oq z)osqWY$m&K6P4!rr8n(U$ln;Nb{UqsNF38A^dIR6U$ zGkkNa!!VEDk)~qCG(o2gTxJJrijrvo%{ZDu|IhN1K5~O;@t;`*~2&qQ?|{za+jrQ0LIO>Yewx)_LoppTDVl?LL~j zt=+5l-Rmm#s&*#|C-Brye2JIpc>mx*_?>PA<#-cb<3u^m!lJ>XKEpelxwxJ(@GkmY zYN~Nw<%|U)kPM?00MZn=y*4_gHr%;!4=V|9{m5W`4 z&NB3~0lCz+w6-BLZCjU`(WGvX$6IP~I0<`866X&;1C$=-m=t0JV$XtO@a;Ve8{lC{ zh1gOZb65&B{#tp_!{se-QVbc^Kl($d7dSWMvs9NLRs`6~TIZez_cw0g?v@&6s@)B< zDDKb!z3@RP{nSCwxfMB?tt+@U?3t}8T!s5Mc(_-%Socm}gSyKL6l(tNjY~G{z|~}K zJO0OWSsSwGSbJZ7gZv-bE){E~IChn3rAUzMh>|lU56W|*WfstiWroeVn%tZ_Lg73`sXkf?e z?@h~RUXODmbCXjtlM+GUZg4QKPMo*g@wVgbrRSH@jA3`PTcU+*p!O^{z}B+d2k^~J z1UHvGF(>HX!qz4^_B_bGzQ;X_`%G#uoqMq1x_wKRy01zZ zn`Gh^KeM=EnuBN`cq&9}(64_j6}&(wW}=07l)C3Z@T;whz>Bm)E+?L30QOGeS0wNz zSsQH|D(osBt4c+T#diDTP6H^?E&Zz02rh-!q?*Y7I31sim;weere8EbC-Ax3g|#qV zdd_~8$3*jA3J)OTwL#)0DfU%|B=+{{N^liu=HxT|y_Yg~Nzf@Gw`}WH=n|eseH5Oh zqJfaoM6t&|9k$*3P^KtKero{(L_B@hVPuX1K6PEeIa|mnS<<@j5cSnDeZjfI>X{uW zRZRL!{zqqYY3K?jrEh_{^q3`?r&>tBr@1+sG3&cN35n-ih`6;Ty(bwPzK~7_7VpHl=)TGeKRQZp#kD%c<5&RBF8s!vT44aO^MB#lYHDj+FIy{X!Z61n!_6Z! zvc{F&;y?YQ)oEPOE&k2#7mvfv8*Jm5z#O9ag!axsgv!P_8}=fzg9aR06qf~h*C-j= zmN=J!p7we+*^`b>e4s-zxR|T=Ibwy+2It87(_%!KXSzSZ`Kup@1vb6RPnQ>NEZTro zgp$T$4;nw@^FAW1(g_%ILzB&Y7x16Yxz!J^hDU?}vS4d#Tp-rIETdc8BFEa7P3sn) z8IHAo3g;{3So^1NepMjWzM%U)v~dk$?ePn3d?*lWZ@aJ>_m?2n9=~vZA;#8tT#;9) zy{Y(}h)*0onX(T@z}r`^?N~q_#>jZ`@k1(LIN#%Bru6gMoaxTRj*al1+U|hX0^W_U zKGKZ*riYo->3mZDR{JEZ5GItDG}UdArf%ZHyPO?T zTtl2&LhekARo!UBqeoF5CbP+De!ws8^phoA&!;$NK`uBdF%5Qb51#|8Mdl}72;NtX z+%C!)6L_EX!$-)Cf_1YM>o~OP3n%et`xN;t3;lU_MZ-IddmjYX<$F7zr74A$X6mq( zM(1;A*i({t52D=hCeX2|11jufmpm6Avbb5;pns>fg0?<``f})V9jI`S1}Fc52Jb~y z8ifb?i}bcN%+dAWc6%;%>Q8a>B~c*xO{v3g>g}f}pG%(Q&RFuCt={7_Ih~F-FdDlY z?chupb3DDDC40D*Z*(HqU~Qj1nw< zt5dE^mP)wi;MoUx)M4+^D*OAMuBA=hvr7@5+Dd&!bg%^a6W8p`;IQLJ-`9_wf>T?r zk>$wA$>UXH^YmZp-_79EhvQxKtF$jIE!Av(qg1{5$5PeilvV8J!d20E%%;#?6DAQF%XjN9ET*V;mN)e^+0PXf9v`rC3iQquWKV{#`>}DDZSHihcdZ zP@fX4iTU`f!Dlu;Gw{jAhh#=@5Evf=?K42wVqqYoP7TOrlliiqmR9{-l$oXE3l7TL9_@Nkg z!kTaNmUgPCN*L3>5}Hzp|LGz4nDgc}C-oOPPCLzwON$HFP`f^aHpl61KdX&C4dj{B z=4oO*JX=a}0f-5bGE7x=UDshfEMjUS9|5XkK*Z`U zP?&FcEwa2aX@B8$Xa{MHcKdm6de!RE?e^&7tHIZj4)g%{#ZM4va+u!=Z4;B$%BwL8 z=7;s`0!3OO@w0p_oZ z6~5|!6?dF$*1mc)V95i&h+mZBCR*s}jW~6;?3*fEMG!+c($$s!a@X%1lFEHY+5Q6d-~HYf&lJg0URnwOaxC zH%N72rtrWFR%~00|4VOl;7w7cbyyEiJ+4MvK{zP4mP0lN27b%_Waw7ZB;O`Ou1bl9 z@77{Jd9Q-k#eH-a@ga--Kj1D?6_bKHjZyM!O+uop{Ot!nviH=N$<_gtSpJ0L=0sDbg-0p;h$b+>P0>Sl8V5SKhe)Tb4?fU*kGrE zHqM%a+)YE;$=7zW=6V~fIoj_$F+ju|ga0X!7TKOG$Q&8--{wz)Uw^m^U_LhI5##r{e5nQq|!ll`+> zTqfrMxCR`;-TguShxh=}Uz7c_5_4DaEhuSP!1Bh)3q6`-wLII#0$);B*&wN0Y?5wQuQ(0V^nZ9} z>Rg`sHH+tR?48U6lDM^ySvN|tws)j4umX31?%m)C+s8}@+Y*)h*;zLGv$N`KqF68a zM^!VaxAMBZB)5gvguN&N6L3;~MD-k4cv5IyXQ^H`W9>w4VUe#PqBfzlqH)wKsn{+2 zVs4)c5syUcqJ;!0^pwVAl-DJP*z4(>5i7zEA1%go(Hpjp^aV3Hs?}uFYAR|~hgvo9 zM%+18UJvsI&q5celrRb_a>hXDa4FXhp7chU{){!~h6x`1ggj@JScMVTaao|1PnW7? zOx?Q`Bo@#NAh8VFhZFQUpcw|L+jE+Ij5!%K)6}3JGWs2UG1jlE-YWAC$PoxFH2gh6 zBTtEFsM?>5VAly8YCv zLmQ5P#w1!(kO`dn;;#b-(25U1Rg}7{D*gAiu*kdl|I-%Ad~&%h?+*QXTjGDUE&Bh^ zmV2&f%hW(ymIvArA-844FWU06(v~?%*h4B2c@>F~X^}Er6vM&_OZ~3KBJ zOVA|jAP-;%c>w;*s5RnxdCgu`4U72TMC-VGwuX5rgCkm;3c4NZ(qR7Wlc+aOH|t(C z;k+NX`oUcT{P56!-!;N916t)VG05#84=>s~Mh#pDjz@Wv>0K|i!`SpS^y}uz`zx6( zqf){_(>MDGdx&s}zJl*i0?vn}gyQ_O@_C^txN0{IuiE0_ReR$At=hSjY?2mh_D*>H zuEd(X2NZWPf5?6p;&L`hsn*cl_MY?%?Jh*QV{JtDf9|$mW@B~>M~1fkm${7?d-%CA zrfBHOps$<|g3&p8HWb(o;rK)VuVm+7IC^(Bua(z$SWPI_`0f3ndq-7kN>nw8`JpCS zD5p`Pw{Q>1{J9<{j96hUNLaO!9D@<7fEapLF zUTHC}LhYTJJoz3zNCsx%&Yw*tR`p#m_pYdKon}w#YoZ~C+ ztz3`wadqRE#`o>Xoy>?XowJ~}uOKB zwes8tPAqUw7$CC?E!SZlv*zou_xX`OMmeYOQc=K=J&(83UBu&T=na+cqj|rn?{D7X z)r6_|m2+HWWi#F`Q(K_taD&UMZ7`H%6xCMCl zKF=cP0~IZ=1Lquw{{o$FK^fsFYqoO50A37nNM`bpVrycB`O+vvU{%;Pt<1#9oS6%o zhpf4~jlyewwTy49c4Rp!9ETn1!mPrILgM8}ZpQys(sz-}M}DU+XPjFUf0Rhq9n5+f zB^W=T>&VxNd|$x$NV}e=)=!pO&-L1_XniuZetC}@dyba&(>2*P;z`8nw_*ygRTA?TAM^ zh)4WEQc(1Q{8a?CBz%#Zd@d@SMean$(~cqJ*!?r%i>n%>8fLa+#0O7R?D@kL@kyNDSkM%p677>5xI8H;k;vrbUnoH1?xr{SY=+N$UVDF5@b)sV12HV z=T*JC_Q4wYe8ViY?`{?Oi<$%jGt#>hvk5+U=Mg)IxP8pf$0V!#giGce+b*^- zu&XQyd)gRyK+$uTUr0=BP`Ot?7$g4+QsYc8b%HXHP5p?eh6pV>ve4Fqw;*$v(EI+Zt z9a0((9+`4nebBrEKHg#Sd!FbkmES{Is^ho>AxW~iwfK$eOoA{5Z~B)f6gj&yTL`;? z_Vn=iEt1iQyq88p8fdO9yz?XYt-NC21$(QJE6z?qDWS+1MLfVGf=&ZHUMukSi9au>sIzRKNH1FZ*8NkcYsZ+QUV^g_!;Vfc=6uml?K)n8VJR9d4 zjb~E_b==>?sRI@JvvMMi*LN8-2l4!DZs&gR4qXO}X}s`-9AhMuD>a~ZGs*PsjgrPV z8d^ls85!N`G8|jkYeu0KY@inG-e4Wxx}pwN@HrI?6H8GGqmg^cZA=3KQ}{Tv-l*D+ z_s4UE*|B)@^YT;HmZg|cI+H4G6z)(3?w}TqmG4>=xQpud4=>g43vhs@5~Xev{fwJg zPyM61=0Ek&qAO~r=uIZxGU?`b?bmfqJNC%9)`WKwp5^?8pTVOu;Vk^?H3q|}jPT(a(_2HSoUWh_s)JE{bW0O1LOa8X#|~p9?RFS7B`v=70wN zmAAJewgjJ`Kb5g5A(u2|8V(uGfXJazBgfgW7Z_bp^wrC0K2d z`wKY}dt|JUa5-A`mwsfR3y`yS!1pN{vdDK{;}qHEF}ITDp|O)SB#S)tw{}h}Zw7Dmsy)JUM{|1q z#FBXDHefkTEC;Hf5%_|p(<@~Az;n$~G%U&n()jU?8WT9abtuFas>yVdS3XH*^W}&xk)QR*ml9)r zUwX{?vh;+h3NvmGX7sEwbIA@^B1SO>@jskt#(y32CoipG@+YJm02c=qHQ(GEfAgd= zODVj3163NXrg04SRHEKD zIs~i0fDCWebnc`ZIJAjYV49SK4v3fhagElpTTv5Lzw3Z0|8@Jd{P=w2keArWZ%TUL zYQgS-vEAd^yCU>AH|KooA#PjeIZ*r-B&`@XS%=`wNV>}BP;Ph{;`w~gI-UVH6Z#v( zE6QjJDHQt=Q{o_mZynF(8sNt(#s}GqXo2G@RNnwE)<8 z)$?-T<$~QbiHqf5mcr|9$4VrvP!e$Hn8*n{y?G+~Y!3SD2K0>JNy*op(x;SSRDOfw zpFFd;`1NW6DbmfMW#p$Iq8<2#V(gnZ}HUdMKsa~ng@Q-5KvKDV3&oaktr*ghFm6oIwLg^b!F2EI>%Qo4 zP}=yan=QKmvE<2p>I*0{BsDS~a74+J)EcQ|JBgmzToC=Rww_sI# zOc^Px<}yYKpG7ob?0b3>AbX_ zbU%4D^WMW}zr3z!@1=d0_FUR~r^@lm6qSZ|vEZYO!PtzG`WxC6&vNCe>WZu2|I|qy z!p1MRTRP*8F+Tr|L+_+>wK+VT1@*Uz|#n}9T$V((F-cp7FZ-I zDe~7;Wt6>ApC4eYuKhmjJ;Wml*pr`YFmO9owMbTZZyzVqHjjUT6Ztvd{8Zme`>@`m zH79bf+fAK6B`Cbc&i$A7`9{=B52>$R(wYb-m@-4OOChGCpccrdZHh6s!?y!jwM~iK zw>0wDu@!&6_1rTJH|3J%CZw_gNU;7Aq78;t7k5`q^6~2nHgnhKy==c1F%g-0Y|R^8 z`tZquKHP9!9(OSN1jkM&-U)fY9G9PSJr{cHmHo|jj-QMOLPELp9anq=nbeDX zea@YTt?M}sboZ+|;aOeF?Z}DhBnqJAPr_nBK7gfwVbpk7l=F7c>spnl1V*-Qv50 z3mV7dGF#{zYczAL2|c%hGBn=dkD6!L4Sn{|=k7>ksEAUjeTMKDi5(9>`j8L3I!x0}a9`hqg=#UZ2k^(r$WAzq9 z>BFuC{8@F`H~NTnBI4--#}CU6l_g2XMVt(*&3)5)h~$MMWrR^nGVW&@;lh`sLl*>yh8@BX>hYu z>j|&6e@^21{m_k@wbW-Orp5ru=@(muv|IIZ&JuRo`u+a|j`?}-G_)W37{pAp>+`GN zm)sE}yokMGp0m?kt;bRKi)+X8>z!xas&i^hZ1?wTNAX={hu!dLhz$3{cDJq70!gLL zx&%>JCxKE+Qiuv4ic>!e&y=4#wK!4-&a}hU)x$^P2>VsmGY!P(yImyBGs0^wlIL}t znn!C%pIkv&sc@^wNh@Bx`zq)vBe2@1_bzHa?W|oDgBqNLOzuaHHPTW6OAc0P6DUKt zRSk`wDn_|K9vW9*!cD}PIFuAJ;S}AYSs+u7Y6)2Ssf~2Tuz)mQgqs|SxA-OAEtj=` zwCjq`rX+StrEYp7C%{X5b@bB03*sM!lIC4N zu0q6p_q=rL1#!*L7{q;Fg`>5&x)!-Z=p5p{d)DIWLqo9&{qFfau9gf%;_5>~px-^e z$JKj>lzi?5-FFpT5DSO&xO(ppVJO{IctI?H{^H08`u#P2zl{&+F^K0F4R7NuDaD9% z_QMw#x*3m-D|V_%qVq*3cc?pgW`pQztl#JU=-70{pvZ*AJp zKr^t;l@A|G%tp&f*$%fbu;Ld3OZ5d;lAH82HsLv!!>NVVcNNc8F=-{h&AF>L1^Y)F z17wHvg}i~|a@kGV-KtG&GOQe%h+1h8H#an(-3rWK-r>Mm%o`vUV3w-Yq<G-jYt3^U~dGz4qa`WlrP)?esu z)AHYZ9Ph2cT#sULF51YzZfv2wW<>~evBm{tzt?Z25+t{FYqJ-Typ~E&Vs2ty66V9C ztP99FmVg;>&md3)dM3@efcSs8bw(zAlV^ulX=y0D9IDWtEb{^WuVtF>Ka|<;jZos} zdnTfs*@Gb{XXYT${)s3jeULExiqYSLyWl6;aap!IkNNZyJ_?tkT1GTZjYvfk%lNFC zk1yJo&H8l>Q`;9}KVmo2Ramc)Wrro}_2kPrkH_R?A-4#8U>I1&?)QW*E{wC^0^^3-@A;d_Q17-A_U)#WB{%*8{JhzDeM>Cyc1~BK)Q` zkefe-Tj%@>H0+49;uYThGp8mXNhneTV|fIq&1|9#>-;$U7GHdnNqqn|EgRO!9AqQ2 zaZB84%RRi-5)KJK9SR&@NE`Ed*X7*OIdlE$DqttTXY4@L2B7#IC~jV-L}E>b{PC&B zGJh+mm$`JW-2h2x4sTw)*M7irH8|2~+*l#s&rW1g8$H^t9JK}+YaeFf*GD%mSaHvm+x4+3VZXMkLaUj8V>JukzAfuw^GDAg zU1;MzKFemot~~UdH)GYp9QN3=@TUs>&F-A%JWBiN-HybuLO5u^pFCQw6>BXhuLODh zZs>d^d^T-WGt|F?9T5xJWlKsSe9`^zcV2X$(8&UuAGX|qd5v$ZPe@ixr`&4D%)pr_ zTW^Rec^B?l2|kJ#`+!3Otk&Gp?08hlSO3+@jK*222U77jDC9% z6v0X{N}vf$A7*bDBf^JPp#!zUbl_o`4%|9|4pa@(fpzfz)ox8 z=FKRk(&CHLxP_I`mB=$7{ly!6YQ<0mYTYlYdkG1i>gMlxc;8xYa7~fCtms@L^N6$QclV$nE8B z@&ULCxOaptOMXVF@ZeDJWxL^Z3A+Z~X>2lH@jiG2nd0TQ2H&?1?;8?dA0Te|q@Hc3 zw-6mxc;d_NBs>V%-sJnf(W}}?pRRDvpUT|x7=hJc#cM8jm?^&D_;Y9hukc~2McmKC z_93wrywvwR@dR!7bz8!#DP<8Ehw^v`mU^3JlosQ9F=c+RMGxj*8PPKdt{+zDkfct zs@61=v{)WdvXAUv_2UZ4Ktku3*5YkyM%K;EWm@%+Z1wrarSpuf+4!Dr{$U9zc75kn z#)F}bV2pPhWQ3i6C0!nn7f_o~n7JW{s2MBVFu=G=T9!HEohiQ~t<}@WS-cbZifEf;v@>Nvlw0c8EP3>5 zbr;*|S+XxNP2=*%<&BlkGnWpogctpvw*Yk#F#{toFz8ucDxF5(>c-<|ndlYn?e1D< zR@+piaTvRiYStmG zk!u*^&IUIG8;dZ6|E!?b{KZS}m8Lcul+T601{Ckanoh{GWl0)K1!!iV z6q32J3_=fN!4c!8L4qNgGBIJJkDIY;_h0UIqzDP7v)?fYyKGD^MpVj zSq;RMAo_uVzDo6Km!6lOHaly})E1Id7I%^rVrl1JWTX>DsB)bLP7du%8~TjNJAI!p z2I22RF6@CTJXC_5MGFY$BeQ7ri9NlG^TU$k95nZrB4PoOvYe^vlIZIVNiLV=jpajG z+pIlwReY^Z)uCQ(v^+zZ zq7X-;@Nv$-FDv-LBd{paS!)@z_N+?yT>4Zlb8YwXeH4510q}E?lMi)R+kNjo(!#ay zu^htr3qQYQI5#2L+ zKqLLMJUEUR!==GpB6yA@nvr8m=SSsflv#BJ!UJ8Q7@_JJBt>DSL;s}gIJBqF{0P`b zppU=>l|5GO9J0bo!3d9)OQKh5mn_zH>1t!UbPaT#8EskMVp$;I5W;&r{KhoC^GrYR zh-~mH90^T~6c)b)Iw{k+P7PYZk0$*D*(=n@n`INS;C4bQ<4vdJQr^XLXp% zw3ZS%Rv>xhO7$cAH|CFZYW6ZU@aXYft=9PNWK_c4;1BM-yheZtH?ki!+Zvti*j=~E zc_sstZQ)4zKyOxZ)VVT7K1sTpyZwq+9!dZf2a=Ql-AME9lNs!0rbhJ=?D42Wh-#)R z1)d-1Y&Jq_()g}o#tJ3S7}7Gu+C{mOP*u3BK#ns-B-%uCmES)iCoYyXULy`IbOnT_K+-7` zd_aHxpT^^qH}ZdvNOPIbC5`cO?6sd`y$`=}c=g=Wos0}7DpSfY)@>#1(UeP_yts}I zKzAbNL+=*vL9W0kE9D9tZCVHY{tbLKV}~vI z2DVwMk&Lc?>EIK;#rn+4k&)4?L2D3Q$7*oX_fcn8seV?7g}HD6J3b*^F@j&-CzO>C zr@<;PAZy9Ag!c1@O^6eo@(0K0ubA~V;evd(GV-P?M!pxix|j0f9IUzMQ43aEDDbCF zm#fU?$99*}tSIb1^%#AR?KheokC`90tWpbVbF)u~_J&X{*1w{|@Dv~3w{jypt~Pq#jlJZ7WLq#sv!?w0)aL)ICAN_^Mnae=)+du9L9n zQ;(O+cgVE_?HF0hOpfk>jl`64YH#TY{R^^XT)C53IP%Uno+^6kY3~nZb!*tHFZ6XC zTJs;h8cT}D{M6FbHL@SQ(W2&cff%#PHGLI3A=R_aqYhllEYoAhV}*jj&jLH1_^u&v zjaomaZ$dt7I$BG1!rhMzqoQVkGZYzyU+xF=!H}w?kQ?iivH>;Tw_&olhnz`eH%bz(orZ@fi z99fB&5p&r>SvRT7zj%$heYATSFb3}XkCd6pQ+A_ZPMw>&IZ!V0>Lsd6QTXq`Z{R<@ zmmQ|5Z9vu?)y0CAABCl4ZTFdX+3kt>_5G@tgxuP2wm$|~$1l03JJ-oHCop@jnCI-a z3~1Zn)lduiYTfG9ndkn)T($yK^pF4ePw!t=jQ+>2TJjI3)q(M^_&y=aW{)I&>KxF* z1sV=kCFbfR+TZrOn6xK(cIFrRBhp@Jy(b?QK9_oRCG?~r23mpDBi86$m^;soI9{v| zZU4Zhk7O=va~0r8gV7GIwoi;4qM9lFoq&9lxzO<)caL*I$2^@^STC>R&>`B5w}5P9-suh!+MQJj|I9?-PlsKj~4rPGbeGVfbqB+%GlJ zWMi1ZW6hLLl(Y3HG>+nkzT&BN;j=;SGMT9v{K)!E|CRb>;F)(l4PNR;R(Kh?B0j+$ zvBLXc*WBTUUOl2mDbM4FUWy}y-p}78*Ed*$1pPZ52KmaJ{$1X$(YG}BtFh}Hbz%iL zpxIKpaAYYZzX!8>FLqYMt67RXB!}pMm14t=em8tbtz~J5193v1X@icKv~5$NbtO<;jajcB8LUWDA!9FC8q~k>+d0e`iN%MNnc}dywLdgpMeCR z9EAqH>8{4U>I3xM+V_t4IJ6g^N!Obip~2{X?j>YIWh@>1OVCXzJ@9A#K`u5I-lWg; zijVs(&d=b5LHWGVJ#VX-$ruOnHyrqjmYIj&lple#A2fGtGQZ){q{zK!hW!aVifOxk zO8XMMM()w|=+Vr}ngs9z|K5u~!@JopZa_p<7*{eqT5=|S2ho)k1`u&$;W8wZMOD!u zsZ2|pS%s>?FGh~ZN~N?;=pGea%7pw0c}cJ|k?e$8GE~bE+O&HwXM&(B<|OQAGRERj zxh162O73p*jNUV7s(KWumMJu>fjr9KB3$8Pd* zuK@jHcteX0nMC7zOWa22xiO|te`{u2B~2oeCIFMqU^@?7gZB<9HgemA_sxR%XV}OE z*pSt*4!)l(z^_8VQJ^_PbH|B!gE;Fl)f8H}H+1H>t06P-Qs;v2Y+A&B=LjyX8nh4b z#>i|~+DOuMx0uM1Io)*EzK6OX`muv@%V>XE;vOQ$eiq>~`K%ikn^Gd}B11Y{8CcmfiL zU6DY3$w%#={-uvXTNab7p$}=_nQuygj19>;PKW?M!3de<;c{E2BU2t7Tgxd=T({UJ zZG$wf0$K=j$^*=?O6UI}>`maKDANA%>gk>%$t0O11VX|w$t0KqWReg!3cC!GWOAWF z@j!9c;c|O8G>W^cvMv)2#cM@^3JEHTx(F%>AuACv2)F`@JCkEK5Yeoxj3l5vBru)( z_kDV1xZd~u|KT&~?&_-Qs;8c+dg?jdvcBe^@?#wbk+RunXTgzyFDsv>T>rznzTu=J0Fb-)j_#>pK1*&1M~>_XG=~ z0qCrKJwMDIkRZ0JE#CF8I{NB(8BuWgehMz>Fj8Nw%@$WHG@Q~(L?^wvm8rIDoUz*sSMfQn;{{M_l=k)n zDbBxP9lD(Q|C{>?H^={d=QZCy30l=p5#FcN8OU=@yxgc_!Ylad!0tA^=l)$mh{I>` zHTiT3e6QuZjR8+9NMk;GjK{iOSXv0~(>1&&NFfy76Sm?jtp%Jj=}TXM^AwC|cvxO= z;IMoTI8SrIfqK9g=eXBVw3lYE0_e0Dh!&G;GDMJgqH zAxdv=?V)FRDVb*(9guxiYfrV1?v-#^!juQ`(A9eqI1*1nM#VAdrXkRW;AkFpow{lr zr}ZHme1N^sztqpfjw=1JPZ@JrxS6hVHLS(2WB)DAN4=nbXg>VhN3nUREWtev{)42e zXvVo}Xnuh|7onVo{k&rC8tx@&fJyuP9;N=bIl=?`&y^4Pbnr#7U0FZ7eVc}~pf3LO zE6@eYRqKPiHLRu_IB(E#n+DplqanDK_95=lx~sU9kql+qhDH@C(kYFvy{WV0ngGmDZVpQPp+>a38o(2CL*#CcPE`)cp9sp3b9mq&6;)C zwP3prXqy$@rT14FKvc^%2;MK%fU8Qi0o=@2NIEQdUt>(-FgF*u{rUf-`b&1W*Qnzv zVW-5a!1Fu~X}*4E7A#>?VRd;3dCT0i;@VxQi2ibJ)-AAj|BLodIhi)@72zYn8>-gt z_MxZK9;z0#BJg6w-~ zfWd#x<+fhe51nFt{IcBY@$Yi%nqL0ny3`)53}K17qG8|T@?H;lxSKOXk)1BOYdy5) zJ&YgiGbBqP`LD)CmdTI}s_&N1rBNIFD~*a_rO{tmIW~c7%F>p)o1ctn@Jn=+_6DeC z@N%^YN}s2+2I=27HBYpRkojpR!TEUA`6m93shESmDT)Nrzq$(7tga?R>4>tFkccpSMZXL7f?<>xd#{+%;gLC_k@2E^ZvZnVe+C>z6H~bH5lJ&4ksIZNawkr3L>5rTs znE>%T`g{f8JTGzw=ywH%w+s=NV8{pE4y7i`u27|>tbX+l-o_t;cSr{Bkg$~)V~lsE z*(O1!n`$zmrkQRU2Y+o5Qmn$y3gSeyu<~O3E)HH(n6#>&`w->L+vOs2tZ7_W?(sQvlM)Ym*guwe{ zn(ZKL8%If{{c9mz?e?kq>On`W!&zt%WQxihNL*cm-PzPLM1mfPZ&fi=P_?fuHxk z<2@JjiAGTcZV$>jsYm|3Q+3U58}Q?{`qWvLH5;;ovOuezaK+@+xx1d-tf_6CxFT$m z+8fU$VVpnci$t`UTIXd5U5E2Q&#$T^4MwBa*WCXOV*M2vUx#0cD(kE6SL9;cr{R9R z+h1>qQm+&BYTCl84+Ew*+u}x#lHOG4EEh7^^L8At(}D)pcz0%AWgn zQBSsdpc$%#mZ;$tNUxdn4)h<6DAeA{p5k5pbPnm8g6Wf{BYi5B3Zb_pN~vc9Z((gS zx!E<*<9|ctNS{KfZ}NZK^u3t8Q}B&7z2}Bqixk`mwr`VmSL+D5ZQ2O=`-x$jDiz7b zJKlG>cv#ktk~HXZ%!&UA$?53DMUNjAIR66{qx)=uXFqJB`p9o4BB(R6^Erf*J?9?u z-GG!>q~zAa4+d~98|Tqk3JwL^Ga6F(!d9Z2b+t@V_XTO;8h8UFTKJJ`Fz!A$_X{}F_PP6AU^x;%Y(T%C706QF_iW^dK_kpSx$Wn&Q(f} zk&KbES46rg%KYr*kdGs-XyR2h5k)$C`3VojAD?|q>1x5FwBMM>q!)Wpm-$Zl^@(@N z@9OWA-x_nLycrr{6F@D*V;vc1I3y<->3i^eKX3Lt<;$yc_hXJQT_T+^5E}rpd0oA( zR98|dzirkP@t^~@!4BE)`8=jv_7%wtws$({a+5B@QOmyug`!VU%aK89`+-28sBC$CQ*Fep^>Rdq2CW#^sfjSi+*hz36?oUY z0s;0Z<@+DQ>R%3Dx!R;r(i*g~$34+@0k%F1z;mSf7 zbvsvSiX=n=rI_v)(B2H#23$aU#|GNF>bh|;)IU-iKGXF0YOBtzO|p*}DgB5OgT4_S zj&O>hpT9241yjr~K0%4pV2b+1cM&$^1lOzkud9y(1V`t6UU4Okbv8Dw_+w$0^}N3P7vGnt19y-7;@f*o z9lDw1gMsD+b_n#hZa`b~i?3z?Ps$8=3+?;~-yCzoKSv(JmB#5+_z6_Mzs@>liyHQv zMoq0XuW8uVHXQDqqO%nAHivs-K;Mmz_!K@6ve`J9bLeI6;WhB}R#1@M9Q#qd)zhr@ zj@NVE=^E$^-pgd}fv2s#Zf^T3>x=T8=IG|5Tf5(mE#G1#TRVR_^x?Me z<*l`=pg%mqHr}=m=b)+@oQOicw$Yi0p0B=EL+vTtYlxUB3t<-ul3srSx{=9EpE5oHr`_zZ|x zjAgODIwlu z4qBFJfXd|O@pdYpe76ccO?Xkh%TQ^(y{Ug}m9?pP-`H^PU6iiWL-5N$&ff-d-jAFG zO_#rWBN2T?{rj_XRU8;@N%BClAw*WBN z+w=zo5-YG@z5tz;?S7mqdriT)7pDUv0jT^?@C0%c@>K;#Fy<;g+?%HgMNG_Ko+{K*YC8daR-|OS zC>I*5pi?x{7>%ArufGdrZ$tlfG>2ffLOnDNO5K}LF3$!%SmSZI%;+zBn^JbRG2B~* zGMOlQ7p^Zf>y)y$T`s!`Wx4H7TeF(~Y8{469&iV92DqbO$37ZMWzKvg7uAg-419?P z9=(FRzu!f@eb)1wb^4`wyTU*G186BN54kI5Q}uhHNkXKj)eMTT=bz8pt=XQYh<$`hDwslsr7B-?dj6r${xe5+7isCSP79?C8;_ z);#ohR>Wfko@U|7I#G;uqO1meT3#@zId1!EYnw7sWsLKdX#ls&@0Y;$_zhS?jwox$ zuo>g5yjzR1v)~toNfYE*4SY3h#)}g^dApytZh#&^boRKa82f1O^#CLGbb$+PeQ59n z>$`2#mQOrbZP;pSD^eGr2X6vDgZ$d^`h)0atc|mc+#$lsUxHs|5phJ}Ny6s!336_& z15#J7Zwa)Znb8Va2Q)8X)uWMiHyU$^!xCA_k`KwkjAmI#JR}=tL?^-5XB1LQTB8?y zfh2g3q`OaG@wIws7-})>x0u5v23iL@O*ykT5&!mO$RzaAc$GGNWm`F-C$WytykU`< z&@YUTbogi1n9Vs;&ADZg5K;O#pK*hWbaS=*qNMh zBjP8owN9 z;wM%?Gb*oB4~p&^IWFfL*`WSL9&uMWe8D?YnozqX2jA;qIrv_WGv}y>rF;oVJ5koG zk>G+jN0-l7w~xUoONrg)=b*>KA3`L*`a#fBrCbuCN2aWo|6&NKJ!vm5jJMy1zwl)S z_+rnoUGT>I_-m5$C$0kC(VEK9=|1t~W>=`^Z+-sUM^9av>+& zD0>%|ogz8^pvyqt??MT7#%=nuZmK3jtLTl+(cG=MQxnN_=}{-pXJJ~AUb;9G&}&-F zC*54i-3XhDl=*)?e}bi-cjpv;F88D)iOX4Z?S^Mn{e1QhVwLs2=E;cFa!8&y;Rf3w z+5BMSKPTO)_CBVG{0HbP{ZujA{)JqAz$E6?Mt-ICt`pW^hg|ymcjZk7eriZ8eMNrS z;D_C2z|E*Yhv1}_WO$v)ZJ79Qokx|_;Ng-!MqI0UPwMY?dI+{>m9tlXdvh>&I*uxB zmDjO(kf<_iN53jF&K3@?!aw07R`H@YT zhjMxX-{!t?;6uU_sXu-jmyz{t`Ti!%>lw9>5@vEHy05gy*k=RF$_uGurd zyQ9}Nc0c@5NJ)%nl2lx~Hp(uvuIY|aQVshNy-I5ByYg(j9rYpYE2_h$muhiVGsE^@ z*ohc|DSoffssi2tKkTmKvfw(p8{c`slC&!D9jo7Tazqr15@ zoE_VE2foDdT^0PMrA*So&zrhfU8E^_()2T8;0Z-fCjRU*;p{Wwq@V6>0S%(L9NWWk z;kPkA`^FCA8fCQo?28zt_rbiQOIF7p>Mo4oGRo`I+sHxGUv~CJ7HOpPz6eATh;?fuQ(uZhU!01wZaS#4uy$_3M+=i3I!Gyv>=&OAX%8mj~flDEbu%CpcMX)r7Xl)wl znT9gSnb$1qlkuC41ize)vC&+F_J~lxPo{@xCP8MRbIxdJdyAETp1Cg&47p_eM7mZQ*fZZ zzgkk(Ucp_p(6yGv)gr)e4>w8gBIr0L{`G(0Zti|(GRE8zeDn2LgDHr&q{$LrdNAHx zf3UkST2I*X!j-S6x~vM`Ze9utE@g%oT%2@(v!n&vZ?frVj&KwM2RkY7C9d%G%Y7j{ zKXleGVwRi*K7<**1_?&)?|DYJ#HAuaw@rsgEjPGWYNmT!Y4y^%+OaOferSFG17fUg zgaI#H!R9&3m~?wZR``%Db}b)=xd?A)TDKpo#!Mkia4ut-D_j~tl&!diT*b<#)Q!G9 za}|c9d@q0PM45a0lT-Ih02fHtY;5t^q7wTt`wMo!0epk)C&`CZ0yM((%YXKr z9~g}@yZ(`%Ve3!rR)IFrnY6Y&9+sGBjF2h^4}-e$rwfQ(AXUJ+nLKM=umY0bz-t}u z0Lez2;SXbs)zD*R|6v__aQ_p2VSFbG!ld5`iYc)k)#Ox7@&imelKBEq6 zLo`;%xY@vuGo+Ugek;X~_{FCOhdiu3bYhrmr>HAxcl>8JG*OEZVJq>AZz+5b7IAA1+L6^0(s5# zYit(rTX>2P9N)@@Wd&xwoe5f``rB~;vD&xeiRG0}zfh)a6gULrDMRV_v`+q@q&vT%_( zT(Y2EYxTBm(B{Lolkz+){hEQRgs&S6&*EvN^=0dS-RAw_)J7KLKuZ3*)@ZkX{7OU> zzG$@{6I-5An+Kh@BI6V!o(r@oU}Ijjs}De-|bPn`3l zlUdA89hX>Pv6#vv7Q!X#FFOBFr4#y87eW!;^yfJfYKuEnAuOqLR_7ccdsc?zCf!kB z0H{a{bh)ECLn1>VbrH-7Pmy-_YUlXc?_jfIvF$CS*pyr35Kw^+xrpYOK+#U8zRd_1 z+R!FT)&`v#U-EyOU35OV8PA+I6r9xiWWIcIZHdg=i#xdxJQ?WgKf8f*+26^A@^}ki z?NxP#sJKooAD?%VLT4Vuxd}Y?pV-Ps3m^&^ryrtWeM0vE;P|(&BAP8vS)V6QvGZMr zIr6kO+9h1v{wV4+&jP<4qsk7!rXjS|->2$^?_`~M&X_oBqMN%V-cl-BZ2#GPq>!5u z?A_O5}|9csSG7DEGLB;VNVjyox1N$nQPy#d6EHo_O0y@_Smw zJMI)CZIOKyy))p;W|MeSTw{HvhU@xL!!Bf0+%De?sfI<0sxXK%aIZz%Xsp(Y<#t2% z(YyermFcOkm$4_!F&p|U`mKvaZiD-*%KA1qLa`NG)kwnoELERY`WJ9VwLVnh^ePK< zJPueS*{Su;g7>*~I!)KJVR)}@mn~n|V0U#leyXakWQ+PAa=vM?82uDWe*&^hE@DJy zM5G1&pg!ng%xK|KA9JBbhO$4x$GW?$^GKDo3a7rv6y3QZi;k@5!oAKW{RrOt8N#|Tyn@aoz4M9T{W$xS@s zdXB)Zj+Mqp9@0wPTy?vcC*QnPtMGJK#-pyI;<0W^=sWOL_esONF#^8F(vP{`D0r^% zXdHd-E&RUlu(X*jRrNoBQOxKsYm!#xDeaL~&fwDi?tWC!eHlhjl=B{?pVCV7^f`AV zJjDw!8mSmD3xiNvES^GI^iwN5Xer8X_hdL|gh)eO>+{d^X*AEVC;PFIaLzx|-)g@% zgywmb#gds^nkOe3H+e|w2L2xgYdrRXb&Wmw^!-wBsGe(tW5)Q9xW{SM=betntT&PM zY9#xMXNxtgDItNI`Yibw3A|(J(|c~+<%DIx%AUXMx`3Rq%t3y!OTvpTVRxL~t1tT5 zH?3D!bO}4;jlCh@_^V6K;!RdBUvw5}$-T6ew1Fma;z#?Wu3gRc;+|-ZHMXZ8Km0hT zg|%0x1?}VOWzKmiVS;71^@YZo-3Hq&aY>e`Ht>p#YXiH!b=-#CVl9{TVw}EkG3NcBx(e`lcLQnP6-x9^=VUT7k#5rg)`Odpg81=L7 zIe8A9|In5LfEB^)1zbsv7GJ7$ylK6`9czOu3SRXK;0iDf9!!U`@yp6ZTmL&mF;^@Y z{W5qHqm;^_X|!>duF69w84znY>`A~uPu`-A!(FOsZpvh`yJCGEv+#q(r^5 zJ2;lXvbWsD5GRgoy7#*1d#^#Ce3u6`IfFGxh&}j?Cs;1D6eaICBs&uidvuWCgn;Vf zW6t}U5j7BZQRjV!hl685cziWHuEj1Bco{4zs*cq&BeVfy!4ct*!UexHru2cnilIt5 zi&e^*hDume&xoJa$#D^`_&Cn2?_~N_a!$u;IqLxVw+NOhFRYXck=u5lQZ8bM=&Cn& z>h)o+`{KAvon@6=f)a&HQ-m^IVbU8oZOyj(JbpZ;c9#<#fd(XB-?5P7JwKcph#8-2 z>GAz#ANlek8*TD4omv-ZbHFR^g<7oj3lUMFgR9u3FK=*A zcRn#1u`;Ai=z*Ri%X~&2xmB)_M!J$-g|t5u{>j5i;!Nj#lZHcz;u6pM#t-Wh-oR+2 znZO$ujkH+s1}xwDaom7wTNjqOV2~jZb(5~ za-0WfE#p@}lRhKQy-nmZrkt2i#!W!&Mf#2wM77!`8dnHYg-jvq@L4V<$^KIVpOFM9 z+sE)?XWoY#=Fy^7%I}-{ICm4p8wQ1wf?p1`$Aq;#CXci`k09mO<^O`7p<247e_aE8 z>Dx9vVII!*{qV?0s~EM6u4-vQ|6Y$;d3yqH6>nWwV~HVvqy5q-REZ*5eG>J`p3Do@Ec@S?L2;#Ur4$_ zYH6rfM9;zziG)s2zZ&q@tJ+R6JXCj6?qOdER<&O|G@i%3q-!MSA-Cl1HLZZ&P{ywz z<90e3LB3e%Fs)DR;`m($ z@uNsFtyl-ok2EeP5cN(iJ%!U+i)*Xs=R}hZf*<1YYPGbi?=ugc zx%XOlkylw~!ZU!^-}^6itNUXXune#Ji!Xrv58&o{noCwtKld2tD(2-hhKD& zdZP+)Xb5U5`1%`R01SVxM(kUH-!eqACHTD-@LsEeXHeBmpgESqk0_u=F-Qr11>HPb z_&W|Tln`%d4PsWVs>!hLw3{5%a)*uEL3W<^j^kc7)v+x*#0*h4CAMR4{GGDyTsf$- zavST{XRGov>^tm8`+7|Q=N-1}i!&=yuDbW9-$(l1If)347yAVMEHk!TeleOXYZQ?vT$(?fdzh&=- zUp5{oo57Dp)J()sz2I6Tp7U(QD!x?=6BpPktQXwd?2-aEiuw=^%4<4S&{YF^@D)~q zJ24&W=pzH6#hMar(QokP5AbMs-uLK$s^~X(vv6PlYven@HTIO6b>RLSe!2DeqREj_ zYd{nSwe&6K2Q;J5D$?s$BleaKyw5PVuGk1WO|>*5u@!qVpyl*oA={KbPUEg&m!_GIBC2l)@JL>8)>b@@MmMV>Kggv z7TmFwW8D6_;g3=HXDijML)|RH<^H1ElJ`i+T1dpc=w=0vxwy1B@KwHcIJ;H_FYZF} z9%T=7dB%X&;CAAy62kAGFAJ6M7q%Ivr1i(;5$n~`%)v~+^e1;L^j4D)X-UZDcaW5s z^755xY1&{K-nY3^(c10q*WoRSdXq4`*ZP(#xj4BfrQ{sOcWu9^=p07&PyOoTKY3mU z^k0-CSg>`aHvhNb8`tup96n1ewGTFXw8`@!BOR|HpJiUlQSF0iV~o ziic&^4M{!!nV;7wn35!|Dkkr9nf>ZPe;IH-y_O@|BAmpw&N$stLG%U2{e!@mI&oz@ zo0S(*Uax_FJsv*eN8EO=nAT9fKSsF&AJN66@r+B+i)$^4Y4RyJ4?P?KkAmmn+gMvP5B=ofPw;E3S6jjRZo_pit_SeVt8rSYw}|Q$f`jnt znHpPX!ALd{`moO37uom%@DFxhbVgz<@^5t2c@I2%3;wc|eoq4M4{YXlaXW=dX5uUN zA@a^wmT%#cZnt;q$|x7`O6~WDhjn#eGDfEarvlQ7QcH6Yvx(O6B+Pi%y3OR)IqT2i4H$$LC6+@t@TtZf3agZ z<-S8UncKCWw*Wf?_?T9{_xhj#--jG$W4*uZrQt_UTAH*6Tf<6zP}YZ!JtXM>{tpSr z*$J!+V;|zpTDBuFW&*~I#tQSW?3o(ycANzwU#as7yax8uqin(}>Pl);oQ?LLFbUq< zy<@<^DY9X8lKw8I!V}Fsy=-!h3;S%17CIX&C87iLA|ym~mUvg@a`aOy``Y9w=D7vw z*u{*lyAV^#_|yV1@yR6dmPRMmjRW0IoWHsC{3f=Z;-tL;4YzL*RaMx8wn$~Y>P=x% zuFO}**#f6>lh-3`(n;1{%Dn<{ngrMu6Ato^>l|E$)}_jbbd}bImgX%OV-Azz_v!+7 zv3s?FJHuXe;4W-0YvfQC^5HP5Lxrmue8SZVpO82H;`?A=fq2Tn?O^b-#Yu0Xb>Zz? zb-FnE35p~~+K1|Pzg*aCVG_TWhg6JoDdF2yT;*V19jAjn0go{tUPC1MO7tsz%dqDw zZN8{-s9Qeowt{mWftK^i>Oq`ClUODlw;z&|)lo#Z22#E*r<98x#A-rxSkRQ`&5V73HlO!6|^Va8G)C-mw&{VaTqfueSzT~4(iPEF%6ZJwy7vEaD0FqbM#<4(Z)Bl7|A6goH2*`~aZ(!PE) zm{wk2Ac9g7F*iAkMgc}+(Rts%09Io2irP$kM<P}NMR~0G z6GVu zGsq@0=_My3e^(rr4K+Eo;*KiqZ7oSpt?pDYSb4#3mY_1^6}Z8N3!5*V0zJ1q&R$T` z)~=n^x^_4D$e9SsE;eblD;+)xLtp+xMg*u0(8ORbTM>nll{c@3=T7YGsyjIC`?JuS z)P76B5?Dt#tylrjlIP%eYKdD_ovWLahc$R;_2z_(3T@2mF}RLzk3$@=hKJ zpOG|Ius&lA<1%zu2clhbYr{&z7I5G*g-Uz(hg`njy>Ue%OZeJc)QI$9-SODq9#d&>VndL9fAUf?h>j1MDD7>2{Ny z+D-z3I*?KL-sk}ABJ?`U2Vub=X-Fd=niHS zBA781*K%C#-}~wCDY#1YrI6>Y#yM56#hj>USO&Y!y&9o|ZHE2~3z2ReW{^Opp|@Bo zu;*QH(O5I@Ozkook6ay&5NT>44`J4R?U#V$;77F)r zs{2k6Q71v2B~?`7OvI$yEB-Cg8gy&LD2dm`xx)}Wc;w?`*BS1e81WIVbWc~qjfk#b z{suoL#aG0^ch3h--Hn2>f5s{^QuW#vS!IOI3s1f?-*XE1tE)g{ZtPRhQm(36p4hR$ zcE-I0d|-ZqDxJKjap~XT8GcJ74W~)NDIx7&?&ESC?3aWXL_(dluH<_;dBbrz`M@6; z))XB^_&3cqbq73C>-Ks>;34d!v!XOxPGvRym%N((D31I|JkYnUFSH{eVR?bjw4!X~ zy@zVwur=Gb%V7Ht?yT~W5uA?0%;wIUi!qTxmb8nwyNw3JgmigVQ zgtfS`sOqT!=jJNgXr# zA*sO8q^k78lB|n3)BPSE8p;LcA8emsG}PV^A@Ae*qC8VMhaZ<`SfXFUnCXsUglNpv z-c;zl(Ou*}Xymf(Xq%ZhS!kP?t5#)v>Gs$7De^?WruI6ukzZ1M-y%2F7xvLqd*nB2 z??xf=aO5|UhqFafpbnK%$KMq1o>fC!HTg(QIKe@_^Exc8dd&}a&){>D=sOINah)ti z>KR_|IWFtgpFe}KxB1Qpn@`$qLyCUu@EIzxu$w7$=E7?@`i2ti!_>xqyB-V1+PttY z=A7?~0Yn15urKPI@9zW6wl{2j@N0D#^NAJx#7*o_vm>})bJaY{tzR!+G;Q z!rY>H6(WUTjvLxH;$1=m>B?2>GAfoR-VYDUk~s}@{LDcP=Wzkr59in#xd-Ql1gyBk zuZ7PM+dA#jn7h+kADp=n?^>j61*}=Dy5$L zwDVioq!aD}r42m%FVkB=X+yBY=le6B6p@XuAZi5NG;O9$_{W z@$V1&3WkXzX`ET*F^cDWNyF<<152xPf9vtLEe&v{HLiVt(!Ye)E6Nu!%qFeuS-$Hv z55evD0Hl0qF^3kNd`%0Fci~N2(;NK6C1bERbP+-9_ity$Iqfos;hi|}AT2i3TlKA)4 zQc3?f?7-PXSE&7?Fny3lbFMb2Mc5owORau%fZsg2*5A9O1A-|P9u|Adq_^kq%S>~K z^wVH@gQ^OUL@X$wa)XXbqi-E??NOlc{7@TqtT6R)X#b|-1ZLhZRDTkn?O#TFxD5D* zun8{GN7BI>`lUv<{1Y&ucL4l8J*A%WIXi{@?CC*7(W?|1P@oM|m~_O%!d@(f0KxC}S6v)@I^TKS@7EyiY= zV}(e#L~!KN!Nu{Wa`9puuK1c%Cr<>VKXwyzRY`NTfT6LVHMQhzL@CCOYckF|ZND8h zEa!Z=Xwz3G{iE^tfPeN?EA0|8UUSoV96W2JqebSA1MQ7*f#PdSwZ-85uy1PM?RL+e zzBN6(X<-QEMe8kZB0u#j0`ssH?Ocobp_WDuh*bTjj!u7V~Q(;K(v$Q0d3ds_)Xi!VtfMW_XD7F>;@SeJGDPEwgH!`hD>7 zF>A(vga0Q$>^sPq1@l=TRY@Ed+`)b(HJR#nay zxtBqMEytdWxKFD^Zbt&*x`TU^8rg1KA=b`!aJjR`MfW)`a2=IatqV2`*K@GC27qmID3s0_d}hyU~WPeXZ|dMI@ab)5s=pDPMQbm`Jt_3tgn4 zo$q?B__d-<&|0M4Y>;1p*5cAf&iPi$qbL2VjoUU`PLVtj0^4UmQ*j*~>AzAM-Jc4- z_D6Z>U4P)GMjrY_;#Z40Ojnhmd&(Dl4?e0uyyE|WS6tWn7mqMMYDkXjb~D+O|8T&{()wAHc3d%1@3e?p1|G0oZwqA=TsnV zX%1PvPzgg$bKuUN^Nr^LKoHWF0?Z?|O@S7Z-h`#v;Lt&NX@OZ*-A~@u-mRm2Pblx9 zYbK3XqYr=l*>XvHwl(iI?9+OEpZX&7{O($0d(d_h_BZ$!3yjWW&p}zdT+<0WExi2H zgeUvur>~SJ`VwkX?{%ZSH=V&ZK5m7MnzVK1?>Y|3&XMdK^Y9q4dMY4uN?6C^Ta;Qz!-ZBkclk1g z{d2R+H)iDG^sEvuzF z@r6EDhJ!)>=RW&8%9m+heJ}9kB!vez?E7xe?_n=rd`~&CQ|mTf*SfEfCs~9ikBc8X zdM9)NS$fP#E?WQv0}3*EvH0?00nuqy;H859lGMZ}$}vWI?{&L8w}*@XeH8Cj!QzZ& zog4Efrd3}IUwN^K&~a?io@j-Jt3{RP+TB9X&vaY90gsGk*g4oR6b;jQ1Fg-*0B?T= zpN6P$vxpD-=eCXVxC5VJebF_})zK``HQu3P8M&@1>pg9SkRfbcyjJ40*3K`pjB=Eb zL;H;)X*06)QSEF}xy$blU{(mrxX;Z}m%6yrM}sY0-fa0^yQI}x1TB9HA_L@PKS6q| zoYw@+mdrBXv(? zV^^yKZ%HjZ4GN;qWiWAwIG@TJpxU1US20t>ffi~-M-tn-hqzCt+g4fNKQ+{cT zWn;^*uX>2Ez8N~5GC|w&Ur(0(Y3yG~pdH6KF$ceZ9n@OPvp-nH4FUN3x%5r}T15iQ z3wV8USirewA~c3xfM!(0Ujv#^W0rnh`|Z+q8ie#iXJeud$Wvku$hj%%Ht4AZbkq#{ zT{6oepWO#=CH*_F8}L`p&U<)AoM^1&?5NpDy5EEY^gYrLjuT_VazqJ&tb4}N+Lp!1 zkL%0HuFL?AwIM3rPO=4me)P}m^T6M|BTpP5julNy6Y2z5h3TcVL9}<(6}`OEOJ~ne*Sn$R9-VP$pOnu0tb-GaEE8|8Vx zOba3*Y?S94*V$CkFh|eNJGHhsV>sAh-tsMEOSX-&-FW!yNZ7bR+a>YvS;NzZ&qik- zK0AWjP`ewxIkIdo+rTr2gf&`f9R7P(hz5HL{~K4!*^!B%dFk#W`Q$};%GxjF+(lo& z#;jE~B%;5W+yv?|dW#sA=X{g* zLbzz@$zjq6ovU~;68O@qye$=UYjfD7PuiD+L>8;D>v2hz>>begHW_PxE8v|4WUJs> zWOw-%fq&8En?FF>hWj$Re6wNK5VY|l9XGOJ*n>TTe&R@4mvLD_x^>Zc$V>gUpNNH$1W8b$%6Q(_ zk2v4weMg2Ci&0{X<8wJVXZBO;pNf8p&m+n6(wvD7len{XiI`cEAZ8YmzPb^VEY*I0 zzezl`z$i{CW=VuWcjEbwyk*uyBB)=|O5{@zYyL2-Pix_aF+Y~WeNoQa_7>R)t@D0R z>xdYW$st0Jb2-wYez22WLW?}-@>`aZY8zRM9S=K7%HGKiExbzeQd@sC_M1`e+!c^V zsDz8_AK|6L{kbc+8?=3p0ZM!Ob*P!;?53}#&{v9b{$_6i{OLqHLKVv1A1ScJ(E|H* zoPTf9)rSG1??7j?=D_YQy^3=iZ1A>{p4zAbgg-DMUgzRNu-c_uTMfu%9-SX8Ifgh$ z6V2Guh_62Zd)m!zn(+yM4*N`jJbt<7uy1bP0(^zq#!@KG`C9g)C#pEQD7wUQ=~lkW zcXL1DhF`js>++@dM<7Z%ENCvxMEWF1^Kpmtas46iw4y=!9Y{}vWeo0+9^G&J!H;i* zcLMHn@iWxtEs1yBigw*Wjp@LOwq{*m^`%&7P4b*5mNwNrS3IHRTbdLcnc@yw z!X;wm2$NR#Q_h9S{=BDa9p5Qm>TUYG6f?u0amn|mL z(zvtq-?;0kOJmG=i-43aU@c=8z}VyqY>?&KkWCXvvoLVDF*C) zipvNA?}FqQhvXWT=&+06zYLx6EFaVDiYzshOcW<{p7|rK+$`}Q{R|e<%=DG-Sid-h zI3cu?(28w@Eo^Gbe}fD^{jCZ{TcqDsJfWf|TIDR#LyEsZ-A#96 zFEK&7Yg25i{Z{;Q!5LP(_aWY$0lC}VU!XSBn3&{%p6S+;vc*6r>VH*$=abD>YAXek z@G`WG%+#ZB#A!`HuZkg`19!%BOtzSr={b3xxeB{AgS@4+;yF+Ww31-I4aH9KmeEFg zNtbWz5b@%Vp5=BNJ;)nASK%53f5VPC}$H$qE7*~O_f7U_u!S^X4CppxK$Z4S7lh4q3yMitCLlM0U0sZhY zwK{k;DQnUK;4`ZURd)a7a+Pd1oP^)Lqw($Xw4CQ;`})!O#RaT8t%8{VbMUt>!#mJR zYS*>!#ymDufj$D%4OD8D;TUQ>CeOq#ANRA&WY6)_N0?Vv(RHXDdj*qHE7}ztVp10( z-mom9^1i|~NY8zBh9!Z1Nt_`PUh?qM&DM_=OBTf`BS|y>`WlA$u~P>foK3@m=_stG z-Z?{bR5C^?IdMMrD7WlP<}0b)K?u6Anre80HuJ7?Rsz%CaL&Tt|2kDzjaGILt|%?O zZ1GI%MkaQHz^WL4`Q$G^1U%&a`|AP%LU zp>hWE=YX33{S{0kyfyXp{Yb0b%7LBmE1=_d+N(S&(~lm4nSh;IFqs?);$O~C3vWSP z7*Df7;T?Z4*mUMv2>I7b-Cz(B=YCv({cP@Hcv8<%Cvs?SZfx^>cv`^h104kq5QZpv zY68||!wK4XT(z}ClPGahjr4cv5+0Nc z`QHBwXY~v1e1O^+@?9FF_pi#!%y|uzi&Jv#M6Pouzk@^(G2Nv<%G~Csu6VkING{e)HfzL zh#R}jVXCEFb!R`x?-tm}Qcx%TW1t6gR4mR&w1ec5kFydTxPD(P==5zE(iC<2UK--* zd0r=MRQG@`-&*IVlX`HUTd#HqwzP#L8_`Ky5%py)+Lz+M9#~r??yjv`*CfQW#G#cF z`!mqWu`7aYeBTlU?UZX<==VI{<-2bmY?3awq73m5I(;)Qx8m1`e{g*}uy4%}9EIZB z%S;cg0iT7X7rA0;t8m7OxBcd?^Om*9_d8$3(@5LAeZ>2`#}#TTt<_0(OAU5z!KJ&g zMSjc)s&@i=`Mn2O`ZU+u%OIz9vh+Bd(zp$L1Dn9$A2ip-zp9p2$tj@2Cy@*cd?10i z)re{HL}QQ=h<1nC@@)J*sw=&2+p?}&?8M?JPr|;@ki`V^&ui4(NMFpd86U>^PfmRJ z^s@QOPJ56y^&cK3SB2@Jo%EdB=y~r!_}Orh)RD{G>k0Ph4fJW8E!0MR`qK5PqR)e~ z?&C50^p3Y5V(F7yr9Q+sr>ds+m@v~#4_68p~U{s!_sp2 zCLnL|eV=Vx#xjn(|Ltix8)MRCaZ?ZDYHBcc=wNRWV>(@XyU0ytX+BrtgE8XQ$Q2c% z%cNXUF{Xyl4qYbQW!Dx_u2i3E>!L7m7pzaWE&2d|O%K+Qy=h^-sCN8*$*LN72vD^7 zhJ4ZB(cFtugkPI)2y!l#dVBj@ONz$a?moSEQ(@~_Rh|GJ99yijn{-o%uXu9@ECuu< z;CXn8teVD4W|cYJb(X6lD`U|G50okaG{P$9N$s7Dc|A2_R`cj!AJ;n?-diU2 zIHMQ1V2Nxdj^&8P;!d77b%yYy6GFBCBoQ*S*fh?WK6ZB)A0uO#pj(L zue(`Y1TE3Ephr#6;|YOv?}3Uhy9;7;pf~qbWQ%_*ejfT%f3NVb3n5;8M5XOBXnllx zb&B`&H*o&~es7dk7dWM{6|dtO*R%zHW0k+`f}^4uPwc|_iYnz=S+QBUK3B1+cvI=- zlILOR8e{uJtF6>RKbU!cat8Q%hzS2>H;Z{uPDHyAcTo#^Ge_HUUkE%PR&c<#J1gKz zZGfKd#j}j_95k~7+ST6H)y4ieQDhH5eJ2V!r6R-jmlD|;$~nnY#StXg?|?q5!k0pki%-F5zzA<_Qedoz0_K} zU@2x2G*<2vbCbNjed*#Fr+xZhvk`Dv9c|q)omH*kHVigMSq7OzyphZk`3#A%~q}%$LLHq{w*zg z$;Fd;4_l8b@z`5Uejd(b$JFa~TaEOG;i*_P60U*ttm}5$R9mO--QgEK>80l-g^+S&Ii`W%j~ps&BeWcU(T`#R> z{gxBjpIbk%0akf@*K@kmQmkV)#8~nqJuEEQuP`_JG&Z4wNi}_|6Eqp)+f3rzds)UM z7x{K)DL9o3k?)5w%D0ZXu~IBI^@Vn~YY)Ni1G`~FDb*z?bty6To}qGpv04S)VJi6p zN$awy%t`H!E!#a>X?$p~mNtqBTDluJd9}Qb)dYP*BqI0~a={Lo0a6DF9AMN6}rOn%LRdcsD-CTS+eo>c7 z^}O{_sAC5!u(HmqZ5`(5mnEZbDuxczILw&H>k@rUxeXsa`RHB*qOM?#Zmo}8b0{0pWIMb>puLoJ+<0s1d34(h(tmt(Q zjrrBMPiqC?0`;Bn!f;g|Bd+UXc$0Zg=;dB=%DBLzx(M%e@#TeT+|iXaUSB`rO^FbV zC~Hp`;Z9DRvi8&|V{P7}Ed{r^V5@b<46e!Gb(WhUx4ga{L%nV$XIfyV`SfA0rbGjr z*I_oYOq!JkWoHzm1Q!@cO3fkiLik^3wagZM455dfOGJs)BdwK$;(=B59>!nxKBa3lwzIqn9QVO!~FJR zzI%p};7f2rzq2l}l&j>B^7kQeAMn$RUncngfX~5NSPg+EF}gUu=$Nl(5Roz(U?D$S zwOv*DFP~FB(7>5Q+l!$5{^ff_Ue_=Tzqr{Bqiw96N)7v7ANH5Wu6bGWYS_1d^a2Do z_L6t*WX91qfo6g4u;Of-4=}c;a5dN8ShTk0m@jkim@gIfpW844j4<33fp9LMAgF_AYSuaoFb*JMUdq&J1ltB|H`FXG(?P5( ztt@OatzFA%+cFiE-Pkno)_vPE2)mlL;&l|d3>TdPgUs*!IWvRQxBY*A|JM&*XU;j# zc`l#l{yd-O^Lc-8FNW3y)@e>Qm3^NB&r_Izyn$yj{E3hWW-O1%u{aS6w7p>6 z!{>Rlj~VralW^~qZa4JNPD9&~NhSGcO8>vnTUkeOuHA!QwCZVns#=o7$t@n#uEc7S zaX;Bh?; zrrFnfuYv7mV7*+d1;P*4JV!beS@3p=)qZ-pK6r#qbEz%-H7D9Z~A3}i;6~3U~5X<0z?^V9o zqPT$XH(ejB;N3TPN5+1qEMcvygA14Oq0{3Ep3(VR??&o}7)$?K`E5r#aHH+{Jo6wk zlBGwkbQ5uV`&0blq!DvvUAHGcHa(pLp59WsL}A& z^6ihV*26Dz?$LGF_pSloR{z*KNe3Tf!&a|xXa(Im$iAAeH#KP2NyC|S(g@8uDIM1+ zBb}$Z>*kRqhCQ^;eY$#Xkz)NY@RCE&bCueE)((DLM;wRUDUtp}_ubGyS}F}oV3TQvFg#Mf1IQ|- z>t$wRH>J+F2Ujzl`YO|P-b`=mYSZ;mcrv*)Rn&f?3;1l5iLu%9Y45*X+3R;)bIf8V zt1?cU^xKGk?KXjV23I+*>Iwhzx*P5vq0aZ?I)uw;pZp8lo@82zlIQ+ka`{aN5{{NNcU!idbWQhKnc-CR+xJ9mo z+t#vLe93*oJf%y!8?N$G;_ksNbH^f{Tw{07JIQLz9mDa)bPQf=?oi|D;;DB~vd0St zP2RRw53Ap&v0uKitMBr@YpY6B@Slp_RC1l0Sp&X8`>u}5JFczDWhaMavdItyyobbYg{*|XRCImAPskf}e1*0f6ga(EZDp|=XS zKpXndTX)&q@f0W}*@7w8ckeQHJcu__$L^|kuCcRTD@+SePS(n{?|SocGw@9Pd0v0Y zAM(7sIyz16m{9fOCVQDJ)w^dn9xB5KC zZ(QFCE1q%;tE{R(4EOey%PuRdY`|qmuO0hxrE!^68*n-3y)?~voT&#Df@G2A;2aU}L; z;dgNFX!+}11w!C!pEnim-CjL@JG{g2`-3;hHNA*rIWMe4np%T<3ZIMX!V<%?rm?wQ z+GR9?8LGa3U#B+&zh8TK{C?(T@O#9210|XrDA6@tGk3c$G46BT>-aXjw-dh?yl&U6 z{@iHeZRc0c#SZ*Q&hWUIegXe3tVIzx3|F?{V*T z`Rkzmn)5U+^xMW1ZgLQmefW$u9mDLM`w`U4#A&^SLGGmp%hM%h{JG26Mmv2fedp|9AAe z%<~q>E|dE6QQ|erF5?O`f%?t!P6BmPytm5kqYIP)_i^4)a3A9xiQkdl5%^8@rpSI$ z`}3XPO@w=#Hy*zlZybKZz0tCtq<%k)Hx%yPo)C;1zePV=({+8P=c2h|ES{%yb?&4- z$3tgk%JtTrZ5JsmEAi%xf4E4w3+u0we|Dd`_@(Ea?tj70vF;Oyf%@&D923RQY)Jztg?t9NZym1!T-A?43;3>U~&GwL`~+VlqwmShl|MxG#1Nqs_deU}oVCu0_uM(}`b_E%Qsb zjNd|{bQ<)v)G|gaV}Q)e7|6?H1+lRyuG!Oo1FzI#C*$*Iv4*|VIio02is?I-2K5lElW};Sjf>XB zR6ombVgF@#T9&U;PZMfbJzw)YJcxvT;3vi|WBouR*4k`!AFx{a;Y}$6ui| z>KBxf-7Y_+=P`B#DgERBp!C1dxe3xxY&v_==b3&e8u}`Bi`K1VpSQ4ePS4c7R!_~1 zgI8G7Qx@VY4g99)`*rtJZ>wkJfG|OxQAR({=!$0_cKO6Jy5iY&!_<)hp3xQ0pRm`T zhPFccd)n)-!n3h1qBx=ed#w~^^TX~?9-O{X=-j%W=njLm38nk)Ua~6}0xSl-MbHh^ zwt4Q82*<{Vc1Sz}@U^s?5zp~Ffq5JCVg!!oSST6Gcb~ol@sPkP44wKfjxu89XTD!?t(ZoSes1qzwAw44*}YNLD$sA~jge&#=HOc)BoOAn z?e^XOj#i~yYm_QJPF_! z+MOi@3QGxU^gGOIeV+ImOy>O4)23Z0A6{zeV51jh>z}$%iagK62^CY30TbLGlLeBc0h&`$V36&Y#(e!Lc+zVT`j;;;7P2o$aZtCv(Iy>cf4$$ImDfb z8Ggv=n<2zr>&M#FCq4O6pQo{x>d_$AW_W$e;>v@=Q4=rrbOYi2*JkU}F5+-j9voIM z%&INig%Wt}GgA8w&Qi8%(hGOx;d3~qCPttXopcM PC3;3!3tnBk(311tVHbxl~6R|pwjo%$5{eC)_8owmlG){LJyBQo=^D;o zIH>+#_1w{;sr3N?uJWL;g#+%!GVoW0E!a;0AGpK0h$%~A;nM)0X`nGrwH4nz=-v(`@M9PyKY|N1`FG--^!)Ys->GOvQe>GM2{ zGnI?*b*P?8Wp@T!gWjT zq5@%c9Yk#3x-|Ps{0P@N)kqb%BfEwY3i>Wr1OWbIgA#lsqABghoKF4SemH7 zsaxz3NlR$gxToN*#yt&p4ept^$Kr0pJs$UQxF_Lm!d;7d9_}f)TX9dreH!kWxX-}d zi2H2Z$Kh_neIo9QaK9aQJMJdjAENI#LCzZot?BWK^xTto7R_IfSbJyDorOoGjl=1U z!7hyQRZ@UBbzaaPuTYIdxUA=$ij0vb3ky%cPYL;%iOU4-l(C|8F}YDP=Abw6I(H-+ zp4%W@{EaFT*5Sj$cQ+32g1#-rI;rd5-NB}7qdi_fxHb~137-UoBhbzw=|O< zaL^g}iUP8`ph~CI*K|!sJSvPy8a;gE0{0y<*KYA+AlMMFQC?8YT8^&vsFqf$IXCknz!=>r|Akanve^w%j=cPPR<%$ zsX545H5kWfO~c{h?S>u}y327*3=rC%gSIcnQalvL>;5>BZ^j`wLkY#+8;X??FYmq0 ztvu`Z`y-_%`@Je+^hMl+W6Bmm_CSGI$IY4=7^dA}Vy;1b9VqP$QY>)US@(}0|6G>b z<#7dM$z8CqJW!TgQ%LBwCl*8&1E-dY$veGQl`UAtOEJc86|l3=e6%yyplCbF&5^Q= zGa=9Yfjy(P6{khSOCYOqmB_d1ih`LP$pBj_3N7W!y@C^%hs?{$Fi1>5)@a%p^s%9~ zukp*Iw~Lii1<2ZHQ|H+q?swu z96#Kd3wsn1oY4MzhHcqo4i(2r+?1Fu#~Mi_T3<7FIZ+pea4W15VT73E3xmAEHPMd~ zSJHK6nai`#N2i`=mbg4KeL>p42K`jNL2O^aMeFq3(2ZKT$$L!NW zBK1;~%aiSc?9x)A^$3t`zi&<~NGkklfuHWN812_86Ph&cq{wiwIwbdyDcr%pT2Ez3 z*C&-K87=P|vm%XH`6V!&mVgQ~%trutSd7w+)M-hF2 za$k>UJFeuSLX_XO1+bIq9;Pk}mY?zUkf`R@m61)XeSSD;(?g!qz}aEBj>kxss(16y zZ_}h|4n9}QjvV5I)lA5BeFe?$D9B5Yg+plT03+E%auXvoM0xgCrf>ba;<-OUSNsdm z6{Lmgk+A6vMQ5c=j*?JN5_;y|X?Yj-s>V$OcGP!}+en68H>rA%JgdCpBiGkLLYn7? zkG1pH-de`!%#uMvQhJ^~=nv!Tuv&J^V4w`Y(no2x1=BWPYv$UX;?zYLCx(Pu!y$Xd z@ff2fQ0&EWF4~Zb^UaFFR#%Wd>OZnf8SZ{s-8uIb4Y5xrN+Vvs_mRA{doJ!Bv+tf_ zNKmWC1Gk5F>w$KskE|4#(Mw^oZ6(RQJvzK6;`(*vvgR4nr>qERdm+Stv~w|Tr6RWq08}V7326vL|kHXd1GxiO$-)C1L?<@O! zwG)e*uQFr*WM-XzFfS4(Sm`|3=h=B%W5Q0}1q~FYx*22GTM6^8}k=tq^OHxmzxp@_==sv~RnYO1xRNTKL&85*@ zCBGTnWm^3yp6Q+GS~bJeszkkLc~ffyvu!WFD8O|$Bv^J}tPa)*)kCoVQT<>oJhfoj z^w3x!_@Y*XxAX4r+1Pu2Uw(uwNE%!+&n}F@p0{`2iSEP5HD=%q%AtK$a44vLu7%{yy z6c?w@ck>Febuj{IS)>&0zS`l_khXf9l%#z{8Qq+`LSd&;MaezJwMK9yZYClH-}0pq zE%&Z?sy+!V-2F_V{7bEcP*UoePR0L{H_rR?SktI`Je0ea*;ayBtyB&=tqG@m~!rLzEkp9hD{KhpSw4<^ zqd@#S2aG?Jbwi6^ksKyhs)j0CqzWj-F!z&y{t>Ma9L_s4Ucp_xi_2n(1QD zf{u2sjaVWopy4N!F{9rGDmh~z`LQ=bhBl+ODh4~}hriB**oAA3CCg_lnakRBc}^f4 z<-r;d)|2@oYgy<6b2wi;3|U9aiX-m=8jlbehhIfxy!gmIHZvw5F{|!8qa4(n38^pC zcaOS*LrdevrWHiB@cMDJZ02yZ-eJ9*p2?H~^0MmAQ~vQbr@fhN+sI-iG{Mki%Ern< z#vzi?S1~G|RG!0%ZM$sQBTej2M*A9RgN-7zXj6N_j^AsIxSV2 zu)5}-*~4W1Mr2YO_WrHFgjE=J1;ckj!WdR)+69*M4-9UTIRr>A8rXq|v=6I=wthpT zJua8ce%;*W{0quxRaVT$|EOhCUg>$KgZ3~r4Bzg^<*zm5lD$2YA=dGlxYSloak!Js zY3Njd&%fErjJgbIYV^VvDF(u7OSQZL&IPr=)6yypTA)sVeGnnr=h@*MW7$;v()2;? zn$V9daX2w~vEW-3k2Bim7SM_LS!GJohN2J5q3uE_Y-)%6;6(nkQ*rW;P!BXz=rOn* zY&LOgpCo#9dKoEV6?D7(X$kN7JWH_4{@QtmtQ#J}y5VdO&6`Jo?*C(RNvk6wqJ4W_l3R#ul9&fS z?PRSM%RE?);v&^(^}HHZAk+el`niA1<3~c{DA3Z>xzIJ5aEnMj<3)Zn>*Pib7Ev>! zj>_%&0_NpUnk_|Pu;vK;)O>$FUj`N)YvtRM+l3Vo!%+2r0t@V=J zLNA#9qx>W2TSxTe5`9>AR1kfTgjr^>q{uXmF1ktOE*G=O=lNp~@-czga*A(%v~L#V@BcOzvY2tPC%4Z#+=X!`Gr$j!O190-kJh6!@(~h=-oa zgWma~JWysVU5WTL+hAMca0~~jt(e@hB|H!jkP`ZW0K|h>++%T9>0rUnNZRjLJ?7q3 zKqN9O@%L7CG%N#*j&Q2Rs85N|epZtxU^uYrw}Ck4ULe3BBS7x8IAta=^ik zB0ZXbmdNt&Og|}2?e93sixq9v3OlU9GY@8C9l{fO&x9g5%zimc&44gJAj}5G0y)h5 zUxe8Ji>8A`O6x0xdn~9aC8t!`Tq1{={);eQ^@piPn0tQ~=B{S5946-%VW>_&YWGc1 zJBy!;SWUH0^&akGfm0EC3AHh20pgLq;NQeLL;d<*$i=eND)nH| z5oTWp?O&=BE`ZBHt#Omm-C(j__wm|LxBXvc-5`@my9R_j=fn z5f>E2(>eV!`r2Sz3a1=H-hOBe^WvPqcSRm^+v1gHC2JLN2unK|e({nbdqVQ!mGo|K zCu3h>h8wJ^L@2)idYtl^D@ukL9?_->vq}**;I7z!Hm%JqVpA~YLtBN(e+pWTeoN z%i;fiUsS-Z_5E-!g;hRN$Ng}h1si)#o35!no#m+qJR`}N?g{c~K{&1%dyAjV>h?}mPadMH3;^>0W)-fovb10jp zzD$(OIh4)uzKnu2YdYKy!Ts~T;c!0$_xir!GB2d0>)f)-o&$Yal>NCSmpvc$=?app zx1dfgzWTINRA<|9;t}kRt581--q+!(#B(05&+!b4ibdQ? zzRetyu=4TRc`H#C(esvYc0JmnDu%O@-AK+}Q^wg(l&J`Z0G8xSx_qE@dY=Nc`atWH zK-%sB=}C2uOy^gIwB^Q)TuD8@$st(o$L{ zRyKyJoqECsf-cx1Sjx2Hvf--4^-x^rP{{Btkq#PwqOmCTgk(Q{Lb73XTM16_Lvy0B z+a;Vt`IJsZ<}VRMJmzxwLt_{JOlb_Y>_bn>#YraGFJ(+w%a52bTDT3eiA=pFl;{$$S#i@C$ z?APe$#q|4KoK!8o7XoB9eoVjLvCSi7zp20Q`+Ip6NQi^F=3zyfX}lgwR435*ZGNl& zJz9SMtp7bse*a7VyNnKL{zqV+gB399(td;VJ0C(_-UBU&P}#8#ESVp?yJj zLvCL|eZzt4HmY|T^DFD_f%Z>q*1A2k_ITDYhjf8}qGf^=nVH~es&ru=ZwX0n+NPW_ zZG2PNN;=Pq#M#Vu%Gpiv%9+h&D~AKCM9n8y%9hlG+}W%L3V_+I;aDAHOf02K2z`Ng zs*D~SIvvILT1a^_X;cdO$qy{4i`{7ggjk&61Xj~zw|6|a7WFaB7XR3@pg8fx2kWgZ? zWId@Ut$|6O(zRqa>9+~cZxeLXitMhfO69bDH>qZ$(#w{7-~`o3=g%%?24|waegGHA zSvY0^4XvDoUiBt(_=%{%7_UVAt?H#QUWxkqU2j799zx9a#_-GP!wWf3B8*2LSD^L! zZG_4$3h+AUTJe2g?7&?<3&40nV~d>L%$V|-<%ABs#jH88yoSFfr8Dx=^#hJwYFVcQf7_Wj&SZI`BT=@!ZQSZkrKwFFf zZ)5W>=-7jIe~#yZ0>({z4SFY(aJtXMowcymKF@m}pTs`n=Y0Olo=z{dbFJmF=c4y# zeE$7hihAjnHgNqK*p#m^*N56y)<$EOr9QM)_p&Y)E4UoAnx+uJZat*Kh8BCJm~9Me zS7k@L9Z*zb^VEHv7b=o6L%SWLd;K^EO4=On6YqI;jYGAokj9_G-g7^;uGiyz5x5{cVdt66m}d~ z@!DyZ5$AM`pB^@h#af1i?Bh&Hn6e-#L;Rz}R4xczQ_Eyd5!uXw;0pV(vnHR?+?-zW zy6H2dKd9vlxGJ~(>}z(jUjC}i*G?1Ks?}JPOz`gzP&O0ZH%Gu$`&Y1zfKh1N8Iwzy z*i5UW?Ncd6b-uhhmA_n6M$q_^CnLJU=S5}60H3P-tKgiDg8g5WY+*Sj8};uc{K=Rs z)IWGD!SN>P2Y-7!gZiiP6;?(S3vKJvinfhdDX6d~U?O9IkG@g;XC@yzu5Ykz)YXQs zc>x$nKF_Vb5qXNXXB4m|y;LBsV-+v|GKYF0lR)z&t+Oj(k3(qLXnCL@){qO-df?p4daG6d^@n$NtSsqomLChffrCP0G#p7d{EOU4%X-pL`cqN*vwPACQej!r)Nu#g@9r_c{SLTWdkm058HV0E9q|

    hk4f5*+}dGI6W*~<5Y*wJbt6fH4%em`6W@RnC{ZEM*Y zX11i8A+%Mo%&4E7|B{3y%$PRuQ%SfpCJ%3Sl7BXJ(9*=mL+o6}Me#CXcp-u{FnXM7 zq`n}jj67CW)!b`{M~`^ca(C2*Q!0Y?aZcvS!ZriW8H9z9i4YqQBc54St6AFS3X%6& z{#E2_-L%r8RpfKsR0hFy7Ufq{$E_S(MAYIWih6?S>)K}-SP@X)Gj~$$<~$yGj}N4V zb3aNg2YPmtS`?n6SRLrpwDzaxMjb7b({m%enR0s8K$%l|m35PeFN-+xofGjC3w`e* z=dE~3!qZHrtml0vGwL4ah z3NjPdAaCHy2A-dy8GBNEO@*HM_~Y0;h01;dE(*BB(HZv)H{>9}mnjz6^oeyPtutb? z#RHgC3Y^5L#hSUY#ngZ~2j>RrQP#K?nm3MNHkd@SLq6NXbRqtH*)Qa{T1>~4nc#Ki zfZMv=#I&tr5;<6-<)U(j6=8-Rk<+SXV@yMF%EJtS9i%tjzzo-n87_K2YR6$sCZ?F} z5!{Fw7CSig5m)8ZB+xVn=LbVbPgkZu#_yg+6ad#gA16=!^d48XHgka()Qaz1K|TlZ zCZ8)TD&GWdK#jS2j~XYnj61Z7>G;U;6lAIy_f=@6z3!;Svx-(HcbnXQf*nO+?YY&V zZC~&zj2k7bUnsV&S=h;NTOZ3_sawSZ4U62#EeCF!rNljCK{4NHSPdH{8IW@{U}UMO zEYjMy0xKSz=rELJ*euIDASe4gIX-YX_t@;g_es7T_PI$(B4HQR!jhTE{kI?|Pu}a8 z+IKtdE4;s8`)bJ!jT*&|~Wr=8%cZ9MOUda{Dm-V-y+;T+vl}qnI!p@vfFwr^@bIV_sU4p#+t@0dl z2{KpX;A@=eJCH}1TMRR(_&Sn04kx|F*Oyx%wu|&OxcTgelQ6x+6RZa%&=SeDt<$_&G;3# zmuMY?-B{3G@04pZcl$h-aKg&m&AA5lUCmusZ&1e#@brRxnB;+Juf7put=zu@eVSwi zqV@?Z2bTzK#{_WyLzQjoAUQdF&Aav1@+~-7`t*hkvWZirP(l)M_`q9W4ZnTN@K`uj zI8t@kfZw2uM;OZRVQ>ygy26%R^4R)hny~%}@*9JEJfelxT8for(-dvTX%59{O0_Cx z4XuavIMC|ln!DoZlYpdV{E3-Wn{B@b(pf6V#2jahcA7hVka>T6D9wJ^_DyTR)5dOH zjorIu{HMyYCTmihXbt_X$fi9+b~#uYDLyxSs(hf?YK1ixW@-WY`-54g95=^OZ@&lr z?+KlY_B1=VUR`nuHV=xMZ-*_CYIbrFc({SxIL^u@+zJ1)LamTex!oDCqOgqi(Bqqp zklk3zjrqkc`w38rl|sc_gqs&SPGm=FgXzfqW@xis-ks}y5pE3T^=Du@#qb!dF3}o{ zpJTj}xxS{YkO4W{3%9IOu$C>4H#zT7ivgG7KT0=^fVqC%@|!K_>!ypYpzX$I4VOHx zLh{|z@vQEWXZ;PTubHfh3AUl&dLU3=B^Z}lJ}QoA`-Ufeb@iS^xxW5V6>bl)Y_!C} zddN4h^T4=2ah$>Kl(;irI1<$+_r|IN&*d&Ot?$}7OEH~>h_)=npt$ZDFy#JQ8+kuT9`ghC+&*FWE`1dt0D@$7E#Vl*4 zbiV^<-sJw#5$3mQA=ihg{jy_Y0c)rs>6`IbAsdbMWwqcI$|%mE;-^@@zYxI~)>&VG zU0Z*T#2VXZ==bx3GZt%OKc8>L_*m?t13bU+XJG9LD{v!XwKYh24>Uj~5jGXT3j~i4 zyRLZbvf{Ipcw)x0z+js#(#XrWKXRB1rOIiCupi6DdMW$2)+4S!X`qF$*|c*nEhw{= zVXa!4eF^se7NU+yvo68@ACs{RZSD!@%{-n|KGHlpkVEzO=UqiID)4W1&ncK`orM_g zMGSBE&Oi+JB8FEG!<4QGyQdcvS&Q)XK74(pw*X)7!`G+rM^ zb7th5IIq(J+t@cHS#+c9)~l%ew@Ara)P!6M)u@FGqOTue>a5bxoPm8h5Bw=`qUEw! z=R`Y-K-r@o|I_=V+-eD*>MYJfX3{BWPD;NUkkSKg8?dYUEf9?Og;cU6z39JBat2!y zC`C&nIZ0rH%XEI={K7@_{@GdRVlr+=>7Dr*b!U`K%|btQs`0yB^0RAUWbT`XrO;_aZ#r@R(NBK7OMDyx?ms84Zvdb|bp!wy{ z?nf;&5^8+!VYlIL&Ox6Vb{L0YJPj>S4NUdPo}PKB15 z-Z%|sh&ny^m?)i*g`%P2o}^fD`x*y&$*idO=BRyadVFx)!T9z$WX)2ngdxj!5(m3% z&HQ?dHA@%Thhcozm^4#agwSAJ^Fhy0@cwuul z=rQP5SbG{?lE!G);>^DRNYA2_lGA{_f+!{7uEJfVd!nAz5`GVT`@WWZv3hzoFRVgH zCgUfZXX2hxC5oNFMVHeYYosdjL4yDLs9g-dORLd4J=y3bd9v@64umcI*Sm@0$zD zZV}(YsvbS9^lcaU65#8t1xOD&g_opE?a2DDia8;QCRT4=SOZ;-qYL-Rzo6U*TN@R# zLt2_RlmnwbgSVL>Rn44%zHN7D>elXI4B3V3%YkwO_a_DAmx6l|?n$_-vYxA8M+&fL zpkj;4kZ0p)ku`u1H5TW?r7uaNv>cFe4YqLDNjG&oZP;Mjfc8PyVjL{pk*vSbmsTrV zM6IE5?%ElYt8rWXU@N_Se&4L!|cL+7wpheK5zRk`J}x0 zb4qokA|BgVe@^w3KVuEhG~conEq<<|#+=qzSaD*(F)3;M!2G8CEWaQ1{m=ZKs80;! z7pQ5!$S>;982)j1MU2}N38@o)@SzI}%g_o(8U4AB2;}}hB4Gvn_uPMjTEDQ6u=;+j z3}EjhopJxcp;$S4+VM6;GPnlnG3@KoXdB7eF{2-G=1CFSuO$^^f(7G7X_RfFl!eP^ zD|%w1G|{*LzmsU5c7hu?w#NK8Rw{$AQkf^_6y#XV7>^!8k6qn81wHm5^w@{HF%HOQ z1@d~t+y=SOYHkv(VCdAp>u?!y$7#)9WSNl!l}Fq7nAsTT4YmGtBFR&&Nu+fW;h&B+CRinCph*VcbN4G+|B-HW9!_4D$GBZ95m^)n;7KdtVt*NK}%nZ;dos%Z^k!Sv#j!KsRA!1rszl;7mbyr9N*Sj&aQ!MzA6S~9nENUZlIjP z+8oL{8nHLvj#P{crJ|Bg*8YT=ds7NQ&HV{A_p+Ryr_F-Q-=g`1(vj*ED|4vj#L$ZT zWl+hC{GGUs39($5JBGbk+Sy*xDB16O0%cD5o1~3~J@kpj>{@>J9#W0> zx1kAQW5wnZPN&uvr!FMwv{7UCD=toC89sCP;{|2%t=PdPD|3(1w;62<);tM#U zSceMunufa?_Y@;+j>8Va`tcMu=uBiYLfSTj-y?3Q|E$wA#81aPmKm06DYMe&x$nlP z#1ENez#Y9^#18L8cEk@ZKUMpkS+v`Gkye(kh+_)ISdCaY?lxTXtniIOyWOq3=qdM+ zUoK;^$Y(t7OfML29f5Kkwd|tjx4smV>!>9cJ$L(PWlihP6}xo>$udtj6F%nnh^Lzg z9~R%}yr+>zO6@=JOEzla<+O=TpJz+&PFGw&j&c{$pXehw%3Vl5-KPo2QSO6#rjO(( z_rd)Z$Qn?8qP9+F*DVA3RZ}~7y0~a9N9We-@LXA!GtB^NuuMj!IHlNcP4}>Oou#3E zGv>69>-A4;N@mk0oSHP9ft>`#xDM1te#3qkc|6;mVKY?BhgBbN1G4Nm=aaS6+SMVE z-wbLOWV`a&cESyB#!Pn{7iKq3<;2X!4u_UrAU5q;jPZfzhG0ymT)(Z@UjDTCsFN_W z3>8N^O)={*r(7s6E9FO)mG6KKcZ}$_f9>-q4vmrVmeIJ|74-MO z0l(~TN!P8Yg_nW6k0;bZHO}3s%pfmmGT|Ovhf%wSWRwy38NjGLdxduo*T!-i>~s0y zO&jd<`8e1!nl}E~azc;?XA@^|z=9?I-&Ar}m2WY3I>}lC`S{UE+(&u~#hVM*W#osR z&pF%WXYzmA*@(GLRVT{xQ3>Xw;TQq>SFq^YPOM=4=h`EJBfx-pCl~Wh213M&+5(+b zCyzd86Me&w(}$4L2-0l8=+hV&eZFmK{n@y20pkYEX_{Y&+pkMKUoD~ieTVby;C_1Gcw*~NFe$i*55uHeMdJCLmELwh1H0{6R$)=K9R|1kS>+~4 zZ~KB7Tx*c|Emn!uK5fgjIiyrvDYhL?IHVEAn0i`+J>*l$T(qNn=@Zq`aP5UB-n5Uj zXVjw&VGdTICe%2WO^M5(vjgzRvN336>J+%qiw9a zL2ct{l-P3QrxI-I{&=}=z$^B%>{feTlIn1b_ONMPR65198}jJfEU3yphI$BR)H zJq3YygYzE6r>SFC4l82YsyHBmV?NTG7VKkEHbZVC5!k*e9&;b3?K&_6Z486t7<=F zl2t8 zs?Y-}dRL$ntI!AL_ihdWe`GmQpJQtGIZC=5k6^E2#y#bA8=E4OK3qIRIlY+~{it}S zGVc(hd(Ad8gm2nvCw}Nw8}41q(yg{_kiZhM{i%Y_vK--OgV!>L;-XkM#C8fcU-Ix} z9llucW!xqBIR1mH33dYJR>eXuwE*QQfGahrDo6+A+fUs(oYT+9IaYt-ssiEj5Z;<^ zB{W>bOl9#Q&H@skKa?x7DeG(nz{-Eu$?9?z?m((D4pUgVn9ToR_c!qUU*eB0!N!bc zAiYvxM|((L?_@@wbBfZj*aloo3ToEGq(1!bZooQ2DdlJ`Hi zSn^!k>?4YAmIJG+vL%%>ne$*DM?XE%64e?aoIt$RsC4XC<=Tz{{W_}U&Gr7h==Y#5 zWeGi_O@P{m;h%{H7oap}A*1^ac@glHS_bW0cZ$R#y@YW%*cXT|vIXj!!M;Fi;NU)t z_GC1la*V*t3Yl{4hh29S_&gVT=M)iw(=C>ZmrJ^sN2tcAwis8=!na(>2HJc!$!omhA7u`E#8e>KQD{|G7n51~XB&28F%K4LaO&cS z+6;;C=Ou;lf?cRFlwT_6YRbwLHH>Zt?4k)8zDD5|a7Jc=RMoL@hjDFDPHegd*J<>` zYQ$|YZevdGrS(o&J+LygakbJM?`JUjL)Z|4E@tdj#ShKxSW*20R;r-4g>zfKOgzOnv^EfT)}`{rAzTxs zylh__D5gB`fVPAQa$B}LDW#**0^Ap5Ma%S+L5JI9AS`kwI@S87&47|o#c05ra%BCy zMEvC;mqsODv(h!B03o3%GvDig&MqisK6UX1;tiS%B$dhO&_5~>FZU*uv%#NNXphIhoiyWPP)$^d&hxNf8Q>gVUo7d_j${qjl|JsZ0R z$}7}ki} zF@v=_!`;Ke>9j@L6Yg$+7U*Lo?C_AD@avEWdVp*ZFz(MBWHX_ev{!I?dqgQVs~~an zd+Ql+3gS(suERE>9P&U)zXg)N$}t}dXmN#i((0C}siTu*Bc%d;Y@8`5+cv20{gpTb zZLjBR$)6Vaq);s;zJ{I)EnLkkOReSame7hEIfMs>{j4{i1k#k)S2{t6tA$31zZe(6YDpdR~xXF_Sksr(5o} zFr#dc>R;0}cQ=C&Q@UpF9y9G2tez8%k4r!im!FnP|3(UQ`w;zurPXV|C{d#^dx z&{HHmO~DhVI6;zNp<0C;ir2QdSow+5o(8>~MP(_k)$C_)V+>pi3#&bX2xPbt(xZ&G z5CtT=DCGj}Yvfc>H8ReUcalk)BSu&tr7E)a278s+-1uiZGbSG#dBy?V8s)GtQ^g@; z7MzNo;@!(<%sfe_N6PNP`~@dM+gatLy!Gbv6C;6F4I4aeQ;*iZG#NIl`5*@17U%>T zDwgd_TvfJD)vn}E@8vQ^oJt3Ni4Q!BkIXupjAYlwq1^cvLq&LPgxo^uT$^g7TU6ow zx?DS*B9CXci?8tg=ZRWLipe9#ME&=F z^FQbp{$ZOAd81q<$|-Oe1Ji(fjeZbxIivD?9yh4FCK|v&Id3nwHzvGfR-FvbGuS4| zdATA}aLKj9uCG{V*V)+Y9X4jzJqVZgk&Z=Z(p$} z6xi^Y7|D3*35xd=;vme2pNUi&$8bLUB{13=MCDe+pH$mepg1UB9??jlLNZ%K;@JsC z!!R~u%OQ4`N}p}<$KmtrzCk$sZ;L{vL5w@e-OgstJvq|eX6IhpmhhVSeS5}9#kTM~ z_O=Y$`?i-|87R9KZAu`9 zrkM;FuN3cHs8?>QsaI@cGRHdKw>KlMa(lXs9s9nG$!K<#$#GBVvh6-&yBE2Eu2kBT zu6uUB0lw~8mtvc0YmK>6Zb5D*?K7z@t?635`+Yl`y&K=(x03}$^m>lU8F#+Hr2@`Y z>q#o5cbwU%YoF(v1C{&NSrm4ZwrOrsTHZSIpc9%EHTCb9LmP*sO+1CU*;o`96)Q1* z(|fQzUUzLt4oOQn89&{)Al}%p_Z{<3ji%NdO=-Sk}$`bW-rLAoE2YC3pb6G zTnJltkg9U4`5Nj|WyJiEhk4vcvB%?twLbclXUhatS3&hA2P!Q=vu%| zLhne#K`1=nm-|Tb;)`7FCGJCZ_>Z8M&LDyA^%DD@fIghp%f6qR%*YtMRv(>u()V!K z5fgwH%R&=6dw`D#vJZ?C7ed4A7Z**dxWvoeNtTvQJ~3+ENYMr%^I3 zzv<=myrmo%cYZ&MeAHJt`(gWYX5JFg_$OUdC3LQ|Twa0=<9M*_Fs9%1bLjuVkMIA< z59NhzkL6KDI*c?7*)o7!nU^r>a+8&EN$u;SlA?UC+Y|CSvzbYOu0CdnsX!3!YKHbh-fiY=t#mcfph7C9F>EYz4D^ zCUgnWrcAhKue|d(o@PX{c?5WlL@bjcGIz|VdL6rSl(g2Gch zg7q0t_!Vs3Q99c}*Qx+*4Y+Gi$Lni>$&9lEdo2UIPbTvPyD#m%Lq2VM<$3$86@+{< zt5S3D=Cjsvk#MRpJJGyoP6{iUC238wASb@ltT>@=QH}F?PWX13m5tyFvrPJRr)r#I z>Q1xg#KeQMd;M~Gcb3eYKC_6`|IPgLo`lNy#^uRBB>tdcQk3n2Dx?K+cL*=EvwXOi zWSJ|C)7&kMW0>>~C&feVozA>sg`4SS+!MQZnTIs8cYK&=DxnxC^k&~X{h<+3b60<8 zzk8?i*69<9cbW4}PCPWuGNkdr3fR~(5VeX|M^p%}y2|-i|6IWFgYRnEP7|u4v)h9g_cx200Eh?1Lnb>d;>Nc8W8B_ocP} zAZA(Ofi~1|gLwbXO3j{s`-T?AJYhiD#w??=5_i>L*=}k+YR=a^7&X`19c|4=&HdON zS)hT{pK^TZ!1?h7&rjWIYdOB0>jvK+?QI1vMJc1Dwzmq`_BsCR;BkSOdpoQjpv-K3YPPB9{?}i%EQ`ObON_Cg&ZM)|qHM!D>^AeV%{!aatdARlyqz=iPsl zv_-lCMGg2BrL!>ZaCsPe@_>A>C(W{aA?KbbEq=mbG2rAf&9ZnI=QepG4TB4K_}{)X zu88>qFhKTG-<}9tihAjZC&oQOerH;8Fp5DJVw1=A1Wsi?fUcG!iPaKj9QBdZtpf8o z^wYNA0o!OOziB8xCYjZepW?R!hV-^3)@i0`ig%jTC*vE1b~TSxZ?x8uXp0{=-*(Pp z?%QeZz-SSM(PAj-ZE!b@7BSr+)}g33MK_^uGKWs$RGqF(i2os6!SRClBhT*n?rQWb zhvg+p$a^C#N#K+4?(x3wu7(&??XgOlcYi)Az>c(g6mVUqi}W@3(T<=g_oL_PgnU<9IA6ulGo(S1a3q2(T+ z-k}tn2aZaVZ=YtOga3w890?lagBgx)SmPB(-qCQz7jfP)=_j z1CHy*3MTe|g2}{n5zoE2XfDT|uRT_Q+&pu0)Jy}#Z+JyW8}sf%Uachyx#4?Ja}4Lc z6X=)ZzRlZE8?+NI9h`(Vg58>lb3cDDvM|!Bk*Us-B7sSZ@gJxHMD;L<>=lFusMh0r zpS~tbT!89GobwZ%RJXsRbz+^iKwI>hdCbZ5#s$fX66Pl?3WW?^GS;2y=gYsc=Yq$o zaq~=)v#?yXkiAXeH|WSTwc`<^QmsNch04v3MH87Ix*c?OHZWd^=x_(UBEj zw?O6}t;}zU9Xe_#+s%ga9w+ee&U?NA?%ZbGc~66HZlDb&-GD9S0c}vTUv0^2<t=Q#Q4(VrYFk@=%)T|@E%Q)N!Y^cOCal+KM^0(? zV9i47b^$Ai2(;!cs2kO<)XhJEq`E+)T5$L1%Cq)m*)Ei;?AnEF_JxIF?FD;7LKx1x z6=O+rv)U}23h09h2Wr7LFHE<(khd~l8uDg?EPxAno9Qbpufa*LU{SQ()2iYloLM#? z5et;YyFGjQYno8QX$PUFbezb5%Y&_d3`zS!*p%Jw5X(-=c-BG_hw?ZS<= zOFJZ#P{_|)uMsr=1|>8bR)cx>{XG}#Z3&4~9y-#T-D-|IwGTFlS#NZ9b-kgytu_Ot zm%5Ohd|@H^AJb#n7i`yLxeDjhg@nVfe%X28`)N^TpZ1Y-#QG)YJ%59xH@1dzU+ZDF zsSp>Pb!{}}W2}8yBIIGYtk5U;0|QIs-M%Pm7E0u;K9ZH75~=CCSt76YspYRVef|=8 z3TOXK1IomW=U*!iRox(ze@%A;*kl^ zA&u)c)Dw;A!9KAL@4<27+5Wmnl!_Si^ThlFFfNqpD0W2*`p`b-T(xiQ88xRCh1a(p>nu1HUj-oh2SzHix zfQkYYMXC;q3lTd~pcI8s+-4F1Efi<~CDN*sviM}r`#n#RQqY;-|JBb!Zl8OfyPSLO zx#ygF?m1-=*GYZ*To&gztNGi~@zy$av1N5tB6{vp?2C94g)T9f#d2IGl|W#2%@%f6xo zrn9bBQDeIe{dxKdwp@=sti}8Bw$VuaBecn~%Br*ERKI3W*-TcVIX(4q>rrnyA8sqo zzPR~1eDO5C9cz0!f=ds%RLpj##0Br5xxd;5fc{Hdk9aQP%{OiZ|FJU@U)?u?EJ zIuoYe*{=d~SVrqk?oWp0`x!xk*`nFK z;Y}55piiA(KLATS*weym5jzg&1mGpEhTs^C=@PVnrM6K>^#KHPs>3i!wqp-*9SL## zCi6lo-f6UzWI_LehIKs@xK4!5S@%&u=O*`gr>^X*8MqN#xHAgPL*n^Rly}Jz&$<5t zNYwf=%l-ox%tPFOG{krK9`CAsX&C>#zSOesku%E&z7yx%1k*d=^)MBAvwZj)c@@5d ziX>RzgdsP@Cleu84mmyyxf^{1GlE?K;6pGYSP{$!UPQB^`*f5>fj&)=@*8mnBvGVL z*teBeW4#`DFc&bYv{7nVkWPgLU?+)VSXW6%x#zAuW6W>t9B1M3#~#zcgIT_@(2T#3 z$~N9)mckoyDuX>HR1WT2;zIl?4OYUl^!W8CnGi#?*y zv&+g3;>|KM^o}FGvP1Wc`l~F1p#`+r?=}C+ImSHKvecR)wpeIPF<)|0JN<&YtXD8K zmO1DgY`)k0u`>Z&aH6xeeYh7kox|!Fw&u zkHV04fxBcFN*Xhmwx~jRZHBzg8syCT{i5uD=-rh;)+<;JV-|8?Ma~NcUAxBBzZka zgjBj$E%H8n;3I&rP~nXMCmt7plnNdKh;Nd6xd;{_W|hdl{8xBc(xBsSP^7i$Ekt`~ zTc0FUpsJ)tVMi?@Yt*jR9Jzp};iph6}CF zH+)0*B-rK^=~-7&E3_h`HsIa@TPW>TGm@M}Gv^u_9Kw_J9+g1sj0*C9LF?@;4+qP{ zGr}g1L>%cF&t9yh=(>s0pfHb1r{JJFOOnL>e5xI*rkAXihn{`zFW8xj-}C-AEtOVw zELlKETwnUjdgg`p1y>90@Y!WCxF5OK-`2y^6Mg-UlQ<$R;$^U|SNuOAA`IdNi#?s{ zNrtzVGlI#JB=&TIC$S|x3<%oC3r6X8*ggzn{xFdTU_o0>ly zV-&Y7pc`d)h_Cs*9VM*xr*}xUF>6RGvlM^H z%jAd)#>_?RL_hqcg;!YX!P{Wd1Ko#yaM=Iuu=6#OI^1nb@Vxmnbi%)RyF&iNE3o|H~R3JbSDvcp0ngpYE4onY^WdJO%v|zh^D?Vg*X!*NWxzJnaL= z#9A7z?EbJJdaF*u@0j%a7MoD^agnlq=R0Gu%eAOw{E@Tf4e-uSt?1B-9$fF1f&&+s zffCntlz*Uy6K96LhFiOT4y|_{$H@VeJ!@ui&)5tW&hoMWGB~TSG5N=BET4ura|XN< z?3|@?XRP@+&Ucn!G_V|3GdjInIW8oag8y`CT}hHNW}2@M;)|cP>~@BGA(hNhpck5c zM;INTK9Wx0fSbvZdRg~6NtkAH-kIo!RP!ayC6*cv+fjFc%_-~NB&GZ78o=~X$PsS) zNpKAm3;Q|UX`w5W3|p_$@c&O-!)f7INL`Tv4@!i`r-c(C71E~>6M*jgjZ8W%d=a8p zISHa&{b}Lr5OEP7;JX&+nfDXV0DafB>otnYKLL*q;yGa1pooFjms<{)r#DlX%t#qEFP4H%;D~z_ZhW|Gs|<;@Bt98lF*=d<0J5>> zBfySzhIsO1VUd_m;m+2}8qRVMxU&kL{|_R!+lhbGbPnQfqMpR_uu_*N9r+Xdb~Czs z@l=Izn}@uiEfUoh1J#lXuRJuQ?|Fj2*WX&EKQ-c59aTbN9=7Myu#v$0T7+wz>Z6_>(5#e%?B)}6#qWbuNf3rjyrUN|K6 z__W}O$gVhr6!{N=cY&xA0Pgpq;PB+`y20zRX5;q`@2zg>cqT{ZQc5xv?CKJ?bZE>G zqV1!-ZPqA9C7!BQBX<4Pw?ynTBWfV_DBuU-(dWT9+;=!F90<}Lby{c&##F??Dlm*k zyMj`rPYWLf!+X?e;cfJj@a4bZ3izVp1K=rSfLisnVwsgihtKkMoW z>B*bs5_aGzXFg(A6q<*d{{vXR41EpFSEer54ZhpT9r(=F@^DMN|9ZgF|nm}4bI`-Kx7Of2=iL`2UfPAYF zHCh@VY6aadp!ZB5Mx<3sBcppXkboUc0ZR2WDB0tp1^?6d#uA`0^@cCuV3bBh`SUWs zUybv#7emr)lax+`)Y>oP#^LFc3_ufSd7MSobPKrBQ1AMD9;AGIB@v?C_#Jx8FM zb6vnLGQ-XcD7){tR)&p73WQ zk{Wqa{T}e@@Mqzh?*T8HKLaulc~kAe+#H7P04dDPL4O}eKglS66y-nn6Yp~r<#+kj z;O6MIS|`fy@vBh26XoCaM{$W@O}rfG+N1DZ2Y3^$^4k!rgm52EQIi{|o&>ehBUe)| zL^OtsZ{DfHuQx=e+(-RVegOVz4?Wilvx*{krN1x=81iR~6e+B*d;Pufs@fmMt9hc& zKEkWp{8#X5HX=>Z9(bESylTpz`v~JzA?h?C!k_*^A?h42j_?1%D;k@!Nn_msYNd4D zfD^N@{&DK1(SX)K(f8kIK&ubVlX}wkvzlmC{?h3!ccd>J|}CrK{D1&t>$CYtwF4bIh3xe`YC|_YREzE9exE!ua?4 z5+nZ1PUC&6k2r(Vc)!C(w~fe4KoLf`(?>g15k~ly?-=OiyENCp1)BT4NPi^G`z&ib zztt+`3q&Zr6`YEQCG*T@%|$J-@I*kq(dHDM7G4xR5zHBRTKKC^34N3tZQPDFmP9-e zoE9GOxq8)4bkrr%EP^4)4{^kU_L3Tg0)yU#tUcB?1gUR=r!7gYY{chb1`@`-7yPM5 zIv@?4t7Rs%NVs|2&^VWTZ=M$Uwyg3|i1f?4eh#t&&pN|&?@ON)XDNs$3F#rcHb?l9 zK-W$S`6B!X=dz$%NCtf-TBd<#1vPTBpjn{ZG{F}Su8Sf89mTklMA#n1xFtSXQ@29PgtTuLj>_1ZVY}=!KyahCvx%fZHXYRji8-we{jbw|c`o=-b{f z5BjP%6)P?kqxE348@z;J9*lOSS5=W1(OaEHTx%)t?X>VbtP{e!!6Miv^oB=oy;T$r zQ92)n$CDyFia?*oU`_p=#=H|~!;Lo9btrHaG6r!+XUy?Oh~8|(=?-G1yBY(U9-R~I zw&C%87yP+LRy-|?#rSBh$6zEho7Z_MdMK@+Oyg-G57IqS#i!xxFQy`)hiA&h_~P(Y zMntYZEyPB~cP-C!?bwKSB01`%I_x##O?znNpuJ~u7hWSJn=pdW&^p|0D?@4~MhP1| z36qx!&)m%5Si2E#MA^GZceGSoFhLQiI~BqwpcFBrZ&kXro6% z#7mDh-EW?4`PACSTu^qGnRUel4)v@cl3?%@>H*EZ?r^^yLunMshpi1X+l z?KXm>p?GhB6tBN96z}iwkbaNkn_+l=r-!K6Fub1voeJSrRKEG6J&g7) zT5B{Pw4XscQ5TIq^N{OyXi{Kn;j-$oMD6LB&Nm!?;U5wO3 ziL`qbsCZxI?OMdx9mEV~uA7*55Y&2;*-<;MmhILjZ0^>_WgmMnjTs!b;h=k=(2FN>3EI;FJm=ASK=m^>={d=cLCMOQp?HYyyo+B~ z)3aoC@EFGa^priawd#%1wTTk7A_y-UkP-*bRXt6 z&=(ofjO!+xAkrIYcN!B5`cepbTH+7C3!g*lFT9Taz2H`0u2+ctdL8|G(oOa%qP{iK zCkK9un)*!1=V!RnroLwAV|&ZG-7>4W!8&`93zHKardTz0D!CHdAbEIy5AQ&{l~EgV7S~=XhxRq@$5dru|&mq~+`I#dvoe z$`fB;Sj6OW?T6*8eOWbUt(lZzn(JCDS!bweGSup9X*ZAIFICTU zr%l`r9RZ`8BWl2LqM&}>z}i+D;!ZT;H`8*#xgWL{8ndgCY_||$SXj|I+k$ta)aF^x z#D-dI@CcSDuSe!ALVGFd7a7(BPT!MtFs%^5q4GDH5WbZ2L%h1=Ox6D%Xa+N`K zVm?j-BJvCmRkN4 z^iu|#n0@x?=)v1!4_>M(!zx?hArHKJZBxsqRge#>Us4>xgwWJ7hj3%aM1LnaaMnNB z#JU!<>kP%U7J}+X<%n4G9cIjzc8h(xWsa5NxuuwAo8gc0JX5&cOl?chwhZflh3@Iy z2V~u|RGUk)Y$vSDV8(IK+GrWK6ECl~t9=LEFPLe^Iv9NS zfz73$rGUyt+Owxo{Y#*b@*yO{Pox2n-7CY;Ax1OEWkHSK^bVSL$Km3 zpM2UnaMn+Fco5GHJY*}i7r03HNbBhkehDYu!f%wnA(?^jmQIOQ1H+aXQd{?Dkgl5S z9HTg#S8-i?RRn*Hw(F)0=YOug4XbIVxSFc2Sxs83Cc;i8|AHNJagWCQQ{{F`n&`Kb z)|EacB%FlxdU!n~(aeeP#%j@0D-yH8Y^?ptd1d8wL}G^!EN%>{L|6ogn3+AwPvzegT;=ZDlxS}_4h%o^O zZ)b{{1&1)Hm)^u7RET;Lhrsvr^akin{uew+kDqW2x0#3b9|DJFu;n7P%Kb7Th*d3` zYrPx#BkbG-TD*P0oz}x^!so0O?=>l|T--K$({cxmZX{6?MzK4&k_;6S3txKb^@q zghT%5*#3wIVsp}pLc&XQjs{)-iMktZE;9AucNp4mXClLV^1O2EP2jW>Y4p8pl?$-v zyFzI=S3~RqH_DpBcc4D7-94BKgYN- zGR9|n#<;N87@vxaabaYP^RF7?13hEB`~PW-YOGtjw?q9SuY-*149*aUU&5^R(jw^5 z?-kG@%&^J%2}o}ZRxLU%kF$Z7A3U}v?}|j_ZrNPTi-~Tr=%7@A7ZfR@L5#o$ROTs#4+%MLQGCYVcXl8~U7BCUS+BoPgwhsrl#1 zcdPFyRocdb<9!V~oCYiA#lZcQd!0X9E@QVsDUti?>XF4A@CiV=Fb84+k}U2J?A|Nq zK@Q=Fmvmvk3DGM(Vbmwy@OcpJbpQ0mi96lLkW&d~qI=ta+gE7Mi{efkpVSOK!BAe8-^fk?{Zh+V4ZX4Wy1vXhpR;XK}n^86qwhYfB=N(Tx zavC9VKaZTXkhf{<)c^m6KeuvnCKnIMT_p3>&^Vvb4~*J(-heUu(_gdm`YB_OI_KYp z@iF-`pmT>@o~yUBg@-X$F846xw)*uwS}WAjV;-= z^g*sp%$+jQA*8~>Kn>gfsh4KdI)r{6+M!bc9sK>UgY5tRPxuf_XnhfUo&%hK*Oujm zWSfkSYfg0B15Iy8sPYjtV_bg;i2Q+b-^}?2}jMT8=NT?EXr|)sB;u36So{ z>K-0+i1KDOEBFPaC{-9ec^5AAS;q?GfZn?+ZU!-8Sxw9(zL)M{Lo$XF3 z&jjpmLsUarvt{nI@>Frw?ld|Clbfhz87%ts7w#-}2xHx(#U-r~(VX$Hs!$`p$SraX zSPMg7R}sZ)qL01#{wSVD?d)jYqHCS*rH5DQeEYOcmXm{rnrmB{^>BBnaQ5+sTp z24KntJ|gD_g}XxZ_l?kPkAo}#e4)BDqsnGL`d4!^Y8h8H+8S#sgC!E@MP9+2MeXw9 zBaG{}fF`4=i{sc_+|cKk<79v4UdC#Uz!Stzf#vCqxIKC#c+okqd|<^O!0mBkhp;K6 zE9($8hR9RRwuOT~QAJ6dHOxFbocjCh^}DIJIj7>{}D5Eh5X zYt4eB4xt87|H#f*f1w^V*TNGYy(3wu9yM2n5GMjUk)AmLj?=tE^+>dSqPm^l^Anwy zy;xU7UuDY=Hd{qrm7+kz+CswM(9}y$J~9bDDfas$quXNjoh5p8iwfG4kZ>D$BrQ<4 zM0HtjqGyBQ3$R1b;Z}wI!kg$>YKUlGbPY%QfV*}kW*}^B3^_dN9f>GS&P0J&tyT;oiEPv@1;W>J}YWBp<0-l&Mf2 z)#A+v?{ht}M_8g`wItI8h1^!+(*}5zJ^8RnElb0$`^tsNCoFfbNMdh)MSi$3-kpvmm7j{(D2h*8Hu^v2&;iOChmM4yHp;iQ}j768;fR^gct4^5S%V z_2hZ!XxImhmcJGf#(J`>64=kAF4q=xl<97(#ddc`5!h52O z-yv*`NY0r5|0&}uo3>aJ5gjJ^FxQ<1k1Yw`##ijmh&M@mO!v;N?nL?F{c5CLF}M?& zaVzd3f}4t|C<2}>gr~eBz;Aw_;3h`1eVh27>TAHEL>gY-S@^AKO07t}xzAIwu2PA- z>r=TDR+*4XJ!%u9ojvpHipBYT+Y(F}mJQYf6EvLaOv`#he0vq>oXV~O*RHU!Hx}?Q z<-{W#?gw6VSB(g3YLnVJg2#r3=u7$$DfVN)zL!l-9-#REbO577iRmRb&hkw!Thc6> z5K#v=ET2j@4>C2}xyMR9No~`aGA%2uTIz`=6WnpE*b}9F0D2+=uOAd1hZddgvC=F} zb#Lj#sX|X2ipq+#4u66Mpbzlx2b|&){(a{UpGAh(;W58d#LlDsur*@`?Eiby%WKCL zU5|vHz3JD6~^XzOY&2QUC-!yVh7b* zW-Z#UxOg;LUSi64s=<5625C@EzO>=Xh$*NlWo@s+ zi~AV#pai`c+jF)@8uU@XDt2^fH=NMsAF^K-R{N#trCp9cf9g!mtm=|+pP)691vRE* zn{PIM=NydkOnw_mKIJd0exw?fju~^iPS-N|ds}H%cfi{O?fY9=A968zCg-y7v+onc zAxekD5ax`r{(Z@i7&?TvVSyi}^RM`lD*9k&Al%#l`+*eXzT%4$aq~GJY|biT%N|7D z3qGQnD}3=~4+3i!;oiat;8GdZ8_iQ2(*KQCX36D@TNdVxf4>)I0XAELTdW!Q{W7S7 z?vPfJGsq0=gEw1)J9^dPkN!p7Oq1mhL(Z)H38@Jw3ObAgIS5zj^rY>)6mgHvJhj>IR2P#_+J5Xjqe)KJ zcl}U{Gat>N^HNsNe*s-$NU#LSD^Wi58Z|w4R)`1G^Q$IN&!WAeQYw^s09-hghh?mW zdX${GtCafliJiPzHKcuke{lxEk+mv3Vig#>&eVQ^-^+-#K)90Ucncoe)OvGQ95dc> z$el|5qwz=!>e7r|aU7N1OE$JhF$~pm+ICqO>tlv?VE>ltlEHO4YH^N4_{SbS&g8!f zzx=>S;NUiIHemP9z^tBm6z%h;hJ-OK9q#*Az30rR$OP9Prhj+fyq0=?<_&wnKMrzqMC@KQT!xa z{ckuLk!O26It)Oc?+Q4uH;079f&N%|^;Sm1@|oC^?y@XT>sM9`su6;|dVepS*$`+3 zFAMK^Kk3)$OonU{?ltcJLzB}XbV2H-UZ3>D!uEj9Wr*`~c*3052}HsRugGs8Z_?VL zH5KjiAL%5>=R_l{nnJ>+)@e;wDCIJoc=ve7q5IQA@Hd8Gw+lN>E%@wawLxIVnh4w}z{PD#m23Sbi2 zVtBYq%3b_6H{+fzRvOKr7Os>do8W8DUZw%sn<3f$#Wn@01AeV#y48f#J9x*qmefV< zW8E$A+c5I(tm1BlkqocFzgdUb{D<+o7ZHMz_`9=n2~++pgr-yKsCAF*2L5a72`q;9R^nLP3AJKPT7%wJA~_2Ykp zGX)!jjQ6mQq!##} z@HL4N*hly`*;bWBJ8Rl;qCyD)oII2=IU}n+JFhD3Uv7XDOYbxM>GRAH}ybZ1Z4jI z3kG4`@^1f@<|=r-q4_0ABPjgnGh2hgZywBcFAG%CJ_m(R3na)UI)&t16S<{kWxhf5 zcCMBs73UNe7msAPY9_zb#=)*R%E=HPqimt~7wyo6R17TpSa(YJ3G((S;YY~ZALpEc zUlGbbYadXag}S@(+djA^)Etb=d*re;h(& zQ%Kx4D`#nQRWEq#jOn96Z4E+nx2cgVY!t{DC+F3t@=?|91D*F9tW2F3BC z?{u3LMOZzYUpXue{0ql1x{Gruw_W~~{Wf>mc=+?OQ;ETz1E58;Pg`NPuwsY)VsHsF zXxYt@wQJx<343*brU&dK6Bv!{8*>B;+Y9d z4D$&)6c7DBn9)HC&g74=vkjTe8?fIo`6F@DEF@Hl_1Km~PZZcOTS{^3Px_f%vM%gV zLsvZqp1{u7)LN-SS4|w-33zin3yD{`1CSc2GR_5@&YGWhGdqqt!|i;9I6uEQCE70M zm7YB#_AHsXi%DaavmJQo|JNBEFWo4AL-E>`GX+Tl>fQuzarN&xwK&~?{n-+D#^jQ! zx%xEzIm3))-n z&Lz^d@>gXWWiR)GKa)2ttkpa=mk$VT;;JudmpoDZ0?s)|I(6A^&ZIm!STA@J>b?z>jT2SQ6q;Qd{vhM*@|(ci|zMa!mX8V zl)Wmyru|(gox?L1Pt+}hw8pspT}OTR6@B0~q8E>{{1*bmCTe{)N6ZP}MkoZn4dALrxR<=?8s^XfS)QUZsC|v)6d;TWF zV|hrup``qAL=CVaCJ*$HtMSjaZhQatv4LPt((Nwbth&-=3%|q`YZpX z_0f^mh4*(=Z!#4BuJ!SbdenpYNXarw8eWEH4#E;ieZx7bKl=XGp7(z{cNn9NzCSWI zT7KEN8FYH3qgrk?N8c_yM{_QJ^vawcnY;UU?_RFlA-{Nfb9ME8=PlK{&kd^>UOq(JB~A$=0u*QFl#m|~ zt@V>m30Z+b6+^_6gj0ehFtGeOpZqLt zo}9v2KhfW4EY8WGwWIMw`BTE*-uk8}9gfmzqTfWfqx72S;W~b$>FFJBSh!WJ?pe3F z;8}NN0i^tu6t_?Ba@TRMQB?6C>zE1GFoyavrYq$^J zG>4{$HLR?=2Y+Ul;f#G4TAC3mvqsy2tklyE{N426;(4D= z#I_%OI+5EyC4A@8R1A#Z;&ERl>}}~C>D#f_`v#P2fQwTvy;6G$_Wy))hmeQAA3h~C zqjXvYop9x+oWh-eG~_mma(zBujn&;P6RszcmxTXM-A?fyMOLa51_e zJXtk{gf{dh2z~Xc9@(j;=kC-g$fP9GpAu$>GCkIMFPR?eUzF)V6CyJGDPau8LgV;v zGCeq5+_PRL>XaB=`1g@|XL{}=j3g4gJaa<{`a zrlutosau1adPc(xq_?z?_@htk3&?%lppMtNyD`(NCaQ;tc(WiuVZ&*ulO8DCQ zzs7^LZp4lH!NiTN=9yP;BVPd=y~?*rPzb_Wp7nNlilLm_ulg$lM@i`4KPD5gVQR;YW^uy21yYsX&Kq$C}HJuN_Y-_ z3di!NgyrxSKsfrCcWu}2?Qw`6Jq^ux!m~I5g|`LLHl|o-!Rq)Hdlqg~h1X|LSm2W( zdh1f687r@xnwO#9uLqdiGMsS*h1P%-H-xuaE+{y7E6Y2w(u!VzbAi2D1Cv`~TNzo~ zSt%G?&D56CPTxNpShX`+CdTpQ`3%@$P@OXM3CU{t{jjE;k=k2(d~)D`FetABY`~u zH_s{81ut?W*Pjyo|5;$O{t?y?tutCfQJn3qyM>=Wcr`ujzJGt+gIS3BA?ai!5_<;x z3oBbz8GN{XxeV55D$DG9p_^x7&4hcD2N`k{&e-?C(*k^r{=9EGcE$E*Z|d)$Yb`{J|$mg>`9v}*e&e@i+Wz=q`!fhw2x39H?DtIRCx z-`853-QOU$!`l`-oHB#Lzu`OWg8+-ac*x>o6vn#{{saaA8uL9wU)nHMe`LG`O6dKe z*DYvTj~nS0==*cs)mTT$EsSf1SGiScBzu(SVRM;|TZ&|3x=t+D7jIMCwBo2F?@?}@ z&r6L;yj_ZP_^Su-RT_F$(-dn-zBzOEfl8AlbN8~!mH5|K`4#@<0ZhNeZoqgB^%>xorOLFmy4ehBd<%~v&{mlIE`Wz%FGDP zhksQIQZ<2U$Ta(eir|g#Ur@wLyEU?X!n{B<-y9$hhWlWjIv?w*0IB}LJlr^>Z>IoN43?W{vl%Ox4?~&RCPc}>vc&{j2tS* z_0R;x^I?zf`-C~Dm0A?=T@GL=>( zG~Fktkf+>6sTWaRy_ITX0_nJ&5S;@V^b*nWT=a*>^Eb3YaKKpPG#40`f<{O^sO}lX z3HbV@zAr@Y!Z1DTr&9FpUq}ss&z60{7k)!z6qC`v@C>ygH{5qa&nTMwVL0QyDOz0x zFK|-eW2y&jvviV`=phL26N)i5B_Ch0Pq^D39pwPLi>aVd-ikg^FNX&ptdQSnW}lGd zXN%Od##(6()9^4ZNi*rq={9S?O)X%gu!<|vGp@_KL|J%J3cqW|traWyIzC;Ty;$$` za*3E4?CtmZ&E-tzcaJIf&$?L&&25&qUqc1|P$#F#-I}%qHl)F+oyVWlZ2c1Xw64~; zKZi&C;K2EO+SXj;>qAvZMcXKHCGI^-X1F(vKdhe#UumrB`A(TEkEdGp2~)gze8E-? zQe|Fte7NS8U{zMTo$A$WrPVvn{YGrkYnH)s9<# zVY~#-#cOi#y-&GS!j~AR+^-%zufY1-2i@LyBhDIV9X-1a>wgy>numq>1@4vOZgp!L z(lt%biR%mWDMzEV@G_UoOFWK73+q2bx%ti$UD>buMn!Oak4GuaZ7e9#|2vK1MBas{ z6&ImgVJa4jHwVmN>K3MSCXwoeYY*G$uLEw%MNIJd^0208tfzc{2%vt!hxJBd_svHR z;j0}Gp`0HhIj1APCnLYd=yw}^vp4d_7Ww@eznC|C`+-k3FkH*0k-U9AcD#D)WqbI` zH;^B0_rpm3`^dj+kJkJ*dW(7*(HCmz4gAg*=W?k}Juuw!wUIAZM`{MeLCHsa`bf=B zNAj0MY7S%6TznsH`OlI3hp)`yMLZvXp*(5Mvg zHcu>{ae~QgnImRR#>`HQqy^;$lCAvA%1-T)c9u%C2&a7xcyVBK;S$GDVgYnvV}b9Jp*#8s{y(C5 z-yzRogMG6!yQD0zc>#~!Lp<{ITDpHB4kFxt*e4wHnyn6@ zRs51_RD+VW2=j3FGoJs46Zc#ETGUB7;a+X>shiW#!WJjh#<*Vg&&JZjS(<8oLPxCTOd)>sb#pnJ1U+x z(mqE1-f2xHh%^1ATf)bRFxU&`o5Bb+ z-<_><7_RHP+RYb2V`jBJ#>`=u>*((X)Q{obdf$XyJa98viAW0T4X0Y?WJtPLWoz0T zW?wg(K;@=(PM1vW&(F5)c%i$NAjeAl(wewESqmXnjo-Vn}B$r_Y zF-A$3?3OgX#u=_TR;*dsuIy%|9~v6FlpJ%69k+@966b!uVeV+o+)aQSk9&_(IbPPL z0Tkiid#Zw$9!mrKG(L`3JFMM*{S#*=>qSiTt{3`A5f`qc)Andyvt+2r#^FiB>dS6s zMod0_oyqN_+7j$kkF+~9T4t9F9p9#fFZF>1oleQnqBaf6Q0_U~n#>QoFZ~H;kkk4d zN5p{R;|uzPsF=S?X*8W`vfz5_;J9km^BWz0bkfS zW0gT?oFA7N%Uxo0)1|NzVPpAGd znvA8LoI-+iNvoM|@eeE?SXzTcTEoa_j{UEO!(RSDgF@DCgJqy}|7C@1BgYHPe~<2D-ZYoq#3N6NP2x2{-*wcjFq?_ou1@;4vJ^~9ADKEUS* ze(m0y%Vqfe#ybTqed+;i7C!fyklrh%Kk=3${SPs{$2$q>9b&r4TZ;5H57G4xyvA~c z*upk%G2U(zOT6Wsi1e#s`gQLFq*sgSb>4AEuN2d3(2F9}@C-Owyz|y(v3wzRh!$ay z=Mi{*q)|U6jwn2mMRM~ChU)Opnv$TsGz;wWz-RvqJsIr-%#dQ+E%4$LH-O3C z)%rsk>w3!Fzaqs_*j!~!HIrQ;`Py5!tI?o5rm^Ittu(N$>6ST`n6w=8FU|~pjwROG zXz(6OvP?_cWQesx(^N7oZJ8m-?69Yy+zhj_M2#p$l4EhYeAxYj>yV0dRGFo%RL`yE zFP*fn{0te}-Skb?AQvZ7j)@yq)EYO487Al1_N@jE-xr#Z-@>}`-8tndODg|bvu5fz zi>^5b>lXJcd`05zF9yEUjafQ7m8W}Q8Sb-Aon=mDzaz2v+%cUwt7MQ_>Zr18HpI2Y znrDb*lx}HvETikMaVeNjGqPGUGa9FT;nec?SiW{1%o^z8q?-(9Z8;g#&iy{N;H3TE zpi|V}pYUKGoVpq^^+~s-t}jlS0ae%05NA>1#Oe#-Z*E4HP~V1gx0qFmLY+&&aotME z%kH#IM{sI}xaJyIxUU!j`Vw3JjXSsQ;Q8bgC(oneCU=XAQteB3M1nX#wEFs~N!iQ95Wu5^9s zXAAVM^>xIBC5f8lDF4&~TOGaC1fNWMTf=q!wc@X)BhD(z?5rVnxvt7=x>Q%iXmw`! znuKkLZ?HLj8@DFDyAZK>*ils$rk-k|HvZ$=-F;tT{Q5e+kNvBxaqU&Gcq}@R(6HVx zvmKspE87&MM@?Ac7H%C~FYDSZUCuzv3W~)U8UrN*7B`_JqoLb%8iKn%;Z073-^8*R`fj88T?zS z>Od{`Cbrm_Ayt;xlR1?s&0331A;DLO!&Uj_Jc3e(lipHYlEO=fsR?V`AxBz2>6S=X zZAwWOuxPeSaYR>b5Oo&2Cb0$i_Bmqj^cPOl9XX%O(!A^yK7bWdp>1RZt0P?lm792} z?1Pi%L6Me2~Es0tidk6LgiCt$oruhDv_E>69g|=ej8|D~h zl*XSh@%7G7=s$~F;^fRHc>am!3q0T8VRf^6wR`q`6;3P8I2#{0=`PwdqjXE_3}DQ9 z{`wQBGj>Sl`6p1Xr11e&eIp{)%_$vuBEDfm>5UlCam5Ws;v2SgaWN|Tms^i-*YnMw ze1-I_GV(3@M7i{}v2?NUqy%R4z@P6;_CtjNOesq>8s^VT@ zcVK_>3?BWFL~D$*Pe)&R!^y!Um8~f%hZk_8dEMnZ_iNEIgqj&E~WK0g8Dm>Bh|Bj^Tee~S| zr1LEq{WC3jxPvLSTfpkJJ9L&ImH~3MTgv2`*`x6m&T+Tgchu@(rd3*)VNV_(reTJF zaxrh3haJDW>;Z#H*OzbdUF_z3Nw}YcjQ!@VLFn#B-oa~Z9^ln> zL-&bJ+%=GPr^_!p|BW^7z*E#DpTg*%ndbA&WZP}blGyVtoK zyT4`Q4(0Kc*v|>Z{Q=_wJv8Z)re2cNzUNLYNNt5oQHb;KdPP0OR+X%huX@kjw?MNY zXS-x;E-YGDSB~45wYQb(V1}NwA>u0{P9CE@6?_TF{@N)z6X`~CE#KfQlK5b{xzr-` z^C42yi2h}AXeF-m60bx&ta}+ABkRiE9p>Tne(=9#QXVk^0J*y$8+X~0#PnFCf3#oc zV#*W5{j@;T3jFNVH>-O7-YcAel>MzO-=fuKz*9B#X_}hRG{?)qUoT!vbh59Bq~8IS zy${H6C;#W>X&Ttk`6T?Vd75UmDXHxC9LBZN4coL5cyYud#iPO#hbIA#8c%Y`@=EFB zC3AN_K-7cPffp~9x?XLj_PQ}2O0@QsEWPLl5&g{`i1 z#9`L{X6ev&#>eT|!Bp>gXZ}!W>(GTyXMN{X!!EmE=y&$qg}5Kj%*wUf?_a3aNycCW zE#zDs+^qcHY)ai{&bPXYQG0qK+jhhFf8(!8@h@rH?c+r+>eqkktj&_!wK~K$EGXbd zHDi3s6jr@~$@AFc*6Qpe^JQmCtei;^wcwI6#)8;(IOC!5$CzrGvf#6me04Tfr{I*0 zaV>DaX(_~c7tJ`l*0_9;vICk@`3yb=cC9kL&2nqA!jy$GF?KYwI-i%7Gw_4oA^3b! z6VXqqo91yPp1a&=6_jU-EydmM0(82TU%!B8pGY6U9u2gD= zU{|}7;atB251x<5`{bD#Xc{b|k2xtWq14p@?Oy@B8y~e(Y}z>8I5YXsrM|FOO$i@y z@P^54mzB+MOY<2ETaWVwL;!@Z^)3!Hh31R+>yvm3be)SrL&z@O*NgEgG{ z&S07po=C7g*$Vlzg$20-5EYr_JT|FI<$KMJ(}N=6j6P0snuz!^sR25UG?_&b0g@d+oyf{5;#R{Gqmo zv%m?&C{gQSw=ui@!?gS%ZJQ7UI?2r@N?l(>-en#m4vDc%H}om_3ehINwvykWxK=ql ztsy$F$id&l58~N@UxU+SVysz5@yo_|ohphu!yI+O%e(VpCOa;NSLCZsNY*i~R{#@z z^K6rZKb14P46z=A?8rIl{~+=;yy3^7me>P;17e-6I?gNWKQijn_A zJ-Bij#$M@pjD=KFbA84>gZbodnRFs0b)|Th@(XdkdPfp@guBC?qbB&Npc_`<7W9l# z12{d2DqL3K9#cX$KDQfgJ}y}YJ2;$|rfy(#Ddxt(tDUvk@(qgmzL?v3LqFS|ZcU=x zE^RQP7wDf#T7Q^FUYyi5#tn>N7{p0eG*omls@QI6Y`jCcp*B0E1fG=-r|~rNG4)bc zvp1%J$vtMz#_np(rrz$w-2Vd}jW|`4Gv@fd%p6{_ZWQ{F?0YX0SQCbY>Ro3W?hI@< zJaqD|%nuBsPqZP1t0X_y#$g`W{7m}?hIBhSBFQe>K;=@*EOv0ZrI6Y%cW{<@s51+= zJ}yDdOvdws*lr!#b;8P$#;lHvdD`H9&OYGfFWQfbzu((`bv~F)b6sE`Yz;%~9?w$# zkt9}k2O{6eT?6Dh^(hAOl9Se20jMs{?jj?CAB#Ckx z-J__$d07U|;}h(>OD!MepjooRU-TKYO>^I;p8Ddl9YMT1*o1OZJ6Ec>u9YlcAD0HL zg)|@9!HJnRc9g^{8BQg}+kp)l_Qu_Y82b}RtagSwr$B*7*AlNn7+|;&?P%nw4OwPc zLx1z;bL$P5$*+&KHCh@<`?XhEBY_A`kIz#9mK7~S1V9UHYA4BINWps z7OekJ;$thb<6`3^K6&Aa57`2?iz|NGFxf55oZ6Yf|9m0=_gl9c(oa-cig-ims8nbtc)23Jmt8`z9*&K zF)@eup(SZPjlPO4{?IWobW3uK{FdIl@=8!v;*=wStq>r)2gSM-f7#h=0*XvD;A8D%Bx6S_8LwB_cmE!?}~Q+|mh5 z{%o7PGHfY?6_wxSv_cl5)vvRmzJ#sVznHkY-O@$0>IZnA^)-B`^2HWbmEiCF6XT|K zW=&T46rU@)QYJNgXzWxNA1N(Itl_Dh{U)DnV;f4{+`H|~9^ zSG5-!tbJ}tO^h%0@!Lzn-`|X{?-XlDsn~}$V|=PYuB)GMZYeOT;>R}jjjmgyV>US1 zo9Ir{6_~n>efVS-CsBasQ`iPgW^ZJ4w}Q@vgg1k7{yp^s+f(zO5?5dY^x_NW<~H{? zWn-4U$1DZCSXm`f^Fj0X?tyh!|N6(UOzlQS^{o$w2Qd>cYAds-q*JhbJt} z2#tgkO{pnCo_|0ci-^fZ&06TJ>0N9{W8Uv(R!R9Ea4tu9#I)vumiJmylj8Vti>{jM znw!>WQNV(2YUkVx=mRkKvF%jH>3|I0R)A9`vfw?2x!>Z&UdsUYaAzeI(Pz{W(9_bw zc1?9&XXledNz+{eV%V~ouMGLb**3E&)xy)5XJbc{M|TlcrU zttS3Wl*D~I_3qXzo*l#t0)K(8(wYlQQ!l9>KI=T@R@V%!{--$xcKtC}2ZX19MXapr zS!F`kGjg{6n25D(1Mrr{`T<+N zku}q2w6yRYamDm6nNyO@59Lcr6Aml8Ikr4A2k|7i30nXqCMUK8wfA3b?N|A_6R`|3 z&+*E*EbkKk!zE1DQ2mMzi#wTO@I9b~n#m`f>_n$CimwT#SbbynG~8EJo|t3U*}ZA; z)J_~K9mi>-E~S{!Djl0KliB&laZ1A&j_cvyRtIYq#Ht9N=}D^Hu(nB+%e$6JG*hR$ zr8tY>Tp#;6UX{NeC!L$Sr81>C<;43K#dNoF=z;FoxZGAo+oy1#H!djkau>DUxC=LT zD%cF88vOSAhU;55Bf2G{rP8tEl5GdD+iAUu?$`8j#wJ4v&MIDLor!jsfgALGeeorI zlGb?IS%Ml)VeT263lBSAYh-hjuTu=8kg(hjz8#o5&_o_|RiI)f&leX}Grz#uYrK`t zUfFt_T5tnx7FdXIu0?*FWF@y*(R)sU{>2(U)mK%rYvA>{lF5f%wpOCmR=#I_`$A!< ztYnONfA<(BrXdY7j08Mv&9chM^GCI9cPOwwr}FBfvDLNB`kWx_V{*;8l?BZtZ$z~euD|JZ3!poR zsn_UUbPp-`#vNBzpzCnIw&X>39`2N~F*@keAWh_}tb_7A_T635*jU>f(260T2b}8& zH0O-#?{zU7W|zdQ3E!k)TyH>QgBB1~MW!RoC}}-jbk8WrsK1q$X|d;I-dwg@7#Wgo zXlu5~OIoSz83jqu59Ny~swXeMk4>@RR@^e3>Qcq!@Q?t;6^x6?HyztyRjf&MY{I#4 z3}BjKl-6(NzjbmA>Cm=utC{>gt#b^BZz55k)m^R9HM)hv^Huh$g?ag->|6us_WS#mJ79)>B^xmA&ZBRm-)zg*Irh zueWJUnR{U0E}mN`3%UB3hQgAV`uWZ{#Dm3-so4TL#-i?YtXNeXq8k8u1vkdzamSea z&up^Q2T_vEK}2+&%gJqL>ht(SlN7daLE#>sAxqAGXWzbJj(phKeJfTQ9dIdM0z}Kw3$@B%u}12c zZZNte98&fF8*h&E;LSIIKgOG3J$SPhw$2l6dB4M(alo4eJ$SSA|2N+B16I(T)H1Jv zUzqis{n}Z+;00W{8ybI>+CyYO6#S6#p>k^>lKKkO|MKucs1 z%nBmrZVr57sO|S#8mk-4jVm&MOSHSZuB!g+!)1mWc-e{YSMNbfaMs>vVaGL^ajul5HD}jURkMigB(*iJXvc|- zYWPumxa_Ma`)}LV&ffhmYEn!gO>Gi5gFJHvY?aS>uGQC$gbuC0g{-3oS+_&alMCp5 z5gdk|C15Pj?i6Gwh8{VI7)CMLQD&n{&owTg^)SIPC^C+pow~?4oX%N_ZX=ca9dafYpc!Xxz*DRn=7@|21ISDWA@lWGBVE1D<0}Sp+H_PH6-x$N!HxDf(UCR<015HY#XN6i21qlW>-x zt7LUmm1IxCZpUo~m(0h!Aroy;g2F;~Su;UvgY4HMBn!}LZCrB38E@J-w{e9Ab5@7H zS=>~VD&|-}f^Qk3ZX?~fR+;SL^wyO!yoi2`2%k zgp*g5U|Q)cp!Zq9Dfo52whqWslXaF9Yb>;~`OS=L2qYjS`PU)s2+r}%WfB-7xBIhi#f zQb2OeBzgxv!kA)8y_w0y$p!9*F598A>@=j?$YycsCC9_kZfQ(EUV8Z@=)=$df6ToJ zcvMxkKe|s1No4?vNkRZUnJ^<%5{57cNu^Rr%xDr6MLVffDpWy|DpLsr+uO#WyVcg) zC~AXfJ78Z?Y$Ymvjfz1<+rF*W1f&I#ZdzJPNDqmEDZ-HVTl<_-LsEhM|KIn$?-iV! zbM{_)UVH7e*IsKKZxMR~G=_8)F4 zn@hGu_+m&2OOEMLL-XlM`_GJb?Vixb=Bm&9r;9z74O+tnX5hpjre}dUy^d@W)J5me zMkSAL(H|ruuI#efrw-U_wY_2RjHiOdazb5woqn&nL{qfJ4$FR|Nw2;61!_|z`tPHz zeES4IPVJ>s6MKb(%5T~HndaPY+3Y6<*rcI8A@xiz`6?%UDh;R~UCz_Tb6tg$&qTpB`=K^G5HBaA0Kj&9vxy zql!Q9qOI_GUXZChbHD2Y?;Dw$3lr?xGhe#sgdnESwf8Xkg8Fek?$oB7uFNjM1j~1` zV7Sv5>l*IEcPazI?v=xcw|w=?zB~5pX5W1d?PGIDVS61CxzlM`x$ z0mIoh2ldbld9gn-^2I?tWTg5il7W+);7@(pCxmU_+~QROCx*9q)du|nA^Yiq`|$Km zpXLemQ^K!1Ln#`B^*{_E~={WFLLiiXC2FUjlzl!1J=c6SqbLCt5#kyTj8K3G?J-*AX zeky!hb*^fQ!Te7<0~V+s(>yiL@snK5V=WHtQ$NYoJ{I=W%Xkla?4OQXbHg7>v6tks zEyA|-_Rjc{Tn9gq)gafLE;}Eg4Ht9Gl1+#{9+n&eeFO5)S(2BM><&7Gj@_6WRfp%8 z_nq12-eoL1_yO*-Vz(HtFc%*Di+AhhiqrdACt;2d<{m#wUsrtA@tJokD~5*(mYmb~ znK!2M8u%VhGNhU#`gOwALf2b6P2pW1cF%y94IM^!#YHXZGZlES54e+Yk)#*iyC-o! zCov;*hBMGdlJ;NgL)lzUmg^*}NAmR~;2$H_;gn zpK8#I#?JKdY@9tOqou3x9$Au$FMU$q#9$3la%1!U_Qi};)nOs)dfi_+MCX5>>AZtM3rPdH>O@#=usF@mt*B}I*`^Y-Dq@zs2sOPMJ(4@{ zaA&;Z1Z0=&`-eKM@Ys<%@L(r>J^cmHxqF{+npp-L*a{62QRg&IJg(2!Wln|^Lmy=@ z-vquyHPGwb(;ub%k%9OGT1?M&jK$gWdQMl)oL_C8z-iJ_Sh+E1-;WI4dGRk^`_tInoNj(aNlke{FX$D> zDqu}EGW5p_6w=$5;J}=4UHK0u4@u{W{aA)&R^*INYYtVbZ zd4hxuT9O95*1a31;beKA*IRiWUy$e-p>t-UeQ%CTFEQ|$BARo(nl)_T?-wS^A&0v^ zL!EaSCx5B#zgq>_kxGnv&#Rj(;BWL~AzN4c9%}ub_q={T;KFz+g-@WlPID{HpoJqd zKJN>~)O&YtI=rHUow;|@EclBlf>+RcfiYUMmzRWZyPwS{x)4m;W2mdm=6&nVnpy2FaG{c zpBjBnwO|8LoNDd??e#)SazDJl@5dM{#mb(Ep8fjbPT=24)cHBwmuY|N!$ZTK2Y16N zBwn>scnDuOx~PKf+XUc?9@f?qVBI>SjA&>wG>c6*R~EabfzQhx7SdySvHlLMJKNhm z`C8Qr0#X?uQxp(ZcebvZ;1}Q=^vm8zz|R_%^3o=L=b>+7P96TICk*#V!^b_*7>_5w zFaCWE_6N9shU;})^c_()a5Hv0_G!H)i&tp5E|8Ht>-5Yxojgf%D@BykT-`jW3rhgm-#zzJ``=?VenghI6`4 zVC5oUJij?x**&pX*kzB$`P8u39bJ+JIVP2^mD5uzNfY1;d}lE_@~DUQAB-Irqkn|; z2iHxfZo*0gc>}I|t>%!1YafBnjZ>B2ZzN)RC=K>0UJbqBS%-G6f1~1bJ98D%bA|k@ z-!r)gGA%Fb((j#wr)HFUwr?UN%4}dQp1<#lE{QIhU59@63c#NJJ_dUwNDnZ6IM*8Z zG3@?_v9`}fKgZyjhKu@Kk4v{lhaAiC1uHgC!v7X|!|+_-C107qDV&i8rlT~}CaOR7 zW~C~eim|0r@pY`YuFrsNQFZX#lBuRPrlJIVm04=1=Gp^*ivb*FY$Wd)sr>`bL*u(= zZzNV^C3b2JFXwI`x9qD*w`LS8Xl zx)L4VMd_~Xw~G!IH@Qxt4e;@dGAIw)S4Bf#sRQHZ-4k0DTdpy`YEO1-F8sDT2Ab%l zT|2sjw3+aOzpO6V@jb7hcW-|8lxv9svFVeG`@H%TmI|x)>}7S|BPF%LyEnSXyEhW; z*Dqmd=la60bq@R_7w`*5hSZGwU_X1I6WCGPmux4!gg5!yYG?rJ(wcfT zY0bURr-|quzVB?ssT*f5EEnQF^zNVhp*I)T{>j#LA9@X$fASjQwitha?j?&PNz#Yj z^k=aCTyzf1Qh(@WnU%B#T>Q`*{m`F))w!oT>&RX=GT}pSPNr_Vke1R5Y1y^Uo>M2x zil|?wI;Z|#-Jh?Hz4oYjd&GO*!P&vkn-p9)2+XF zO`Xb9uKWbclG;Rm3Bh1Jt+V=QlHYkS)Yj z`C7@9ubt7{G4}x46m8h^3H!#}V75lUc37zNCK#?tNf;Ks59@{fuzGg4koar!bLM|} zehoibf+no@*Jhk5M89PGrjLn+y|4|7exv}q?jler?6eml|BqfH4zu*i*HOmo14p|K zK$gfRe#$nO2h8ozMjBbt6rts1CFWk0k~l1!kM4~|p3pj)d)&Ox{5yP4<}qW;kqJe_ zeYy*xd-vn}9>Su*g714L!p2QlxYu|LrBf{GGV!>1gZZ>ah0=cw4uJJ6MjcQC*e^_g zz0<5b>_byp`jQ=Cn`5_vVvv<0d~nUX^NEsm>kfKzCLhEoKW=pNMW(FndmKAN-4@ND zP93%_e9NOHTS|A}w0ygc#_Snf_)=_wc1!H`uwP#ZjatY~I@#6m=XE~fSF}rnjYQ`$ z@9a)GcOj1Re>-*1I9buF!#)z8M`3%kwD(SUZ5DfV+u_}8;BTFFNeQCQgl1pT0vqHJ@+;o-_^G?ZPtcmUqy4^=3A3`+4eNx#wJE5C4u(K00K>gbA-QTZ? z&}`Z7-T%;jZ!WI=53OIfAN}%~AN!;Bd(%lO48Mu`3xxfv;g^Kr3~j~*4LqDh16INR zi5DWV4(DX1Id%=MTDA+gzi!>E2nF{E^Y;Vuk>5kabLzICb;c-F{wcvamA5itCmZw5 z{wT99uz7#)gZH*yuXB*r?Z}Yh;){;l?uyf$R_XmAoFJ_{o%>2G*XfG!q`{Zm%F|0; znN+mMqfXFlxy7Tx-A$k{2h(y^K^P}`q0Zs0dr9W9Hseaz(sR13zz=&W2CM4 zi24cDBP>zv}VBl9Yq5{f%I3zQ%}`0b6Ly;ESb?zC^TbSZzMsCE#3c1oT*g@CAKyu_x8t zdU5E;#iSw)&KeJ0j4MjUz3*a<9e#h2%RKlNq3=-6zE<_H_C?`g&5OXmhiAYWY%JRN z%!nFv_A|iDF>AMLwmo5t#||sjoK>eoEq-=hKn*@cF8|Xz5x#i-^TLOo3Ao>X;dh>B z+<$iAxA@}bMSR0tDuZhFd(IvmiUk3@V^I@T$qwF;?WwUC(ss_jIO$5^3(Sc6yiRhV{V%pjB@g8CndEUcc8l za24dW&Ve{^BJ}JkOnxo?wOOwTDRGBgDCy4iKidtBvy>Tq`XtE9`n6#etKQ5W6&_Q) zNOSAG;mYnb+OcEL#0K&&#B_&|1$v67m-6(2|D>F$&dK0WPRB`Fu@~ObpvGYr{2`~` zT1{>1I*s0+@(%G5x;=4SLW&OgEMmI70<^GKg994s)9qE^xr#WWUZQ&j*uLExv-?cv zRnP2>f&^AL+?g7EUr)_x-GA+%^Uw6}bUJqL~ z*sf+veXJkW18)=2uc>lB^38ALu>Ev&V7GQeJ<+UTcGv^*iaPoJ$L2~)5!AIkw>BFs{^N+uZaB}biY*RrZ)bU zF}Y{1naykOy9*Tk!OqvuefI-&(teoN&C)kvPA2iaI7^V)mx=RuoV;WAKt8I4QL*F; z3BJ-1JJ_ds;U+#}U*qjm-+%Q-g(bHfF~Pp0ccuXr{QaFQ3*+(eFx4!%Wn{U*B-Jjt z@!270qe^^9vjszv~rJk&upA_PA-~={VjaEUyL(P#Lnv`$Oy!) zC_Gi*djQ_O5k*Cj1G%G4|2(LaGLNwwo1x1Hsm%2Tt{7+B8;>Qi<> z{MM4%{lQ7y0@9Mymj2m8XN#hlLMVsD(O{Qw#dJ;Xin7nfOrw64I6gf%zG*!Nz54XL z?{1E#Fjd__?=(4dT6*3O4O1x8h|m~0G$uXwhr{(0dKW@x%b~N8!@cl5Ga<%B0|;f)u1!4ox+8jU6_9K6~e?Irb;`WN!O#QuXP%8v8V9$FWpV9Nvx zsk*Sks&!M16{qsmL)dwPLLeo@Yj`>7aa8X6-u<8|AHG3l(f8cvnBi%^7L;E7pf}u* zc<=%9J*Kw@J7G6|<>49T34`HkP?muPXyEPcj(Ge@z$~v@cI(4kW$@vk+eYz(Pc;{F zx+yN=af5k!-PBv@G!y&OFz7zu>2MczXrP1|P{K*Q`M@dN-l$?RUI*@I(plQYMHElt zZ@RNX`1Y_AqdGh2sW|Os_|^map4ma?cjCGi7s;Sm3L7{JX(v&Dvz==DW=LqsC+qv2 z!ZHEAa6j+dR79&f@%1A^UeNvAfu2rU+vCrMAyw|cd!4sp&o!IxxtwK(ydu2vp6f#z z)M}9zQmROCAeTW7?G+Nj<3I4m|DIa+EFW%Agf%oIZC-Ap-@dw_0zk+pf7nK@w z*t+fm@6C%6@OSm16OIqOHH%i4Jd5+H%DMIUTRJx$e~adRg)>Ui+-LDOcW$L`-$xeU zwY^sKldk7I?>&pIEm;bP7BSe(0PM_ndz`FO@N4F|lvGyd8DO&;u1_s^ix-}8R7 zSlN-jhqkkM%kZT);CW;&dgbEKYa_&^-0rP=i@eKMdi8Hf8j>39&^}uHqGzVu`vQ7@ z33`9!>AY9S$Kcf%?~$Q8w1vV}yfOu0bIvaDOfHww>+wGQtn^IW&!n^e>(R$y-9(ue zM)rClWjJ6)*ayIWl06VE6Q4seA;Oi@xD)@Q#`D(KT(9G7F|QY%{pY?p;mv7p&U~}? zWLUzKH=(tdcbBF2q|OKnE!(U&lMoWVa2jHx67F0#<;@7ZN94U%-+OX`QHqV`vHFB} z*Hi2)#81qFC07FG8n`A2S6Oml?`H3E$k!G?>t4TSYHxf|d}+KrzGMnUlO@6nJnkCY zAI4pW`vKge6TywGXH7As*kxdC{-!V9e#K={uz_a)*@v}Jzc;Ex1n9dhoVrVeb91Z(s)5<6*!*|B`x33|!_&wf>Rs>c z3!sUxS1L0XW$IC8f0+asvor%kqV%>dl*qkf+@MecXbGs=BJ+|Ez2 zLx4=Vb2D}a_|8}-Nn4@4aOXDj`^^(UVXFGN&ve$-9C#FCOnbr+>}h|?asA@Z2iWiA zVpj_dhM|A)=fj=P7YPaby!xD8ufCY2pX*z{UY)k|OPnKq@oeeSXtUIMI(_NuT)+NQ zv(%H1^O;7vg+gj{PcHg1m-nal!WK@;^~nOt*vrf4>U%Pos_DuX z58ekkAU~V>Wf*AG`-gX}C;IqKXMeMhcKhM97JB=(lUC(3d}V(P`x5M>`15a3-|(~- z4nsFTcgn#1lc;r5228lW*?A!R$wN|Yv+NUk(^_Etp%>EDmqhh|YR!4yYrvKM_La ziY`DmpT=tj`fugw$qZ^A>7@*7+pU_b zJ!#Ftmi{JqSi`A2ow0p_Gk)l$?ml#KasuVt4NmR4KE})at7lqCDzLXMaUK3PB)*8h z_QX^8+myI|eU69vd0LNN%oT6;Oe_8L8y$zv<jD157iKQHa8Umu>j=&+EmC3h;v%sV@$ zVum>Tj)tcls)WYV``+83i}ZF)CT!lX?|Y^>qWgVsVp1_-_9iy}T%QG)J;R6D&OVx7 zlQ>?-bG)wSc>OHL>l1aV<1B6W7t1P^_-My_pe{rWes}(j?rczk`_a;sm^<0PS=WVi zglFMvJwn}S;R!5$_GW$D#GSLY2#ErA zw}}a#cF2^;1728J@>e(mn7ET9J{N~7 z&y5VNJD+1Nr5*OqwU|-Tlg9Tn-~6oy1{x6Aa#?ZSUAwO|M?T4T%Y`i~4*!DzukM%u zyRwn21wD}LC9F)vdBc{>!=0}+zNLG7-D?q#Pqtt8+OspKnzk9|9)zSHx-6?_<9iL~ zblWw9D&1@obVprt-+~1A(UA-_^f_i6NmG%1$Mgg`(H7t#d3YH1Eb55IA?K|li-CbV zCw1k{`}9a;k@P0O>*&QH51?Q;18Xt(!bLty5O!>Mw~71;hB1cuALAn2I5$+&Ey|wM zT<}q>F-IH}%x?_cc>$-cxVPh^lKfudw5FFOhkZeDj`Q?4@#3IIKQMxQ*756-;aTho zb^d95jVk<^!8_P$c%x&9x%xG1X3|{9{yT4Njd|R7tmpcehM318I~A^}IAw$_<#ob$ zeN(!G1&ITKra`Y6yuC)#zpv&)@^_aN1AlkI()(ijJmhn1CUorb=nH6PI`#V|xe8I| zT#}3lOH`)K&^2bF_PDwO_5u|v#Fhl~t97#OtMa5@P4el;K-GM??z z@4eTE)cq=r>c~CD1atV|{41X^>Usf_2AKI?kIE#N@r?^W!V;oPg=+|5YV#I^xe5U@ z>nu_5iu>RZd-_(EM!q25?oki2{AuRo0WosgK7OW}v+Ex6 z=8I&d8QCRdXV)dzw}lydvh6B7C)uYDglS;|JvcM+ggwi?9bP=z-yeK~fBx zi8YhVGtlE#cH6qA*U{|QgY!sOGWSiZQ@bFMH%A|z-VL~A6Ulyj%`W2*V!!W&zB4S% zr@^OL#>t@ywIkZG!x(P<$x&fBtYvjO7;Lq);L`f6x4s&74rjH|v!cUrq60m7vGdi3U!4+rlL^m^EFrhQ`hI_W zzPewZ|M08EQ)=U13t%~!1}n0XKObP*`&XwyW~N#8q%qb!|KM^9$vcO@3nm>+!3qb> zpES~vy=wTbi&qUl@coYdiEslrCHlSo1f!~dwvi~OD*xRASogt_Gd}m515{tCeSEi+ zXB6@j7Lj}gefY~McFi)D`o}(D`G+3q`CoX3EL!}h$F;|vPX=%0|vyWrW{%fFt>28%T8>d#IJk9NaHmG)2~zG^cBKm4S@ zx9dJ3E&RxyvlHRXRgIHpgLz+Jc)oq%Nsrp`TEFEgU_^e!Da#bimpFZdwN}2xa2&m_ znT%6V*nEdUuSXl*2fNAe6myr+aBzFH%=4AuU7GCJSr6-X7*&=(_M#W=hDG$g=waRY(o^B# zuz~$CSWDPeVKHaltFFSQ2Q6xCnsB%2%Z`at>!(nIA^AF}~>Y%l>@?PWVgRZ>>l8R*$b)MD@zwJ07 zCfl=T2lKQuGq(1KS>#PZ`f1ST;+tUKSRpSG*3Is7c6&-gQ1O_bKDK1K2He7ZxN#gL7G( zRt)D@C@j=vkk_uTW2S<4Q5&j>kfd1-`UVdVxu;Zmv2Kq?m-HI>sYp9U7LIDj>$1Mc z#kaYZPt8RyEI;7UtnBq@#6zCDb_?CY3&yUJuBjbSbyGU@Bxe@MPm$hy4rew3&Y5VqsT@~B{N6>f*pn+kWV(}&c!ix_FO zU1gp+5YD1{kA%m4wl%!CXAKUG1#l@h_kdFY#I{!>x@Se}#Ur@u46`Cs~T_nhlTC;fp*Sx zu{1tFv)h)URFxt-pTtjT-Q2C77W%g6f7{l=i~H z64##M$mH5vsvXAYgF9j;FQZYP4BVwvFa~_xy+$o?9hkAcz^ifg zdw~DTAi0LEJ9Csw-U@DH^3lj+G`qultGo%h&~X@G!@}SL@Fq!K+P4_hhe^hzHV=7_ z%N&++1~UF1JUM&dyWmWUd3fk+$SPvGaEb-V_KWa_oIKOrkl+f zhr7;3i&IW-cFyg!IU*eRR`t7q%$X1a7{(wU-fR!#!nbtvW~Bk_RY$H`i7DMku*-Xr<5zwh(zC65MO zun0Ooq=jr_SC6%M$9=P65>9tkztm&3?YL9Vze8{p>}QW^rSF<(;hSxu9voWy`JrtW zcHBqyLi)|)M8ziu;iW? zvI_;JTlu(A-S;#&RhD|DuQyh|S8qP=QA6K5uWKY#bL4*52N6C4_g!?3Lu2OMGXu6* z(dO$vD}k<(>dUAmmUhjtZ`QGSOR;KCIo(+`-yXLvtuCpz9xYrQu03S6ABeTCkLpTj zIuIMT4v-1S=G}#Pvl9Vtl`Yi1(6B>wHp}|?b=_TxMO!jPrXAnlvRWsA6QVTc+0b8u z1{C~L_RdKYdap=J?VFELop(Hy+s_%h=A<>ki(@7{3JqS%vId!;dC zDUa=*4$mDEzSwaetmGzv*AS-lcuOXV&(uj1ut<$PDk`91kt32xO!))T{${ z3nU1easO?ih8@Pe80FUC4izr86ZfBHY4A}OE!!)XbL4BOyy;OIrmNAi8|8Lih?T-k zUuankt_EC-Av^o15@!_neSA#I{*G(*pS3I>;U8Zog@1`K4Z>7k~ApEJpJN+7PUl|bY`bx_>aeayFI)r~b5#N`>wE+2;aNUe+ z3BpZs{%;(S^7#n&g#r0A$@$-ldtN|zF6v#0>j_+i2=APLGhnDuzmE6)xMu$aeTwkE zA^)clUU^*0>TrFGs|4Y10G{fd{8!i~;(7^J1;Slbp!;EnJE3KIT$^!KBm7I$y%XV@ zucdnGabJb-r)#8qjw9c1aZN)$WeBId=@((vu!qVeT(F!`aa@`_Tf>s>l){DkgX53f zAiWdLbp2TxTS`a2OWkRl-u>65;eRvFUysY>7b;$maXYXcPc{YDKUBIvcqRVOMkyW5 zsb{`bv+jofyL^=V#=FyK-O?iU3uG~D;Y-pp^+dscNY8^;soB?aq-V-!^snuZmgx{^ z`jeK01_Tayj9|AQZu!QQ|(wW#RR!Z3t} z4XlJ^u`5xVNTx*v1pbc+0H8?2tL9uWRVx%v zn670zgf4ZD_Ft;vupJQ}XwO6?O_`{gt+`44dYB=sE?Vku{bb%RK~2|$=^~>i>ZeS* zB4+lLVtnG<@s#SH)IRBI?B$IrX$F88b|#m-PQS{tROa zbU;x2Tr)7PaQnvCms%-=F&``r3lIN&;I#KVQ>iuDuy7i&F+Tx66@F^`pl5*=Yqeov zVY-MhkO&A39IlIqjEbHRrqc;3sG(60)9=$i2<#}Xzu@xV>c>Stb3Du-;Ge z`!AHP@C5*$)NgzR+@#-teD}(CL%tMlc>nGD4*%-a15Yf^etzengdd8RT=V>C(}hXD z$$Iiv4}7Y7b4^md^QDI5F#BZId3^Dv_w({+v}pJ2d1H(D%-NME)fG8qS#u0$i#FbM z)AAno9mguBw@djd9>K++Rxfd*7Gt>%9EldC$B&hF|X#E5G0K`LFoh_Q@x7 z7i*tZ8FBx1L(yWoKl?~*Gw!)xKVf(l_sv)CfBR3kFPM8~eRRH3PxkcTpWd)wLq7Yc zd~Nf)yYksJ57qr`^xb<7WOH=eelDzfBn5GV~^?e zCy#8(?>J>DZgI3ZEKQEv94%|byvoYj{LA?D4h7660syfx zXlqmx1!9ZfdMH6YIhTayh?&ZY(qsX#E+>}+#5 zTY}2Bw%ROmj()gu+eTkXQG#d?-R)MZ&FyY%Z))0v^6EiR>f7t-#u^>3mqBuHz=E9|!i0d3!T@*&m09YFjH%EFgx`AiT_6C>ERKeCS4P zki$(h+{#Z5ucZDJD_X>SXM0PVA3np{Jy#e_VOopq4 zWw>!QyR7x}-YVT!xh-pLOF25Nsi>?et|-SWxP@AUi78SEN@WUKEWjqW=xh}m9B%2^ z>TGVdv^0oKjuwvPltvVnCneRdkdjf=Xd2jf;&hQ$kpko__=H0_R4XZkSZ!-_i*0tB z*i1uB;Txo(mSK7R4RZcvmUYfnsx2i(xxR2mOUQ6Oj1oNWfzf3W@`or>>QM3SGZo7~Zke-o~{wGL+*u`B5~_aZKCH&emp2n|LFBWo0IlZ>(G`a%%$e zbP;N{aMYACBM|hHTkb!Ox>CHD+-&I(osER@R=cIuof6Qm3cQTcmr8;8U~6qe9jPm+ zf1ItGL}}iSrLVSDi`ynPHd)+useVDU!qOn-l~&IqWHdQIomyIK);3VuWKcyn=6+Ip z3dQA15kw6dLAnV5WJrpY<7^E845InQ(t=CVd!o1QP3|^ZGu4dh6-eVT6+~31(Xlp= z$_LT906I_5e)=8IMw!0*b8Bz4&?4Z2CR(qg9*@@ZnMu7}-_&kvb2{7X!Tr#{N0Kq4 zFIt+8M04^QFuU3uZfUTTHoDsgZ=_z7QU}5XE;S+twNX~%=H+w+WfcVHZgaX^QgRw& zkd@0f#NQ5?ity4IlUv1v4Mq_G^ihEmIiVDY95jjQ~*8TiRh0U&F)#RWq){7+8?>_Ok8^t?zcQC zg?$^&L<%z@tV^e2w?wL_tMF5^+yJ=aGF%M8ZUtOQ0Nk_yxWh8s-`BxsI^fCz;J!te zQf{vdw+~^1;VL#g0PgJoxQ}GGM-bKpxN8F7HV44%li}6 z0Ip7kTaB>)7zI}o0JmC(OGjATD7fSRxJnt0A?#~)aJlE#`rAML2EgUY za61w9ps%e`JDv-G%aP%3N7!|v;5q`}3^H6L!kR~wTN(hDEW;%tECX;eeXwe#O6Yt0 zRBcDX^gsV=$`svn-u+?HU?G*0V{`%fn_0J<*+fZnIT~1uraWP5U_IC7})tCVCAqeuvsBs<*+fZ z*&$%%uraXnLcq#lV_-8vz{+7`VADgu%3)(*(?YnbHE2+rMuJ;?KG4LuSzxruwq-7)fH{W4=+r5WIS*oPEqa=hr0&3LB4Q z56lG_K5YZB${b~EQeZVq;luhF~n#SPA;Hb|s+Uzq|T=7~v-RR2;Z_QMh^hpBOJ zIfJvkEnQseq`f1`bT`>-Er4tt|Y=$Y^r7 zA-ESs1LiE%xVXHyhF1k)nQ~ZuQC?MHae1KsJanRqL?j*S619m zP*qz|uD~x)@;1pL8o(};!>UUQ3Z(M^QCuX4@$!&EenokCL4FNi4XKWcN5z|ptAjxR zlOw~F7F=IY8c^pYa=2VKvzb>8qacS@@DMRmX>(Oxak;VL25MKPuf3FKZGK*9v9T(z zhNMkd3zc@53XH3y4$839vy3(;e%Zf8k;wb1!1El1IlTN(GA%CAQUqEJ zis;`bwvuIbs5?jy3Cu_sSX5J9P+iRdipxWX1GUY?g|%o)K@|a3uQJL(l~o1TOL_al$|_6+ zwWv;cAyw8NUS6BgW8QdEsJzdlizI}jyBFKNUQ?RDfwvuU@c9`9Fcl{)GXn6 zPpCtjl>Y}T6)i2&VDS!-@kTkr@IhLwVltg{tOacZ&Pay~vaH+(6#QVt*@BV;yg_iP zU{K$iw9ca@zL4Mtz93RXgM)o<8DesSjSdQMQsU>PE?v(wG1<}bf0zbC!nTrv;H-!B<2p$|ZU=6@+ouiEN@^6CnCYZ1uf1keGTI3aDo zx=+}JKyj_b(ZUN35l2kiTKVXP2-0|P%@{>+pv}Zf0(=adbl5<}1=oS|$W#fMWbN)C z-o_V)BSQ<#Dc)33YARLadK~Z2p+%ZlOxqJaJ(Vy%d~z6K{XaRZnl_orPYw%cKQEh@ zUJJAjnnbDrQP8pVBbc?DT5<5}17S(?vK!OIOa0|pu?dm+*iY5V-HS747b#H0`N|30 zlgLc9!v9K#X;@(t9&MgD>Ksra$_iTD2EfH5Z2u{T~KF0W;6dy}PAtg(Yo+ewebpDu8uOc-j)E01u)h7ATrjSlNYv7)h& zX4UEd#FESP#oc&`xSK-7RiooksgQZfaUt`R<3i+F2z=!_F{pD!2C~OE@%90$uz#Up z-{`<$Yf=Mtla>aAxqWz)kIG&)|9s&YtKzzFk^xZZJAZQ)N=B%=ywhEgtWyk}^CxGS#7ZOlLz&HogUKzk(oRG&m04}(d zzEm{s0Z6!7!=}o$4UhxJqf=88qH$=Z>b2qL93KU zQ@wO|$nOsMyn- zbW)RJY;CK}Mu|w(h0;1(L4IU@aFea6NiK@H+FQXT5Cqq4Tu)CM?amU`C@dPtN0OWJ_H3Xn3!ngDX>!>uzEz*YV5MR5crj(Hc>j_9A;v1; z8IZKLIeeo$_&&LFJ$egH#xD)Rr^%})zxZMZ~ zXd8uXAb4L}SVogmSzq}S<_fB#NG#~9n~NEUV*7u55eu02d`c6p5J)J!81xZGDt{J1 zPaL6xLi{hhflA)~VBeF!GXdla4Sw)<&iF8aQ*OL`l;OQpTLP!(c=^aMQm3Gg9lVeI zEuq@;K9(jk>52xRI+P7b(bEsktHa9*)%U=!OVw8)4dc~SAsOSv`$!4u9O~yIFQFn4 z<|8@d#XDL!8DitaE98mCkCoAhLFZ-Y>n7gU9IJde^1h}%m4bP%lPC;%X4G?dKL$}E zAACTtFA>cdWzzU*i$4vm&{8=8^Am+p#sRz^z4Bi&2c{igCI;Tg?LI@E#aU{5>++? z=8b1iUEb@A6=MAx70Kg?wq+uVEL;n4<vI9FZm8vt{^f`MoSa5K%=en|wGxc`sD*z+nUxg1q_tdRdMT+#Y46BA%tO z)q+VnUOcTA5cRPtkYFo>fXV`vks%5EG7yOa5Gs$M9Sm)NEZx9RcZPaHcfFPBMlc4d zC)JDY^4<#C@1ux5;(%KUO>zVfejiJUO_4O5T*L#>WV^&QAYCiRNnSNyi>kSr(tq7_pG9(?zgrq-R zT#!y8{{^^~EaFms>Z^c0Qu6lYJ6g!aafa7tymAbNba5drj1)?uKNwRwX22heD*h}q zNCyrmyE%y2lS=oMJz8*f*|Mn!Ac|Zz(1yQC?{b;t&NeZhs2C_gW&QA|qWpnlzAn)dRpCL_acYr_6t(Z`UFw?pmwE{C7k9ynZf zXvzb>A)|(3hy@z;z#%StQ#J^kkV!;GI?0ggwh^bjF5s}+w|17=8flPa5+#cH*m__) z;Crv)N8HLg9oPmW_qB(c1dW<*gg<=F&@@-DM-&6+ntW>PpJ&Bc`BFO?Wuq(TzC*CW zg?o*Vt@uvI>2wtQJGA=wd8u2J7?p(8q7=S6CUj{b zOlXkpcz}>?*i^cK7MK$<9-*J4`7|b72$-P$BowkWjP2(Z;N*C5)T^5${0_>C(u^19 zwrr4#VPnz+#3ijx5B`s4B{if6|3?@JH*YAa5TPkk%&kdqYC%1KaQS3ZdiKrFT059~ zNO|R0ND6Hv|ID{FxgC<>1$s&n@|rD>6t>uHn9aVhj0W20kpz>rV34ajtZe>#l4H`? z_`;;)gL(5rstfTA7TCFQ`LYPfFLI;ZnM`_a9QA&a@=kJH!1&-ZT05HDu!5BG!?T4> z7)S&=N_H()^B8_|!!s+ojn|{GjoVOWWYCQ9i9vbg{B58fr7v5M^fna{yB;BasjI}n@nNC%tVDHb;<8|!fQ{hVruv}pdJHYWbx?U7O-gAQ9aL6FlT;GLD`f@uAwXP>nb3d0 zgL7jh&n;tZBV{Sqi~0>`oDSHvp+*@UN?b=%a13L1TZgg+VFr&{fRLX&_E?G}rqcH= zKPU`bb=e|Qbv7+EG=egpD27BXL!*c@zc2}-k@fXGHJsW) z1i+t-^h88J>Vj6wMm~l9tvbjyijXk`s)bPTyb#PnDqSi9ST(v->3xkPcC$3y!c)2B zWwk{_3X6oXO(L=a;`-XM7V=YSi+^JJ^Om-YZgIY7Qfz4Xyd=wOo&kY^XCI@h;Ln2Z z1~OzrF;UU0$p|n915T|4QYm%d)6<_`EVFcokPpt6i^eOapc-Sq9p(WFjah8*!P77U3~F zI~(zMT%hb@K-qll_YpR^4=yzZq6`e|Yyu0UjOpdZL^9vIg zvqhH0bjbLt5PE;se$A`^{N*SA0jIm1mUhVaCoc|xS#bO-q`FuolxGxsnKt>htC`YP z6=`U-;e8pn*icsV3p#TC{|NWc=f1tHwBku~(6{JGRU1_|S{k9zEDv-b4HzSTlGEeH z_4S+$t@CMGfVR@3ANLy@d^i@kd{S~VZI|Sg7RNFO5pFao z7vr8ScLSp_lIs$@kW)>@sS8d`;S;16SEUhmT$S);(won6D@`o77guEg?ggKG{4so; z2r9Bd6=(s+Po>f6bt(`?~_vAQd4s}u>^8!RY$KS^) zhm)k^ElZ4ey7A>dvf?wXKO~=3GN2JaKPh6?fNirRXSFmmNZW55Gk4%bidIh-IHg7h z*5ax{qX_j5=_HKu2|bUfEXdex9haaVRa|UG5>814biBL@>HLi((U*ZE8*MJJ_Q7Se zbF7q4h$(;kR`K&1al^!mw@r%ahTb=tJ))33N%F14_XmQSxrOTFIvtnhgG2c{RE-JpZq^!K+x~l3` z*WYmCO*h|CTUT$jHQF8Pn_FD1?)Hrxx844eJMO&ur$4{%{s;bZ^TUrkw)OERe*N^b z&prRbuHU}W_4@DLcyn*hyZb-<=#QWD9ys**7hnG6uYdEL{QJKKPvZmTBmTJ%ikH5u zJcs(%Hw7+*#{?LJyOlSTfu+Jpq;(UN3OFI6p$x_QtdR|lZs)2Nw_|OFrxl6RGC7T+ zXF{+1XQHh0eOa?AIwmGr3fOPN&_d|6xm&I#lMWl0sWwL|32R872Lk(QvJi2*EYb>v z^G9N_84gDHo@Jw@wV5ymEW3ps8f|S>JMLAMW(oadLc_fQAzbcbxXby!>SG_KqJJ+T zla|1!@ge(=DxVHpkJe^|N0Mny(C&e68;}zpD~h3ksjl>i0J!*pcI4Lg;GD+G}QfBy-UtcCdLAju+Jg<~Ol(P81E)@+O zNrpwMsa(fxIJc@4QaVa$zZH=Y}toEFk+R&+2RL2f6FPD&w!tJdfq~>w z1yE0UP2-+gs5-o}q-=1E1L1K?n`;7G(_;q{WS5lwsLJWd`Ter`Q_P*AVS$**fF zs~2TvWgGHDU*yBFc0 z#cGpIjrc6V2-67?Xb^c&@!=g@aE)#iAHz|s46j$9NQ2VJ3PHXxl1kv9zCYDeD1_)i z;Al6mg%3)4s7A#`gR=aJva-B#Q#F;61U=^b(qe8W2j&cJ)186D!3#}c|}cdIRrf_pW^cCQ43RTWgabJ@^2K3F%R(-#biXHK#a}DR6Djx zqw*;&E-S97Mf>xMsMXSM6ij|q{@9F0!BniOsjV>AR*|8KlFjILQQLV>`vCATDjza- z98=gR7@Rnb0U4bSn3K!GVAZ=U%&NSqnlV75>XT=>e0@M2#$+?L&Q-N#g=N9zjVcQ( z>t*wyel94lSXEe5TUJm;c0XhL*<4&&P)mU3idE&xz>g|R!9!C)C61%y-zb=B%$C}+ z;_9-zn*8x#FvTQlieQff9fWn8!fvhK5mPlCu4jJ8NeSFr4HEBqkAk`)I)u?|2aK`+5x zEcrU8=+Kh>FXF*S$m+jHME=Q$*u(vSye3NOzH*+@%fFSUtR@^H&#~o0Qj7EDv8DUl z?5``abfICBR@44L+98Zn2KFf9*ULml!;~EC7xfA%6zG<1oW7G-K?!_x@A-I$#?fjt zw7wIzDX4_|S~}98FphZjq~l6T2{LNfV5AB><1z=Eo3F8y2U!&dN5{%r#3XJp`1jEZ|m+crh zoE!U&fI0-lHP~Ejb~$gUx0S3V=de&>z*z(DH4KBVDyR!DCy37XoO6O4u_fB3PrK(`esDe^j;M7t&@8NyV*-d4Dk(oUAd7Nz&Q3;Hr zMg&qoISyw@5ZJ)@J@6-O+;V7dCkuW!3i(Qv(@IHM4l~A3pL7l3h|AQH~Nt6_UNQK+yYmj=3eE!lMn{3i3phxnD%MY5Y zNC?IDCIt>r3Y-LkcLj#xlzj?DvTZa+54;_%`{~n@aw~O|4&H!ce7t#Gs8OTmjKW|C zH=MsI&0!(p0-G2rUTJoyIHj4qP9tb5zeaw;Mn+gC>m1ql$B~hd8w#pxYHKPgO~qC7 zZ?n1Vn_A~%2h`NwU{hWjS{>vVcK%wI!#R({FDx0G$F**8u9%#hxk_BVT+GT$!9G)Y z$qIbQ4t!Y<__8qYWl>5>ig?X6BG)s_tFA7nA`@N>6;+p}* zq)A1tJZKw>nm`$nFMRHHm&@6T^(``}j#NdtXnI^oWyM`XwI3V9^RFaLq*X3XT=}z! zoP*$ewjDNjw8GYtD2vz$+Aub^!<9-4JWIHnHu3t!CHs3w>Yz-tPn%?kl8C52oFI`!XR3(63IH->RMMIPAHaZ}i+;#zq3#iuN{SgPM&k zgu1~X-j1>J4Jq%FEZ=DOBjn-D%n2B`%q7aWjmm_hMJ6?lN}w^5Y5_sB4Z;^pmw+ym z-slhjZk$2J;O7|LXV6AFROEu{J$@ae@sx@kT|+*ma{2zw99iCK+>o2ALfpXhcOWw1 zIz6M%s(e*xDZ!8F_tA4q?pj`6SvwYdsQEy7Q8}+h?Pvsd^4wL%8p8@a z7!$ofE@*VLGTX;aOIF-~X~!b+AgzYpHtawDy*iRMWDrVoSIWpKSBp!H3!lZv zh4ac>4P*Q8z7OI1@V-wIXm2O`ETYY~L z&pa#EI(vJ|`dZo^CEL?QD>aWrw71wgAP1z400EP@i!{?4ob4cK88{!ZtxVyE*z&#z zUa*r~k*odnw1- z7MgC-1CBeTj6p;qq@ZTPc90K?EOi7DWzm#jk_>bOZ&Pg}{4+yKj)%$nI)aIi8cfU* zq?K^uEV`R9GkJQ7s&(Hcu7DpW^qTv&WMZnEjmcp2+O2KkZM6hJlv(aqLoMgJ(xCa$ zBJ~6hNVcbl%de5D;l2%XuEo(ri30a4^{C!b9DO(>rAUk`SaU2O&`{%b8E0(U+Z;{q z`OX%YN73j)R?dfxR?b!MPY-oUDEsjVxWNg}XsIcL&^{O+!9t$~ruBszczB4iNAUzy z?RqkTP?lUw)3D`NVXjf$<*M8a?-ur#9K+w9Enh_3{OD;(12G2{Yv(7g66fY z87pvZ>Bh*=S`wmug!fY4V5xL8Zjw4j5Y+g7pAZ7x-Chr$2Ut8 zruX-7{aShlQ3!emaX>r9kU=Y@(UHp94hX1+i5C1WIW8uJWKYR9- zC!TBF_um(`%w4o{=;H;y`Fily!c9Hc_K3UFV`sJ8^mNs- zKfRW6V*WkVee56N`K~qV{`k(4YfD#u+WW5;&*hh8|Fqz&>W<3gPwl*JNA52lelq9G z+rRrFPCxSffls2R{=N2BrWyO6|H(BCx6E|c-C6NveCph4(X#M{?w`Fo=huJrZk_Bn@}G}vzw+lN|94gLUmkeoz6m|w80~r6GA8Y=dFu-5 zj{E+uV$JPswvJj_x4H8NkQeS2fR;=V4)`tDC)-E~(?4XG*-luw;u{B)6hs5d2PVG4 zX<+j}c~?d=u?SZ#E_%+PXxth$2gCg9N8U7Adh~vyJ>e z=u1cItzf@e$YLAR4m&I-5mwrENQ_rD)&^Q3Ksy|=0}cY>o5v*RSAto<4Y|Zg`Wuj? z$cqP2TPTQtxkzeir_RF`p8hZv6?WONZg#cPz}MnV^#YBc4-`mx?a*W=dh4)?WtFRN zJi8X^KR9jVVl|XA8n$5*Bq`$oqc#y$wp-gtH3R&GVp31e^V@w{C;NC!3Qj>8amiA# zYa9(7x6Es3tA$ZaYY=A1IE8}?$a3@PR6&|brW%q(!^s*#xNMhFI$%sq2D8fPC z#0qL8`8G7w@ZJRKBkD4!zl2W%pnlDbl?7BmLx_zz_R zisnUfI(yH1T4pZIt1hZFFw!o-jsMyZK9{M86pOdxIi5vL4l&NOj^MJ+hkQeM&agY3 z>)jY=w25;B#QJ9~q%9q7)eMA!h-n8q>S}{tJ|_CyS}f=sHw_(RJx4!k&o$7iT!0g9 z^rg&NapecmFPda%fK3``kBkW1 zzo&%bfcrOKEcKw5$^5e-q38kieT%KF7A)g@T%4RTJiup}h0|<2XRMGmtrGKt`bl8` za1+iXCkL=V@Pptaw=xXfCNXWGBh+&g7D4?0At^m)UgWDaYsP&X{0OKQvLX`(RF9Vp zu_%o(ZvJ<3zyz`V@Cz@R=0r?C`sOf~SLHhzOL#EvZUqCUu=%ZH9I;=kMJ>YD2p|AeEx~y$= z&381ft>tx6_{hM@1_Q{DwV>l0z+SqWz(b->4J1C{FW7H$83j>&j9qcF%juTDwME?_&I!bj}ae;PvWsTxRUJc^W=-oOKO+$ zLAcv)VeKxsG;6Ri;``{Lbb%M~n1S!~Ogto&6&OxoftLbDVf0M-&=ogX&CX1rP{#h_ z3hB;YLi{;0UCpon;p=3WU+6EWGfjWzfpt{Rcee^NO;dXRhRc=LhlqRl5G2 zUpwGZM!}ZhyOH$!_i&BxfJj6$pOD|(?BckejE6Q*i}n@_C|2WnU|&eE7EJb1q_vjQ zR9`-#463B{Np1XcBuQekd%u>Q{z%JqMKHEJQxK&&hRr1=iWlMkFWOLBFR_#C_#gb= zi1`@LG^!uru6)bmx&NoVYk`llxc0NzY#_YBpokAPD0G8)LwHz77DAAeM?w-L2>Ov^ z6S5{GkqwY2TtXGA^@0Ah7PMOLRqL}}ds`JPDiV#OBf3q&hb@rp0*|9@t_ z-EVi3fYi3X-a5(I`DW(Knfc~DXU?2Y)~_ygtAwGBPPNNDLH!7RBW`f3Yk%lg9fHTO zgtUa|D>t+b9C^$AuGueq=4;FRJ$*az6W#Kc+_R{)?E6<-`ad%tKIN2MX6TM(xng@~a^XyfaC8z-Twa0zrW@XGqQUZVs!-s(iuD$G$jgJZD~M;0%i#eT zn+=Qx7`HXQI<;CRDh#M0i*bA=oN;+}2VSB?pquUFs*em`=QkPRt2FKH z968g#_pi|?KQr(l?2jC7+y5Q-DF_!Q^E0er(?gz;!fx|`?$-}pDCZ2f>W=|`Nrqbu zMV+k~=~f=N*TO!I?r*u(Vc12PZj}#v7Ho#UjPRbsgF72^F88_B0OG@T4N_{-x817O zsY<26c4JIwM>>hUp)&&Xk}%O+jz~RVp8|IuEPB9{d5``X`4|lYK^(T$#-d=o6$%E>Sz-fxHc>}p zUCFg*eCXLTisqMF5U-j%b!ySn$k`CuE%+~)SaU6H#G*wXG@5?+*3EhSrn}r*U;W~v z`vzY7(fg0wacJM27pI?>{`e0+c**^M_llP;AN=CgC%2xS{ocAQMK#M84lX2`*Y1ETdmgx~RNc zgN4i84Hkx;WwjP>^;HEVu(i$73@r)Q!R*(PDBFuV?ZT?g-dk}qg}qdotk1!Hr4FS9a{`p=(%Yp+%k8;eboJ>F}J2lFU53w8i}extzu%bS^~=~wsx*BDfVASf4f^rJgW{D zasqA;wL=qs}k59vt}Wd7pbn>5WqK;tJwRFv!42 zVB$y+TT2Nt`#;lQ%&7qe0%BoqWo7a8m=`QF2zpftcd9NAdP8>`zRrmB!Ojmfx$9*H7m5d#%?GK zz(~PXk3tL z{EYl%%cPoAs#&n%eg!6tV5=`wV|W|s-ts~$R;Lyh;)@4XX%U`gpCx@B{aX|`A~TKB ze;{omJ6P1H|F@BS!bCiCpD-S_y-yg^S9$4|En$4Kh)IW^y-%1Zcy8r$#7#GGz3^L$ z?^R~pWcNF_I@y7Dy1`3<-*)6q@yz4~#;q z8XsXmIOmY^S*2bMBTcx!SVvL8UdV|jY1pu|A%omK6WpGjy;M(lx!j2^PogI|sfWvz zl+;tb`7^hAZnImx{CwnHiyn(SBTAHc??H?a@X5JP+jsm5V*uRy{!7~#pv79=bH8?9 z`vk@ixYru@7od#;akb$;b@(LM#1B60R({xTKBxUJ2YvdpzIl$P+#IRM^*~FZQ=S+` z1V* zGiM+aDdI^y?!B;X8ZW!}+ItS(F@Djw-mjjs=;Ft#0yTap63ZS3U@_nPs#_h}k$@|5{& zvvA{vQ@^6*Xh#0xWjNWgs2Hp{fO^uNat{c|#Kr24yi;-!u|k2Ur4OFzI2UUbv7yMm zCAr9!xWJ(k)S(p;2g`8^v0+_oWOV{^<^~dc+gz4E--Fvy-EWmTeU9=%l*A8jHR&z4 zDhA92i2EJ4`sv$lwFd55KoD>NpdCQdO<8a!)!fpaWo(v3o|MB#r;jD_v{?6G3zm^j zhgok(2i8IhGVbCpx83vtZ{$QR(X53yGK3)&2TV+4gFJ1Li#zH8@_B48p2 zX^F4_dNGbgMXZaYWF2qZspwR8SVleuW$J8-!Ldcbi0jOQzUdaq!hvj76NZZ*j-${v zwHxiumc(Qm)Iw?4V^Dh>3emK}Q7sf*!&u;?YX+kDmmzhOHFt-oT=c|Bt6blt!X7u0 zd#1%w&(5Z%Na%oOnK$~f%~~h zH|38yAAM*e{h>(vB;;Wz(mn}!So?)eH}A_x`j5b`n>q{ThOz)MR_SqS>2m3nV52lP z;EW}10z)1dK|~sH=4gd*4JM{Xaq0=r$4rH1r3USR&Wj>fqFM%+-z!n=0RwMED0h5Lu2OeF z&Zh!#Hx5!>(&7Sez#fmB1F_uFnTLKo(GD;k_m+`I^`#Bf8i}5y9xW@D$Z%C-xAV;| zKVo!l5N)qJ0ULGjkkRfmHI3SaG}Y-J;iX*uOYqG1yA?L`bUkgvN$ya`yF1hgSsiLf zvcfZtsj`se`GXSG_5q3AwLk2!AfuawX|Yw$mwY16IT?(KW{P!EIL&EN#m)veV3QbXaQ}ID4``=GZ2*%}1~F9a_!IXIzqOs>*MahDV;NSX-T?UXZvr06ZHKY9(nhTG4yDfM zP_A@@VZ-8@oRX+cJSkD_gFSdK?Bf#ERd}8cC_WK;UbrF3ChavcekJ%39jyt1feyol zE0dA-lP23YH4$xEsfyP<_86)|vlIM_;Kw|!gw48bp^ZEqhfZ@shw^wkRMK(SsGu); zyg2sTSmseFX!-3U6V=_I>l|ZH)>-BdIdZ5FnMSjCP@pFb8FIM3tZSVra&z9o2;WMJ z?L!D91l7|4?2Mghbbq(N%>I~9t6G9scHu?<4STU5?;f?DmozNu%q2 zBmFE=SExMMOGfHy?reVH*l6!Kw*C)|KjhHJy$(Hgi%9So2w~FC3e^YeTGJG|m0U!y z%vEV0ZKy@%SlC6F9Hw>J!?YwJ1{};RqtZx;WR5ET_85uvy$p~c$RA=p!?|&e0wal; zgaS7>A^SRRnl1h6qhRb&rx7udG0c!My;HA6TC<#?}7sg9Nr^S-C&s#iQ}(go~~XHnb#=NqGvdi z=DC@AZ1|hBH!R>;m@QTtW)@_hXER+-1bXIhprRj|qC=0z3M+JEOR6lh9OKbaD5?wtU>>j>QHE_i2-iGlC*4T){~EezO{va{g(AF9 z*?rzPG^2Zn3@Z+clmXXCHs8*9CuR(dYZ*3bRJF#o53Qr>C@DseqiNzEN>nk%O+Iq;5LNBGv4Xmg z;k#m9u~j(@UxcryEUV$2HJGojRSc5xvW+R%l;FR4(M18$(=VjpEjwDR04ug(eN*_v>J73;77xNve) zEP8JoopyvjZRDIqsDSymxa7vg1&4p+WssL8&~?K+b9e1;jEmWjMU_qlLxd<~Y(BZhd+McJ#;Ofc)P>wMS; zu#y$rPs33xUmORw=43W1KRzNr5)k@0#6M=;u5ZKC#Vd6|~^7PO3fBSrhr}lUhbv!7W*qUVYXpNtMNtxq>;Nz zn<%=z%nm(f(~GkaU}WTj#RM)F1{nv>Q}JG5C%!sPEV8E4RVo-~c^Sx}1g&qR#-#^N zB4=3hO3G$fQ;TL!p3f`o)|BE3ydV#qV$+cP@ZG6Z0k)H-goUMY4beGsi{?T(X$6nG zr)t6hCLT+;+cMOn9ZQk-%h|HA9Yo#b~Fe(2MvBkH=1i>^u_{vrj~R+18;z9Vg$GJKifW0YJ8VF?A2fRG_bk z;M`v>t3ap_Y(2^pR@WMBn$$}fY1du1U;A{m!4yQj>Z0$*m1@^CLl76xcb8J*3b48Q zd<Y@{e-B=$WvYzPTON@-cU>>@DmvH&$m%=Gq7v*DKK&Q^MkXxjdLlt3PF^;G) zQ@ZSQ>B!s+?mF%sb&-5JTAs~$#M+1LNy;CM}+S~#e^{hj$3lXa)i3Y**fYq z6_bn=;N>z7a`>DDTC8tmYjZ|E{nEOj!LMKFc?arhns__3c1hgu5E;)9TGdjIn;ZH* zhl$ha^b;NfB{T|Oiw?vm6G?~JLh(?-fH6%vTvf!y3a10Xg%DHyj2A7xcRPw}ZZzQz4eyOoo@?G~`Ec|#Q3P@UA z8=Hilr6LUrIJ{!45u7UCuug&&JgR`h;ANJOqJ7RrhLZ3Lq2xx*8Bz23mR~>e;U%}6 zaOY1yeQT2~_{VbK^F0nT*I^h=XW0?rABp}ATh{L^n(*%*-8y=I#+p^`6(9I-uX*#W zO`n`KC4J8a+fQgXIOmCR_5O#Rf8o5GhIyl3IOF(R`mg-X{f8%zJj`J#)_I_6xygo^8YZA%kFbyxPCm z57-QN;*IBz_X9Qu$K97xpE`W>FZ!p9u221s(c^30%m01<;)3(mUsUk>{?8SRLBtnu z;h9%G?7e1QCw}nyo0hu7f+0bd&U0`}(yv<@(C zi9}ort)vSbU{$>LYUDzwAx9$e!#p7`y91TtQk@ZZwN*8uN=KtO{4w)#dtB=+orRr{ zToRCeX2-+#-MS6OmyRU5B#oGbVKrQ`642j9;WvYI%HjNIMS9`vsG8O`$F_mFSE?61 zadALvpMWg(Tgm6Iy?^m9mwwnZ@yvrIcdRb!H|NTF&rSEfa`CVex9wiA^A^v4_jq~b z*b`oB$(}I3-~ZY$*8TW4vJ;$D$!WDVb7O|j^cbZlP@>I`4lk99K*L9bhKEJuM6)=3((#Ed=sO(|7!ewQ z&1f-nnnA3pUCpkV{ddcLfQp|2t<6=){FHxG8A>J=GZ*;sGjwioSD&d0RcT3iapm8) zD3;(_2gO}Z%g8>%tLhtPs@l56MbpkwU#1^AfA!emnYXrY_CKEP_WHMcm@5;*FcrXc z@aEM;vt$!=g7KNTho2X_mhrUaT3xNMST^HNZU;hb`Km{Rx`bFN?8##=pl@1u{h zPl|7cGNHK0pwU2Jwfz)lUyJ*|G|TSOcDb?dHvT^_?zo8Gc>GJ2z%>cV`|#5)^%&p@ zz&^l}fDZxB0S*Ix1MocKQrltw5%4g8g&dg?n5g5jre-Y1wod#_Gy>Jug{yNMnxG&~3(>>57 z3t%1ym3SsBGT_8LmgiSgbh&HEFFB&+OG@NmQ1EbUPB`Gh#PH#M(3Ahm_ z;Ea1L&y#UC#c9@qF2PRkE<6(+FyO@fD$k&&|J4icjVVdUa}~~?QdCx6SW;S)0Wo!@ za`SR>bMi7OD=Q;p5o7#fw%4I5Aod`2jj=Zwd#|xm>WMc<`HkIZ?DfWeO6;NPfN`H3 z)cDiI9;#Z6eXFsz8v7GtpMJj1N2#$_8v8C|?=ZG%(0J*_t~B;(+c&*qwVgKj5AP2e zbQ&)1x$tfSdMa-+n)5Zl!39TESZ+n$9Zw!C1_+DZ0q(b=f{df1Kq(uX;kF-br-}Hl z+j4LsX)BM*)5DWMClF)Mp9J+P9sgZeb#<5enck#c^Za#nbp`9T%=K^EIuY7*8Ka=nj^Gt+)ymEZ`?4!(Do8fj7>Y`{uM%d z?fwgdcG<=zp}lQuqoLj4M}&eKHv~x|AMYr%7p|)o+9~(X6WSY39YH+VKijmQJlG+$ zZ*N&8v@e)+q0oNvV4Kip+K5t6SGQbf_xfE(XhWjW&|WOG>*}TvPq8*^+80dvuF(G7 zZ5={8ZQ*L6eNoE43GHoLS29ik(`3GNnj28=%Y?R0W9ua5A9zYO)qLpIpn;0%tPMFM z#)d03opTJ7UI0soaLvm5aD7d*zIX9F#m(la7u5ro*3f*cWN`IsA#=+obzmjQ=o=uC_V++sr(B>v76^jd|XP z=gs!~o6P2qYol;WAvxSq3dU_nE7va*)Ek>TY9eTy3C~hvrx^Q6W9NOIpjH8BuKyL^ z#ifc<%$)fw^svBxguOl!tmvPohehVsUp&XLEJYCdW=8z!5loMEPq3ohX_XQ8yQnaN z5W>%cE8Xf%=qIYWB!ZK()~)tk>{gd|Q2(j+9|C8=?tdi{*v;}co!Id(X^7o3C0MP` z?7VC)!yQ?c-@E-#{{WlB=XfS0f+tC^{ZsHvp!-N>r`?Z$cn$6oUj{f*oUc2)CkP%k zMD12}7|JJi;T;HX7$EPJXrCk}shxn0fUnyW9)aLLd-x+I9@T%QZjYOwO~U47lb_uk zke?DUY|E3&3yX`pDXk-g>!{mLLSDc}|74)@9stmHFYIei@u=5M_Nald=L0rX%6NU| zW@A5P>}QQV;5@W>(Aj~wTLI4ko&qGF0ex+wJnAoi6I9>?mn-^QLgymx_i!a>5g0Oc zl}l_Fp5?MJ>^6iE@TfX6U1BqQBYPS z5C?wx0p5k1;Pg|NuLO)I@i>DLA~!Rg1jli>8@ zyiIVylkfnZ37o?Tw*RwuCcJ2XG#IuUZo*yzHvT7Y6ELl60503-0JsT54A^vha1*i( z(4V8PA8rEOwyA)du+V_*PrC(vgf;{8gQnZCR_LwJGr17D0qJj>+ks2C&j9`HIJ2Nv zFa2I5zz$NT9K!3*J`_p)z$GWb`<>j+&`V3ZHLW%+UyYNTm=f1gawF!Bn zK1?{n#t&S=bOQ`Xz@MGYCD6ayGbT%`%!h(qNhITc;chLZ7&z~_^#D^=heGkcLC?MLn#-z`u_HYrtJG}Sa98@{in`f zcG3rXo;j!ariuFsue$ez;cA-7o|@o2zo6%*gYW$Fy4s(geciox*WDJp>57}ro%-V+ zZvEb1|9~g<{U*WN=gMD=+;@Iv(&p>Vd*$Kft1eji(zdfVrS-}B$y+!7`j^{2m{mH% zov7D+xffouVE>K}lKP{Q6S$U!a{gc)B)>AxhbZeseS7hMYB)Zmv81Xs!|47Wex^4k z-Gor;gYI!-^Csj>7@s%J4&X^h2q6nJBFPwPe54sp)n&jKUeyBq|M;>HBqN8L=o$k! z)y+^EMx>hdYFrt_jmx~;@nf>%@IuWs#OgAfs`l;ct3VtxVeHs3v1!TY2ymk*P}@dY zog_l72olaOD)4R^@p9rb6Tu5#Rw8C@9HuEz3ts@3Qc_YO$!2C}W}neX;y8Fk_~^XR z|M?5T@6&d9L-$x=soRh z35!92DGLF>|DOMU*Z;*U(2_c7J;;bC_7e6TGT$~N{hP9EXuLSV4960BoBEy;X&J^U zcXZd|inJV+f?lqPX^iD%rvzx)v5TEsOE_|3Jr-_(U#3qwgNA2zZS=9sV>UT*;6#`Q z25VE$^l<)Z+41)sB8u{6W+PZ}z#i`e7$%a+X}@xrV?Si6Nu~?KQAQ?6@Ab<~hbwx& zF#-(&J2t-%k#yYnL$<$z6!6(t@~E9jS{l=Sx4JUn^4r2mud?@_(9~2n1zk!4hsvP4 zmC$NuPQwbHl6lZH5oRPHgRmm{9%rJ{&Xh7DGw-ePutl`6rg(*VBueXSKO%GwwEz8JLinVw5 zQ4nG%dY}fEzoMG`S6{}c0#iIf&=YylS>X>RtSfnlk~NTRyNd>5b}qEZ+?NoNHFTec z8z0+hRX*Pe4E0i^mnk<8%i2>!4&x_J#RPsJ0Km@=2te`wgIfF~?Dm^~M^XMCC}IAg z*1*}!)`fxoKllHK*8htk`hWa-ZF2uVw89GBgnWg}cPnl5W0uG>n9OZru0ufTNXepX zu34{sfAOxZ0qdO`NlY#*rp$S}cr>nt5cSEhb=@06Bba?{O!n(?sJXahK zU~^8M&A%o{q^KrBVzkhrXR*PPUL!N7`%sRlMW2r=V62F#W|vGzn@jSY)K*_I{aFgL zn3h+pC3Sots`@?{Osa~XXFvnR_J<(#@GBS|mCK=a4 zW8ZeoUwD`FRt!gl(LxnLMKOH%n(HEKf1gI5U^tD(>Ipsk%U%C}d~Atw zbi zKA3gzH`wZZt04G3{drw%_sE!xspp8#J3Nv(xS86L4qd#M1k7 zIK4bgL_myQwh@o~$?();D+|6{JQHS91g z{YQ7YH!Q;>=;(Sl7uk-|KF-$J+<&Tc0hu+f%Fi1vRq{4ktARJv)8` z&^l8Sre-4t6pf{TX#Kdkzli88J4#Z;BK5vkpDK?04;U ze*8cDYv2DId`-_j#|^tR&xX&Ax5w*@}k~f5`lesyL{blwezesR9(Z^wJ{Ew1j$wRHf~S5DLo953D5OqH%4CL z$W<$ToruWU2SW(>ccfTLPlUmR%!&Y6Rsh@^E!VVoQ;FEky1G4Ww;OY7!ly5aatEmY zjU+7NgUyJbXD0=j%FTb7r-pcfx5ZJwOcJt~_4NhJ*#Z^*YtBeRe zDU(vAX>qT-y&B^z8mGi~%I58YdUsQ8Jzw5B!rwEN?=QDatTaZ`o)1mNzR~+tVw%|D zdT>MkCe%#L8k~3#CGYBXecU}d3XDqR(M7sUggOX=nwzY2E5*Se7txBc;o!U{^*tc>|C!z z!XU(&a@Qo020H?NuUfiPsg}GL-1KhIYQ(I6)N=@p%3YvK2f=q*Z$~;0U@LsoZuZh? ztY)yZLVq{E`+*N%nK$a!ng?{~&7iNw#5`SG7`u1AvUSXV+4ktq4!UGCKRG`;y|g772B2Oup!M_8AeWbVVguj z9d&P<-x#cBOdeZ58}+<*X>wFzFNh-#H&=uCFdpFiK?LlB=~iu=D8KJ1R5^PH)Ccs! z7aeTZKg>aVUSIfssZy|)tvOtlv4Tjf{rU7u4*&+m%VCHs3~ zXou>wc{4XOfpRW&CUk;L8GX=2Pw$CH=#uKC{Q8x!H1@K&oA2)Nn7_|W_u47&x$AomONG_PSShkqN9lNa_$={>4DEt$e!aP0 z&VN4Ld2)7VlffJ#Z*jx|2-ubjd-{aI!s7Znx2}=QX_*_84U8xg6O8Iq4Jro(?LThw z-`|f{m#>67% zwC;XhdTVWc3t%AJ=+WAPWUu!;wjbYTMCb&fo?R?1|J!MQtO@CZ(ER4t)kC@Fu5WJ? z3U7kH0=kk3Tl7EGr|nAh!@b|FuNYgbV@;Aufv=~hw>T4&q=u2`Pc^z_$+4bj#BJwl zH3%sn8ZhhlqS_i8Y#5oZaI68YJYZZ8TDc=jLi*UUzLHE{5B^GyMLpa(Bh*Y9qxsS?% z+{mtUSo@UJCA(v5Jjr)D@u?mKj!E7tm#j_T%Dp~pe?AM<9=ZAq8!FDt)tyx09UwLs zpy zvR!H0Lyy)h-|NV6`b-EbqJDpehAC(4^l52m6!f}2s#^k}=Bl8yamI8^7|$z$@XH`Y z%OKV)Snxn7iNk71ms5aJlx17h8&|28p_If1*1UKKR6%8D8AU|EroR4`u+9&exiD(d z+(0IvU=NB%RczYXwtfqJ@r{IRJDVUv?lp?AM)EnE#FYTHTGFvrK$ERb#oRu{ZjhBa474E#!uC=5Dvj#IGJvhANBHZt;=s%)$RW7;e!yNR8mppN`kfc z_;IRhAG=m!}WHQ!EhU&VN)Les++Q z^gD4c4q*nCBxLXbBlgF|mb`br+t+T?At2DfXylR@nfOwU3xhcJ$rD^PZA>tCP>M2Q zi`R5&5gzB_v(z=wOze%p3EC&AXk~-%!`r0QMH5g-V<;}cKhnepO`lc*9S)$W7*e2> z;q{nI7)|Qt7o%oFFEnUqDN?P^q(^FBzUs>e zqy#UBg5Jv}95~4A+Jf<^@8IKAE-+-Mtpq0qDM{pkjJ#;Yz{kfGnicqRJy%IbDvxbZ7bSy9B??Ou9vrmU zt^2btuUL;(N~n%~dhh0IjT?MmSkD9va3Fye(RD6uHbQFF21#2W_+XjzS2|F*TdIBz zDSIbA=L=*FK~25CWn+wzf`+C;nKKo^p2B4x!d*IR4n+-NXd{fV!xTG76ppAa0lk{3 zxC!2Xj5kFmX;d4tT)1qoc|AwB^~0<()x$_$(!OfDe&j_A=nV#`{|-}Y^_O91k1Qj(Q_sARVjbA#Nmc@Y^59z8e`oaA zySvMalJ{?Rm;R8)-yarE@%NH7o(a1_#Rp*ssIYOd(R81f+kVV1+9YX% zlx+&eQs_g7YGrVc zwaY7AgBqs8%Sh)5PBJX)QfVC;yLy+~(;AY}T8L1y#VAn4Ytgp1cTjF?2$rb1h3X+x zMf@=U13>1ZeiqVP7zR^}a2CW&62!#I-pDv@yO zSW|}47H-$LGT(^2yb0doM8{ZDF12zSS{V-S`Z?HfJP(3(c3xGLiyZ#Bx7$>QkDBq? zg6gPBD0+SZBFOv!w7qxA0X+`|Y7>k~k}iT&A&`un-X~L^#~u>CB_cnYeKZj(HXbpP|pvVvLwU&vHmq#i+C#V?Nq))^Hm}hY`2Re-S;8p zTJTh?K4R(@-nFaazqRnCGW9ZTY|eI5dLzxy);T1$58->XZQDkqyQWd1Ng!M3E@q)P z^zpe5VsI8QSX91z;|iZ?G@%8aX6<1Fte{EN*u2zJ+t#$@t^<#x_uM5T~Hg zONTF$OPm8-agW5!uZ#|uKd{b0zQ_+#T+uV{h?g^RX-%xlmlLwKR?ZIaB97yls+)#* zK{bl^Om7fpivo2P>mMzAtlX+&SV~y`FjE0&gB&Tbqm2-v3MO4dpQ}sp&{cU;w3edV zLbtrmpn#+d+Cpa{N)XLN1d;~QlvS6k%f=E*wMq6@|0zC zU=hw;t6@V2uUW;fpvRMl4V3;W8??FI~S3DkssoG}D3F#`S7Bw(g~CbCbq%-8WV2kZiZRmj83 z9;yVZQoPgwj1a%g>E7Rb-wh{AJeF8bpBYbdbNf6zpe5wK&{=y-oAg`|(nWVAgW7=E zI8_iN4v9l?4&k|N@VegWB!mx079c^% zu^G1}V;Iw$_HQYrYhVGJ0-qgDZ073~!zU0~i@fA7l#)k}VzTzB&e>aF@C7eGr(IX$4!-f2y|oP#XmegU<8#EuB@7)|5If|kmlLUmn|EU@ z;6f$0feEmD6bJHwZEGnHEQu66!wYbHK+7uj!>c4`7#DX*ivkui+VOA)hlnj7md#2D zQRLOyGh=~Ae@kw`$6JZSX1paisZ)>cy*k#)guBpqMHK@17U%h?>l*(ZAy91H-B*Md z#UuqrOmb^Z$;MvmgDm-CQ|TTwj9 zg;CX(?D9hWyY9M*NGWCI{|?tUgXQh%_r`lI!^*VvP}^P(PVKNj{lU`A09I6lepGur z1EVC!hfiSdk;P*LF&cOT;OaU5i%${La!ky$8V@@PUO*T9Q5!nOBFboS$6Ik{5d$7! zk_ZhusL5p0&lz-k2e*ZQ3zNqKKRA@TYU<00?vNn+wAT$zUvL{FZWx0MG(1u$>uuVKU^;MStX>jp70QNte%cWZ*R7~} z?TDdeipVtUw16rdk~M_@QYeZ!i!f^rM{+xN?_8vW>11ZJw&o?LW!!_QvGZ)RrlYCF zZXD@`T~SgtDsqllkxz}FR8bVxh|1mE%!Owx?SfwboBq-j^&qL8IdWe4WD1C)DL+su zV;H(eix@-871W!EP?*zWoh|=r(4DR4X?fbP%=cQw{3><^rc8N?A$-IiBQ>t&|GJKB zGzx%!2|?uxJ3-Ne@uGChKL}~vi!1|;IJKa2?-?-`)y^q+#>dVDp*DH-rXR5mDDMwv zD(6DLr%<^gmCp!!IpnVf%ktL^ne&DPqc0a~ zW?^AL|Co2#-I~=M61+91$(0GoKkz#6&+&-4J1~6=i6;$!EmHe_$i3+Kd&jukx*5EeC^jQUG`mtOcIA;3gf?JrLP6LRD@Y z{xJMkKSv(ZV4P#rP`fFZDBrN*fG@+Lwvl5v3;^uW{uCEMjEWK-JD_y{63ZM{M5JFN zp)F1lT+^nB1YqzB4Sbg@3UDV%F2BYctO(D|WHgiKRLAJc%*6^0o#7skA)?$TxkAeR zH!5>q(k>&78+#B8DH%AsIRXHY7Hlbu=&I2^o4F6{JWKSrK1PPmT)&js6yWXp+Ji}t zm^wtAVzwe{H77v^vN1?o0|#kO%HX+9v0fF+bB5#Juuq1_d1?{ANt-o2<2hN&civb{~GcA43JP^Fn$I`;Ex&t-Qysi<++rV3v#Lx1< zEYZlwrGUt;Oq`fE%c|z36TBBlNVu@xkt;Hj$=RPzvSbFi1)rr|%sIxP#Y7I~eoM%V zC%V_K^4i!o2ArQF3K0>{tRS`XU;M33&5J#X1~My`aUXL?Tr0ytECQfix9qweHyUOV zf}+c4g!O_@Gm*eWH-(s+7y3#2+oq2XgcEN=>u(Ps8hXba)C%~n6GVeXf8+h%Y?UEM zEP+e}1xF&GNSdhX(k(A88>;bmU0ji!{CaY1J42Nz1H zIaMSbK2L8{PIk{<>_YHbWXv!kw1~vRQFVvVye(abJmPlox&Y=mEXQA<7-PZ^e#Wuc zl(-`KAn-yxEbunqdHH{pKRX<4Z;wX-n{g*rG`^i5#KMp{$)EWIxXs8%w(LZG5Od15 zte*Ogu=A1;)DLkSsfaVTC5|TJJixsSK?5>*X7mhgjia%*e+t4Ts(zAtwB>BM2PLVq zaXP&U#0)(cm{@i`wFung>2s9#0ZwS8+S9EeauMrq_Ri3C6-!0W`V4;}0tXp*cx%g9 zR$eG+$a)`W2Ry2tgz7D_sDAJ;RDMX4>}V$vk%i!&rxKZR$CLQu)`l4Oe_V zKrD@v{-k4jw*gjzk4hwSlE+dGQ~dnm zfn_B~>pz8sTGU@RT#O;fn${^6Mg@M2RfbWpP*Cr5_&hE`Gc_rme8tH%K~ULGPzAOW zC>LGF*+g=yGfjJ*L=)I%6^8O5C#Jtis}>_pMzkEH5^bX&K7a0{Q!-odP}3q7@r`4Y zzhA6O7QV?rkRI2Lt+rBu;HO+Ym=k!W1%OAv+>^0Lo6sDu=#a5xGxUtM=Gb1_Sa4J8 zZs9!&#I=8m<~>@6A#u`YqT^oho{&uCAx30{86={CD=-ft*DwlCjX#5eVa}g7(Zt$> z^>Zu;0g))E_hb&H35q>m)}V>+ckJn6Cp6jOQ|7S>I>!G*4yT{h=;-B&8g^SJWh&Xo>5^b61~C4>tTq8kq=dtby%q^=R7OPMH)5| z&jp1%?887bO8#$-B+4@E+oa=lcKt8s3JlWv8xIm@B-7U5aluyc^`n>dH@|Y-N%p(? zyLK9wtk|U9Qx|0U6^WyI7nCU*T14Gdc?8%$tA+ZIG}oO6f(L|0b(+85w@vL^U`rV~ z2X)TnR$Xu(U;-yNS#zc=71b7)W-1@uz2fuvGNTd|HE#IsB7q>@4JefpJA6{*YcH=< z^X7oImH=cuRmjpeWHKxhCa$3^h2*qb1Hw#59CZQkbjgf4K@h3X2rB-2ESJB4=FyQe z`xSs0O~MlhrXg;AVa*FeWefua=Bg7raq;tThEtVOHZv4+2j4#MdAs!bF%^v+ry?l+ z3nl^x9oz8nPXBBI$Lj~o@Ft!Wcdzh#z_^FXIMU1sCj$WCMe}nD@KIxMZk2P#bmyS~ zO&s4gTnt@pljFfIFnzj{ z-cpTG!(PQio}3QP$)KH~>lH`eJ!W1WVN5&ai~Bq$fJ$XBkpzaPqcdpf04FM`d+&Y2 zW`t0sJcU7SNSEE(7%gNs_Si#n9jS7zA+e|9hcSkN708=tgJPew6aIahDaj%x58*?| zc}pD0=iLh=Lc$WDZ(<*}il{#H{)+b%Wi!c-^Qf>qOTtB(?wP8>LDy@OVri9#&6I0k zO@Y41KU5BLO`9}!>>fZgw4?K3*@f-XQ;601K-OEBs5GU;-ViOzARThYQ@De81ceO^ z9{z!wjSLy6XzOdap~1cL8V#x_Orm>|JcJpy4n^)V(_sgKlg%{2i%exfD$@yZ3ROO; zK?;;@l`h{*10f~JyJ!4)e2qkT5nBqGm>bGC(*(`SABnRM839;q97n(iQCFlze$Mir zvLa(RmLwr}7bgqEPQfD(2=9VGp878ZM7M^nme!~4UU>tHRHfdgl_1pIrq-GvW$u3$4 z-Y(Ye?45=Xe+l%y3Pl%@P%=g{XRt^CM);c77si^EF6Ow&`s>0b7Rj+tQ>oOh)AD2F z5vE3m3l6HJZ>ulggn3g2CSZGH+fQvIL7hw;Dx=USujeRf!lXl-4p#HU9XuDVjsHtx z|0pxuAYk(1)ufocF2Qsuc0)5MXmKlWmuST7Iu(w@no`T3pvfHJ3`?AGM@&+3;K9dV zgqNuDbu1S$EtR0g)7M_<5Q`nKwZ1*}=J)kR#0=oqzq!C*sVZvEk#~w2Mzn~xppds8 z^Rrgf8jNk~R8>CSp1s_y$3O_qeA|Av_R=|fhzhb-Gbl?&0116Z8jE-vz5Ei%buV~z z^|R`kI-Jbump`N8*H({!%)R zS>`#92{pWV{4RfA9saz(cxIW#v%9_iO9aHBLeP=i7hEujw2|bO_n+Vn zP7vqOmuipX8$0?m>ylqL8O;Xv8N2ot4_U*rro&7NdJvY>(jQ`m_z+8@-g?l-d7c zFw)f2q}|#HtV9m=)=XNDP#=&9(4i0T>HuShesyjAZP05l(kS_9Aw3fN%kYEjyts!t>5 zJ!_)P5XogI@DV5roC+UZ0;M_ZbT+&JAdoXAxx!4twJrnHE`p#DYTn0eA4Ya*toR7@ zpLerVSOQYeqleSh?friuh+WM;5rpor(b-)m351wr++!5Y2b1h2T8JuHvm8iThAFxu z$aq57PNUbLO*Ljd9480cosJ{yEPg9Nu@oz|f;Kf!9K?bz7y(%-C~opCapZ9YQ}*4e zP=YELlPJjl!m=74HAz@K`C>c}iU~?S8y=JaLbfk0#gT`U4!E3SYrgpMTEOt{GBztR zVMf6Ui*~B#@9j3peH%1{3zb)HWF2%{JUA5-q_DL)iWzgxqLI6`5s^ifseCN4UsHoF{`Py|X_m#pXDED5F-dKUI!TOOM-D zIv%(+?CPAXnKn5DxSP8Bu!4IwDyzS#|F>2@nkc;jbn|6|1S00X|Br)VF`cR%b=F*fn2-m_V2z$Xsl>-(f`}LA z(who9`EMo7?{VT;Vq-|$(}lo2Zfmw8^cz|#6|(Q}WRz*=S5T&!oT-#Spqn}Fjl?O& zA5lod5=m277pNCInkxz2F$?`OTJXR|feyH17PO~zy(o_Wj@Rq zxg3WaE1{&dCQs?1fzwICc|a@9iJ<9mhfb;?z z$yMbFYNnaZlgz~?!&4&Al`w;V!`;-&t&0~PD`AZNs^$)!k|Z>(_g`AA+ymuK)>f`( zX2l$ti#_HE(6`q)Os2`HS1^taU4g=SEH9xKX>w;w0m7iod8iAk87NHcSj?gAis{}I zWC5T-a?-l!Nw>_?;$izLH@8e}%_ECsB%~50#aoy@3`YHTvt(7nvIuh;oJQ0-#*G`+ z95OYksx~BxVqZWc8#&fx*Cw7VLALcG?x)tvU8k7)GFxnoUR)_Ci;-A|A`sK5_B}5F zJ&(IgUBsx~j5mkVhKg5|Qmw$&%%@O8mW_JD&1@Db38(jcb zU^EFBerQ$+5A{*+T-!h1d%7nFuT&d=8v*1|J(drQTqmeM6m-hLiVPueA%*FUN+9Pa z6LXMYUM@+Bn#R-UHKL<+o34li)70t_9q%i>CXQpJLk*| z&+tRU9i1PcQRz{JIJEWL%r8_J20vvC+oE+6ey<7YmI4Q(bau2Bnv2XTVery;OdOy$ zT!FE0hKi?OXmmzjW}$~x`k*f((OvnltJS_ZlPGvkH#el`6FX;I>mLuFLgy{Bh}d>l zUggKyC|HqPNYzxHmRjOVz;PGPx5aA+fXp+nfC^2a>}O9Iw_AsbibxVjM!^RQ4=r3g zs2tWFfkO-ts#`CS3Xr3eoZ-Eo%wK2F{YP)^Wy_Q%$cX$!!vab?tktie3CZEQLIR1z zNMfWapIz#TcUo6fMh-;nWIR}}mv@l7R+YzsDvwCb4ER@eR?_*O1a}o>JtBuRs6SaOC)Vze-hn>N zRU{tMRMr_X-Tv^}{m4J+sb*I}Iu6r-)aI&y%{7 z8Jy#>k+>=%APE=vZyA4P&s1~C`*FgV{r`!OKpRlG$XGD=U2$R2gV%Au^!2z z#@3QcXr_&LQf~YrznQX$S2=aygdTD)f0=LD!l+E)Q9uER-<=){@d0W%Fm5h%Y%Ble&i{ zgQt37Jc)d%g^H3$M%1QzVZ2Wy9~vGyPy~=$UNYlGAa^ZXVDaR?etj@^G+5ePUQpLv zg{QTePK`6hOe?Y>aMho?=dn@>R7woCpJ?`>yfyhScV zi7RKw?5?v>D^ZkW6K+wiiX=Jk^!ZCeT_L}49}?hR&K;hUYWsOD3QFYb_jcD1<2WRi z2M2?=al6sAIDN-@D=%#XA__jUiAy$)b*qV0hD#i94$*hIj%mUXqIG_`&gg?8k_vLM zZbLtIS^j|l?drp&$P>fcIZC$YDv|lpGjvu;d{A!?iuUPL3czPrHvMyT!Hx@t zq?Ar7AO>-H$pHs5ycs^d>au%;19l!3Cl9h|B3ww_rm}i_2n)M>lC$Pl249d~t|KtT zR~k?i$xMkW>AKctnP0R%9(Eyn$FFt;TBQP>b_hWu#9&!=Hzl4?4j}u4uWHP2Gc`^G zkM}h4iE}J?zIoZEbD7idb^1;nds}k~Ls+h0yijKaQkxcV9&8@SMn%eGcAH%Gg{x(3 zQgFoNgj1W=XJmEY-;E)PNsv7W+N|REjPVlM$y~*U{8pmeXjYKL13dWzu6O^5A@X9_ zz;VcuCoT>3BUPBEs-;A_N@(88kr4WE&9j`_ZR%}j$64y?>z_w!8Z@f4jK23gfEncG z!7c%={~0wTOdy1$u}GF_cqFCXC@dg|<_hOT$rEz^Bhs!31tu4ULQorCbUsPsJ$%pn3>RrNNg{NflJq>gR8B?*Ys;ryw zaP~V+VkNUz-xPre_z6j2PHjJWS!pXx6lY)RB*-}z8-|p$a7jh)8jW>`# zHXj3^f*a&UqvcVTh)h{YEOUu29!Me1L6(>ta?=V2L#LZBMzYMdg6K`RQIrSo3gZF$DY%&aC}aY%z?4>s=_S!&|hDL+{A*I*9GnP8;WdPK)g1tvFj{BZlNgPQ<4{~-w6T;m4pNgSMHntK3B?;?vU)2@oa*9zD2o&d;vK5vw$`Bm70JO~pG)`g1 zv;KM7G1^#w(F>WR$+#H_V#w-`RLNs0lVIF|ad49`Mls1y4?vn3^Zz&Rt%TW~DP>$AxF&-%y0 z&Pbf?d@u2q!gyGU#z!&&+!FWr@RW}WPn;pWV{hf+g*{1|ndCj^+e^J>s%?{~6zQ~_ zvI%-CRrOf&()uOtC1NSl&}|%aMCDvS0Fp4(iob*S=h4`$xGhE!w`KqmEp0kt9+5pm zM=#_7E)DYLtpUuS$Fcz^7f~&z+nmc%QhB0^#d*654A=hX1w$~@BW5=GaNwyXt-I!u z+F^wo2vnQf*|H{|1iG^gO<|gByNQgjui6OSBMm>m&pcqZTE_yRZn0yF0NfHSo zW>&SRVHeuqI!^%7i_UbfpTkMU^%74!srBx&36LwBE*OypZ2XD8J(I4*EY-`6Ju1k5 z?dW!OJQE8_UIIyJ;$Vd}j)8p?ezZF9xO#$;Kxn1M=ky5j2Jb?>VDx7iPLb5)Gx$?X z^=4Aad*Y%5Xu)sX#%Xi1$L6sRCX*WY(>l1(gS4-?TTtZX9O_ar4AW4L4&Ta zy!EIX_$D~fVD+opAwT#zzc99h-= zB&A9}=altBMUkKcpW%!D+vBa(v-q^yrPNnV3vGhT8olKwv0ta4KR77 zSIw)X@NK&xZsQIbzK4*hNxJ5_EG9T!!v0DdC4#^y{|L+s)Nw=2iW&sdOvBMgX&q@2 z+)w|B9QjC=@uzFQ;`7~|JwaM1O;oFbqEv3WFuI~@(8OW2p-)v{>!<-oz&4$GVCLK5 ziThh($=^^JJ#u}c<3o3`)|v;S@4LSZ49izY1vmUz^$%C0n*n~2S07Gy$Lzw-BgYN| zD>=&%g2~pB=Dojv@{jjEZvtkPe98itkQy>}pTPFKlNx8um7q*i)A}K9Ayxyv&yC(@ zuZA23Mvgt*RvEfIx#T{7@538DSF9^p@N~rGA0zMg0spMhw6=@FA2A>lq#Fs;7f%BP zXbRACpyhbXRxNN^?zYU*9uiAdU?K$g@d!6~)Tiqi<)rtH)3tpn`x-8@e|a(exPIK4 zX;7)aJAbvkG8@bmKdFL%4p2R8QH&$W0w9(*aXxf`!);{%DtX~gSYzFN5 zduQ)%f4SLC-%^hqh5NOg__F-bnOe>1Sh|_~_&6TiHV~LG|9MKqzY_Q~&%pxaV3Bk% zH{u{f+^_k7hKPLRs!e?$1vj!27ShcPYVzg{KLre34X!R5CppaF9=>Jw`s;yP3eXm) zBLJl#zT}aT)Rj|f{c#|3fP+ZluLi&bn-*|6y<0&v1?Vq0ZMT>JJ!El-0 zoFt(Rm&{m&ASY6AH?})q#$xlZ+BgEKU~bG|4!aMYT&lT z6#hkaR$jx2&oN@1bXC8~Fn#~`{JZG(>#uOvvC&&Ie5KFZW&0;X$HM~*p&7vRXDYbT zC=M4m-16JMtVY=$q^7Q3c&M6Q#reqkyXVg;twecN(Tsynepl#Y*s}BhX=y?*ArL8k zS)9Nu$>QejDGUKThiQGoLk@gsgM9n?O2~4F6uW~ zaXySJb%;;uAK`77sSwS<6$`d;w|K!`J!#6RCjYlPx*c*dY}87QWaMSAi|cH43-90T zc#tjG#u^m`3#n&}wJr9GK);aC2u0v64Nh@yo|KE^Cey`L4PhxsEtQuhDWQ^E65BiN zsavQ-{rkqjN*D<-TZ|ZaH_T@Te;CLf+_s}?LLn^aC z?Q%KiD-!+$)2=v3VBYdb%~Hk?7lJfdxSQG7gR6oLIX#_f>~v|#g5oJnV~!OF#MfZ& zOm^C(frs4aP=3u;H7nB{g&sQ8T}4HuEAzSH-iUT9L*^^R)+oV>cB$vV>cN zCi))NKNt$oG0m%`;i-ueFn|!sh`mDfAl8p2kN@+X% z2?7HHky6A2G4I{SCnCS?-c`CJ7H+&U30OAiqpIe~g;h`#Bcoc0PUw$N%t%(bk2=Ad z!lEm*j!C7}K+G99Y3;Vbz617{3RpPDpNnjhgjUh0-{dRQ#7DR2mNoQ&a(iIWE`dhI zfyN}Epi1$1CBIJsQOfO-{or+0xlZ(h3rfmkgSQnhC7;m<1+5__$+|2`er?jvr_bU@ z+0URJ50Kx%{wpRaPtY0URFo)_kbIsCQA9_}9R>$BtNoN&fJIEm#H~uV4dMB)cWx5t z_Uz82SfYv;qprnBAEl4+Ho(C!TZ1~Xs);4XKP5GQ49VG&mtbcXx_uTvDQg%NR9T9l znad&);Mi`AmCLdlSC`H)Un!B17^%l>L;{`otjNF=cy_==){ujKDYCU&;jYL!`{0A4azSy=JQH8c!JdYouxqhwbb>qs$f{F$T(@M5AXuVU_8J=f#QdzzXFB+&rPg4u*3-*$?s%uElUOqwkKOg#*Jz$ zMR>OT?1ira)r?NNP3w0B=DtQ64+5o&uRHQ^n~JS2fE8~EtBx*)KAH&R9dzz|#RtyG z)>KK<&k#%1A)_)Nt7(0t|06wt7-IZdEAu0riDktyNs_j!<*5Qkkb0vB#bS<0UbPrm zgfa29UsoH?YlM-hQoudSQI1Tm%p9t!EoKEu*q?ucb3JQ9z0dvynv-WR`3atAE}s;Z z=gkW4Z#3u{qB1mD8csuLB=wL4lu;g9b)RjL8_i-=+HVS) zw-f-Eae_bDGR?%efMOWI(d?};po#@$+fosYJgEo+huJgKMWV1Kb#mtqG#NTDF9{{) z=KVcqVS@H%<}+ZoLCK*iO)p88eFV$290{ZRH_Zb}8p_5?$9sQP_7t$W{QL;jf41jZFTb})&UCCvH`lcTsue%@&*5MAe zSPS6H#$njg7pn(Pw(|TXjVV=Ymnb*Ro|lT8z7*2(2v9^q#PCVf{e^6=;9TmmDCDdYIKJNPCM?wh@Qt4!MfzBPa1Py5Xnc@_h!_G>#siGw` z4BuV*LgBX#+VkM}egyK@JQm#r5X(RmXr>VbTc0xc^T)L|hbir2!mHPb zd7R;eCnbw&h*RcL8tRta_6NKA$FaBlIyQs2s+~qP`S+dd35tjLRHMN3S?5;o52j?r zssQVFjoHIcco~qKLCtc@v~BrC#*qBg~ZeSkY~c$YQ~ zm|%Q@(t3}zg$W!Ch_-fQqP7W~4S(^q=3w^Z3+KwM9pIUDrJ>8_kN^3v`7NU;L3SU05(igCf}OM5x;NB`2iCuM$S|Fd!ij9i?(N29Yk%s zVWs>1-oxsTJvp#*R&voVVNy|2{C?jNvR0%7(1w2T^~o*fgdrQ2lNzBe84HJWMf^_w zUF?cyT;&C&G;vq**3-nswUe&`=z7T{Mby!fkX5nw!smd<68VoE=Tu^nEO}MV+aXB% ziN<=+n^vVRu&NQ(NA9jE)D*>i)^A=ieenLOgQbc)wuCxanXz9%8BlnU8ANHh)S1-? z==u{$&R`QdUT=|;yMY-*CYmALrMIO=O|2u9YY1JDDuaaJcaB$Xcg#4_^dM+$aS~Em zIf#2dpO&F|i)3Do9j)gnZvFy1-vkpVkJBUx+ObQ0-4<84DRo5>4a@-|d z_ZgdSiV|~ko?CD2PRl6-8r^6?W-_qez-D|VNtTdQ6K{udBL{nj)j>ju9W6ujVNJ26 zd{&y@9O`*`7WJ;O%-gS>Z)ku~bN0IO}=wr$(K zZQJ&3+qP}neY^X%Z5y|3Yx;dBGnwT3lF9s@C#m|e>!d1m>Zz=?_t|?bBY_o0ufCWn zyWyHuP#+0-Fp{^!<%sAi_|!;4>Py;n5aPnoNW~n~bWmn8ENN;M3B+`WpFf0X;L!M< zb0V^w&Q%kpI^L!g{GD&XPVu9~L1n`L=H6=72@aY#j=EgNLTWsE;zwlp?035rY<(&I zB2`T}>ENE#wB48k@&*d7`cwcq{xIcB#@ct{>JkY+zuxE3Plr&J(IScjb(=25i6MzLnMfwN4*64U|;n|P)rw&o#_vl%)y zM^)W6-9oxNj&q1g5Rrb5>KAHOw4^^eXpWrwsJoYbI&AZLL}sOhnLnQ&lX0gZKhkjF zqrwyH8{{PY;Wp6OoPc!l?^1QTI3Y91GQYKiVO07bgqXyr)-l#&xIsq34*UfHv1fA{v>Av3~AK2l>u07`7>yIA%b zSY||%f2ANJ;ZbWu>t(_*Q_7=rnBI~%6_~BmtdzIAHQaQIB>&S>mTdFqCq2sFbDndR{O!BUXu$wTXg+sLyr%#p@b>V70Am&yF^fw` zBv=b0nVw$sqn10OObuDCm)5^rq&Qs5br7MT&y9^%6Ag2q5K4isxnkl0kmYL2*I^V3 z1LS!EVt(FlNe`QIf8iJQ0njE#_aX?9fz6p4mvp*xh8zbMQayGHcNSNz%~_AZpPr$g znnh-jkM}9)dftfYKu=uNalf^xyU(e<1569}r1|Tjo@3X@{AXO%G_`7pWKl}4)wtSq zcQb_jcZQK9Qy_OukDw&Yv}OmxKUJtrMwJW9GD`VUdcb5lVgIRbAy*;2S96jVsaQ}r^s9E7*5OoB2dyZO0R6!=%R`s&-5KBdr%`5DK zzD|Y#NCa<=lf03|0g%a3@_9@wh;y`fnCxZ6S(s=^k#pLVQ&PEV)I{Fl3I;hi<4n0? ziimh{X)FIfH?_*&=4piR{Dgzvz*E50M>nCSKLJV>NzZNx!-D$zLG9EYbx9B_Qqzjj zeNnCA-5;Kf!!{XwKbyl1D^QAUrYIexgq$!8E+ThC6U(3s_q-h)MFhbrEz%6@G#=kA z$Tmr+tkZ?6r(hYseX@tkzBsU~(wg={G3j6)h9K3(%#&GRD4jn$K>EwOG%y4cX#cCn zFp}$Uc!{}A{en&$Qt&mN9%#xOqZBvcWgu!MaEmZ3MJTX? zKUJdaXD27yrgvP8prTas$jF!@bK?( zQ~-XVLpK_gWO4Xv;;3@1HeMk7Uc1Toee0JGlP zez$DspP!$dj=PdoqhW#%H>tf(qE!jOVEOkm$CZIhTtM6~>!RTS;Sie9tcQ|YzBjqD ziyEU?x7dYE4ohv;Wlt}l#+EJSn6Bz=Nfw0Wm80|9Y?{$E_Pz)EkgCEk6mfxh`hl{W zh?&~LaLIe@?|r#d`}4LYz;f)N!p$nJC^g@Bp3sNwMp-Q9#)O*L3t+B6dFkJWZ=Q~V zvFMQH=y_5R5`gfAKBTV`%K}S=z8wC;!r}QE`^leppTb}AL*oj1#MCF86{kMk7w17O zEs_qg7j&4hx}uhiTt?D8DiAzjik|Hv1q-52wh)x;to5D+y2i!~eB5{ZJsUf*pijaF zpd>)7!kw7}m}vWtWEbwwyVD1TEoa`Rk~0+*HMTrA;##|RIJz}3txB??ySFGE?ReQ7 zcAMnW$9?F&J{HoJF3a=@P7u1tqa!cnMKI8xCkx`c>DhqGfo$!5-$608|P)PqfoPGJ* zL*n_&ghsC^F3$bkpRu?al^l%p1s8LVxz1}yiU?X8Q2ZeWYT{i17(J-V>bpYcF_`>t zbnf~+DmDs*IloU@7T5OuhL~}3us>lNd=WFT z!(Yu&g%Cg*R-EyEtqDK<{dFSSqyC100_6Rws}HEfzybwl z>~K{5PnkEPTz8X8V9KnIu6J^wU^6U#t@}M-wvnkx`wWm!_={)Meoi@Im9SSA|4Xss zrR(v)Nu^=zc}j<&=cXbgxY4<3&d$q+R+;jpLv5quLvC|=L*_O>ZR7Ri@5jRBAciVY z&eiex)Vu%3U#r{Gyn6_gtBc!j51ij#sQ^$LLh^Egq=4}W~uQq6} zRY4UM#2ElSG{V9|iG=5e2~|z4U1PvISEqR!AbNTA|vI4>@9yf2n<&(4U_$9n%%OKh`BnQe}Ae zfm=2pOsA%$V=vKQnO>dy)Saic<@o&eZhyCK{XNQVv8w90vbjZ%MW}j=-l)$JYJs zc@5ZbZ0gE$&38i2=$d2i)$!aXQe$RW&y3+oSA!UZu@3@&!$jZA`}@nSC)ejW?z-yv zWH}v{K>PCZwzk>1Q1`BFm&AT+Kh9m7rOUu%CXan$5ZvHnZYOO*z)c?r`NZ?%;nfX| zb)4CGqEVp8ckIjSbDs7(_lf{|=bC;<8$VUx9Pl-v-*@EqC+CkofC_~0-Te3>jLiwM zvE27}-@MvnWo_^0Htp_fYt@|RR$ulj4@BLu$7+Cn>}2;v)IL3_q$17+T6XlLM_f*8 z2djsQNhx(tqK|HPzfEdfSBgF`Bbrjig(lko1Ej{8SdCJ3%wjxaKj-Uz%CF>!SKbxx ze<;5if1zRiVR8n~YVO2fv@w89%|p!iXHa^|_)~<1M|tlM&m4WHRcdb9YuaEjR6QlJ z+o`|o%;;9h=&wffDWe1x%Z77^6rNMA{Zq{}yBw<>keFzsMxnHW()KGvb=`KBg9zBU zm=N66XwP0m6jc+cG6^_|@+yIc4faQKL(l=D^;qQu_?~dN{g*6_uDRA&a`DjI?+2V> zQP;$EwYU7TCowhTd;f4w$=qsYRC608^0?GVCJp#a-8#hUvmsBFeTN{TCQA@&D_GO; zG;ds821hS}FiFf3>8M3Y*EPk$V#|r2lI^oIO6(DK&uUiUuAU=~1WgV}TpsD| zUiL}jFb2e*xLQ@vgBTQIDTOxA<|gg^3zZlKh)WOCXNskYbKC5DkeX}wCjsYrA#&h6 z2PY4rmzBkUqDPKsb#Vi5q(!q?-_88ruZ_Sn%(uxv%@m0)jnj4w_iXiS^-Oz#&a-a{ zf3eItPI91s$X_g2LhekqrMa@u|D&EFDa$+}k7U=S0kT!8D);M57*QiL$u8o%LvTCji5^VDIwLIF}-I<3h>^ z)!gr581+I~m9s^-Q9yE8YrLxNnWozdLQxB5vucB`Qd%3>L~>MfExcQzUlbR_Qy@o4 zk~UARV4-f4VDtQ(eQK%os*S}BC#S0n&!r#ZkP;4Ro>QJJbDlXh@%1hJw(b%Y{~>J& za&)NR-AUA~3-n$L!E64_MQd*pjf?rss0A9Zy8R$2)HpAiE7p7@VzI?@q~UMO-LsYf z9<*6qtb=5nr_dLNhyOxlzEe4!vpHeqRml*3asm}^%2N58AykQBs!8$i!T7JTCx9qX z0F;bMb-08S!LtzHhmF#L)u)rT;0=iceB2skLTOtCaOBi~h@v|!YH3tOh!o2UTvXLB zc2*i%SrvdjVFXvWXn_+icdbIXzXA84h)gjT2x4o&CMxm_hRu{c_Hwb)uvxTcQAC(nueRh# zfR!8l_~ZMlg)8moCa}mvxeDe3T=f1~;BLtYCsWJQ3Zk?w1l|wQN{8Vhggl)v$u-t? znzFNx(F&~&1}{_q!LC{qz)qYNfz2SI^p|sfwEIJVbAg^p9!@~kIz)z`bLI>KYE;Md z3y~^&ex_jQy*k9PlsQ-P=W^QNi=rtF3_XsR`-7RV-5j@sL*ScyKRMVXBzfc$VX+p0 zB?r&lmbi*oDU$beBjoRJ6s^DnZO#~JySG4R(i4_7)vT9~ImNLYVM$EID-k^L7buPC zG&+;+M`Q^Hj1cY%DN#6WE;x}ccwN;Fka>fP2iP0rlo0I$llD+DKQ~NCiU&y}hhiu#N-45|%Br z2|56dkFkI+9toI}hQf$fVamY7roEV}W0IXp8|LEl`Qu~_?8jY-#nA;7a~%44avFE6 z+L8Gj&~U)Y@9S#1yKNZAu(-77)Gp9rM#%-l8bF{C+sCc=91=GCfK0KVZ2QnyX}Gt# zz>@PXLy~JK*(gYc71E$o@4C6iLwc&_)d3B;U&+QA(nn)A*jdd9Cvasq#4)Z~xu88u zo8_IT{6(8&{fS-{?`Jq`sM~f!5dIdJ@l748%+X zq*+oG%ZBh358rert=40A$@LhBWXT%T!OBG|4qzU8Je1Eldu~oWf!*%TQ!Z{_L)-7s);liNtOvA$1bKJ82OTN8PUG-ip%8n%z8ou+&8)4lZ zh9)KQ&jC-6i=rG!R#EHHG4_(Kp~5aFmmK3fE%gT{=QB)Q=DSzZjTh&S-q&wFin>7pU8hLSt0J>G#OriU~;7R*<-P(_+P#ATYNMH9=?W;b>WFtN~n z5+h`Txy6gagvOjWWd|}iMP!Wf1S04lPIZqtKM^5Tw83kx_LgIPwKzi8sC=$SK zA)*JXds~BS&a{LdiBz~i!-!Q$25!Ls52n}WdL^HXmDvN-r|8pDwWwNjUml~XO`x;h z2J>WqD8a%jyjg;J8A(H+D-^|AnDP%`e=m9Kh1H5-9`I z!)SQ%(7P%hC3r~e)JzzdhyOh(K9qS;gem~j;-V+6Nrqu+-~iy5$S^mup>~k&u#=@CZ!wstEJIsnp~mL{DXljm3K9fqKC`C*-sLg zi6LQ-CG$z7hf(=Wf$iIL!6#7-H^bOwfciOV&Ue!{Bm+7X5?DiQnv-a*7UdCVDKQ_) z3bLD**`xdBn|!f;%shO(_(UYMi7I+nyM9S2jLAi~Zr*>S zq^gZUOn(FO%X`T|?4J^LS}K2674B3FxGjP_zx)%{M+WKvZV2BSjdG{a|JA17dj)=R zyn|L#9gVoQgjcc-vot(MR0#S+&bNm5!En59(arJ)thAwTh{`1XlvZV{S}WOEo^4Pb0LNi@&}2fNN+9u?G!7;%YK@ z`2A>Muq^Sz!;X*H>*?fkOW}Ov^*X>LuIjH>!`t(|z;;XS?iOP?@K2j5g>;(oP|eNY zgvq*J2nrb@mN`lou4HZC8t?BV&+kuNuK39l5thiG(fo-!XNp1K-=Nz+T}rL59&HGa zz|tf+t;C_Z*e8oPM(Z0gV1oF5TOY1%t3bxbaakKjBJ{%Y{Tiu2!$GvKxP6vHApDa8 zp>3R`X3!>FpZ>+gCK54cCts{p@3SB<-F{krJqY{OPhY3K(80z6Q>50HBNQ>Gdg&Ko z|4Fj`Sa?fAQ{&joPs3=nr4iF5Zb^m=u)>`&k0iU&Q(vRbN~cz~iNTat9$w^O+$*#p z=sPTl*O14(JNtfnq#?D+?q%EjC#PExs6HU6gi^tB+?cn1+F}U%*IV=Ey;Yv&{@s}& z$2>^WJ#(oHE&hroCs}b;-Y- za)Zb5uoKAE%GS@nZ42fll>J}IUr7G-?|R~hpsNm`bUTt~ul|$tnVJDi{I;js{Ca#X zQ{5Gw5kjurG4|;YCh}bUpO5Nx?)rA+TTlhZ0i$0T87V&<7lE{gA4WF4I)AIr&Ni;6 zqQBa7W6b8ij%1tgyE6|AA$-?3ey{nI#G?&uUp=Z+Jix)MH@~m?J-Pl_-k~LMr|C<7 zLwoXjs>TlG7t`>FJcBimBGsaMH+udr(_g*?dR1bH-~uI!BALMnbgU%pXt{jQA;H-5 zNsU<*PW56=v0ruDlHIOYvU=4adXrazgjU>;sryqsS7JRnP5?#la$PvDdZEBTs|G#Ny@IQ zAnt;^=rZbreFE=;vL6=wqEQkK3QXf!6STS_lU-X08LJo|18oWe<{_r~&ua5lh?+4J zt|^VId^_-_wiF#-l=FB9Tf^FJLyfR|N;E7qXpvS_v~xM|*a5g)J323!qJ@PkV{d`m zexVwHP*qxMHZyBv@bbW_ebE8NNC>I#~9M&JOj*2mx{ZqW|%IfhhFcVzRI68t3y8^_xlq(%~ zmaUOTEx|$CgA~h!Ch;u`v(FCnV!jGEhn?NF2Ds zw@+;XNao+z7N7PTG5DKpb8-c?;JY-muyAdGN2j4{~XK%%Q{7r6;@`( zk=8Qr)zK~B4hpOk``mKr&%&<^0M9#| z;pREW6?M+d?uh>Hp=nTb-3nPbG2r$bsn`dxFv@john%U{iW=Noy^uh<=_Si*a4PpY z`Rby7a|utVXS)PJ9u$!a80c7jBkGX??!}*76AP%iZLx<~gH}YuD%$bifcMeBSCSCL zpf#`LqK4!IUjJ-gvK|IQk>{ITzJIb3d1oMd{YrjGo3qfVB4{Q=E0QZN5Ko((N?sk_PQ+c z|2eEEs;;usk73D#b%2!$-ISSEi-|LY$V+N(p8B$|z|4P;wX$I9WbGsfPE)z2ln-0h zqwm(>gq^CWCBiUCac|kRb?VxElgrv}k z{9@no^{I$T(r8>su-1&eM~zYz#gFCW#dR_ppk_E4*)@@+t}MPDPkr3Q$pj7VHYteR zz089$#h?fhXWsBHU7UiGgNz`9I&gVkNYB1dJ%xQsM2Z9)?Xvm^TlK0LOYOBxndVC6^xx48!kjd zp$}zt(A3!19hr;4%7#*mT?Zll1_G5q2Ov0S!v-S*$qB$x6iv?h<3*ObD7i65IE?CQ zs>1cTj-jDIJE=ceL7bQX3?P>@z_Z}D7gonGIj|E&Wd15~VKGNHQs)@Y*!9WkPbSGt z^-x9LL@=+S(Tr<|i%v%a+-7w&I4=H9lqaA@yJj~9J*!I4J|CbVf}?icWgyH9VU>Yr zxZS z3`76DW2L6Sp~DcwM4nigz|cj;JDnLPd`NIHE#Tv%AyHl{AlJ=k3NtSlm&gQC*Lber z^?BL{3x}Repz#P3Fb{1gM&v%QkAm1aC=!Pb%87^;c)_PP<1Oyba6OEmHN_^hoD5id z9?HHzy(b$CARF7SrU~TDnBsV~Jom1%{6!Vs=t5F)kYaTmkDhMG)0OSo!YHb5K;3E5 zvw>AiPfsqCs&MdISFiY1q@klvcK$8my_&L7tg78==-%(@QUy^`@b)%iKp}OwO<#iJ zY>!7FRr-M5TSva_VPs(rE3L;5{dCC-O|E+MmuGkEecdg>iUdXfRR812phXuyL@bKJXi|F&BTYs}#LYFy=w7aqH4I_|o=Mjn3~ zw#0lkP+C_vZ%lh_q*kBo=Pg%NuM(}%pU-md{>kDOmy4!MyVu3C+o~|J=rFaMu6Zje z*=wdpK<+WMV-N{=C&Rm5Z1*`4jA2~NzI9tv-Y|jxt&uq#Tdm}TD%tVP^T`%lI_3oK z4%@}5h2efXFTZyCz@GV62wy_q3faL2Ltu6Ph;%z8y_coxT?2T>ehNc_F$UzPuN$Vd zw1cPKcE=N?btlo)(Rzb%qG+l6>fz|1(3e@NuQcnV|Gp>dv{aTAZ~kLYW@WPGYjVNl z_3uO-G7L#yMk>#?y0f-ss$WxLb=cKYzVFhaS^v?7(Q0=~#gnIMs8qJRfG5X@N|fvP)HyVuGa8fegr{j!Ww`kOlRF8@5;bqj z?kC@9^s2m`E{~^l@si$a2FPi+G=9qroA^T5aca8ZlSI)9b(vyps9Z2L4~|cWV5D`4Zd0j3RR`cI1FCBs_@y!zGp@sJIX@wTV-~S> za=t>swC+3kHJWg2u~Q2g>C!Ls)9z3m!?mXRb#nHs(7FRD$M?;MFfCP~9G|Zh>r|a` z@rwP{%Sig?U4J^LaM<-QXeiO~xGp8pF z)igQRBwF2#d2okUSvB4%^1jtazt<}htR3>dCVGEwY(Vu7YJIoO7wnf1-WlRxChY2d zd?)@Fas79+p|i9f*?kiT000*N3G`oa{r}T};{PeJ|9`nr0R4E!{H*@}_cf^|XP+gA z*rR^tC+Hmprqd;bb|Hg86{d$RG484X?v3V#aS3nxam~XXDS(PYhOF$I4RL=wwK3w< zOgtfe=9+pL@;8Ese&{u8vg~_r3(WLxE7lH{){61n0uff$RZ*JK{m; zMwa~K9iOcn=|n|iZN%;Jzf+xL0>&m6oF)lsz_xl<%9EGh*!;t7b@~eUt$nZoBgP%` z!7}LDmCKuFVSCjtzTv}2Wqh0;AC+rmu&EvpL$FE){1u;TLW#n zbqlmO9&pn5*4i)9eY($Y;Cey8fkkWVoxki^tz;gsnM+J&pzW8f?0VXQ%gT+&JCR={ z5C3XG#!=x&`n%DiasnG66;a{J=M>Gbr1Q6KjysB!pYaAo0zBNg4_?q#5CM=!_{uW-Rn9W{_`BM;L&v1Cim@Jz~;X4^%f32R9%A zlm%l0v<)i^;vqrXUZW6J|#k| z3-5zU1hj~mLu7)~71v<60>W#SyifsnX8I>+!#0K(7i}RO^5j7I*;tR1{KanL?111x z*-&v^fPzBx3{J|P5{_xs4Sxb0`fHexRP&G@H6Z42E-(fz$eD=ml0;a@FOaisY|{)T z=t_?AK6U95!UQQ(lKf^ebTcg3EP;>v9Go$_CfgC!q#^G%16BzVzM?)c{}^7$_7np1 zsPi3PJhW?OXT#kTyAdKBJA-8-Rd8`L4u&~ow6uFlnB;FOR;###`4vtZgwH53^$B$;Hx^r1mKhW1kK!1Q3hh6f0-%Zu<1SK8`hG<>MDTlUePF z(V?&5RxDv7*?90tM5B6pnKtDi2S4h<)+F>S?W5A;Qn@X~O-e6e+#D64PmYv0v3(;e ze4ZPh%AD@tM$Lo6J$9NbdQ+5-TMTwS&N%o4UK!fiiO%E3+Si>GHqZCh#>|dtGx{p5 zu2G0`vw2`~eUKV@@8(p-=VS9oC@y4}z^4v+pZ3l7q=1V>IJ)Lui%5{y_U!5YSJ*T2 z{_+>xs9j3`hV0T}^P5-YmKh=04%$4IHnlcv!uuP4`P65R%ISIcEM-7s^G;3eyJ-b^ z;B zIG<^xE%xJZ*iDNxF-}K-s2NQ5IG=j0G@CJITXx0A8Y#h`#@PY2iqO6UojGl4sBhXN zG7G&|JOq?10@*o2gCHy-?zqNGXMiYGTcWzU^@rpb+g99+(;LmX32RPa!|170mJ_X# z5uT4iz13_7sSm2G{Gag2H9Sf(?Fhe#ZEDz2#|JO zrN%Mdfb$c!oJnrhg(=FHg~K{AC0{d3+XfLLUAI8V+SMXopnksW9i-Q`ZUK+awJRD#bLrHke9i&g zqTHrkzNjPCH%Gfjle<#8xDn%9@@Zjd-Ep_{Jl>tTawYJ;0$_&}%pBhj07Cwt-~Vl! z6!P z*XowWY10a8SEfGo%a6yZ9Y1eRowjkU^yO*)Lc0|;H>Mr!jq0=s_Khm(RtA?MD}ejD z>ij@ahu*LGP25be(kk(FBkkmn9)0DJz54`l{YivUy*SjU%F$@M$7wfe>z)m?4D4@m zxO={Hc@Z9zWw=fWSd#nE{04uqblEIxc*O3r}rAU&0j~X*OJ-St5-@{A3_J- zljo?|MJ!9L4uWc2SQ?9EA!`_-2-bZ+1E_RQs`>KSzAknh&%D5IU_djATlbv5NJfxZ zXFMZ6Jb6C?$~d`FZwOe9qX`9gqXIjV@V4~Je215g*>?ou?6`|e%V=sgC!UCiA%=Bd zdBxp^IYBilnzEebA9>3>5rUtWPGL6~ri2T<{I@Z*Nv0H8hU04+A2-gma60Jz;6h$v ziJnxq)xL*_Tz$P2beqtLc`Z;VU!b4kaU#R7 zD>(==h}zM4#@pNQT!L)yQTkFn!0qR5VG9)PWBCaM(`)Kq*_9$77_GC#Ax&wCuv@C% ztl@`TNSiq^fN8tE%r!L3pk2LsnH&HH9o`}XnK$sy`1*uH`uHaIOY9t9>k&aV6xFqk zkk+2qwx)J_lf(PwaVE!v;CkZzCS*f=TRtdGYa^^8HVK&mF{lL3E0hzTf@CR3dUCcG4*narcm>A*5=KTKH8mH?5Q+`x#eu z(R%@Z5t9|0F4u3gB1uV*%_3Geqttv!;Z4ZLtO?2FLL2&LDzO?j9}alMYsTY9RNXKp za?o0ocBV%q(kDKMEd&(aSfvf#IK;@@xHjUz!jl6r`RI`V3?>HAo`b(%>PBIMA+q`0 z*~}cGBnc{UVivd#d9S#S@CXstET|E>NJR~U#tdlxULE#2+a-KBRd`Q+Uj+kC8TcJB zCm4-JCidg(G%6s*QNVXDs9kUYVM73Uz|Z6XHbdVDA^9L?LI{m$x+B37FuKa?ME1Eo zbi(M%_|I}?DOkmNK*(sJMNlIuWR$8zg#NkMdq#`K3xT!}5=VPmkcw|AD7y$KLDq`& zp!}%SoBxs}Y?^g7imSQlM1hD7DR;Vx<)Ooz!P$JU9^_| zwm5RGoyj>VF(ZK}0IkRlRM!u=RdLgPUHFcaUAA!a4Ykh#F=GHk4*O`pCFn3xvApFT z&^N_;MnB$UH5+c)!WNRP>!uH%x)oj15fSM%&<4Fu;2LWKzX7E~3!YzNq6yvJC?{y| z(lQ;@Dz#Wkkd`*Dljkvl1)d_ja(5L_bsm`fy6{hgC0~LhYY&)Ljwm`NQ_OY;V~Nxr zVPmRTO=Qpw9PjL7u;%pB#c9UTc29VX1Nqw2kLmKH((Bfeg_DP7s!A< zUd_|+q^XtaPI~(f7MBPTwt3o}9>GOX2i~%r9X^YTKGN{8;j3f^!YxIs3!eQiK~B4? zs>_N5A0|vabUIc%xQFgvSy1mqBaqrB+}2`|z3Qz#`oJe&N|(fI zmA{9`)$A2NIh^>?JOY{|3kh`?J*wyi!aHC_$Zob}D)I+j?PcdrQI4y=|5uB?k(Y5m z+Ybs6{k)0-padAZI=k50YS`K^Sh|?n{x{VkEF=X`=KmyP|1Vc{ybyfgk1=VTuoobb zUIVc|{@*XT%yuW0ae&-9)_g3uz5UWZC>{CZ&$L&S>tEN6Ji!)FI1OVk31Z8f77~y# zg2-L1S2SX6283o2!y1n<=^1I#;}&K&+{ev;(k;n9*7C;bq%lQo^%Tzc{1M|6B5zUyCcS`}0NBe)a?Nf9!{%lf8rU|EEpKju)~HW<(6R4*5hdys~So zMRRAOFNE}_qkG7VDFT-zmeSFZ{`Mx}vyJ29+vYXv9Z60@;7j7+uB+a2SY;u!D85X= z;_B{klL8uoF!W;54#JHNk!kU!_V&9HL*&;8Tk+2v3W)Tw<@(K+msvQvKpv4vrm4Ot zrJDyYl|-PK)ktgI`GzAYn&mO}o$SR`fbMIL!ODbNy+AB_E^ZYjBxO^h!4>U-}M<7^!^7PmnMvpQH1!D9i4sZ)G)$1vW*%?O?53vdwVapBck>Z^ zTSr8#TyGZi(hrYd-9Z+*=Tozf_k%4QA6{sZ!_3hqo!>0m2M!-!TX6#2TT@%BntK3H z3IDy&`yBhtXbz>r87_KO_En+1#fIp9m>ySFHr}xNW$TIl&1o1}`9rW=-#d_*enUvk zw$ZIZhwK5YnakNpz@-5rP%@WsP#~lL1|~2|_`nor4>YM8AdmtzggSW$w!}%a3Oarh z3bMJ9kZLR53Sa&*l=N3Zvz??en$&fuYKFYLYoIs3TY>W>7<}Ze_013$;>caFu1Kv8mzvT_=N#S;jaT=n;<+sj!g~UUT#j^xqRoO0Glk>de;kC zyAv2X-OG{^+l*O7K&z6#(R2gKR2C9gFIW~RF~OX8$e_1bCMRQ16plNy+*rHt#9s;! zL>fS4fOan}Gc4cV-ROZXsMw|X8s_X^v_NCDP&BRDfRXX!pOF?Q;bdmM(GXY$!w@qQ zyVYIhSWaeiF^iyqO-tk<*NG59kz|?hG0-MH68#xSax<39M3Ac-S$mS(L7^wmzzs`9jHPnKpq69@Tfm4InaR--rlZ= z|C~Cpu;OO?rdzKVV@+@gam4({-*gf_c^ipkkZq%P$Lj;x(AM>0wuDIBo#{7GSGTTR zHU3kmqMjIn-K`8)3N3?s^JvNBcY{APk<#nhz=nUw79y>3s!}9{7QIgnW60TQAxfnC zM=iQ?+3UHH#A!~>spw@8VZ-f~LKgxg^It59 zjvdmzK6u0`Y}~uFhRCjmv)(~)v(O}_=od*fbfczJwT4sy-D-@YiLK*1Y@g%T&u{>T z(!chAI|#|7t)hC9mO*xQ5GgizS8xy}p7e48Q=qzQL#LRqM>}k@I1nD4<`J?lS5JHj z8N9TMUOxFA#WMqz9F)Kn%4cQ%mj4i+M5vkxOPE{D!8)-0Nxh5JECnlYg#^=ux0ct@|? zyxq`)6MS*;(1Xa-`hQRyX-V(70y83dQv&+xR-Y zqp~fCCx*kmu1_W060W7O>Ded7kta7!kA9?~lgHwJJ@LX%`Ph<%+l5B*7ns!v@fz0A zngqk~L&NS=mscy8xZ21PtneBve@9pgM7#inNxDo{}kNGKroCml2(x7}&mIr3ZW|cMA0~ zv)UpVylM2wGe@kgw!i8vqdknp{++zeZ^;vp73Sf%z-pOBofmWd=TPQh;%ENWA+qG( zu%jGMJ*gp*h;8CTIzAyh-_;nmaIj?`J&FqLh3rH#`0UZ z$a%U~3_;6$KUnIDN=l2$%nLyl<3ud=Kd7F1Nn~m(Jk-*^#?Ka_0+~@lGLmJj%oH1k zRfSxYeidibQc#dj<`02Y#UYTVpejkbRi-nUw?s}GL_$N$R;39wHC1yf26&i&sR7Ld z3+h?Us@D2%XXV8=gxlFhZ?4vP7Lxk?erEVzq1!I2i5=z#vr!uWfd7E|f1%si#nZ<0 zza@u5U2W$qNp#0qBajGj%(?Kr!$! zoN^nEW;ALIpxgvguf5Kl@3Z}J0)AVf+ZdTS?nDlu=3v(=Fj7=|zMFHo`_KZLQ$BxxCX7x7+3&d22bh{DQ)EOKP0Sh2r4ASIg!Ct_0blGVrs z9DlR;z7X6d_@ZreXfKAT4GA#TeGUn8NPf_C5_eI~ysSKkRQ6Wd@6X5~pJY7WWSaP5 zZZ|wN2H6R_eQexw!a&#{kZJnnh7>!VWcpcb9+-Euz7oR@{6P}fH_sfldBzyP6-do$ z>>GzWN|o6zUvCW`dj89f-S^f!i&JK+)hLn!C;4XONDnKqu%hIlaHl(dhxtv8XwoSY zB;)q=VBJfC^nIJ)6#Y@vVcqpD*X$(op~xCp?KIxFZoz*@7cQJDI?}YTKQV%Ll6Y|^ ziYE#f?IXKDGKePQNqs|S25IVXc}%s80guYQL{u|wZL`*yBGZT6H)uYGzC}4oo;4}P%j~7;uFW5L-oHdqcb(S+!L-+tw7%av(9F#Ry z=%=?nD0Yq$JcHQbZiu5o5#@a-|H*B0&?gx0YLEU8{IdgsL6|q^-o_sO^tD@i*h0ro z{KEDpq(BJS5ElMF(fXbRBQ0EyR3@_S4$@Ys|5Shtk@3?U9Jus*Nbq1XB?>i@M1hJ`0$#-;_JjQK zble_#Dq-g**hkg;<=X{wFInv2v&eL&*VPq`>zIx*oaCFTBTi z(1c&G%atWh^CH#Lcow=?UZ{(3P9EOkT>@w9ou(SvKZ|iHJ4A@38U(EGWKdJSENW7{ zrO|JYy86qoB`+nLT|tQ3f~;@%j2v!<-_CNp)HKhVWQ$ZOnRm7yZ=YBJrGky`a3nBM zp2)9ftyT&>vO^iJ&|6)1s>+nttuhlH5HS zr$35xCMkwRRt@T7g}~+2o&%28JZbl3Zf-4?hZ+H@eqvULf+Qia9|;-9EY$H|F#?)L z-|UE~@O(eqdRX%KBPEw>a64gE4Xz7kI9)0RfyS33BQ~-}UlQw~E0Ma8>j|hV4USpY zQQqQp-q|)qZqoQ{U6;-x!Cysnjjo_`0on3Dj|9Y!?3&O0kLSB4)()Qvt>7nTCJZEr zrUkdim2A0H3v@R+dG_C9c>^+qq`H^H?(npL9M5`y%kco|y6yht5f%(@7zORmqB@Z= zNL~msu7Kw}R@m$WqJABwpT`_wzm6kee>U>0Uq~$YvR}r3o@!!2omoL=`*ltyEuXLd zQOAKFOPd2vrc8sT)8s%gs&i-Qc)GM01sS;OMuVS9J7X7j#gb2-nq?J}`nZ64;>~&Q z=L=HL@fVLp?m#6CIU%)yNa_{E;=;F*Wz!^n=gXMO9ca*R9OV;B7g9>${ut=MOK2>w z)Z#ItxCcJi4TOcixa5ce$-$kNw3^-@g>=&y_q`Tk<&5Xr55Buz(Jh;{=>NDzA!wna zJY}x!fv~^+*4zA;H+hN$-?LL;MctNV%5&jBkF3Q~K)_KaiO4*vE+XSS{-`%)ldjdo zcW(|J;XSpW`ajyc4!5S3r;R8`m4Ngry<_ME>0Ll-h$v0q66r{_ zgaFc{_l|TSD82JVuU?gVzyIKu=b24Ta^Cms=FHBsyEC(A;VpEWl;A$o*%aH$PeNxg zPsMx>!zRICN@3cndrH}6(bvLJN4O+gQwK-G=pWSH#q&J z<@9{KvW#SADomqtL=0=((HFgVQEQnX?@8uzTcf(Up|hM4Ve2{K$3s?#1jiy88AB$BgE>J{bcKaqZiUIrU+OXE7~@GUkZZ*_znjg_~Hf_>-pazg4Tk;@wOc$@36svCO7i{ z7&LhR^gkue)FZP-CryUfF-->RZ-IR^377KQuSSW`a~w@;bdrp;t0Xf25_FPyJq!*x z>FXRXrM^o+{cxXzM!GnUMtZ|N6lIc>AKR*wAKnsSP5e4Ou3^9x6+yZ8TGJJ>Lhvzd{Pd!H ze+_4FOEXQJWuQ+sCBq{W7y`aZ?tvl`90qiTSproQwDAewfMu_t(7!WQYH}B_2^MJL zSuIyf$3p`@M+l-G=sbeQ4OL03T(Z84+J%@~x&-H=Lj`k+0Kd%reN<~K1Q^o}1>kc* zg0krpLbBtUp-J3=nl^NKP!xOhcvPh2ly!dW&!R(B(6(ww?t4+`^+bqK_uKiET0e_~ z{I;IB#Jpbxa-5#Zc;nl*55E=N;7?GMdw&)R1r8P2ke>xJKkBMlH~%OuRbq{XADc!D z-Lj0{0$Jl>IK|yD0$RJUhpRg%S1=vRTX+TzyHBlG4~saHcr5WrU24?! zKF;FJM%_s5Jf%Y-3^psPK8oT#jv(HFB{L#^!uzi( z)e)4~g)2_XtWVphoKFcX*El%b1w|5M zx}Pu?MC#O6;TNR4V;lJ1Dma*nl-xU*<8Hl>=#?MNzY!oTd;*UbsK2Rh`#ImgOcQ@S z&~^w|r70`UDi$gQ&oj|oYs#T0dy{U!@AnrxUQgb!RjY4J&XLWEXg1*Vtl+dx`o4V5 zowUM*Gnl^AsAbYki_qbT(238a@>U+oRwglV86BZ-Sz_NBhD3ok9#w>cj)>FGGvu@o z3UraGp$wXej*L-jxWqx_&a6aN4#5oAx0mwC5d+$o{F!(|$3S7e_y#EowiLY&?>T2d zRxxsf8NCU@?#j}Q4pnz?3UHkm{f=;BUJM)$DoOh!WovjG@IRhBM6P&64jEqPSDcb= zgM>O2byTN5tQ=x=Dz>%{e#j*!d=`+>6{JaI3Efg4V!f?Fq`affh<)W8A2WLb@HSp- zFS0#flNKf*ye;DOd|Tt#o6?O~Dr||c^J0?UKSkBE4e)h%XAOTzZZ9=|Tl($-xAqA- zUwufYRAa#%uwXxwJAN`{C&O7KNxr|X@&?D}1qCM#H(E=2t6sh+feHdUIq}8koLSZu zUt*QmsxG1w`tLA!r&#w$E|HMbxCju3Gfz)8#n_DjCvq3GLI~&bX2_2!4Oq)u@%7lY z2>Ietj`sX9U4p4bj@@fB{m%^A^eg5h$Lx%XRqJMzD+j6EbOt86UW7W_IM#@VxgBNRp8_g_8@5U8nBz~a0!=RF)u2{Znh@}qsoV;LhxURh=x6Ryz z&(a7DJJhOZ3-j!0uUPZTI6Rpvtp2hPNA*BR$bsJh5sDLXQK-AFORBKV%vsISn1ecd zWiYaI&C%VrN%R!#=|oOsYFZ%FXECqT_$m&wGq{J5ZZB!!d|mC+t*1z>3fMAdgZx3^ zK4~=Iu}Ms;Y&7{w7$R>SR6=y3!iE%=HF*^ax+&ZqR;}q5yH!5u^+N9XqEcL^Ds_s; zf|A*C)4_Z5(w)kDN9tvjkrGwU6w!r3wijMTo=zo-HFL!^5pH)85AR4tlvWAz4rar! z6XJ=Z@m15y(3dx^C!it_9O zopsxhf+vl!dN$&Pg+{k3Ndgp=flva1QpFZ+W`XDcb%EmeIqWj;zuI4RH!2 zPMzPwBps#*s(7YTEpMa=f`FO^T8g9zo+QVV7i)vicj81$&4V6Ak5%I{#E;|i+ z5UOd2v?pA}(6uTm)MDot=_#a`r(X4;g^xi91XZGGEvG+i#9VxmB$Fgn+3v*@I|=+k zM70GMk)%o{dy&^=k>$=T2+p@Zbc(d=qkGlvGgcYosfA9>4E&P+~wKHq!-*S$6AKhyGiIqOyotg!=Kmq&mYui2hp!`F8QCn<)job^6giyQ zc*Q{fr7fHDXsf6M#Yc4>D+dF*q26$HH3WXT`$oxU{OL`%w~E_^wzId9GqWyQE%@gp z^-ivlY#-fr<~Q7P_8)&9=nH3yq^ZdP?mLkvSa0pEt}m|_j0d#lZsh*A1CeFsSji6M zEaSe0T08o=ZscNSqWxREO7h^eE&LWS+T01VM}WUawy2W()nDtG0&oJ2%cTkv2n_1d6UR48DD8(x zcAjv@?p_yF8>FzH@Cbe(PY)7foV5TpcI1+R0sYll5MY#55qeJms&d17>nY@ASp7fZ`RMGiQHZU& z`X@dvrSgp{)`@@@tHN8ADMg;O93gVBjb}sJZ7h6cT5oMkFw`3r)emi7hnT!{G&^f36JRK|-TL1Pdri_NYYymWXnQDs=RZCt?q`}_=CIdWw zH1^P86Ml?%5+s+IPE4|4kz{2~WxCTz*>>d(l-VjZLxv8xHiwsWtpZbZ-d$ICaZ$^cpUHENW(U38cvaL`f$b@cfj1nBIKwiY= zb0(P@-t>}X7IS)XKmccrYIP#R?MY^jsO!(GObB2% z7P#9Hf%oN=u@!*z83SiN2GV#>POA$;M+UiHfIkJ)z;5qsI9DkC(mEu23nKEypE;pD z{AI+(*ERN%SV!8~Ouke+NmK;R7Ky~5!Wtd%0tu-|deR6rsYn3GwKvOop*wOV0`0&x)i}L!ud$}~~qA_IRSb0p-XNqP*_`0Vk8}VVvJVYc%^8(zq?oWGuSdn|f9OGP zfO1}7y0vL_SQsknrcvTOWO+r#t4+u$DE3+Fc@=udiqe3bDtF(=LlR@_F;W~$^xTM& z8XhOKIMCn0P5tgMpr0*&Gsc|Ak9uWaNM;`Y1 z9X(CkJk=nDkKmlg?Dt4}377yNIwash$of9(p}3d3Md>>3xN9pKGwFWR_cH+T|Y?H6@d zB;jO!h`_D_JLrQGu|HjpbLJ8M8xfl?1Pt^EEj=IN$F7R{w$TY3p~5i#t+6%SUEQBh z8oPi>%mMtavBviH-wf*i6c(kes8_Ofv+X1huYq7plDt;9JX&ZHUA{HKWYt*jD#E5W zQI!>LeK%q(ZG1GmoJ@V(IZ-`4e0T8X-Jr?QM$RxmhJ$lY!GjK*Hxq(w$kC0mbH}|@ z62e#;@CsMaI5tCFtley&jXfST|FbhOZ|)8l@`|97UhfKMnUL?Ye^R)Dfb|@V;;5R z4}``#?|Qk8!L?@Zn+vqy#4Sj|;+MceMsvsXz~$@rTtC9>IAqqR5@~dRoeWf6ZI9lk zgp>be^M-Mgo%WGooas~U!gQbaX7d0%S}dblDz!iY?rHoB9U)qI+i1?S%-|A8b*$qu1rVg-|&6z zNlvM8CO&^hC^XZhn$ZQDd`aHVnT!-C_s!{F9H9m8++=v?Fj{5{@?qGuR(pfq)Bj{9 zUP0$Z4gWFGuuyHHM_XhHQuRXV06WTj-u&6jf8OXR-a_!n63nbUCN(GR5UQfL|2&_CF|o&kDE<|1-4n z7Z?rA%^B^#0zEGi{22@RJ3)}kWrAOkk(XI6#{~Z3X?Ok6ia#O*FXJxK z{1c{d8GLzm`U_ke^#goqu6mi_^7QE!gI>%JhCe1#m+^m&ZhxWC&`#q}$-9@wx|iX9 y4mEyGuMKb;Z) literal 0 HcmV?d00001 diff --git a/requirements/goals_and_use_cases.md b/requirements/goals_and_use_cases.md new file mode 100644 index 0000000..866bafc --- /dev/null +++ b/requirements/goals_and_use_cases.md @@ -0,0 +1,150 @@ +Move this to docx + +# Goals: +1. Define use cases +2. Define modes +3. Define behaviors +4. Define IMU motion sensitivities and hysterisis +4. Generate requirements +5. Code +6. Test +7. Maintain user interface, especially three button interface, so we don't piss off customers who learn one way to have the rug pulled out from under them because another customer made a persausive argument to make a change. + A. Also reduces the number of demo videos we would have to make. + B. Also reduces support calls. (We want to scale, cannot clone Kenny, and even if we could) +8. Limit changes to three button interface to absolute minimum. + +## Possible 3-button UI changes +1. Keep left to right 3 button motion for pairing/enabling bluetooth. +2. Implement right to left 3 button motion to disable bluetooth (without losing pairing keys) +3. Add mode change to the middle button hold-to-config. + A. Make the mode change the first option for quick change. + B. Display rainbow or TBD in 1/4 ring increments for different modes + i. 1/4 - Treestand Hunting (Default) + ii. 1/2 - Still Hunting ; May be too complicated, doesn't cover future bright ideas. + iii. 3/4 - 3D / Field ; May be too complicated, doesn't cover future bright ideas. + iv. Full - Target ; May be too complicated, doesn't cover future bright ideas. + +Recommend only revert to default without app. See Goal 7. The three button interface is already to complex for most people, who will use application anyway. + +## Possible Cyber Shot UI changes +1. Allow users to setup their own scenerios. + A. Scenerios can be named and saved for loading up settings the user previously liked. +2. We can provide templates (e.g., Vegas, Still Hunting) as starting points that can be modified and renamed. +3. Configurable options include: + A. How to turn on + B. If, how, and when to disable illumination. (e.g., 1 shot, N shots, timer, motion, combination thereof) + C. If, how, and when to enable bluetooth. (e.g., 1 shot, N shots, timer, motion, combination thereof) + D. If, how, and when to transfer shots. (E.g., automatically after 1 shot, automatically after N shots, only when requested) + E. How to enter lowest power mode (button, timeout, lack of motion, etc.) + + +# Illumination (On/off) settings +## Auto illumination setting + 1. Button + 2. IMU based setting AI_IMU_MOV0 +## Auto deillumination setting + 1. Button based + 2. IMU based setting ADI_IMU_STILL0 + 3. IMU based shot detected ADI_IMU_SD0 + 4. Timer based setting ADI_TIMER0 + +# Bluetooth settings +## Bluetooth pairing + 1. LCR roll +## Auto Bluetooth enable + 1. Auto bluetooth was previously enabled and illumination just enabled + 2. IMU based shot detected AI_IMU_BT0 +## Auto Bluetooth shot transfer +## Manual Bluetooth shot transfer +## Auto Bluetooth disable +## Manual bluetooth disable + 1. RCL roll + + +## Use cases + +Goals: Preserve battery life without annoying the archer. Archer may actively be using bluetooth at times and may want to connect/autoconnect (assumes previously paired). Archer may want to have the radio disabled to preserve battery life in other situations. + +### Stand Hunting +Auto illumination on and auto illumination off presents technical challanges while stand hunting. + + Typically Pick Bow from bow hanger, less frequently hunter is holding bow. Many hunters will want auto +illumination, while others may want push button. + For auto illumination, if the hunter is holding the bow for long periods, the scope may stay illuminated for long + periods without any benefit, draining battery, due to motion sensitivity setting to tigger illumuination off. +. + However, requiring a button push to illuminate while stand hunting may be annoying to some, and if possible we should endeavor to use + motion hysterisys or some other motion based solution. + + Illumination Time The archer may draw back for extended periods waiting for the best, most ethical kill shot. If possible + detecting bow draw and staying illuminated until release would be goood, to avoid time based trigger to turn + Illumination off. + Shots Typicallly 1 + Enable: Cyber Shot app only, Disable: TBD. + 3D / Field Enable: Cyber Shot app only, Disable: TBD. + Target Enable: Cyber Shot app only, Disable: TBD. + +- Archer is elevated and stationary, often in saddle or tree stand. Bow is often on a bow hanger. Wind may rock bow. Archer may hold bow for extended times, and may hold the bow drawn for extended times. Archer may take follow up shot if opportunity or exigencies require. + +### Still Hunting +- Archer spot-and-stalk / ground hunting. Archer and bow is mobile. Archer may hold bow for extended times, and may hold the bow drawn for extended times. Archer may take follow up shot if opportunity or exigencies require. Phone with app most often stays with archer, needs to auto reconnect with cyber scope, if archer is using shot data with scoring. Archer may select to only enable bluetooth after shot, waiting for game to bleed out. + +### Target + +#### NFAA Indoor round + +Bows stay behind line during arrow retrieval, archers walk downrange to score and retrieve. Phone with app most often stays with archer who may be using it to log scores. The phone app needs to automatically reconnect with cyber scope if archer is using shot data with scoring. + +Note: Possible conflict. + 4.3 The method of breaking ties will be at the discretion of the tournament director. + 5.2 X-rings shall be counted and used as tie breakers and will be considered part of the official score. + +Kenny says: In the event of tie, and Xs are also tied, in practice the tournament director can do 1 arrow shoot off closest to center, or 20 arrow most "inside out" (arrow is inside the X without touching circle) Xs. + + +##### One Spot Indoor round +-One spot indoor round, NFAA Indoor (300): standard unit is 60 arrows. end of 5 arrows, 4 ends per game, 3 games per round, max score of 300. Possible some events may not use Xs as tiebreaker. + +##### Five spot Indoor round +-Five spot indoor round, NFAA Indoor (300): standard unit is 60 arrows. end of 5 arrows, 4 ends per game, 3 games per round, max score of 300. Possible some events may not use Xs as tiebreaker. + +#### Vegas Round + +-Vegas round, NFAA Indoor (900): standard unit of 30 arrows. end of 3 arrows, 5 ends per game. 3 games per round, max score of 900. Bullseye 10 points, X is a tie breaker. 3 targets/spots on sheet, archer can use any or all. + +-NFAA Outdoor Target: end of 5 arrows. 4 minutes per end. Classic 600 + 900 Round shot across two days. + +-NFAA Outdoor Field: Field, Hunter, and Animal Rounds across three days. Each round typically 28 targets, 4 arrows per target (112 arrows per round). Walk-through course--archers carry bows to target to score and pull arrows, then to next station. Some hang bow on rack at target while pulling arrows, but bow is carried the whole course. Phone with app most often stays with archer, needs to auto reconnect with cyber scope, if archer is using shot data with scoring. + +References: + -https://nfaausa.com/files/nfaa-round-basics-160223233655.pdf + + -https://nfaausa.com/about/constitution/official-nfaa-rounds + +### 3D +-ASA: 1 arrow per shooter per target, shot from designated stake. Pro/Am: two rounds of 20 targets (40 arrows total, one or two days). Federation qualifiers: minimum one round of 20 targets in one day. No strict per-arrow time limit--pacing is group-based. + +-IBO: 40 targets, walk-through course, 1 arrow per target, max score 440. World Championship: two 20-target courses over two days. No per-shot time limit--group pacing applies. + +-Regional Park Walk-Through: (rules typically require bow left on hanger when retrieving arrows, with some exceptions) + +### Indoor practice +- Arrows are shot. Bow stays behind line during arrow retrieval, archers walk downrange to retrieve arrows. Phone with app most often stays with archer, needs to auto reconnect with cyber scope, if archer is using shot data with or without scoring. + +### Outdoor practice +- Field + One or more arrows are shot at each target. Bow is hung at the shooting zone, archers walk to target to retrieve arrows. Phone with app most often stays with archer, needs to auto reconnect with cyber scope, if archer is using shot data with or without scoring. + +- Target + Arrows are shot. Bow stays behind line during arrow retrieval, archers walk downrange to retrieve arrows. Phone with app most often stays with archer, needs to auto reconnect with cyber scope, if archer is using shot data with or without scoring. + +### 3D practice +- Arrows are shot. Bow stays behind line during arrow retrieval, archers walk downrange to retrieve arrows. Phone with app most often stays with archer, needs to auto reconnect with cyber scope, if archer is using shot data with or without scoring. + +### Future +- Future competition rules may come out requiring future updates. A way to avoid this is to allow the user to define his own scenerio as discussed in "Possible Cyber Shot UI changes" above. + +### Excluded +-USA Archery / World Archery: No electronics of any kind on bow. Cyber Scope not permitted. + + diff --git a/requirements/tbd.md b/requirements/tbd.md new file mode 100644 index 0000000..ca24a68 --- /dev/null +++ b/requirements/tbd.md @@ -0,0 +1,94 @@ +# Move this to docx + +# Cyber Scope--Mode Use Cases (Draft) + +## Power States (Working Definitions) + +The following states are tenatively defined. However, each scenerio may have different settings for a state. This is just preliminary, need to work out the logic. + +|---------|-----------------------------------------------------------------------------------------------------| +| State | Description | +|---------|-----------------------------------------------------------------------------------------------------| +| Off | Lowest sleep mode. Wake only on center button press. | +| Sleep | Lowest MCU sleep mode, IMU wakes on acceleration threshold met. | +| Idle | MCU low-power, IMU sampling. No illumination. Bluetooth active. | +| Active | Full illumination per user config. IMU active. | +|---------|-----------------------------------------------------------------------------------------------------| + +--- + +|---------------|---------------------------------------------------------------| +| Mode | How to Enable/Disable | +|---------------|---------------------------------------------------------------| +| Stand Hunting | Default. Selectable via 3-button interface or Cyber Shot app. | +| Still Hunting | Enable: Cyber Shot app only, Disable: TBD. | +| 3D / Field | Enable: Cyber Shot app only, Disable: TBD. | +| Target | Enable: Cyber Shot app only, Disable: TBD. | +|---------------|---------------------------------------------------------------| + +- Mode is sticky--survives power cycles until changed. +- Return to Stand Hunting is always possible via 3-button interface (no app required). +- Entry into Still, 3D, or Target requires Cyber Shot app to keep 3-button interface manageable. + +--- + +## Mode 1: Stand Hunting (Default Mode) + +- Bow on hanger for long periods, occasionally picked up. +- On draw, archer may hold at full draw 5+ minutes waiting for shot opportunity. +- Wake: IMU motion above threshold (deliberate pickup, not wind/vibration). +- Stay Active: Sleep threshold lower than wake threshold--scope stays lit through extended stillness at full draw. +- Active → Sleep: Return to stillness below sleep threshold for duration 10 seconds. +- Off → Off: Center button full power-off; center button power-on shows battery on alignment ring. +- Post-shot: Archer may review shot data on phone via Cyber Shot app. May happen minutes to hours later. + +--- + +## Mode 2: Still Hunting + +- Archer stalking with bow in hand all day. Constant motion. +- IMU wake would drain battery--motion is continuous. +- Wake: Center button only. +- Active: Illumination stays on until manually turned off. No auto-off on shot detection--archer may need follow-up shot. +- Off: Center button only. No timeout, no IMU-based sleep. +- Post-shot: No change in state. Illumination remains on. +- Shot data review: Archer may review shot data on phone via Cyber Shot app. May happen minutes to hours later. + +--- + +## Mode 3: 3D + +- Archer walks course, carries bow between stations. +- At station, archer presses center button → Active (no battery display, immediate illumination). +- Archer shoots. IMU detects shot → illumination off immediately. +- Archer carries bow to target, retrieves arrows, walks to next station. Scope stays non-illuminated despite motion. +- Wake: Center button only. IMU motion does NOT trigger illumination. +- Post-shot: Illumination off immediately. Scope enters Off or Idle. +- Timeout: 2 minutes, may add as fallback if archer forgets to shoot / walks off. +- Shot data review: Archer may review shot data and enter score per shot in Cyber Shot app. Typically 1 shot per station. + +--- + +## Mode 4: Target + +- Bow on stand or hanger between ends/practice groups. May swing due to stabilizers. +- Archer picks up bow and IMU wakes MCU → Active. +- Shoots multiple arrows per end. Timer resets on each shot detected. +- When done, archer hangs bow or sets on stand. +- Wake: IMU motion above threshold (must distinguish deliberate pickup from stabilizer oscillation on hanger). +- Stay Active: Timeout resets on each shot detected and on each pickup event. +- Active → Sleep: Timeout expires with no shot/pickup. +- Known issue: Target bows with stabilizers oscillate on hangers, preventing motion-based idle. Mitigation: archer presses center button or settles bow. Firmware does not attempt to distinguish oscillation from pickup. +- Bluetooth: TBD--may use Standby for shot data transfer after end. +- Shot data review: Archer may review shot data and enter score per shot in Cyber Shot app. Typically 5 shots per end. Shot data correlated with score for replay. + +--- + +## Open Questions + +1. Standby vs Idle distinction: depends on whether Bluetooth is needed post-shot (3D, Target). Deferred to per-mode state definitions. +2. All timeout durations TBD. +3. Stand Hunting asymmetric wake/sleep thresholds--values TBD. +4. Battery display: shown on full power-on (Stand Hunting). Suppressed on center-button quick wake (3D). Target TBD. +5. 3D center button behavior: first press → Active, second press → Off (full power-on with battery display on next press). Post-shot state TBD (Idle vs Off, depends on Bluetooth/shot data transfer needs). +6. Per-mode state definitions (STAND_S0, STILL_S0, 3D_S0, TARGET_S0, etc.)--deferred until use cases complete. diff --git a/src/app/app.c b/src/app/app.c new file mode 100644 index 0000000..397dfce --- /dev/null +++ b/src/app/app.c @@ -0,0 +1,537 @@ +/** + * @file app.c + * @brief Application module + * @author Kenny Tran + * @date May 05 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include + +#include "../lib/errno.h" +#include "../lib/print.h" +#include "../lib/fs.h" + +#include "../bsp/als.h" +#include "../bsp/battery.h" +#include "../bsp/button.h" +#include "../bsp/imu.h" +#include "../bsp/led_ring.h" +#include "../bsp/led.h" +#include "../bsp/mcu.h" +#include "../bsp/nvm_ext.h" +#include "../bsp/power.h" +#include "../bsp/rtc.h" +#include "../bsp/wdt.h" + +#include "app_cli.h" +#include "ble.h" +#include "bubble.h" +#include "fiber.h" +#include "fsm.h" +#include "event.h" +#include "light_sensor.h" +#include "motion.h" +#include "pin.h" +#include "scope_ring.h" +#include "shot_storage.h" +#include "shot_capture.h" + +#include "app.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS, STRUCTURES & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static FS_file_t _app_file; +static APP_info_s _app_info = (APP_info_s) { + .main_pcb_version = { 1, 0, 0 }, + .ring_pcb_version = { 1, 0, 0 }, + .version = { APP_MAJOR, APP_MINOR, APP_BUILD }, + .mfg_id = 888000000, + .mfg_date = 20251215, + .pn = APP_PN, + .sn = APP_SN, + .power_cnt = 0, + .cli_enabled = (uint8_t) true, + .imu_temp_offset = 25 +}; + + +/*------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *-----------------------------------------------------------------------------------------------*/ +static void _app_init (void); +static void _app_cmd_register (void); +static void _app_cli_cmd (int32_t argc, char **argv); +static void _app_info_read (void); +static void _app_info_write (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void APP_init (void) +{ + volatile uint32_t irq_key = irq_lock (); + + /* BSP */ + (void) MCU_init (); + + /* Initialize filesystem for file IO. */ + (void) FS_init (); + + /* Initialize and start watchdog. */ + (void) WDT_init (); + + /* Fetch application info stored. */ + _app_info_read (); + /* Update power count. */ + _app_info.power_cnt++; + /* Write updates to flash. */ + _app_info_write (); + + /* Initialize app modules. */ + _app_init (); + + /* Register app command. */ + if (_app_info.cli_enabled) + { + _app_cmd_register (); + } + + (void) irq_unlock (irq_key); +} + +void APP_start (void) +{ + /* Install application tasks */ + if (_app_info.cli_enabled) + { + APP_CLI_task_install (); + } + + EVENT_task_install (); + MOTION_task_install (); + SCOPE_RING_task_install (); + FSM_task_install (); + + /* README: Not used in initial release. */ + SHOT_CAPTURE_task_install(); + SHOT_STORAGE_task_install(); + + /* Install BSP tasks */ + IMU_task_install (); + IMU_interrupt_handler_task_install (); +} + +void APP_reset (void) +{ + MCU_reset (true); +} + +void APP_cli_enable (bool en) +{ + _app_info.cli_enabled = (uint8_t) en; + + /* Write updates to flash. */ + _app_info_write (); + + if (MCU_reset (true) != ERRNO_SUCCESS) + { + PRINT (INFO, "Failure to reset.\r\n"); + } +} + +void APP_factory_reset (void) +{ + /* Get current application info to preserve after erasing flash. */ + APP_info_s info = APP_info_get (); + /* Get motion calibration info. */ + MOTION_config_s *p_motion_cfg = MOTION_config_get (); + + PRINT (INFO, "Performing factory reset...\r\n"); + + /* Erase entire flash. */ + if (NVM_EXT_erase_all () != ERRNO_SUCCESS) + { + PRINT (INFO, "Failure to erase flash.\r\n"); + } + + /* Restore application info that should be preserved. */ + _app_info = info; + + /* Update app version. */ + _app_info.version.major = APP_MAJOR; + _app_info.version.minor = APP_MINOR; + _app_info.version.build = APP_BUILD; + /* Clear power count. */ + _app_info.power_cnt = 0; + + /* With flash erased, calling _app_info_read should find no file and write a new file + with current info structure. */ + _app_info_read (); + + /* Set back calibration info so we don't lose it. */ + MOTION_config_set (*p_motion_cfg); + + if (MCU_reset (true) != ERRNO_SUCCESS) + { + PRINT (INFO, "Failure to reset.\r\n"); + } +} + +void APP_temp_calibrate (float current_temp) +{ + float temp = 0.0; + + if (IMU_temp_c_read (&temp) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read IMU temp sensor.\r\n"); + return; + } + + /* Update offset and save to flash. */ + _app_info.imu_temp_offset = (current_temp - temp); + + _app_info_write (); +} + +float APP_temp_c_get (void) +{ + float temp = 0.0; + + if (IMU_temp_c_read (&temp)) + { + PRINT (ERROR, "Failure to read IMU temp sensor.\r\n"); + } + + return (temp + _app_info.imu_temp_offset); +} + +APP_info_s APP_info_get (void) +{ + return _app_info; +} + + +/*------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *-----------------------------------------------------------------------------------------------*/ +static void _app_init (void) +{ + /* Application */ + if (_app_info.cli_enabled) + { + APP_CLI_init (); + } + else + { + /* Disable PRINT. */ + PRINT_level_set (DISABLED); + } + + BLE_init (); + BUBBLE_init (); + FIBER_init (); + FSM_init (); + EVENT_init (); + LIGHT_SENSOR_init (); + MOTION_init (); + PIN_init (); + SCOPE_RING_init (); + + /* README: Not used in initial release. */ + SHOT_STORAGE_init(); + SHOT_CAPTURE_init(); +} + +static void _app_cmd_register (void) +{ + /* APP */ + CLI_cmd_register (CLI_CMD_APP, (char *) "app", _app_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "ble", BLE_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "bubble", BUBBLE_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "fiber", FIBER_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "fsm", FSM_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "event", EVENT_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "light_sensor", LIGHT_SENSOR_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "motion", MOTION_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "pin", PIN_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "scope_ring", SCOPE_RING_cli_cmd); + /* README: Not used in initial release. */ + CLI_cmd_register (CLI_CMD_APP, (char *) "ss", SHOT_STORAGE_cli_cmd); + CLI_cmd_register (CLI_CMD_APP, (char *) "sc", SHOT_CAPTURE_cli_cmd); + + /* BSP */ + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "als", ALS_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "battery", BATTERY_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "button", BUTTON_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "imu", IMU_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "led_ring", LED_RING_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "led", LED_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "mcu", MCU_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "nvm", NVM_EXT_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "power", POWER_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "rtc", RTC_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "wdt", WDT_cli_cmd); + + /* LIB */ + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "errno", ERRNO_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "fs", FS_cli_cmd); + CLI_cmd_register (CLI_CMD_DEBUG, (char *) "print", PRINT_cli_cmd); +} + +static void _app_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "APP\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Main PCB Version : %d.%d.%d\r\n", _app_info.main_pcb_version.major, + _app_info.main_pcb_version.minor, + _app_info.main_pcb_version.build); + PRINT (INFO, "Ring PCB Version : %d.%d.%d\r\n", _app_info.ring_pcb_version.major, + _app_info.ring_pcb_version.minor, + _app_info.ring_pcb_version.build); + PRINT (INFO, "Build Version : %d.%d.%d\r\n", _app_info.version.major, + _app_info.version.minor, + _app_info.version.build); + PRINT (INFO, "Build DTS : %s %s\r\n", APP_BUILD_DATE, APP_BUILD_TIME); + PRINT (INFO, "Part Number : %s\r\n", _app_info.pn); + PRINT (INFO, "Serial Number : %s\r\n", _app_info.sn); + PRINT (INFO, "MFG ID : %d\r\n", _app_info.mfg_id); + PRINT (INFO, "MFG Date : %d\r\n", _app_info.mfg_date); + PRINT (INFO, "Power On Count : %d\r\n", _app_info.power_cnt); + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-cli")) + { + if (!strcmp (argv[1], "true")) + { + APP_cli_enable (true); + } + else if (!strcmp (argv[1], "false")) + { + APP_cli_enable (false); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-mfgid")) + { + _app_info.mfg_id = strtoul (argv[1], NULL, 0); + + /* Write changes to flash. */ + _app_info_write (); + } + else if (!strcmp (argv[0], "-mfgdate")) + { + _app_info.mfg_date = strtoul (argv[1], NULL, 0); + + /* Write changes to flash. */ + _app_info_write (); + } + else if (!strcmp (argv[0], "-pn")) + { + uint8_t len = strlen(argv[1]); + + /* -1 for null termination. */ + if (len > (sizeof (_app_info.pn)-1)) + { + PRINT (INFO, "Max length of %d characters!\r\n", (sizeof (_app_info.pn)-1)); + return; + } + + memset (_app_info.pn, 0, sizeof (_app_info.pn)); + memcpy (_app_info.pn, argv[1], len); + + /* Write changes to flash. */ + _app_info_write (); + } + else if (!strcmp (argv[0], "-sn")) + { + char sn[sizeof (_app_info.sn)]; + uint8_t len = strlen(argv[1]); + + /* -4 for null termination and "CS_" prefix for BLE. */ + if (len > (sizeof (_app_info.sn)-4)) + { + PRINT (INFO, "Max length of %d characters!\r\n", (sizeof (_app_info.pn)-4)); + return; + } + + memset (_app_info.sn, 0, sizeof (_app_info.sn)); + memcpy (_app_info.sn, argv[1], len); + + /* Write changes to flash. */ + _app_info_write (); + + /* Setup initial advertising name to prefix + sn. */ + strcpy (sn, BLE_DEVICE_NAME_PREFIX); + strcat (sn, _app_info.sn); + + BLE_adv_name_set (sn); + } + else if (!strcmp (argv[0], "-tc")) + { + float offset = strtof (argv[1], NULL); + + APP_temp_calibrate (offset); + } + else + { + goto USAGE; + } + } + else if (argc == 4) + { + if (!strcmp (argv[0], "-mpcb")) + { + _app_info.main_pcb_version.major = (uint8_t) strtoul (argv[1], NULL, 0); + _app_info.main_pcb_version.minor = (uint8_t) strtoul (argv[2], NULL, 0); + _app_info.main_pcb_version.build = (uint8_t) strtoul (argv[3], NULL, 0); + + /* Write changes to flash. */ + _app_info_write (); + } + else if (!strcmp (argv[0], "-rpcb")) + { + _app_info.ring_pcb_version.major = (uint8_t) strtoul (argv[1], NULL, 0); + _app_info.ring_pcb_version.minor = (uint8_t) strtoul (argv[2], NULL, 0); + _app_info.ring_pcb_version.build = (uint8_t) strtoul (argv[3], NULL, 0); + + /* Write changes to flash. */ + _app_info_write (); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: app [OPTION...]\r\n"); + PRINT (INFO, " -cli Enables or disables command line.\r\n"); + PRINT (INFO, " <%%b> true to enable, false to disable.\r\n"); + PRINT (INFO, " -mpcb Sets the main pcb version.\r\n"); + PRINT (INFO, " <%%d> Major\r\n"); + PRINT (INFO, " <%%d> Minor\r\n"); + PRINT (INFO, " <%%d> Build\r\n"); + PRINT (INFO, " -rpcb Sets the ring pcb version.\r\n"); + PRINT (INFO, " <%%d> Major\r\n"); + PRINT (INFO, " <%%d> Minor\r\n"); + PRINT (INFO, " <%%d> Build\r\n"); + PRINT (INFO, " -mfgid Sets the manufacturing ID.\r\n"); + PRINT (INFO, " <%%d> Manufacturing ID\r\n"); + PRINT (INFO, " -mfgdate Sets the manufacturing date.\r\n"); + PRINT (INFO, " <%%d> Format YYYYMMDD\r\n"); + PRINT (INFO, " -pn Sets the part number.\r\n"); + PRINT (INFO, " <%%s> Part number (max of 15 chars)\r\n"); + PRINT (INFO, " -sn Sets the part number.\r\n"); + PRINT (INFO, " <%%s> Part number (max of 12 chars)\r\n"); + PRINT (INFO, " -tc Calibrates temperature sensor offset\r\n"); + PRINT (INFO, " <%%f> current temperature\r\n"); + } +} + +static void _app_info_read (void) +{ + ERRNO_e err; + APP_info_s info; + + err = FS_open_or_create (APP_FILENAME, &_app_file, sizeof (APP_info_s), (FS_FILE_DEPTH_DEFAULT * 8)); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_app_file, (uint8_t *) &info, 0, sizeof (APP_info_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + /* Write defaults to file. */ + _app_info_write (); + return; + } + + /* Validate read file. */ + if (info.power_cnt > 1000000) + { + /* Write defaults to file. */ + _app_info_write (); + return; + } + + /* Update app info to what was read from flash and validated. */ + _app_info = info; + + /* Update app version if, current is newer. */ + _app_info.version.major = APP_MAJOR; + _app_info.version.minor = APP_MINOR; + _app_info.version.build = APP_BUILD; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* App info has been modified, need to delete old file and write new info. */ + err = FS_delete (APP_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old app file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (MOTION_FILENAME, &_app_file, sizeof (APP_info_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create app file.\r\n"); + } + else + { + PRINT (INFO, "New app file created successfully\r\n"); + } + + /* Write default info to file. */ + _app_info_write (); + } + else + { + /* Write defaults to file. */ + _app_info_write (); + } +} + +static void _app_info_write (void) +{ + if (FS_write (_app_file, (uint8_t *) &_app_info) != ERRNO_SUCCESS) + { + PRINT (INFO, "Failed to write app info to file.\r\n"); + } +} diff --git a/src/app/app.h b/src/app/app.h new file mode 100644 index 0000000..409d765 --- /dev/null +++ b/src/app/app.h @@ -0,0 +1,93 @@ +/** + * @file app.h + * @brief Application module + * @author Kenny Tran + * @date May 05 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef APP_H_ +#define APP_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define APP_MAJOR (1) +#define APP_MINOR (3) +#define APP_BUILD (2) +#define APP_BUILD_DATE (__DATE__) +#define APP_BUILD_TIME (__TIME__) +#define APP_PN ("CYBERSCOPE-000") +#define APP_SN ("251215-XXX") + +#define APP_FILENAME ("app") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct APP_version +{ + uint8_t major : 8; + uint8_t minor : 8; + uint8_t build : 8; +} __attribute__((packed, aligned(1))) APP_version_s; + +typedef struct APP_info +{ + APP_version_s main_pcb_version; + APP_version_s ring_pcb_version; + APP_version_s version; + uint32_t mfg_id; + uint32_t mfg_date; /* Format YYYYMMDD */ + char pn[16]; + char sn[16]; + uint32_t power_cnt; + uint8_t cli_enabled; + float imu_temp_offset; +} __attribute__((packed, aligned(1))) APP_info_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void APP_init (void); + +void APP_start (void); + +void APP_reset (void); + +void APP_cli_enable (bool en); + +void APP_factory_reset (void); + +void APP_temp_calibrate (float current_temp); + +float APP_temp_c_get (void); + +APP_info_s APP_info_get (void); + + +#endif /* APP_H_ */ diff --git a/src/app/app_cli.c b/src/app/app_cli.c new file mode 100644 index 0000000..618b1c2 --- /dev/null +++ b/src/app/app_cli.c @@ -0,0 +1,109 @@ +/** + * @file cli.c + * @brief Command line interface + * @author Kenny Tran + * @date Apr 25 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include + +#include "../lib/errno.h" +#include "../lib/print.h" +#include "../lib/cli.h" + +#include "app_cli.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +K_THREAD_STACK_DEFINE(_app_cli_stack, APP_CLI_STACK_SIZE); + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static struct k_thread _app_cli_thread; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _app_cli_task (void *p1, void *p2, void *p3); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void APP_CLI_init (void) +{ + PRINT (INFO, "Initializing APP_CLI...\r\n"); + + /* Initialize CLI. */ + if (CLI_init () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize CLI\r\n"); + + } +} + +void APP_CLI_task_suspend (void) +{ + k_thread_suspend (&_app_cli_thread); +} + +void APP_CLI_task_resume (void) +{ + k_thread_resume (&_app_cli_thread); +} + +void APP_CLI_task_install (void) +{ + static k_tid_t id; + + id = k_thread_create (&_app_cli_thread, + _app_cli_stack, + APP_CLI_STACK_SIZE, + _app_cli_task, + NULL, + NULL, + NULL, + APP_CLI_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&_app_cli_thread, "APP_CLI_task"); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _app_cli_task (void *p1, void *p2, void *p3) +{ + while (1) + { + CLI_task (); + + k_msleep (APP_CLI_TASK_PERIOD_MS); + } +} diff --git a/src/app/app_cli.h b/src/app/app_cli.h new file mode 100644 index 0000000..dd6d295 --- /dev/null +++ b/src/app/app_cli.h @@ -0,0 +1,50 @@ +/** + * @file cli.h + * @brief Command line interface + * @author Kenny Tran + * @date Apr 25 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef APP_CLI_H_ +#define APP_CLI_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include "../lib/cli.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define APP_CLI_TASK_PERIOD_MS (CLI_TASK_PERIOD_MS) +#define APP_CLI_STACK_SIZE (2048) +#define APP_CLI_THREAD_PRIORITY (5) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void APP_CLI_init (void); + +void APP_CLI_task_suspend (void); + +void APP_CLI_task_resume (void); + +void APP_CLI_task_install (void); + +void APP_CLI_cmd (int32_t argc, char **argv); + + +#endif /* APP_CLI_H_ */ diff --git a/src/app/ble.c b/src/app/ble.c new file mode 100644 index 0000000..11d9345 --- /dev/null +++ b/src/app/ble.c @@ -0,0 +1,2265 @@ +/** + * @file ble.c + * @brief BLE module + * @author Kenny Tran + * @date Jul 06 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../lib/errno.h" +#include "../lib/fs.h" +#include "../lib/print.h" +#include "../lib/rgb.h" + +#include "../bsp/als.h" +#include "../bsp/rtc.h" +#include "../bsp/wdt.h" + +#include "app.h" +#include "bubble.h" +#include "fiber.h" +#include "fsm.h" +#include "motion.h" +#include "pin.h" +#include "scope_ring.h" +#include "shot_capture.h" +#include "shot_storage.h" +#include "uuid.h" + +#include "ble.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _BLE_DEVICE_NAME_LEN (sizeof(BLE_DEVICE_NAME) - 1) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS, STRUCTURES & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct bt_conn_cb _ble_conn_s; +typedef struct bt_data _ble_data_s; +typedef struct bt_conn_auth_cb _ble_conn_auth_s; +typedef struct bt_conn_auth_info_cb _ble_conn_auth_info_s; +typedef struct bt_gatt_exchange_params _ble_mtu_exchange_params_s; +typedef struct bt_gatt_cb _ble_gatt_cb_s; +typedef struct k_work_delayable _ble_delayed_work_s; +typedef struct _ble_char +{ + uint32_t uuid[4]; /* 128-bit */ + bool notify; + struct bt_gatt_attr *p_notify_service; + BLE_read_cb_t read_cb; + BLE_write_cb_t write_cb; + BLE_ccc_cb_t ccc_cb; +} _ble_char_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +/* BLE core */ +static FS_file_t _ble_settings_file = NULL; +static FS_file_t _ble_config_file = NULL; +static bool _ble_fs_ready = false; +static atomic_t _ble_settings_loading = ATOMIC_INIT(0); +static bool _ble_enabled = false; +static bool _ble_connected = false; +static struct bt_conn *_ble_current_conn = NULL; +static BLE_settings_s _ble_settings = { 0 }; +static BLE_config_s _ble_config = (BLE_config_s) { + .adv_name = BLE_DEVICE_NAME_PREFIX, + .enabled = false, +}; +static _ble_conn_s _ble_conn_cbs; +static _ble_conn_auth_s _ble_conn_auth_cbs; +static _ble_conn_auth_info_s _ble_conn_auth_info_cbs; +static _ble_mtu_exchange_params_s _ble_mtu_exchange_params; +static _ble_gatt_cb_s _ble_gatt_cbs; + +static _ble_data_s _ble_ad_data[] = +{ + (_ble_data_s) { + .type = BT_DATA_FLAGS, + .data_len = sizeof((uint8_t []) { (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR) }), + .data = (const uint8_t *) ((uint8_t []) { (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR) }) + }, + (_ble_data_s) { + .type = BT_DATA_NAME_COMPLETE, + .data_len = (sizeof (BLE_DEVICE_NAME_PREFIX) - 1), + .data = (const uint8_t *) BLE_DEVICE_NAME_PREFIX + } +}; +static const _ble_data_s _ble_scan_data[] = +{ + BT_DATA_BYTES(BT_DATA_UUID128_ALL, UUID_CONTROLS_SERVICE_VAL) +}; +static _ble_delayed_work_s _ble_delayed_work; +static _ble_char_s _ble_char_table[BLE_CHAR_COUNT]; + +/* Shot data transfer state. */ +static uint8_t _ble_shot_data_status = 0; +static uint8_t *_ble_shot_data_buf = NULL; +static uint32_t _ble_shot_data_total_len = 0; +static uint32_t _ble_shot_data_sent = 0; +static bool _ble_shot_data_transferring = false; +static uint16_t _ble_shot_data_chunk_size = 0; +static struct k_work _ble_eraseall_work; +static struct k_work _ble_dur_loc_work; +static uint8_t _ble_dur_loc_duration; +static uint8_t _ble_dur_loc_location; + +/* BLE pairing. */ +static struct k_work _ble_pair_smp_work; +static struct k_work_delayable _ble_pair_delay_work; +static struct bt_conn *_ble_pair_conn = NULL; +static struct k_work_delayable _ble_settings_write_dwork; +static struct k_work_delayable _ble_bond_clear_dwork; + +/* Custom settings backend (persists Zephyr BT bond keys into _ble_config). */ +static struct settings_store_itf _settings_itf; +static struct settings_store _settings_store; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +/* Connection callbacks */ +static void _ble_connected_cb (struct bt_conn *conn, uint8_t err); +static void _ble_disconnected_cb (struct bt_conn *conn, uint8_t err); +static void _ble_security_changed_cb (struct bt_conn *conn, bt_security_t level, + enum bt_security_err err); +static void _ble_auth_cancel_cb (struct bt_conn *conn); +static void _ble_pairing_complete_cb (struct bt_conn *conn, bool bonded); +static enum bt_security_err _ble_pairing_accept_cb (struct bt_conn *conn, const struct bt_conn_pairing_feat *const feat); +static void _ble_pairing_failed_cb (struct bt_conn *conn, enum bt_security_err reason); +static void _ble_att_mtu_updated_cb (struct bt_conn *conn, uint16_t tx, uint16_t rx); +static void _ble_pair_delay_handler (struct k_work *work); +static void _ble_mtu_exchange_cb (struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params); + +/* Config persistence. */ +static void _ble_settings_read (void); +static void _ble_settings_write (void); + +static void _ble_config_read (void); +static void _ble_config_write (void); + +/* Advertising name characteristic */ +static ssize_t _ble_adv_name_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_adv_name_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); + +/* Bond clear. */ +static ssize_t _ble_bond_clear_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); + +static void _ble_reenable_later (struct k_work *work); + +/* Work handlers */ +static void _ble_eraseall_work_handler (struct k_work *work); +static void _ble_dur_loc_work_handler (struct k_work *work); +static void _ble_settings_write_work_handler (struct k_work *work); +static void _ble_bond_clear_dwork_handler (struct k_work *work); + +/* APP characteristics */ +static ssize_t _ble_app_main_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_ring_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_mfg_id_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_mfg_date_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_pn_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_sn_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_power_count_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_cli_enabled_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags); +static ssize_t _ble_app_cli_enabled_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_app_reset_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags); + +/* SCOPE RING characteristics */ +static ssize_t _ble_level_option_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_level_option_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_level_option_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_level_viscosity_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_level_viscosity_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_level_viscosity_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_level_sensitivity_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_level_sensitivity_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_level_sensitivity_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_ring_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_ring_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_ring_color_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_ring_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_ring_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_ring_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_indicator_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_indicator_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_indicator_color_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_indicator_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_indicator_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_indicator_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_level_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_level_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_level_color_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_level_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_level_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_level_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value); + +/* BUBBLE / FIBER / PIN */ +static ssize_t _ble_bubble_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_bubble_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_bubble_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_fiber_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_fiber_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_fiber_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_pin_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_pin_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_pin_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value); + +/* MOTION */ +static ssize_t _ble_level_calibrate_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_level_calibrate_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_level_calibrate_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_level_angle_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_level_angle_changed (const struct bt_gatt_attr *attr, uint16_t value); + +/* ALS */ +static ssize_t _ble_ambient_light_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_ambient_light_changed (const struct bt_gatt_attr *attr, uint16_t value); + +/* FSM */ +static ssize_t _ble_fsm_timeout_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_fsm_timeout_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_fsm_timeout_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_fsm_auto_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_fsm_auto_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_fsm_auto_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_fsm_mode_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_fsm_mode_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_fsm_mode_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_fsm_app_config_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_fsm_app_config_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); + +/* BATTERY */ +static ssize_t _ble_battery_level_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_battery_level_changed (const struct bt_gatt_attr *attr, uint16_t value); + +/* SHOT CAPTURE */ +static ssize_t _ble_shot_capture_tap_ths_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_capture_tap_ths_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_shot_capture_quiet_time_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_capture_quiet_time_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_shot_capture_shock_time_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_capture_shock_time_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_shot_capture_user_shot_cnt_reset (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_capture_user_shot_cnt_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _shot_capture_user_shot_cnt_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_shot_capture_shot_cnt_reset (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_capture_shot_cnt_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _shot_capture_shot_cnt_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value); +static ssize_t _ble_shot_capture_capture_en_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_capture_capture_en_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_shot_capture_dur_loc_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_capture_dur_loc_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static ssize_t _ble_shot_data_request_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); +static ssize_t _ble_shot_data_request_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +static void _ble_shot_data_request_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value); +static void _ble_shot_data_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value); +static void _ble_shot_data_send_next (struct bt_conn *conn, void *user_data); + +/* BLE PAIRING */ +static void _ble_pair_smp_handler (struct k_work *work); +static void _ble_bond_clear (void); + +/* RTC */ +static ssize_t _ble_rtc_secs_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); +{ + uint32_t s_elapsed = (uint32_t) RTC_seconds_elapsed (); + + return bt_gatt_attr_read (conn, attr, buf, len, offset, s_elapsed, sizeof (s_elapsed)); +} + +/* SETTINGS BACKEND */ +static ssize_t _settings_read_cb (void *cb_arg, void *data, size_t len); +static int _settings_load (struct settings_store *cs, const struct settings_load_arg *arg); +static int _settings_save (struct settings_store *cs, const char *name, + const void *value, size_t val_len); +static int _settings_entry_find (const char *key); +static int _settings_entry_alloc (const char *key); + + +/*-------------------------------------------------------------------------------------------------- + * BLE SERVICES & CHARACTERISTICS + *------------------------------------------------------------------------------------------------*/ +BT_GATT_SERVICE_DEFINE(_ble_service, + BT_GATT_PRIMARY_SERVICE(UUID_CONTROLS_SERVICE), /* Index 0 */ + + /* ADV_NAME - Index 1, 2, 3 */ + BT_GATT_CHARACTERISTIC(UUID_BLE_ADV_NAME, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_adv_name_read, _ble_adv_name_write, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* BOND_CLEAR - Index 4, 5 (write-only, no CCC) */ + BT_GATT_CHARACTERISTIC(UUID_BLE_BOND_CLEAR, BT_GATT_CHRC_WRITE, + BT_GATT_PERM_WRITE, NULL, _ble_bond_clear_write, NULL), + + /* RING_COLOR - Index 6, 7, 8 */ + BT_GATT_CHARACTERISTIC(UUID_RING_COLOR, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_ring_color_read, _ble_ring_color_write, NULL), + BT_GATT_CCC(_ble_ring_color_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* RING_BRIGHTNESS - Index 9, 10, 11 */ + BT_GATT_CHARACTERISTIC(UUID_RING_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_ring_brightness_read, _ble_ring_brightness_write, NULL), + BT_GATT_CCC(_ble_ring_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* INDICATOR_COLOR - Index 12, 13, 14 */ + BT_GATT_CHARACTERISTIC(UUID_INDICATOR_COLOR, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_indicator_color_read, _ble_indicator_color_write, NULL), + BT_GATT_CCC(_ble_indicator_color_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* INDICATOR_BRIGHTNESS - Index 15, 16, 17 */ + BT_GATT_CHARACTERISTIC(UUID_INDICATOR_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_indicator_brightness_read, _ble_indicator_brightness_write, NULL), + BT_GATT_CCC(_ble_indicator_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* LEVEL_COLOR - Index 18, 19, 20 */ + BT_GATT_CHARACTERISTIC(UUID_LEVEL_COLOR, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_color_read, _ble_level_color_write, NULL), + BT_GATT_CCC(_ble_level_color_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* LEVEL_BRIGHTNESS - Index 21, 22, 23 */ + BT_GATT_CHARACTERISTIC(UUID_LEVEL_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_brightness_read, _ble_level_brightness_write, NULL), + BT_GATT_CCC(_ble_level_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* BUBBLE_BRIGHTNESS - Index 24, 25, 26 */ + BT_GATT_CHARACTERISTIC(UUID_BUBBLE_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_bubble_brightness_read, _ble_bubble_brightness_write, NULL), + BT_GATT_CCC(_ble_bubble_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* FIBER_BRIGHTNESS - Index 27, 28, 29 */ + BT_GATT_CHARACTERISTIC(UUID_FIBER_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fiber_brightness_read, _ble_fiber_brightness_write, NULL), + BT_GATT_CCC(_ble_fiber_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* PIN_BRIGHTNESS - Index 30, 31, 32 */ + BT_GATT_CHARACTERISTIC(UUID_PIN_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_pin_brightness_read, _ble_pin_brightness_write, NULL), + BT_GATT_CCC(_ble_pin_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* LEVEL_CALIBRATE - Index 33, 34, 35 */ + BT_GATT_CHARACTERISTIC(UUID_LEVEL_CALIBRATE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_calibrate_read, _ble_level_calibrate_write, NULL), + BT_GATT_CCC(_ble_level_calibrate_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* LEVEL_SENSITIVITY - Index 36, 37, 38 */ + BT_GATT_CHARACTERISTIC(UUID_LEVEL_SENSITIVITY, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_sensitivity_read, _ble_level_sensitivity_write, NULL), + BT_GATT_CCC(_ble_level_sensitivity_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* LEVEL_OPTION - Index 39, 40, 41 */ + BT_GATT_CHARACTERISTIC(UUID_LEVEL_OPTION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_option_read, _ble_level_option_write, NULL), + BT_GATT_CCC(_ble_level_option_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* LEVEL_VISCOSITY - Index 42, 43, 44 */ + BT_GATT_CHARACTERISTIC(UUID_LEVEL_VISCOSITY, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_viscosity_read, _ble_level_viscosity_write, NULL), + BT_GATT_CCC(_ble_level_viscosity_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* LEVEL_ANGLE - Index 45, 46, 47 */ + BT_GATT_CHARACTERISTIC(UUID_LEVEL_ANGLE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_level_angle_read, NULL, NULL), + BT_GATT_CCC(_ble_level_angle_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* AMBIENT_LIGHT - Index 48, 49, 50 */ + BT_GATT_CHARACTERISTIC(UUID_AMBIENT_LIGHT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_ambient_light_read, NULL, NULL), + BT_GATT_CCC(_ble_ambient_light_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* FSM_TIMEOUT - Index 51, 52, 53 */ + BT_GATT_CHARACTERISTIC(UUID_FSM_TIMEOUT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_timeout_read, _ble_fsm_timeout_write, NULL), + BT_GATT_CCC(_ble_fsm_timeout_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* FSM_AUTO_BRIGHTNESS - Index 54, 55, 56 */ + BT_GATT_CHARACTERISTIC(UUID_FSM_AUTO_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_auto_brightness_read, _ble_fsm_auto_brightness_write, NULL), + BT_GATT_CCC(_ble_fsm_auto_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* FSM_APP_CONFIG - Index 57, 58, 59 */ + BT_GATT_CHARACTERISTIC(UUID_FSM_APP_CONFIG, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_app_config_read, _ble_fsm_app_config_write, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_MPCB_VERSION - Index 60, 61, 62 */ + BT_GATT_CHARACTERISTIC(UUID_APP_MPCB_VERSION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_main_pcb_version_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_RPCB_VERSION - Index 63, 64, 65 */ + BT_GATT_CHARACTERISTIC(UUID_APP_RPCB_VERSION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_ring_pcb_version_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_VERSION - Index 66, 67, 68 */ + BT_GATT_CHARACTERISTIC(UUID_APP_VERSION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_version_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_MFG_ID - Index 69, 70, 71 */ + BT_GATT_CHARACTERISTIC(UUID_APP_MFG_ID, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_mfg_id_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_MFG_DATE - Index 72, 73, 74 */ + BT_GATT_CHARACTERISTIC(UUID_APP_MFG_DATE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_mfg_date_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_PN - Index 75, 76, 77 */ + BT_GATT_CHARACTERISTIC(UUID_APP_PN, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_pn_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_SN - Index 78, 79, 80 */ + BT_GATT_CHARACTERISTIC(UUID_APP_SN, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_sn_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_POWER_COUNT - Index 81, 82, 83 */ + BT_GATT_CHARACTERISTIC(UUID_APP_POWER_COUNT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_app_power_count_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_CLI_ENABLED - Index 84, 85, 86 */ + BT_GATT_CHARACTERISTIC(UUID_APP_CLI_ENABLED, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_app_cli_enabled_read, _ble_app_cli_enabled_write, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* APP_RESET - Index 87, 88 (write-only, no CCC) */ + BT_GATT_CHARACTERISTIC(UUID_APP_RESET, BT_GATT_CHRC_WRITE, + BT_GATT_PERM_WRITE, NULL, _ble_app_reset_write, NULL), + + /* BATTERY_STATUS - Index 89, 90, 91 */ + BT_GATT_CHARACTERISTIC(UUID_BATTERY_STATUS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_battery_level_read, NULL, NULL), + BT_GATT_CCC(_ble_battery_level_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* FSM_MODE - Index 92, 93, 94 */ + BT_GATT_CHARACTERISTIC(UUID_FSM_MODE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_mode_read, _ble_fsm_mode_write, NULL), + BT_GATT_CCC(_ble_fsm_mode_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), + + /* RTC - Index 95, 96, 97 */ + BT_GATT_CHARACTERISTIC(UUID_RTC_SECS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY), + BT_GATT_PERM_READ, _ble_rtc_secs_read, NULL, NULL), + BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)), +); + +BT_GATT_SERVICE_DEFINE( + _shot_capture_service, BT_GATT_PRIMARY_SERVICE(UUID_SHOT_CAPTURE_SERVICE), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_TAP_THS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_tap_ths_read, _ble_shot_capture_tap_ths_write, NULL), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_QUIET_TIME, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_quiet_time_read, _ble_shot_capture_quiet_time_write, NULL), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOCK_TIME, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_shock_time_read, _ble_shot_capture_shock_time_write, NULL), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_USER_SHOT_CNT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_user_shot_cnt_read, _ble_shot_capture_user_shot_cnt_reset, NULL), + BT_GATT_CCC(_shot_capture_user_shot_cnt_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOT_CNT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_shot_cnt_read, _ble_shot_capture_shot_cnt_reset, NULL), + BT_GATT_CCC(_shot_capture_shot_cnt_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_CAPTURE_EN, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_capture_en_read, _ble_shot_capture_capture_en_write, NULL), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_DUR_LOC, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_dur_loc_read, _ble_shot_capture_dur_loc_write, NULL), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOT_DATA_REQUEST, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE), + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_data_request_read, _ble_shot_data_request_write, NULL), + BT_GATT_CCC(_ble_shot_data_request_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOT_DATA, BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_NONE, NULL, NULL, NULL), + BT_GATT_CCC(_ble_shot_data_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), +); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void BLE_init (void) +{ + PRINT (INFO, "Initializing BLE...\r\n"); + + if (FS_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize filesystem.\r\n"); + } + + _ble_config_read (); + _ble_settings_read (); + + _ble_conn_cbs.connected = _ble_connected_cb; + _ble_conn_cbs.disconnected = _ble_disconnected_cb; + _ble_conn_cbs.security_changed = _ble_security_changed_cb; + _ble_gatt_cbs.att_mtu_updated = _ble_att_mtu_updated_cb; + bt_conn_cb_register (&_ble_conn_cbs); + bt_gatt_cb_register (&_ble_gatt_cbs); + + _ble_conn_auth_cbs.passkey_display = NULL; + _ble_conn_auth_cbs.passkey_entry = NULL; + _ble_conn_auth_cbs.passkey_confirm = NULL; + _ble_conn_auth_cbs.pairing_accept = _ble_pairing_accept_cb; + _ble_conn_auth_cbs.cancel = _ble_auth_cancel_cb; + _ble_conn_auth_info_cbs.pairing_complete = _ble_pairing_complete_cb; + _ble_conn_auth_info_cbs.pairing_failed = _ble_pairing_failed_cb; + + if (bt_conn_auth_cb_register (&_ble_conn_auth_cbs)) + { + PRINT (ERROR, "Failed to register authorization callbacks.\n"); + return; + } + if (bt_conn_auth_info_cb_register (&_ble_conn_auth_info_cbs)) + { + PRINT (ERROR, "Failed to register authorization info callbacks.\n"); + return; + } + + if (!strcmp (_ble_config.adv_name, BLE_DEVICE_NAME_PREFIX)) + { + strcat (_ble_config.adv_name, APP_info_get().sn); + _ble_config_write (); + } + _ble_ad_data[1].data = _ble_config.adv_name; + _ble_ad_data[1].data_len = strlen (_ble_config.adv_name); + bt_set_name (_ble_config.adv_name); + + /* Work items. */ + k_work_init_delayable (&_ble_delayed_work, _ble_reenable_later); + k_work_init_delayable (&_ble_pair_delay_work, _ble_pair_delay_handler); + k_work_init_delayable (&_ble_settings_write_dwork, _ble_settings_write_work_handler); + k_work_init_delayable (&_ble_bond_clear_dwork, _ble_bond_clear_dwork_handler); + k_work_init (&_ble_eraseall_work, _ble_eraseall_work_handler); + k_work_init (&_ble_dur_loc_work, _ble_dur_loc_work_handler); + k_work_init (&_ble_pair_smp_work, _ble_pair_smp_handler); + + + /* Set up notify service attribute pointers. */ + /* _ble_service */ + _ble_char_table[(uint32_t) BLE_CHAR_ADV_NAME].p_notify_service = &_ble_service.attrs[2]; + /* BLE_CHAR_PAIRING (BOND_CLEAR) is write-only — no notify needed */ + _ble_char_table[(uint32_t) BLE_CHAR_RING_COLOR].p_notify_service = &_ble_service.attrs[7]; + _ble_char_table[(uint32_t) BLE_CHAR_RING_BRIGHTNESS].p_notify_service = &_ble_service.attrs[10]; + _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_COLOR].p_notify_service = &_ble_service.attrs[13]; + _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_BRIGHTNESS].p_notify_service = &_ble_service.attrs[16]; + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_COLOR].p_notify_service = &_ble_service.attrs[19]; + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_BRIGHTNESS].p_notify_service = &_ble_service.attrs[22]; + _ble_char_table[(uint32_t) BLE_CHAR_BUBBLE_BRIGHTNESS].p_notify_service = &_ble_service.attrs[25]; + _ble_char_table[(uint32_t) BLE_CHAR_FIBER_BRIGHTNESS].p_notify_service = &_ble_service.attrs[28]; + _ble_char_table[(uint32_t) BLE_CHAR_PIN_BRIGHTNESS].p_notify_service = &_ble_service.attrs[31]; + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_CALIBRATE].p_notify_service = &_ble_service.attrs[34]; + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_SENSITIVITY].p_notify_service = &_ble_service.attrs[37]; + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_OPTION].p_notify_service = &_ble_service.attrs[40]; + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_VISCOSITY].p_notify_service = &_ble_service.attrs[43]; + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_ANGLE].p_notify_service = &_ble_service.attrs[46]; + _ble_char_table[(uint32_t) BLE_CHAR_AMBIENT_LIGHT].p_notify_service = &_ble_service.attrs[49]; + _ble_char_table[(uint32_t) BLE_CHAR_FSM_TIMEOUT].p_notify_service = &_ble_service.attrs[52]; + _ble_char_table[(uint32_t) BLE_CHAR_FSM_AUTO_BRIGHTNESS].p_notify_service = &_ble_service.attrs[55]; + _ble_char_table[(uint32_t) BLE_CHAR_FSM_APP_CONFIG].p_notify_service = &_ble_service.attrs[58]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_MPCB_VERSION].p_notify_service = &_ble_service.attrs[61]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_RPCB_VERSION].p_notify_service = &_ble_service.attrs[64]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_VERSION].p_notify_service = &_ble_service.attrs[67]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_MFG_ID].p_notify_service = &_ble_service.attrs[70]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_MFG_DATE].p_notify_service = &_ble_service.attrs[73]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_PN].p_notify_service = &_ble_service.attrs[76]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_SN].p_notify_service = &_ble_service.attrs[79]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_POWER_COUNT].p_notify_service = &_ble_service.attrs[82]; + _ble_char_table[(uint32_t) BLE_CHAR_APP_CLI_ENABLED].p_notify_service = &_ble_service.attrs[85]; + /* APP_RESET is write-only, no notify */ + _ble_char_table[(uint32_t) BLE_CHAR_BATTERY_LEVEL].p_notify_service = &_ble_service.attrs[90]; + _ble_char_table[(uint32_t) BLE_CHAR_FSM_MODE].p_notify_service = &_ble_service.attrs[93]; + + /* _shot_capture_service */ + _ble_char_table[(uint32_t) BLE_CHAR_USER_SHOT_CNT].p_notify_service = &_shot_capture_service.attrs[8]; + _ble_char_table[(uint32_t) BLE_CHAR_TOTAL_SHOT_CNT].p_notify_service = &_shot_capture_service.attrs[11]; + _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA_REQUEST].p_notify_service = &_shot_capture_service.attrs[18]; + _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA].p_notify_service = &_shot_capture_service.attrs[21]; + + /* Check and enable BLE. */ + if (_ble_config.enabled) + { + BLE_enable (true, false); + } +} + +void BLE_adv_name_set (char *p_str) +{ + memset (_ble_config.adv_name, 0, sizeof (_ble_config.adv_name)); + strcpy (_ble_config.adv_name, p_str); + _ble_config_write (); + + _ble_ad_data[1].data = _ble_config.adv_name; + _ble_ad_data[1].data_len = strlen (_ble_config.adv_name); + + k_work_schedule (&_ble_delayed_work, K_MSEC(500)); +} + +void BLE_enable (bool en, bool save) +{ + int32_t err; + + if (en) + { + err = bt_enable (NULL); + if (err) + { + PRINT (ERROR, "Enabling bluetooth failed (err %d)\r\n", err); + return; + } + + PRINT (INFO, "Bluetooth enabled\r\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load (); + } + + err = bt_le_adv_start (BT_LE_ADV_CONN, _ble_ad_data, ARRAY_SIZE(_ble_ad_data), + _ble_scan_data, ARRAY_SIZE(_ble_scan_data)); + if (err) + { + PRINT (ERROR, "Advertising failed to start (err %d)\r\n", err); + return; + } + + PRINT (INFO, "Advertising successfully started\r\n"); + _ble_enabled = true; + } + else + { + err = bt_le_adv_stop (); + if (err) + { + PRINT (ERROR, "Advertising failed to stop (err %d)\r\n", err); + return; + } + + PRINT (INFO, "Advertising stopped\r\n"); + + err = bt_disable (); + if (err) + { + PRINT (ERROR, "Disabling bluetooth failed (err %d)\r\n", err); + return; + } + + PRINT (INFO, "Bluetooth disabled\r\n"); + _ble_enabled = false; + } + + if (save) + { + _ble_config.enabled = _ble_enabled; + _ble_config_write (); + } +} + +bool BLE_is_enabled (void) +{ + return _ble_enabled; +} + +bool BLE_is_connected (void) +{ + return _ble_connected; +} + +void BLE_notify (BLE_char_e idx, const void *p_data, uint16_t len) +{ + if (!_ble_connected) + { + return; + } + else if (!_ble_char_table[(uint32_t) idx].notify) + { + return; + } + + int err = bt_gatt_notify (NULL, _ble_char_table[(uint32_t) idx].p_notify_service, p_data, len); + if (err) + { + PRINT (ERROR, "BLE notify failed for char %d (err %d)\r\n", idx, err); + } +} + +void BLE_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "BLE\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Status : %s\r\n", (_ble_enabled) ? "enabled" : "disabled"); + PRINT (INFO, "Advertising Name: %s\r\n", _ble_config.adv_name); + PRINT (INFO, "Bonded : %s\r\n", (_ble_settings.bonded) ? "yes" : "no"); + if (_ble_settings.bonded) + { + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str (&_ble_settings.peer_addr, addr, sizeof (addr)); + + PRINT (INFO, " Peer Addr : %s\r\n", addr); + } + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--name")) + { + PRINT (INFO, "%s\r\n", _ble_config.adv_name); + } + else if (!strcmp (argv[0], "--bondclr")) + { + _ble_bond_clear (); + } + else + { + goto USAGE; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-e") || !strcmp (argv[0], "--enable")) + { + if (!strcmp (argv[1], "true")) + { + BLE_enable (true, false); + } + else if (!strcmp (argv[1], "false")) + { + BLE_enable (false, false); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "--name")) + { + BLE_adv_name_set (argv[1]); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: ble [OPTION...]\r\n"); + PRINT (INFO, " -e | --enable Enables BLE\r\n"); + PRINT (INFO, " <%%b> true = enable\r\n"); + PRINT (INFO, " false = disable\r\n"); + PRINT (INFO, " --name Gets/sets BLE advertising name\r\n"); + PRINT (INFO, " <%%s> name\r\n"); + PRINT (INFO, " --bondclr Clears stored bond and bond keys\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — BLE core + *------------------------------------------------------------------------------------------------*/ +static void _ble_connected_cb (struct bt_conn *conn, uint8_t err) +{ + if (err) + { + PRINT (INFO, "Connection failed (err %u)\n", err); + return; + } + + _ble_connected = true; + _ble_current_conn = bt_conn_ref (conn); + + PRINT (INFO, "Connected\n"); + + /* Always request MTU exchange immediately on connect — Android won't + * re-initiate it on bonded reconnects, causing transfers to run at + * 20 bytes/chunk instead of 514. */ + _ble_mtu_exchange_params.func = _ble_mtu_exchange_cb; + int mtu_err = bt_gatt_exchange_mtu (conn, &_ble_mtu_exchange_params); + if (mtu_err) + { + PRINT (ERROR, "BLE: MTU exchange request failed (err %d)\r\n", mtu_err); + } + + if (_ble_settings.bonded) + { + PRINT (INFO, "BLE: bonded, requesting security\n"); + int sec_err = bt_conn_set_security (conn, BT_SECURITY_L2); + if (sec_err) + { + PRINT (ERROR, "BLE: bt_conn_set_security failed (err %d)\n", sec_err); + bt_conn_disconnect (conn, BT_HCI_ERR_AUTH_FAIL); + } + } + else + { + PRINT (INFO, "BLE: unbonded, scheduling pairing in 1000ms\n"); + k_work_schedule (&_ble_pair_delay_work, K_MSEC(1000)); + } +} + +// static void _ble_connected_cb (struct bt_conn *conn, uint8_t err) +// { +// if (err) +// { +// PRINT (INFO, "Connection failed (err %u)\n", err); +// return; +// } + +// _ble_connected = true; +// _ble_current_conn = bt_conn_ref (conn); + +// PRINT (INFO, "Connected\n"); + +// if (_ble_settings.bonded) +// { +// PRINT (INFO, "BLE: bonded, requesting security\n"); +// int sec_err = bt_conn_set_security (conn, BT_SECURITY_L2); +// if (sec_err) +// { +// PRINT (ERROR, "BLE: bt_conn_set_security failed (err %d)\n", sec_err); +// bt_conn_disconnect (conn, BT_HCI_ERR_AUTH_FAIL); +// } +// } +// else +// { +// /* Delay pairing request to let Android finish MTU exchange and +// * service discovery first. 1000ms is enough for Android 16. */ +// PRINT (INFO, "BLE: unbonded, scheduling pairing in 1000ms\n"); +// k_work_schedule (&_ble_pair_delay_work, K_MSEC(1000)); +// } +// } + +static void _ble_disconnected_cb (struct bt_conn *conn, uint8_t reason) +{ + _ble_connected = false; + + if (_ble_current_conn) + { + bt_conn_unref (_ble_current_conn); + _ble_current_conn = NULL; + } + + k_work_cancel_delayable (&_ble_pair_delay_work); + + _ble_pair_conn = NULL; + + /* Reset shot data transfer state. */ + _ble_shot_data_transferring = false; + k_sem_reset(&read_shotdata_consumed); + + PRINT (INFO, "Disconnected (reason %u)\n", reason); + + /* Restart advertising so the phone can reconnect. */ + int err = bt_le_adv_start (BT_LE_ADV_CONN, _ble_ad_data, ARRAY_SIZE(_ble_ad_data), + _ble_scan_data, ARRAY_SIZE(_ble_scan_data)); + if (err) + { + PRINT (ERROR, "Advertising failed to restart (err %d)\r\n", err); + } + else + { + PRINT (INFO, "Advertising restarted\r\n"); + } +} + +static void _ble_security_changed_cb (struct bt_conn *conn, bt_security_t level, + enum bt_security_err err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + bt_addr_le_to_str (bt_conn_get_dst (conn), addr, sizeof (addr)); + + if (!err) + { + PRINT (INFO, "Security changed: %s level %u\n", addr, level); + } + else + { + PRINT (INFO, "Security failed: %s level %u err %d\n", addr, level, err); + + if (_ble_settings.bonded && + (err == BT_SECURITY_ERR_AUTH_FAIL || + err == BT_SECURITY_ERR_PIN_OR_KEY_MISSING)) + { + PRINT (INFO, "BLE: stale bond detected, clearing\n"); + _ble_bond_clear (); + } + } +} + +static void _ble_auth_cancel_cb (struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str (bt_conn_get_dst (conn), addr, sizeof (addr)); + + PRINT (INFO, "Pairing cancelled: %s\n", addr); +} + +static void _ble_pairing_complete_cb(struct bt_conn *conn, bool bonded) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str (bt_conn_get_dst(conn), addr, sizeof(addr)); + PRINT (INFO, "Pairing completed: %s, bonded: %d\n", addr, bonded); + + if (bonded) + { + _ble_settings.bonded = true; + _ble_settings.peer_addr = *bt_conn_get_dst(conn); + k_work_schedule (&_ble_settings_write_dwork, K_MSEC(2000)); + PRINT (INFO, "BLE: bond saved for %s\n", addr); + } +} + +static enum bt_security_err _ble_pairing_accept_cb ( struct bt_conn *conn, const struct bt_conn_pairing_feat *const feat) +{ + PRINT (INFO, "BLE: pairing accept\n"); + return BT_SECURITY_ERR_SUCCESS; +} + +static void _ble_pairing_failed_cb (struct bt_conn *conn, enum bt_security_err reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + uint8_t state; + + bt_addr_le_to_str (bt_conn_get_dst (conn), addr, sizeof (addr)); + + PRINT (INFO, "Pairing failed conn: %s, reason %d\n", addr, reason); + + if (reason == BT_SECURITY_ERR_PIN_OR_KEY_MISSING) + { + _ble_bond_clear (); + } +} + +static void _ble_att_mtu_updated_cb (struct bt_conn *conn, uint16_t tx, uint16_t rx) +{ + PRINT (INFO, "ATT MTU updated: TX %u, RX %u (usable payload: %u bytes)\n", tx, rx, tx - 1); +} + +static void _ble_pair_delay_handler (struct k_work *work) +{ + if (!_ble_current_conn) + { + return; + } + + PRINT (INFO, "BLE: initiating pairing after delay\n"); + int err = bt_conn_set_security (_ble_current_conn, BT_SECURITY_L2); + if (err) + { + PRINT (ERROR, "BLE: bt_conn_set_security failed (err %d)\n", err); + bt_conn_disconnect (_ble_current_conn, BT_HCI_ERR_AUTH_FAIL); + } +} + +static void _ble_mtu_exchange_cb (struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params) +{ + if (err) + { + PRINT (ERROR, "BLE: MTU exchange failed (err %d)\n", err); + } + else + { + PRINT (INFO, "BLE: MTU exchange complete, MTU = %u\n", bt_gatt_get_mtu (conn)); + } +} + +static void _ble_settings_read (void) +{ + ERRNO_e err; + BLE_settings_s settings; + + /* Increasing file depth since BLE needs to write at least once on every power up. */ + err = FS_open_or_create (BLE_SETTINGS_FILENAME, &_ble_settings_file, sizeof (BLE_settings_s), (FS_FILE_DEPTH_DEFAULT * 8)); + if (err == ERRNO_SUCCESS) + { + _ble_fs_ready = true; + + err = FS_read (_ble_settings_file, (uint8_t *) &settings, 0, sizeof (BLE_settings_s)); + if (err != ERRNO_SUCCESS) + { + _ble_settings_write (); + return; + } + _ble_settings = settings; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config struct changed size — wipe and recreate. */ + err = FS_delete (BLE_SETTINGS_FILENAME); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to delete old ble settings file.\r\n"); + return; + } + err = FS_open_or_create (BLE_SETTINGS_FILENAME, &_ble_settings_file, sizeof (BLE_settings_s), (FS_FILE_DEPTH_DEFAULT * 8)); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create ble settings file.\r\n"); + return; + } + + PRINT (INFO, "New ble settings file created successfully\r\n"); + + _ble_settings_write (); + } + else + { + _ble_settings_write (); + } +} + +static void _ble_settings_write (void) +{ + if (_ble_settings_file == NULL) + { + PRINT (ERROR, "BLE settings file not open, skipping write.\r\n"); + return; + } + if (FS_write (_ble_settings_file, (uint8_t *) &_ble_settings) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write ble settings to file.\r\n"); + } +} + +static void _ble_config_read (void) +{ + ERRNO_e err; + BLE_config_s cfg; + + err = FS_open_or_create (BLE_FILENAME, &_ble_config_file, sizeof (BLE_config_s), FS_FILE_DEPTH_DEFAULT); + if (err == ERRNO_SUCCESS) + { + err = FS_read (_ble_config_file, (uint8_t *) &cfg, 0, sizeof (BLE_config_s)); + if (err != ERRNO_SUCCESS) + { + _ble_config_write (); + return; + } + _ble_config = cfg; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config struct changed size — wipe and recreate. */ + err = FS_delete (BLE_FILENAME); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to delete old ble file.\r\n"); + return; + } + err = FS_open_or_create (BLE_FILENAME, &_ble_config_file, sizeof (BLE_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create ble configuration file.\r\n"); + return; + } + + PRINT (INFO, "New ble file created successfully\r\n"); + _ble_config_write (); + } + else + { + _ble_config_write (); + } +} + +static void _ble_config_write (void) +{ + if (_ble_config_file == NULL) + { + PRINT (ERROR, "BLE config file not open, skipping write.\r\n"); + return; + } + if (FS_write (_ble_config_file, (uint8_t *) &_ble_config) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write ble config to file.\r\n"); + } +} + +static void _ble_reenable_later (struct k_work *work) +{ + BLE_enable (false, false); + BLE_enable (true, false); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — Settings backend + * + * Zephyr's BT stack calls settings_save_one() for each bond sub-key when a + * bond is formed. settings_load() is called at BLE_enable() time to restore + * keys. We store all entries in the BLE_config_s.settings[] array and flush + * the whole struct to external flash via _ble_settings_write(). + *------------------------------------------------------------------------------------------------*/ + +/* Find a slot whose key matches. Returns index or -1. */ +static int _settings_entry_find (const char *key) +{ + for (int i = 0; i < BLE_PAIRING_MAX_ENTRIES; i++) + { + if (_ble_settings.settings[i].used && + strncmp(_ble_settings.settings[i].key, key, BLE_PAIRING_MAX_KEY_LEN) == 0) + { + return i; + } + } + return -1; +} + +/* Allocate a new slot, pre-filling the key. Returns index or -1 if full. */ +static int _settings_entry_alloc (const char *key) +{ + for (int i = 0; i < BLE_PAIRING_MAX_ENTRIES; i++) + { + if (!_ble_settings.settings[i].used) + { + strncpy(_ble_settings.settings[i].key, key, BLE_PAIRING_MAX_KEY_LEN - 1); + _ble_settings.settings[i].key[BLE_PAIRING_MAX_KEY_LEN - 1] = '\0'; + _ble_settings.settings[i].used = true; + return i; + } + } + return -1; +} + +static ssize_t _settings_read_cb (void *cb_arg, void *data, size_t len) +{ + const BLE_settings_entry_s *e = (const BLE_settings_entry_s *) cb_arg; + size_t copy_len = (len < e->val_len) ? len : e->val_len; + + memcpy (data, e->val, copy_len); + + return (ssize_t) copy_len; +} + +/** + * @brief Called by settings_load(). Replay all stored entries back to Zephyr. + */ +static int _settings_load (struct settings_store *cs, const struct settings_load_arg *arg) +{ + PRINT (INFO, "BLE settings: load called, _ble_settings_file=%p\r\n", (void*)_ble_settings_file); + + if (!_ble_fs_ready) + { + PRINT (WARNING, "BLE settings: FS not ready, skipping load\r\n"); + return 0; + } + + /* Prevent _ble_settings_write() during load */ + atomic_set(&_ble_settings_loading, 1); + + for (int i = 0; i < BLE_PAIRING_MAX_ENTRIES; i++) + { + BLE_settings_entry_s *e = &_ble_settings.settings[i]; + if (!e->used) continue; + + PRINT (INFO, "BLE settings: replaying '%s' (%u bytes)\r\n", e->key, e->val_len); // ADD + + const char *name = e->key; + if (arg->subtree) + { + size_t sub_len = strlen(arg->subtree); + if (strncmp(name, arg->subtree, sub_len) != 0) continue; + name += sub_len; + if (*name == '/') name++; + } + + int err = settings_call_set_handler (name, e->val_len, _settings_read_cb, e, arg); + if (err) + { + PRINT (WARNING, "BLE settings: set_handler err %d for '%s'\r\n", err, e->key); + } + } + + /* Restore */ + atomic_set (&_ble_settings_loading, 0); + + return 0; +} + +/** + * @brief Called by settings_save_one() / settings_delete(). + * Upserts or removes one entry then flushes to FS. + */ +static int _settings_save (struct settings_store *cs, const char *name, + const void *value, size_t val_len) +{ + PRINT (INFO, "BLE settings: saving '%s' (%u bytes)\r\n", name, (unsigned)val_len); + + /* bt/hash is recomputed every boot — no need to persist it. */ + if (strcmp (name, "bt/hash") == 0) + { + return 0; + } + + /* Skip 0-byte deletes of keys we don't have — nothing to delete. */ + if (value == NULL || val_len == 0) +{ + /* During load, the BT stack issues 0-byte saves as housekeeping — + * never delete a key we have stored, as the real data is about to + * be (or just was) replayed. */ + if (atomic_get (&_ble_settings_loading)) + { + return 0; + } + + /* Outside of load, only delete if explicitly present. */ + int idx = _settings_entry_find (name); + if (idx >= 0) + { + memset (&_ble_settings.settings[idx], 0, sizeof (BLE_settings_entry_s)); + if (_ble_fs_ready) + { + k_work_schedule (&_ble_settings_write_dwork, K_MSEC(2000)); + } + } + return 0; +} + + int idx = _settings_entry_find (name); + if (idx < 0) + { + idx = _settings_entry_alloc (name); + } + if (idx < 0) + { + PRINT (ERROR, "BLE settings: table full, cannot save '%s'\r\n", name); + return -ENOMEM; + } + + size_t clamped = (val_len > BLE_PAIRING_MAX_VAL_LEN) ? BLE_PAIRING_MAX_VAL_LEN : val_len; + memcpy (_ble_settings.settings[idx].val, value, clamped); + _ble_settings.settings[idx].val_len = (uint8_t) clamped; + + if (_ble_fs_ready && !atomic_get(&_ble_settings_loading)) + { + k_work_schedule (&_ble_settings_write_dwork, K_MSEC(2000)); + } + + return 0; +} + +/* Required entry point when CONFIG_SETTINGS_CUSTOM=y. + Zephyr has the prototype. */ +int settings_backend_init (void) +{ + PRINT (INFO, "BLE: settings_backend_init called\r\n"); + _settings_itf.csi_load = _settings_load; + _settings_itf.csi_save = _settings_save; + _settings_store.cs_itf = &_settings_itf; + + settings_dst_register (&_settings_store); + settings_src_register (&_settings_store); + + return 0; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — Pairing challenge + *------------------------------------------------------------------------------------------------*/ +static void _ble_pair_smp_handler (struct k_work *work) +{ + if (_ble_pair_conn == NULL) return; + + int err = bt_conn_set_security (_ble_pair_conn, BT_SECURITY_L2); + if (err) + { + PRINT (ERROR, "BLE pair: bt_conn_set_security failed (err %d)\r\n", err); + bt_conn_disconnect (_ble_pair_conn, BT_HCI_ERR_AUTH_FAIL); + } +} + +static void _ble_bond_clear (void) +{ + if (_ble_settings.bonded) + { + bt_unpair (BT_ID_DEFAULT, &_ble_settings.peer_addr); + } + + _ble_settings.bonded = false; + + memset (&_ble_settings.peer_addr, 0, sizeof(_ble_settings.peer_addr)); + memset (_ble_settings.settings, 0, sizeof(_ble_settings.settings)); + + _ble_settings_write (); + + PRINT (INFO, "BLE bond cleared.\r\n"); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — Advertising name characteristic + *------------------------------------------------------------------------------------------------*/ +static ssize_t _ble_adv_name_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + BLE_adv_name_set ((char *) buf); + return len; +} + +static ssize_t _ble_adv_name_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + return bt_gatt_attr_read (conn, attr, buf, len, offset, + _ble_config.adv_name, strlen (_ble_config.adv_name)); +} + +static ssize_t _ble_bond_clear_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + k_work_schedule (&_ble_bond_clear_dwork, K_MSEC(1000)); + + return len; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — APP characteristics + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_app_main_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t version[3]; + version[0] = APP_info_get ().main_pcb_version.major; + version[1] = APP_info_get ().main_pcb_version.minor; + version[2] = APP_info_get ().main_pcb_version.build; + return bt_gatt_attr_read (conn, attr, buf, len, offset, version, 3); +} + +static ssize_t _ble_app_ring_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t version[3]; + version[0] = APP_info_get ().ring_pcb_version.major; + version[1] = APP_info_get ().ring_pcb_version.minor; + version[2] = APP_info_get ().ring_pcb_version.build; + return bt_gatt_attr_read (conn, attr, buf, len, offset, version, 3); +} + +static ssize_t _ble_app_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t version[3]; + version[0] = APP_info_get ().version.major; + version[1] = APP_info_get ().version.minor; + version[2] = APP_info_get ().version.build; + return bt_gatt_attr_read (conn, attr, buf, len, offset, version, 3); +} + +static ssize_t _ble_app_mfg_id_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint32_t mfg_id = APP_info_get ().mfg_id; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &mfg_id, sizeof (mfg_id)); +} + +static ssize_t _ble_app_mfg_date_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint32_t mfg_date = APP_info_get ().mfg_date; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &mfg_date, sizeof (mfg_date)); +} + +static ssize_t _ble_app_pn_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + APP_info_s info = APP_info_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, info.pn, strlen(info.pn)); +} + +static ssize_t _ble_app_sn_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + APP_info_s info = APP_info_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, info.sn, strlen(info.sn)); +} + +static ssize_t _ble_app_power_count_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint32_t power_count = APP_info_get ().power_cnt; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &power_count, sizeof (power_count)); +} + +static ssize_t _ble_app_cli_enabled_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) +{ + uint8_t en = *((uint8_t *) buf); + APP_cli_enable (en); + return len; +} + +static ssize_t _ble_app_cli_enabled_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t cli_enabled = APP_info_get ().cli_enabled; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &cli_enabled, sizeof (cli_enabled)); +} + +static ssize_t _ble_app_reset_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + APP_reset (); + return len; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — SCOPE RING characteristics + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_level_option_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_level_option_set ((SCOPE_RING_level_option_e) *((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_level_option_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t option = SCOPE_RING_config_get ().level_option; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &option, sizeof(option)); +} + +static void _ble_level_option_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_OPTION].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_level_viscosity_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_level_viscosity_set (*((float *) buf)); + return len; +} + +static ssize_t _ble_level_viscosity_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + float viscosity = SCOPE_RING_config_get ().level_viscosity; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &viscosity, sizeof(viscosity)); +} + +static void _ble_level_viscosity_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_VISCOSITY].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_level_sensitivity_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_level_sensitivity_set (*((float *) buf)); + return len; +} + +static ssize_t _ble_level_sensitivity_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + float sensitivity = SCOPE_RING_config_get ().level_sensitivity; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &sensitivity, sizeof(sensitivity)); +} + +static void _ble_level_sensitivity_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_SENSITIVITY].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_ring_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, (RGB_color_e) *((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_ring_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SCOPE_RING_config_get ().background_color; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_ring_color_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_RING_COLOR].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_ring_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, *((uint8_t *) buf), true); + return len; +} + +static ssize_t _ble_ring_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SCOPE_RING_config_get ().background_brightness; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_ring_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_RING_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_indicator_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, (RGB_color_e) *((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_indicator_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SCOPE_RING_config_get ().indicators_color; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_indicator_color_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_COLOR].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_indicator_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, *((uint8_t *) buf), true); + return len; +} + +static ssize_t _ble_indicator_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SCOPE_RING_config_get ().indicators_brightness; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_indicator_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_level_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, (RGB_color_e) *((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_level_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SCOPE_RING_config_get ().level_color; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_level_color_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_COLOR].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_level_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, *((uint8_t *) buf), true); + return len; +} + +static ssize_t _ble_level_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SCOPE_RING_config_get ().level_brightness; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_level_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — BUBBLE / FIBER / PIN + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_bubble_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + BUBBLE_brightness_set (*((uint8_t *) buf), true); + return len; +} + +static ssize_t _ble_bubble_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = BUBBLE_brightness_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_bubble_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_BUBBLE_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_fiber_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + FIBER_brightness_set (*((uint8_t *) buf), true); + return len; +} + +static ssize_t _ble_fiber_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = FIBER_brightness_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_fiber_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_FIBER_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_pin_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + PIN_brightness_set (*((uint8_t *) buf), true); + return len; +} + +static ssize_t _ble_pin_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = PIN_brightness_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_pin_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_PIN_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — MOTION + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_level_calibrate_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + FSM_level_calibrate (); + return len; +} + +static ssize_t _ble_level_calibrate_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + const char *value = attr->user_data; + return bt_gatt_attr_read (conn, attr, buf, len, offset, value, sizeof(*value)); +} + +static void _ble_level_calibrate_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_CALIBRATE].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_level_angle_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + float angle = MOTION_angle_get (MOTION_AXIS_Y); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &angle, sizeof(angle)); +} + +static void _ble_level_angle_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_ANGLE].notify = (value == BT_GATT_CCC_NOTIFY); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — ALS + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_ambient_light_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + ALS_data_s data; + uint32_t val = 0; + + if (ALS_read (&data) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read ALS.\n"); + } + else + { + val = data.visible; + } + + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_ambient_light_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_AMBIENT_LIGHT].notify = (value == BT_GATT_CCC_NOTIFY); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — FSM + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_fsm_timeout_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + FSM_timout_set (*((uint32_t *) buf)); + return len; +} + +static ssize_t _ble_fsm_timeout_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint32_t val = FSM_timout_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _ble_fsm_timeout_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_FSM_TIMEOUT].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_fsm_auto_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + FSM_auto_brightness_set ((bool) *((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_fsm_auto_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t auto_brightness = (uint8_t) FSM_auto_brightness_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &auto_brightness, sizeof (auto_brightness)); +} + +static void _ble_fsm_auto_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_FSM_AUTO_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_fsm_mode_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + FSM_mode_set ((FSM_MODE_e) *((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_fsm_mode_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t mode = (uint8_t) FSM_mode_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &mode, sizeof (mode)); +} + +static void _ble_fsm_mode_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_FSM_MODE].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_fsm_app_config_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + FSM_app_config_set ((bool) *((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_fsm_app_config_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t app_config = (uint8_t) FSM_app_config_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &app_config, sizeof (app_config)); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — BATTERY + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_battery_level_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + float batt_life = FSM_battery_life_get (); + return bt_gatt_attr_read (conn, attr, buf, len, offset, &batt_life, sizeof(batt_life)); +} + +static void _ble_battery_level_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_BATTERY_LEVEL].notify = (value == BT_GATT_CCC_NOTIFY); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — SHOT CAPTURE + *------------------------------------------------------------------------------------------------*/ + +static ssize_t _ble_shot_capture_tap_ths_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 1U) { + PRINT(INFO, "_ble_shot_capture_tap_ths_write: Incorrect data length"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + SHOT_CAPTURE_tap_ths_set(*((uint8_t *) buf)); + return len; +} + +static ssize_t _ble_shot_capture_tap_ths_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SHOT_CAPTURE_config_get()->tap_ths; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static ssize_t _ble_shot_capture_quiet_time_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 1U) { + PRINT(INFO, "_ble_shot_capture_quiet_time_write: Incorrect data length"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + SHOT_CAPTURE_quiet_time_set(*((LSM6DS3TR_C_QUIET_e *) buf)); + return len; +} + +static ssize_t _ble_shot_capture_quiet_time_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = (uint8_t)SHOT_CAPTURE_config_get()->quiet_time; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static ssize_t _ble_shot_capture_shock_time_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 1U) { + PRINT(INFO, "_ble_shot_capture_shock_time_write: Incorrect data length"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + SHOT_CAPTURE_shock_time_set(*((LSM6DS3TR_C_SHOCK_e *) buf)); + return len; +} + +static ssize_t _ble_shot_capture_shock_time_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = (uint8_t)SHOT_CAPTURE_config_get()->shock_time; + return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static ssize_t _ble_shot_capture_user_shot_cnt_reset (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 1U) { + PRINT(INFO, "_ble_shot_capture_user_shot_cnt_reset: Incorrect data length"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + uint8_t zero = 0; + if (*((uint8_t *) buf) == 1) { + SHOT_CAPTURE_config_get()->user_shot_cnt = 0; + } + BLE_notify (BLE_CHAR_USER_SHOT_CNT, &zero, sizeof (zero)); + return len; +} + +static ssize_t _ble_shot_capture_user_shot_cnt_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint32_t val = SHOT_CAPTURE_config_get()->user_shot_cnt; + return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _shot_capture_user_shot_cnt_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_USER_SHOT_CNT].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_shot_capture_shot_cnt_reset (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 1U) { + PRINT(INFO, "_ble_shot_capture_shot_cnt_reset: Incorrect data length"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + uint8_t zero = 0; + if (*((uint8_t *) buf) == 1) { + SHOT_CAPTURE_config_get()->shot_cnt = 0; + } + BLE_notify (BLE_CHAR_TOTAL_SHOT_CNT, &zero, sizeof (zero)); + return len; +} + +static ssize_t _ble_shot_capture_shot_cnt_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint32_t val = SHOT_CAPTURE_config_get()->shot_cnt; + return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static void _shot_capture_shot_cnt_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_TOTAL_SHOT_CNT].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static ssize_t _ble_shot_capture_capture_en_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 1U) { + PRINT(INFO, "_ble_shot_capture_capture_en_write: Incorrect data length\r\n"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + SHOT_CAPTURE_config_get()->shot_capture_en = (*((uint8_t *) buf) == 1) ? 1 : 0; + return len; +} + +static ssize_t _ble_shot_capture_capture_en_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t val = SHOT_CAPTURE_config_get()->shot_capture_en; + return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val)); +} + +static ssize_t _ble_shot_capture_dur_loc_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 2U) { + PRINT(INFO, "_ble_shot_capture_dur_loc_write: Incorrect data length\r\n"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + _ble_dur_loc_duration = ((uint8_t *) buf)[0]; + _ble_dur_loc_location = ((uint8_t *) buf)[1]; + k_work_submit(&_ble_dur_loc_work); + return len; +} + +static ssize_t _ble_shot_capture_dur_loc_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t dur_loc[2]; + dur_loc[0] = SHOT_CAPTURE_config_get()->shot_duration; + dur_loc[1] = SHOT_CAPTURE_config_get()->shot_location; + return bt_gatt_attr_read(conn, attr, buf, len, offset, &dur_loc, sizeof(dur_loc)); +} + +/* + * SHOT DATA REQUEST characteristic + * + * Phone writes: + * 0x01 = Request shot data read from flash + * 0x02 = Start transfer (phone ready to receive data chunks) + * 0x03 = Consumed (phone confirmed all data received) + * 0x04 = Erase all shot data + * 0x05 = Get file count + * + * Device notifies: + * 0x01 = Data ready [1 byte status + 4 bytes total_len] + * 0x02 = No file found + * 0x03 = Timeout + * 0x04 = Erase done + * 0x05 = Erase failed + * 0x06 = File count [1 byte status + 4 bytes count] + */ +static ssize_t _ble_shot_data_request_write (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + if (len != 1U) { + PRINT(INFO, "_ble_shot_data_request_write: Incorrect data length\r\n"); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + uint8_t cmd = *((uint8_t *)buf); + + if (cmd == 0x01) { + PRINT(INFO, "BLE: Shot data read requested\r\n"); + SHOT_STORAGE_request_read(READ_REQ_BLE); + } else if (cmd == 0x02) { + if (_ble_shot_data_buf == NULL || _ble_shot_data_total_len == 0) { + PRINT(WARNING, "BLE: Start transfer requested but no data available\r\n"); + return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + } + PRINT(INFO, "BLE: Phone ready, starting shot data transfer (%u bytes)\r\n", _ble_shot_data_total_len); + _ble_shot_data_sent = 0; + _ble_shot_data_chunk_size = bt_gatt_get_mtu(conn) - 3; + PRINT(INFO, "BLE: chunk size %u bytes (MTU %u)\r\n", _ble_shot_data_chunk_size, _ble_shot_data_chunk_size + 3); + + if (_ble_shot_data_chunk_size < 182) + { + PRINT (WARNING, "BLE: MTU too low (%u), aborting transfer\n", _ble_shot_data_chunk_size + 3); + uint8_t status = 0x07; + BLE_notify (BLE_CHAR_SHOT_DATA_REQUEST, &status, sizeof(status)); + return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + } + + _ble_shot_data_transferring = true; + _ble_shot_data_send_next(conn, NULL); + } else if (cmd == 0x03) { + PRINT(INFO, "BLE: Phone confirmed shot data consumed\r\n"); + _ble_shot_data_buf = NULL; + _ble_shot_data_total_len = 0; + _ble_shot_data_sent = 0; + _ble_shot_data_transferring = false; + _ble_shot_data_status = 0; + SHOT_STORAGE_read_done(); + } else if (cmd == 0x04) { + PRINT(INFO, "BLE: Erase all shot data requested\r\n"); + k_work_submit(&_ble_eraseall_work); + } else if (cmd == 0x05) { + uint32_t cnt = SHOT_STORAGE_get_file_cnt(); + uint8_t notify_buf[5]; + notify_buf[0] = 0x06; + notify_buf[1] = (uint8_t)(cnt & 0xFF); + notify_buf[2] = (uint8_t)((cnt >> 8) & 0xFF); + notify_buf[3] = (uint8_t)((cnt >> 16) & 0xFF); + notify_buf[4] = (uint8_t)((cnt >> 24) & 0xFF); + BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, notify_buf, sizeof(notify_buf)); + PRINT(INFO, "BLE: File count requested: %u\r\n", cnt); + } else { + PRINT(INFO, "_ble_shot_data_request_write: Unknown command 0x%02X\r\n", cmd); + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + return len; +} + +static ssize_t _ble_shot_data_request_read (struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, + &_ble_shot_data_status, sizeof(_ble_shot_data_status)); +} + +static void _ble_shot_data_request_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA_REQUEST].notify = (value == BT_GATT_CCC_NOTIFY); +} + +static void _ble_shot_data_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value) +{ + _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA].notify = (value == BT_GATT_CCC_NOTIFY); + PRINT(INFO, "BLE: Shot data notifications %s\r\n", value ? "enabled" : "disabled"); +} + +static void _ble_shot_data_send_next (struct bt_conn *conn, void *user_data) +{ + if (conn == NULL || !_ble_shot_data_transferring) { + return; + } + + if (_ble_shot_data_sent >= _ble_shot_data_total_len) { + _ble_shot_data_transferring = false; + PRINT(INFO, "BLE: Shot data transfer complete: %u bytes sent\r\n", _ble_shot_data_sent); + return; + } + + uint32_t remaining = _ble_shot_data_total_len - _ble_shot_data_sent; + uint16_t chunk_size = (remaining > _ble_shot_data_chunk_size) + ? _ble_shot_data_chunk_size : (uint16_t)remaining; + + struct bt_gatt_notify_params params = { + .uuid = NULL, + .attr = _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA].p_notify_service, + .data = &_ble_shot_data_buf[_ble_shot_data_sent], + .len = chunk_size, + .func = _ble_shot_data_send_next, + }; + + int err = bt_gatt_notify_cb(conn, ¶ms); + if (err) { + if (err == -ENOMEM) { + k_msleep(1); + _ble_shot_data_send_next(conn, NULL); + return; + } + PRINT(ERROR, "BLE: notify failed (err %d), aborting transfer\r\n", err); + _ble_shot_data_transferring = false; + return; + } + + _ble_shot_data_sent += chunk_size; +} + +void BLE_shot_data_ready (uint8_t *p_data, uint32_t total_len) +{ + _ble_shot_data_buf = p_data; + _ble_shot_data_total_len = total_len; + _ble_shot_data_sent = 0; + _ble_shot_data_status = 0x01; + + uint8_t notify_buf[5]; + notify_buf[0] = _ble_shot_data_status; + notify_buf[1] = (uint8_t)(total_len & 0xFF); + notify_buf[2] = (uint8_t)((total_len >> 8) & 0xFF); + notify_buf[3] = (uint8_t)((total_len >> 16) & 0xFF); + notify_buf[4] = (uint8_t)((total_len >> 24) & 0xFF); + BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, notify_buf, sizeof(notify_buf)); +} + +void BLE_shot_data_not_found (void) +{ + _ble_shot_data_buf = NULL; + _ble_shot_data_total_len = 0; + _ble_shot_data_sent = 0; + _ble_shot_data_transferring = false; + _ble_shot_data_status = 0x02; + BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, &_ble_shot_data_status, sizeof(_ble_shot_data_status)); +} + +void BLE_shot_data_timeout (void) +{ + _ble_shot_data_buf = NULL; + _ble_shot_data_total_len = 0; + _ble_shot_data_sent = 0; + _ble_shot_data_transferring = false; + _ble_shot_data_status = 0x03; + BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, &_ble_shot_data_status, sizeof(_ble_shot_data_status)); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS — Shot data work handlers + *------------------------------------------------------------------------------------------------*/ +static void _ble_dur_loc_work_handler (struct k_work *work) +{ + SHOT_CAPTURE_task_disable_and_wait(); + SHOT_STORAGE_wait_write_task_idle(); + SHOT_CAPTURE_shotdata_duration_location_set(_ble_dur_loc_duration, _ble_dur_loc_location); + SHOT_CAPTURE_task_resume(); +} + +static void _ble_eraseall_work_handler (struct k_work *work) +{ + SHOT_CAPTURE_task_disable_and_wait(); + SHOT_STORAGE_wait_write_task_idle(); + + if (SHOT_STORAGE_eraseall() == ERRNO_SUCCESS) { + _ble_shot_data_status = 0x04; + } else { + _ble_shot_data_status = 0x05; + } + BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, &_ble_shot_data_status, sizeof(_ble_shot_data_status)); + + SHOT_CAPTURE_task_resume(); +} + +static void _ble_settings_write_work_handler (struct k_work *work) +{ + _ble_settings_write (); +} + +static void _ble_bond_clear_dwork_handler (struct k_work *work) +{ + _ble_bond_clear (); +} diff --git a/src/app/ble.h b/src/app/ble.h new file mode 100644 index 0000000..ed9d5ec --- /dev/null +++ b/src/app/ble.h @@ -0,0 +1,187 @@ +/** + * @file ble.h + * @brief BLE module + * @author Kenny Tran + * @date Jul 06 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef BLE_H_ +#define BLE_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define BLE_FILENAME ("ble") +#define BLE_SETTINGS_FILENAME ("ble_settings") + +#define BLE_DEVICE_NAME_PREFIX ("CS_") + +/* + * Settings backend storage limits. + * Zephyr's BT stack stores ~8 sub-keys per bonded peer (LTK, peer addr, + * IRK, CSRK, flags, CCC entries, etc.). 24 slots gives comfortable headroom. + */ +#define BLE_PAIRING_MAX_ENTRIES (8) +#define BLE_PAIRING_MAX_KEY_LEN (32) +#define BLE_PAIRING_MAX_VAL_LEN (96) + + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct BLE_config +{ + char adv_name[16]; + bool enabled : 1; + uint8_t reserved : 7; +} __attribute__((packed, aligned(1))) BLE_config_s; + +typedef struct +{ + char key[BLE_PAIRING_MAX_KEY_LEN]; + uint8_t val[BLE_PAIRING_MAX_VAL_LEN]; + uint8_t val_len; + bool used : 1; + uint8_t reserved : 7; +} __attribute__((packed, aligned(1))) BLE_settings_entry_s; + +typedef struct BLE_settings +{ + bt_addr_le_t peer_addr; /* 7 bytes: type (1) + addr (6) */ + bool bonded : 1; + uint8_t reserved : 7; + BLE_settings_entry_s settings[BLE_PAIRING_MAX_ENTRIES]; +} __attribute__((packed, aligned(1))) BLE_settings_s; + +/* Read callback */ +typedef ssize_t (*BLE_read_cb_t) (struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset); +/* Write callback. */ +typedef ssize_t (*BLE_write_cb_t) (struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); + +/* CCC callback. */ +typedef void (*BLE_ccc_cb_t) (const struct bt_gatt_attr *attr, uint16_t value); + + +/* Characteristics table for notifications. */ +/*-------------------------------------------------------------------------------------------------- + * ATTRIBUTE INDEX LOOKUP TABLE + * + * Each characteristic with CCC takes 3 indices: + * - Characteristic declaration + * - Characteristic value (this is what we notify on) + * - CCC descriptor + * + * Formula: value_index = 1 + (char_position * 3) + 1 + * Where char_position is 0-based position in service definition + *------------------------------------------------------------------------------------------------*/ +typedef enum +{ + BLE_CHAR_ADV_NAME = 0, + BLE_CHAR_PAIRING, + BLE_CHAR_RING_COLOR, + BLE_CHAR_RING_BRIGHTNESS, + BLE_CHAR_INDICATOR_COLOR, + BLE_CHAR_INDICATOR_BRIGHTNESS, + BLE_CHAR_LEVEL_COLOR, + BLE_CHAR_LEVEL_BRIGHTNESS, + BLE_CHAR_BUBBLE_BRIGHTNESS, + BLE_CHAR_FIBER_BRIGHTNESS, + BLE_CHAR_PIN_BRIGHTNESS, + BLE_CHAR_LEVEL_CALIBRATE, + BLE_CHAR_LEVEL_SENSITIVITY, + BLE_CHAR_LEVEL_OPTION, + BLE_CHAR_LEVEL_VISCOSITY, + BLE_CHAR_LEVEL_ANGLE, + BLE_CHAR_AMBIENT_LIGHT, + BLE_CHAR_FSM_TIMEOUT, + BLE_CHAR_FSM_AUTO_BRIGHTNESS, + BLE_CHAR_FSM_APP_CONFIG, + BLE_CHAR_APP_MPCB_VERSION, + BLE_CHAR_APP_RPCB_VERSION, + BLE_CHAR_APP_VERSION, + BLE_CHAR_APP_MFG_ID, + BLE_CHAR_APP_MFG_DATE, + BLE_CHAR_APP_PN, + BLE_CHAR_APP_SN, + BLE_CHAR_APP_POWER_COUNT, + BLE_CHAR_APP_CLI_ENABLED, + BLE_CHAR_BATTERY_LEVEL, + BLE_CHAR_FSM_MODE, + + /* Shot capture service. */ + BLE_CHAR_USER_SHOT_CNT, + BLE_CHAR_TOTAL_SHOT_CNT, + BLE_CHAR_SHOT_DATA_REQUEST, + BLE_CHAR_SHOT_DATA, + + BLE_CHAR_COUNT +} BLE_char_e; + + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void BLE_init (void); + +void BLE_adv_name_set (char *p_str); + +void BLE_enable (bool en, bool save); + +bool BLE_is_enabled (void); + +bool BLE_is_connected (void); + +void BLE_notify (BLE_char_e idx, const void *p_data, uint16_t len); + +/** + * @brief Notify BLE that shot data is ready, then push it to phone via notifications. + * @param p_data Pointer to shot data buffer (header + data). + * @param total_len Total length of header + shot data. + */ +void BLE_shot_data_ready(uint8_t *p_data, uint32_t total_len); + +/** + * @brief Notify BLE that no shot data file was found. + */ +void BLE_shot_data_not_found(void); + +/** + * @brief Notify BLE that a timeout occurred while waiting for phone to send shot data consumed. + */ +void BLE_shot_data_timeout(void); + +void BLE_cli_cmd (int32_t argc, char **argv); + + +#endif /* BLE_H_ */ diff --git a/src/app/bubble.c b/src/app/bubble.c new file mode 100644 index 0000000..c883a89 --- /dev/null +++ b/src/app/bubble.c @@ -0,0 +1,261 @@ +/** + * @file bubble.c + * @brief Bubble LED Module + * @author Kenny Tran + * @date Mar 08 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/gamma.h" +#include "../lib/print.h" + +#include "../bsp/led.h" + +#include "ble.h" + +#include "bubble.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static FS_file_t _bubble_file; +static BUBBLE_config_s _bubble_config = (BUBBLE_config_s) { + .brightness_step = BUBBLE_BRIGHTNESS_STEP_DEFAULT, + .reserved_0 = 0xFF, + .reserved_1 = 0xFF, + .reserved_2 = 0xFF +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _bubble_config_read (void); +static void _bubble_config_write (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void BUBBLE_init (void) +{ + PRINT (INFO, "Initializing BUBBLE...\r\n"); + + if (LED_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize LEDs.\r\n"); + return; + } + + /* Initialize filesystem. */ + if (FS_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize filesystem.\r\n"); + return; + } + + /* Fetch configuration settings stored for bubble. */ + _bubble_config_read (); +} + +void BUBBLE_brightness_set (uint8_t step, bool save_settings) +{ + if (step > BUBBLE_BRIGHTNESS_STEPS) + { + PRINT (ERROR, "Invalid brightness step.\r\n"); + return; + } + + /* Set brightness step. */ + _bubble_config.brightness_step = step; + + if (save_settings) + { + /* Write new settings to file. */ + _bubble_config_write (); + + /* Notify changes to BLE subscriber. */ + BLE_notify (BLE_CHAR_BUBBLE_BRIGHTNESS, &step, sizeof (step)); + } +} + +uint8_t BUBBLE_brightness_get (void) +{ + return _bubble_config.brightness_step; +} + +void BUBBLE_enable (bool en) +{ + float duty; + + if (en) + { + /* 100 for 100% max duty cycle. BUBBLE_BRIGHTNESS_STEPS + 1 extra step for off. */ + duty = (float) GAMMA_corrected_get ((uint32_t) _bubble_config.brightness_step, + (uint32_t) (BUBBLE_BRIGHTNESS_STEPS + 1), + (uint32_t) 100, + (double) BUBBLE_BRIGHTNESS_GAMMA_CF); + } + else + { + duty = 0.0; + } + + if (LED_pwm_set (LED_BUBBLE, duty) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to set bubble LED brightness.\r\n"); + } +} + +void BUBBLE_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "BUBBLE UV LED\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "brightness_step (0 - %d) : %d\r\n", BUBBLE_BRIGHTNESS_STEPS, _bubble_config.brightness_step); + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-e") || !strcmp (argv[0], "--enable")) + { + if (!strcmp (argv[1], "true")) + { + BUBBLE_enable (true); + } + else if (!strcmp (argv[1], "false")) + { + BUBBLE_enable (false); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-s")) + { + uint8_t step = (uint8_t) strtoul (argv[1], NULL, 0); + + BUBBLE_brightness_set (step, true); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: bubble [OPTION...]\r\n"); + PRINT (INFO, " -e | --enable Enables bubble LED\r\n"); + PRINT (INFO, " <%%b> true = enable\r\n"); + PRINT (INFO, " false = disable\r\n"); + PRINT (INFO, " -s Sets bubble UV LED brightness step\r\n"); + PRINT (INFO, " <%%d> Brightness step [0-%d]\r\n", BUBBLE_BRIGHTNESS_STEPS); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _bubble_config_read (void) +{ + ERRNO_e err; + BUBBLE_config_s cfg; + + err = FS_open_or_create (BUBBLE_FILENAME, &_bubble_file, sizeof (BUBBLE_config_s), FS_FILE_DEPTH_DEFAULT); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_bubble_file, (uint8_t *) &cfg, 0, sizeof (BUBBLE_config_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + /* Write defaults to file. */ + _bubble_config_write (); + return; + } + + /* Validate read file. */ + if (cfg.brightness_step > BUBBLE_BRIGHTNESS_STEPS) + { + /* Write defaults to file. */ + _bubble_config_write (); + return; + } + + /* Update bubble configuration to what was read from flash and validated. */ + _bubble_config = cfg; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete (BUBBLE_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old bubble file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (BUBBLE_FILENAME, &_bubble_file, sizeof (BUBBLE_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create bubble configuration file.\r\n"); + } + else + { + PRINT (INFO, "New bubble file created successfully\r\n"); + } + + /* Write default config to file. */ + _bubble_config_write (); + } + else + { + /* Write defaults to file. */ + _bubble_config_write (); + } +} + +static void _bubble_config_write (void) +{ + if (FS_write (_bubble_file, (uint8_t *) &_bubble_config) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write bubble configuration to file.\r\n"); + } +} diff --git a/src/app/bubble.h b/src/app/bubble.h new file mode 100644 index 0000000..3113465 --- /dev/null +++ b/src/app/bubble.h @@ -0,0 +1,65 @@ +/** + * @file bubble.h + * @brief Bubble LED Module + * @author Kenny Tran + * @date Mar 08 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef BUBBLE_H_ +#define BUBBLE_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define BUBBLE_BRIGHTNESS_STEPS (10) +#define BUBBLE_BRIGHTNESS_STEP_DEFAULT (5) +#define BUBBLE_BRIGHTNESS_GAMMA_CF (1.6) /* Gamma correction factor */ + +#define BUBBLE_FILENAME ("bubble") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct BUBBLE_config +{ + uint8_t brightness_step : 8; + uint8_t reserved_0 : 8; + uint8_t reserved_1 : 8; + uint8_t reserved_2 : 8; +} __attribute__((packed, aligned(1))) BUBBLE_config_s; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void BUBBLE_init (void); + +void BUBBLE_brightness_set (uint8_t step, bool save_settings); + +uint8_t BUBBLE_brightness_get (void); + +void BUBBLE_enable (bool en); + +void BUBBLE_cli_cmd (int32_t argc, char **argv); + + +#endif /* BUBBLE_H_ */ diff --git a/src/app/event.c b/src/app/event.c new file mode 100644 index 0000000..47b1857 --- /dev/null +++ b/src/app/event.c @@ -0,0 +1,984 @@ +/** + * @file event.c + * @brief Event module + * @author Kenny Tran + * @date Nov 19 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" +#include "../lib/debug.h" + +#include "../bsp/button.h" +#include "../bsp/battery.h" +#include "../bsp/wdt.h" + +#include "../app/ble.h" +#include "../app/motion.h" +#include "../app/shot_capture.h" + +#include "event.h" + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum +{ + _EVENT_STATE_INIT, + _EVENT_STATE_IDLE, + + _EVENT_STATE_LP, + _EVENT_STATE_CP, + _EVENT_STATE_RP, + + _EVENT_STATE_CH, /* Center press and long hold to enter subconfig. */ + + _EVENT_STATE_SUBCONFIG, + _EVENT_STATE_SUBCONFIG_LP, + _EVENT_STATE_SUBCONFIG_CP, + _EVENT_STATE_SUBCONFIG_RP, + _EVENT_STATE_SUBCONFIG_CH, /* Center press and long hold in subconfig to exit subconfig. */ + + _EVENT_STATE_RHLP, /* Right hold, Left pressed */ + + _EVENT_STATE_RHCP, /* Right hold, center pressed */ + + _EVENT_STATE_LHCP, /* Left hold, center pressed */ + + _EVENT_STATE_LHRP, /* Left hold, right pressed */ + + _EVENT_STATE_RHCHLP, /* Right hold, center hold, left pressed */ + + _EVENT_STATE_LHCHRP, /* Left hold, center hold, right pressed */ + + + _EVENT_STATE_SUBCAL, + _EVENT_STATE_SUBCAL_CH, +} _event_state_e; + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +K_THREAD_STACK_DEFINE(_event_stack, EVENT_STACK_SIZE); + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static struct k_thread _event_thread; +static _event_state_e _event_state = _EVENT_STATE_INIT; +static EVENT_e _event = EVENT_NONE; +static uint32_t _event_timer_ms = 0; +static uint16_t _event_batt_charging __attribute__((section(".noinit"))); + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _event_task (void *p1, void *p2, void *p3); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void EVENT_init (void) +{ + PRINT (INFO, "Initializing EVENT...\r\n"); + + /* Initialize BUTTON module. */ + if (BUTTON_init () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize BUTTON.\r\n"); + } + + /* Initialize shot capture. */ + (void) SHOT_CAPTURE_init (); +} + +void EVENT_reset (void) +{ + _event_state = _EVENT_STATE_IDLE; + _event = EVENT_NONE; + _event_timer_ms = 0; +} + +bool EVENT_in_process (void) +{ + /* An event is in process if we are not in idle. */ + if (_event_state != _EVENT_STATE_IDLE) + { + return true; + } + + return false; +} + +EVENT_e EVENT_last_get (void) +{ + EVENT_e evt = _event; + + /* Clear event once read. */ + _event = EVENT_NONE; + + return evt; +} + +void EVENT_force (EVENT_e event) +{ + switch (event) + { + case EVENT_CHARGER_CONNECTED: + _event = EVENT_CHARGER_CONNECTED; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_CHARGER_DISCONNECTED: + _event = EVENT_CHARGER_DISCONNECTED; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_BATTERY_FULL: + _event = EVENT_BATTERY_FULL; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_ACCEL_MOTION: + _event = EVENT_ACCEL_MOTION; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_GYRO_MOTION: + _event = EVENT_GYRO_MOTION; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_POWER: + _event = EVENT_POWER; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_DECREASE_PIN_BRIGHTNESS: + _event = EVENT_DECREASE_PIN_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_INCREASE_PIN_BRIGHTNESS: + _event = EVENT_INCREASE_PIN_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_DECREASE_RING_BRIGHTNESS: + _event = EVENT_DECREASE_RING_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_INCREASE_RING_BRIGHTNESS: + _event = EVENT_INCREASE_RING_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_CONFIG_ENTER: + _event = EVENT_CONFIG_ENTER; + _event_state = _EVENT_STATE_CH; + break; + + case EVENT_SUBCONFIG_PREV: + _event = EVENT_SUBCONFIG_PREV; + _event_state = _EVENT_STATE_SUBCONFIG; + break; + + case EVENT_SUBCONFIG_MODE_CHANGE: + _event = EVENT_SUBCONFIG_MODE_CHANGE; + _event_state = _EVENT_STATE_SUBCONFIG; + break; + + case EVENT_SUBCONFIG_NEXT: + _event = EVENT_SUBCONFIG_NEXT; + _event_state = _EVENT_STATE_SUBCONFIG; + break; + + case EVENT_CONFIG_EXIT: + _event = EVENT_CONFIG_EXIT; + _event_state = _EVENT_STATE_SUBCONFIG_CH; + break; + + case EVENT_AUTO_BRIGHTNESS: + _event = EVENT_AUTO_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_BLE_ENABLE: + _event = EVENT_BLE_ENABLE; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_BLE_DISABLE: + _event = EVENT_BLE_DISABLE; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_BLE_CONNECTED: + _event = EVENT_BLE_CONNECTED; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_BLE_DISCONNECTED: + _event = EVENT_BLE_DISCONNECTED; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_FACTORY_RESET: + _event = EVENT_FACTORY_RESET; + _event_state = _EVENT_STATE_IDLE; + break; + + case EVENT_SUBCAL_START: + _event = EVENT_SUBCAL_START; + _event_state = _EVENT_STATE_SUBCAL; + break; + + case EVENT_SUBCAL_NEXT: + _event = EVENT_SUBCAL_NEXT; + _event_state = _EVENT_STATE_SUBCAL; + break; + + case EVENT_SHOT_DETECTED: + _event = EVENT_SHOT_DETECTED; + _event_state = _EVENT_STATE_IDLE; + break; + } + + /* Reset timer counter. */ + _event_timer_ms = 0; +} + +void EVENT_task_suspend (void) +{ + k_thread_suspend (&_event_thread); +} + +void EVENT_task_resume (void) +{ + k_thread_resume (&_event_thread); +} + +void EVENT_task_install (void) +{ + static k_tid_t id; + + id = k_thread_create (&_event_thread, + _event_stack, + EVENT_STACK_SIZE, + _event_task, + NULL, + NULL, + NULL, + EVENT_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&_event_thread, "EVENT_task"); +} + +void EVENT_cli_cmd (int32_t argc, char **argv) +{ + EVENT_e evt; + + if (argc == 0) + { + evt = EVENT_last_get (); + + PRINT (INFO, "EVENT\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Last event : "); + switch (evt) + { + case EVENT_NONE: + PRINT (INFO, "NONE\r\n"); + break; + + case EVENT_CHARGER_CONNECTED: + PRINT (INFO, "CHARGER_CONNECTED\r\n"); + break; + + case EVENT_CHARGER_DISCONNECTED: + PRINT (INFO, "CHARGER_DISCONNECTED\r\n"); + break; + + case EVENT_BATTERY_FULL: + PRINT (INFO, "BATTERY_FULL\r\n"); + break; + + case EVENT_ACCEL_MOTION: + PRINT (INFO, "ACCEL_MOTION\r\n"); + break; + + case EVENT_GYRO_MOTION: + PRINT (INFO, "GYRO_MOTION\r\n"); + break; + + case EVENT_POWER: + PRINT (INFO, "POWER\r\n"); + break; + + case EVENT_DECREASE_PIN_BRIGHTNESS: + PRINT (INFO, "DECREASE_PIN_BRIGHTNESS\r\n"); + break; + + case EVENT_INCREASE_PIN_BRIGHTNESS: + PRINT (INFO, "INCREASE_PIN_BRIGHTNESS\r\n"); + break; + + case EVENT_DECREASE_RING_BRIGHTNESS: + PRINT (INFO, "DECREASE_RING_BRIGHTNESS\r\n"); + break; + + case EVENT_INCREASE_RING_BRIGHTNESS: + PRINT (INFO, "INCREASE_RING_BRIGHTNESS\r\n"); + break; + + case EVENT_CONFIG_ENTER: + PRINT (INFO, "CONFIG_ENTER\r\n"); + break; + + case EVENT_SUBCONFIG_PREV: + PRINT (INFO, "SUBCONFIG_PREV\r\n"); + break; + + case EVENT_SUBCONFIG_MODE_CHANGE: + PRINT (INFO, "SUBCONFIG_MODE_CHANGE\r\n"); + break; + + case EVENT_SUBCONFIG_NEXT: + PRINT (INFO, "SUBCONFIG_NEXT\r\n"); + break; + + case EVENT_CONFIG_EXIT: + PRINT (INFO, "CONFIG_EXIT\r\n"); + break; + + case EVENT_AUTO_BRIGHTNESS: + PRINT (INFO, "AUTO_BRIGHTNESS\r\n"); + break; + + case EVENT_BLE_ENABLE: + PRINT (INFO, "EVENT_BLE_ENABLE\r\n"); + break; + + case EVENT_BLE_DISABLE: + PRINT (INFO, "EVENT_BLE_DISABLE\r\n"); + break; + + case EVENT_BLE_CONNECTED: + PRINT (INFO, "BLE_CONNECTED\r\n"); + break; + + case EVENT_BLE_DISCONNECTED: + PRINT (INFO, "BLE_DISCONNECTED\r\n"); + break; + + case EVENT_FACTORY_RESET: + PRINT (INFO, "FACTORY RESET\r\n"); + break; + + case EVENT_SUBCAL_START: + PRINT (INFO, "SUBCAL_START\r\n"); + break; + + case EVENT_SUBCAL_NEXT: + PRINT (INFO, "SUBCAL_NEXT\r\n"); + break; + + case EVENT_SHOT_DETECTED: + PRINT (INFO, "SHOT_DETECTED\r\n"); + break; + } + + PRINT (INFO, "_event_state : %d\r\n", _event_state); + } + else if (argc == 2) + { + if (!strcmp (argv[0], "--set") || !strcmp (argv[0], "-s")) + { + if (!strcmp (argv[1], "cc")) + { + EVENT_force (EVENT_CHARGER_CONNECTED); + } + else if (!strcmp (argv[1], "cd")) + { + EVENT_force (EVENT_CHARGER_DISCONNECTED); + } + else if (!strcmp (argv[1], "bfull")) + { + EVENT_force (EVENT_BATTERY_FULL); + } + else if (!strcmp (argv[1], "accel_motion")) + { + EVENT_force (EVENT_ACCEL_MOTION); + } + else if (!strcmp (argv[1], "gyro_motion")) + { + EVENT_force (EVENT_GYRO_MOTION); + } + else if (!strcmp (argv[1], "power")) + { + EVENT_force (EVENT_POWER); + } + else if (!strcmp (argv[1], "p-")) + { + EVENT_force (EVENT_DECREASE_PIN_BRIGHTNESS); + } + else if (!strcmp (argv[1], "p+")) + { + EVENT_force (EVENT_INCREASE_PIN_BRIGHTNESS); + } + else if (!strcmp (argv[1], "r-")) + { + EVENT_force (EVENT_DECREASE_RING_BRIGHTNESS); + } + else if (!strcmp (argv[1], "r+")) + { + EVENT_force (EVENT_INCREASE_RING_BRIGHTNESS); + } + else if (!strcmp (argv[1], "cfg")) + { + EVENT_force (EVENT_CONFIG_ENTER); + } + else if (!strcmp (argv[1], "cfg-")) + { + EVENT_force (EVENT_SUBCONFIG_PREV); + } + else if (!strcmp (argv[1], "cfgmode")) + { + EVENT_force (EVENT_SUBCONFIG_MODE_CHANGE); + } + else if (!strcmp (argv[1], "cfg+")) + { + EVENT_force (EVENT_SUBCONFIG_NEXT); + } + else if (!strcmp (argv[1], "cfgexit")) + { + EVENT_force (EVENT_CONFIG_EXIT); + } + else if (!strcmp (argv[1], "autobright")) + { + EVENT_force (EVENT_AUTO_BRIGHTNESS); + } + else if (!strcmp (argv[1], "bleen")) + { + EVENT_force (EVENT_BLE_ENABLE); + } + else if (!strcmp (argv[1], "bledis")) + { + EVENT_force (EVENT_BLE_DISABLE); + } + else if (!strcmp (argv[1], "bleconn")) + { + EVENT_force (EVENT_BLE_CONNECTED); + } + else if (!strcmp (argv[1], "bledisconn")) + { + EVENT_force (EVENT_BLE_DISCONNECTED); + } + else if (!strcmp (argv[1], "freset")) + { + EVENT_force (EVENT_FACTORY_RESET); + } + else if (!strcmp (argv[1], "calstart")) + { + EVENT_force (EVENT_SUBCAL_START); + } + else if (!strcmp (argv[1], "calnext")) + { + EVENT_force (EVENT_SUBCAL_NEXT); + } + else if (!strcmp (argv[1], "shotdet")) + { + EVENT_force (EVENT_SUBCAL_NEXT); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: event [OPTION...]\r\n"); + PRINT (INFO, " -s | --set Set an event\r\n"); + PRINT (INFO, " <%%s> cc (charger connected)\r\n"); + PRINT (INFO, " cd (charger disconnected)\r\n"); + PRINT (INFO, " bfull (battery full)\r\n"); + PRINT (INFO, " accel_motion\r\n"); + PRINT (INFO, " gyro_motion\r\n"); + PRINT (INFO, " power\r\n"); + PRINT (INFO, " p- (decrease pin brightness)\r\n"); + PRINT (INFO, " p+ (increase pin brightness)\r\n"); + PRINT (INFO, " r- (decrease ring brightness)\r\n"); + PRINT (INFO, " r+ (increase ring brightness)\r\n"); + PRINT (INFO, " cfg\r\n"); + PRINT (INFO, " cfg-\r\n"); + PRINT (INFO, " cfgmode\r\n"); + PRINT (INFO, " cfg+\r\n"); + PRINT (INFO, " cfgexit\r\n"); + PRINT (INFO, " ble\r\n"); + PRINT (INFO, " freset\r\n"); + PRINT (INFO, " calstart\r\n"); + PRINT (INFO, " calnext\r\n"); + PRINT (INFO, " shotdet\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _event_task (void *p1, void *p2, void *p3) +{ + static bool prev_ble_connected = false; + static uint32_t prev_shot_count = 0; + bool left_pressed; + bool center_pressed; + bool right_pressed; + bool is_charging; + + while (1) + { + DBG_STAT_INC(event_task_counter); + DBG_STAT_INC_IF(i2c_transfer_active,event_task_during_i2c_counter); + /* Read button states. */ + (void) BUTTON_is_pressed (BUTTON_LEFT, &left_pressed); + (void) BUTTON_is_pressed (BUTTON_CENTER, ¢er_pressed); + (void) BUTTON_is_pressed (BUTTON_RIGHT, &right_pressed); + + /* Check charging status. */ + is_charging = BATTERY_is_charging (); + + switch (_event_state) + { + case _EVENT_STATE_INIT: + /* Update previous charging state. */ + _event_batt_charging = (_event_batt_charging << 1) | (uint16_t) is_charging; + + /* On init, prev shot count will be 0. This will always generate a SHOT DETECTED event in IDLE. + Set here to prevent false event. */ + prev_shot_count = SHOT_CAPTURE_config_get ()->user_shot_cnt; + + _event_state = _EVENT_STATE_IDLE; + break; + + case _EVENT_STATE_IDLE: + /* Check for button presses. */ + if (left_pressed) + { + _event_state = _EVENT_STATE_LP; + } + else if (center_pressed) + { + _event_state = _EVENT_STATE_CP; + } + else if (right_pressed) + { + _event_state = _EVENT_STATE_RP; + } + + /* Check if charger is connected / disconnected. */ + if ((_event_batt_charging == 0) && is_charging) + { + float voltage; + + if (BATTERY_voltage_get (&voltage)) + { + PRINT (ERROR, "Failure to read battery voltage.\r\n"); + } + + /* If we are greated than 4.1V, connected event likely means it's because the battery is full and pulse charging. */ + if (voltage < 4.1f) + { + _event = EVENT_CHARGER_CONNECTED; + } + } + else if ((_event_batt_charging == 0xFFFF) && !is_charging) + { + float voltage; + + if (BATTERY_voltage_get (&voltage)) + { + PRINT (ERROR, "Failure to read battery voltage.\r\n"); + } + + /* If we are greated than 4.1V, disconnected event likely means it's because the battery is full. */ + if (voltage >= 4.1f) + { + _event = EVENT_BATTERY_FULL; + } + else + { + _event = EVENT_CHARGER_DISCONNECTED; + } + } + /* Check for BLE connection. */ + else if (!prev_ble_connected && BLE_is_connected ()) + { + _event = EVENT_BLE_CONNECTED; + } + else if (prev_ble_connected && !BLE_is_connected ()) + { + _event = EVENT_BLE_DISCONNECTED; + } + /* Check for shot. */ + else if (SHOT_CAPTURE_config_get ()->user_shot_cnt != prev_shot_count) + { + _event = EVENT_SHOT_DETECTED; + } + /* Check for accel motion. */ + else if (MOTION_linear_detected ()) + { + _event = EVENT_ACCEL_MOTION; + } + + /* Check for gyro motion. */ + else if (MOTION_angular_detected()) + { + _event = EVENT_GYRO_MOTION; + } + + /* Update previous shot count. */ + prev_shot_count = SHOT_CAPTURE_config_get ()->user_shot_cnt; + + /* Update previous charging state. */ + _event_batt_charging = (_event_batt_charging << 1) | (uint16_t) is_charging; + + /* Update previous BLE state. */ + prev_ble_connected = BLE_is_connected (); + break; + + case _EVENT_STATE_LP: + if (left_pressed) + { + /* Check if center is pressed while left is held. */ + if (center_pressed) + { + _event_state = _EVENT_STATE_LHCP; + } + else if (right_pressed) + { + _event_state = _EVENT_STATE_LHRP; + } + /* Check if hold time meets short hold interval. */ + else if (_event_timer_ms >= EVENT_SHORT_HOLD_PERIOD_MS) + { + /* Center button has been held for EVENT_SHORT_HOLD_PERIOD_MS. Set event */ + _event = EVENT_DECREASE_RING_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + /* Reset timer counter. */ + _event_timer_ms = 0; + } + else + { + _event_timer_ms += EVENT_TASK_PERIOD_MS; + } + } + else + { + /* Left button pressed and released. Set event and reset to idle state. */ + _event = EVENT_DECREASE_PIN_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_CP: + if (center_pressed) + { + /* Check if hold time meets long hold interval. */ + if (_event_timer_ms >= EVENT_LONG_HOLD_PERIOD_MS) + { + /* Center button has been held for EVENT_LONG_HOLD_PERIOD_MS. Set event and move to sub config state. */ + _event = EVENT_CONFIG_ENTER; + _event_state = _EVENT_STATE_CH; + /* Reset timer counter. */ + _event_timer_ms = 0; + } + else + { + _event_timer_ms += EVENT_TASK_PERIOD_MS; + } + } + else + { + /* Reset timer counter. */ + _event_timer_ms = 0; + /* Center button pressed and released. Set event and reset to idle state. */ + _event = EVENT_POWER; + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_RP: + if (right_pressed) + { + /* Check if left is pressed while right is held. */ + if (left_pressed) + { + _event_state = _EVENT_STATE_RHLP; + } + /* Check if center is pressed while right is held. */ + else if (center_pressed) + { + _event_state = _EVENT_STATE_RHCP; + } + /* Check if hold time meets short hold interval. */ + else if (_event_timer_ms >= EVENT_SHORT_HOLD_PERIOD_MS) + { + /* Center button has been held for EVENT_SHORT_HOLD_PERIOD_MS. Set event */ + _event = EVENT_INCREASE_RING_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + /* Reset timer counter. */ + _event_timer_ms = 0; + } + else + { + _event_timer_ms += EVENT_TASK_PERIOD_MS; + } + } + else + { + /* Right button pressed and released. Set event and reset to idle state. */ + _event = EVENT_INCREASE_PIN_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_CH: + if (!center_pressed) + { + /* Once released, go to subconfig state. */ + _event_state = _EVENT_STATE_SUBCONFIG; + } + break; + + case _EVENT_STATE_SUBCONFIG: + if (left_pressed) + { + _event_state = _EVENT_STATE_SUBCONFIG_LP; + } + else if (center_pressed) + { + _event_state = _EVENT_STATE_SUBCONFIG_CP; + } + else if (right_pressed) + { + _event_state = _EVENT_STATE_SUBCONFIG_RP; + } + break; + + case _EVENT_STATE_SUBCONFIG_LP: + if (!left_pressed) + { + /* Left button pressed while in subconfig. Set event and go back to subconfig state. */ + _event = EVENT_SUBCONFIG_PREV; + _event_state = _EVENT_STATE_SUBCONFIG; + } + break; + + case _EVENT_STATE_SUBCONFIG_CP: + if (center_pressed) + { + /* Check if hold time meets long hold interval. */ + if (_event_timer_ms >= EVENT_LONG_HOLD_PERIOD_MS) + { + /* Center button has been held for EVENT_LONG_HOLD_PERIOD_MS. Set event and reset state. */ + _event = EVENT_CONFIG_EXIT; + _event_state = _EVENT_STATE_SUBCONFIG_CH; + /* Reset timer counter. */ + _event_timer_ms = 0; + } + else + { + _event_timer_ms += EVENT_TASK_PERIOD_MS; + } + } + else + { + /* Reset timer counter. */ + _event_timer_ms = 0; + /* Center button pressed and released while in sub config. Set event and go back to subconfig state. */ + _event = EVENT_SUBCONFIG_MODE_CHANGE; + _event_state = _EVENT_STATE_SUBCONFIG; + } + break; + + case _EVENT_STATE_SUBCONFIG_RP: + if (!right_pressed) + { + /* Right button pressed while in subconfig. Set event and go back to subconfig state. */ + _event = EVENT_SUBCONFIG_NEXT; + _event_state = _EVENT_STATE_SUBCONFIG; + } + break; + + + case _EVENT_STATE_SUBCONFIG_CH: + if (!center_pressed) + { + /* Once released, go back to idle. */ + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_RHLP: + if (left_pressed && right_pressed) + { + /* Check if hold time meets long hold interval. */ + if (_event_timer_ms >= EVENT_CALIBRATION_PERIOD_MS) + { + /* Right and left habe been held for EVENT_CALIBRATION_PERIOD_MS. Set event. */ + _event = EVENT_SUBCAL_START; + _event_state = _EVENT_STATE_SUBCAL; + /* Reset timer counter. */ + _event_timer_ms = 0; + } + else + { + _event_timer_ms += EVENT_TASK_PERIOD_MS; + } + } + else + { + /* Reset timer counter. */ + _event_timer_ms = 0; + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_RHCP: + if (right_pressed && center_pressed) + { + /* Check if left is pressed while right and center are held. */ + if (left_pressed) + { + _event_state = _EVENT_STATE_RHCHLP; + } + } + else + { + /* Reset to idle state. */ + _event_state = _EVENT_STATE_IDLE; + } + break; + + + case _EVENT_STATE_LHRP: + if (!right_pressed) + { + /* If left pressed and held, then right pressed and released, set auto brightness event. */ + _event = EVENT_AUTO_BRIGHTNESS; + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_LHCP: + if (left_pressed && center_pressed) + { + /* Check if right is pressed while left and center are held. */ + if (right_pressed) + { + _event_state = _EVENT_STATE_LHCHRP; + } + } + else + { + /* Reset to idle state. */ + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_RHCHLP: + if (right_pressed && center_pressed && left_pressed) + { + + } + else + { + /* Reset timer counter. */ + _event_timer_ms = 0; + /* On all three button released, enable BLE. */ + _event = EVENT_BLE_DISABLE; + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_LHCHRP: + if (left_pressed && center_pressed && right_pressed) + { + /* Check if hold time meets long hold interval. */ + if (_event_timer_ms >= EVENT_FACTORY_RESET_PERIOD_MS) + { + /* Center button has been held for EVENT_FACTORY_RESET_PERIOD_MS. Set event. */ + _event = EVENT_FACTORY_RESET; + _event_state = _EVENT_STATE_IDLE; + /* Reset timer counter. */ + _event_timer_ms = 0; + } + else + { + _event_timer_ms += EVENT_TASK_PERIOD_MS; + } + } + else + { + /* Reset timer counter. */ + _event_timer_ms = 0; + /* On all three button released, enable BLE. */ + _event = EVENT_BLE_ENABLE; + _event_state = _EVENT_STATE_IDLE; + } + break; + + case _EVENT_STATE_SUBCAL: + if (center_pressed) + { + /* Reset timer counter. */ + _event_timer_ms = 0; + _event_state = _EVENT_STATE_SUBCAL_CH; + } + break; + + case _EVENT_STATE_SUBCAL_CH: + if (!center_pressed) + { + /* Reset timer counter. */ + _event_timer_ms = 0; + _event = EVENT_SUBCAL_NEXT; + _event_state = _EVENT_STATE_SUBCAL; + } + break; + } + + /* Feed watchdog at end of loop. */ + WDT_feed (); + + k_msleep (EVENT_TASK_PERIOD_MS); + } +} diff --git a/src/app/event.h b/src/app/event.h new file mode 100644 index 0000000..86d2af0 --- /dev/null +++ b/src/app/event.h @@ -0,0 +1,92 @@ +/** + * @file event.h + * @brief Event module + * @author Kenny Tran + * @date Nov 19 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef EVENT_H_ +#define EVENT_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define EVENT_TASK_PERIOD_MS (100) +#define EVENT_STACK_SIZE (1024) +#define EVENT_THREAD_PRIORITY (7) + +#define EVENT_SHORT_HOLD_PERIOD_MS (750) +#define EVENT_LONG_HOLD_PERIOD_MS (3000) +#define EVENT_FACTORY_RESET_PERIOD_MS (10000) + +#define EVENT_CALIBRATION_PERIOD_MS (10000) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum EVENT +{ + EVENT_NONE = 0, + EVENT_CHARGER_CONNECTED, + EVENT_CHARGER_DISCONNECTED, + EVENT_BATTERY_FULL, + EVENT_ACCEL_MOTION, + EVENT_GYRO_MOTION, + EVENT_POWER, + EVENT_DECREASE_PIN_BRIGHTNESS, + EVENT_INCREASE_PIN_BRIGHTNESS, + EVENT_DECREASE_RING_BRIGHTNESS, + EVENT_INCREASE_RING_BRIGHTNESS, + EVENT_CONFIG_ENTER, + EVENT_SUBCONFIG_PREV, + EVENT_SUBCONFIG_MODE_CHANGE, + EVENT_SUBCONFIG_NEXT, + EVENT_CONFIG_EXIT, + EVENT_AUTO_BRIGHTNESS, + EVENT_BLE_ENABLE, + EVENT_BLE_DISABLE, + EVENT_BLE_CONNECTED, + EVENT_BLE_DISCONNECTED, + EVENT_FACTORY_RESET, + EVENT_SUBCAL_START, + EVENT_SUBCAL_NEXT, + EVENT_SHOT_DETECTED, +} EVENT_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void EVENT_init (void); + +void EVENT_reset (void); + +bool EVENT_in_process (void); + +EVENT_e EVENT_last_get (void); + +void EVENT_force (EVENT_e event); + +void EVENT_task_suspend (void); + +void EVENT_task_resume (void); + +void EVENT_task_install (void); + +void EVENT_cli_cmd (int32_t argc, char **argv); + +#endif /* EVENT_H_ */ diff --git a/src/app/fiber.c b/src/app/fiber.c new file mode 100644 index 0000000..d4135d6 --- /dev/null +++ b/src/app/fiber.c @@ -0,0 +1,261 @@ +/** + * @file fiber.c + * @brief Fiber LED Module + * @author Kenny Tran + * @date Jul 18 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/gamma.h" +#include "../lib/print.h" + +#include "../bsp/led.h" + +#include "ble.h" + +#include "fiber.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static FS_file_t _fiber_file; +static FIBER_config_s _fiber_config = (FIBER_config_s) { + .brightness_step = FIBER_BRIGHTNESS_STEP_DEFAULT, + .reserved_0 = 0xFF, + .reserved_1 = 0xFF, + .reserved_2 = 0xFF +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _fiber_config_read (void); +static void _fiber_config_write (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void FIBER_init (void) +{ + PRINT (INFO, "Initializing FIBER...\r\n"); + + if (LED_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize LEDs.\r\n"); + return; + } + + /* Initialize filesystem. */ + if (FS_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize filesystem.\r\n"); + return; + } + + /* Fetch configuration settings stored for fiber. */ + _fiber_config_read (); +} + +void FIBER_brightness_set (uint8_t step, bool save_settings) +{ + if (step > FIBER_BRIGHTNESS_STEPS) + { + PRINT (ERROR, "ERROR : Invalid brightness step.\r\n"); + return; + } + + /* Set brightness step. */ + _fiber_config.brightness_step = step; + + if (save_settings) + { + /* Write new settings to file. */ + _fiber_config_write (); + + /* Notify changes to BLE subscriber. */ + BLE_notify (BLE_CHAR_FIBER_BRIGHTNESS, &step, sizeof (step)); + } +} + +uint8_t FIBER_brightness_get (void) +{ + return _fiber_config.brightness_step; +} + +void FIBER_enable (bool en) +{ + float duty; + + if (en) + { + /* 100 for 100% max duty cycle. FIBER_BRIGHTNESS_STEPS + 1 extra step for off. */ + duty = (float) GAMMA_corrected_get ((uint32_t) _fiber_config.brightness_step, + (uint32_t) (FIBER_BRIGHTNESS_STEPS + 1), + (uint32_t) 100, + (double) FIBER_BRIGHTNESS_GAMMA_CF); + } + else + { + duty = 0.0; + } + + if (LED_pwm_set (LED_FIBER, duty) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to set fiber LED brightness.\r\n"); + } +} + +void FIBER_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "FIBER UV LED\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "brightness_step (0 - %d) : %d\r\n", FIBER_BRIGHTNESS_STEPS, _fiber_config.brightness_step); + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-e") || !strcmp (argv[0], "--enable")) + { + if (!strcmp (argv[1], "true")) + { + FIBER_enable (true); + } + else if (!strcmp (argv[1], "false")) + { + FIBER_enable (false); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-s")) + { + uint8_t step = (uint8_t) strtoul (argv[1], NULL, 0); + + FIBER_brightness_set (step, true); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: fiber [OPTION...]\r\n"); + PRINT (INFO, " -e | --enable Enables fiber LED\r\n"); + PRINT (INFO, " <%%b> true = enable\r\n"); + PRINT (INFO, " false = disable\r\n"); + PRINT (INFO, " -s Sets fiber UV LED brightness step\r\n"); + PRINT (INFO, " <%%d> Brightness step [0-%d]\r\n", FIBER_BRIGHTNESS_STEPS); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _fiber_config_read (void) +{ + ERRNO_e err; + FIBER_config_s cfg; + + err = FS_open_or_create (FIBER_FILENAME, &_fiber_file, sizeof (FIBER_config_s), FS_FILE_DEPTH_DEFAULT); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_fiber_file, (uint8_t *) &cfg, 0, sizeof (FIBER_config_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + /* Write defaults to file. */ + _fiber_config_write (); + return; + } + + /* Validate read file. */ + if (cfg.brightness_step > FIBER_BRIGHTNESS_STEPS) + { + /* Write defaults to file. */ + _fiber_config_write (); + return; + } + + /* Update fiber configuration to what was read from flash and validated. */ + _fiber_config = cfg; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete (FIBER_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old fiber file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (FIBER_FILENAME, &_fiber_file, sizeof (FIBER_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create fiber configuration file.\r\n"); + } + else + { + PRINT (INFO, "New fiber file created successfully\r\n"); + } + + /* Write default config to file. */ + _fiber_config_write (); + } + else + { + /* Write defaults to file. */ + _fiber_config_write (); + } +} + +static void _fiber_config_write (void) +{ + if (FS_write (_fiber_file, (uint8_t *) &_fiber_config) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write fiber configuration to file.\r\n"); + } +} diff --git a/src/app/fiber.h b/src/app/fiber.h new file mode 100644 index 0000000..8c3cc6f --- /dev/null +++ b/src/app/fiber.h @@ -0,0 +1,65 @@ +/** + * @file fiber.h + * @brief Fiber LED Module + * @author Kenny Tran + * @date Jul 18 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef FIBER_H_ +#define FIBER_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define FIBER_BRIGHTNESS_STEPS (10) +#define FIBER_BRIGHTNESS_STEP_DEFAULT (5) +#define FIBER_BRIGHTNESS_GAMMA_CF (1.6) /* Gamma correction factor */ + +#define FIBER_FILENAME ("fiber") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct FIBER_config +{ + uint8_t brightness_step : 8; + uint8_t reserved_0 : 8; + uint8_t reserved_1 : 8; + uint8_t reserved_2 : 8; +} __attribute__((packed, aligned(1))) FIBER_config_s; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void FIBER_init (void); + +void FIBER_brightness_set (uint8_t step, bool save_settings); + +uint8_t FIBER_brightness_get (void); + +void FIBER_enable (bool en); + +void FIBER_cli_cmd (int32_t argc, char **argv); + + +#endif /* FIBER_H_ */ diff --git a/src/app/fsm.c b/src/app/fsm.c new file mode 100644 index 0000000..a21fbca --- /dev/null +++ b/src/app/fsm.c @@ -0,0 +1,2237 @@ +/** + * @file fsm.c + * @brief Finite State Machine + * @author Kenny Tran + * @date July 13 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include +#include + +#include "../wrappers/uart.h" +#include "../wrappers/spi.h" +#include "../wrappers/pwm.h" +#include "../wrappers/i2c.h" + +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/print.h" +#include "../lib/rgb.h" +#include "../lib/debug.h" + +#include "../bsp/als.h" +#include "../bsp/battery.h" +#include "../bsp/button.h" +#include "../bsp/mcu.h" +#include "../bsp/power.h" +#include "../bsp/nvm_ext.h" +#include "../bsp/imu.h" +#include "../bsp/wdt.h" + +#include "../app/app.h" +#include "../app/app_cli.h" +#include "../app/ble.h" +#include "../app/bubble.h" +#include "../app/fiber.h" +#include "../app/event.h" +#include "../app/light_sensor.h" +#include "../app/pin.h" +#include "../app/motion.h" +#include "../app/scope_ring.h" +#include "../app/shot_capture.h" + +#include "fsm.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +K_THREAD_STACK_DEFINE(_fsm_stack, FSM_STACK_SIZE); + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum +{ + _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS = 0, + _FSM_EVT_SUBCONFIG_PIN_BRIGHTNESS, + _FSM_EVT_SUBCONFIG_FIBER_BRIGHTNESS, + _FSM_EVT_SUBCONFIG_RING_BACKGROUND_COLOR, + _FSM_EVT_SUBCONFIG_RING_BACKGROUND_BRIGHTNESS, + _FSM_EVT_SUBCONFIG_RING_LEVEL_COLOR, + _FSM_EVT_SUBCONFIG_RING_LEVEL_BRIGHTNESS, + _FSM_EVT_SUBCONFIG_RING_INDICATORS_COLOR, + _FSM_EVT_SUBCONFIG_RING_INDICATORS_BRIGHTNESS, + _FSM_EVT_SUBCONFIG_LEVEL_SENSITIVITY, + _FSM_EVT_SUBCONFIG_LEVEL_OPTION, +} _fsm_evt_subconfig_e; + +typedef enum +{ + _FSM_EVT_SUBCAL_SET_FLAT = 0, + _FSM_EVT_SUBCAL_FLAT, + _FSM_EVT_SUBCAL_SET_BACK, + _FSM_EVT_SUBCAL_BACK, + _FSM_EVT_SUBCAL_SET_FRONT, + _FSM_EVT_SUBCAL_FRONT, +} _fsm_evt_subcal_e; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static struct k_thread _fsm_thread; +static FS_file_t _fsm_file; +static FSM_e _fsm_state = FSM_INIT; +static bool _fsm_state_entry = false; +static uint32_t _fsm_timeout_cnt = 0; +static FSM_config_s _fsm_config = (FSM_config_s) { + .motion_timeout_ms = FSM_MOTION_TIMEOUT_PERIOD_MS, + .auto_brightness = false, + .mode = FSM_MODE_TARGET +}; +static bool _fsm_evt_busy = false; +static _fsm_evt_subconfig_e _fsm_evt_subconfig = _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS; +static _fsm_evt_subcal_e _fsm_evt_subcal = _FSM_EVT_SUBCAL_SET_FLAT; +static uint32_t _fsm_motion_timer = 0; +static bool _fsm_app_config = false; +static float _fsm_batt_life = 0.0; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _fsm_task (void *p1, void *p2, void *p3); +static void _fsm_config_read (void); +static void _fsm_config_write (void); +static void _fsm_state_set (FSM_e state); +static void _fsm_evt_handler (EVENT_e event); +static void _fsm_auto_brightness (bool enabled); +static void _fsm_low_power_enter (bool wake_on_motion, bool wake_on_charge); +static void _fsm_battery_read (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void FSM_init (void) +{ + PRINT (INFO, "Initializing FSM...\r\n"); + + /* Fetch FSM state from RAM if stored. */ + if (MCU_ram_fetch ((uint8_t *) &_fsm_state, sizeof (_fsm_state)) != ERRNO_SUCCESS) + { + _fsm_state = FSM_INIT; + } + + /* Initialize battery module. */ + if (BATTERY_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize Battery.\r\n"); + } + + /* Initialize 5V step-up converter. */ + if (POWER_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize Power.\r\n"); + } + + /* Fetch configuration settings stored for fsm. */ + _fsm_config_read (); +} + +void FSM_level_calibrate (void) +{ + /* Force event calibration. */ + EVENT_force (EVENT_SUBCAL_START); +} + +void FSM_task_suspend (void) +{ + k_thread_suspend (&_fsm_thread); +} + +void FSM_task_resume (void) +{ + k_thread_resume (&_fsm_thread); +} + +void FSM_task_install (void) +{ + static k_tid_t id; + + id = k_thread_create (&_fsm_thread, + _fsm_stack, + FSM_STACK_SIZE, + _fsm_task, + NULL, + NULL, + NULL, + FSM_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&_fsm_thread, "FSM_task"); +} + +void FSM_timout_set (uint32_t ms) +{ + if (ms < 1000) + { + PRINT (ERROR, "Minimum timeout is 1000 ms.\r\n"); + } + + _fsm_config.motion_timeout_ms = ms; + + /* Write new settings to file. */ + _fsm_config_write (); +} + +uint32_t FSM_timout_get (void) +{ + return _fsm_config.motion_timeout_ms; +} + +void FSM_auto_brightness_set (bool enabled) +{ + _fsm_config.auto_brightness = enabled; + + /* Write new settings to file. */ + _fsm_config_write (); + + BLE_notify (BLE_CHAR_FSM_AUTO_BRIGHTNESS, &enabled, sizeof (enabled)); +} + +bool FSM_auto_brightness_get (void) +{ + return _fsm_config.auto_brightness; +} + +void FSM_mode_set (FSM_MODE_e mode) +{ + _fsm_config.mode = (uint8_t) mode; + + /* Write new settings to file. */ + _fsm_config_write (); + + BLE_notify (BLE_CHAR_FSM_MODE, &mode, sizeof (mode)); +} + +FSM_MODE_e FSM_mode_get (void) +{ + return (FSM_MODE_e) _fsm_config.mode; +} + +void FSM_app_config_set (bool val) +{ + _fsm_app_config = val; +} + +bool FSM_app_config_get (void) +{ + return _fsm_app_config; +} + +float FSM_battery_life_get (void) +{ + return _fsm_batt_life; +} + +void FSM_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "FSM\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "state : %d\r\n", _fsm_state); + PRINT (INFO, "timeout (ms) : %d\r\n", _fsm_config.motion_timeout_ms); + PRINT (INFO, "timeout counter (ms) : %d\r\n", _fsm_timeout_cnt); + PRINT (INFO, "auto brightness : %s\r\n", (_fsm_config.auto_brightness ? "true" : "false")); + PRINT (INFO, "mode : %d\r\n", _fsm_config.mode); + PRINT (INFO, "app config : %s\r\n", (_fsm_app_config ? "true" : "false")); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--list")) + { + PRINT (INFO, " 0 = FSM_INIT\r\n"); + PRINT (INFO, " 1 = FSM_CHARGING\r\n"); + PRINT (INFO, " 2 = FSM_LOW_BATTERY\r\n"); + PRINT (INFO, " 3 = FSM_POWER_ON\r\n"); + PRINT (INFO, " 4 = FSM_IDLE\r\n"); + PRINT (INFO, " 5 = FSM_SLEEP\r\n"); + PRINT (INFO, " 6 = FSM_3D_SLEEP\r\n"); + PRINT (INFO, " 7 = FSM_CHARGER_CONNECTED\r\n"); + PRINT (INFO, " 8 = FSM_CHARGER_DISCONNECTED\r\n"); + PRINT (INFO, " 9 = FSM_LEVELING\r\n"); + PRINT (INFO, " 10 = FSM_POWER_OFF\r\n"); + PRINT (INFO, " 11 = FSM_OFF\r\n"); + PRINT (INFO, " 12 = FSM_BATTERY_FULL\r\n"); + PRINT (INFO, " 13 = FSM_BLE_ENABLE\r\n"); + PRINT (INFO, " 14 = FSM_BLE_DISABLE\r\n"); + PRINT (INFO, " 15 = FSM_RAINBOW\r\n"); + PRINT (INFO, " 16 = FSM_FLAT_CALIBRATE\r\n"); + PRINT (INFO, " 17 = FSM_BACK_CALIBRATE\r\n"); + PRINT (INFO, " 18 = FSM_FRONT_CALIBRATE\r\n"); + PRINT (INFO, "255 = FSM_DEBUG\r\n"); + } + else + { + goto USAGE; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-s")) + { + FSM_e state = (FSM_e) strtoul (argv[1], NULL, 0); + + _fsm_state_set (state); + } + else if (!strcmp (argv[0], "-m")) + { + FSM_MODE_e mode = (FSM_MODE_e) strtoul (argv[1], NULL, 0); + + FSM_mode_set (mode); + } + else if (!strcmp (argv[0], "-ab")) + { + if (!strcmp (argv[1], "true")) + { + FSM_auto_brightness_set (true); + } + else if (!strcmp (argv[1], "false")) + { + FSM_auto_brightness_set (false); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-to") || !strcmp (argv[0], "--timeout")) + { + uint32_t timeout_ms = strtoul (argv[1], NULL, 0); + + FSM_timout_set (timeout_ms); + } + else if (!strcmp (argv[0], "-ac") || !strcmp (argv[0], "--appcfg")) + { + if (!strcmp (argv[1], "true")) + { + FSM_app_config_set (true); + } + else if (!strcmp (argv[1], "false")) + { + FSM_app_config_set (false); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: fsm [OPTION...]\r\n"); + PRINT (INFO, " --list Lists all FSM states\r\n"); + PRINT (INFO, " -s Sets new FSM state\r\n"); + PRINT (INFO, " <%%d> [0 - 18, 255]\r\n"); + PRINT (INFO, " -m Sets FSM mode\r\n"); + PRINT (INFO, " <%%d> [0 - target, 1 - 3D]\r\n"); + PRINT (INFO, " -ab Sets auto brightness\r\n"); + PRINT (INFO, " <%%b> true = enabled, false = disabled\r\n"); + PRINT (INFO, " -to | --timeout Enables fiber LED\r\n"); + PRINT (INFO, " <%%d> timeout in milliseconds\r\n"); + PRINT (INFO, " -ac | --appcfg Enables BLE app config\r\n"); + PRINT (INFO, " <%%b> true = enabled, false = disabled\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _fsm_task (void *p1, void *p2, void *p3) +{ + EVENT_e event; + bool state_finished; + static bool in_use = false; + static uint32_t timer_60s = 0; + static FSM_e prev_state = FSM_INIT; + static uint32_t batt_low_timeout = 0; + static uint32_t timer_3s = 0; + + /* On startup, read the battery before turning on LEDs (causing voltage drop). */ + _fsm_battery_read (); + + while (1) + { + DBG_STAT_INC(fsm_task_counter); + DBG_STAT_INC_IF(i2c_transfer_active,fsm_task_during_i2c_counter); + /* Check for events. */ + event = EVENT_last_get (); + + /* Handle any events from event. */ + _fsm_evt_handler (event); + + /* Process if event occurs, else go into state machine. */ + if (_fsm_evt_busy) + { + /* If we are in config and stuck at one state for too long, exit. */ + if (_fsm_timeout_cnt >= FSM_CONFIG_TIMEOUT_PERIOD_MS) + { + /* Force exit once we've hit timeout. */ + _fsm_evt_handler (EVENT_CONFIG_EXIT); + + /* Reset event so we are no longer in subconfig. */ + EVENT_reset (); + + /* Reset timeout counter. */ + _fsm_timeout_cnt = 0; + } + } + else + { + if (in_use) + { + /* Indicate battery status every 60 seconds if in use. */ + if (timer_60s == 0) + { + if (!BATTERY_is_charging ()) + { + /* Don't let it get below 10 %. */ + if (_fsm_batt_life <= (float) 0.10) + { + PRINT (INFO, "Dead battery. %f\r\n"); + _fsm_state_set (FSM_POWER_OFF); + } + else if (_fsm_batt_life <= (float) 0.20) + { + PRINT (INFO, "Low battery.\r\n"); + /* Calling _fsm_state_set() resets timer. If timeout matches battery check interval, + we go to FSM_LOW_BATTERY to blink status then return to operation. If timer resets, + we keep switching states and end up in a loop. */ + _fsm_state = FSM_LOW_BATTERY; + } + } + + /* Increment timer. */ + timer_60s += FSM_TASK_PERIOD_MS; + } + else if (timer_60s >= 60000) + { + /* Reset timer. */ + timer_60s = 0; + } + else + { + /* Increment timer. */ + timer_60s += FSM_TASK_PERIOD_MS; + } + } + + /* Get status of scope ring state. */ + (void) SCOPE_RING_state_get (&state_finished); + + switch (_fsm_state) + { + case FSM_INIT: + if (BATTERY_is_charging ()) + { + _fsm_state_set (FSM_CHARGING); + } + else + { + /* If configured for 3D, go straight to leveling to save time. */ + if (_fsm_config.mode == FSM_MODE_3D) + { + _fsm_state_set (FSM_LEVELING); + } + else + { + _fsm_state_set (FSM_POWER_ON); + } + } + break; + + case FSM_CHARGING: + { + /* _fsm_timeout_cnt is reset every state change. Use as a flag for one time entry. */ + if (_fsm_state_entry) + { + /* Enable 5v */ + POWER_enable (true); + + /* Disable other LEDs. */ + SHOT_CAPTURE_enable (false); + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + + SCOPE_RING_state_set (SCOPE_RING_STATE_CHARGING, FSM_CHARGING_BLINK_PERIOD_MS); + } + } + break; + + case FSM_LOW_BATTERY: + if (batt_low_timeout >= FSM_LOW_BATTERY_BLINK_PERIOD_MS * 5) + { + if (in_use) + { + /* Calling _fsm_state_set() resets timer. If timeout matches battery check interval, + we go to FSM_LOW_BATTERY to blink status then return to operation. If timer resets, + we keep switching states and end up in a loop. */ + _fsm_state = FSM_LEVELING; + } + else + { + _fsm_state_set (FSM_IDLE); + } + + /* Reset timeout count. */ + batt_low_timeout = 0; + } + else + { + static uint8_t bubble_brightness; + + if (batt_low_timeout == 0) + { + bubble_brightness = BUBBLE_brightness_get (); + + /* Enable 5v */ + POWER_enable (true); + /* Set to max in case user has it off, but don't save config to flash. */ + BUBBLE_brightness_set (BUBBLE_BRIGHTNESS_STEPS, false); + } + else if (batt_low_timeout == FSM_LOW_BATTERY_BLINK_PERIOD_MS) + { + BUBBLE_enable (true); + } + else if (batt_low_timeout == (FSM_LOW_BATTERY_BLINK_PERIOD_MS * 2)) + { + BUBBLE_enable (false); + } + else if (batt_low_timeout == (FSM_LOW_BATTERY_BLINK_PERIOD_MS * 3)) + { + BUBBLE_enable (true); + } + else if (batt_low_timeout == (FSM_LOW_BATTERY_BLINK_PERIOD_MS * 4)) + { + BUBBLE_enable (false); + + /* Restore bubble brightness. */ + BUBBLE_brightness_set (bubble_brightness, false); + } + + /* Update timeout count. */ + batt_low_timeout += FSM_TASK_PERIOD_MS; + } + break; + + case FSM_POWER_ON: + if (_fsm_state_entry) + { + /* Enable 5v */ + POWER_enable (true); + + /* Run through scope ring power up sequence. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_POWER_ON, 0); + } + else if (state_finished) + { + _fsm_state_set (FSM_LEVELING); + } + break; + + case FSM_IDLE: + /* If the phone app is configuring parameters, we want to go to level to see changes.*/ + if (_fsm_app_config) + { + if (BLE_is_connected ()) + { + IMU_motion_int_enable (false, 0x2, 0x1); + _fsm_state_set (FSM_LEVELING); + } + else + { + _fsm_app_config = false; + } + } + else if (_fsm_state_entry) + { + /* When idle, this means scope is stationary. Perform gyro recalibration. */ + MOTION_gyro_recalibrate (); + + /* When idling, we turn off LEDs to save power. */ + SHOT_CAPTURE_enable (false); + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + + /* Disable 5v */ + POWER_enable (false); + + /* Read battery when all LEDs are off. */ + _fsm_battery_read (); + + /* Enable IMU motion wake up function every time we enter idle*/ + IMU_motion_int_enable (true, 0x2, 0x1); + + /* Reset motion timer. */ + _fsm_motion_timer = 0; + + /* Reset in-use flag. */ + in_use = false; + + /* Reset 60s timer for low battery status. */ + timer_60s = 0; + } + else + { + /* Check calibration status. */ + if (MOTION_gyro_recalibrate_status_get () == 1.0f) + { + /* Never go to sleep if BLE is enabled, else connection breaks. */ + if (!BLE_is_enabled ()) + { + _fsm_state_set (FSM_SLEEP); + } + // /* If idle for too long, power down to save power. */ + // if (_fsm_timeout_cnt >= FSM_IDLE_TIMEOUT_PERIOD_MS) + // { + // _fsm_state_set (FSM_OFF); + // } + // else + // { + // /* Never go to sleep if BLE is enabled, else connection breaks. */ + // if (!BLE_is_enabled ()) + // { + // _fsm_state_set (FSM_SLEEP); + // } + // } + } + } + break; + + case FSM_SLEEP: + PRINT (INFO, "System is sleeping...\r\n"); + + /* In sleep, enter low power and allow motion and charger to wake processor. */ + _fsm_low_power_enter (true, false); + break; + + case FSM_3D_SLEEP: + if (_fsm_state_entry) + { + /* First power everything off to get proper battery reading. */ + SHOT_CAPTURE_enable (false); + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + + /* Disable 5v */ + POWER_enable (false); + + /* Read battery. */ + _fsm_battery_read (); + + /* Never go to sleep if BLE is enabled, else connection breaks. */ + if (!BLE_is_enabled ()) + { + /* In off state, enter low power and disable motion but enable charger interrupts to wake processor. */ + _fsm_low_power_enter (false, true); + } + } + /* If in 3D sleep for more than 30 minutes, user likely forgot to turn system off. */ + else if (_fsm_timeout_cnt >= 1800000) + { + /* Disable BLE and power off.*/ + BLE_enable (false, false); + + _fsm_state_set (FSM_OFF); + } + break; + + case FSM_CHARGER_CONNECTED: + if (_fsm_state_entry) + { + /* Enable 5v */ + POWER_enable (true); + + /* Run through charger connected sequence. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_CHARGER_CONNECTED, 0); + } + else if (state_finished) + { + if (in_use) + { + _fsm_state_set (FSM_LEVELING); + } + else + { + _fsm_state_set (FSM_CHARGING); + } + } + break; + + case FSM_CHARGER_DISCONNECTED: + if (_fsm_state_entry) + { + /* Enable 5v */ + POWER_enable (true); + + /* Run through charger disconnected sequence. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_CHARGER_DISCONNECTED, 0); + } + else if (state_finished) + { + if (in_use) + { + _fsm_state_set (FSM_LEVELING); + } + else + { + _fsm_state_set (FSM_BATTERY_FULL); + } + } + break; + + case FSM_LEVELING: + /* When device is not stationary, we abort gyro recalibration. */ + MOTION_gyro_recalibrate_abort (); + + /* If the phone app is configuring parameters, reset timer so we stay in leveling until config is done.*/ + if (_fsm_app_config) + { + if (BLE_is_connected ()) + { + _fsm_timeout_cnt = 0; + } + else + { + _fsm_app_config = false; + } + } + + if (_fsm_state_entry) + { + /* Enable 5V power. */ + POWER_enable (true); + + _fsm_auto_brightness (_fsm_config.auto_brightness); + + /* Enable peripherals with saved settings. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_ON, 0); + + /* Set in-use flag so we don't shutdown if charger is connected. */ + in_use = true; + } + else if (_fsm_timeout_cnt >= _fsm_config.motion_timeout_ms) + { + _fsm_state_set (FSM_IDLE); + } + else + { + /* If we are in the leveling state for FSM_MOTION_TIMEOUT_MAX_MS and no shot has + been detected, assume false motion and go to sleep to save power. */ + // FIXME FSM_MOTION_TIMEOUT_MAX_MS value, sleep or off? + if (_fsm_motion_timer >= 120000) + { + /* When idling, we turn off LEDs to save power. */ + SHOT_CAPTURE_enable (false); + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + + /* Disable 5v */ + POWER_enable (false); + + /* Read battery when all LEDs are off. */ + _fsm_battery_read (); + + /* Reset motion timer. */ + _fsm_motion_timer = 0; + + /* Reset in-use flag. */ + in_use = false; + + /* Reset 60s timer for low battery status. */ + timer_60s = 0; + + _fsm_state_set (FSM_SLEEP); + } + else + { + _fsm_auto_brightness (_fsm_config.auto_brightness); + + SHOT_CAPTURE_enable (true); + BUBBLE_enable (true); + FIBER_enable (true); + PIN_enable (true); + + /* Update motion timer. */ + _fsm_motion_timer += FSM_TASK_PERIOD_MS; + } + } + break; + + case FSM_POWER_OFF: + if (_fsm_state_entry) + { + /* First power everything off to get proper battery reading. */ + SHOT_CAPTURE_enable (false); + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + + /* Disable 5v */ + POWER_enable (false); + + /* Read battery. */ + _fsm_battery_read (); + + /* Reset 3s timer. */ + timer_3s = 0; + + /* Enable 5v */ + POWER_enable (true); + + /* Run through scope ring power down sequence. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_POWER_OFF, 0); + } + else if (state_finished) + { + /* Disable BLE if enabled before shutting down. FSM_CHARGING is a state of shut down. */ + if (BLE_is_enabled ()) + { + BLE_enable (false, false); + } + + if (BATTERY_is_charging ()) + { + _fsm_state_set (FSM_CHARGING); + } + else + { + _fsm_state_set (FSM_OFF); + } + } + break; + + case FSM_OFF: + PRINT (INFO, "System is powering off...\r\n"); + + /* In off state, enter low power and disable motion but enable charger interrupts to wake processor. */ + _fsm_low_power_enter (false, true); + break; + + case FSM_BATTERY_FULL: + PRINT (INFO, "Battery is full. System is powering off...\r\n"); + + /* In battery full state, enter low power and disable motion and charger interrupts that wake processor. */ + _fsm_low_power_enter (false, false); + break; + + case FSM_BLE_ENABLE: + if (_fsm_state_entry) + { + /* Enable 5v */ + POWER_enable (true); + + /* Run through scope ring BLE enabled sequence. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_BLE_ENABLED, 0); + } + else if (state_finished) + { + _fsm_state_set (FSM_LEVELING); + } + break; + + case FSM_BLE_DISABLE: + if (_fsm_state_entry) + { + /* Enable 5v */ + POWER_enable (true); + + /* Run through scope ring BLE disabled sequence. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_BLE_DISABLED, 0); + } + else if (state_finished) + { + _fsm_state_set (FSM_LEVELING); + } + break; + + case FSM_RAINBOW: + /* Enable 5V power. */ + POWER_enable (true); + + /* Turn everything off. */ + SHOT_CAPTURE_enable (false); + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_RAINBOW, 0); + break; + + + case FSM_FLAT_CALIBRATE: + if (timer_3s == 0) + { + /* Enable 5v */ + POWER_enable (true); + + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_FLAT, 0); + + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (timer_3s < 3000) + { + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (timer_3s == 3000) + { + /* Start calibration */ + MOTION_calibrate (MOTION_CAL_ORIENTATION_FLAT); + + /* Set scope ting state to display calibration status. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_FLAT_CALIBRATION, 0); + + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (MOTION_calibrate_status_get (MOTION_CAL_ORIENTATION_FLAT) == (float) 1.0) + { + uint8_t notify_data = MOTION_CAL_ORIENTATION_FLAT; + + /* Reset 3 second timer for next calibration. */ + timer_3s = 0; + + /* Notify flat cal is done. */ + BLE_notify (BLE_CHAR_LEVEL_CALIBRATE, ¬ify_data, sizeof (notify_data)); + + _fsm_evt_handler (EVENT_SUBCAL_NEXT); + } + break; + + case FSM_BACK_CALIBRATE: + if (timer_3s == 0) + { + /* Enable 5v */ + POWER_enable (true); + + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_BACK, 0); + + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (timer_3s < 3000) + { + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (timer_3s == 3000) + { + /* Start calibration */ + MOTION_calibrate (MOTION_CAL_ORIENTATION_BACK); + + /* Set scope ting state to display calibration status. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_BACK_CALIBRATION, 0); + + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (MOTION_calibrate_status_get (MOTION_CAL_ORIENTATION_BACK) == (float) 1.0) + { + uint8_t notify_data = MOTION_CAL_ORIENTATION_BACK; + + /* Reset 3 second timer for next calibration. */ + timer_3s = 0; + + /* Notify back cal is done. */ + BLE_notify (BLE_CHAR_LEVEL_CALIBRATE, ¬ify_data, sizeof (notify_data)); + + _fsm_evt_handler (EVENT_SUBCAL_NEXT); + } + break; + + case FSM_FRONT_CALIBRATE: + if (timer_3s == 0) + { + /* Enable 5v */ + POWER_enable (true); + + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_FRONT, 0); + + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (timer_3s < 3000) + { + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (timer_3s == 3000) + { + /* Start calibration */ + MOTION_calibrate (MOTION_CAL_ORIENTATION_FRONT); + + /* Set scope ting state to display calibration status. */ + SCOPE_RING_state_set (SCOPE_RING_STATE_FRONT_CALIBRATION, 0); + + timer_3s += FSM_TASK_PERIOD_MS; + } + else if (MOTION_calibrate_status_get (MOTION_CAL_ORIENTATION_FRONT) == (float) 1.0) + { + uint8_t notify_data = MOTION_CAL_ORIENTATION_FRONT; + + /* Reset 3 second timer for next calibration. */ + timer_3s = 0; + + /* Notify front cal is done. */ + BLE_notify (BLE_CHAR_LEVEL_CALIBRATE, ¬ify_data, sizeof (notify_data)); + + _fsm_evt_handler (EVENT_SUBCAL_NEXT); + } + break; + + case FSM_DEBUG: + break; + + default: + /* NOTE: Should never get here. Put back into idle. */ + PRINT (INFO, "ERROR: Invalid FSM state.\r\n"); + _fsm_state_set (FSM_IDLE); + } + } + + _fsm_state_entry = (_fsm_state != prev_state) ? true : false; + + /* Update previous state. */ + prev_state = _fsm_state; + + /* Update elapsed time. */ + _fsm_timeout_cnt += FSM_TASK_PERIOD_MS; + + /* Feed watchdog at end of loop. */ + WDT_feed (); + + k_msleep (FSM_TASK_PERIOD_MS); + } +} + +static void _fsm_config_read (void) +{ + ERRNO_e err; + FSM_config_s cfg; + + err = FS_open_or_create (FSM_FILENAME, &_fsm_file, sizeof (FSM_config_s), FS_FILE_DEPTH_DEFAULT); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_fsm_file, (uint8_t *) &cfg, 0, sizeof (FSM_config_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + /* Write defaults to file. */ + _fsm_config_write (); + return; + } + + /* Validate read file. */ + if (cfg.motion_timeout_ms > FSM_MOTION_TIMEOUT_MAX_MS) + { + /* Write defaults to file. */ + _fsm_config_write (); + return; + } + else if (cfg.auto_brightness > 1) + { + /* Write defaults to file. */ + _fsm_config_write (); + return; + } + else if (cfg.mode > FSM_MODE_COUNT) + { + /* Write defaults to file. */ + _fsm_config_write (); + return; + } + + /* Update fsm configuration to what was read from flash and validated. */ + _fsm_config = cfg; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete (FSM_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old fsm file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (FSM_FILENAME, &_fsm_file, sizeof (FSM_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create fsm configuration file.\r\n"); + } + else + { + PRINT (INFO, "New fsm file created successfully\r\n"); + } + + /* Write default config to file. */ + _fsm_config_write (); + } + else + { + /* Write defaults to file. */ + _fsm_config_write (); + } +} + +static void _fsm_config_write (void) +{ + if (FS_write (_fsm_file, (uint8_t *) &_fsm_config) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write fsm configuration to file.\r\n"); + } +} + +static void _fsm_state_set (FSM_e state) +{ + /* Reset timer every time we change states. */ + _fsm_timeout_cnt = 0; + + _fsm_state = state; +} + +static void _fsm_evt_handler (EVENT_e event) +{ + uint8_t bubble_brightness; + uint8_t pin_brightness; + uint8_t fiber_brightness; + SCOPE_RING_config_s scope_ring_config; + + switch (event) + { + case EVENT_NONE: + break; + + + case EVENT_CHARGER_CONNECTED: + _fsm_state_set (FSM_CHARGER_CONNECTED); + break; + + case EVENT_CHARGER_DISCONNECTED: + if (_fsm_state == FSM_CHARGING) + { + _fsm_state_set (FSM_BATTERY_FULL); + } + else + { + _fsm_state_set (FSM_CHARGER_DISCONNECTED); + } + break; + + case EVENT_BATTERY_FULL: + if (_fsm_state == FSM_CHARGING) + { + _fsm_state_set (FSM_BATTERY_FULL); + } + break; + + case EVENT_GYRO_MOTION: + if (_fsm_state == FSM_LEVELING) + { + _fsm_state_set (FSM_LEVELING); + } + break; + + case EVENT_ACCEL_MOTION: + if (_fsm_state == FSM_IDLE) + { + _fsm_state_set (FSM_LEVELING); + } + break; + + case EVENT_POWER: + /* If power button is pressed while in charging state or 3D sleep, power on and go to leveling. */ + if (_fsm_state == FSM_CHARGING || _fsm_state == FSM_3D_SLEEP) + { + _fsm_state_set (FSM_LEVELING); + } + else + { + _fsm_state_set (FSM_POWER_OFF); + } + break; + + case EVENT_DECREASE_PIN_BRIGHTNESS: + pin_brightness = PIN_brightness_get (); + fiber_brightness = FIBER_brightness_get (); + + /* Pin LEDs are active. */ + if (pin_brightness && !fiber_brightness) + { + if (pin_brightness > 1) + { + /* Decrease brightness step. */ + pin_brightness--; + /* Set new brightness step. */ + PIN_brightness_set (pin_brightness, true); + } + } + /* Fiber LEDs are active. */ + else if (fiber_brightness && !pin_brightness) + { + if (fiber_brightness > 1) + { + /* Decrease brightness step. */ + fiber_brightness--; + /* Set new brightness step. */ + FIBER_brightness_set (fiber_brightness, true); + } + } + /* Shouldn't have both active but in case both are... */ + else + { + if (pin_brightness > 1) + { + /* Decrease brightness step. */ + pin_brightness--; + /* Set new brightness step. */ + PIN_brightness_set (pin_brightness, true); + } + if (fiber_brightness > 1) + { + /* Decrease brightness step. */ + fiber_brightness--; + /* Set new brightness step. */ + FIBER_brightness_set (fiber_brightness, true); + } + } + break; + + case EVENT_INCREASE_PIN_BRIGHTNESS: + pin_brightness = PIN_brightness_get (); + fiber_brightness = FIBER_brightness_get (); + + /* If both are inactive, default to pin. */ + if (!pin_brightness && !fiber_brightness) + { + if (pin_brightness < PIN_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + pin_brightness++; + /* Set new brightness step. */ + PIN_brightness_set (pin_brightness, true); + } + } + /* Pin LEDs are active. */ + else if (pin_brightness && !fiber_brightness) + { + if (pin_brightness < PIN_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + pin_brightness++; + /* Set new brightness step. */ + PIN_brightness_set (pin_brightness, true); + } + } + /* Fiber LEDs are active. */ + else if (fiber_brightness && !pin_brightness) + { + if (fiber_brightness < FIBER_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + fiber_brightness++; + /* Set new brightness step. */ + FIBER_brightness_set (fiber_brightness, true); + } + } + /* Shouldn't have both active but in case both are... */ + else + { + if (pin_brightness < PIN_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + pin_brightness++; + /* Set new brightness step. */ + PIN_brightness_set (pin_brightness, true); + } + if (fiber_brightness < FIBER_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + fiber_brightness++; + /* Set new brightness step. */ + FIBER_brightness_set (fiber_brightness, true); + } + } + break; + + case EVENT_DECREASE_RING_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + /* Do not fully disable. Minimum step of 1. */ + if (scope_ring_config.background_brightness > 1) + { + scope_ring_config.background_brightness--; + + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_brightness, false); + } + /* Do not fully disable. */ + if (scope_ring_config.indicators_brightness > 1) + { + scope_ring_config.indicators_brightness--; + + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, scope_ring_config.indicators_brightness, false); + } + /* Do not fully disable. */ + if (scope_ring_config.level_brightness > 1) + { + scope_ring_config.level_brightness--; + + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, scope_ring_config.level_brightness, false); + } + + /* Saving one will save the rest stored in RAM. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_brightness, true); + break; + + case EVENT_INCREASE_RING_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + /* Check if enabled and can be increased. */ + if (scope_ring_config.background_brightness && scope_ring_config.background_brightness < SCOPE_RING_BRIGHTNESS_STEPS) + { + scope_ring_config.background_brightness++; + + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_brightness, false); + } + /* Check if enabled and can be increased. */ + if (scope_ring_config.indicators_brightness && scope_ring_config.indicators_brightness < SCOPE_RING_BRIGHTNESS_STEPS) + { + scope_ring_config.indicators_brightness++; + + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, scope_ring_config.indicators_brightness, false); + } + /* Check if enabled and can be increased. */ + if (scope_ring_config.level_brightness && scope_ring_config.level_brightness < SCOPE_RING_BRIGHTNESS_STEPS) + { + scope_ring_config.level_brightness++; + + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, scope_ring_config.level_brightness, false); + } + + /* Saving one will save the rest stored in RAM. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_brightness, true); + break; + + case EVENT_CONFIG_ENTER: + /* Set event busy flag for configuration. */ + _fsm_evt_busy = true; + + /* Enable 5V power. */ + POWER_enable (true); + + /* Reset timer. */ + _fsm_timeout_cnt = 0; + + /* Disable shot capture. */ + SHOT_CAPTURE_enable (false); + + switch (_fsm_evt_subconfig) + { + case _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS: + /* Enable bubble and disable all other peripherals. */ + /* We set to default so user can see and know which one they are setting. */ + BUBBLE_brightness_set (BUBBLE_BRIGHTNESS_STEP_DEFAULT, true); + BUBBLE_enable (true); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + break; + + case _FSM_EVT_SUBCONFIG_PIN_BRIGHTNESS: + /* Enable pin and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + /* We set to default so user can see and know which one they are setting. */ + PIN_brightness_set (PIN_BRIGHTNESS_STEP_DEFAULT, true); + PIN_enable (true); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + break; + + case _FSM_EVT_SUBCONFIG_FIBER_BRIGHTNESS: + /* Enable fiber and disable all other peripherals. */ + BUBBLE_enable (false); + /* We set to default so user can see and know which one they are setting. */ + FIBER_brightness_set (FIBER_BRIGHTNESS_STEP_DEFAULT, true); + FIBER_enable (true); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_COLOR: + /* Enable level and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + /* We set to default so user can see and know which one they are setting. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, true); + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_BACKGROUND_COLOR, FSM_CONFIG_BLINK_PERIOD_MS); + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_BRIGHTNESS: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_BACKGROUND_BRIGHTNESS, 0); + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_COLOR: + /* Enable level and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + /* We set to default so user can see and know which one they are setting. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, true); + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_COLOR, FSM_CONFIG_BLINK_PERIOD_MS); + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_BRIGHTNESS: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_BRIGHTNESS, 0); + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_COLOR: + /* Enable level and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + /* We set to default so user can see and know which one they are setting. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, true); + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_INDICATORS_COLOR, FSM_CONFIG_BLINK_PERIOD_MS); + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_BRIGHTNESS: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_INDICATORS_BRIGHTNESS, 0); + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_SENSITIVITY: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_SENSITIVITY, 0); + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_OPTION: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_OPTION, 0); + break; + } + break; + + case EVENT_SUBCONFIG_PREV: + /* Reset timer. */ + _fsm_timeout_cnt = 0; + + switch (_fsm_evt_subconfig) + { + case _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS: + bubble_brightness = BUBBLE_brightness_get (); + + if (bubble_brightness > 0) + { + /* Decrease brightness step. */ + bubble_brightness--; + /* Set new brightness step. */ + BUBBLE_brightness_set (bubble_brightness, true); + /* Need to call enable again to update. */ + BUBBLE_enable (true); + } + break; + + case _FSM_EVT_SUBCONFIG_PIN_BRIGHTNESS: + pin_brightness = PIN_brightness_get (); + + if (pin_brightness > 0) + { + /* Decrease brightness step. */ + pin_brightness--; + /* Set new brightness step. */ + PIN_brightness_set (pin_brightness, true); + /* Need to call enable again to update. */ + PIN_enable (true); + } + break; + + case _FSM_EVT_SUBCONFIG_FIBER_BRIGHTNESS: + fiber_brightness = FIBER_brightness_get (); + + if (fiber_brightness > 0) + { + /* Decrease brightness step. */ + fiber_brightness--; + /* Set new brightness step. */ + FIBER_brightness_set (fiber_brightness, true); + /* Need to call enable again to update. */ + FIBER_enable (true); + } + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_COLOR: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.background_color == RGB_COLOR_RED) + { + /* Loop around. */ + scope_ring_config.background_color = RGB_COLOR_WHITE; + } + else + { + scope_ring_config.background_color--; + } + + /* Set new color. */ + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_color); + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.background_brightness > 0) + { + /* Decrease brightness step. */ + scope_ring_config.background_brightness--; + /* Set new brightness step. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_brightness, true); + } + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_COLOR: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_color == RGB_COLOR_RED) + { + /* Loop around. */ + scope_ring_config.level_color = RGB_COLOR_WHITE; + } + else + { + scope_ring_config.level_color--; + } + + /* Set new color. */ + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, scope_ring_config.level_color); + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_brightness > 0) + { + /* Decrease brightness step. */ + scope_ring_config.level_brightness--; + /* Set new brightness step. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, scope_ring_config.level_brightness, true); + } + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_COLOR: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.indicators_color == RGB_COLOR_RED) + { + /* Loop around. */ + scope_ring_config.indicators_color = RGB_COLOR_WHITE; + } + else + { + scope_ring_config.indicators_color--; + } + + /* Set new color. */ + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, scope_ring_config.indicators_color); + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.indicators_brightness > 0) + { + /* Decrease brightness step. */ + scope_ring_config.indicators_brightness--; + /* Set new brightness step. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, scope_ring_config.indicators_brightness, true); + } + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_SENSITIVITY: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_sensitivity < (float) SCOPE_RING_LEVEL_SENSITIVITY_MAX) + { + /* Decrease sensitivity. */ + scope_ring_config.level_sensitivity+=0.25f; + /* Set new level sensitivity. */ + SCOPE_RING_level_sensitivity_set (scope_ring_config.level_sensitivity); + } + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_OPTION: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_option > SCOPE_RING_LEVEL_OPTION_0) + { + /* Decrease option. */ + scope_ring_config.level_option--; + /* Set new level option. */ + SCOPE_RING_level_option_set (scope_ring_config.level_option); + } + break; + } + break; + + case EVENT_SUBCONFIG_MODE_CHANGE: + /* Reset timer. */ + _fsm_timeout_cnt = 0; + + if (_fsm_evt_subconfig == _FSM_EVT_SUBCONFIG_LEVEL_OPTION) + { + /* Circled around. Exit */ + /* Reset subevent for next configuration. */ + _fsm_evt_subconfig = _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS; + + /* Reset event so we are no longer in subconfig. */ + EVENT_reset (); + + /* Reset event busy flag for configuration. */ + _fsm_evt_busy = false; + + _fsm_state_set (FSM_LEVELING); + + /* Set state entry flag for next FSM entry. */ + _fsm_state_entry = true; + break; + } + else + { + _fsm_evt_subconfig = (_fsm_evt_subconfig_e) (_fsm_evt_subconfig + 1); + } + switch (_fsm_evt_subconfig) + { + case _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS: + /* Enable bubble and disable all other peripherals. */ + /* We set to default so user can see and know which one they are setting. */ + BUBBLE_brightness_set (BUBBLE_BRIGHTNESS_STEP_DEFAULT, true); + BUBBLE_enable (true); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + break; + + case _FSM_EVT_SUBCONFIG_PIN_BRIGHTNESS: + /* Enable pin and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + /* We set to default so user can see and know which one they are setting. */ + PIN_brightness_set (PIN_BRIGHTNESS_STEP_DEFAULT, true); + PIN_enable (true); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + break; + + case _FSM_EVT_SUBCONFIG_FIBER_BRIGHTNESS: + /* Enable fiber and disable all other peripherals. */ + BUBBLE_enable (false); + /* We set to default so user can see and know which one they are setting. */ + FIBER_brightness_set (FIBER_BRIGHTNESS_STEP_DEFAULT, true); + FIBER_enable (true); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_COLOR: + /* Enable level and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + /* We set to default so user can see and know which one they are setting. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, true); + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_BACKGROUND_COLOR, FSM_CONFIG_BLINK_PERIOD_MS); + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_BRIGHTNESS: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_BACKGROUND_BRIGHTNESS, 0); + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_COLOR: + /* Enable level and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + /* We set to default so user can see and know which one they are setting. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, true); + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_COLOR, FSM_CONFIG_BLINK_PERIOD_MS); + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_BRIGHTNESS: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_BRIGHTNESS, 0); + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_COLOR: + /* Enable level and disable all other peripherals. */ + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + /* We set to default so user can see and know which one they are setting. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, true); + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_INDICATORS_COLOR, FSM_CONFIG_BLINK_PERIOD_MS); + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_BRIGHTNESS: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_INDICATORS_BRIGHTNESS, 0); + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_SENSITIVITY: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_SENSITIVITY, 0); + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_OPTION: + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_OPTION, 0); + break; + } + break; + + case EVENT_SUBCONFIG_NEXT: + /* Reset timer. */ + _fsm_timeout_cnt = 0; + + switch (_fsm_evt_subconfig) + { + case _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS: + bubble_brightness = BUBBLE_brightness_get (); + + if (bubble_brightness < BUBBLE_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + bubble_brightness++; + /* Set new brightness step. */ + BUBBLE_brightness_set (bubble_brightness, true); + /* Need to call enable again to update. */ + BUBBLE_enable (true); + } + break; + + case _FSM_EVT_SUBCONFIG_PIN_BRIGHTNESS: + pin_brightness = PIN_brightness_get (); + + if (pin_brightness < PIN_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + pin_brightness++; + /* Set new brightness step. */ + PIN_brightness_set (pin_brightness, true); + /* Need to call enable again to update. */ + PIN_enable (true); + } + break; + + case _FSM_EVT_SUBCONFIG_FIBER_BRIGHTNESS: + fiber_brightness = FIBER_brightness_get (); + + if (fiber_brightness < FIBER_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + fiber_brightness++; + /* Set new brightness step. */ + FIBER_brightness_set (fiber_brightness, true); + /* Need to call enable again to update. */ + FIBER_enable (true); + } + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_COLOR: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.background_color == RGB_COLOR_WHITE) + { + /* Loop around. */ + scope_ring_config.background_color = RGB_COLOR_RED; + } + else + { + scope_ring_config.background_color++; + } + + /* Set new color. */ + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_color); + break; + + case _FSM_EVT_SUBCONFIG_RING_BACKGROUND_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.background_brightness < SCOPE_RING_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + scope_ring_config.background_brightness++; + /* Set new brightness step. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_brightness, true); + } + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_COLOR: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_color == RGB_COLOR_WHITE) + { + /* Loop around. */ + scope_ring_config.level_color = RGB_COLOR_RED; + } + else + { + scope_ring_config.level_color++; + } + + /* Set new color. */ + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, scope_ring_config.level_color); + break; + + case _FSM_EVT_SUBCONFIG_RING_LEVEL_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_brightness < SCOPE_RING_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + scope_ring_config.level_brightness++; + /* Set new brightness step. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, scope_ring_config.level_brightness, true); + } + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_COLOR: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.indicators_color == RGB_COLOR_WHITE) + { + /* Loop around. */ + scope_ring_config.indicators_color = RGB_COLOR_RED; + } + else + { + scope_ring_config.indicators_color++; + } + + /* Set new color. */ + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, scope_ring_config.indicators_color); + break; + + case _FSM_EVT_SUBCONFIG_RING_INDICATORS_BRIGHTNESS: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.indicators_brightness < SCOPE_RING_BRIGHTNESS_STEPS) + { + /* Increase brightness step. */ + scope_ring_config.indicators_brightness++; + /* Set new brightness step. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, scope_ring_config.indicators_brightness, true); + } + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_SENSITIVITY: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_sensitivity > (float) SCOPE_RING_LEVEL_SENSITIVITY_MIN) + { + /* Increase sensitivity. */ + scope_ring_config.level_sensitivity-=0.25f; + /* Set new level sensitivity. */ + SCOPE_RING_level_sensitivity_set (scope_ring_config.level_sensitivity); + } + break; + + case _FSM_EVT_SUBCONFIG_LEVEL_OPTION: + scope_ring_config = SCOPE_RING_config_get (); + + if (scope_ring_config.level_option < (SCOPE_RING_LEVEL_OPTION_COUNT - 1)) + { + /* Increase option. */ + scope_ring_config.level_option++; + /* Set new level option. */ + SCOPE_RING_level_option_set (scope_ring_config.level_option); + } + break; + } + break; + + case EVENT_CONFIG_EXIT: + /* Reset subevent for next configuration. */ + _fsm_evt_subconfig = _FSM_EVT_SUBCONFIG_BUBBLE_BRIGHTNESS; + + /* Reset event busy flag for configuration. */ + _fsm_evt_busy = false; + + /* Set state entry flag for first iteration in FSM. */ + _fsm_state_entry = true; + + _fsm_state_set (FSM_LEVELING); + break; + + case EVENT_AUTO_BRIGHTNESS: + /* Toggle auto brightness setting. */ + FSM_auto_brightness_set (!_fsm_config.auto_brightness); + PRINT (INFO, "auto brightness = %d...\r\n", _fsm_config.auto_brightness); + break; + + case EVENT_BLE_ENABLE: + _fsm_state_set (FSM_BLE_ENABLE); + BLE_enable (true, true); + break; + + case EVENT_BLE_DISABLE: + _fsm_state_set (FSM_BLE_DISABLE); + BLE_enable (false, true); + break; + + case EVENT_BLE_CONNECTED: + break; + + case EVENT_BLE_DISCONNECTED: + break; + + case EVENT_FACTORY_RESET: + // TODO / FIXME : Maybe we don't want to erase the entire flash if we event logs are used. + APP_factory_reset (); + break; + + case EVENT_SUBCAL_START: + /* Set event busy flag for calibration. */ + _fsm_evt_busy = true; + + /* Enable 5V power. */ + POWER_enable (true); + + /* Reset timer. */ + _fsm_timeout_cnt = 0; + + /* Disable shot capture. */ + SHOT_CAPTURE_enable (false); + + /* Default subcal event state is _FSM_EVT_SUBCAL_SET_FLAT. */ + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_FLAT, 0); + break; + + + case EVENT_SUBCAL_NEXT: + /* Reset timer. */ + _fsm_timeout_cnt = 0; + + if (_fsm_evt_subcal == _FSM_EVT_SUBCAL_FRONT) + { + /* Circled around. Exit */ + /* Reset subevent for next calibration. */ + _fsm_evt_subcal = _FSM_EVT_SUBCAL_SET_FLAT; + + /* Reset event so we are no longer in subcal. */ + EVENT_reset (); + + /* Reset event busy flag for configuration. */ + _fsm_evt_busy = false; + + _fsm_state_set (FSM_LEVELING); + break; + } + else + { + _fsm_evt_subcal = (_fsm_evt_subcal_e) (_fsm_evt_subcal + 1); + } + + switch (_fsm_evt_subcal) + { + case _FSM_EVT_SUBCAL_SET_FLAT: + _fsm_evt_busy = true; + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_FLAT, 0); + break; + + case _FSM_EVT_SUBCAL_FLAT: + _fsm_evt_busy = false; + _fsm_state_set (FSM_FLAT_CALIBRATE); + break; + + case _FSM_EVT_SUBCAL_SET_BACK: + _fsm_evt_busy = true; + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_BACK, 0); + break; + + case _FSM_EVT_SUBCAL_BACK: + _fsm_evt_busy = false; + _fsm_state_set (FSM_BACK_CALIBRATE); + break; + + case _FSM_EVT_SUBCAL_SET_FRONT: + _fsm_evt_busy = true; + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_FRONT, 0); + break; + + case _FSM_EVT_SUBCAL_FRONT: + _fsm_evt_busy = false; + _fsm_state_set (FSM_FRONT_CALIBRATE); + break; + } + break; + + case EVENT_SHOT_DETECTED: + if (_fsm_config.mode == FSM_MODE_3D) + { + _fsm_state_set (FSM_3D_SLEEP); + } + else + { + /* If shot is detected, reset timeout counter and motion timer. */ + _fsm_timeout_cnt = 0; + _fsm_motion_timer = 0; + } + break; + + default: + break; + } +} + +static void _fsm_auto_brightness (bool enabled) +{ + static bool was_enabled = false; + static uint8_t bubble_brightness; + static uint8_t fiber_brightness; + static uint8_t pin_brightness; + static SCOPE_RING_config_s scope_ring_config; + uint8_t step; + + if (!enabled && !was_enabled) + { + return; + } + else if (enabled && !was_enabled) + { + /* Preserve settings. */ + bubble_brightness = BUBBLE_brightness_get (); + fiber_brightness = FIBER_brightness_get (); + pin_brightness = PIN_brightness_get (); + scope_ring_config = SCOPE_RING_config_get (); + } + + if (enabled) + { + step = LIGHT_SENSOR_step_get (); + /* Handle return step of 0 (total darkness). */ + if (step == 0) + { + /* Set to minimum so we can still see LEDs. */ + step = 1; + } + + /* Read current config for bubble to see if configured to be on. */ + if (bubble_brightness) + { + /* If configured to be on, we scale to light sensor step reading, but do not save config. */ + BUBBLE_brightness_set (step, false); + } + /* Read current config for fiber to see if configured to be on. */ + if (fiber_brightness) + { + /* If configured to be on, we scale to light sensor step reading, but do not save config. */ + FIBER_brightness_set (step, false); + } + /* Read current config for pin to see if configured to be on. */ + if (pin_brightness) + { + /* If configured to be on, we scale to light sensor step reading, but do not save config. */ + PIN_brightness_set (step, false); + } + /* Read current config for scope ring level to see if configured to be on. */ + if (scope_ring_config.level_brightness) + { + if (step < SCOPE_RING_BRIGHTNESS_STEPS) + { + /* If configured to be on, we scale to scope ring level step reading, but do not save config. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, (step + 1), false); + } + else + { + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, step, false); + } + } + /* Read current config for scope ring indicators to see if configured to be on. */ + if (scope_ring_config.indicators_brightness) + { + if (step < SCOPE_RING_BRIGHTNESS_STEPS) + { + /* If configured to be on, we scale to scope ring indicators step reading, but do not save config. */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, (step + 1), false); + } + else + { + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, step, false); + } + } + /* Read current config for scope ring background to see if configured to be on. */ + if (scope_ring_config.background_brightness) + { + /* If configured to be on, we scale to scope ring background step reading, but do not save config. + * NOTE: We cap brightness for background LEDs to 5 steps, since they are not needed when ambient + * lighting conditions are sufficient. Because of this we will need to scale (divide by 2). */ + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, (uint8_t) ((step / 2) + 1), false); + } + } + else + { + BUBBLE_brightness_set (bubble_brightness, false); + FIBER_brightness_set (fiber_brightness, false); + PIN_brightness_set (pin_brightness, false); + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, scope_ring_config.level_brightness, false); + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, scope_ring_config.indicators_brightness, false); + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, scope_ring_config.background_brightness, false); + } + + was_enabled = enabled; +} + +static void _fsm_low_power_enter (bool wake_on_motion, bool wake_on_charge) +{ + k_sched_lock (); + (void) irq_lock (); + + if (wake_on_motion) + { + /* Set to leveling here because RAM is retained for the state when waking on boot. */ + _fsm_state_set (FSM_LEVELING); + + /* Enable RAM retention for FSM state.*/ + if (MCU_ram_retain ((uint8_t *) &_fsm_state, sizeof (_fsm_state), true) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to configure RAM retention for FSM state.\r\n"); + } + } + else + { + /* Disable RAM retention of FSM state.*/ + if (MCU_ram_retain ((uint8_t *) &_fsm_state, sizeof (_fsm_state), false) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to disable RAM retention for FSM state.\r\n"); + } + } + + /* Turn everything off. */ + SHOT_CAPTURE_enable (false); + BUBBLE_enable (false); + FIBER_enable (false); + PIN_enable (false); + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + + if (SHOT_CAPTURE_save_shot_cnt ()) + { + PRINT (INFO, "Failure to write shot count to flash before OFF.\r\n"); + } + + if (ALS_enable (false)) + { + PRINT (INFO, "Failure to disable ambient light sensor.\r\n"); + } + + if (wake_on_motion) + { + /* Enable interrupts from motion so system can wake. */ + /* Set lowest threshold to lowest threshold for motion/movement detection. + 1 LSB = range / 64 (e.g., for ±2g, 1 LSB ≈ 31.25 mg). */ + if (IMU_motion_int_enable (true, 0x2, 0x1)) + { + PRINT (INFO, "Failure to enable IMU motion interrupt.\r\n"); + } + } + else + { + /* Disable motion interrupt so IMU doesn't wake processor on motion. */ + if (IMU_motion_int_enable (false, 0x1, 0x0)) + { + PRINT (INFO, "Failure to disable IMU motion interrupt.\r\n"); + } + } + + if (IMU_sleep ()) + { + PRINT (INFO, "Failure to put IMU in sleep mode.\r\n"); + } + if (NVM_EXT_power_down ()) + { + PRINT (INFO, "Failure to power down flash.\r\n"); + } + if (POWER_enable (false)) + { + PRINT (INFO, "Failure to disable power.\r\n"); + } + + if (BATTERY_wake_on_charge (wake_on_charge)) + { + PRINT (INFO, "Failure to configure battery wake on charge.\r\n"); + } + + /* Reconfigure GPIO interrupt for center button to be used to wake from sleep. */ + if (BUTTON_wake_on_press (BUTTON_CENTER)) + { + PRINT (INFO, "Failure to configure GPIO center button as interrupt to wake from sleep. %d\r\n"); + } + + /* Put mcu to sleep. */ + MCU_sleep (); +} + +static void _fsm_battery_read (void) +{ + float voltage; + + if (BATTERY_voltage_get (&voltage) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read battery voltage.\r\n"); + } + + _fsm_batt_life = BATTERY_level_calculate (voltage); + + /* Set battery status for feedback on LED ring. */ + SCOPE_RING_battery_indicator_set (_fsm_batt_life); + + /* Notify BLE of battery status. */ + BLE_notify (BLE_CHAR_BATTERY_LEVEL, &_fsm_batt_life, sizeof (_fsm_batt_life)); +} diff --git a/src/app/fsm.h b/src/app/fsm.h new file mode 100644 index 0000000..8598b9d --- /dev/null +++ b/src/app/fsm.h @@ -0,0 +1,139 @@ +/** + * @file fsm.h + * @brief Finite State Machine + * @author Kenny Tran + * @date July 13 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef FSM_H_ +#define FSM_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "motion.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define FSM_TASK_PERIOD_MS (100) +#define FSM_STACK_SIZE (2048) +#define FSM_THREAD_PRIORITY (7) + +#define FSM_LOW_BATTERY_BLINK_PERIOD_MS (200) + +#define FSM_CONFIG_BLINK_PERIOD_MS (1000) +#define FSM_CONFIG_TIMEOUT_PERIOD_MS (300000) /* Timeout if stuck in config for 5 minutes. */ + +#define FSM_MOTION_TIMEOUT_PERIOD_MS (10000) +#define FSM_MOTION_TIMEOUT_MAX_MS (360000) /* 6 minutes. */ + +#define FSM_BUTTON_PRESS_DURATION_SLEEP_MS (3000) + +#define FSM_IDLE_TIMEOUT_PERIOD_MS (600000) /* If idle for 10 minutes, power down. */ + +#define FSM_CHARGER_CONNECTED_PERIOD_MS (400) +#define FSM_CHARGING_BLINK_PERIOD_MS (1000) + +#define FSM_FILENAME ("fsm") + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct FSM_config +{ + uint32_t motion_timeout_ms; + uint8_t auto_brightness : 8; + uint8_t mode : 2; + uint8_t reserved_0 : 6; + uint8_t reserved_1 : 8; + uint8_t reserved_2 : 8; +} __attribute__((packed, aligned(1))) FSM_config_s; + +typedef enum FSM_MODE +{ + FSM_MODE_TARGET = 0, + FSM_MODE_3D, + + FSM_MODE_COUNT, + FSM_MODE_INVALID = FSM_MODE_COUNT +} FSM_MODE_e; + +typedef enum FSM +{ + FSM_INIT = 0, + FSM_CHARGING, + FSM_LOW_BATTERY, + FSM_POWER_ON, + FSM_IDLE, + FSM_SLEEP, + FSM_3D_SLEEP, + FSM_CHARGER_CONNECTED, + FSM_CHARGER_DISCONNECTED, + FSM_LEVELING, + FSM_POWER_OFF, + FSM_OFF, + FSM_BATTERY_FULL, + FSM_BLE_ENABLE, + FSM_BLE_DISABLE, + + FSM_RAINBOW, + + FSM_FLAT_CALIBRATE, + FSM_BACK_CALIBRATE, + FSM_FRONT_CALIBRATE, + + FSM_DEBUG = 255 +} FSM_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void FSM_init (void); + +void FSM_level_calibrate (void); + +void FSM_task_suspend (void); + +void FSM_task_resume (void); + +void FSM_task_install (void); + +void FSM_timout_set (uint32_t ms); + +uint32_t FSM_timout_get (void); + +void FSM_auto_brightness_set (bool enabled); + +bool FSM_auto_brightness_get (void); + +void FSM_mode_set (FSM_MODE_e mode); + +FSM_MODE_e FSM_mode_get (void); + +void FSM_app_config_set (bool val); + +bool FSM_app_config_get (void); + +float FSM_battery_life_get (void); + +void FSM_cli_cmd (int32_t argc, char **argv); + + +#endif /* FSM_H_ */ diff --git a/src/app/light_sensor.c b/src/app/light_sensor.c new file mode 100644 index 0000000..a3460d5 --- /dev/null +++ b/src/app/light_sensor.c @@ -0,0 +1,210 @@ +/** + * @file light_sensor.c + * @brief Light Sensor Module + * @author Kenny Tran + * @date Mar 08 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../bsp/als.h" + +#include "light_sensor.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _LIGHT_SENSOR_BRIGHTNESS_MAX (65535) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static float _light_sensor_brightness_table[LIGHT_SENSOR_BRIGHTNESS_STEPS + 1]; +static uint8_t _light_sensor_step; +static ALS_data_s _light_sensor_data = (ALS_data_s) { + .visible = 0, + .infrared = 0 +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _light_sensor_brightness_gamma_populate (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void LIGHT_SENSOR_init (void) +{ + PRINT (INFO, "Initializing LIGHT_SENSOR...\r\n"); + + if (ALS_init () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize ALS\r\n"); + } + + /* Set gain to 4 */ + if (ALS_gain_set (ALS_GAIN_4) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to set ALS gain\r\n"); + } + + /* Set integration time to 100 ms */ + if (ALS_int_time_set (ALS_INT_TIME_100MS) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to set ALS integration time\r\n"); + } + + /* Set measurement rate to 500 ms */ + if (ALS_measure_rate_set (ALS_MEASURE_RATE_100MS) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to set ALS measurement rate\r\n"); + } + + /* TODO: configure gain and measurement time. Need to test heuristically to determine. */ + + /* Populate light sensor brightness gamma table. */ + _light_sensor_brightness_gamma_populate (); + + /* Enable LTR329 for each reading. */ + if (ALS_enable (true) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to enable ALS\r\n"); + } +} + +uint8_t LIGHT_SENSOR_step_get (void) +{ + uint8_t i; + bool new_data = false; + ALS_data_s reading; + uint32_t retries = 0; + + // /* Enable LTR329 for each reading. */ + // if (ALS_enable (true) != ERRNO_SUCCESS) + // { + // PRINT (ERROR, "Failure to enable ALS\r\n"); + // } + + /* Wait for new reading. */ + do { + if (ALS_new_data_available (&new_data) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read ALS status"); + } + else + { + if (new_data) + { + break; + } + } + retries++; + } while (retries < LIGHT_SENSOR_READ_TRIES); + + if (retries >= LIGHT_SENSOR_READ_TRIES) + { + return (LIGHT_SENSOR_BRIGHTNESS_STEPS / 2); + } + + /* Grab reading. */ + if (ALS_read (&reading) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read ALS data"); + } + + /* Set local vars. */ + _light_sensor_data.visible = reading.visible; + _light_sensor_data.infrared = reading.infrared; + + /* Compare with companding table to get step. */ + for (i = 0; i < LIGHT_SENSOR_BRIGHTNESS_STEPS; i++) + { + /* NOTE: Currently only looking at visible. */ + if (_light_sensor_data.visible > _light_sensor_brightness_table[i]) + { + /* +1 since we want interface range 1-10. */ + _light_sensor_step = i + 1; + } + } + + // /* Disable LTR329 after reading to conserve power. */ + // if (ALS_enable (false) != ERRNO_SUCCESS) + // { + // PRINT (ERROR, "Failure to disable ALS\r\n"); + // } + + return _light_sensor_step; +} + +void LIGHT_SENSOR_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + /* Perform a reading. */ + LIGHT_SENSOR_step_get (); + + PRINT (INFO, "LIGHT SENSOR\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Visible + IR : %d\r\n", (_light_sensor_data.visible + _light_sensor_data.infrared)); + PRINT (INFO, "Visible : %d\r\n", _light_sensor_data.visible); + PRINT (INFO, "IR : %d\r\n", _light_sensor_data.infrared); + PRINT (INFO, "Step : %d\r\n", _light_sensor_step); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _light_sensor_brightness_gamma_populate (void) +{ + const uint8_t n_zeroes = 1; + const uint8_t temp_steps = LIGHT_SENSOR_BRIGHTNESS_STEPS + n_zeroes; + float temp_arr[temp_steps + 1]; /* +1 to hold 0th value. */ + uint32_t i; + + /* Using a gamma correction factor of 3.5, the beginning of the curve are zeroes. + Therefore, we (heuristically) calculate with extra steps and count zeroes (n_zeroes) and + fill lookup table starting after zeroes. */ + for (i = 0; i <= temp_steps; i++) + { + temp_arr[i] = (float) pow (((float) i / (float) temp_steps), LIGHT_SENSOR_BRIGHTNESS_GAMMA_CF) * + (float) _LIGHT_SENSOR_BRIGHTNESS_MAX; + } + + /* We start index at 1 to reserve index 0 for 0 %. */ + _light_sensor_brightness_table[0] = 0.0; + + /* Fill lookup table with the non-zeroes. */ + for (i = 1; i <= LIGHT_SENSOR_BRIGHTNESS_STEPS; i++) + { + _light_sensor_brightness_table[i] = temp_arr[i + n_zeroes]; + } +} diff --git a/src/app/light_sensor.h b/src/app/light_sensor.h new file mode 100644 index 0000000..635076c --- /dev/null +++ b/src/app/light_sensor.h @@ -0,0 +1,52 @@ +/** + * @file light_sensor.h + * @brief Light Sensor Module + * @author Kenny Tran + * @date Mar 08 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef LIGHT_SENSOR_H_ +#define LIGHT_SENSOR_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define LIGHT_SENSOR_BRIGHTNESS_STEPS (10) +#define LIGHT_SENSOR_BRIGHTNESS_GAMMA_CF (4) /* Gamma correction factor */ + +#define LIGHT_SENSOR_READ_TRIES (10) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void LIGHT_SENSOR_init (void); + +uint8_t LIGHT_SENSOR_step_get (void); + +void LIGHT_SENSOR_cli_cmd (int32_t argc, char **argv); + + +#endif /* LIGHT_SENSOR_H_ */ diff --git a/src/app/log.c b/src/app/log.c new file mode 100644 index 0000000..2a0b0f9 --- /dev/null +++ b/src/app/log.c @@ -0,0 +1,192 @@ +/** + * @file log.c + * @brief Log Module + * @author Kenny Tran + * @date Jan 29 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include + +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../bsp/mcu.h" + +#include "../app/app.h" + +#include "log.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct k_work_delayable _log_delayed_work_s; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static FS_file_t _log_file; +static _log_delayed_work_s _log_delayed_work; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _log_open_file (void); +static void _log_mcu_reset (_log_delayed_work_s dly_work); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void LOG_init (void) +{ + PRINT (INFO, "Initializing LOG...\r\n"); + + /* Initialize filesystem. */ + if (FS_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize filesystem.\r\n"); + return; + } + + _log_open_file (); + + /* Initialize delayed function to reset MCU so prints still come through, + since immediate reset misses prints. */ + k_work_init_delayable (&_log_delayed_work, _log_mcu_reset); +} + +void LOG_msg (char *msg) +{ + // LOG_s log_entry; + + // /* Set power on count. */ + // log_entry.power_cnt = APP_info_get ().power_cnt; + + // /* Clear buffer. */ + // memset (log_entry.msg, 0, LOG_MAX_MSG_LEN); + + // /* Copy message. */ + // strcpy (log_entry.msg, msg); + + // if (FS_write (_log_file, (uint8_t *) &log_entry) != ERRNO_SUCCESS) + // { + // PRINT (ERROR, "Failed to log message to file.\r\n"); + // } + + // /* Schedule MCU reset. */ + // k_work_schedule (&_log_delayed_work, K_MSEC(500)); +} + +LOG_s LOG_last_get (void) +{ + ERRNO_e err; + LOG_s log_entry; + + /* Read file. */ + err = FS_read (_log_file, (uint8_t *) &log_entry, 0, sizeof (LOG_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + log_entry.power_cnt = 0; + /* Clear buffer. */ + memset (log_entry.msg, 0, LOG_MAX_MSG_LEN); + } + + return log_entry; +} + +void LOG_cli_cmd (int32_t argc, char **argv) +{ + LOG_s log_entry = LOG_last_get (); + + if (argc == 0) + { + + PRINT (INFO, "LOG\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Power-on Count : %d\r\n", log_entry.power_cnt); + PRINT (INFO, "Message : %s\r\n", log_entry.msg); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _log_open_file (void) +{ + ERRNO_e err; + LOG_s log_entry; + + err = FS_open_or_create (LOG_FILENAME, &_log_file, sizeof (LOG_s), LOG_ENTRY_DEPTH); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_log_file, (uint8_t *) &log_entry, 0, sizeof (LOG_s)); + + /* If file not valid, log initial entry. */ + if (err != ERRNO_SUCCESS) + { + LOG_msg ("Initial entry"); + } + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete (LOG_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old log file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (LOG_FILENAME, &_log_file, sizeof (LOG_s), LOG_ENTRY_DEPTH); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create log file.\r\n"); + } + else + { + PRINT (INFO, "New log file created successfully\r\n"); + } + + /* Log initial entry. */ + LOG_msg ("Initial entry"); + } +} + +static void _log_mcu_reset (_log_delayed_work_s dly_work) +{ + MCU_reset (true); +} diff --git a/src/app/log.h b/src/app/log.h new file mode 100644 index 0000000..606537f --- /dev/null +++ b/src/app/log.h @@ -0,0 +1,59 @@ +/** + * @file log.h + * @brief Log Module + * @author Kenny Tran + * @date Jan 29 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef LOG_H_ +#define LOG_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define LOG_MAX_MSG_LEN (64) +#define LOG_ENTRY_DEPTH (32) + +#define LOG_FILENAME ("log") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct LOG +{ + uint32_t power_cnt; + uint8_t msg[LOG_MAX_MSG_LEN]; +} __attribute__((packed, aligned(1))) LOG_s; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void LOG_init (void); + +void LOG_msg (char *msg); + +LOG_s LOG_last_get (void); + +void LOG_cli_cmd (int32_t argc, char **argv); + + +#endif /* LOG_H_ */ diff --git a/src/app/motion.c b/src/app/motion.c new file mode 100644 index 0000000..09dc00d --- /dev/null +++ b/src/app/motion.c @@ -0,0 +1,664 @@ +/** + * @file motion.c + * @brief Motion Module + * @author Kenny Tran + * @date Jan 27 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include + +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../bsp/imu.h" +#include "../bsp/wdt.h" + +#include "../wrappers/gpio.h" + +#include "motion.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _MOTION_PI ((float) 3.14159265359) +/* Exponential moving average filter constant (0.0 to 1.0, higher = more smoothing). + * Also used as scalar for sensor fusion. */ +#define _MOTION_ALPHA ((float) 0.5) + +#define _MOTION_GYRO_MAG_WINDOW_SIZE (10) + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +K_THREAD_STACK_DEFINE(_motion_stack, MOTION_STACK_SIZE); + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static struct k_thread _motion_thread; +static FS_file_t _motion_file; +static MOTION_config_s _motion_config = { + .accel_cal = { + .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 1.0, .y = 1.0, .z = 1.0 } + }, + .gyro_cal = { + .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 1.0, .y = 1.0, .z = 1.0 } + }, + .angular_threshold = MOTION_ANG_THRESHOLD_DEFAULT, + .back_cross_axis_factor = 0.0, + .front_cross_axis_factor = 0.0, +}; +static MOTION_vector_s _motion_orientation = { .x = 0.0, .y = 0.0, .z = 0.0 }; +static IMU_data_s _motion_accel_data; +static IMU_data_s _motion_gyro_data; +static float _motion_dt; +static uint32_t _motion_recal_readings = 0; +static bool _motion_gyro_recalibrate = false; +static bool _motion_imu_detected = false; +static MOTION_calibration_data_s _motion_recal_gyro = { .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 1.0, .y = 1.0, .z = 1.0 } }; + +static uint32_t _motion_cal_readings[MOTION_CAL_ORIENTATION_COUNT] = { 0, 0, 0 }; +static bool _motion_cal[MOTION_CAL_ORIENTATION_COUNT] = { false, false, false }; + +/* Variables to dump measured X and Y angles to terminal. */ +static uint32_t _motion_timeout_cnt = 0; +static bool _motion_data_dump = false; +static uint32_t _motion_data_dump_ms = 0; + +/* Variables to to calculate angular magnitude for motion from gyro. */ +static float _motion_angular_mag = 0.0; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _motion_task (void *p1, void *p2, void *p3); +static void _motion_config_read (void); +static void _motion_config_write (void); +static void _motion_data_process (MOTION_vector_s accel_raw, MOTION_vector_s gyro_raw, float dt); +static MOTION_vector_s _motion_apply_cal (MOTION_vector_s raw, MOTION_calibration_data_s cal); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void MOTION_init (void) +{ + PRINT (INFO, "Initializing MOTION...\r\n"); + + /* Initialize IMU. */ + if (IMU_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize IMU.\r\n"); + } + + /* Set delta time, / by 1000.0 to get seconds. */ + _motion_dt = (float) MOTION_TASK_PERIOD_MS / (float) 1000.0; + + /* Get configuration settings. */ + _motion_config_read (); +} + +bool MOTION_linear_detected (void) +{ + bool detected = _motion_imu_detected; + + /* Reset motion detected flag. */ + _motion_imu_detected = false; + + return detected; +} + +bool MOTION_angular_detected (void) +{ + static float mag_history[_MOTION_GYRO_MAG_WINDOW_SIZE] = { 0 }; + static float sum = 0.0f; /* Running sum for average. */ + static float sum_sq = 0.0f; /* Running sum of squares for variance. */ + static uint8_t idx = 0; + + /* Remove old value from sum. */ + float old_mag = mag_history[idx]; + sum -= old_mag; + sum_sq -= (old_mag * old_mag); + + /* Add new value to sums */ + mag_history[idx] = _motion_angular_mag; + sum += _motion_angular_mag; + sum_sq += (_motion_angular_mag * _motion_angular_mag); + + /* Update index */ + idx = (idx + 1) % _MOTION_GYRO_MAG_WINDOW_SIZE; + + /* Calculate variance: Var(X) = E[X²] - E[X]² */ + float mean = sum / _MOTION_GYRO_MAG_WINDOW_SIZE; + float mean_sq = sum_sq / _MOTION_GYRO_MAG_WINDOW_SIZE; + float variance = mean_sq - (mean * mean); + + /* Detect motion when variance exceeds threshold */ + return (variance >= _motion_config.angular_threshold); +} + +void MOTION_angular_threshold_set (float variance) +{ + _motion_config.angular_threshold = variance; + + /* Write new threshold to flash. */ + _motion_config_write (); +} + +float MOTION_angle_get (MOTION_axis_e axis) +{ + float comp; + + switch (axis) + { + case MOTION_AXIS_X: + /* y relative to IMU, x relative to board */ + return (_motion_orientation.y); + + case MOTION_AXIS_Y: + /* x relative to IMU, y relative to board */ + if (_motion_orientation.y > 0) + { + comp = (_motion_orientation.y * _motion_config.back_cross_axis_factor); + } + else + { + comp = (_motion_orientation.y * _motion_config.front_cross_axis_factor); + } + + return (_motion_orientation.x - comp); + + default: + PRINT (ERROR, "Invalid axis.\r\n"); + return 1234.567; + } +} + +void MOTION_calibrate (MOTION_cal_orientation_e orientation) +{ + if (orientation >= MOTION_CAL_ORIENTATION_INVALID) + { + PRINT (ERROR, "Invalid calibration orientation.\r\n"); + return; + } + + /* Reset motion calibration readings. */ + _motion_cal[(uint32_t) orientation] = true; + _motion_cal_readings[(uint32_t) orientation] = 0; +} + +float MOTION_calibrate_status_get (MOTION_cal_orientation_e orientation) +{ + if (orientation >= MOTION_CAL_ORIENTATION_INVALID) + { + PRINT (ERROR, "Invalid calibration orientation.\r\n"); + return 1.0; + } + + return ((float) _motion_cal_readings[(uint32_t) orientation] / (float) MOTION_CAL_READINGS); +} + +void MOTION_gyro_recalibrate (void) +{ + /* Reset motion recalibration readings. */ + _motion_recal_readings = 0; + + _motion_recal_gyro = (MOTION_calibration_data_s) { .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 1.0, .y = 1.0, .z = 1.0 } }; + + _motion_gyro_recalibrate = true; +} + +void MOTION_gyro_recalibrate_abort (void) +{ + _motion_gyro_recalibrate = false; +} + +float MOTION_gyro_recalibrate_status_get (void) +{ + return ((float) _motion_recal_readings / (float) MOTION_GYRO_RECAL_READINGS); +} + +MOTION_config_s *MOTION_config_get (void) +{ + return &_motion_config; +} + +void MOTION_config_set (MOTION_config_s cfg) +{ + _motion_config = cfg; + + _motion_config_write (); +} + +void MOTION_task_suspend (void) +{ + k_thread_suspend (&_motion_thread); +} + +void MOTION_task_resume (void) +{ + k_thread_resume (&_motion_thread); +} + +void MOTION_task_install (void) +{ + static k_tid_t id; + + id = k_thread_create (&_motion_thread, + _motion_stack, + MOTION_STACK_SIZE, + _motion_task, + NULL, + NULL, + NULL, + MOTION_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&_motion_thread, "MOTION_task"); +} + +void MOTION_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + float x_adjusted = MOTION_angle_get (MOTION_AXIS_X); + float y_adjusted = MOTION_angle_get (MOTION_AXIS_Y); + + PRINT (INFO, "MOTION\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "X (Corrected) : %.3f\r\n", (double) x_adjusted); + PRINT (INFO, "Y (Corrected) : %.3f\r\n", (double) y_adjusted); + PRINT (INFO, "Angular Threshold : %.3f\r\n", (double) _motion_config.angular_threshold); + PRINT (INFO, "Back Cal Factor : %.3f\r\n", (double) _motion_config.back_cross_axis_factor); + PRINT (INFO, "Front Cal Factor : %.3f\r\n", (double) _motion_config.front_cross_axis_factor); + PRINT (INFO, "Calibration Data\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Accelerometer\r\n"); + PRINT (INFO, " Offset X : %.3f\r\n", (double) _motion_config.accel_cal.offset.x); + PRINT (INFO, " Offset Y : %.3f\r\n", (double) _motion_config.accel_cal.offset.y); + PRINT (INFO, " Offset Z : %.3f\r\n", (double) _motion_config.accel_cal.offset.z); + PRINT (INFO, " Scalar X : %.3f\r\n", (double) _motion_config.accel_cal.scalar.x); + PRINT (INFO, " Scalar Y : %.3f\r\n", (double) _motion_config.accel_cal.scalar.y); + PRINT (INFO, " Scalar Z : %.3f\r\n", (double) _motion_config.accel_cal.scalar.z); + PRINT (INFO, "Gyroscope\r\n"); + PRINT (INFO, " Offset X : %.3f\r\n", (double) _motion_config.gyro_cal.offset.x); + PRINT (INFO, " Offset Y : %.3f\r\n", (double) _motion_config.gyro_cal.offset.y); + PRINT (INFO, " Offset Z : %.3f\r\n", (double) _motion_config.gyro_cal.offset.z); + PRINT (INFO, " Scalar X : %.3f\r\n", (double) _motion_config.gyro_cal.scalar.x); + PRINT (INFO, " Scalar Y : %.3f\r\n", (double) _motion_config.gyro_cal.scalar.y); + PRINT (INFO, " Scalar Z : %.3f\r\n", (double) _motion_config.gyro_cal.scalar.z); + } + else if (argc == 2) + { + if (!strcmp (argv[0], "--cal")) + { + MOTION_cal_orientation_e orientation = (MOTION_cal_orientation_e) strtoul (argv[1], NULL, 0); + + PRINT (INFO, "Calibrating with %d readings...\r\n", MOTION_CAL_READINGS); + + MOTION_calibrate (orientation); + } + else if (!strcmp (argv[0], "--dump")) + { + _motion_data_dump_ms = strtoul (argv[1], NULL, 0); + _motion_data_dump = true; + } + else if (!strcmp (argv[0], "--theta")) + { + float deg = strtof (argv[1], NULL); + + MOTION_angular_threshold_set (deg); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: motion [OPTION...]\r\n"); + PRINT (INFO, " --cal Calibrates IMU (offsets).\r\n"); + PRINT (INFO, " <%%d> 0 = flat, 1, = back, 2 = front.\r\n"); + PRINT (INFO, " --dump Dumps calculated angles to terminal.\r\n"); + PRINT (INFO, " <%%d> Duration in milliseconds.\r\n"); + PRINT (INFO, " --theta Sets new angular threshold.\r\n"); + PRINT (INFO, " <%%f> Value to set.\r\n"); + } +} + +void MOTION_detected_flag_set (void) +{ + _motion_imu_detected = true; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _motion_task (void *p1, void *p2, void *p3) +{ + while (1) + { + /* Read accelerometer data. */ + if (IMU_xl_read (&_motion_accel_data) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read IMU accel data.\r\n"); + } + + /* Read gyroscope data. */ + if (IMU_gyro_read (&_motion_gyro_data) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read IMU gyro data.\r\n"); + } + + _motion_data_process ((MOTION_vector_s) _motion_accel_data, (MOTION_vector_s) _motion_gyro_data, _motion_dt); + + if (_motion_cal[MOTION_CAL_ORIENTATION_FLAT]) + { + static MOTION_calibration_data_s accel = { .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 0.0, .y = 0.0, .z = 0.0 } }; + static MOTION_calibration_data_s gyro = { .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 0.0, .y = 0.0, .z = 0.0 } }; + + accel.offset.x += _motion_accel_data.x; + accel.offset.y += _motion_accel_data.y; + accel.offset.z += _motion_accel_data.z; + + gyro.offset.x += _motion_gyro_data.x; + gyro.offset.y += _motion_gyro_data.y; + gyro.offset.z += _motion_gyro_data.z; + + if (++_motion_cal_readings[MOTION_CAL_ORIENTATION_FLAT] == MOTION_CAL_READINGS) + { + _motion_config.accel_cal.offset.x = accel.offset.x / (float) MOTION_CAL_READINGS; + _motion_config.accel_cal.offset.y = accel.offset.y / (float) MOTION_CAL_READINGS; + /* Add 1G (gravity) for z, since IMU is pointing down. */ + _motion_config.accel_cal.offset.z = (accel.offset.z / (float) MOTION_CAL_READINGS) + 1.0f; + + _motion_config.gyro_cal.offset.x = gyro.offset.x / (float) MOTION_CAL_READINGS; + _motion_config.gyro_cal.offset.y = gyro.offset.y / (float) MOTION_CAL_READINGS; + _motion_config.gyro_cal.offset.z = gyro.offset.z / (float) MOTION_CAL_READINGS; + + /* Reset static values for next calibration. */ + _motion_cal[MOTION_CAL_ORIENTATION_FLAT] = false; + + PRINT (INFO, "\r\nCalibration complete!\r\n"); + + /* Write new settings to file. */ + _motion_config_write (); + + /* Reset static values in case calibration is ran again.*/ + accel = (MOTION_calibration_data_s) { .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 0.0, .y = 0.0, .z = 0.0 } }; + gyro = (MOTION_calibration_data_s) { .offset = { .x = 0.0, .y = 0.0, .z = 0.0 }, + .scalar = { .x = 0.0, .y = 0.0, .z = 0.0 } }; + } + else if (_motion_cal_readings[MOTION_CAL_ORIENTATION_FLAT] % 10 == 0) + { + PRINT (INFO, ".\r\n"); + } + } + else if (_motion_cal[MOTION_CAL_ORIENTATION_BACK]) + { + static float x_sum = 0.0; + static float y_sum = 0.0; + + x_sum += _motion_orientation.x; + y_sum += _motion_orientation.y; + + if (++_motion_cal_readings[MOTION_CAL_ORIENTATION_BACK] == MOTION_CAL_READINGS) + { + x_sum /= (float) MOTION_CAL_READINGS; + y_sum /= (float) MOTION_CAL_READINGS; + + _motion_config.back_cross_axis_factor = x_sum / y_sum; + + /* Reset static values for next calibration. */ + _motion_cal[MOTION_CAL_ORIENTATION_BACK] = false; + + PRINT (INFO, "\r\nCalibration complete!\r\n"); + + /* Write new settings to file. */ + _motion_config_write (); + + /* Reset sums for next calibration. */ + x_sum = 0.0; + y_sum = 0.0; + } + else if (_motion_cal_readings[MOTION_CAL_ORIENTATION_BACK] % 10 == 0) + { + PRINT (INFO, ".\r\n"); + } + } + else if (_motion_cal[MOTION_CAL_ORIENTATION_FRONT]) + { + static float x_sum = 0.0; + static float y_sum = 0.0; + + x_sum += _motion_orientation.x; + y_sum += _motion_orientation.y; + + if (++_motion_cal_readings[MOTION_CAL_ORIENTATION_FRONT] == MOTION_CAL_READINGS) + { + x_sum /= (float) MOTION_CAL_READINGS; + y_sum /= (float) MOTION_CAL_READINGS; + + _motion_config.front_cross_axis_factor = x_sum / y_sum; + + /* Reset static values for next calibration. */ + _motion_cal[MOTION_CAL_ORIENTATION_FRONT] = false; + + PRINT (INFO, "\r\nCalibration complete!\r\n"); + + /* Write new settings to file. */ + _motion_config_write (); + + /* Reset sums for next calibration. */ + x_sum = 0.0; + y_sum = 0.0; + } + else if (_motion_cal_readings[MOTION_CAL_ORIENTATION_FRONT] % 10 == 0) + { + PRINT (INFO, ".\r\n"); + } + } + else if (_motion_gyro_recalibrate) + { + _motion_recal_gyro.offset.x += _motion_gyro_data.x; + _motion_recal_gyro.offset.y += _motion_gyro_data.y; + _motion_recal_gyro.offset.z += _motion_gyro_data.z; + + if (++_motion_recal_readings == MOTION_GYRO_RECAL_READINGS) + { + _motion_config.gyro_cal.offset.x = _motion_recal_gyro.offset.x / (float) MOTION_GYRO_RECAL_READINGS; + _motion_config.gyro_cal.offset.y = _motion_recal_gyro.offset.y / (float) MOTION_GYRO_RECAL_READINGS; + _motion_config.gyro_cal.offset.z = _motion_recal_gyro.offset.z / (float) MOTION_GYRO_RECAL_READINGS; + + /* Reset static values for next calibration. */ + _motion_gyro_recalibrate = false; + + /* Write new settings to file. */ + _motion_config_write (); + } + } + + if (_motion_data_dump) + { + if (_motion_timeout_cnt >= _motion_data_dump_ms) + { + /* Reset timeout count. */ + _motion_timeout_cnt = 0; + /* Reset data dump flag. */ + _motion_data_dump = false; + } + else + { + /* Increment timeout counter. */ + _motion_timeout_cnt += MOTION_TASK_PERIOD_MS; + } + + // PRINT (INFO, "x = %.3f, y = %.3f\r\n", (double) _motion_orientation.y, + // (double) _motion_orientation.x); + } + + /* Feed watchdog at end of loop. */ + WDT_feed (); + + k_msleep (MOTION_TASK_PERIOD_MS); + } +} + +static void _motion_config_read (void) +{ + ERRNO_e err; + MOTION_config_s cfg; + + err = FS_open_or_create (MOTION_FILENAME, &_motion_file, sizeof (MOTION_config_s), (FS_FILE_DEPTH_DEFAULT * 8)); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_motion_file, (uint8_t *) &cfg, 0, sizeof (MOTION_config_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + /* Write defaults to file. */ + _motion_config_write (); + return; + } + + /* Validate read file. */ + // FIXME: find better way to validate + if (isnan (cfg.accel_cal.offset.x) || isnan (cfg.accel_cal.offset.y) || isnan (cfg.accel_cal.offset.z)) + { + cfg.accel_cal.offset = (MOTION_vector_s) { .x = 0.0, .y = 0.0, .z = 0.0 }; + } + if (isnan (cfg.accel_cal.scalar.x) || isnan (cfg.accel_cal.scalar.y) || isnan (cfg.accel_cal.scalar.z)) + { + cfg.accel_cal.scalar = (MOTION_vector_s) { .x = 1.0, .y = 1.0, .z = 1.0 }; + } + if (isnan (cfg.gyro_cal.offset.x) || isnan (cfg.gyro_cal.offset.y) || isnan (cfg.gyro_cal.offset.z)) + { + cfg.gyro_cal.offset = (MOTION_vector_s) { .x = 0.0, .y = 0.0, .z = 0.0 }; + } + if (isnan (cfg.gyro_cal.scalar.x) || isnan (cfg.gyro_cal.scalar.y) || isnan (cfg.gyro_cal.scalar.z)) + { + cfg.gyro_cal.scalar = (MOTION_vector_s) { .x = 1.0, .y = 1.0, .z = 1.0 }; + } + if (isnan (cfg.angular_threshold)) + { + cfg.angular_threshold = MOTION_ANG_THRESHOLD_DEFAULT; + } + + /* Update motion configuration to what was read from flash and validated. */ + _motion_config = cfg; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete (MOTION_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old motion file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (MOTION_FILENAME, &_motion_file, sizeof (MOTION_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create motion configuration file.\r\n"); + } + else + { + PRINT (INFO, "New motion file created successfully\r\n"); + } + + /* Write default config to file. */ + _motion_config_write (); + } + else + { + /* Write defaults to file. */ + _motion_config_write (); + } +} + +static void _motion_config_write (void) +{ + if (FS_write (_motion_file, (uint8_t *) &_motion_config) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write motion configuration to file.\r\n"); + } +} + +static void _motion_data_process (MOTION_vector_s accel_raw, MOTION_vector_s gyro_raw, float dt) +{ + /* Apply calibration parameters to raw data. */ + MOTION_vector_s accel_corrected = _motion_apply_cal (accel_raw, _motion_config.accel_cal); + MOTION_vector_s gyro_corrected = _motion_apply_cal (gyro_raw, _motion_config.gyro_cal); + + if (_motion_data_dump) + { + PRINT (INFO, "%.3f, %.3f, %.3f, %.3f, %.3f, %.3f\r\n", accel_corrected.x, accel_corrected.y, accel_corrected.z, + gyro_corrected.x, gyro_corrected.y, gyro_corrected.z); + } + + /* Integrate gyro rates. */ + float gyro_x = _motion_orientation.x + gyro_corrected.x * dt; + float gyro_y = _motion_orientation.y + gyro_corrected.y * dt; + + float accel_x = atan2f (accel_corrected.y, sqrtf(accel_corrected.x * accel_corrected.x + accel_corrected.z * accel_corrected.z)) * 180.0f / _MOTION_PI; + float accel_y = atan2f(-accel_corrected.x, sqrtf(accel_corrected.y * accel_corrected.y + accel_corrected.z * accel_corrected.z)) * 180.0f / _MOTION_PI; + + /* Smoothing filter. */ + _motion_orientation.x = _MOTION_ALPHA * gyro_x + ((1.0f - _MOTION_ALPHA) * accel_x); + _motion_orientation.y = _MOTION_ALPHA * gyro_y + ((1.0f - _MOTION_ALPHA) * accel_y); + /* Z angle cannot be determined from accel/gyro alone (need magnetometer). */ + _motion_orientation.z += (gyro_corrected.z * dt); + + /* Compute magnitude of angular velocity (deg/s) */ + _motion_angular_mag = sqrtf ((gyro_corrected.x * gyro_corrected.x) + + (gyro_corrected.y * gyro_corrected.y) + + (gyro_corrected.z * gyro_corrected.z)); +} + +static MOTION_vector_s _motion_apply_cal (MOTION_vector_s raw, MOTION_calibration_data_s cal) +{ + return (MOTION_vector_s) { + .x = (raw.x - cal.offset.x) * cal.scalar.x, + .y = (raw.y - cal.offset.y) * cal.scalar.y, + .z = (raw.z - cal.offset.z) * cal.scalar.z + }; +} + diff --git a/src/app/motion.h b/src/app/motion.h new file mode 100644 index 0000000..1273990 --- /dev/null +++ b/src/app/motion.h @@ -0,0 +1,120 @@ +/** + * @file motion.h + * @brief Motion Module + * @author Kenny Tran + * @date Jan 27 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef MOTION_H_ +#define MOTION_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../bsp/imu.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define MOTION_TASK_PERIOD_MS (50) +#define MOTION_STACK_SIZE (2048) +#define MOTION_THREAD_PRIORITY (6) + +#define MOTION_ANG_THRESHOLD_DEFAULT (0.035) +#define MOTION_CAL_READINGS (100) + +#define MOTION_GYRO_RECAL_READINGS (50) + +#define MOTION_FILENAME ("motion") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef IMU_data_s MOTION_vector_s; + +typedef struct MOTION_calibration_data +{ + MOTION_vector_s offset; + MOTION_vector_s scalar; +} MOTION_calibration_data_s; + +typedef struct MOTION_config +{ + MOTION_calibration_data_s accel_cal; + MOTION_calibration_data_s gyro_cal; + float angular_threshold; + float back_cross_axis_factor; + float front_cross_axis_factor; +} __attribute__((packed, aligned(1))) MOTION_config_s; + +typedef enum MOTION_axis +{ + MOTION_AXIS_X = 0, + MOTION_AXIS_Y +} MOTION_axis_e; + +typedef enum MOTION_cal_orientation +{ + MOTION_CAL_ORIENTATION_FLAT = 0, + MOTION_CAL_ORIENTATION_BACK, + MOTION_CAL_ORIENTATION_FRONT, + + MOTION_CAL_ORIENTATION_COUNT, + MOTION_CAL_ORIENTATION_INVALID = MOTION_CAL_ORIENTATION_COUNT +} MOTION_cal_orientation_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void MOTION_init (void); + +bool MOTION_linear_detected (void); + +bool MOTION_angular_detected (void); + +void MOTION_angular_threshold_set (float variance); + +float MOTION_angle_get (MOTION_axis_e axis); + +void MOTION_calibrate (MOTION_cal_orientation_e orientation); + +float MOTION_calibrate_status_get (MOTION_cal_orientation_e orientation); + +void MOTION_gyro_recalibrate (void); + +void MOTION_gyro_recalibrate_abort (void); + +float MOTION_gyro_recalibrate_status_get (void); + +MOTION_config_s *MOTION_config_get (void); + +void MOTION_config_set (MOTION_config_s cfg); + +void MOTION_task_suspend (void); + +void MOTION_task_resume (void); + +void MOTION_task_install (void); + +void MOTION_cli_cmd (int32_t argc, char **argv); + +void MOTION_detected_flag_set (void); + +#endif /* MOTION_H_ */ diff --git a/src/app/pin.c b/src/app/pin.c new file mode 100644 index 0000000..51b081b --- /dev/null +++ b/src/app/pin.c @@ -0,0 +1,261 @@ +/** + * @file pin.c + * @brief Pin LED Module + * @author Kenny Tran + * @date Mar 08 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/gamma.h" +#include "../lib/print.h" + +#include "../bsp/led.h" + +#include "ble.h" + +#include "pin.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static FS_file_t _pin_file; +static PIN_config_s _pin_config = (PIN_config_s) { + .brightness_step = PIN_BRIGHTNESS_STEP_DEFAULT, + .reserved_0 = 0xFF, + .reserved_1 = 0xFF, + .reserved_2 = 0xFF +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _pin_config_read (void); +static void _pin_config_write (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void PIN_init (void) +{ + PRINT (INFO, "Initializing PIN...\r\n"); + + if (LED_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize LEDs.\r\n"); + return; + } + + /* Initialize filesystem. */ + if (FS_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize filesystem.\r\n"); + return; + } + + /* Fetch configuration settings stored for pin. */ + _pin_config_read (); +} + +void PIN_brightness_set (uint8_t step, bool save_settings) +{ + if (step > PIN_BRIGHTNESS_STEPS) + { + PRINT (ERROR, "Invalid brightness step.\r\n"); + return; + } + + /* Set brightness step. */ + _pin_config.brightness_step = step; + + if (save_settings) + { + /* Write new settings to file. */ + _pin_config_write (); + + /* Notify changes to BLE subscriber. */ + BLE_notify (BLE_CHAR_PIN_BRIGHTNESS, &step, sizeof (step)); + } +} + +uint8_t PIN_brightness_get (void) +{ + return _pin_config.brightness_step; +} + +void PIN_enable (bool en) +{ + float duty; + + if (en) + { + /* 100 for 100% max duty cycle. PIN_BRIGHTNESS_STEPS + 1 extra step for off. */ + duty = (float) GAMMA_corrected_get ((uint32_t) _pin_config.brightness_step, + (uint32_t) (PIN_BRIGHTNESS_STEPS + 1), + (uint32_t) 100, + (double) PIN_BRIGHTNESS_GAMMA_CF); + } + else + { + duty = 0.0; + } + + if (LED_pwm_set (LED_PIN, duty) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to set pin LED brightness.\r\n"); + } +} + +void PIN_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "PIN UV LED\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "brightness_step (0 - %d) : %d\r\n", PIN_BRIGHTNESS_STEPS, _pin_config.brightness_step); + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-e") || !strcmp (argv[0], "--enable")) + { + if (!strcmp (argv[1], "true")) + { + PIN_enable (true); + } + else if (!strcmp (argv[1], "false")) + { + PIN_enable (false); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-s")) + { + uint8_t step = (uint8_t) strtoul (argv[1], NULL, 0); + + PIN_brightness_set (step, true); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: pin [OPTION...]\r\n"); + PRINT (INFO, " -e | --enable Enables pin LED\r\n"); + PRINT (INFO, " <%%b> true = enable\r\n"); + PRINT (INFO, " false = disable\r\n"); + PRINT (INFO, " -s Sets pin UV LED brightness step\r\n"); + PRINT (INFO, " <%%d> Brightness step [0-%d]\r\n", PIN_BRIGHTNESS_STEPS); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _pin_config_read (void) +{ + ERRNO_e err; + PIN_config_s cfg; + + err = FS_open_or_create (PIN_FILENAME, &_pin_file, sizeof (PIN_config_s), FS_FILE_DEPTH_DEFAULT); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_pin_file, (uint8_t *) &cfg, 0, sizeof (PIN_config_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + /* Write defaults to file. */ + _pin_config_write (); + return; + } + + /* Validate read file. */ + if (cfg.brightness_step > PIN_BRIGHTNESS_STEPS) + { + /* Write defaults to file. */ + _pin_config_write (); + return; + } + + /* Update pin configuration to what was read from flash and validated. */ + _pin_config = cfg; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete (PIN_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old pin file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (PIN_FILENAME, &_pin_file, sizeof (PIN_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create pin configuration file.\r\n"); + } + else + { + PRINT (INFO, "New pin file created successfully\r\n"); + } + + /* Write default config to file. */ + _pin_config_write (); + } + else + { + /* Write defaults to file. */ + _pin_config_write (); + } +} + +static void _pin_config_write (void) +{ + if (FS_write (_pin_file, (uint8_t *) &_pin_config) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write pin configuration to file.\r\n"); + } +} diff --git a/src/app/pin.h b/src/app/pin.h new file mode 100644 index 0000000..378f733 --- /dev/null +++ b/src/app/pin.h @@ -0,0 +1,65 @@ +/** + * @file pin.h + * @brief Pin LED Module + * @author Kenny Tran + * @date Mar 08 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef PIN_H_ +#define PIN_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define PIN_BRIGHTNESS_STEPS (10) +#define PIN_BRIGHTNESS_STEP_DEFAULT (5) +#define PIN_BRIGHTNESS_GAMMA_CF (1.6) /* Gamma correction factor */ + +#define PIN_FILENAME ("pin") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct PIN_config +{ + uint8_t brightness_step : 8; + uint8_t reserved_0 : 8; + uint8_t reserved_1 : 8; + uint8_t reserved_2 : 8; +} __attribute__((packed, aligned(1))) PIN_config_s; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void PIN_init (void); + +void PIN_brightness_set (uint8_t step, bool save_settings); + +uint8_t PIN_brightness_get (void); + +void PIN_enable (bool en); + +void PIN_cli_cmd (int32_t argc, char **argv); + + +#endif /* PIN_H_ */ diff --git a/src/app/scope_ring.c b/src/app/scope_ring.c new file mode 100644 index 0000000..85e2094 --- /dev/null +++ b/src/app/scope_ring.c @@ -0,0 +1,1887 @@ +/** + * @file scope_ring.c + * @brief Scope ring module + * @author Kenny Tran + * @date Apr 05 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include + +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/print.h" +#include "../lib/rgb.h" + +#include "../bsp/battery.h" +#include "../bsp/led_ring.h" +#include "../bsp/power.h" +#include "../bsp/wdt.h" + +#include "../app/ble.h" +#include "../app/motion.h" + +#include "scope_ring.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _SCOPE_RING_N_LEDS (LED_RING_PIXELS) +#define _SCOPE_RING_R_INDICATOR_IDX (_SCOPE_RING_N_LEDS / 4) +#define _SCOPE_RING_L_INDICATOR_IDX (_SCOPE_RING_N_LEDS - _SCOPE_RING_R_INDICATOR_IDX) + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +K_THREAD_STACK_DEFINE(_scope_ring_stack, SCOPE_RING_STACK_SIZE); + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static struct k_thread _scope_ring_thread; +static FS_file_t _scope_ring_file; +static SCOPE_RING_config_s _scope_ring_config = (SCOPE_RING_config_s) { + .indicators_color = SCOPE_RING_INDICATORS_COLOR_DEFAULT, + .indicators_brightness = SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, + .level_color = SCOPE_RING_LEVEL_COLOR_DEFAULT, + .level_brightness = SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, + .background_color = SCOPE_RING_BACKGROUND_COLOR_DEFAULT, + .background_brightness = SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, + .level_option = SCOPE_RING_LEVEL_OPTION_DEFAULT, + .level_viscosity = SCOPE_RING_LEVEL_VISCOSITY_DEFAULT, + .level_sensitivity = SCOPE_RING_LEVEL_SENSITIVITY_DEFAULT +}; +static SCOPE_RING_state_e _scope_ring_state = SCOPE_RING_STATE_OFF; +static uint32_t _scope_ring_timer_cnt = 0; +static float _scope_ring_battery_life = 0.0; +static uint32_t _scope_ring_state_duration = 0; +static bool _scope_ring_state_finished = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _scope_ring_task (void *p1, void *p2, void *p3); +static void _scope_ring_level_process (float angle, SCOPE_RING_level_option_e level_option); +static void _scope_ring_background_set (void); +static void _scope_ring_mirrored_set (uint8_t led, uint8_t r, uint8_t g, uint8_t b); +static void _scope_ring_enable (bool en); +static void _scope_ring_config_read (void); +static void _scope_ring_config_write (void); + +static float _scope_ring_angle_hysteresis_get (float angle); +static float _scope_ring_level_viscosity_get (float angle, float alpha); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void SCOPE_RING_init (void) +{ + PRINT (INFO, "Initializing SCOPE_RING...\r\n"); + + /* Initialize battery module. */ + if (BATTERY_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize Battery.\r\n"); + } + + /* Initialize LED ring. */ + if (LED_RING_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize LED ring.\r\n"); + return; + } + + /* Initialize filesystem. */ + if (FS_init () < ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to initialize filesystem.\r\n"); + return; + } + + /* Fetch configuration settings stored for scope ring. */ + _scope_ring_config_read (); +} + +void SCOPE_RING_state_set (SCOPE_RING_state_e state, uint32_t duration_ms) +{ + if (state >= SCOPE_RING_STATE_INVALID) + { + PRINT (ERROR, "Invalid state.\r\n"); + return; + } + + /* Reset task timer on every state change. */ + _scope_ring_timer_cnt = 0; + + /* Reset state finished flag. */ + _scope_ring_state_finished = false; + + _scope_ring_state = state; + _scope_ring_state_duration = duration_ms; +} + +SCOPE_RING_state_e SCOPE_RING_state_get (bool *p_finshed) +{ + if (p_finshed == NULL) + { + return _scope_ring_state; + } + else + { + *p_finshed = _scope_ring_state_finished; + } + + return _scope_ring_state; +} + +void SCOPE_RING_color_set (SCOPE_RING_led_e led, RGB_color_e color) +{ + if (color >= RGB_COLOR_COUNT) + { + PRINT (ERROR, "Invalid color.\r\n"); + return; + } + + switch (led) + { + case SCOPE_RING_LED_BACKGROUND: + _scope_ring_config.background_color = color; + BLE_notify (BLE_CHAR_RING_COLOR, &color, sizeof (color)); + break; + + case SCOPE_RING_LED_INDICATORS: + _scope_ring_config.indicators_color = color; + BLE_notify (BLE_CHAR_INDICATOR_COLOR, &color, sizeof (color)); + break; + + case SCOPE_RING_LED_LEVEL: + _scope_ring_config.level_color = color; + BLE_notify (BLE_CHAR_LEVEL_COLOR, &color, sizeof (color)); + break; + + default: + PRINT (ERROR, "ERROR : Invalid LED.\r\n"); + return; + } + + /* Write new settings to file. */ + _scope_ring_config_write (); +} + +void SCOPE_RING_brightness_set (SCOPE_RING_led_e led, uint8_t step, bool save_settings) +{ + if (step > SCOPE_RING_BRIGHTNESS_STEPS) + { + PRINT (ERROR, "Invalid brightness step.\r\n"); + return; + } + + switch (led) + { + case SCOPE_RING_LED_BACKGROUND: + _scope_ring_config.background_brightness = step; + break; + + case SCOPE_RING_LED_INDICATORS: + _scope_ring_config.indicators_brightness = step; + break; + + case SCOPE_RING_LED_LEVEL: + _scope_ring_config.level_brightness = step; + break; + + default: + PRINT (ERROR, "Invalid LED.\r\n"); + } + + if (save_settings) + { + /* Write new settings to file. */ + _scope_ring_config_write (); + + switch (led) + { + case SCOPE_RING_LED_BACKGROUND: + BLE_notify (BLE_CHAR_RING_BRIGHTNESS, &step, sizeof (step)); + break; + + case SCOPE_RING_LED_INDICATORS: + BLE_notify (BLE_CHAR_INDICATOR_BRIGHTNESS, &step, sizeof (step)); + break; + + case SCOPE_RING_LED_LEVEL: + BLE_notify (BLE_CHAR_LEVEL_BRIGHTNESS, &step, sizeof (step)); + break; + + default: + PRINT (ERROR, "Invalid LED.\r\n"); + } + } +} + +void SCOPE_RING_battery_indicator_set (float percentage) +{ + if (percentage > 1.0) + { + PRINT (ERROR, "Invalid battery percentage. %f\r\n", percentage); + _scope_ring_battery_life = 1.0; + } + else if (percentage < 0.0) + { + PRINT (ERROR, "Invalid battery percentage.%f\r\n", percentage); + _scope_ring_battery_life = 0.0; + } + else + { + _scope_ring_battery_life = percentage; + } +} + +void SCOPE_RING_level_option_set (SCOPE_RING_level_option_e option) +{ + if (option >= SCOPE_RING_LEVEL_OPTION_COUNT) + { + PRINT (ERROR, "Invalid leveling option.\r\n"); + _scope_ring_config.level_option = SCOPE_RING_LEVEL_COLOR_DEFAULT; + } + else + { + _scope_ring_config.level_option = option; + } + + /* Write new settings to file. */ + _scope_ring_config_write (); + + BLE_notify (BLE_CHAR_LEVEL_OPTION, &option, sizeof (option)); +} + +void SCOPE_RING_level_viscosity_set (float alpha) +{ + _scope_ring_config.level_viscosity = alpha; + + /* Write new settings to file. */ + _scope_ring_config_write (); + + BLE_notify (BLE_CHAR_LEVEL_VISCOSITY, &alpha, sizeof (alpha)); +} + +void SCOPE_RING_level_sensitivity_set (float sensitivity) +{ + _scope_ring_config.level_sensitivity = sensitivity; + + /* Write new settings to file. */ + _scope_ring_config_write (); + + BLE_notify (BLE_CHAR_LEVEL_SENSITIVITY, &sensitivity, sizeof (sensitivity)); +} + +SCOPE_RING_config_s SCOPE_RING_config_get (void) +{ + return _scope_ring_config; +} + +void SCOPE_RING_task_suspend (void) +{ + k_thread_suspend (&_scope_ring_thread); +} + +void SCOPE_RING_task_resume (void) +{ + k_thread_resume (&_scope_ring_thread); +} + +void SCOPE_RING_task_install (void) +{ + static k_tid_t id; + + id = k_thread_create (&_scope_ring_thread, + _scope_ring_stack, + SCOPE_RING_STACK_SIZE, + _scope_ring_task, + NULL, + NULL, + NULL, + SCOPE_RING_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&_scope_ring_thread, "SCOPE_RING_task"); +} + +void SCOPE_RING_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "SCOPE RING\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "timer counter (ms) : %d\r\n", _scope_ring_timer_cnt); + + PRINT (INFO, "State : "); + switch (_scope_ring_state) + { + case SCOPE_RING_STATE_OFF: + PRINT (INFO, "off\r\n"); + break; + case SCOPE_RING_STATE_ON: + PRINT (INFO, "on\r\n"); + break; + case SCOPE_RING_STATE_SET_FLAT: + PRINT (INFO, "setflat\r\n"); + break; + case SCOPE_RING_STATE_FLAT_CALIBRATION: + PRINT (INFO, "calflat\r\n"); + break; + case SCOPE_RING_STATE_SET_BACK: + PRINT (INFO, "setback\r\n"); + break; + case SCOPE_RING_STATE_BACK_CALIBRATION: + PRINT (INFO, "calback\r\n"); + break; + case SCOPE_RING_STATE_SET_FRONT: + PRINT (INFO, "setfront\r\n"); + break; + case SCOPE_RING_STATE_FRONT_CALIBRATION: + PRINT (INFO, "calfront\r\n"); + break; + case SCOPE_RING_STATE_CONFIG_LEVEL_COLOR: + PRINT (INFO, "lc\r\n"); + break; + case SCOPE_RING_STATE_CONFIG_LEVEL_BRIGHTNESS: + PRINT (INFO, "lb\r\n"); + break; + case SCOPE_RING_STATE_CONFIG_INDICATORS_COLOR: + PRINT (INFO, "ic\r\n"); + break; + case SCOPE_RING_STATE_CONFIG_INDICATORS_BRIGHTNESS: + PRINT (INFO, "ib\r\n"); + break; + case SCOPE_RING_STATE_CONFIG_BACKGROUND_COLOR: + PRINT (INFO, "bc\r\n"); + break; + case SCOPE_RING_STATE_CONFIG_BACKGROUND_BRIGHTNESS: + PRINT (INFO, "bb\r\n"); + break; + case SCOPE_RING_STATE_RAINBOW: + PRINT (INFO, "rainbow\r\n"); + break; + case SCOPE_RING_STATE_CHARGING: + PRINT (INFO, "charging\r\n"); + break; + case SCOPE_RING_STATE_CHARGER_CONNECTED: + PRINT (INFO, "cc\r\n"); + break; + case SCOPE_RING_STATE_CHARGER_DISCONNECTED: + PRINT (INFO, "cd\r\n"); + break; + case SCOPE_RING_STATE_POWER_ON: + PRINT (INFO, "power_on\r\n"); + break; + case SCOPE_RING_STATE_POWER_OFF: + PRINT (INFO, "power_off\r\n"); + break; + case SCOPE_RING_STATE_BLE_ENABLED: + PRINT (INFO, "ble_en\r\n"); + break; + case SCOPE_RING_STATE_BLE_DISABLED: + PRINT (INFO, "ble_dis\r\n"); + break; + } + PRINT (INFO, "Indicators color : "); + switch (_scope_ring_config.indicators_color) + { + case RGB_COLOR_RED: + PRINT (INFO, "red\r\n"); + break; + case RGB_COLOR_GREEN: + PRINT (INFO, "green\r\n"); + break; + case RGB_COLOR_BLUE: + PRINT (INFO, "blue\r\n"); + break; + case RGB_COLOR_ORANGE: + PRINT (INFO, "orange\r\n"); + break; + case RGB_COLOR_YELLOW: + PRINT (INFO, "yellow\r\n"); + break; + case RGB_COLOR_PURPLE: + PRINT (INFO, "purple\r\n"); + break; + case RGB_COLOR_PINK: + PRINT (INFO, "pink\r\n"); + break; + case RGB_COLOR_WHITE: + PRINT (INFO, "white\r\n"); + break; + } + PRINT (INFO, "Indicators brightness step : %d\r\n", _scope_ring_config.indicators_brightness); + PRINT (INFO, "Level color : "); + switch (_scope_ring_config.level_color) + { + case RGB_COLOR_RED: + PRINT (INFO, "red\r\n"); + break; + case RGB_COLOR_GREEN: + PRINT (INFO, "green\r\n"); + break; + case RGB_COLOR_BLUE: + PRINT (INFO, "blue\r\n"); + break; + case RGB_COLOR_ORANGE: + PRINT (INFO, "orange\r\n"); + break; + case RGB_COLOR_YELLOW: + PRINT (INFO, "yellow\r\n"); + break; + case RGB_COLOR_PURPLE: + PRINT (INFO, "purple\r\n"); + break; + case RGB_COLOR_PINK: + PRINT (INFO, "pink\r\n"); + break; + case RGB_COLOR_WHITE: + PRINT (INFO, "white\r\n"); + break; + } + PRINT (INFO, "Level brightness step : %d\r\n", _scope_ring_config.level_brightness); + PRINT (INFO, "Background color : "); + switch (_scope_ring_config.background_color) + { + case RGB_COLOR_RED: + PRINT (INFO, "red\r\n"); + break; + case RGB_COLOR_GREEN: + PRINT (INFO, "green\r\n"); + break; + case RGB_COLOR_BLUE: + PRINT (INFO, "blue\r\n"); + break; + case RGB_COLOR_ORANGE: + PRINT (INFO, "orange\r\n"); + break; + case RGB_COLOR_YELLOW: + PRINT (INFO, "yellow\r\n"); + break; + case RGB_COLOR_PURPLE: + PRINT (INFO, "purple\r\n"); + break; + case RGB_COLOR_PINK: + PRINT (INFO, "pink\r\n"); + break; + case RGB_COLOR_WHITE: + PRINT (INFO, "white\r\n"); + break; + } + PRINT (INFO, "Background brightness step : %d\r\n", _scope_ring_config.background_brightness); + PRINT (INFO, "Leveling option : %d\r\n", _scope_ring_config.level_option); + PRINT (INFO, "Level sensitivity (degrees) : %f\r\n", (SCOPE_RING_LEVEL_SENSITIVITY_MAX + + SCOPE_RING_LEVEL_SENSITIVITY_MIN - + _scope_ring_config.level_sensitivity)); + PRINT (INFO, "Level viscosity (alpha) : %f\r\n", _scope_ring_config.level_viscosity); + } + else if (argc == 2 || argc == 3) + { + if (!strcmp (argv[0], "-s") || !strcmp (argv[0], "--set")) + { + if (!strcmp (argv[1], "off")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_OFF, 0); + } + else if (!strcmp (argv[1], "on")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_ON, 0); + } + else if (!strcmp (argv[1], "setflat")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_FLAT, 0); + } + else if (!strcmp (argv[1], "calflat")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_FLAT_CALIBRATION, 0); + } + else if (!strcmp (argv[1], "setback")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_BACK, 0); + } + else if (!strcmp (argv[1], "calback")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_BACK_CALIBRATION, 0); + } + else if (!strcmp (argv[1], "setfront")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_SET_FRONT, 0); + } + else if (!strcmp (argv[1], "calfront")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_FRONT_CALIBRATION, 0); + } + else if (!strcmp (argv[1], "lc")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_COLOR, 0); + } + else if (!strcmp (argv[1], "lb")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_LEVEL_BRIGHTNESS, 0); + } + else if (!strcmp (argv[1], "ic")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_INDICATORS_COLOR, 0); + } + else if (!strcmp (argv[1], "ib")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_INDICATORS_BRIGHTNESS, 0); + } + else if (!strcmp (argv[1], "bc")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_BACKGROUND_COLOR, 0); + } + else if (!strcmp (argv[1], "bb")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CONFIG_BACKGROUND_BRIGHTNESS, 0); + } + else if (!strcmp (argv[1], "rainbow")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_RAINBOW, 0); + } + else if (!strcmp (argv[1], "charging")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CHARGING, 0); + } + else if (!strcmp (argv[1], "cc")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CHARGER_CONNECTED, 0); + } + else if (!strcmp (argv[1], "cd")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_CHARGER_DISCONNECTED, 0); + } + else if (!strcmp (argv[1], "power_on")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_POWER_ON, 0); + } + else if (!strcmp (argv[1], "power_off")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_POWER_OFF, 0); + } + else if (!strcmp (argv[1], "ble_en")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_BLE_ENABLED, 0); + } + else if (!strcmp (argv[1], "ble_dis")) + { + SCOPE_RING_state_set (SCOPE_RING_STATE_BLE_DISABLED, 0); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-i") || !strcmp (argv[0], "--indicators")) + { + if (!strcmp (argv[1], "red")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_RED); + } + else if (!strcmp (argv[1], "green")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_GREEN); + } + else if (!strcmp (argv[1], "blue")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_BLUE); + } + else if (!strcmp (argv[1], "orange")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_ORANGE); + } + else if (!strcmp (argv[1], "yellow")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_YELLOW); + } + else if (!strcmp (argv[1], "purple")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_PURPLE); + } + else if (!strcmp (argv[1], "pink")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_PINK); + } + else if (!strcmp (argv[1], "white")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, RGB_COLOR_WHITE); + } + else + { + goto USAGE; + } + + if (argc == 3) + { + uint8_t step = (uint8_t) strtoul (argv[2], NULL, 0); + + SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, step, true); + } + } + else if (!strcmp (argv[0], "-l") || !strcmp (argv[0], "--level")) + { + if (!strcmp (argv[1], "red")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_RED); + } + else if (!strcmp (argv[1], "green")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_GREEN); + } + else if (!strcmp (argv[1], "blue")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_BLUE); + } + else if (!strcmp (argv[1], "orange")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_ORANGE); + } + else if (!strcmp (argv[1], "yellow")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_YELLOW); + } + else if (!strcmp (argv[1], "purple")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_PURPLE); + } + else if (!strcmp (argv[1], "pink")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_PINK); + } + else if (!strcmp (argv[1], "white")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, RGB_COLOR_WHITE); + } + else + { + goto USAGE; + } + + if (argc == 3) + { + uint8_t step = (uint8_t) strtoul (argv[2], NULL, 0); + + SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, step, true); + } + } + else if (!strcmp (argv[0], "-b") || !strcmp (argv[0], "--background")) + { + if (!strcmp (argv[1], "red")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_RED); + } + else if (!strcmp (argv[1], "green")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_GREEN); + } + else if (!strcmp (argv[1], "blue")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_BLUE); + } + else if (!strcmp (argv[1], "orange")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_ORANGE); + } + else if (!strcmp (argv[1], "yellow")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_YELLOW); + } + else if (!strcmp (argv[1], "purple")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_PURPLE); + } + else if (!strcmp (argv[1], "pink")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_PINK); + } + else if (!strcmp (argv[1], "white")) + { + SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, RGB_COLOR_WHITE); + } + else + { + goto USAGE; + } + + if (argc == 3) + { + uint8_t step = (uint8_t) strtoul (argv[2], NULL, 0); + + SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, step, true); + } + } + else if (!strcmp (argv[0], "-ss") || !strcmp (argv[0], "--sensitivity")) + { + if (argc == 2) + { + float sensitivity = strtof (argv[1], NULL); + + SCOPE_RING_level_sensitivity_set (sensitivity); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-v") || !strcmp (argv[0], "--viscosity")) + { + if (argc == 2) + { + float alpha = strtof (argv[1], NULL); + + SCOPE_RING_level_viscosity_set (alpha); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-lo")) + { + uint8_t option = (uint8_t) strtoul (argv[1], NULL, 0); + + SCOPE_RING_level_option_set ((SCOPE_RING_level_option_e) option); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: scope_ring [OPTION...]\r\n"); + PRINT (INFO, " -s | --set Sets scope ring state\r\n"); + PRINT (INFO, " <%%s> off\r\n"); + PRINT (INFO, " on\r\n"); + PRINT (INFO, " setflat\r\n"); + PRINT (INFO, " calflat\r\n"); + PRINT (INFO, " setback\r\n"); + PRINT (INFO, " calback\r\n"); + PRINT (INFO, " setfront\r\n"); + PRINT (INFO, " calfront\r\n"); + PRINT (INFO, " lc - level color\r\n"); + PRINT (INFO, " lb - level brightness\r\n"); + PRINT (INFO, " ic - indicators color\r\n"); + PRINT (INFO, " ib - indicators brightness\r\n"); + PRINT (INFO, " bc - background color\r\n"); + PRINT (INFO, " bb - background brightness\r\n"); + PRINT (INFO, " rainbow\r\n"); + PRINT (INFO, " charging\r\n"); + PRINT (INFO, " cc - charger connected\r\n"); + PRINT (INFO, " cd - charger disconnected\r\n"); + PRINT (INFO, " power_on\r\n"); + PRINT (INFO, " power_off\r\n"); + PRINT (INFO, " -i | --indicators Sets color and brightness of the indicators\r\n"); + PRINT (INFO, " <%%s> red\r\n"); + PRINT (INFO, " green\r\n"); + PRINT (INFO, " blue\r\n"); + PRINT (INFO, " orange\r\n"); + PRINT (INFO, " yellow\r\n"); + PRINT (INFO, " purple\r\n"); + PRINT (INFO, " pink\r\n"); + PRINT (INFO, " white\r\n"); + PRINT (INFO, " <%%d> [0-%d]\r\n", SCOPE_RING_BRIGHTNESS_STEPS); + PRINT (INFO, " -l | --level Sets the level colors\r\n"); + PRINT (INFO, " <%%s> red\r\n"); + PRINT (INFO, " green\r\n"); + PRINT (INFO, " blue\r\n"); + PRINT (INFO, " orange\r\n"); + PRINT (INFO, " yellow\r\n"); + PRINT (INFO, " purple\r\n"); + PRINT (INFO, " pink\r\n"); + PRINT (INFO, " white\r\n"); + PRINT (INFO, " <%%d> [0-%d]\r\n", SCOPE_RING_BRIGHTNESS_STEPS); + PRINT (INFO, " -b | --background Sets the background colors\r\n"); + PRINT (INFO, " <%%s> red\r\n"); + PRINT (INFO, " green\r\n"); + PRINT (INFO, " blue\r\n"); + PRINT (INFO, " orange\r\n"); + PRINT (INFO, " yellow\r\n"); + PRINT (INFO, " purple\r\n"); + PRINT (INFO, " pink\r\n"); + PRINT (INFO, " white\r\n"); + PRINT (INFO, " <%%d> [0-%d]\r\n", SCOPE_RING_BRIGHTNESS_STEPS); + PRINT (INFO, " -ss | --sensitivity Sets the LED level sensitivity\r\n"); + PRINT (INFO, " <%%f> (level, %.1f to %.1f)\r\n", SCOPE_RING_LEVEL_SENSITIVITY_MIN, SCOPE_RING_LEVEL_SENSITIVITY_MAX); + PRINT (INFO, " -v | --viscosity Sets the LED level viscosity\r\n"); + PRINT (INFO, " <%%f> (alpha)\r\n"); + PRINT (INFO, " -lo Sets the leveling option\r\n"); + PRINT (INFO, " <%%d> [0-2]\r\n"); + + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _scope_ring_task (void *p1, void *p2, void *p3) +{ + while (1) + { + uint8_t led; + + /* If timer counter has reached specified task duration, set state finished flag. */ + // if ((_scope_ring_state_duration != 0) && (_scope_ring_timer_cnt == _scope_ring_state_duration)) + // { + // _scope_ring_state_finished = true; + // } + + switch (_scope_ring_state) + { + case SCOPE_RING_STATE_OFF: + /* Turn off ring LEDs. */ + _scope_ring_enable (false); + break; + + case SCOPE_RING_STATE_ON: + if (_scope_ring_config.indicators_brightness == 0 && + _scope_ring_config.level_brightness == 0 && + _scope_ring_config.background_brightness == 0) + { + /* Disable FET to save on quiescent power. */ + _scope_ring_enable (false); + } + else + { + float angle = MOTION_angle_get (MOTION_AXIS_Y); + + _scope_ring_background_set (); + _scope_ring_level_process (angle, _scope_ring_config.level_option); + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_SET_FLAT: + { + static bool up_down = true; + static uint8_t brightness = 0; + + for (led = 0; led < _SCOPE_RING_N_LEDS; led++) + { + LED_RING_set (led, brightness, 0, 0); + } + + _scope_ring_enable (true); + + if (up_down) + { + if (brightness == 100) + { + up_down = false; + } + else + { + brightness++; + } + } + else + { + if (brightness == 0) + { + up_down = true; + } + else + { + brightness--; + } + } + } + break; + + case SCOPE_RING_STATE_FLAT_CALIBRATION: + { + static uint8_t prev_bound = 0; + uint8_t led_bound = (uint32_t) (MOTION_calibrate_status_get (MOTION_CAL_ORIENTATION_FLAT) * (float) _SCOPE_RING_N_LEDS); + uint8_t led_idx; + + if (led_bound == prev_bound) + { + break; + } + else + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led_idx = 0; led_idx < led_bound; led_idx++) + { + LED_RING_set (led_idx, 100, 0, 0); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + + prev_bound = led_bound; + } + break; + + case SCOPE_RING_STATE_SET_BACK: + { + static bool up_down = true; + static uint8_t brightness = 0; + + for (led = 0; led < _SCOPE_RING_N_LEDS; led++) + { + LED_RING_set (led, 0, brightness, 0); + } + + _scope_ring_enable (true); + + if (up_down) + { + if (brightness == 100) + { + up_down = false; + } + else + { + brightness++; + } + } + else + { + if (brightness == 0) + { + up_down = true; + } + else + { + brightness--; + } + } + } + break; + + case SCOPE_RING_STATE_BACK_CALIBRATION: + { + static uint8_t prev_bound = 0; + uint8_t led_bound = (uint32_t) (MOTION_calibrate_status_get (MOTION_CAL_ORIENTATION_BACK) * (float) _SCOPE_RING_N_LEDS); + uint8_t led_idx; + + if (led_bound == prev_bound) + { + break; + } + else + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led_idx = 0; led_idx < led_bound; led_idx++) + { + LED_RING_set (led_idx, 0, 100, 0); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + + prev_bound = led_bound; + } + break; + + case SCOPE_RING_STATE_SET_FRONT: + { + static bool up_down = true; + static uint8_t brightness = 0; + + for (led = 0; led < _SCOPE_RING_N_LEDS; led++) + { + LED_RING_set (led, 0, 0, brightness); + } + + _scope_ring_enable (true); + + if (up_down) + { + if (brightness == 100) + { + up_down = false; + } + else + { + brightness++; + } + } + else + { + if (brightness == 0) + { + up_down = true; + } + else + { + brightness--; + } + } + } + break; + + case SCOPE_RING_STATE_FRONT_CALIBRATION: + { + static uint8_t prev_bound = 0; + uint8_t led_bound = (uint32_t) (MOTION_calibrate_status_get (MOTION_CAL_ORIENTATION_FRONT) * (float) _SCOPE_RING_N_LEDS); + uint8_t led_idx; + + if (led_bound == prev_bound) + { + break; + } + else + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led_idx = 0; led_idx < led_bound; led_idx++) + { + LED_RING_set (led_idx, 0, 0, 100); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + + prev_bound = led_bound; + } + break; + + + case SCOPE_RING_STATE_CONFIG_LEVEL_COLOR: + if (_scope_ring_timer_cnt >= (_scope_ring_state_duration / 2)) + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + + if (_scope_ring_timer_cnt == _scope_ring_state_duration) + { + _scope_ring_timer_cnt = 0; + } + } + else + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + _scope_ring_level_process (0.0, _scope_ring_config.level_option); + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_CONFIG_LEVEL_BRIGHTNESS: + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + _scope_ring_level_process (0.0, _scope_ring_config.level_option); + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + break; + + case SCOPE_RING_STATE_CONFIG_INDICATORS_COLOR: + if (_scope_ring_timer_cnt >= (_scope_ring_state_duration / 2)) + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + + if (_scope_ring_timer_cnt == _scope_ring_state_duration) + { + _scope_ring_timer_cnt = 0; + } + } + else + { + /* SCOPE_RING_BRIGHTNESS_STEPS + 1 extra step for off. */ + RGB_s indicators = RGB_gamma_corrected (_scope_ring_config.indicators_color, + _scope_ring_config.indicators_brightness, + (SCOPE_RING_BRIGHTNESS_STEPS + 1), + RGB_BRIGHTNESS_MAX, + SCOPE_RING_BRIGHTNESS_GAMMA_CF); + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + LED_RING_set ( 3, indicators.r, indicators.g, indicators.b); + LED_RING_set ( 9, indicators.r, indicators.g, indicators.b); + LED_RING_set (15, indicators.r, indicators.g, indicators.b); + LED_RING_set (21, indicators.r, indicators.g, indicators.b); + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_CONFIG_INDICATORS_BRIGHTNESS: + { + /* SCOPE_RING_BRIGHTNESS_STEPS + 1 extra step for off. */ + RGB_s indicators = RGB_gamma_corrected (_scope_ring_config.indicators_color, + _scope_ring_config.indicators_brightness, + (SCOPE_RING_BRIGHTNESS_STEPS + 1), + RGB_BRIGHTNESS_MAX, + SCOPE_RING_BRIGHTNESS_GAMMA_CF); + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + LED_RING_set ( 3, indicators.r, indicators.g, indicators.b); + LED_RING_set ( 9, indicators.r, indicators.g, indicators.b); + LED_RING_set (15, indicators.r, indicators.g, indicators.b); + LED_RING_set (21, indicators.r, indicators.g, indicators.b); + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_CONFIG_BACKGROUND_COLOR: + if (_scope_ring_timer_cnt >= (_scope_ring_state_duration / 2)) + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + + if (_scope_ring_timer_cnt == _scope_ring_state_duration) + { + _scope_ring_timer_cnt = 0; + } + } + else + { + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + _scope_ring_background_set (); + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_CONFIG_BACKGROUND_BRIGHTNESS: + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + _scope_ring_background_set (); + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + break; + + case SCOPE_RING_STATE_CONFIG_LEVEL_SENSITIVITY: + { + uint8_t bound = (uint8_t) ((SCOPE_RING_LEVEL_SENSITIVITY_MAX + + SCOPE_RING_LEVEL_SENSITIVITY_MIN - + _scope_ring_config.level_sensitivity) / + (float) SCOPE_RING_LEVEL_SENSITIVITY_MAX * + (float) LED_RING_PIXELS); + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + /* Use purple to display sensitivity. */ + RGB_s purple = RGB_gamma_corrected (RGB_COLOR_PURPLE, + 10, + (SCOPE_RING_BRIGHTNESS_STEPS + 1), + RGB_BRIGHTNESS_MAX, + SCOPE_RING_BRIGHTNESS_GAMMA_CF); + + for (led = 0; led < bound; led++) + { + LED_RING_set (led, purple.r, purple.g, purple.b); + } + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_CONFIG_LEVEL_OPTION: + { + RGB_s indicators = RGB_gamma_corrected (_scope_ring_config.indicators_color, + (RGB_BRIGHTNESS_MAX / 2), + SCOPE_RING_BRIGHTNESS_STEP_DEFAULT, + RGB_BRIGHTNESS_MAX, + SCOPE_RING_BRIGHTNESS_GAMMA_CF); + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + switch (_scope_ring_config.level_option) + { + case SCOPE_RING_LEVEL_OPTION_0: + LED_RING_set (15, indicators.r, indicators.g, indicators.b); + LED_RING_set (21, indicators.r, indicators.g, indicators.b); + break; + case SCOPE_RING_LEVEL_OPTION_1: + LED_RING_set (18, indicators.r, indicators.g, indicators.b); + break; + case SCOPE_RING_LEVEL_OPTION_2: + LED_RING_set ( 9, indicators.r, indicators.g, indicators.b); + LED_RING_set (21, indicators.r, indicators.g, indicators.b); + break; + } + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_RAINBOW: + { + static uint16_t count = 0; + LED_RING_rainbow (count, 1, 255, 100, 1); + + count += 256; + + _scope_ring_enable (true); + } + break; + + case SCOPE_RING_STATE_CHARGING: + if (_scope_ring_timer_cnt < (_scope_ring_state_duration / 2)) + { + uint8_t bounds = (uint8_t) (_scope_ring_battery_life * _SCOPE_RING_N_LEDS); + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led = 0; led < bounds; led++) + { + LED_RING_set (led, 0, 0, 50); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + } + else if (_scope_ring_timer_cnt == (_scope_ring_state_duration / 2)) + { + float voltage; + float life_remaining; + + /* Disable scope ring to prevent voltage drop before getting battery reading. */ + _scope_ring_enable (false); + + if (BATTERY_voltage_get (&voltage) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to read battery voltage.\r\n"); + } + + life_remaining = BATTERY_level_calculate (voltage); + + /* Set battery status for feedback on LED ring. */ + SCOPE_RING_battery_indicator_set (life_remaining); + } + else if (_scope_ring_timer_cnt >= _scope_ring_state_duration) + { + _scope_ring_timer_cnt = 0; + } + break; + + case SCOPE_RING_STATE_CHARGER_CONNECTED: + if (!_scope_ring_state_finished) + { + static uint8_t bounds = 0; + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led = 0; led < bounds; led++) + { + LED_RING_set (led, 50, 50, 50); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + + if (bounds == (_SCOPE_RING_N_LEDS - 1)) + { + /* Force finish flag. */ + _scope_ring_state_finished = true; + + bounds = 0; + } + else + { + bounds++; + } + } + break; + + case SCOPE_RING_STATE_CHARGER_DISCONNECTED: + if (!_scope_ring_state_finished) + { + static uint8_t bounds = (_SCOPE_RING_N_LEDS - 1); + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led = 0; led < bounds; led++) + { + LED_RING_set (led, 50, 50, 50); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + + if (bounds == 0) + { + /* Force finish flag. */ + _scope_ring_state_finished = true; + + bounds = (_SCOPE_RING_N_LEDS - 1); + } + else + { + bounds--; + } + } + break; + + case SCOPE_RING_STATE_POWER_ON: + if (!_scope_ring_state_finished) + { + uint8_t bounds = (uint8_t) (_scope_ring_battery_life * _SCOPE_RING_N_LEDS); + static uint8_t brightness = 0; + + for (led = 0; led < _SCOPE_RING_N_LEDS; led++) + { + if (led < bounds) + { + LED_RING_set (led, 0, brightness, 0); + } + else + { + LED_RING_set (led, 0, 0, 0); + } + } + + _scope_ring_enable (true); + + if (brightness == 100) + { + _scope_ring_state_finished = true; + brightness = 0; + } + else + { + brightness++; + } + } + break; + + case SCOPE_RING_STATE_POWER_OFF: + if (!_scope_ring_state_finished) + { + uint8_t bounds = (uint8_t) (_scope_ring_battery_life * _SCOPE_RING_N_LEDS); + static uint8_t brightness = 100; + + for (led = 0; led < _SCOPE_RING_N_LEDS; led++) + { + if (led < bounds) + { + LED_RING_set (led, 0, brightness, 0); + } + else + { + LED_RING_set (led, 0, 0, 0); + } + } + + _scope_ring_enable (true); + + if (brightness > 0) + { + brightness--; + } + else if (brightness == 0) + { + _scope_ring_state_finished = true; + brightness = 100; + } + } + break; + + case SCOPE_RING_STATE_BLE_ENABLED: + if (!_scope_ring_state_finished) + { + static uint8_t bounds = 0; + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led = 0; led < bounds; led++) + { + LED_RING_set (led, 0, 0, 50); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + + if (bounds == (_SCOPE_RING_N_LEDS - 1)) + { + /* Force finish flag. */ + _scope_ring_state_finished = true; + + bounds = 0; + } + else + { + bounds++; + } + } + break; + + case SCOPE_RING_STATE_BLE_DISABLED: + if (!_scope_ring_state_finished) + { + static uint8_t bounds = (_SCOPE_RING_N_LEDS - 1); + + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + for (led = (_SCOPE_RING_N_LEDS - 1); led > bounds; led--) + { + LED_RING_set (led, 0, 0, 50); + } + + /* Enable FET for power to ring prior to sending LED ring data. */ + _scope_ring_enable (true); + + if (bounds == 0) + { + /* Force finish flag. */ + _scope_ring_state_finished = true; + + bounds = (_SCOPE_RING_N_LEDS - 1); + } + else + { + bounds--; + } + } + break; + } + + _scope_ring_timer_cnt += SCOPE_RING_TASK_PERIOD_MS; + + /* Feed watchdog at end of loop. */ + WDT_feed (); + + k_msleep (SCOPE_RING_TASK_PERIOD_MS); + } +} + +static void _scope_ring_level_process (float angle, SCOPE_RING_level_option_e option) +{ + uint8_t led_idx; + uint8_t multiplier; + RGB_s level; + RGB_s indicators; + + /* Negating angle turns on opposite LED. Indicating to cant the other direction to level. */ + // angle = -angle; + + /* Calculate scaled level color and brightness. SCOPE_RING_BRIGHTNESS_STEPS + 1 extra step for off. */ + level = RGB_gamma_corrected (_scope_ring_config.level_color, + _scope_ring_config.level_brightness, + (SCOPE_RING_BRIGHTNESS_STEPS + 1), + RGB_BRIGHTNESS_MAX, + SCOPE_RING_BRIGHTNESS_GAMMA_CF); + + /* Calculate scaled indicators color and brightness. SCOPE_RING_BRIGHTNESS_STEPS + 1 extra step for off. */ + indicators = RGB_gamma_corrected (_scope_ring_config.indicators_color, + _scope_ring_config.indicators_brightness, + (SCOPE_RING_BRIGHTNESS_STEPS + 1), + RGB_BRIGHTNESS_MAX, + SCOPE_RING_BRIGHTNESS_GAMMA_CF); + + // angle = _scope_ring_angle_hysteresis_get (angle); + + angle = _scope_ring_level_viscosity_get (angle, _scope_ring_config.level_viscosity); + + switch (option) + { + case SCOPE_RING_LEVEL_OPTION_0: + /* Check if angle is within desired window. */ + if (angle >= -_scope_ring_config.level_sensitivity && + angle <= _scope_ring_config.level_sensitivity) + { + if (_scope_ring_config.level_brightness == 0) + { + _scope_ring_background_set (); + } + else + { + _scope_ring_mirrored_set (0, level.r, level.g, level.b); + } + } + else if (angle < 0) + { + multiplier = _SCOPE_RING_N_LEDS - _SCOPE_RING_L_INDICATOR_IDX; + + for (led_idx = _SCOPE_RING_L_INDICATOR_IDX; led_idx < _SCOPE_RING_N_LEDS; led_idx++) + { + if (angle <= (-_scope_ring_config.level_sensitivity * multiplier--)) + { + _scope_ring_mirrored_set (led_idx, indicators.r, indicators.g, indicators.b); + return; + } + } + } + else + { + multiplier = _SCOPE_RING_R_INDICATOR_IDX; + + for (led_idx = _SCOPE_RING_R_INDICATOR_IDX; led_idx > 0 ; led_idx--) + { + if (angle >= (_scope_ring_config.level_sensitivity * multiplier--)) + { + _scope_ring_mirrored_set (led_idx, indicators.r, indicators.g, indicators.b); + return; + } + } + } + break; + + case SCOPE_RING_LEVEL_OPTION_1: + /* Check if angle is within desired window. */ + if (angle >= -_scope_ring_config.level_sensitivity && + angle <= _scope_ring_config.level_sensitivity) + { + if (_scope_ring_config.level_brightness == 0) + { + _scope_ring_background_set (); + } + else + { + /* Set both left and right leds when level. */ + LED_RING_set (_SCOPE_RING_R_INDICATOR_IDX, level.r, level.g, level.b); + LED_RING_set (_SCOPE_RING_L_INDICATOR_IDX, level.r, level.g, level.b); + } + } + else if (angle < 0) + { + /* Set left LED if we need to cant left for level. */ + LED_RING_set (_SCOPE_RING_L_INDICATOR_IDX, indicators.r, indicators.g, indicators.b); + } + else + { + /* Set right LED if we need to cant right for level. */ + LED_RING_set (_SCOPE_RING_R_INDICATOR_IDX, indicators.r, indicators.g, indicators.b); + } + break; + + case SCOPE_RING_LEVEL_OPTION_2: + /* Check if angle is within desired window. */ + if (angle >= -_scope_ring_config.level_sensitivity && + angle <= _scope_ring_config.level_sensitivity) + { + if (_scope_ring_config.level_brightness == 0) + { + _scope_ring_background_set (); + } + else + { + /* Set both left and right leds when level. */ + LED_RING_set (_SCOPE_RING_R_INDICATOR_IDX, level.r, level.g, level.b); + LED_RING_set (_SCOPE_RING_L_INDICATOR_IDX, level.r, level.g, level.b); + + // LED_RING_set (_SCOPE_RING_R_INDICATOR_IDX-1, level.r, level.g, level.b); + // LED_RING_set (_SCOPE_RING_R_INDICATOR_IDX+1, level.r, level.g, level.b); + + // LED_RING_set (_SCOPE_RING_L_INDICATOR_IDX-1, level.r, level.g, level.b); + // LED_RING_set (_SCOPE_RING_L_INDICATOR_IDX+1, level.r, level.g, level.b); + } + } + else if (angle < 0) + { + multiplier = _SCOPE_RING_N_LEDS - _SCOPE_RING_L_INDICATOR_IDX - 2; + + for (led_idx = (_SCOPE_RING_N_LEDS - 2); led_idx > _SCOPE_RING_L_INDICATOR_IDX; led_idx--) + { + if (angle <= (-_scope_ring_config.level_sensitivity * multiplier--)) + { + LED_RING_set (led_idx, indicators.r, indicators.g, indicators.b); + LED_RING_set ((led_idx + (_SCOPE_RING_N_LEDS / 2)) % _SCOPE_RING_N_LEDS, indicators.r, indicators.g, indicators.b); + return; + } + } + } + else + { + multiplier = _SCOPE_RING_R_INDICATOR_IDX - 2; + + for (led_idx = 2; led_idx < _SCOPE_RING_R_INDICATOR_IDX; led_idx++) + { + if (angle >= (_scope_ring_config.level_sensitivity * multiplier--)) + { + LED_RING_set (led_idx, indicators.r, indicators.g, indicators.b); + LED_RING_set (led_idx + (_SCOPE_RING_N_LEDS / 2), indicators.r, indicators.g, indicators.b); + return; + } + } + } + break; + } +} + +static void _scope_ring_background_set (void) +{ + uint8_t led_idx; + /* Background LEDs never need to be full brightness, we scale back max to 40 instead of 255. + * With max of 40, we use a different gamma value of 1.2. + * SCOPE_RING_BRIGHTNESS_STEPS + 1 extra step for off. */ + RGB_s ring = RGB_gamma_corrected (_scope_ring_config.background_color, + _scope_ring_config.background_brightness, + (SCOPE_RING_BRIGHTNESS_STEPS + 1), + 40, + 1.2); + + /* Turn on all leds to ring color. */ + for (led_idx = 0; led_idx < _SCOPE_RING_N_LEDS; led_idx++) + { + LED_RING_set (led_idx, ring.r, ring.g, ring.b); + } +} + +static void _scope_ring_mirrored_set (uint8_t led, uint8_t r, uint8_t g, uint8_t b) +{ + /* Set top half. */ + LED_RING_set (led, r, g, b); + + /* Check if we are on left half. */ + if (led > (_SCOPE_RING_N_LEDS / 2)) + { + /* Also set mirrored side. */ + LED_RING_set ((_SCOPE_RING_N_LEDS - led) + (_SCOPE_RING_N_LEDS / 2), r, g, b); + } + /* Check if we are on right half. */ + else + { + /* Also set mirrored side. */ + LED_RING_set (((_SCOPE_RING_N_LEDS / 2) - led), r, g, b); + } +} + +static void _scope_ring_enable (bool en) +{ + if (!en) + { + /* If disabled, send data to turn off all LEDs before removing power. */ + if (LED_RING_clear () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to clear LED ring.\r\n"); + } + + if (LED_RING_update () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to update LED ring.\r\n"); + } + + /* Enable / disable FET gating LED ring power. */ + LED_RING_enable (false); + } + else + { + /* Enable FET gating LED ring power before sending pattern. */ + LED_RING_enable (true); + + if (LED_RING_update () != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to update LED ring.\r\n"); + } + } +} + +static void _scope_ring_config_read (void) +{ + ERRNO_e err; + SCOPE_RING_config_s cfg; + + err = FS_open_or_create (SCOPE_RING_FILENAME, &_scope_ring_file, sizeof (SCOPE_RING_config_s), FS_FILE_DEPTH_DEFAULT); + if (err == ERRNO_SUCCESS) + { + /* Read file. */ + err = FS_read (_scope_ring_file, (uint8_t *) &cfg, 0, sizeof (SCOPE_RING_config_s)); + + /* Check file entry is valid. */ + if (err != ERRNO_SUCCESS) + { + /* Write defaults to file. */ + _scope_ring_config_write (); + return; + } + + /* Check if file contents are valid. */ + if (cfg.indicators_color >= RGB_COLOR_INVALID || + cfg.level_color >= RGB_COLOR_INVALID || + cfg.background_color >= RGB_COLOR_INVALID || + cfg.indicators_brightness > SCOPE_RING_BRIGHTNESS_STEPS || + cfg.level_brightness > SCOPE_RING_BRIGHTNESS_STEPS || + cfg.background_brightness > SCOPE_RING_BRIGHTNESS_STEPS || + cfg.level_option >= SCOPE_RING_LEVEL_OPTION_COUNT || + cfg.level_sensitivity <= (float) 0.0 || + cfg.level_viscosity <= (float) 0.0) + { + /* If not valid, write current configuration to file. */ + _scope_ring_config_write (); + return; + } + + /* Update scope ring configuration to what was read from flash and validated. */ + _scope_ring_config = cfg; + } + else if (err == ERRNO_SIZE_MISMATCH) + { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete (SCOPE_RING_FILENAME); + + if (err != ERRNO_SUCCESS) + { + /* Something else is going on... */ + PRINT (ERROR, "Failure to delete old scope ring file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (SCOPE_RING_FILENAME, &_scope_ring_file, sizeof (SCOPE_RING_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failure to create scope ring configuration file.\r\n"); + } + else + { + PRINT (INFO, "New scope ring file created successfully\r\n"); + } + + /* Write default config to file. */ + _scope_ring_config_write (); + } + else + { + /* Write defaults to file. */ + _scope_ring_config_write (); + } +} + +static void _scope_ring_config_write (void) +{ + if (FS_write (_scope_ring_file, (uint8_t *) &_scope_ring_config) != ERRNO_SUCCESS) + { + PRINT (ERROR, "Failed to write scope ring configuration to file.\r\n"); + } +} + +static float _scope_ring_angle_hysteresis_get (float angle) +{ + static float prev_angle = 0.0; + + /* Increasing. */ + if (angle > prev_angle) + { + if (angle > prev_angle + SCOPE_RING_HYS_ANGLE_THRESHOLD) + { + /* Update previous angle. */ + prev_angle = angle; + } + else + { + angle = prev_angle; + } + } + /* Decreasing. */ + else if (angle < prev_angle) + { + if (angle < prev_angle - SCOPE_RING_HYS_ANGLE_THRESHOLD) + { + /* Update previous angle. */ + prev_angle = angle; + } + else + { + angle = prev_angle; + } + } + + return angle; +} + +static float _scope_ring_level_viscosity_get (float angle, float alpha) +{ + static float previous_angle = 0.0; + + previous_angle = alpha * angle + (1.0f - alpha) * previous_angle; + + return previous_angle; +} diff --git a/src/app/scope_ring.h b/src/app/scope_ring.h new file mode 100644 index 0000000..87018e0 --- /dev/null +++ b/src/app/scope_ring.h @@ -0,0 +1,162 @@ +/** + * @file scope_ring.h + * @brief Scope ring module + * @author Kenny Tran + * @date Apr 05 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef SCOPE_RING_H_ +#define SCOPE_RING_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/rgb.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define SCOPE_RING_TASK_PERIOD_MS (10) +#define SCOPE_RING_STACK_SIZE (2048) +#define SCOPE_RING_THREAD_PRIORITY (11) + +#define SCOPE_RING_BRIGHTNESS_STEPS (10) +#define SCOPE_RING_BRIGHTNESS_GAMMA_CF (1.6) + +#define SCOPE_RING_BRIGHTNESS_STEP_DEFAULT (5) +#define SCOPE_RING_INDICATORS_COLOR_DEFAULT (RGB_COLOR_RED) +#define SCOPE_RING_LEVEL_COLOR_DEFAULT (RGB_COLOR_GREEN) +#define SCOPE_RING_BACKGROUND_COLOR_DEFAULT (RGB_COLOR_WHITE) +#define SCOPE_RING_LEVEL_VISCOSITY_DEFAULT (0.03) +#define SCOPE_RING_LEVEL_SENSITIVITY_DEFAULT (0.5) +#define SCOPE_RING_HYS_ANGLE_THRESHOLD (0.25) + +#define SCOPE_RING_LEVEL_OPTION_DEFAULT (SCOPE_RING_LEVEL_OPTION_0) + +#define SCOPE_RING_LEVEL_SENSITIVITY_MIN (0.25) +#define SCOPE_RING_LEVEL_SENSITIVITY_MAX (3.0) + +#define SCOPE_RING_FILENAME ("scope_ring") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum SCOPE_RING_led +{ + SCOPE_RING_LED_BACKGROUND = 0, + SCOPE_RING_LED_INDICATORS, + SCOPE_RING_LED_LEVEL +} SCOPE_RING_led_e; + +typedef enum SCOPE_RING_level_option +{ + SCOPE_RING_LEVEL_OPTION_0 = 0, + SCOPE_RING_LEVEL_OPTION_1, + SCOPE_RING_LEVEL_OPTION_2, + + SCOPE_RING_LEVEL_OPTION_COUNT, + SCOPE_RING_LEVEL_OPTION_INVALID = SCOPE_RING_LEVEL_OPTION_COUNT +} SCOPE_RING_level_option_e; + +typedef enum SCOPE_RING_state +{ + SCOPE_RING_STATE_OFF = 0, + SCOPE_RING_STATE_ON, + + SCOPE_RING_STATE_SET_FLAT, + SCOPE_RING_STATE_FLAT_CALIBRATION, + SCOPE_RING_STATE_SET_BACK, + SCOPE_RING_STATE_BACK_CALIBRATION, + SCOPE_RING_STATE_SET_FRONT, + SCOPE_RING_STATE_FRONT_CALIBRATION, + + SCOPE_RING_STATE_CONFIG_LEVEL_COLOR, + SCOPE_RING_STATE_CONFIG_LEVEL_BRIGHTNESS, + SCOPE_RING_STATE_CONFIG_INDICATORS_COLOR, + SCOPE_RING_STATE_CONFIG_INDICATORS_BRIGHTNESS, + SCOPE_RING_STATE_CONFIG_BACKGROUND_COLOR, + SCOPE_RING_STATE_CONFIG_BACKGROUND_BRIGHTNESS, + SCOPE_RING_STATE_CONFIG_LEVEL_SENSITIVITY, + SCOPE_RING_STATE_CONFIG_LEVEL_OPTION, + + SCOPE_RING_STATE_RAINBOW, + SCOPE_RING_STATE_CHARGING, + SCOPE_RING_STATE_CHARGER_CONNECTED, + SCOPE_RING_STATE_CHARGER_DISCONNECTED, + SCOPE_RING_STATE_POWER_ON, + SCOPE_RING_STATE_POWER_OFF, + SCOPE_RING_STATE_BLE_ENABLED, + SCOPE_RING_STATE_BLE_DISABLED, + + SCOPE_RING_STATE_INVALID +} SCOPE_RING_state_e; + +typedef struct SCOPE_RING_config +{ + RGB_color_e indicators_color : 8; + uint8_t indicators_brightness : 8; + RGB_color_e level_color : 8; + uint8_t level_brightness : 8; + RGB_color_e background_color : 8; + uint8_t background_brightness : 8; + uint8_t level_option : 4; + float level_viscosity; + float level_sensitivity; +} __attribute__((packed, aligned(1))) SCOPE_RING_config_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void SCOPE_RING_init (void); + +/* Set duration_ms to 0 to run indefinitely. */ +void SCOPE_RING_state_set (SCOPE_RING_state_e state, uint32_t duration_ms); + +SCOPE_RING_state_e SCOPE_RING_state_get (bool *p_finshed); + +void SCOPE_RING_color_set (SCOPE_RING_led_e led, RGB_color_e color); + +void SCOPE_RING_brightness_set (SCOPE_RING_led_e led, uint8_t step, bool save_settings); + +void SCOPE_RING_battery_indicator_set (float percentage); + +void SCOPE_RING_level_option_set (SCOPE_RING_level_option_e option); + +void SCOPE_RING_level_viscosity_set (float alpha); + +void SCOPE_RING_level_sensitivity_set (float sensitivity); + +SCOPE_RING_config_s SCOPE_RING_config_get (void); + +void SCOPE_RING_task_suspend (void); + +void SCOPE_RING_task_resume (void); + +void SCOPE_RING_task_install (void); + +void SCOPE_RING_cli_cmd (int32_t argc, char **argv); + + +#endif /* SCOPE_RING_H_ */ diff --git a/src/app/shot_capture.c b/src/app/shot_capture.c new file mode 100644 index 0000000..b8712d6 --- /dev/null +++ b/src/app/shot_capture.c @@ -0,0 +1,1001 @@ +/** + * @file shot_capture.c + * @brief Shot Capture module + * @author Eric Xu + * @date Oct 15 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include +#include + +#include "shot_capture.h" + +#include "../app/shot_storage.h" +#include "../app/ble.h" + +#include "../lib/debug.h" +#include "../lib/fs.h" +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../bsp/als.h" +#include "../bsp/imu.h" +#include "../bsp/imu_reg.h" +#include "../bsp/wdt.h" + +#include "../wrappers/i2c.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +K_MUTEX_DEFINE(shot_data_mutex); /* Protect shared shot_data struct */ +K_MUTEX_DEFINE(FIFO_ring_buf_mutex); /* Protect shared FIFO ring buffer struct */ +K_MUTEX_DEFINE(stat_mutex); /* Protect debug statistic variables */ + +volatile FIFO_ring_buf_s FIFO_ring_buf = {.buf = NULL, + .write_ptr = 0, + .read_ptr = 0, + .loop_ctr = 0}; + +volatile shot_data_status_s shot_data = {.start = 0, + .location = 0, + .words_per_sample_set = WORDS_PER_SAMPLE_SET, + .bytes_per_sample_set = BYTES_PER_SAMPLE_SET, + .size = SHOTDATA_SIZE_IN_BYTES_DEFAULT, + .bytes_after_shot = BYTES_AFTER_SHOT_DEFAULT, + .marked = false, + .ready = false, + .saved = false}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static FS_file_t _shot_capture_file; +static SHOT_CAPTURE_config_s _shot_capture_config = { + .shot_duration = SHOTDATA_DURATION_DEFAULT, + .shot_location = SHOTDATA_LOCATION_DEFAULT, + .fs_g = LSM6DS3TR_C_FS_G_ABSTR_250dps, + .odr_g = LSM6DS3TR_C_HZ_TO_ODR_G(GYRO_ODR_DEFAULT_HZ), + .fs_xl = LSM6DS3TR_C_FS_XL_2g, + .odr_xl = LSM6DS3TR_C_HZ_TO_ODR_XL(ACCEL_ODR_DEFAULT_HZ), + .tap_ths = 25, + .quiet_time = LSM6DS3TR_C_QUIET_4_OVER_ODR_XL, + .shock_time = LSM6DS3TR_C_SHOCK_16_OVER_ODR_XL, + .user_shot_cnt = 0, + .shot_cnt = 0, + .shot_capture_en = 1 +}; +static atomic_t _shot_capture_is_enabled = ATOMIC_INIT(0); +static atomic_t _start_capture = ATOMIC_INIT(0); + +static uint32_t _user_shot_cnt_rt; +static uint32_t _shot_cnt_rt; +static uint8_t _shot_capture_en_rt; +static LSM6DS3TR_C_FS_G_ABSTR_e _fs_g_orig; +static LSM6DS3TR_C_ODR_G_e _odr_g_orig; +static LSM6DS3TR_C_FS_XL_e _fs_xl_orig; +static LSM6DS3TR_C_ODR_XL_e _odr_xl_orig; +K_MUTEX_DEFINE(shot_capture_enable_mutex); /* Serialize shot capture enable/disable process */ +/* Idle fence for shot capture task */ +K_SEM_DEFINE(shot_capture_idle_sem, 0, 1); +static atomic_t _shot_capture_idle_requested = ATOMIC_INIT(0); + +atomic_t _shot_storage_buf_is_in_use = ATOMIC_INIT(0); + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +K_THREAD_STACK_DEFINE(_shot_capture_task_stack, SHOT_CAPTURE_TASK_STACK_SIZE); + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _shot_capture_task(void *p1, void *p2, void *p3); +static ERRNO_e _read_shot_data_into_ring_buf(uint32_t fifo_unread_sample_set_in_bytes, bool end); +static ERRNO_e _read_FIFO_into_ring_buf(int32_t overshoot, uint32_t bytes_to_read); +static void _print_shot_data(void); +static void _shot_capture_config_read(void); +static ERRNO_e _shot_capture_config_write(void); +static void _shot_capture_shotdata_param_cal(void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ +void SHOT_CAPTURE_init(void) +{ + PRINT(INFO, "Initializing SHOT_CAPTURE...\r\n"); + + /* Initialize IMU. */ + if (IMU_init() < ERRNO_SUCCESS) { + PRINT(CRITICAL, "Failure to initialize IMU.\r\n"); + } + + /* read shot capture config from nvm */ + _shot_capture_config_read(); + + _user_shot_cnt_rt = _shot_capture_config.user_shot_cnt; + _shot_cnt_rt = _shot_capture_config.shot_cnt; + _shot_capture_en_rt = _shot_capture_config.shot_capture_en; +} + +void SHOT_CAPTURE_tap_ths_set(uint8_t tap_ths) +{ + if (tap_ths > 31) { + PRINT(ERROR, "Invalid tap threshold. Use default tap threshold.\r\n"); + if (_shot_capture_config.tap_ths != 4) { + _shot_capture_config.tap_ths = 4; + _shot_capture_config_write(); + } + } else { + if (_shot_capture_config.tap_ths != tap_ths) { + _shot_capture_config.tap_ths = tap_ths; + _shot_capture_config_write(); + } + } + + if (atomic_get(&_shot_capture_is_enabled)) { + /* set tap threshold to be tap_ths * fs_xl/2^5 */ + if (IMU_set_tap_ths(_shot_capture_config.tap_ths) != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to set tap threshold in IMU.\r\n"); + } + } +} + +void SHOT_CAPTURE_quiet_time_set(LSM6DS3TR_C_QUIET_e quiet_time) +{ + if (quiet_time > LSM6DS3TR_C_QUIET_12_OVER_ODR_XL) { + PRINT(ERROR, "Invalid quiet time. Use default quiet time.\r\n"); + if(_shot_capture_config.quiet_time != LSM6DS3TR_C_QUIET_4_OVER_ODR_XL) { + _shot_capture_config.quiet_time = LSM6DS3TR_C_QUIET_4_OVER_ODR_XL; + _shot_capture_config_write(); + } + } else { + if (_shot_capture_config.quiet_time != quiet_time) { + _shot_capture_config.quiet_time = quiet_time; + _shot_capture_config_write(); + } + } + + if (atomic_get(&_shot_capture_is_enabled)) { + /* set the quiet time */ + if (IMU_set_quiet(_shot_capture_config.quiet_time) != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to set quiet time in IMU.\r\n"); + } + } +} + +void SHOT_CAPTURE_shock_time_set(LSM6DS3TR_C_SHOCK_e shock_time) +{ + if (shock_time > LSM6DS3TR_C_SHOCK_24_OVER_ODR_XL) { + PRINT(ERROR, "Invalid shock time. Use default shock time.\r\n"); + if(_shot_capture_config.shock_time != LSM6DS3TR_C_SHOCK_16_OVER_ODR_XL) { + _shot_capture_config.shock_time = LSM6DS3TR_C_SHOCK_16_OVER_ODR_XL; + _shot_capture_config_write(); + } + } else { + if (_shot_capture_config.shock_time != shock_time) { + _shot_capture_config.shock_time = shock_time; + _shot_capture_config_write(); + } + } + + if (atomic_get(&_shot_capture_is_enabled)) { + /* set the shock time */ + if (IMU_set_shock(_shot_capture_config.shock_time) != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to set shock time in IMU.\r\n"); + } + } +} + +void SHOT_CAPTURE_shotdata_duration_location_set(uint8_t duration, uint8_t location) +{ + if (duration < SHOTDATA_DURATION_MIN || duration > SHOTDATA_DURATION_MAX) { + PRINT(ERROR, "Invalid shot data duration. Use default shotdata duration.\r\n"); + if (_shot_capture_config.shot_duration != SHOTDATA_DURATION_DEFAULT) { + _shot_capture_config.shot_duration = SHOTDATA_DURATION_DEFAULT; + _shot_capture_config.shot_location = SHOTDATA_DURATION_DEFAULT / 2; + _shot_capture_config_write(); + _shot_capture_shotdata_param_cal(); + } + } else { + if (_shot_capture_config.shot_duration != duration || _shot_capture_config.shot_location != location) { + _shot_capture_config.shot_duration = duration; + if (location < 0 || location > duration - 1) { + PRINT(ERROR, "Invalid shot data location. Set to middle of shotdata duration.\r\n"); + _shot_capture_config.shot_location = duration / 2; + } else { + _shot_capture_config.shot_location = location; + } + _shot_capture_config_write(); + _shot_capture_shotdata_param_cal(); + } + } +} + +SHOT_CAPTURE_config_s *SHOT_CAPTURE_config_get(void) +{ + return &_shot_capture_config; +} + +ERRNO_e SHOT_CAPTURE_enable(bool enable) +{ + ERRNO_e err = ERRNO_SUCCESS; + uint8_t reg_val = 0; + uint8_t *p_data; + uint32_t size = 0; + + k_mutex_lock(&shot_capture_enable_mutex, K_FOREVER); + + if (enable && !atomic_get(&_shot_capture_is_enabled) && _shot_capture_config.shot_capture_en && !atomic_get(&_shot_storage_buf_is_in_use)) { + k_mutex_lock(&shot_data_mutex, K_FOREVER); + size = shot_data.size; + k_mutex_unlock(&shot_data_mutex); + err = SHOT_STORAGE_get_buf(size, &p_data); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Cannot enable shot capture, shot storage buffer allocation failed!\r\n"); + goto out; + } else { + k_mutex_lock(&FIFO_ring_buf_mutex, K_FOREVER); + FIFO_ring_buf.buf = p_data; + FIFO_ring_buf.write_ptr = 0; + k_mutex_unlock(&FIFO_ring_buf_mutex); + } + + /* enable shot capture interrupt mode */ + IMU_set_shot_capture_mode(true); + + /* save the original settings for accel and gyro */ + err = IMU_get_fs_xl(&_fs_xl_orig); + + if (err == ERRNO_SUCCESS) { + err = IMU_get_odr_xl(&_odr_xl_orig); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_get_fs_g(&_fs_g_orig); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_get_odr_g(&_odr_g_orig); + } + + /* set accel and gyro to desired settings for shot capture */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_fs_xl(_shot_capture_config.fs_xl); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_set_odr_xl(_shot_capture_config.odr_xl); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_set_fs_g(_shot_capture_config.fs_g); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_set_odr_g(_shot_capture_config.odr_g); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_fifo_th_set(1800); + } + + /* set tap threshold to be 4 * fs_xl/2^5 which is 250mg when fs_xl = 2g*/ + if (err == ERRNO_SUCCESS) { + err = IMU_set_tap_ths(_shot_capture_config.tap_ths); + } + + /* set the quiet time to be 4 * 1/ODR_XL + * in the double-tap case, the quiet time window is the time during which a second tap is + * ignored after the detection of the first tap (for debounce). When the lir bit is set to 0, the quiet time + * also defines the length of the interrupt pulse (in both single and double-tap cases). + * since we set lir to 1, the quiet time here has no effect. + */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_quiet(_shot_capture_config.quiet_time); + } + + /* set the shock time + * the shock time window defines the maximum duration of the overthreshold event: + * the acceleration must return below the threshold before the shock window has expired, + * otherwise the tap event is not detected. + */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_shock(_shot_capture_config.shock_time); + } + + /* enable interrrupt for tap event */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_interrupts_enable(LSM6DS3TR_C_INTERRUPTS_ENABLE); + } + + /* enable tap detection on x direction of imu*/ + if (err == ERRNO_SUCCESS) { + err = IMU_set_tap_x_en(LSM6DS3TR_C_TAP_X_EN_ENABLED); + } + + /* enable latched interrupt*/ + if (err == ERRNO_SUCCESS) { + err = IMU_set_lir(LSM6DS3TR_C_LIR_ENABLED); + } + + /** + * Single-tap interrupt driven to INT1 pin + * According to AN5130 In order to enable the latch feature on the single-tap interrupt + * signal, both the LIR bit and the INT1_DOUBLE_TAP bit of MD1_CFG have to be set to 1: + * the interrupt is kept active until the TAP_SRC register is read. + */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_int1_single_tap(LSM6DS3TR_C_INT1_SINGLE_TAP_ENABLED); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_set_int1_double_tap(LSM6DS3TR_C_INT1_DOUBLE_TAP_ENABLED); + } + + if (err == ERRNO_SUCCESS) { + atomic_set(&_shot_capture_is_enabled, 1); + } + + PRINT(INFO, "SHOT_CAPTURE enabled.\r\n"); + } else if ((!enable || !_shot_capture_config.shot_capture_en || atomic_get(&_shot_storage_buf_is_in_use)) && atomic_get(&_shot_capture_is_enabled)) { + /* Disable single-tap interrupt driven to INT1 pin */ + err = I2C_bytes_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_MD1_CFG, ®_val, 1); + + /* Disable interrupts, lir, and tap detection on X, Y, Z axis */ + if (err == ERRNO_SUCCESS) { + err = I2C_bytes_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_TAP_CFG, ®_val, 1); + } + + /* reads tap source register to clear the interrupt source */ + if (err == ERRNO_SUCCESS) { + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_TAP_SRC, ®_val, 1); + } + + /* disable IMU fifo and FTH interrupt is disconnected to INT1 pin*/ + if (err == ERRNO_SUCCESS) { + err = IMU_fifo_th_set(0); + } + + /* if shot_capture_sem is non zero reset it because _shot_capture_task no longer needs to run */ + k_sem_reset(&shot_capture_sem); + + /* restore original accel and gyro settings */ + if (err == ERRNO_SUCCESS && _fs_xl_orig != _shot_capture_config.fs_xl) { + err = IMU_set_fs_xl(_fs_xl_orig); + } + + if (err == ERRNO_SUCCESS && _odr_xl_orig != _shot_capture_config.odr_xl) { + err = IMU_set_odr_xl(_odr_xl_orig); + } + + if (err == ERRNO_SUCCESS && _fs_g_orig != _shot_capture_config.fs_g) { + err = IMU_set_fs_g(_fs_g_orig); + } + + if (err == ERRNO_SUCCESS && _odr_g_orig != _shot_capture_config.odr_g) { + err = IMU_set_odr_g(_odr_g_orig); + } + + k_mutex_lock(&FIFO_ring_buf_mutex, K_FOREVER); + FIFO_ring_buf.buf = NULL; + k_mutex_unlock(&FIFO_ring_buf_mutex); + + /* disable shot capture interrupt mode */ + IMU_set_shot_capture_mode(false); + + atomic_set(&_shot_capture_is_enabled, 0); + + PRINT(INFO, "SHOT_CAPTURE disabled.\r\n"); + } else { + err = ERRNO_SUCCESS; + goto out; + } + +out: + k_mutex_unlock(&shot_capture_enable_mutex); + return err; +} + +bool SHOT_CAPTURE_is_enabled(void) +{ + return atomic_get(&_shot_capture_is_enabled) ? true : false; +} + +ERRNO_e SHOT_CAPTURE_save_shot_cnt(void) +{ + ERRNO_e err = ERRNO_SUCCESS; + + if (_user_shot_cnt_rt != _shot_capture_config.user_shot_cnt || + _shot_cnt_rt != _shot_capture_config.shot_cnt || + _shot_capture_en_rt != _shot_capture_config.shot_capture_en) { + //_user_shot_cnt_rt = _shot_capture_config.user_shot_cnt; + //_shot_cnt_rt = _shot_capture_config.shot_cnt; + //_shot_capture_en_rt = _shot_capture_config.shot_capture_en; + err = _shot_capture_config_write(); + } + + return err; +} + +void SHOT_CAPTURE_task_install(void) +{ + static k_tid_t id; + static struct k_thread data; + + id = k_thread_create(&data, + _shot_capture_task_stack, + SHOT_CAPTURE_TASK_STACK_SIZE, + _shot_capture_task, + NULL, + NULL, + NULL, + SHOT_CAPTURE_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&data, "SHOT_CAPTURE_task"); +} + +void SHOT_CAPTURE_task_disable_and_wait(void) +{ + /* 1. Disable capture (serialized with FSM) */ + atomic_set(&_shot_storage_buf_is_in_use, 1); + SHOT_CAPTURE_enable(false); + + /* 2. Request idle confirmation from _shot_capture_task */ + k_sem_reset(&shot_capture_idle_sem); + atomic_set(&_shot_capture_idle_requested, 1); + + /* + * 3. Wake the capture task so it can see the idle request. + * If it's already waiting on shot_capture_sem, this + * lets it loop once and post the idle confirmation. + */ + k_sem_give(&shot_capture_sem); + + /* 4. Wait for confirmation that capture task is idle */ + k_sem_take(&shot_capture_idle_sem, K_FOREVER); + atomic_set(&_shot_capture_idle_requested, 0); +} + +void SHOT_CAPTURE_task_resume(void) +{ + atomic_set(&_shot_storage_buf_is_in_use, 0); +} + +void SHOT_CAPTURE_cli_cmd(int32_t argc, char **argv) +{ + if (argc == 0) { + PRINT (INFO, "SHOT CAPTURE\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "shot capture enabled: %s\r\n", SHOT_CAPTURE_is_enabled() ? "true" : "false"); + } else if (argc == 1) { + if (!strcmp (argv[0], "-ps") || !strcmp (argv[0], "--printshot")) { + _print_shot_data(); + } else if (!strcmp (argv[0], "-sc") || !strcmp (argv[0], "--startcapture")) { + atomic_set(&_start_capture, 1); + PRINT(INFO, "Manual start capture triggered.\r\n"); + } else if (!strcmp (argv[0], "-tw") || !strcmp (argv[0], "--testwrite")) { + uint8_t *p_data; + uint32_t test_size = 1000; + uint32_t write_ptr_offset = 700; /* 70% into buffer to test wraparound */ + ERRNO_e err; + + /* 1. Get shared buffer from shot_storage */ + err = SHOT_STORAGE_get_buf(test_size, &p_data); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to get buffer: %d\r\n", err); + return; + } + + /* 2. Fill buffer with sequential data starting at write_ptr offset. + * Stream cursor reads from write_ptr forward with wrapping: + * buf[700]=0, buf[701]=1, ..., buf[999]=299, buf[0]=300, ..., buf[699]=699 + */ + for (uint32_t i = 0; i < test_size; i++) { + uint32_t buf_pos = (write_ptr_offset + i) % test_size; + p_data[buf_pos] = (uint8_t)(i & 0xFF); + } + + /* 3. Set up shot_data so file_create picks up the right values */ + k_mutex_lock(&shot_data_mutex, K_FOREVER); + shot_data.start = write_ptr_offset; + shot_data.size = test_size; + shot_data.time = k_uptime_get_32(); + shot_data.ambient_light_intensity = 12345; + shot_data.marked = true; + shot_data.ready = true; + shot_data.saved = false; + k_mutex_unlock(&shot_data_mutex); + + /* 4. Create file and write to flash directly (synchronous) */ + err = SHOT_STORAGE_file_create(); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to create file: %d\r\n", err); + return; + } + SHOT_STORAGE_write(); + + /* 5. Fill buffer with new sequential data starting at write_ptr offset. */ + for (uint32_t i = 0; i < test_size; i++) { + uint32_t buf_pos = (write_ptr_offset + i) % test_size; + p_data[buf_pos] = (uint8_t)((i+10) & 0xFF); + } + + /* 6. Reset shot_data flags */ + k_mutex_lock(&shot_data_mutex, K_FOREVER); + shot_data.marked = false; + shot_data.ready = false; + shot_data.saved = true; + k_mutex_unlock(&shot_data_mutex); + + PRINT(INFO, "Test write complete!\r\n"); + PRINT(INFO, " Data size: %u bytes\r\n", test_size); + PRINT(INFO, " Write ptr (start): %u (tests buffer wraparound)\r\n", write_ptr_offset); + PRINT(INFO, " Expected content after header: 0,1,2,...,255,0,1,...,187 (repeating mod 256)\r\n"); + PRINT(INFO, " Run 'shot_storage --read' to verify.\r\n"); + } else { + goto USAGE; + } + } else if (argc == 2) { + if (!strcmp(argv[0], "-e") || !strcmp(argv[0], "--enable")) { + if (!strcmp(argv[1], "true")) { + SHOT_CAPTURE_enable(true); + } else if (!strcmp(argv[1], "false")) { + SHOT_CAPTURE_enable(false); + } else { + goto USAGE; + } + } else if (!strcmp(argv[0], "-ss") || !strcmp(argv[0], "--setshock")) { + if (!strcmp(argv[1], "0")) { + SHOT_CAPTURE_shock_time_set(LSM6DS3TR_C_SHOCK_4_OVER_ODR_XL); + } else if (!strcmp(argv[1], "1")) { + SHOT_CAPTURE_shock_time_set(LSM6DS3TR_C_SHOCK_8_OVER_ODR_XL); + } else if (!strcmp(argv[1], "2")) { + SHOT_CAPTURE_shock_time_set(LSM6DS3TR_C_SHOCK_16_OVER_ODR_XL); + } else if (!strcmp(argv[1], "3")) { + SHOT_CAPTURE_shock_time_set(LSM6DS3TR_C_SHOCK_24_OVER_ODR_XL); + } else { + goto USAGE; + } + } else if (!strcmp(argv[0], "-sq") || !strcmp(argv[0], "--setquiet")) { + if (!strcmp(argv[1], "0")) { + SHOT_CAPTURE_quiet_time_set(LSM6DS3TR_C_QUIET_2_OVER_ODR_XL); + } else if (!strcmp(argv[1], "1")) { + SHOT_CAPTURE_quiet_time_set(LSM6DS3TR_C_QUIET_4_OVER_ODR_XL); + } else if (!strcmp(argv[1], "2")) { + SHOT_CAPTURE_quiet_time_set(LSM6DS3TR_C_QUIET_8_OVER_ODR_XL); + } else if (!strcmp(argv[1], "3")) { + SHOT_CAPTURE_quiet_time_set(LSM6DS3TR_C_QUIET_12_OVER_ODR_XL); + } else { + goto USAGE; + } + } else if (!strcmp(argv[0], "-sths") || !strcmp(argv[0], "--setthreshold")) { + if (atoi(argv[1]) >= 0 && atoi(argv[1]) <= 31) { + SHOT_CAPTURE_tap_ths_set((uint8_t)atoi(argv[1])); + } else { + goto USAGE; + } + } else { + goto USAGE; + } + } else if (argc == 3) { + if (!strcmp(argv[0], "-sdl") || !strcmp(argv[0], "--shotdurloc")) { + uint8_t duration = (uint8_t)atoi(argv[1]); + uint8_t location = (uint8_t)atoi(argv[2]); + SHOT_CAPTURE_shotdata_duration_location_set(duration, location); + } else { + goto USAGE; + } + } + else { + USAGE: + PRINT(INFO, "Usage: shot_capture [OPTION...]\r\n"); + PRINT(INFO, " -ps | --printshot Prints shot data from ring buffer\r\n"); + PRINT(INFO, " -sc | --startcapture Manually starts shot capture\r\n"); + PRINT(INFO, " -tw | --testwrite Writes test data to flash (use 'shot_storage --read' to verify)\r\n"); + PRINT(INFO, " -e | --enable Enables shot capture\r\n"); + PRINT(INFO, " <%%b> true = enable\r\n"); + PRINT(INFO, " false = disable\r\n"); + PRINT(INFO, " -ss | --setshock Sets shock time\r\n"); + PRINT(INFO, " <%%d> 0 = 4/ODR_XL 1 = 8/ODR_XL 2 = 16/ODR_XL 3 = 24/ODR_XL\r\n"); + PRINT(INFO, " -sq | --setquiet Sets quiet time\r\n"); + PRINT(INFO, " <%%d> 0 = 2/ODR_XL 1 = 4/ODR_XL 2 = 8/ODR_XL 3 = 12/ODR_XL\r\n"); + PRINT(INFO, " -sths | --setthreshold Sets tap threshold\r\n"); + PRINT(INFO, " <%%d> Tap threshold value (0-31)\r\n"); + PRINT(INFO, " -sdl | --shotdurationandlocation Sets shot duration and location\r\n"); + PRINT(INFO, " <%%d> Shot duration\r\n"); + PRINT(INFO, " <%%d> Shot location\r\n"); + } +} + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _shot_capture_task(void *p1, void *p2, void *p3) +{ + uint16_t fifo_unread_words; + uint8_t empty; + uint8_t smart_full; + uint8_t fifo_overrun; + uint8_t fifo_fth_hit; + uint8_t tap_src_reg; + uint32_t fifo_unread_sample_set_in_bytes; + int32_t overshoot; + uint32_t bytes_to_read; + uint32_t bytes_to_read_into_dummy = 0; + uint32_t bytes_to_read_after_shot = 0; + + //timing_init(); + //timing_start(); + + while(1) { + /* Wait for IMU interrupt indefinitely*/ + int ret = k_sem_take(&shot_capture_sem, K_FOREVER); + if (ret != 0) { + /* if k_sem_reset is called ret is -EAGAIN, in that case just continue and wait for the next give */ + continue; + } + + /* Check if someone requested us to confirm idle */ + if (atomic_get(&_shot_capture_idle_requested)) { + k_sem_give(&shot_capture_idle_sem); + /* Stay in loop, will block again on shot_capture_sem. + * Since capture is disabled, no new IMU interrupts will come. */ + continue; + } + + DBG_STAT_INC_SAFE(fifo_read_task_counter, stat_mutex); + /* always check the single tap source register first to clear the interrupt source*/ + I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_TAP_SRC, &tap_src_reg, 1); + /** + * Dectected shot and the ring buffer does not contain shot data then record current + * shot into ring buffer + */ + k_mutex_lock(&shot_data_mutex, K_FOREVER); + k_mutex_lock(&FIFO_ring_buf_mutex, K_FOREVER); + if (((tap_src_reg & 0x40) || atomic_get(&_start_capture)) && !shot_data.marked) { + shot_data.saved = false; + bytes_to_read_after_shot = shot_data.bytes_after_shot; + DBG_STAT_INC_SAFE(read_shot_data_counter, stat_mutex); + IMU_fifo_status_1to2_read(&fifo_unread_words, &empty, &smart_full, &fifo_overrun, &fifo_fth_hit); + /** + * each sample set has shot_data.words_per_sample_set 16-bit samples depending on the + * odr of acclerometer and gyroscope + * we always work on the integer # of sample set in bytes + */ + fifo_unread_sample_set_in_bytes = (fifo_unread_words / shot_data.words_per_sample_set) * shot_data.bytes_per_sample_set; + /* shot data is at the end of the IMU FIFO */ + _read_shot_data_into_ring_buf(fifo_unread_sample_set_in_bytes, true); + + ///* disable shot data interrupt*/ + //I2C_bytes_write (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_MD1_CFG, (uint8_t[]){0}, 1); + } else { + /** + * 1. no shot detected and shot data is not marked in the ring buffer F,T + * 2. no shot detected and shot data is marked in the ring buffer F,F + * 3. shot detected and shot data is marked in the ring buffer T,F + */ + DBG_STAT_INC_SAFE(fifo_waterM_total_hit_counter, stat_mutex); + IMU_fifo_status_1to2_read(&fifo_unread_words, &empty, &smart_full, &fifo_overrun, &fifo_fth_hit); + if (fifo_fth_hit) { + DBG_STAT_INC_SAFE(fifo_waterM_hit_counter, stat_mutex); + DBG_STAT_INC_IF_SAFE(fifo_overrun, fifo_overrun_counter, stat_mutex); + DBG_STAT_TRACK_VALUE_SAFE(fifo_unread_words, unread_words_before_read, stat_mutex); + DBG_STAT_TRACK_MAX_SAFE(fifo_unread_words, max_unread_words_before_read, stat_mutex); + fifo_unread_sample_set_in_bytes = (fifo_unread_words / shot_data.words_per_sample_set) * shot_data.bytes_per_sample_set; + /** + * shot data is ready but has not been written completely into the flash + * dump shot data into a dummy buffer to not to cause corruption in the ring buffer + */ + if (shot_data.ready == true) { + DBG_STAT_INC_SAFE(read_into_dummy_counter, stat_mutex); + IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_BYPASS); /* reset the fifo content */ + IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_CONTINUOUS); + //I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_DATA_OUT_L, &_dummy_buf[0], fifo_unread_sample_set_in_bytes); + } else { + DBG_STAT_INC_SAFE(fifo_read_FTH_counter, stat_mutex); + if (shot_data.marked == true) { + /** + * Takes care of scenario 2 and 3. Read FIFO high watermark data to specified amount + * defined in shot_data.bytes_after_shot + */ + if (bytes_to_read_after_shot > fifo_unread_sample_set_in_bytes) { + bytes_to_read = fifo_unread_sample_set_in_bytes; + bytes_to_read_after_shot = bytes_to_read_after_shot - fifo_unread_sample_set_in_bytes; + } else { + if (bytes_to_read_after_shot < fifo_unread_sample_set_in_bytes) { + bytes_to_read_into_dummy = fifo_unread_sample_set_in_bytes - bytes_to_read_after_shot; + } + bytes_to_read = bytes_to_read_after_shot; + shot_data.ready = true; + } + overshoot = FIFO_ring_buf.write_ptr + bytes_to_read - shot_data.size; + _read_FIFO_into_ring_buf(overshoot, bytes_to_read); + + if (bytes_to_read_into_dummy) { + /* blocking read */ + IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_BYPASS); /* reset the fifo content */ + IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_CONTINUOUS); + //I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_DATA_OUT_L, &_dummy_buf[0], bytes_to_read_into_dummy); + bytes_to_read_into_dummy = 0; + } + + if (shot_data.ready) { + shot_data.start = FIFO_ring_buf.write_ptr; + SHOT_STORAGE_begin_write(); + } + IMU_fifo_status_1to2_read(&fifo_unread_words, &empty, &smart_full, &fifo_overrun, &fifo_fth_hit); + DBG_STAT_TRACK_VALUE_SAFE(fifo_unread_words, unread_words_after_read, stat_mutex); + DBG_STAT_TRACK_MAX_SAFE(fifo_unread_words, max_unread_words_after_read, stat_mutex); + } else { + /* takes care of scenario 1 and read FIFO high watermark data*/ + if (NULL != FIFO_ring_buf.buf) { + /* break it into 2 reads if this read is going to exceed the ring buffer size */ + overshoot = FIFO_ring_buf.write_ptr + fifo_unread_sample_set_in_bytes - shot_data.size; + _read_FIFO_into_ring_buf(overshoot, fifo_unread_sample_set_in_bytes); + /* debug*/ + IMU_fifo_status_1to2_read(&fifo_unread_words, &empty, &smart_full, &fifo_overrun, &fifo_fth_hit); + DBG_STAT_TRACK_VALUE_SAFE(fifo_unread_words, unread_words_after_read, stat_mutex); + DBG_STAT_TRACK_MAX_SAFE(fifo_unread_words, max_unread_words_after_read, stat_mutex); + + /* check if the shot motion happens when during the i2c transfer of FIFO high watermark data*/ + I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_TAP_SRC, &tap_src_reg, 1); + + if ((tap_src_reg & 0x40) || atomic_get(&_start_capture)) { + shot_data.saved = false; + DBG_STAT_INC_SAFE(read_shot_data_after_fifo_read_counter, stat_mutex); + IMU_fifo_status_1to2_read(&fifo_unread_words, &empty, &smart_full, &fifo_overrun, &fifo_fth_hit); + fifo_unread_sample_set_in_bytes = (fifo_unread_words / shot_data.words_per_sample_set) * shot_data.bytes_per_sample_set; /* convert integer # of sample set to bytes*/ + /* shot data is not necessarily at the end of IMU FIFO*/ + _read_shot_data_into_ring_buf(fifo_unread_sample_set_in_bytes, false); + k_sem_take(&shot_capture_sem, K_NO_WAIT); + } + } else { + IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_BYPASS); /* reset the fifo content */ + IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_CONTINUOUS); + //I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_DATA_OUT_L, &_dummy_buf[0], fifo_unread_sample_set_in_bytes); + /* debug*/ + IMU_fifo_status_1to2_read(&fifo_unread_words, &empty, &smart_full, &fifo_overrun, &fifo_fth_hit); + DBG_STAT_TRACK_VALUE_SAFE(fifo_unread_words, unread_words_after_read, stat_mutex); + DBG_STAT_TRACK_MAX_SAFE(fifo_unread_words, max_unread_words_after_read, stat_mutex); + } + } + } + } + } + atomic_set(&_start_capture, 0); + k_mutex_unlock(&FIFO_ring_buf_mutex); + k_mutex_unlock(&shot_data_mutex); + } +} + + // start_time = timing_counter_get(); + // busy_wait_active = true; + // k_busy_wait(300000); + // busy_wait_active = false; + // end_time = timing_counter_get(); + // cycles_with_dma = timing_cycles_get(&start_time, &end_time); + +static ERRNO_e _read_shot_data_into_ring_buf(uint32_t fifo_unread_sample_set_in_bytes, bool end) +{ + ERRNO_e err; + int32_t overshoot; + uint32_t shot_location; + uint32_t unread_sample_set; + uint32_t unread_sample_set_in_half; + + ALS_read((ALS_data_s *) &shot_data.ambient_light_intensity); + shot_data.time = k_uptime_get_32(); + + /* determine the location of shot_data in the ring buffer */ + if (end == false) { + /* if shot happens during reading high watermark data, we guess the shot data happens in the middle */ + unread_sample_set = fifo_unread_sample_set_in_bytes / shot_data.bytes_per_sample_set; + if (unread_sample_set == 1) { + unread_sample_set_in_half = 1; + } else { + unread_sample_set_in_half = unread_sample_set / 2; + } + overshoot = FIFO_ring_buf.write_ptr + (unread_sample_set_in_half * shot_data.bytes_per_sample_set) - shot_data.size; + } else { + overshoot = FIFO_ring_buf.write_ptr + fifo_unread_sample_set_in_bytes - shot_data.size; + } + + if (overshoot < 0) { + shot_data.location = FIFO_ring_buf.write_ptr + fifo_unread_sample_set_in_bytes - shot_data.bytes_per_sample_set; + } else { + if (overshoot > 0) { + shot_data.location = overshoot - shot_data.bytes_per_sample_set; + + } else { + shot_data.location = shot_data.size - shot_data.bytes_per_sample_set; + } + } + + /* break it into 2 reads if this read is going to exceed the buffer size*/ + overshoot = FIFO_ring_buf.write_ptr + fifo_unread_sample_set_in_bytes - shot_data.size; + err = _read_FIFO_into_ring_buf(overshoot, fifo_unread_sample_set_in_bytes); + + shot_data.marked = true; + + return err; +} + +static ERRNO_e _read_FIFO_into_ring_buf(int32_t overshoot, uint32_t bytes_to_read) +{ + ERRNO_e err; + + if (overshoot < 0) { + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_DATA_OUT_L, &(FIFO_ring_buf.buf[FIFO_ring_buf.write_ptr]), bytes_to_read); + FIFO_ring_buf.write_ptr = FIFO_ring_buf.write_ptr + bytes_to_read; + } else if (overshoot > 0) { + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_DATA_OUT_L, &(FIFO_ring_buf.buf[FIFO_ring_buf.write_ptr]), (shot_data.size - FIFO_ring_buf.write_ptr)); + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_DATA_OUT_L, &(FIFO_ring_buf.buf[0]), overshoot); + FIFO_ring_buf.write_ptr = overshoot; + FIFO_ring_buf.loop_ctr += 1; + } else { + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_DATA_OUT_L, &(FIFO_ring_buf.buf[FIFO_ring_buf.write_ptr]), bytes_to_read); + FIFO_ring_buf.write_ptr = 0; + FIFO_ring_buf.loop_ctr += 1; + } + + return err; +} + +static void _print_shot_data(void) +{ + uint32_t read_ptr; + bool is_shot_data_marked; + bool is_shot_data_ready; + bool is_shot_data_saved; + uint32_t size; + + k_mutex_lock(&shot_data_mutex, K_FOREVER); + is_shot_data_marked = shot_data.marked; + is_shot_data_ready = shot_data.ready; + is_shot_data_saved = shot_data.saved; + size = shot_data.size; + read_ptr = shot_data.start; + k_mutex_unlock(&shot_data_mutex); + + if (is_shot_data_ready && FIFO_ring_buf.buf != NULL) { + /* print each sample set in G GyroX, GyroY, GyroZ, AccX, AccY, AccZ format */ + PRINT(INFO, "%d\r\n", LSM6DS3TR_C_FS_XL_TO_G(_shot_capture_config.fs_xl)); + PRINT(INFO, "%d\r\n", LSM6DS3TR_C_ODR_XL_TO_HZ(_shot_capture_config.odr_xl)); + PRINT(INFO, "GyroX, GyroY, GyroZ, AccX, AccY, AccZ\r\n"); + for (int i = 0; i < size; i += 12) { + PRINT (INFO, "%d,%d,%d,%d,%d,%d\r\n", *((int16_t *) &FIFO_ring_buf.buf[read_ptr]), \ + *((int16_t *) &FIFO_ring_buf.buf[read_ptr + 2]), \ + *((int16_t *) &FIFO_ring_buf.buf[read_ptr + 4]), \ + *((int16_t *) &FIFO_ring_buf.buf[read_ptr + 6]), \ + *((int16_t *) &FIFO_ring_buf.buf[read_ptr + 8]), \ + *((int16_t *) &FIFO_ring_buf.buf[read_ptr + 10])); + if ((read_ptr + 10) == (size - 2)) { + read_ptr = 0; + } else { + read_ptr += 12; + } + /* prevent watchdog timeout due to slow print on serial port over uart*/ + WDT_feed (); + } + + k_mutex_lock(&shot_data_mutex, K_FOREVER); + shot_data.marked = false; + shot_data.ready = false; + shot_data.saved = true; + k_mutex_unlock(&shot_data_mutex); + } else { + PRINT (INFO, "Shot data is not ready!\r\n"); + } +} + +static void _shot_capture_config_read(void) +{ + ERRNO_e err; + SHOT_CAPTURE_config_s cfg; + + err = FS_open_or_create(SHOT_CAPTURE_FILENAME, &_shot_capture_file, sizeof(SHOT_CAPTURE_config_s), FS_FILE_DEPTH_DEFAULT); + if (err == ERRNO_SUCCESS) { + /* Read file. */ + err = FS_read(_shot_capture_file, (uint8_t *) &cfg, 0, sizeof(SHOT_CAPTURE_config_s)); + + /* Check file entry is valid */ + if (err != ERRNO_SUCCESS) { + /* write defaults to file */ + _shot_capture_config_write(); + return; + } + + /* Check file entry is valid. */ + if (cfg.shot_duration < 1 || cfg.shot_duration > 10 || + cfg.shot_location < 0 || cfg.shot_location >= cfg.shot_duration || + cfg.shot_duration * 3 * 2 * (LSM6DS3TR_C_ODR_G_TO_HZ(cfg.odr_g) + LSM6DS3TR_C_ODR_XL_TO_HZ(cfg.odr_xl)) > _SHOT_STORAGE_FILE_SIZE) { + PRINT(ERROR, "Invalid shot_duration in shot capture configuration file. Writing defaults.\r\n"); + _shot_capture_config_write(); + return; + } + + /* Update shot capture configuration to what was read from flash. */ + _shot_capture_config = cfg; + if (_shot_capture_config.shot_duration != SHOTDATA_DURATION_DEFAULT || + _shot_capture_config.shot_location != SHOTDATA_LOCATION_DEFAULT || + _shot_capture_config.odr_g != LSM6DS3TR_C_ODR_G_416Hz || + _shot_capture_config.odr_xl != LSM6DS3TR_C_ODR_XL_416Hz) { + _shot_capture_shotdata_param_cal(); + } + } else if (err == ERRNO_SIZE_MISMATCH) { + /* Config has been modified, need to delete old file and write new configuration. */ + err = FS_delete(SHOT_CAPTURE_FILENAME); + + if (err != ERRNO_SUCCESS) { + /* Something else is going on... */ + PRINT(ERROR, "Failure to delete old shot capture file.\r\n"); + return; + } + + /* Create new file. */ + err = FS_open_or_create (SHOT_CAPTURE_FILENAME, &_shot_capture_file, sizeof (SHOT_CAPTURE_config_s), FS_FILE_DEPTH_DEFAULT); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failure to create shot capture configuration file.\r\n"); + } + else { + PRINT(INFO, "New shot capture created successfully\r\n"); + } + + /* Write default config to file. */ + _shot_capture_config_write(); + } else { + /* write default */ + _shot_capture_config_write(); + } +} + +static ERRNO_e _shot_capture_config_write(void) +{ + ERRNO_e err; + + err = FS_write(_shot_capture_file, (uint8_t *) &_shot_capture_config); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to write shot capture configuration to file.\r\n"); + } + return err; +} + +static void _shot_capture_shotdata_param_cal(void) +{ + uint32_t sample_set_odr_Hz; + + sample_set_odr_Hz = LSM6DS3TR_C_ODR_XL_TO_HZ(MIN((uint8_t)_shot_capture_config.odr_g, (uint8_t)_shot_capture_config.odr_xl)); + + k_mutex_lock(&shot_data_mutex, K_FOREVER); + shot_data.words_per_sample_set = ((LSM6DS3TR_C_ODR_G_TO_HZ(_shot_capture_config.odr_g) / sample_set_odr_Hz) + + (LSM6DS3TR_C_ODR_XL_TO_HZ(_shot_capture_config.odr_xl) / sample_set_odr_Hz)) * 3; + shot_data.bytes_per_sample_set = shot_data.words_per_sample_set * 2; + shot_data.size = _shot_capture_config.shot_duration * sample_set_odr_Hz * shot_data.bytes_per_sample_set; + shot_data.bytes_after_shot = (_shot_capture_config.shot_duration - _shot_capture_config.shot_location) * sample_set_odr_Hz * shot_data.bytes_per_sample_set; + k_mutex_unlock(&shot_data_mutex); +} diff --git a/src/app/shot_capture.h b/src/app/shot_capture.h new file mode 100644 index 0000000..cfc96b7 --- /dev/null +++ b/src/app/shot_capture.h @@ -0,0 +1,146 @@ +/** + * @file shot_captre.h + * @brief Shot Capture module + * @author + * @date Oct 16 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef SHOT_CAPTURE_H_ +#define SHOT_CAPTURE_H_ + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include +#include /* Zephyr MIN() macro */ + +#include "../bsp/imu.h" +#include "../bsp/imu_reg.h" + +#include "../lib/errno.h" +#include "../lib/debug.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define SHOT_CAPTURE_TASK_STACK_SIZE (1024) +#define SHOT_CAPTURE_THREAD_PRIORITY (4) + +#define SHOTDATA_DURATION_MIN (2) +#define SHOTDATA_DURATION_MAX (10) +#define GYRO_ODR_DEFAULT_HZ (416) +#define ACCEL_ODR_DEFAULT_HZ (416) +#define SHOTDATA_DURATION_DEFAULT (4) +#define SHOTDATA_LOCATION_DEFAULT (SHOTDATA_DURATION_DEFAULT / 2) +#define SAMPLE_SET_ODR_DEFAULT_HZ MIN(GYRO_ODR_DEFAULT_HZ, ACCEL_ODR_DEFAULT_HZ) +#define BYTES_PER_SAMPLE_SET (GYRO_ODR_DEFAULT_HZ / SAMPLE_SET_ODR_DEFAULT_HZ + ACCEL_ODR_DEFAULT_HZ / SAMPLE_SET_ODR_DEFAULT_HZ) * 3 * 2 +#define WORDS_PER_SAMPLE_SET (GYRO_ODR_DEFAULT_HZ / SAMPLE_SET_ODR_DEFAULT_HZ + ACCEL_ODR_DEFAULT_HZ / SAMPLE_SET_ODR_DEFAULT_HZ) * 3 +#define SHOTDATA_SIZE_IN_BYTES_DEFAULT (SHOTDATA_DURATION_DEFAULT * SAMPLE_SET_ODR_DEFAULT_HZ * BYTES_PER_SAMPLE_SET) +#define BYTES_AFTER_SHOT_DEFAULT (SHOTDATA_DURATION_DEFAULT - SHOTDATA_LOCATION_DEFAULT) * SAMPLE_SET_ODR_DEFAULT_HZ * BYTES_PER_SAMPLE_SET + +#define SHOT_CAPTURE_FILENAME ("shot_capture") + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct FIFO_ring_buf_s { + uint8_t *buf; + uint16_t write_ptr; + uint16_t read_ptr; + uint32_t loop_ctr; +} FIFO_ring_buf_s; + +typedef struct shot_data_status { + /* system up time in ms */ + uint32_t time; + /* start location of shot data in the ring buffer */ + uint32_t start; + /* location of the shot data sample in the ring buffer */ + uint32_t location; + /* words per sample set */ + uint8_t words_per_sample_set; + /* words per sample set */ + uint8_t bytes_per_sample_set; + /* size of shot data in bytes */ + uint32_t size; + /* bytes to read after shot data */ + uint32_t bytes_after_shot; + /* ambient light intensity */ + uint32_t ambient_light_intensity; + /* flag to determine if shot data has been marked */ + bool marked; + /* flag to determine if there are enough samples after shot data */ + bool ready; + /* flag to show if the shot data window is written in to the flash */ + bool saved; +} shot_data_status_s; + +typedef struct SHOT_CAPTURE_config { + uint8_t shot_duration; + uint8_t shot_location; + LSM6DS3TR_C_FS_G_ABSTR_e fs_g; + LSM6DS3TR_C_ODR_G_e odr_g; + LSM6DS3TR_C_FS_XL_e fs_xl; + LSM6DS3TR_C_ODR_XL_e odr_xl; + uint8_t tap_ths; + LSM6DS3TR_C_QUIET_e quiet_time; + LSM6DS3TR_C_SHOCK_e shock_time; + uint32_t user_shot_cnt; + uint32_t shot_cnt; + uint8_t shot_capture_en; +} __attribute__((packed, aligned(1))) SHOT_CAPTURE_config_s; + + +/*-------------------------------------------------------------------------------------------------- + * GLOBAL VARIABLE DECLARATION + *------------------------------------------------------------------------------------------------*/ +extern volatile FIFO_ring_buf_s FIFO_ring_buf; +extern volatile shot_data_status_s shot_data; +extern struct k_mutex shot_data_mutex; +extern struct k_mutex FIFO_ring_buf_mutex; +extern struct k_mutex stat_mutex; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void SHOT_CAPTURE_init(void); + +void SHOT_CAPTURE_tap_ths_set(uint8_t tap_ths); +void SHOT_CAPTURE_quiet_time_set(LSM6DS3TR_C_QUIET_e quiet_time); +void SHOT_CAPTURE_shock_time_set(LSM6DS3TR_C_SHOCK_e shock_time); + +void SHOT_CAPTURE_shotdata_duration_location_set(uint8_t duration, uint8_t location); + +SHOT_CAPTURE_config_s *SHOT_CAPTURE_config_get(void); + +ERRNO_e SHOT_CAPTURE_enable(bool enable); + +bool SHOT_CAPTURE_is_enabled(void); + +ERRNO_e SHOT_CAPTURE_save_shot_cnt(void); + +void SHOT_CAPTURE_task_install(void); + +/** + * @brief Disable shot capture and wait until the capture task is confirmed idle. + * Must NOT be called from _shot_capture_task itself. + */ +void SHOT_CAPTURE_task_disable_and_wait(void); + +void SHOT_CAPTURE_task_resume(void); + +void SHOT_CAPTURE_cli_cmd(int32_t argc, char **argv); + + +#endif /* SHOT_CAPTURE_H */ \ No newline at end of file diff --git a/src/app/shot_storage.c b/src/app/shot_storage.c new file mode 100644 index 0000000..4bda1ae --- /dev/null +++ b/src/app/shot_storage.c @@ -0,0 +1,835 @@ +/** + * @file shot_storage.c + * @brief Shot Storage Module + * @author Kenny Tran + * @date Sep 02 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include +#include +#include /* Zephyr MIN() macro */ + +#include "../app/app.h" +#include "../app/shot_capture.h" +#include "../app/ble.h" +#include "../app/motion.h" + +#include "../lib/errno.h" +#include "../lib/fs.h" +#include "../lib/print.h" + +#include "../bsp/imu.h" +#include "../bsp/nvm_ext.h" +#include "../bsp/wdt.h" + +#include "../wrappers/spi.h" + +#include "shot_storage.h" + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +/** + * @brief For use with reading NVM external status bit. + * */ +#define _SHOT_STORAGE_NVM_STATUS_BUSY_MASK (0x01) +#define _SHOT_STORAGE_NVM_EXT_STATUS_BUSY (0x01) + +#define _SHOT_STORAGE_NVM_PAGE_PROG_LEN (4) + + +K_THREAD_STACK_DEFINE(_shot_storage_write_stack, SHOT_STORAGE_WRITE_THREAD_STACK_SIZE); +K_THREAD_STACK_DEFINE(_shot_storage_read_stack, SHOT_STORAGE_READ_THREAD_STACK_SIZE); + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC VARIABLE DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static SHOT_STORAGE_file_s _shot_storage_files[SHOT_STORAGE_MAX_FILES]; +static SHOT_STORAGE_file_s _shot_storage_current_file = { + .header_ver = 0, + .shot_data_format_ver = 0, + .reserved = 0, + .shot_data_offset = sizeof(SHOT_STORAGE_file_s), + .gyro_bits_per_axis = 16, + .accel_bits_per_axis = 16, + .IMU_orientation = 0x0FED, /* away from user: -x, to the right of user: -y, towards earth: -z */ +}; +K_SEM_DEFINE(write_shotdata_sem, 0, 1); +static char _temp_name[SHOT_STORAGE_FILENAME_LEN]; +static uint8_t _shot_storage_buf[_SHOT_STORAGE_FILE_SIZE]; +static uint32_t _shot_storage_addr = _SHOT_STORAGE_START_ADDR; +static uint32_t _shot_storage_write_pages = 0; +static uint32_t _shot_storage_bytes_remaining = 0; +static uint8_t _shot_storage_staging_buf[_SHOT_STORAGE_NVM_PAGE_PROG_LEN + NVM_EXT_PAGE_SIZE]; +static uint32_t _shot_storage_logic_stream_start = 0; +static FS_file_t _shot_storage_tracking_file; + +/* Circular file storage tracking (persisted to internal flash) */ +static SHOT_STORAGE_tracking_s _shot_storage_tracking = { + .write_idx = 0, + .file_cnt = 0, + .total_written = 0, +}; + +/* Read task synchronization */ +K_SEM_DEFINE(read_shotdata_request, 0, 1); /* Trigger read task */ +K_SEM_DEFINE(read_shotdata_buf_ready, 0, 1); /* Buffer ready for consumer */ +K_SEM_DEFINE(read_shotdata_consumed, 0, 1); /* Consumer finished reading */ + +/* Idle fence for write task */ +static K_SEM_DEFINE(shot_storage_write_idle_sem, 0, 1); +static atomic_t _shot_storage_write_idle_requested = ATOMIC_INIT(0); + +/* Track who requested the read */ +static atomic_t _read_request_source = ATOMIC_INIT(READ_REQ_NONE); + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _shot_storage_files_init (void); +static bool _shot_storage_is_empty (uint8_t *p_data, uint32_t len); +static ERRNO_e _shot_storage_nvm_page_write (uint32_t addr, uint8_t *p_src, uint32_t rem_len); +static void _shot_storage_nvm_wait_til_ready (void); +static void _shot_storage_write_task (void *p1, void *p2, void *p3); +static void _shot_storage_read_task (void *p1, void *p2, void *p3); +static void _shot_storage_tracking_read (void); +static ERRNO_e _shot_storage_tracking_write (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void SHOT_STORAGE_init (void) +{ + PRINT (INFO, "Initializing SHOT STORAGE...\r\n"); + + if (NVM_EXT_init () < ERRNO_SUCCESS) + { + PRINT (CRITICAL, "Failure to initialize NVM_EXT.\r\n"); + } + + _shot_storage_files_init (); +} + +void SHOT_STORAGE_task_install (void) +{ + static k_tid_t write_id; + static struct k_thread write_data; + static k_tid_t read_id; + static struct k_thread read_data; + + write_id = k_thread_create (&write_data, + _shot_storage_write_stack, + SHOT_STORAGE_WRITE_THREAD_STACK_SIZE, + _shot_storage_write_task, + NULL, + NULL, + NULL, + SHOT_STORAGE_WRITE_THREAD_PRIORITY, + 0, + K_NO_WAIT); + + k_thread_name_set(&write_data, "SHOT_STORAGE_write_task"); + + read_id = k_thread_create (&read_data, + _shot_storage_read_stack, + SHOT_STORAGE_READ_THREAD_STACK_SIZE, + _shot_storage_read_task, + NULL, + NULL, + NULL, + SHOT_STORAGE_READ_THREAD_PRIORITY, + 0, + K_NO_WAIT); + + k_thread_name_set(&read_data, "SHOT_STORAGE_read_task"); +} + +ERRNO_e SHOT_STORAGE_get_buf(uint32_t size, uint8_t **pp_data) +{ + if (size > _SHOT_STORAGE_FILE_SIZE) { + *pp_data = NULL; + PRINT (CRITICAL, "Failure to get buffer. Requested size too large!\r\n"); + return ERRNO_EXCEED_FILE_SIZE; + } + + /* return pointer to the statically allocated shot data buffer */ + *pp_data = &_shot_storage_buf[sizeof(SHOT_STORAGE_file_s)]; + + return ERRNO_SUCCESS; +} + +ERRNO_e SHOT_STORAGE_file_create(void) +{ + if (_shot_storage_current_file.shot_data_size > _SHOT_STORAGE_FILE_SIZE) { + PRINT (CRITICAL, "Failure to create new file. File size too large!\r\n"); + return ERRNO_EXCEED_FILE_SIZE; + } + + /* Assign file name. */ + // int len = snprintf(_temp_name, SHOT_STORAGE_FILENAME_LEN, "%s", p_filename); + // if (len >= SHOT_STORAGE_FILENAME_LEN) { + // PRINT(WARNING, "Filename truncated: %s\n", p_filename); + // return ERRNO_EXCEED_NAME_SIZE; + // } + + /* fill file header info */ + k_mutex_lock(&shot_data_mutex, K_FOREVER); + _shot_storage_logic_stream_start = shot_data.start; + _shot_storage_current_file.time_of_shot = shot_data.time; + _shot_storage_current_file.ambient_light_intensity = shot_data.ambient_light_intensity; + _shot_storage_current_file.shot_data_size = shot_data.size; + k_mutex_unlock(&shot_data_mutex); + + _shot_storage_current_file.power_cnt = APP_info_get ().power_cnt; + _shot_storage_current_file.gyro_sample_rate_hz = LSM6DS3TR_C_ODR_G_TO_HZ(SHOT_CAPTURE_config_get()->odr_g); + _shot_storage_current_file.accel_sample_rate_hz = LSM6DS3TR_C_ODR_XL_TO_HZ(SHOT_CAPTURE_config_get()->odr_xl); + _shot_storage_current_file.gyro_full_scale_dps = LSM6DS3TR_C_FS_G_TO_DPS(SHOT_CAPTURE_config_get()->fs_g); + _shot_storage_current_file.accel_full_scale_g = LSM6DS3TR_C_FS_XL_TO_G(SHOT_CAPTURE_config_get()->fs_xl); + _shot_storage_current_file.accel_cal = MOTION_config_get ()->accel_cal; + _shot_storage_current_file.gyro_cal = MOTION_config_get ()->gyro_cal; + + return ERRNO_SUCCESS; +} + +void SHOT_STORAGE_begin_write(void) { + k_sem_give(&write_shotdata_sem); +} + +void SHOT_STORAGE_write (void) { + ERRNO_e err = ERRNO_SUCCESS; + struct stream_cursor cur; + uint32_t copied = 0; + uint32_t slot = _shot_storage_tracking.write_idx; + + /* Generate unique name using total_written counter */ + //int len = snprintf(_shot_storage_current_file.name, SHOT_STORAGE_FILENAME_LEN, "%s_%u", _temp_name, (_shot_storage_tracking.total_written + 1)); + //if (len >= SHOT_STORAGE_FILENAME_LEN) { + // PRINT(WARNING, "Generated filename truncated\n"); + //} + + /* If all slots are full, erase the oldest slot before writing */ + if (_shot_storage_tracking.file_cnt >= SHOT_STORAGE_MAX_FILES) { + uint32_t oldest_addr = _SHOT_STORAGE_START_ADDR + (slot * _SHOT_STORAGE_TOTAL_FILE_SIZE); + if (NVM_EXT_erase_block(oldest_addr, NVM_EXT_BLOCK_SIZE_64KB)) { + PRINT(CRITICAL, "Failure to erase oldest slot %u!\r\n", slot); + return; + } + PRINT(INFO, "Overwrote oldest file: power_cnt: %u, time: %.3f s, (slot %u)\r\n", _shot_storage_files[slot].power_cnt, (float) _shot_storage_files[slot].time_of_shot / 1000.0, slot); + } + + SHOT_STORAGE_stream_cursor_init(&cur, (const uint8_t *)&_shot_storage_current_file, sizeof(SHOT_STORAGE_file_s), + &_shot_storage_buf[sizeof(SHOT_STORAGE_file_s)], _shot_storage_current_file.shot_data_size, _shot_storage_logic_stream_start); + + _shot_storage_addr = _SHOT_STORAGE_START_ADDR + (slot * _SHOT_STORAGE_TOTAL_FILE_SIZE); + + while (stream_cursor_remaining(&cur)) { + copied = stream_cursor_read(&cur, &_shot_storage_staging_buf[4], NVM_EXT_PAGE_SIZE); + err = _shot_storage_nvm_page_write(_shot_storage_addr, _shot_storage_staging_buf, copied); + if (err != ERRNO_SUCCESS) break; + _shot_storage_addr += NVM_EXT_PAGE_SIZE; + } + + if (err != ERRNO_SUCCESS) { + /* Erase the partially written slot */ + uint32_t fail_addr = _SHOT_STORAGE_START_ADDR + (slot * _SHOT_STORAGE_TOTAL_FILE_SIZE); + NVM_EXT_erase_block(fail_addr, NVM_EXT_BLOCK_SIZE_64KB); + PRINT(CRITICAL, "Write failed, erased partial data at slot %u\r\n", slot); + } else { + SHOT_CAPTURE_config_get()->shot_cnt++; + SHOT_CAPTURE_config_get()->user_shot_cnt++; + BLE_notify(BLE_CHAR_USER_SHOT_CNT, &(SHOT_CAPTURE_config_get()->user_shot_cnt), sizeof(SHOT_CAPTURE_config_get()->user_shot_cnt)); + /* Update tracking */ + _shot_storage_files[slot] = _shot_storage_current_file; + if (_shot_storage_tracking.file_cnt < SHOT_STORAGE_MAX_FILES) { + _shot_storage_tracking.file_cnt++; + } + _shot_storage_tracking.write_idx = (slot + 1) % SHOT_STORAGE_MAX_FILES; + _shot_storage_tracking.total_written++; + _shot_storage_tracking_write(); + } +} + +bool SHOT_STORAGE_write_done (void) +{ + return (_shot_storage_write_pages == 0 && _shot_storage_bytes_remaining == 0); +} + +uint8_t *SHOT_STORAGE_last_read (SHOT_STORAGE_file_s *p_file) +{ + uint32_t addr; + uint16_t len; + uint32_t newest_idx; + + if (!_shot_storage_tracking.file_cnt) + { + return NULL; + } + + /* Newest file is the one most recently written */ + newest_idx = (_shot_storage_tracking.write_idx - 1 + SHOT_STORAGE_MAX_FILES) % SHOT_STORAGE_MAX_FILES; + addr = _SHOT_STORAGE_START_ADDR + (newest_idx * _SHOT_STORAGE_TOTAL_FILE_SIZE); + len = _shot_storage_files[newest_idx].shot_data_size + sizeof(SHOT_STORAGE_file_s); + + if (NVM_EXT_read(addr, _shot_storage_buf, (uint32_t) len)) + { + PRINT(CRITICAL, "Failure to read shot data from external NVM...\r\n"); + } + + *p_file = _shot_storage_files[newest_idx]; + + return _shot_storage_buf; +} + +void SHOT_STORAGE_last_erase(void) +{ + uint32_t addr; + uint32_t newest_idx; + + if (!_shot_storage_tracking.file_cnt) { + PRINT(INFO, "No files exist!\r\n"); + return; + } + + newest_idx = (_shot_storage_tracking.write_idx - 1 + SHOT_STORAGE_MAX_FILES) % SHOT_STORAGE_MAX_FILES; + addr = _SHOT_STORAGE_START_ADDR + (newest_idx * _SHOT_STORAGE_TOTAL_FILE_SIZE); + + if (NVM_EXT_erase_block (addr, NVM_EXT_BLOCK_SIZE_64KB)) { + PRINT(CRITICAL, "Failure to erase shot data from external NVM...\r\n"); + return; + } + + memset(&_shot_storage_files[newest_idx], 0xFF, sizeof(SHOT_STORAGE_file_s)); + _shot_storage_tracking.file_cnt--; + _shot_storage_tracking.write_idx = newest_idx; + _shot_storage_tracking_write(); +} + +ERRNO_e SHOT_STORAGE_eraseall(void) +{ + uint32_t addr; + + for (addr = _SHOT_STORAGE_START_ADDR; addr < _SHOT_STORAGE_END_ADDR; addr += NVM_EXT_BLOCK_SIZE_64KB) { + if (NVM_EXT_erase_block(addr, NVM_EXT_BLOCK_SIZE_64KB)) { + PRINT(CRITICAL, "Failure to erase shot data from external NVM...\r\n"); + return ERRNO_NVM_FAILURE; + } + k_msleep(5); /* Sleep to allow other tasks to run and prevent WDT reset during long erase */ + } + + /* Reset tracking and file headers */ + memset(_shot_storage_files, 0xFF, sizeof(_shot_storage_files)); + _shot_storage_tracking.write_idx = 0; + _shot_storage_tracking.file_cnt = 0; + _shot_storage_tracking.total_written = 0; + _shot_storage_tracking_write(); + + return ERRNO_SUCCESS; +} + +uint32_t SHOT_STORAGE_get_file_cnt(void) +{ + return _shot_storage_tracking.file_cnt; +} + +void SHOT_STORAGE_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) { + PRINT (INFO, "SHOT STORAGE\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Files : %u / %u\r\n", _shot_storage_tracking.file_cnt, SHOT_STORAGE_MAX_FILES); + PRINT (INFO, "Write index : %u\r\n", _shot_storage_tracking.write_idx); + PRINT (INFO, "Total saved : %u\r\n", _shot_storage_tracking.total_written); + } + else if (argc == 1) { + if (!strcmp (argv[0], "--list") || !strcmp (argv[0], "-l")) { + uint32_t i; + + if (!_shot_storage_tracking.file_cnt) { + PRINT (INFO, "No files stored.\r\n"); + } + /* List from oldest to newest */ + for (i = 0; i < _shot_storage_tracking.file_cnt; i++) { + uint32_t idx = (_shot_storage_tracking.write_idx - _shot_storage_tracking.file_cnt + i + SHOT_STORAGE_MAX_FILES) % SHOT_STORAGE_MAX_FILES; + PRINT (INFO, "Slot %2u: power_cnt: %u, time: %.3f s\r\n", idx, _shot_storage_files[idx].power_cnt, (float) _shot_storage_files[idx].time_of_shot / 1000.0); + } + } + else if (!strcmp (argv[0], "--test")) { + #define FNAME ("shot_storage") + uint8_t *p_data; + SHOT_STORAGE_get_buf(50000, &p_data); + SHOT_STORAGE_file_create(); + + memset (p_data, 9, 50000); + + PRINT (INFO, "writing...\r\n"); + + SHOT_STORAGE_write(); + + PRINT (INFO, "writing done. Current file count in flash: %u \r\n", _shot_storage_tracking.file_cnt); + } + else if (!strcmp (argv[0], "--done")) { + PRINT (INFO, "%s\r\n", SHOT_STORAGE_write_done () ? "done" : "not done"); + } + else if (!strcmp (argv[0], "--read") || !strcmp (argv[0], "-r")) { + SHOT_STORAGE_file_s file; + + /* Request the read task to load data from flash */ + SHOT_STORAGE_request_read(READ_REQ_CLI); + + /* Wait for buffer to be ready */ + if (SHOT_STORAGE_wait_buf_ready() != 0) { + PRINT(ERROR, "Failed to get read buffer\r\n"); + return; + } + + uint8_t *p_data = _shot_storage_buf; + + /* Copy the entire file header struct (includes name) */ + memcpy(&file, p_data, sizeof(SHOT_STORAGE_file_s)); + + /* Print file name with guaranteed null termination */ + PRINT(INFO, "Power Count: %u\r\n", file.power_cnt); + PRINT(INFO, "Time of shot: %u ms\r\n", file.time_of_shot); + PRINT(INFO, "Header version: %u\r\n", file.header_ver); + PRINT(INFO, "Shot data format version: %u\r\n", file.shot_data_format_ver); + PRINT(INFO, "Ambient light intensity: %u\r\n", file.ambient_light_intensity); + PRINT(INFO, "Shot data offset: %u bytes\r\n", file.shot_data_offset); + PRINT(INFO, "Shot data size: %u bytes\r\n", file.shot_data_size); + PRINT(INFO, "Gyro bits per axis: %u\r\n", file.gyro_bits_per_axis); + PRINT(INFO, "Accel bits per axis: %u\r\n", file.accel_bits_per_axis); + PRINT(INFO, "Gyro sample rate: %u Hz\r\n", file.gyro_sample_rate_hz); + PRINT(INFO, "Accel sample rate: %u Hz\r\n", file.accel_sample_rate_hz); + PRINT(INFO, "Gyro full scale: %u dps\r\n", file.gyro_full_scale_dps); + PRINT(INFO, "Accel full scale: %u g\r\n", file.accel_full_scale_g); + PRINT(INFO, "IMU orientation: 0x%04X\r\n", file.IMU_orientation); + + /*The shot data in the buffer is already in order thanks to the stream cursor function */ + PRINT(INFO, "GyroX, GyroY, GyroZ, AccX, AccY, AccZ\r\n"); + for (int i = sizeof(SHOT_STORAGE_file_s); i < sizeof(SHOT_STORAGE_file_s) + file.shot_data_size; i += 12) { + PRINT(INFO, "%d,%d,%d,%d,%d,%d\r\n", *((int16_t *) &p_data[i]), \ + *((int16_t *) &p_data[i + 2]), \ + *((int16_t *) &p_data[i + 4]), \ + *((int16_t *) &p_data[i + 6]), \ + *((int16_t *) &p_data[i + 8]), \ + *((int16_t *) &p_data[i + 10])); + WDT_feed(); /* prevent watchdog timeout due to slow print on serial port over uart */ + } + + SHOT_STORAGE_last_erase(); + PRINT (INFO, "Power cnt: %u, time: %.3f s erased...\r\n", file.power_cnt, (float) file.time_of_shot/1000.0); + + /* Signal read task that we're done */ + SHOT_STORAGE_read_done(); + } + else if (!strcmp (argv[0], "--eraseall") || !strcmp (argv[0], "-ra")) { + if (SHOT_STORAGE_eraseall() == ERRNO_SUCCESS) { + PRINT(INFO, "Erased all 64kB sectors and reset tracking!\r\n"); + } + } + } +} + +void SHOT_STORAGE_stream_cursor_init(struct stream_cursor *cur, + const uint8_t *header, uint32_t header_size, + const uint8_t *buffer, uint32_t data_size, + uint32_t write_ptr) +{ + cur->header = header; + cur->header_size = header_size; + cur->buffer = buffer; + cur->data_size = data_size; + cur->write_ptr = write_ptr; + cur->total = header_size + data_size; + cur->offset = 0; +} + +uint32_t stream_cursor_read(struct stream_cursor *cur, uint8_t *dst, uint32_t len) +{ + uint32_t copied = 0; + + while (copied < len && cur->offset < cur->total) { + uint32_t want = len - copied; + + if (cur->offset < cur->header_size) { + /* + * Region 1: Header + * Straightforward linear copy from header array. + */ + uint32_t hdr_remaining = cur->header_size - cur->offset; + uint32_t chunk = MIN(want, hdr_remaining); + + memcpy(dst + copied, cur->header + cur->offset, chunk); + copied += chunk; + cur->offset += chunk; + + } else { + /* + * Region 2: Circular buffer data + * + * Map the logical data offset to a physical buffer position, + * then copy the largest contiguous chunk available without + * crossing the buffer boundary. + */ + uint32_t data_offset = cur->offset - cur->header_size; + uint32_t buf_pos = (cur->write_ptr + data_offset) % cur->data_size; + + /* How much data is left in the logical stream */ + uint32_t data_remaining = cur->data_size - data_offset; + + /* How many contiguous bytes from buf_pos to end of effective region */ + uint32_t contiguous = cur->data_size - buf_pos; + + uint32_t chunk = MIN(want, MIN(data_remaining, contiguous)); + + memcpy(dst + copied, cur->buffer + buf_pos, chunk); + copied += chunk; + cur->offset += chunk; + } + } + + return copied; +} + +uint32_t stream_cursor_remaining(struct stream_cursor *cur) +{ + return cur->total - cur->offset; +} + +void SHOT_STORAGE_wait_write_task_idle(void) +{ + /* 1. Request idle confirmation from _shot_storage_write_task */ + k_sem_reset(&shot_storage_write_idle_sem); + atomic_set(&_shot_storage_write_idle_requested, 1); + + /* + * 2. Wake the write task so it can see the idle request. + * If it's already waiting on shot_capture_sem, this + * lets it loop once and post the idle confirmation. + */ + k_sem_give(&write_shotdata_sem); /* nudge write task so it can see the request */ + + /* 3. Wait for confirmation that write task is idle */ + k_sem_take(&shot_storage_write_idle_sem, K_FOREVER); + atomic_set(&_shot_storage_write_idle_requested, 0); +} + +void SHOT_STORAGE_request_read(read_request_source_e source) +{ + atomic_set(&_read_request_source, (atomic_val_t)source); + k_sem_give(&read_shotdata_request); +} + +int SHOT_STORAGE_wait_buf_ready(void) +{ + return k_sem_take(&read_shotdata_buf_ready, K_FOREVER); +} + +void SHOT_STORAGE_read_done(void) +{ + k_sem_give(&read_shotdata_consumed); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _shot_storage_files_init (void) +{ + /* Read tracking info from internal flash */ + _shot_storage_tracking_read(); + + /* Read file headers for all valid slots based on tracking */ + if (_shot_storage_tracking.file_cnt > 0) { + for (uint32_t i = 0; i < _shot_storage_tracking.file_cnt; i++) { + uint32_t idx = (_shot_storage_tracking.write_idx - _shot_storage_tracking.file_cnt + i + SHOT_STORAGE_MAX_FILES) % SHOT_STORAGE_MAX_FILES; + uint32_t addr = _SHOT_STORAGE_START_ADDR + (idx * _SHOT_STORAGE_TOTAL_FILE_SIZE); + + if (NVM_EXT_read(addr, (uint8_t *) &_shot_storage_files[idx], sizeof(SHOT_STORAGE_file_s))) { + PRINT(CRITICAL, "Failure to read file header from slot %u\r\n", idx); + } + } + } + + PRINT(INFO, "Shot storage: %u files, write_idx=%u, total_written=%u\r\n", + _shot_storage_tracking.file_cnt, _shot_storage_tracking.write_idx, + _shot_storage_tracking.total_written); +} + +static bool _shot_storage_is_empty (uint8_t *p_data, uint32_t len) +{ + uint32_t i; + + /* Loop through the read data. */ + for (i = 0; i < len; i++) { + /* Scan for empty space. */ + if (p_data[i] != 0xFF) { + return false; + } + } + + return true; +} + +static ERRNO_e _shot_storage_nvm_page_write (uint32_t addr, uint8_t *p_src, uint32_t rem_len) +{ + /* Write enable command must set before performing a write. */ + p_src[0] = 0x06; + + if (SPI_xfer (p_src, 1, NULL, 0) != ERRNO_SUCCESS) + { + PRINT (CRITICAL, "SPI failure transferring write enable.\r\n"); + return ERRNO_SPI_FAILURE; + } + + /* Page program opcode is 0x02. */ + p_src[0] = 0x02; + p_src[1] = _SHOT_STORAGE_SPLIT_BYTE (addr, 2); /* A23 - A16 */ + p_src[2] = _SHOT_STORAGE_SPLIT_BYTE (addr, 1); /* A15 - A8 */ + p_src[3] = _SHOT_STORAGE_SPLIT_BYTE (addr, 0); /* A7 - A0 */ + + /* 256 bytes per page + 4 bytes for op code and address. */ + rem_len = rem_len + 4; + + /* Perform write. */ + if (SPI_xfer (p_src, rem_len, NULL, 0) != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + /* Wait for write to complete. */ + _shot_storage_nvm_wait_til_ready (); + + return ERRNO_SUCCESS; +} + +static void _shot_storage_nvm_wait_til_ready (void) +{ + uint8_t op_code = 0x05; + uint8_t rx; + + /* Wait for erase to complete by polling READYn/BUSY bit. */ + do + { + if (SPI_xfer (&op_code, 1, &rx, 1) != ERRNO_SUCCESS) + { + PRINT (CRITICAL, "SPI failure polling ready bit.\r\n"); + } + } while ((rx & _SHOT_STORAGE_NVM_STATUS_BUSY_MASK) == _SHOT_STORAGE_NVM_STATUS_BUSY_MASK); +} + +static void _shot_storage_write_task(void *p1, void *p2, void *p3) +{ + ERRNO_e err = ERRNO_SUCCESS; + uint8_t *p_data; + + while (1) { + int ret = k_sem_take(&write_shotdata_sem, K_FOREVER); + if (ret != 0) { + /* if k_sem_reset is called ret is -EAGAIN, in that case just continue and wait for the next give */ + continue; + } + + /* Check if someone requested us to confirm idle */ + if (atomic_get(&_shot_storage_write_idle_requested)) { + k_sem_give(&shot_storage_write_idle_sem); + continue; + } + + /* Create a file for the current shot data */ + err = SHOT_STORAGE_file_create(); + if (err == ERRNO_SUCCESS) { + SHOT_STORAGE_write(); + err = SHOT_STORAGE_get_buf(shot_data.size, &p_data); + if (err != ERRNO_SUCCESS) { + SHOT_CAPTURE_enable(false); + PRINT(CRITICAL, "Cannot enable shot capture, shot storage buffer allocation failed!\r\n"); + } else { + k_mutex_lock(&FIFO_ring_buf_mutex, K_FOREVER); + FIFO_ring_buf.buf = p_data; + k_mutex_unlock(&FIFO_ring_buf_mutex); + } + } else { + SHOT_CAPTURE_enable(false); + if (err == ERRNO_EXCEED_FILE_SIZE) { + PRINT(CRITICAL, "Cannot enable shot capture, shot data size exceed max file size!\r\n"); + } else { + PRINT(CRITICAL, "Cannot enable shot capture, shot storage filename size exceed limit!\r\n"); + } + } + + k_mutex_lock(&shot_data_mutex, K_FOREVER); + shot_data.marked = false; + shot_data.ready = false; + shot_data.saved = true; + k_mutex_unlock(&shot_data_mutex); + } +} + +static void _shot_storage_read_task(void *p1, void *p2, void *p3) +{ + SHOT_STORAGE_file_s file; + + while (1) { + /* 1. Wait for a read request from CLI or BLE */ + int ret = k_sem_take(&read_shotdata_request, K_FOREVER); + if (ret != 0) { + continue; + } + + read_request_source_e source = (read_request_source_e)atomic_get(&_read_request_source); + + /* Disable shot capture and confrim idle (serialized with FSM via mutex)*/ + SHOT_CAPTURE_task_disable_and_wait(); + + /* Wait for write task to finish and confirm idle */ + SHOT_STORAGE_wait_write_task_idle(); + + /* read last shot data from flash to _shot_storage_buf */ + uint8_t *p_data = SHOT_STORAGE_last_read(&file); + + if (NULL == p_data) { + PRINT(INFO, "No file found!\r\n"); + + if (source == READ_REQ_BLE) { + /* Notify phone that no file was found */ + BLE_shot_data_not_found(); + } else if (source == READ_REQ_CLI) { + /* reset read_shotdata_buf_ready so --read command does not block */ + k_sem_reset(&read_shotdata_buf_ready); + } + /* Resume capture. FSM will call SHOT_CAPTURE_enable(true) if appropriate */ + SHOT_CAPTURE_task_resume(); + continue; + } + + if (source == READ_REQ_BLE) { + /* Notify phone that shot data is ready to be read */ + BLE_shot_data_ready(_shot_storage_buf, + sizeof(SHOT_STORAGE_file_s) + file.shot_data_size); + } else { + /* Notify the CLI read command that buffer is ready */ + k_sem_give(&read_shotdata_buf_ready); + } + + /* Wait for the requester to finish consuming the data */ + if (source == READ_REQ_BLE) { + /* + * BLE path: wait for phone to confirm it received the data. + * Use a timeout so we don't block forever if phone is unresponsive. + * If BLE disconnects during the read, the disconnect callback will + * call k_sem_reset, causing k_sem_take to return -EAGAIN. + */ + ret = k_sem_take(&read_shotdata_consumed, K_MSEC(10000)); + if (ret == 0) { + /* Phone confirmed receipt — safe to erase */ + SHOT_STORAGE_last_erase(); + PRINT(INFO, "Power cnt: %u, time: %.3f s erased...\r\n", file.power_cnt, (float) file.time_of_shot / 1000.0); + } else { + /* Timeout or disconnect — do NOT erase so phone can retry */ + BLE_shot_data_timeout(); + PRINT(WARNING, "BLE read not confirmed (ret=%d), keeping flash data for retry\r\n", ret); + } + } else if (source == READ_REQ_CLI) { + /* + * CLI path: CLI handler will take read_shotdata_buf_ready, + * print the data, call SHOT_STORAGE_last_erase(), then + * give read_shotdata_consumed. + */ + k_sem_take(&read_shotdata_consumed, K_FOREVER); + } + + atomic_set(&_read_request_source, READ_REQ_NONE); + /* Resume shot capture. FSM will re-enable if appropriate */ + SHOT_CAPTURE_task_resume(); + } +} + +static void _shot_storage_tracking_read (void) +{ + ERRNO_e err; + SHOT_STORAGE_tracking_s trk; + + /* allocate two sectors to store this tracking file because it updates for every shot */ + err = FS_open_or_create(SHOT_STORAGE_TRACKING_FILENAME, &_shot_storage_tracking_file, + sizeof(SHOT_STORAGE_tracking_s), (FS_SECTOR_SIZE / (sizeof(SHOT_STORAGE_tracking_s) + + FS_FILE_INDEX_SIZE + + FS_FILE_CHECKSUM_SIZE)) * + (SHOT_STORAGE_TRACKING_FILE_SECTOR - 1)); + + if (err == ERRNO_SUCCESS) { + err = FS_read(_shot_storage_tracking_file, (uint8_t *)&trk, 0, sizeof(SHOT_STORAGE_tracking_s)); + if (err != ERRNO_SUCCESS) { + /* First boot or corrupt — write defaults */ + _shot_storage_tracking_write(); + return; + } + + /* Validate read data */ + if (trk.write_idx >= SHOT_STORAGE_MAX_FILES || trk.file_cnt > SHOT_STORAGE_MAX_FILES) { + PRINT(WARNING, "Invalid tracking data, resetting to defaults\r\n"); + _shot_storage_tracking_write(); + return; + } + + _shot_storage_tracking = trk; + } else if (err == ERRNO_SIZE_MISMATCH) { + /* Struct size changed — delete old file and recreate */ + err = FS_delete(SHOT_STORAGE_TRACKING_FILENAME); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failure to delete old tracking file.\r\n"); + return; + } + + err = FS_open_or_create(SHOT_STORAGE_TRACKING_FILENAME, &_shot_storage_tracking_file, + sizeof(SHOT_STORAGE_tracking_s), (FS_SECTOR_SIZE / (sizeof(SHOT_STORAGE_tracking_s) + + FS_FILE_INDEX_SIZE + + FS_FILE_CHECKSUM_SIZE)) * + (SHOT_STORAGE_TRACKING_FILE_SECTOR - 1)); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failure to create tracking file.\r\n"); + } else { + PRINT(INFO, "New shot storage tracking file created successfully\r\n"); + } + + _shot_storage_tracking_write(); + } else { + _shot_storage_tracking_write(); + } +} + +static ERRNO_e _shot_storage_tracking_write (void) +{ + ERRNO_e err; + + err = FS_write(_shot_storage_tracking_file, (uint8_t *)&_shot_storage_tracking); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to write shot storage tracking to file. Errro: %d \r\n", err); + } + + return err; +} \ No newline at end of file diff --git a/src/app/shot_storage.h b/src/app/shot_storage.h new file mode 100644 index 0000000..41a9aec --- /dev/null +++ b/src/app/shot_storage.h @@ -0,0 +1,176 @@ +/** + * @file shot_storage.h + * @brief Shot Storage Module + * @author Kenny Tran + * @date Sep 02 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef SHOT_STORAGE_H_ +#define SHOT_STORAGE_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include "../lib/errno.h" + +#include "../app/motion.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define SHOT_STORAGE_WRITE_THREAD_STACK_SIZE (1024) +#define SHOT_STORAGE_READ_THREAD_STACK_SIZE (1024) +#define SHOT_STORAGE_WRITE_THREAD_PRIORITY (10) +#define SHOT_STORAGE_READ_THREAD_PRIORITY (5) + +#define SHOT_STORAGE_MAX_FILES (16) + +#define SHOT_STORAGE_FILENAME_LEN (16) + +#define SHOT_STORAGE_TRACKING_FILENAME ("shot_strg_trk") +#define SHOT_STORAGE_TRACKING_FILE_SECTOR (3) + +#define _SHOT_STORAGE_SPLIT_BYTE(x, offset) (0xFF & (x >> (8 * offset))) + +#define _SHOT_STORAGE_START_ADDR (NVM_EXT_SIZE / 2) +#define _SHOT_STORAGE_END_ADDR (NVM_EXT_SIZE - 1) + +#define _SHOT_STORAGE_SECTOR_SIZE (4096) + +#define _SHOT_STORAGE_TOTAL_FILE_SIZE (65536) +#define _SHOT_STORAGE_FILE_SIZE (_SHOT_STORAGE_TOTAL_FILE_SIZE - sizeof(SHOT_STORAGE_file_s)) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct +{ + uint8_t header_ver; + uint32_t power_cnt; + uint32_t time_of_shot; + uint32_t reserved; + uint8_t shot_data_format_ver; + uint32_t ambient_light_intensity; + uint32_t shot_data_offset; + uint32_t shot_data_size; + uint8_t gyro_bits_per_axis; + uint8_t accel_bits_per_axis; + uint16_t gyro_sample_rate_hz; + uint16_t accel_sample_rate_hz; + uint16_t gyro_full_scale_dps; + uint8_t accel_full_scale_g; + uint16_t IMU_orientation; + MOTION_calibration_data_s accel_cal; + MOTION_calibration_data_s gyro_cal; +} __attribute__((packed, aligned(1))) SHOT_STORAGE_file_s; + +/** + * @brief Tracking info for circular file storage. + * Persisted to internal flash across reboots. + */ +typedef struct +{ + uint32_t write_idx; /* Next slot to write to (0 to MAX_FILES-1, wraps) */ + uint32_t file_cnt; /* Number of valid files currently stored (0 to MAX_FILES) */ + uint32_t total_written; /* Total files ever written (monotonic, for unique naming) */ +} __attribute__((packed, aligned(1))) SHOT_STORAGE_tracking_s; + +/** + * Stream cursor that presents a linear view over: + * [header] + [circular buffer data starting at write_ptr] + * + * Handles all boundary crossings (header→data, buffer wrap) transparently. + * The caller just asks for N bytes at a time. + */ +struct stream_cursor { + const uint8_t *header; + uint32_t header_size; + + const uint8_t *buffer; + uint32_t data_size; /* effective size the circular buffer wraps at */ + uint32_t write_ptr; /* where writing stopped = logical start of data */ + uint32_t total; /* header_size + data_size, computed once at init */ + + uint32_t offset; /* current position in the logical stream */ +}; + +typedef enum { + READ_REQ_NONE = 0, + READ_REQ_CLI, + READ_REQ_BLE, +} read_request_source_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC VARIABLE + *------------------------------------------------------------------------------------------------*/ +extern struct k_sem read_shotdata_consumed; + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void SHOT_STORAGE_init (void); + +void SHOT_STORAGE_task_install (void); + +ERRNO_e SHOT_STORAGE_get_buf(uint32_t size, uint8_t **pp_data); + +ERRNO_e SHOT_STORAGE_file_create(void); + +void SHOT_STORAGE_begin_write(void); + +void SHOT_STORAGE_write(void); + +bool SHOT_STORAGE_write_done (void); + +uint8_t *SHOT_STORAGE_last_read (SHOT_STORAGE_file_s *p_file); + +void SHOT_STORAGE_last_erase (void); + +ERRNO_e SHOT_STORAGE_eraseall(void); + +uint32_t SHOT_STORAGE_get_file_cnt(void); + +void SHOT_STORAGE_cli_cmd (int32_t argc, char **argv); + +void SHOT_STORAGE_stream_cursor_init(struct stream_cursor *cur, + const uint8_t *header, uint32_t header_size, + const uint8_t *buffer, uint32_t data_size, + uint32_t write_ptr); + +uint32_t stream_cursor_read(struct stream_cursor *cur, uint8_t *dst, uint32_t len); + +uint32_t stream_cursor_remaining(struct stream_cursor *cur); + + +void SHOT_STORAGE_wait_write_task_idle(void); + +/** + * @brief Request a shot data read from flash. + * @param source Who is requesting (CLI or BLE). + */ +void SHOT_STORAGE_request_read(read_request_source_e source); + +/** + * @brief Wait until the read buffer is ready. + * @return 0 on success, negative on error. + */ +int SHOT_STORAGE_wait_buf_ready(void); + +/** + * @brief Signal that the consumer has finished reading the buffer. + */ +void SHOT_STORAGE_read_done(void); + + +#endif /* SHOT_STORAGE_H_ */ diff --git a/src/app/uuid.h b/src/app/uuid.h new file mode 100644 index 0000000..a9dff19 --- /dev/null +++ b/src/app/uuid.h @@ -0,0 +1,181 @@ +/** + * @file uuid.h + * @brief UUID definitions + * @author Kenny Tran + * @date Jul 10 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef UUID_H_ +#define UUID_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +/* BLE */ +#define UUID_BLE_ADV_NAME_VAL BT_UUID_128_ENCODE(0x00001FFF, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BLE_ADV_NAME BT_UUID_DECLARE_128(UUID_BLE_ADV_NAME_VAL) + +/* BOND CLEAR */ +#define UUID_BLE_BOND_CLEAR_VAL BT_UUID_128_ENCODE(0x00001550, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BLE_BOND_CLEAR BT_UUID_DECLARE_128(UUID_BLE_BOND_CLEAR_VAL) + +/* BASE SERVICE */ +#define UUID_BASE_SERVICE_VAL BT_UUID_128_ENCODE(0xBA5E0000, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BASE_SERVICE BT_UUID_DECLARE_128(UUID_BASE_SERVICE_VAL) + +#define UUID_BASE_EPOCH_VAL BT_UUID_128_ENCODE(0xBA5E0001, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BASE_EPOCH BT_UUID_DECLARE_128(UUID_BASE_EPOCH_VAL) + +#define UUID_BASE_RTC_TICK_VAL BT_UUID_128_ENCODE(0xBA5E0002, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BASE_RTC_TICK BT_UUID_DECLARE_128(UUID_BASE_RTC_TICK_VAL) + +/* CONTROLS SERVICE */ +#define UUID_CONTROLS_SERVICE_VAL BT_UUID_128_ENCODE(0x00001523, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_CONTROLS_SERVICE BT_UUID_DECLARE_128(UUID_CONTROLS_SERVICE_VAL) + +/* APP */ +#define UUID_APP_MPCB_VERSION_VAL BT_UUID_128_ENCODE(0x00001500, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_MPCB_VERSION BT_UUID_DECLARE_128(UUID_APP_MPCB_VERSION_VAL) + +#define UUID_APP_RPCB_VERSION_VAL BT_UUID_128_ENCODE(0x00001501, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_RPCB_VERSION BT_UUID_DECLARE_128(UUID_APP_RPCB_VERSION_VAL) + +#define UUID_APP_VERSION_VAL BT_UUID_128_ENCODE(0x00001502, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_VERSION BT_UUID_DECLARE_128(UUID_APP_VERSION_VAL) + +#define UUID_APP_MFG_ID_VAL BT_UUID_128_ENCODE(0x00001503, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_MFG_ID BT_UUID_DECLARE_128(UUID_APP_MFG_ID_VAL) + +#define UUID_APP_MFG_DATE_VAL BT_UUID_128_ENCODE(0x00001504, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_MFG_DATE BT_UUID_DECLARE_128(UUID_APP_MFG_DATE_VAL) + +#define UUID_APP_PN_VAL BT_UUID_128_ENCODE(0x00001505, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_PN BT_UUID_DECLARE_128(UUID_APP_PN_VAL) + +#define UUID_APP_SN_VAL BT_UUID_128_ENCODE(0x00001506, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_SN BT_UUID_DECLARE_128(UUID_APP_SN_VAL) + +#define UUID_APP_POWER_COUNT_VAL BT_UUID_128_ENCODE(0x00001507, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_POWER_COUNT BT_UUID_DECLARE_128(UUID_APP_POWER_COUNT_VAL) + +#define UUID_APP_CLI_ENABLED_VAL BT_UUID_128_ENCODE(0x00001508, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_CLI_ENABLED BT_UUID_DECLARE_128(UUID_APP_CLI_ENABLED_VAL) + +#define UUID_APP_RESET_VAL BT_UUID_128_ENCODE(0x00001509, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_APP_RESET BT_UUID_DECLARE_128(UUID_APP_RESET_VAL) + +/* SCOPE RING */ +#define UUID_RING_COLOR_VAL BT_UUID_128_ENCODE(0x00001524, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_RING_COLOR BT_UUID_DECLARE_128(UUID_RING_COLOR_VAL) + +#define UUID_RING_BRIGHTNESS_VAL BT_UUID_128_ENCODE(0x00001525, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_RING_BRIGHTNESS BT_UUID_DECLARE_128(UUID_RING_BRIGHTNESS_VAL) + +#define UUID_INDICATOR_COLOR_VAL BT_UUID_128_ENCODE(0x00001526, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_INDICATOR_COLOR BT_UUID_DECLARE_128(UUID_INDICATOR_COLOR_VAL) + +#define UUID_INDICATOR_BRIGHTNESS_VAL BT_UUID_128_ENCODE(0x00001527, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_INDICATOR_BRIGHTNESS BT_UUID_DECLARE_128(UUID_INDICATOR_BRIGHTNESS_VAL) + +#define UUID_LEVEL_COLOR_VAL BT_UUID_128_ENCODE(0x00001528, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_LEVEL_COLOR BT_UUID_DECLARE_128(UUID_LEVEL_COLOR_VAL) + +#define UUID_LEVEL_BRIGHTNESS_VAL BT_UUID_128_ENCODE(0x00001529, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_LEVEL_BRIGHTNESS BT_UUID_DECLARE_128(UUID_LEVEL_BRIGHTNESS_VAL) + +#define UUID_LEVEL_SENSITIVITY_VAL BT_UUID_128_ENCODE(0x0000152E, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_LEVEL_SENSITIVITY BT_UUID_DECLARE_128(UUID_LEVEL_SENSITIVITY_VAL) + +#define UUID_LEVEL_VISCOSITY_VAL BT_UUID_128_ENCODE(0x00001532, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_LEVEL_VISCOSITY BT_UUID_DECLARE_128(UUID_LEVEL_VISCOSITY_VAL) + +#define UUID_LEVEL_OPTION_VAL BT_UUID_128_ENCODE(0x00001533, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_LEVEL_OPTION BT_UUID_DECLARE_128(UUID_LEVEL_OPTION_VAL) + +/* BUBBLE */ +#define UUID_BUBBLE_BRIGHTNESS_VAL BT_UUID_128_ENCODE(0x0000152A, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BUBBLE_BRIGHTNESS BT_UUID_DECLARE_128(UUID_BUBBLE_BRIGHTNESS_VAL) + +/* FIBER */ +#define UUID_FIBER_BRIGHTNESS_VAL BT_UUID_128_ENCODE(0x0000152B, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_FIBER_BRIGHTNESS BT_UUID_DECLARE_128(UUID_FIBER_BRIGHTNESS_VAL) + +/* PIN */ +#define UUID_PIN_BRIGHTNESS_VAL BT_UUID_128_ENCODE(0x0000152C, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_PIN_BRIGHTNESS BT_UUID_DECLARE_128(UUID_PIN_BRIGHTNESS_VAL) + +/* MOTION */ +#define UUID_LEVEL_CALIBRATE_VAL BT_UUID_128_ENCODE(0x0000152D, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_LEVEL_CALIBRATE BT_UUID_DECLARE_128(UUID_LEVEL_CALIBRATE_VAL) + +#define UUID_LEVEL_ANGLE_VAL BT_UUID_128_ENCODE(0x0000152F, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_LEVEL_ANGLE BT_UUID_DECLARE_128(UUID_LEVEL_ANGLE_VAL) + +/* ALS */ +#define UUID_AMBIENT_LIGHT_VAL BT_UUID_128_ENCODE(0x00001530, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_AMBIENT_LIGHT BT_UUID_DECLARE_128(UUID_AMBIENT_LIGHT_VAL) + +/* FSM */ +#define UUID_FSM_TIMEOUT_VAL BT_UUID_128_ENCODE(0x00001531, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_FSM_TIMEOUT BT_UUID_DECLARE_128(UUID_FSM_TIMEOUT_VAL) + +#define UUID_FSM_AUTO_BRIGHTNESS_VAL BT_UUID_128_ENCODE(0x00001534, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_FSM_AUTO_BRIGHTNESS BT_UUID_DECLARE_128(UUID_FSM_AUTO_BRIGHTNESS_VAL) + +#define UUID_FSM_MODE_VAL BT_UUID_128_ENCODE(0x00001541, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_FSM_MODE BT_UUID_DECLARE_128(UUID_FSM_MODE_VAL) + +#define UUID_FSM_APP_CONFIG_VAL BT_UUID_128_ENCODE(0x00001535, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_FSM_APP_CONFIG BT_UUID_DECLARE_128(UUID_FSM_APP_CONFIG_VAL) + +/* BATTERY */ +#define UUID_BATTERY_STATUS_VAL BT_UUID_128_ENCODE(0x00001540, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BATTERY_STATUS BT_UUID_DECLARE_128(UUID_BATTERY_STATUS_VAL) + +/* SHOT_CAPTURE */ +#define UUID_SHOT_CAPTURE_SERVICE_VAL \ + BT_UUID_128_ENCODE(0xdc1753a0, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_TAP_THS_VAL \ + BT_UUID_128_ENCODE(0xdc1753a1, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_QUIET_TIME_VAL \ + BT_UUID_128_ENCODE(0xdc1753a2, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_SHOCK_TIME_VAL \ + BT_UUID_128_ENCODE(0xdc1753a3, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_USER_SHOT_CNT_VAL \ + BT_UUID_128_ENCODE(0xdc1753a4, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_SHOT_CNT_VAL \ + BT_UUID_128_ENCODE(0xdc1753a5, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_CAPTURE_EN_VAL \ + BT_UUID_128_ENCODE(0xdc1753a6, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_DUR_LOC_VAL \ + BT_UUID_128_ENCODE(0xdc1753a7, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_SHOT_DATA_REQUEST_VAL \ + BT_UUID_128_ENCODE(0xdc1753a8, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) +#define UUID_SHOT_CAPTURE_SHOT_DATA_VAL \ + BT_UUID_128_ENCODE(0xdc1753a9, 0x3838, 0x4d8f, 0xb74a, 0x340723444895) + +#define UUID_SHOT_CAPTURE_SERVICE BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_SERVICE_VAL) +#define UUID_SHOT_CAPTURE_TAP_THS BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_TAP_THS_VAL) +#define UUID_SHOT_CAPTURE_QUIET_TIME BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_QUIET_TIME_VAL) +#define UUID_SHOT_CAPTURE_SHOCK_TIME BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_SHOCK_TIME_VAL) +#define UUID_SHOT_CAPTURE_USER_SHOT_CNT BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_USER_SHOT_CNT_VAL) +#define UUID_SHOT_CAPTURE_SHOT_CNT BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_SHOT_CNT_VAL) +#define UUID_SHOT_CAPTURE_CAPTURE_EN BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_CAPTURE_EN_VAL) +#define UUID_SHOT_CAPTURE_DUR_LOC BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_DUR_LOC_VAL) +#define UUID_SHOT_CAPTURE_SHOT_DATA_REQUEST BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_SHOT_DATA_REQUEST_VAL) +#define UUID_SHOT_CAPTURE_SHOT_DATA BT_UUID_DECLARE_128(UUID_SHOT_CAPTURE_SHOT_DATA_VAL) + + +#endif /* UUID_H_ */ diff --git a/src/app/uuid_base.h b/src/app/uuid_base.h new file mode 100644 index 0000000..a0d858a --- /dev/null +++ b/src/app/uuid_base.h @@ -0,0 +1,35 @@ +/** + * @file uuid_base.h + * @brief UUID base definitions + * @author Kenny Tran + * @date Mar 19 2026 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef UUID_BASE_H_ +#define UUID_BASE_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +/* BASE SERVICE */ +#define UUID_BASE_SERVICE_VAL BT_UUID_128_ENCODE(0xBA5E0000, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_BASE_SERVICE BT_UUID_DECLARE_128(UUID_BASE_SERVICE_VAL) + +/* RTC */ +#define UUID_RTC_VAL BT_UUID_128_ENCODE(0xBA5E0100, 0x1212, 0xEFDE, 0x1523, 0x785FEABCD123) +#define UUID_RTC BT_UUID_DECLARE_128(UUID_RTC_VAL) + + +#endif /* UUID_BASE_H_ */ diff --git a/src/bsp/als.c b/src/bsp/als.c new file mode 100644 index 0000000..bf62967 --- /dev/null +++ b/src/bsp/als.c @@ -0,0 +1,634 @@ +/** + * @file ltr329.c + * @brief Driver for LTR-329 ambient light sensor. + * https://www.mouser.com/datasheet/2/239/Lite-On_LTR-329ALS-01%20DS_ver1.1-348647.pdf + * @author Kenny Tran + * @date Mar 06 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../wrappers/i2c.h" + +#include "als.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _LTR329_I2C_DEV_ADDR (0x29) /* I2C address */ +#define _LTR329_REG_PART_ID (0x86) /* Part RD/revision register */ +#define _LTR329_REG_MFG_ID (0x87) /* Manufacturer ID register */ +#define _LTR329_REG_CTRL (0x80) /* ALS control register */ +#define _LTR329_REG_MEAS_RATE (0x85) /* Integration time and data rate */ +#define _LTR329_REG_CH1_0_DATA (0x88) /* Data for channel 1 (read all 4 bytes) */ +#define _LTR329_REG_CH1_1_DATA (0x89) +#define _LTR329_REG_CH0_0_DATA (0x8A) +#define _LTR329_REG_CH0_1_DATA (0x8B) +#define _LTR329_REG_STATUS (0x8C) /* Status register */ + +#define _LTR329_PART_ID (0xA0) /* Expected part ID */ +#define _LTR329_MFG_ID (0x05) /* Expected manufacturing ID */ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct +{ + const uint8_t reg; + const uint8_t bit_start; + const uint8_t bit_len; +} _als_field_s; + +typedef struct +{ + const _als_field_s gain; + const _als_field_s rst; + const _als_field_s enable; + const _als_field_s int_time; + const _als_field_s measure_rate; + const _als_field_s part_no; + const _als_field_s rev_id; + const _als_field_s mfg_id; + const _als_field_s data_valid; + const _als_field_s gain_range; + const _als_field_s data_rdy; +} _als_fields_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _als_init = false; +static const _als_fields_s _als_fields = { + .gain = (_als_field_s) { .reg = _LTR329_REG_CTRL, .bit_start = 4, .bit_len = 3 }, + .rst = (_als_field_s) { .reg = _LTR329_REG_CTRL, .bit_start = 1, .bit_len = 1 }, + .enable = (_als_field_s) { .reg = _LTR329_REG_CTRL, .bit_start = 0, .bit_len = 1 }, + .int_time = (_als_field_s) { .reg = _LTR329_REG_MEAS_RATE, .bit_start = 5, .bit_len = 3 }, + .measure_rate = (_als_field_s) { .reg = _LTR329_REG_MEAS_RATE, .bit_start = 2, .bit_len = 3 }, + .part_no = (_als_field_s) { .reg = _LTR329_REG_PART_ID, .bit_start = 7, .bit_len = 4 }, + .rev_id = (_als_field_s) { .reg = _LTR329_REG_PART_ID, .bit_start = 3, .bit_len = 4 }, + .mfg_id = (_als_field_s) { .reg = _LTR329_REG_MFG_ID, .bit_start = 7, .bit_len = 8 }, + .data_valid = (_als_field_s) { .reg = _LTR329_REG_STATUS, .bit_start = 7, .bit_len = 1 }, + .gain_range = (_als_field_s) { .reg = _LTR329_REG_STATUS, .bit_start = 6, .bit_len = 3 }, + .data_rdy = (_als_field_s) { .reg = _LTR329_REG_STATUS, .bit_start = 2, .bit_len = 1 } +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e ALS_init (void) +{ + uint8_t rx; + + if (_als_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (I2C_init (I2C_CH_0) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + /* Read part ID. */ + if (I2C_bytes_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _LTR329_REG_PART_ID, &rx, 1) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + /* Return error if part ID mismatch. */ + if (rx != _LTR329_PART_ID) + { + return ERRNO_ID_FAILURE; + } + + /* Read manufacturing ID. */ + if (I2C_bytes_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _LTR329_REG_MFG_ID, &rx, 1) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + /* Return error if manufacturing ID mismatch. */ + if (rx != _LTR329_MFG_ID) + { + return ERRNO_ID_FAILURE; + } + + /* Set init flag */ + _als_init = true; + + /* Perform a soft reset. */ + return ALS_reset (); +} + +ERRNO_e ALS_reset (void) +{ + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Write soft reset bit to control register. */ + if (I2C_bits_write (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.rst.reg, + 1, _als_fields.rst.bit_start, _als_fields.rst.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + /* Per datasheet, need to wait at least 10 ms after reset. */ + k_busy_wait (10000); + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_enable (bool en) +{ + uint8_t val = (uint8_t) en; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (en) + { + /* Write enable bit to control register. */ + if (I2C_bits_write (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.enable.reg, + val, _als_fields.enable.bit_start, _als_fields.enable.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + } + else + { + if (I2C_bits_write (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.enable.reg, + val, _als_fields.enable.bit_start, _als_fields.enable.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + } + + /* Per datasheet, need to wait 200 ms for measurement. */ + k_sleep (K_MSEC(200)); + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_is_enabled (bool *p_enabled) +{ + uint8_t rx; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (I2C_bits_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.enable.reg, &rx, + _als_fields.enable.bit_start, _als_fields.enable.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + *p_enabled = (bool) rx; + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_gain_set (ALS_gain_e gain) +{ + uint8_t tx = (uint8_t) gain; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Write gain bits to control register. */ + if (I2C_bits_write (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.gain.reg, + tx, _als_fields.gain.bit_start, _als_fields.gain.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_gain_get (ALS_gain_e *p_gain) +{ + uint8_t rx; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Read gain bits from control register. */ + if (I2C_bits_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.gain.reg, + &rx, _als_fields.gain.bit_start, _als_fields.gain.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + *p_gain = (ALS_gain_e) rx; + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_int_time_set (ALS_int_time_e time) +{ + uint8_t tx = (uint8_t) time; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Write integration time bits to measure rate register. */ + if (I2C_bits_write (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.int_time.reg, + tx, _als_fields.int_time.bit_start, _als_fields.int_time.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_int_time_get (ALS_int_time_e *p_time) +{ + uint8_t rx; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Read integration time bits from measure rate register. */ + if (I2C_bits_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.int_time.reg, + &rx, _als_fields.int_time.bit_start, _als_fields.int_time.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + *p_time = (ALS_int_time_e) rx; + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_measure_rate_set (ALS_measure_rate_e rate) +{ + uint8_t tx = (uint8_t) rate; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Write measurement bits to measure rate register. */ + if (I2C_bits_write (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.measure_rate.reg, + tx, _als_fields.measure_rate.bit_start, _als_fields.measure_rate.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_measure_rate_get (ALS_measure_rate_e *p_rate) +{ + uint8_t rx; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Read measurement bits from measure rate register. */ + if (I2C_bits_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.measure_rate.reg, + &rx, _als_fields.measure_rate.bit_start, _als_fields.measure_rate.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + *p_rate = (ALS_measure_rate_e) rx; + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_new_data_available (bool *p_available) +{ + uint8_t rx; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Read measurement bits from measure rate register. */ + if (I2C_bits_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _als_fields.data_rdy.reg, + &rx, _als_fields.data_rdy.bit_start, _als_fields.data_rdy.bit_len) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + *p_available = (bool) rx; + + return ERRNO_SUCCESS; +} + +ERRNO_e ALS_read (ALS_data_s *p_data) +{ + ALS_data_s rx; + + if (!_als_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Read measurement bits from measure rate register. */ + if (I2C_bytes_read (I2C_CH_0, _LTR329_I2C_DEV_ADDR, _LTR329_REG_CH1_0_DATA, + (uint8_t *) &rx, sizeof (rx)) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + *p_data = rx; + + return ERRNO_SUCCESS; +} + +void ALS_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + ALS_gain_e gain; + ALS_int_time_e int_time; + ALS_measure_rate_e meas_rate; + ALS_data_s data; + bool enabled; + + (void) ALS_gain_get (&gain); + (void) ALS_int_time_get (&int_time); + (void) ALS_measure_rate_get (&meas_rate); + (void) ALS_read (&data); + (void) ALS_is_enabled (&enabled); + + + PRINT (INFO, "LTR329\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Enabled : %s\r\n", (enabled ? "true" : "false")); + PRINT (INFO, "Gain : "); + switch (gain) + { + case ALS_GAIN_1: + PRINT (INFO, "1\r\n"); + break; + case ALS_GAIN_2: + PRINT (INFO, "2\r\n"); + break; + case ALS_GAIN_4: + PRINT (INFO, "4\r\n"); + break; + case ALS_GAIN_8: + PRINT (INFO, "8\r\n"); + break; + case ALS_GAIN_48: + PRINT (INFO, "48\r\n"); + break; + case ALS_GAIN_96: + PRINT (INFO, "96\r\n"); + break; + } + PRINT (INFO, "Integration Time (ms) : "); + switch (int_time) + { + case ALS_INT_TIME_100MS: + PRINT (INFO, "100\r\n"); + break; + case ALS_INT_TIME_50MS: + PRINT (INFO, "50\r\n"); + break; + case ALS_INT_TIME_200MS: + PRINT (INFO, "200\n"); + break; + case ALS_INT_TIME_400MS: + PRINT (INFO, "400\n"); + break; + case ALS_INT_TIME_150MS: + PRINT (INFO, "150\r\n"); + break; + case ALS_INT_TIME_250MS: + PRINT (INFO, "250\r\n"); + break; + case ALS_INT_TIME_300MS: + PRINT (INFO, "300\r\n"); + break; + case ALS_INT_TIME_350MS: + PRINT (INFO, "350\r\n"); + break; + } + PRINT (INFO, "Measurement Rate (ms) : "); + switch (meas_rate) + { + case ALS_MEASURE_RATE_50MS: + PRINT (INFO, "50\r\n"); + break; + case ALS_MEASURE_RATE_100MS: + PRINT (INFO, "100\r\n"); + break; + case ALS_MEASURE_RATE_200MS: + PRINT (INFO, "200\n"); + break; + case ALS_MEASURE_RATE_500MS: + PRINT (INFO, "500\n"); + break; + case ALS_MEASURE_RATE_1000MS: + PRINT (INFO, "1000\r\n"); + break; + case ALS_MEASURE_RATE_2000MS: + PRINT (INFO, "2000\r\n"); + break; + } + PRINT (INFO, "Visible : %d\r\n", data.visible); + PRINT (INFO, "Infrared : %d\r\n", data.infrared); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_init ()); + } + else if (!strcmp (argv[0], "--reset") || !strcmp (argv[0], "-r")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_reset ()); + } + else + { + goto USAGE; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "--enable") || !strcmp (argv[0], "-e")) + { + if (!strcmp (argv[1], "true")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_enable (true)); + } + else if (!strcmp (argv[1], "false")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_enable (false)); + } + + } + else if (!strcmp (argv[0], "-g")) + { + if (!strcmp (argv[1], "1")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_gain_set (ALS_GAIN_1)); + } + else if (!strcmp (argv[1], "2")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_gain_set (ALS_GAIN_2)); + } + else if (!strcmp (argv[1], "4")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_gain_set (ALS_GAIN_4)); + } + else if (!strcmp (argv[1], "8")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_gain_set (ALS_GAIN_8)); + } + else if (!strcmp (argv[1], "48")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_gain_set (ALS_GAIN_48)); + } + else if (!strcmp (argv[1], "96")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_gain_set (ALS_GAIN_96)); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-it")) + { + if (!strcmp (argv[1], "100")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_100MS)); + } + else if (!strcmp (argv[1], "50")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_50MS)); + } + else if (!strcmp (argv[1], "200")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_200MS)); + } + else if (!strcmp (argv[1], "400")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_400MS)); + } + else if (!strcmp (argv[1], "150")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_150MS)); + } + else if (!strcmp (argv[1], "250")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_250MS)); + } + else if (!strcmp (argv[1], "300")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_300MS)); + } + else if (!strcmp (argv[1], "350")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_int_time_set (ALS_INT_TIME_350MS)); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-mr")) + { + if (!strcmp (argv[1], "50")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_measure_rate_set (ALS_MEASURE_RATE_50MS)); + } + else if (!strcmp (argv[1], "100")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_measure_rate_set (ALS_MEASURE_RATE_100MS)); + } + else if (!strcmp (argv[1], "200")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_measure_rate_set (ALS_MEASURE_RATE_200MS)); + } + else if (!strcmp (argv[1], "500")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_measure_rate_set (ALS_MEASURE_RATE_500MS)); + } + else if (!strcmp (argv[1], "1000")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_measure_rate_set (ALS_MEASURE_RATE_1000MS)); + } + else if (!strcmp (argv[1], "2000")) + { + PRINT (INFO, "Ret code : %d\r\n", ALS_measure_rate_set (ALS_MEASURE_RATE_2000MS)); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: als [OPTION...]\r\n"); + PRINT (INFO, " -i | --init Initializes LTR329 ALS module\r\n"); + PRINT (INFO, " -r | --reset Resets LTR329 ALS module\r\n"); + PRINT (INFO, " -e | --enable Enable LTR329 ALS to start measuring\r\n"); + PRINT (INFO, " <%%b> true = enable\r\n"); + PRINT (INFO, " false = disable\r\n"); + PRINT (INFO, " -g Sets the gain\r\n"); + PRINT (INFO, " <%%d> [1, 2, 4, 8, 48, 96]\r\n"); + PRINT (INFO, " -it Sets the integration time (ms)\r\n"); + PRINT (INFO, " <%%d> [100, 50, 200, 400, 150, 250, 300, 350]\r\n"); + PRINT (INFO, " -mr Sets the measurement rate (ms)\r\n"); + PRINT (INFO, " <%%d> [50, 100, 200, 500, 1000, 2000]\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/bsp/als.h b/src/bsp/als.h new file mode 100644 index 0000000..0c645ba --- /dev/null +++ b/src/bsp/als.h @@ -0,0 +1,105 @@ +/** + * @file als.h + * @brief Driver for LTR-329 ambient light sensor. + * https://www.mouser.com/datasheet/2/239/Lite-On_LTR-329ALS-01%20DS_ver1.1-348647.pdf + * @author Kenny Tran + * @date Mar 06 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef ALS_H_ +#define ALS_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum ALS_gain +{ + ALS_GAIN_1 = 0, + ALS_GAIN_2 = 1, + ALS_GAIN_4 = 2, + ALS_GAIN_8 = 3, + /* Note: 4 & 5 unused .*/ + ALS_GAIN_48 = 6, + ALS_GAIN_96 = 7 +} ALS_gain_e; + +typedef enum ALS_int_time +{ + ALS_INT_TIME_100MS, + ALS_INT_TIME_50MS, + ALS_INT_TIME_200MS, + ALS_INT_TIME_400MS, + ALS_INT_TIME_150MS, + ALS_INT_TIME_250MS, + ALS_INT_TIME_300MS, + ALS_INT_TIME_350MS, +} ALS_int_time_e; + +typedef enum ALS_measure_rate +{ + ALS_MEASURE_RATE_50MS, + ALS_MEASURE_RATE_100MS, + ALS_MEASURE_RATE_200MS, + ALS_MEASURE_RATE_500MS, + ALS_MEASURE_RATE_1000MS, + ALS_MEASURE_RATE_2000MS, +} ALS_measure_rate_e; + +typedef struct ALS_data +{ + uint16_t visible; + uint16_t infrared; +} ALS_data_s; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e ALS_init (void); + +ERRNO_e ALS_reset (void); + +ERRNO_e ALS_enable (bool en); + +ERRNO_e ALS_is_enabled (bool *p_enabled); + +ERRNO_e ALS_gain_set (ALS_gain_e gain); + +ERRNO_e ALS_gain_get (ALS_gain_e *p_gain); + +ERRNO_e ALS_int_time_set (ALS_int_time_e time); + +ERRNO_e ALS_int_time_get (ALS_int_time_e *p_time); + +ERRNO_e ALS_measure_rate_set (ALS_measure_rate_e rate); + +ERRNO_e ALS_measure_rate_get (ALS_measure_rate_e *p_rate); + +ERRNO_e ALS_new_data_available (bool *p_available); + +ERRNO_e ALS_read (ALS_data_s *p_data); + +void ALS_cli_cmd (int32_t argc, char **argv); + + +#endif /* ALS_H_ */ diff --git a/src/bsp/battery.c b/src/bsp/battery.c new file mode 100644 index 0000000..e57d085 --- /dev/null +++ b/src/bsp/battery.c @@ -0,0 +1,416 @@ +/** + * @file battery.h + * @brief Battery module + * @author Kenny Tran + * @date Dec 02 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../wrappers/adc.h" +#include "../wrappers/gpio.h" + +#include "../app/app.h" + +#include "battery.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _BATTERY_CAPACITY_STEPS (11) +#define _BATTERY_N_CURVES (5) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct +{ + float voltage; + float capacity; /* NOTE: 1.0 = 100 % */ +} _battery_point_s; + +typedef struct +{ + int8_t temp_c; + _battery_point_s curve_readings[_BATTERY_CAPACITY_STEPS]; +} _battery_table_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _battery_init = false; +static _battery_table_s _battery_tables[_BATTERY_N_CURVES] = +{ + { + .temp_c = -20, + { (_battery_point_s) { .voltage = 4.20, .capacity = 1.00 }, + (_battery_point_s) { .voltage = 3.36, .capacity = 0.90 }, + (_battery_point_s) { .voltage = 3.34, .capacity = 0.80 }, + (_battery_point_s) { .voltage = 3.32, .capacity = 0.70 }, + (_battery_point_s) { .voltage = 3.29, .capacity = 0.60 }, + (_battery_point_s) { .voltage = 3.24, .capacity = 0.50 }, + (_battery_point_s) { .voltage = 3.19, .capacity = 0.40 }, + (_battery_point_s) { .voltage = 3.08, .capacity = 0.30 }, + (_battery_point_s) { .voltage = 2.84, .capacity = 0.20 }, + (_battery_point_s) { .voltage = 2.50, .capacity = 0.10 }, + (_battery_point_s) { .voltage = 2.00, .capacity = 0.00 } } + }, + { + .temp_c = -10, + { (_battery_point_s) { .voltage = 4.20, .capacity = 1.00 }, + (_battery_point_s) { .voltage = 3.58, .capacity = 0.90 }, + (_battery_point_s) { .voltage = 3.55, .capacity = 0.80 }, + (_battery_point_s) { .voltage = 3.52, .capacity = 0.70 }, + (_battery_point_s) { .voltage = 3.48, .capacity = 0.60 }, + (_battery_point_s) { .voltage = 3.43, .capacity = 0.50 }, + (_battery_point_s) { .voltage = 3.34, .capacity = 0.40 }, + (_battery_point_s) { .voltage = 3.27, .capacity = 0.30 }, + (_battery_point_s) { .voltage = 3.15, .capacity = 0.20 }, + (_battery_point_s) { .voltage = 2.90, .capacity = 0.10 }, + (_battery_point_s) { .voltage = 2.50, .capacity = 0.00 } } + }, + { + .temp_c = 0, + { (_battery_point_s) { .voltage = 4.20, .capacity = 1.00 }, + (_battery_point_s) { .voltage = 3.87, .capacity = 0.90 }, + (_battery_point_s) { .voltage = 3.78, .capacity = 0.80 }, + (_battery_point_s) { .voltage = 3.73, .capacity = 0.70 }, + (_battery_point_s) { .voltage = 3.70, .capacity = 0.60 }, + (_battery_point_s) { .voltage = 3.68, .capacity = 0.50 }, + (_battery_point_s) { .voltage = 3.65, .capacity = 0.40 }, + (_battery_point_s) { .voltage = 3.60, .capacity = 0.30 }, + (_battery_point_s) { .voltage = 3.54, .capacity = 0.20 }, + (_battery_point_s) { .voltage = 3.37, .capacity = 0.10 }, + (_battery_point_s) { .voltage = 2.58, .capacity = 0.00 } } + }, + { + .temp_c = 25, + { (_battery_point_s) { .voltage = 4.20, .capacity = 1.00 }, + (_battery_point_s) { .voltage = 4.03, .capacity = 0.90 }, + (_battery_point_s) { .voltage = 3.96, .capacity = 0.80 }, + (_battery_point_s) { .voltage = 3.91, .capacity = 0.70 }, + (_battery_point_s) { .voltage = 3.89, .capacity = 0.60 }, + (_battery_point_s) { .voltage = 3.87, .capacity = 0.50 }, + (_battery_point_s) { .voltage = 3.85, .capacity = 0.40 }, + (_battery_point_s) { .voltage = 3.81, .capacity = 0.30 }, + (_battery_point_s) { .voltage = 3.75, .capacity = 0.20 }, + (_battery_point_s) { .voltage = 3.62, .capacity = 0.10 }, + (_battery_point_s) { .voltage = 3.10, .capacity = 0.00 } } + }, + { + .temp_c = 60, + { (_battery_point_s) { .voltage = 4.20, .capacity = 1.00 }, + (_battery_point_s) { .voltage = 4.07, .capacity = 0.90 }, + (_battery_point_s) { .voltage = 4.02, .capacity = 0.80 }, + (_battery_point_s) { .voltage = 3.99, .capacity = 0.70 }, + (_battery_point_s) { .voltage = 3.97, .capacity = 0.60 }, + (_battery_point_s) { .voltage = 3.95, .capacity = 0.50 }, + (_battery_point_s) { .voltage = 3.91, .capacity = 0.40 }, + (_battery_point_s) { .voltage = 3.89, .capacity = 0.30 }, + (_battery_point_s) { .voltage = 3.83, .capacity = 0.20 }, + (_battery_point_s) { .voltage = 3.75, .capacity = 0.10 }, + (_battery_point_s) { .voltage = 3.37, .capacity = 0.00 } } + } +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static float _battery_interpolate_capacity_at_voltage (_battery_point_s *p_point, float voltage); +static float _battery_interpolate_capacity_across_temps (float T1, float T2, float T, float C1, float C2); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e BATTERY_init (void) +{ + if (_battery_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (GPIO_init () < ERRNO_SUCCESS) + { + return ERRNO_GPIO_FAILURE; + } + + if (GPIO_cfg (GPIO_PIN_BATTERY_CHECK_ENABLE, GPIO_DIRECTION_OUT, GPIO_PULLUP, GPIO_INIT_HIGH, GPIO_INT_STATE_CHANGE_LL_HIGH, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + + if (ADC_init () < ERRNO_SUCCESS) + { + return ERRNO_ADC_FAILURE; + } + + if (ADC_cfg (ADC_CH_0, ADC_GAIN_DIV6, ADC_REF_INT, ADC_RESOLUTION_14_BITS) != ERRNO_SUCCESS) + { + return ERRNO_ADC_FAILURE; + } + + /* Enable buffer to read battery voltage. Active-low. */ + if (GPIO_level_set (GPIO_PIN_BATTERY_CHECK_ENABLE, false)) + { + return ERRNO_GPIO_FAILURE; + } + + /* Configure GPIO to detect when charger / external battery is connected. */ + if (GPIO_cfg (GPIO_PIN_CHARGER_STATUS, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_HIGH, GPIO_INT_FALLING_EDGE, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + + _battery_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e BATTERY_wake_on_charge (bool enable) +{ + if (!_battery_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (enable) + { + /* Reconfigure GPIO interrupt for charge detection to be used to wake from sleep. + * Note: Only logic level type interrupts can be used for GPIO to wake MCU from sleep. This is because + * putting MCU to sleep uses a slower clock and GPIO sense, so rising and falling edge interrupts don't work. */ + if (GPIO_cfg (GPIO_PIN_CHARGER_STATUS, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_HIGH, GPIO_INT_LL_LOW, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + } + else + { + if (GPIO_cfg (GPIO_PIN_CHARGER_STATUS, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_HIGH, GPIO_INT_FALLING_EDGE, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + } + + return ERRNO_SUCCESS; +} + +bool BATTERY_is_charging (void) +{ + /* Active-low */ + return !GPIO_level_get (GPIO_PIN_CHARGER_STATUS); +} + +ERRNO_e BATTERY_voltage_get (float *p_voltage) +{ + const int32_t adc_fullscale = (1 << ADC_RESOLUTION_14_BITS) - 1; + const float v_scaler = BATTERY_VOLTAGE_DIVIDER_R2_OHMS / (BATTERY_VOLTAGE_DIVIDER_R1_OHMS + BATTERY_VOLTAGE_DIVIDER_R2_OHMS); + int32_t adc_raw; + + if (!_battery_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Enable buffer to read battery voltage. Active-low. */ + // if (GPIO_level_set (GPIO_PIN_BATTERY_CHECK_ENABLE, false)) + // { + // return ERRNO_GPIO_FAILURE; + // } + + if (ADC_read (ADC_CH_0, &adc_raw) != ERRNO_SUCCESS) + { + return ERRNO_ADC_FAILURE; + } + + /* NOTE: We are using ADC_REF_INT for reference (0.6V), with prescaler of 1/6. Therefore, the maximum ADC value will be 16383 (14-bits) + with an input voltage of (0.6V * 6) = 3.6V. */ + *p_voltage = ((float) adc_raw / (float) adc_fullscale) * 3.6f; + + /* NOTE: With voltage divider of R2 = 510k and R1 = 1M. A full charge of 4.2V will be seen as ((4.2V * 510k) / (510k + 1M)) ~= 1.419V. + Divide by scaler to get actual voltage. */ + *p_voltage /= v_scaler; + + /* Disable buffer to read battery voltage. Active-low. */ + // if (GPIO_level_set (GPIO_PIN_BATTERY_CHECK_ENABLE, true)) + // { + // return ERRNO_GPIO_FAILURE; + // } + + return ERRNO_SUCCESS; +} + +float BATTERY_level_calculate (float voltage) +{ + float temp = APP_temp_c_get (); + float capacity; + uint8_t curve; + + if (temp <= -20.0f) + { + /* For temperatures at or below -20C, use the -20C curve with interpolation. */ + capacity = _battery_interpolate_capacity_at_voltage (_battery_tables[0].curve_readings, voltage); + + /* Validate interpolation result. */ + if (capacity < 0.0f) + { + /* Below minimum voltage, battery is empty. */ + return 0.0f; + } + + return capacity; + } + else if (temp >= 60.0f) + { + /* For temperatures at or above 60C, use the 60C curve with interpolation. */ + capacity = _battery_interpolate_capacity_at_voltage (_battery_tables[4].curve_readings, voltage); + + /* Validate interpolation result. */ + if (capacity < 0.0f) + { + /* Below minimum voltage, battery is empty. */ + return 0.0f; + } + + return capacity; + } + else + { + /* Loop through the temperature curves. */ + for (curve = 1; curve < _BATTERY_N_CURVES; curve++) + { + /* Find the two curves the temp reading is between. */ + if (temp <= (float) _battery_tables[curve].temp_c) + { + float cap_t1 = _battery_interpolate_capacity_at_voltage (_battery_tables[curve-1].curve_readings, voltage); + float cap_t2 = _battery_interpolate_capacity_at_voltage (_battery_tables[curve].curve_readings, voltage); + + /* Check if either interpolation failed (voltage out of range). */ + if (cap_t1 < 0.0f || cap_t2 < 0.0f) + { + /* Below minimum voltage, battery is empty. */ + return 0.0f; + } + + return _battery_interpolate_capacity_across_temps (_battery_tables[curve-1].temp_c, + _battery_tables[curve].temp_c, + temp, + cap_t1, + cap_t2); + } + } + } + + /* Should never reach here, but return 0 as safe fallback. */ + return 0.0f; +} + +void BATTERY_cli_cmd (int32_t argc, char **argv) +{ + ERRNO_e err; + + if (argc == 0) + { + float voltage = 0.0f; + + PRINT (INFO, "Battery\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + (void) BATTERY_voltage_get (&voltage); + PRINT (INFO, "Voltage : %.3f V\r\n", (double) voltage); + PRINT (INFO, "Level : %.2f %%\r\n", (double) (BATTERY_level_calculate (voltage) * 100.0f)); + PRINT (INFO, "Status : %s...\r\n", BATTERY_is_charging () ? "Charging": "Not Charging"); + } + else if (argc == 2) + { + if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + err = BATTERY_init (); + PRINT (INFO, "Ret code : %d\r\n", err); + } + else + { + goto USAGE; + } + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--help") || !strcmp (argv[0], "?")) + { + goto USAGE; + } + else if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + err = BATTERY_init (); + PRINT (INFO, "Ret code : %d\r\n", err); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: battery [OPTION...]\r\n"); + PRINT (INFO, " -i | --init Initializes peripherals necessary to read battery levels\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static float _battery_interpolate_capacity_at_voltage (_battery_point_s *p_point, float voltage) +{ + uint8_t i; + const uint8_t bounds = _BATTERY_CAPACITY_STEPS - 1; + + if (voltage > 4.2f) + { + return 1.0f; + } + + /* Find the two points where voltage lies between. */ + for (i = 0; i < bounds; i++) + { + if (p_point[i].voltage >= voltage && voltage >= p_point[i + 1].voltage) + { + float V1 = p_point[i].voltage; + float V2 = p_point[i + 1].voltage; + float C1 = p_point[i].capacity; + float C2 = p_point[i + 1].capacity; + + /* Linear interpolation: C(V) = C1 + [(C2 - C1) / (V2 - V1)] * (V - V1) */ + return C1 + (C2 - C1) / (V2 - V1) * (voltage - V1); + } + } + + /* Out of range, return -1 */ + return -1.0f; +} + +static float _battery_interpolate_capacity_across_temps (float T1, float T2, float T, float C1, float C2) +{ + /* Linear interpolation: C(T) = C(T1) + [(C(T2) - C(T1)) / (T2 - T1)] * (T - T1) */ + float slope = (C2 - C1) / (T2 - T1); + + return ((slope * (T - T1)) + C1); +} diff --git a/src/bsp/battery.h b/src/bsp/battery.h new file mode 100644 index 0000000..5b71a4e --- /dev/null +++ b/src/bsp/battery.h @@ -0,0 +1,54 @@ +/** + * @file battery.h + * @brief Battery module + * @author Kenny Tran + * @date Dec 02 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef BATTERY_H_ +#define BATTERY_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define BATTERY_VOLTAGE_DIVIDER_R1_OHMS (100000.0) +#define BATTERY_VOLTAGE_DIVIDER_R2_OHMS (270000.0) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e BATTERY_init (void); + +ERRNO_e BATTERY_wake_on_charge (bool enable); + +bool BATTERY_is_charging (void); + +ERRNO_e BATTERY_voltage_get (float *p_voltage); + +float BATTERY_level_calculate (float voltage); + +void BATTERY_cli_cmd (int32_t argc, char **argv); + + +#endif /* BATTERY_H_ */ diff --git a/src/bsp/button.c b/src/bsp/button.c new file mode 100644 index 0000000..b639e50 --- /dev/null +++ b/src/bsp/button.c @@ -0,0 +1,274 @@ +/** + * @file button.c + * @brief Button module + * @author Kenny Tran + * @date Mar 18 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../wrappers/gpio.h" + +#include "button.h" + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _button_init = false; +static volatile uint32_t _button_left_pressed = 0; +static volatile uint32_t _button_center_pressed = 0; +static volatile uint32_t _button_right_pressed = 0; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _button_left_isr (void); +static void _button_center_isr (void); +static void _button_right_isr (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e BUTTON_init (void) +{ + if (_button_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (GPIO_init () < ERRNO_SUCCESS) + { + return ERRNO_GPIO_FAILURE; + } + + /* Configure buttons and ISR. */ + if (GPIO_cfg (GPIO_PIN_BUTTON_LEFT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, _button_left_isr)) + { + return ERRNO_GPIO_FAILURE; + } + + if (GPIO_cfg (GPIO_PIN_BUTTON_CENTER, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, _button_center_isr)) + { + return ERRNO_GPIO_FAILURE; + } + + if (GPIO_cfg (GPIO_PIN_BUTTON_RIGHT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, _button_right_isr)) + { + return ERRNO_GPIO_FAILURE; + } + + /* Set initialized flag. */ + _button_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e BUTTON_wake_on_press (BUTTON_e button) +{ + if (!_button_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Reconfigure GPIO interrupt for buttons to be used to wake from sleep. */ + switch (button) + { + case BUTTON_LEFT: + if (GPIO_cfg (GPIO_PIN_BUTTON_LEFT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_LL_HIGH, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + break; + + case BUTTON_CENTER: + if (GPIO_cfg (GPIO_PIN_BUTTON_CENTER, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_LL_HIGH, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + break; + + case BUTTON_RIGHT: + if (GPIO_cfg (GPIO_PIN_BUTTON_RIGHT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_LL_HIGH, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + break; + + default: + return ERRNO_INVALID_PARAM; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e BUTTON_is_pressed (BUTTON_e button, bool *p_pressed) +{ + if (!_button_init) + { + return ERRNO_NOT_INITIALIZED; + } + + switch (button) + { + case BUTTON_LEFT: + *p_pressed = GPIO_level_get (GPIO_PIN_BUTTON_LEFT); + /* Reset flag. */ + _button_left_pressed = 0; + break; + + case BUTTON_CENTER: + *p_pressed = GPIO_level_get (GPIO_PIN_BUTTON_CENTER); + /* Reset flag. */ + _button_center_pressed = 0; + break; + + case BUTTON_RIGHT: + *p_pressed = GPIO_level_get (GPIO_PIN_BUTTON_RIGHT); + /* Reset flag. */ + _button_right_pressed = 0; + break; + + default: + *p_pressed = false; + return ERRNO_INVALID_PARAM; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e BUTTON_was_pressed (BUTTON_e button, bool *p_pressed) +{ + bool pressed = false; + + if (!_button_init) + { + return ERRNO_NOT_INITIALIZED; + } + + switch (button) + { + case BUTTON_LEFT: + pressed = (bool) _button_left_pressed; + /* Reset flag. */ + _button_left_pressed = 0; + break; + + case BUTTON_CENTER: + pressed = (bool) _button_center_pressed; + /* Reset flag. */ + _button_center_pressed = 0; + break; + + case BUTTON_RIGHT: + pressed = (bool) _button_right_pressed; + /* Reset flag. */ + _button_right_pressed = 0; + break; + + default: + *p_pressed = false; + return ERRNO_INVALID_PARAM; + } + + *p_pressed = pressed; + + return ERRNO_SUCCESS; +} + +void BUTTON_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + bool was_left; + bool was_center; + bool was_right; + bool is_left; + bool is_center; + bool is_right; + + PRINT (INFO, "BUTTON\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "ISR Count\r\n"); + PRINT (INFO, " Left : %d\r\n", _button_left_pressed); + PRINT (INFO, " Center : %d\r\n", _button_center_pressed); + PRINT (INFO, " Right : %d\r\n", _button_right_pressed); + + (void) BUTTON_was_pressed (BUTTON_LEFT, &was_left); + (void) BUTTON_was_pressed (BUTTON_CENTER, &was_center); + (void) BUTTON_was_pressed (BUTTON_RIGHT, &was_right); + (void) BUTTON_is_pressed (BUTTON_LEFT, &is_left); + (void) BUTTON_is_pressed (BUTTON_CENTER, &is_center); + (void) BUTTON_is_pressed (BUTTON_RIGHT, &is_right); + + PRINT (INFO, "was pressed\r\n"); + PRINT (INFO, " Left : %s\r\n", (was_left ? "true" : "false")); + PRINT (INFO, " Center : %s\r\n", (was_center ? "true" : "false")); + PRINT (INFO, " Right : %s\r\n", (was_right ? "true" : "false")); + PRINT (INFO, "is pressed\r\n"); + PRINT (INFO, " Left : %s\r\n", (is_left ? "true" : "false")); + PRINT (INFO, " Center : %s\r\n", (is_center ? "true" : "false")); + PRINT (INFO, " Right : %s\r\n", (is_right ? "true" : "false")); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + PRINT (INFO, "Ret code : %d\r\n", BUTTON_init ()); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: button [OPTION...]\r\n"); + PRINT (INFO, " -i | --init Initializes button module\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _button_left_isr (void) +{ + _button_left_pressed++; +} + +static void _button_center_isr (void) +{ + _button_center_pressed++; +} + +static void _button_right_isr (void) +{ + _button_right_pressed++; +} diff --git a/src/bsp/button.h b/src/bsp/button.h new file mode 100644 index 0000000..e228562 --- /dev/null +++ b/src/bsp/button.h @@ -0,0 +1,56 @@ +/** + * @file button.h + * @brief Button module + * @author Kenny Tran + * @date Mar 18 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef BUTTON_H_ +#define BUTTON_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum BUTTON +{ + BUTTON_LEFT, + BUTTON_CENTER, + BUTTON_RIGHT +} BUTTON_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e BUTTON_init (void); + +ERRNO_e BUTTON_wake_on_press (BUTTON_e button); + +ERRNO_e BUTTON_is_pressed (BUTTON_e button, bool *p_pressed); + +ERRNO_e BUTTON_was_pressed (BUTTON_e button, bool *p_pressed); + +void BUTTON_cli_cmd (int32_t argc, char **argv); + + +#endif /* BUTTON_H_ */ diff --git a/src/bsp/imu.c b/src/bsp/imu.c new file mode 100644 index 0000000..721889f --- /dev/null +++ b/src/bsp/imu.c @@ -0,0 +1,3606 @@ +/** + * @file imu.c + * @brief LSM6DS3TR-C IMU module + * @author Kenny Tran + * @date Feb 28 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include +#include + +#include "imu.h" +#include "imu_reg.h" +#include "mcu.h" + +#include "../app/motion.h" +#include "../app/shot_capture.h" + +#include "../lib/errno.h" +#include "../lib/print.h" +#include "../lib/debug.h" +#include "../lib/fs.h" + +#include "../wrappers/i2c.h" +#include "../wrappers/gpio.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +K_SEM_DEFINE(_imu_int_sem, 0, 1); +K_SEM_DEFINE(shot_capture_sem, 0, 4); + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +K_THREAD_STACK_DEFINE(_imu_task_stack, IMU_TASK_STACK_SIZE); +K_THREAD_STACK_DEFINE(_imu_interrrupt_handler_task_stack, IMU_INTERRUPT_HANDLER_TASK_STACK_SIZE); + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +/* Defines the physical location of a bit field in the IMU registers */ +typedef struct _imu_field_loc { + const uint8_t reg_addr : 8; // 2-digit hexidecimal address of the register in the IMU + const uint8_t field_start : 3; // the MSB of the field can be from 0th to 7th bit so 3 bits is enough + const uint8_t field_len : 4; // bit length is 1 to 8 +} __attribute__((packed, aligned(1))) _imu_field_loc_s; + +/* list of all the fields that can be configured */ +typedef struct _imu_fields { + /* Accelerometer */ + const _imu_field_loc_s fs_xl_f; + const _imu_field_loc_s odr_xl_f; + const _imu_field_loc_s lpf1_bw_sel_f; + const _imu_field_loc_s lpf2_xl_en_f; + const _imu_field_loc_s hp_slope_xl_en_f; + const _imu_field_loc_s hpcf_xl_f; + const _imu_field_loc_s input_composite_f; + + /* Gyroscope */ + const _imu_field_loc_s fs_125_f; // FS_125 CTRL2_G (0x11) + const _imu_field_loc_s fs_g_f; // FS_G CTRL2_G (0x11) + const _imu_field_loc_s odr_g_f; // ODR_G CTRL2_G (0x11) + const _imu_field_loc_s hp_en_g_f; // HP_EN_G CTRL7_G (0x16) + const _imu_field_loc_s hpm_g_f; // HPM_G CTRL7_G (0x16) + const _imu_field_loc_s lpf1_sel_g_f; // HPM_G CTRL4_C (0x13) + const _imu_field_loc_s ftype_f; // FTYPE CTRL6_C (0x15) + + /* FIFO */ + const _imu_field_loc_s fifo_thresh_low_f; + const _imu_field_loc_s fifo_thresh_high_f; + const _imu_field_loc_s dec_fifo_gyro_f; + const _imu_field_loc_s dec_fifo_xl_f; + const _imu_field_loc_s odr_fifo_f; + const _imu_field_loc_s fifo_mode_f; + + /* OTHERS*/ + const _imu_field_loc_s int1_fth_f; + const _imu_field_loc_s sw_reset_f; + const _imu_field_loc_s bdu_f; + const _imu_field_loc_s interrupts_enable_f; + const _imu_field_loc_s slope_fds_f; + const _imu_field_loc_s tap_x_en_f; + const _imu_field_loc_s tap_y_en_f; + const _imu_field_loc_s tap_z_en_f; + const _imu_field_loc_s lir_f; + const _imu_field_loc_s tap_ths_f; + const _imu_field_loc_s quiet_f; + const _imu_field_loc_s shock_f; + const _imu_field_loc_s single_double_tap_f; + const _imu_field_loc_s wk_ths_f; + const _imu_field_loc_s wake_dur_f; + const _imu_field_loc_s int1_single_tap_f; + const _imu_field_loc_s int1_wu_f; + const _imu_field_loc_s int1_double_tap_f; +} _imu_fields_s; + +typedef struct _imu_field_value { + /* Accelerometer */ + /* Accelerometer full-scale range value (g) + * 0: +/- 2 + * 1: +/- 16 + * 2: +/- 4 + * 3: +/- 8 + */ + LSM6DS3TR_C_FS_XL_e fs_xl_v; + /* Accelerometer ODR value (Hz) + * 0: power-down + * 1: 12.5 + * 2: 26 + * 3: 52 + * 4: 104 + * 5: 208 + * 6: 416 + * 7: 833 + * 8: 1666 + * 9: 3332 + * 10: 6664 + * 11: 1.6 (low power only) + */ + LSM6DS3TR_C_ODR_XL_e odr_xl_v; + /* Accelerometer low-pass filter 1 bandwidth selection (Hz) + * 0: odr/2 + * 1: odr/4 + */ + LSM6DS3TR_C_LPF1_BW_SEL_e lpf1_bw_sel_v; + /* Accelerometer low-pass filter LPF2 selection, default: 0 + * Only valid whe xl_hp_slope_xl_en is set to 0 + * 0: disable + * 1: enable + */ + LSM6DS3TR_C_LPF2_XL_EN_e lpf2_xl_en_v; + /* Accelerometer slope filter / high-pass filter selection, default: 0 + * 0: low-pass path + * 1: high-pass path + */ + LSM6DS3TR_C_HP_SLOPE_XL_EN_e hp_slope_xl_en_v; + /* Accelerometer LPF2 and high-pass filter configuration and cutoff setting, default: 0 + * LPF2 bandwith, only when hp_slope_xl_en_v is set to 0 and lpf2_xl_en_v is set to 1: + * 0: ODR/50 + * 1: ODR/100 + * 2: ODR/9 + * 3: ODR/400 + * slope fileter or high-pass bandwith, only when accel_hp_slope_xl_en is set to 1 + * 0: ODR/4, slope filter + * 1: ODR/100, HPF + * 2: ODR/9, HPF + * 3: ODR/400, HPF + */ + LSM6DS3TR_C_HPCF_XL_e hpcf_xl_v; + /* Composite filter input selection. default: 0 + * only valid when hp_slope_xl_en_v is set to 0 and lpf2_xl_en is set to 1 + * 0: ODR/2 low pass filtered sent to composite filter (low latency) + * 1: ODR/4 low pass filtered sent to composite filter (low noise) + */ + LSM6DS3TR_C_INPUT_COMPOSITE_e input_composite_v; + + + /* Gyroscope */ + /* Gyroscope full-scale range value (dps) + * 0: +/- 125 + * 1: +/- 250 + * 2: +/- 500 + * 3: +/- 1000 + * 4: +/- 2000 + */ + LSM6DS3TR_C_FS_G_ABSTR_e fs_g_v; + /* Gyroscope ODR value (Hz) + * 0: power-down + * 1: 12.5 + * 2: 26 + * 3: 52 + * 4: 104 + * 5: 208 + * 6: 416 + * 7: 833 + * 8: 1666 + * 9: 3332 + * 10: 6664 + */ + LSM6DS3TR_C_ODR_G_e odr_g_v; + /* Gyroscope digital high-pass filter enable, default: 0 + * 0: disable + * 1: enable + */ + LSM6DS3TR_C_HP_EN_G_e hp_en_g_v; + /* Gyroscope digital HP filter cutoff selection selection (Hz), default: 0 + * valid only when hp_en_g_v is set to 1 + * 0: 16m + * 1: 65m + * 2: 260m + * 3: 1.04Hz + */ + LSM6DS3TR_C_HPM_G_e hpm_g_v; + /* gyroscope digital LPF1 enable, default: 0 + * 0: disable + * 1: enable + */ + LSM6DS3TR_C_LPF1_SEL_G_e lpf1_sel_g_v; + /* Gyroscope digital LPF (LPF1) bandwidth selection (Hz) + * Depends on odr_g and only valid when lpf1_sel_g_v is set to 1 + *** 800 1.6k 3.3k 6.6k + * 0: 245 315 343 351 + * 1: 195 224 234 237 + * 2: 155 168 172 173 + * 3: 293 505 925 937 + */ + LSM6DS3TR_C_FTYPE_e ftype_v; + + /* FIFO */ + /* FIFO threshold value. 0 to 2047 16-bit samples */ + uint16_t fifo_threshold_v; + /* Gyro FIFO (first data set) decimation setting, default: 0 + * 0: Gyro sensor not in FIFO + * 1: No decimation + * 2: Decimation with factor 2 + * 3: Decimation with factor 3 + * 4: Decimation with factor 4 + * 5: Decimation with factor 8 + * 6: Decimation with factor 16 + * 7: Decimation with factor 32 + */ + LSM6DS3TR_C_DEC_FIFO_GYRO_e dec_fifo_gyro_v; + /* Accelerometer FIFO (second data set) decimation setting, default: 0 + * 0: Accelerometer sensor not in FIFO + * 1: No decimation + * 2: Decimation with factor 2 + * 3: Decimation with factor 3 + * 4: Decimation with factor 4 + * 5: Decimation with factor 8 + * 6: Decimation with factor 16 + * 7: Decimation with factor 32 + */ + LSM6DS3TR_C_DEC_FIFO_XL_e dec_fifo_xl_v; + /* FIFO ODR selection + * If the device is working at an ODR slower than the one selected, + * FIFO ODR is limited to that ODR value. + * 0: FIFO disabled + * 1: 12.5 Hz + * 2: 26 Hz + * 3: 52 Hz + * 4: 104 Hz + * 5: 208 Hz + * 6: 416 Hz + * 7: 833 Hz + * 8: 1.66 kHz + * 9: 3.33 kHz + * 10: 6.66 kHz + */ + LSM6DS3TR_C_ODR_FIFO_e odr_fifo_v; + /* FIFO mode selection + * 0: Bypass mode. FIFO disabled + * 1: FIFO mode. Stops collecting data when FIFO is full + * 3: Continuous mode until trigger is deasserted, then FIFO mode + * 4: Bypass mode until trigger is deasserted, then Continuous mode + * 6: Continuous mode. If the FIFO is full, the new sample overwrites the older one + */ + LSM6DS3TR_C_FIFO_MODE_e fifo_mode_v; + + /* others */ + /* FIFO threshold interrupt on INT1 pin + * 0: diable + * 1: enable + */ + LSM6DS3TR_C_INT1_FTH_e int1_fth_v; + /* Software reset + * 0: normal mode + * 1: reset all registers to default values + */ + LSM6DS3TR_C_SW_RESET_e sw_reset_v; + /* Block data update (BDU) enable. + * 0: continuous update + * 1: output registers not updated until MSB and LSB have been read + */ + + LSM6DS3TR_C_BDU_e bdu_v; + /* Enable basic interrupts (6D, tap, double tap, free fall, wake up) + * 0: disable + * 1: enable + */ + LSM6DS3TR_C_INTERRUPTS_ENABLE_e interrupts_enable_v; + /* HPF or SLOPE filter selection on wake-up and Activity/Inactivity functions. Refer to + * Default value: 0 + * 0: SLOPE filter applied; 1: HPF applied + */ + LSM6DS3TR_C_SLOPE_FDS_e slope_fds_v; + /* Tap detection on X axis enable + * 0: disable + * 1: enable + */ + LSM6DS3TR_C_TAP_X_EN_e tap_x_en_v; + /* Tap detection on Y axis enable + * 0: disable + * 1: enable + */ + LSM6DS3TR_C_TAP_Y_EN_e tap_y_en_v; + /* Tap detection on Z axis enable + * 0: disable + * 1: enable + */ + LSM6DS3TR_C_TAP_Z_EN_e tap_z_en_v; + /* Latch interrupt request + * 0: interrupt request not latched + * 1: interrupt request latched + */ + LSM6DS3TR_C_LIR_e lir_v; + /* Threshold for tap recognition. + * 1 LSB corresponds to FS_XL/2^5 + * max value is 2**5 - 1 = 31 + */ + uint8_t tap_ths_v; + /* Expected quiet time after a tap detection + * Quiet time is the time after the first detected tap in which there must not be any + * overthreshold event. The default value of these bits is 00b which corresponds to + * 2*ODR_XL time. If the QUIET[1:0] bits are set to a different value, 1LSB + * corresponds to 4*ODR_XL time + */ + LSM6DS3TR_C_QUIET_e quiet_v; + /* Maximum duration of overthreshold event. + * Maximum duration is the maximum time of an overthreshold signal detection to be + * recognized as a tap event. The default value of these bits is 00b which corresponds + * to 4*ODR_XL time. If the SHOCK[1:0] bits are set to a different value, 1LSB + * corresponds to 8*ODR_XL time. + */ + LSM6DS3TR_C_SHOCK_e shock_v; + /* Single and double-tap detection enable + * 0: only single-tap detection enabled + * 1: both single and double-tap detection enabled + */ + LSM6DS3TR_C_SINGLE_DOUBLE_TAP_e single_double_tap_v; + /* Threshold for wakeup. + * 1 LSB corresponds to FS_XL/2^6 max value is 2**6 - 1 = 63 + */ + uint8_t wk_ths_v; + /* Wake up duration event. + * The filtered samples must be greater than wk_ths_v + * for this duration to be considered as a wake up + * event. 1LSB = 1 ODR_time + */ + LSM6DS3TR_C_WAKE_DUR_e wake_dur_v; + /* Single-tap recongnition routing on INT1 pin + * 0: routing of single-tap event to INT1 pin disabled + * 1: routing of single-tap event to INT1 pin enabled + */ + LSM6DS3TR_C_INT1_SINGLE_TAP_e int1_single_tap_v; + /* Routing of wakeup event on INT1. Default value: 0 + * 0: routing of wakeup event on INT1 disabled; + * 1: routing of wakeup event on INT1 enabled + */ + LSM6DS3TR_C_INT1_WU_e int1_wu_v; + /* Double-tap recongnition routing on INT1 pin + * 0: routing of double-tap event to INT1 pin disabled + * 1: routing of double-tap event to INT1 pin enabled + */ + LSM6DS3TR_C_INT1_DOUBLE_TAP_e int1_double_tap_v; +} _imu_field_value_s; + +typedef struct _imu_config { + _imu_field_value_s field_v; + uint8_t temp_enabled; + uint16_t temp_sensitivity; + /* compensation matrix (A^1/2) */ + float A_comp[3][3]; + float bias[3]; + /* Interrupt Eanble Flags + * the bits in the flag indicates which interrupt is enabled + * and routed to the INT1 pin + */ + atomic_t interrupt_mode_flags; +} _imu_config_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _imu_init = false; +static atomic_t _imu_motion_int_enable = ATOMIC_INIT(0); + +static const _imu_fields_s _imu_fields = { + + /* Accelerometer */ + .odr_xl_f = {.reg_addr = LSM6DS3TR_C_CTRL1_XL, .field_start = 4, .field_len = 4}, + .fs_xl_f = {.reg_addr = LSM6DS3TR_C_CTRL1_XL, .field_start = 2, .field_len = 2}, + .lpf1_bw_sel_f = {.reg_addr = LSM6DS3TR_C_CTRL1_XL, .field_start = 1, .field_len = 1}, + .lpf2_xl_en_f = {.reg_addr = LSM6DS3TR_C_CTRL8_XL, .field_start = 7, .field_len = 1}, + .hp_slope_xl_en_f = {.reg_addr = LSM6DS3TR_C_CTRL8_XL, .field_start = 2, .field_len = 1}, + .hpcf_xl_f = {.reg_addr = LSM6DS3TR_C_CTRL8_XL, .field_start = 5, .field_len = 2}, + .input_composite_f = {.reg_addr = LSM6DS3TR_C_CTRL8_XL, .field_start = 3, .field_len = 1}, + + /* Gyroscope */ + .fs_125_f = {.reg_addr = LSM6DS3TR_C_CTRL2_G, .field_start = 1, .field_len = 1}, + .fs_g_f = {.reg_addr = LSM6DS3TR_C_CTRL2_G, .field_start = 2, .field_len = 2}, + .odr_g_f = {.reg_addr = LSM6DS3TR_C_CTRL2_G, .field_start = 4, .field_len = 4}, + .hp_en_g_f = {.reg_addr = LSM6DS3TR_C_CTRL7_G, .field_start = 6, .field_len = 1}, + .hpm_g_f = {.reg_addr = LSM6DS3TR_C_CTRL7_G, .field_start = 4, .field_len = 2}, + .lpf1_sel_g_f = {.reg_addr = LSM6DS3TR_C_CTRL4_C, .field_start = 1, .field_len = 1}, + .ftype_f = {.reg_addr = LSM6DS3TR_C_CTRL6_C, .field_start = 0, .field_len = 2}, + + /* FIFO */ + .fifo_thresh_low_f = {.reg_addr = LSM6DS3TR_C_FIFO_CTRL1, .field_start = 0, .field_len = 8}, + .fifo_thresh_high_f = {.reg_addr = LSM6DS3TR_C_FIFO_CTRL2, .field_start = 0, .field_len = 3}, + .dec_fifo_gyro_f = {.reg_addr = LSM6DS3TR_C_FIFO_CTRL3, .field_start = 3, .field_len = 3}, + .dec_fifo_xl_f = {.reg_addr = LSM6DS3TR_C_FIFO_CTRL3, .field_start = 0, .field_len = 3}, + .odr_fifo_f = {.reg_addr = LSM6DS3TR_C_FIFO_CTRL5, .field_start = 3, .field_len = 4}, + .fifo_mode_f = {.reg_addr = LSM6DS3TR_C_FIFO_CTRL5, .field_start = 0, .field_len = 3}, + + /* Others */ + .int1_fth_f = {.reg_addr = LSM6DS3TR_C_INT1_CTRL, .field_start = 3, .field_len = 1}, + .sw_reset_f = {.reg_addr = LSM6DS3TR_C_CTRL3_C, .field_start = 0, .field_len = 1}, + .bdu_f = {.reg_addr = LSM6DS3TR_C_CTRL3_C, .field_start = 6, .field_len = 1}, + .interrupts_enable_f = {.reg_addr = LSM6DS3TR_C_TAP_CFG, .field_start = 7, .field_len = 1}, + .slope_fds_f = {.reg_addr = LSM6DS3TR_C_TAP_CFG, .field_start = 4, .field_len = 1}, + .tap_x_en_f = {.reg_addr = LSM6DS3TR_C_TAP_CFG, .field_start = 3, .field_len = 1}, + .tap_y_en_f = {.reg_addr = LSM6DS3TR_C_TAP_CFG, .field_start = 2, .field_len = 1}, + .tap_z_en_f = {.reg_addr = LSM6DS3TR_C_TAP_CFG, .field_start = 1, .field_len = 1}, + .lir_f = {.reg_addr = LSM6DS3TR_C_TAP_CFG, .field_start = 0, .field_len = 1}, + .tap_ths_f = {.reg_addr = LSM6DS3TR_C_TAP_THS_6D, .field_start = 0, .field_len = 5}, + .quiet_f = {.reg_addr = LSM6DS3TR_C_INT_DUR2, .field_start = 2, .field_len = 2}, + .shock_f = {.reg_addr = LSM6DS3TR_C_INT_DUR2, .field_start = 0, .field_len = 2}, + .single_double_tap_f = {.reg_addr = LSM6DS3TR_C_WAKE_UP_THS, .field_start = 7, .field_len = 1}, + .wk_ths_f = {.reg_addr = LSM6DS3TR_C_WAKE_UP_THS, .field_start = 0, .field_len = 6}, + .wake_dur_f = {.reg_addr = LSM6DS3TR_C_WAKE_UP_DUR, .field_start = 5, .field_len = 2}, + .int1_single_tap_f = {.reg_addr = LSM6DS3TR_C_MD1_CFG, .field_start = 6, .field_len = 1}, + .int1_wu_f = {.reg_addr = LSM6DS3TR_C_MD1_CFG, .field_start = 5, .field_len = 1}, + .int1_double_tap_f = {.reg_addr = LSM6DS3TR_C_MD1_CFG, .field_start = 3, .field_len = 1}, +}; + +/* default field values */ +static const _imu_field_value_s _imu_field_v_default = { + /* Accelerometer */ + .fs_xl_v = LSM6DS3TR_C_FS_XL_2g, + .odr_xl_v = LSM6DS3TR_C_ODR_XL_416Hz, + .lpf1_bw_sel_v = LSM6DS3TR_C_LPF1_BW_SEL_ODR_DIV_4, + .lpf2_xl_en_v = LSM6DS3TR_C_LPF2_XL_EN_DISABLED, + .hp_slope_xl_en_v = LSM6DS3TR_C_HP_SLOPE_XL_EN_DISABLED, + .hpcf_xl_v = LSM6DS3TR_C_HPCF_XL_00, + .input_composite_v = LSM6DS3TR_C_INPUT_COMPOSITE_LOW_LATENCY, + + /* Gyroscope */ + .fs_g_v = LSM6DS3TR_C_FS_G_ABSTR_250dps, + .odr_g_v = LSM6DS3TR_C_ODR_G_416Hz, + .hp_en_g_v = LSM6DS3TR_C_HP_EN_G_DISABLE, + .hpm_g_v = LSM6DS3TR_C_HPM_G_16mHz, + .lpf1_sel_g_v = LSM6DS3TR_C_LPF1_SEL_G_DISABLE, + .ftype_v = LSM6DS3TR_C_FTYPE_0, + + /* FIFO */ + .fifo_threshold_v = 0, + .dec_fifo_gyro_v = LSM6DS3TR_C_DEC_FIFO_GYRO_DATA_NOT_IN_FIFO, + .dec_fifo_xl_v = LSM6DS3TR_C_DEC_FIFO_XL_DATA_NOT_IN_FIFO, + .odr_fifo_v = LSM6DS3TR_C_ODR_FIFO_DISABLE, + .fifo_mode_v = LSM6DS3TR_C_FIFO_MODE_BYPASS, + + /* Others */ + .int1_fth_v = LSM6DS3TR_C_INT1_FTH_DISABLED, + .sw_reset_v = LSM6DS3TR_C_SW_RESET_NORMAL_MODE, + .bdu_v = LSM6DS3TR_C_BDU_CONTINUOUS, + .interrupts_enable_v = LSM6DS3TR_C_INTERRUPTS_DISABLE, + .tap_x_en_v = LSM6DS3TR_C_TAP_X_EN_DISABLED, + .tap_y_en_v = LSM6DS3TR_C_TAP_Y_EN_DISABLED, + .tap_z_en_v = LSM6DS3TR_C_TAP_Z_EN_DISABLED, + .lir_v = LSM6DS3TR_C_LIR_DISABLED, + .tap_ths_v = 0, + .quiet_v = LSM6DS3TR_C_QUIET_2_OVER_ODR_XL, + .shock_v = LSM6DS3TR_C_SHOCK_4_OVER_ODR_XL, + .single_double_tap_v = LSM6DS3TR_C_SINGLE_DOUBLE_TAP_SINGLE_ONLY, + .wk_ths_v = 2, + .wake_dur_v = LSM6DS3TR_C_WAKE_DUR_1_ODR_XL, + .int1_single_tap_v = LSM6DS3TR_C_INT1_SINGLE_TAP_DISABLED, + .int1_double_tap_v = LSM6DS3TR_C_INT1_DOUBLE_TAP_DISABLED +}; + +static _imu_config_s _imu_cfg = { + .temp_enabled = 1, + .A_comp = { {1.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}, + }, + .bias = {0.0f}, + .interrupt_mode_flags = ATOMIC_INIT(0) +}; + +static IMU_cb_t _imu_wakeup_cb = NULL; +static bool _imu_data_dump = false; +static bool _imu_data_dump_hex = false; +static uint32_t _imu_data_dump_time_ms = 0; + +/* variables for various statistics */ +static atomic_t _interrupt_count = ATOMIC_INIT(0); +static bool _imu_statistic_dump = false; +static atomic_t _imu_fifo_is_set = ATOMIC_INIT(0); + +static volatile IMU_int_source_e _imu_int_source = IMU_INT_SOURCE_NONE; + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC VARIABLE DEFINITIOPNS (allocates memory) + *------------------------------------------------------------------------------------------------*/ +volatile bool busy_wait_active = false; +volatile uint32_t imu_task_counter = 0; +volatile uint32_t imu_task_during_busy_wait_counter = 0; +volatile uint32_t _interface_task_counter = 0; +volatile uint32_t _interface_task_during_i2c_counter = 0; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _imu_task(void *p1, void *p2, void *p3); +static void _imu_interrupt_handler_task(void *p1, void *p2, void *p3); +static ERRNO_e _imu_who_am_i(void); +static ERRNO_e _imu_cfg_init(void); +static float _imu_xl_raw_to_float(int16_t raw); +static float _imu_gyro_raw_to_float(int16_t raw); +static void _imu_config_write (void); +static void _imu_isr(void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e IMU_init (void) +{ + ERRNO_e err; + + if (_imu_init) { + return ERRNO_ALREADY_INITIALIZED; + } + + if (I2C_init (I2C_CH_1) < ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + /* Initialize GPIO for use with IMU interrupt pin. */ + if (GPIO_init () < ERRNO_SUCCESS) { + return ERRNO_GPIO_FAILURE; + } + + /* Perform an ID check. */ + while (_imu_who_am_i() != ERRNO_SUCCESS) { + static uint32_t cnt = 0; + k_busy_wait (10000); + + if (cnt++ == 50) + { + PRINT (ERROR, "IMU WHO AM I failure\r\n"); + return ERRNO_I2C_FAILURE; + } + } + + /* Set init flag before IMU_reset so it doesn't fail. */ + _imu_init = true; + + err = IMU_reset(); + if (err != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + /* Register callback for IMU interrupts. */ + if (GPIO_cfg (GPIO_PIN_IMU_INT, GPIO_DIRECTION_IN, GPIO_PULLDOWN, GPIO_INIT_LOW, GPIO_INT_RISING_EDGE, _imu_isr)) { + PRINT(CRITICAL, "Failure to register IMU interrupt callback.\r\n"); + return ERRNO_GPIO_FAILURE; + } + + /* Configure LSM6DS3 with default settings. */ + err = _imu_cfg_init(); + if (err != ERRNO_SUCCESS) { + return err; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_int_cb_register(IMU_int_source_e src, IMU_cb_t cb) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (src) + { + case IMU_INT_SOURCE_WAKEUP: + _imu_wakeup_cb = cb; + break; + + default: + return ERRNO_INVALID_PARAM; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_gyro_enable(bool en) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + if (en) { + if (_imu_cfg.field_v.odr_g_v == LSM6DS3TR_C_ODR_G_POWER_DOWN) { + _imu_cfg.field_v.odr_g_v = LSM6DS3TR_C_ODR_G_416Hz; + } + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_g_f.reg_addr, + (uint8_t)_imu_cfg.field_v.odr_g_v, + _imu_fields.odr_g_f.field_start, + _imu_fields.odr_g_f.field_len); + + } else { + _imu_cfg.field_v.odr_g_v = LSM6DS3TR_C_ODR_G_POWER_DOWN; + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_g_f.reg_addr, + (uint8_t)_imu_cfg.field_v.odr_g_v, + _imu_fields.odr_g_f.field_start, + _imu_fields.odr_g_f.field_len); + + } + + return err; +} + +ERRNO_e IMU_is_gyro_enabled(bool *en) +{ + ERRNO_e err; + uint8_t gyro_enabled; + + err = I2C_field_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_g_f.reg_addr, + &gyro_enabled, + _imu_fields.odr_g_f.field_start, + _imu_fields.odr_g_f.field_len); + + *en = (bool) gyro_enabled; + + return err; +} + +ERRNO_e IMU_set_fs_g(LSM6DS3TR_C_FS_G_ABSTR_e value) +{ + ERRNO_e err; + uint8_t fs_125 = 0, fs_main = 0; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_FS_G_ABSTR_125dps: fs_125 = 1; fs_main = 0; break; + case LSM6DS3TR_C_FS_G_ABSTR_250dps: fs_main = 0; break; + case LSM6DS3TR_C_FS_G_ABSTR_500dps: fs_main = 1; break; + case LSM6DS3TR_C_FS_G_ABSTR_1000dps: fs_main = 2; break; + case LSM6DS3TR_C_FS_G_ABSTR_2000dps: fs_main = 3; break; + default: return ERRNO_INVALID_PARAM; + } + + /* update runtime value */ + _imu_cfg.field_v.fs_g_v = value; + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fs_125_f.reg_addr, + fs_125, + _imu_fields.fs_125_f.field_start, + _imu_fields.fs_125_f.field_len); + if (err == ERRNO_SUCCESS) { + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fs_g_f.reg_addr, + fs_main, + _imu_fields.fs_g_f.field_start, + _imu_fields.fs_g_f.field_len); + } + + return err; +} + +ERRNO_e IMU_get_fs_g(LSM6DS3TR_C_FS_G_ABSTR_e *value) +{ + ERRNO_e err; + uint8_t fs_125, fs_main; + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fs_125_f.reg_addr, + &fs_125, + _imu_fields.fs_125_f.field_start, + _imu_fields.fs_125_f.field_len); + + if (err == ERRNO_SUCCESS) { + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fs_g_f.reg_addr, + &fs_main, + _imu_fields.fs_g_f.field_start, + _imu_fields.fs_g_f.field_len); + if (err == ERRNO_SUCCESS) { + if (fs_125) { + *value = LSM6DS3TR_C_FS_G_ABSTR_125dps; + } else { + switch(fs_main) { + case 0: *value = LSM6DS3TR_C_FS_G_ABSTR_250dps; break; + case 1: *value = LSM6DS3TR_C_FS_G_ABSTR_500dps; break; + case 2: *value = LSM6DS3TR_C_FS_G_ABSTR_1000dps; break; + case 3: *value = LSM6DS3TR_C_FS_G_ABSTR_2000dps; break; + } + } + /* update runtime value */ + _imu_cfg.field_v.fs_g_v = *value; + } + } + + return err; +} + +ERRNO_e IMU_set_odr_g(LSM6DS3TR_C_ODR_G_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_ODR_G_POWER_DOWN: break; + case LSM6DS3TR_C_ODR_G_12_5Hz: break; + case LSM6DS3TR_C_ODR_G_26Hz: break; + case LSM6DS3TR_C_ODR_G_52Hz: break; + case LSM6DS3TR_C_ODR_G_104Hz: break; + case LSM6DS3TR_C_ODR_G_208Hz: break; + case LSM6DS3TR_C_ODR_G_416Hz: break; + case LSM6DS3TR_C_ODR_G_833Hz: break; + case LSM6DS3TR_C_ODR_G_1666Hz: break; + case LSM6DS3TR_C_ODR_G_3332Hz: break; + case LSM6DS3TR_C_ODR_G_6664Hz: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.odr_g_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_g_f.reg_addr, + (uint8_t)value, + _imu_fields.odr_g_f.field_start, + _imu_fields.odr_g_f.field_len); + return err; +} + +ERRNO_e IMU_get_odr_g(LSM6DS3TR_C_ODR_G_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_g_f.reg_addr, + &raw, + _imu_fields.odr_g_f.field_start, + _imu_fields.odr_g_f.field_len); + + *value = (LSM6DS3TR_C_ODR_G_e)raw; + _imu_cfg.field_v.odr_g_v = *value; + return err; +} + +ERRNO_e IMU_set_hp_en_g(LSM6DS3TR_C_HP_EN_G_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_HP_EN_G_DISABLE: break; + case LSM6DS3TR_C_HP_EN_G_ENABLE: break; + default: return ERRNO_INVALID_PARAM; + } + + if (_imu_cfg.field_v.hp_en_g_v != value) { + _imu_cfg.field_v.hp_en_g_v = value; + } + + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hp_en_g_f.reg_addr, + (uint8_t)value, + _imu_fields.hp_en_g_f.field_start, + _imu_fields.hp_en_g_f.field_len); + return err; +} + +ERRNO_e IMU_get_hp_en_g(LSM6DS3TR_C_HP_EN_G_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hp_en_g_f.reg_addr, + &raw, + _imu_fields.hp_en_g_f.field_start, + _imu_fields.hp_en_g_f.field_len); + *value = (LSM6DS3TR_C_HP_EN_G_e)raw; + + if (_imu_cfg.field_v.hp_en_g_v != *value) { + _imu_cfg.field_v.hp_en_g_v = *value; + } + + return err; +} + +ERRNO_e IMU_set_hpm_g(LSM6DS3TR_C_HPM_G_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_HPM_G_16mHz: break; + case LSM6DS3TR_C_HPM_G_65mHz: break; + case LSM6DS3TR_C_HPM_G_260mHz: break; + case LSM6DS3TR_C_HPM_G_1_04Hz: break; + default: return ERRNO_INVALID_PARAM; + } + + if (_imu_cfg.field_v.hpm_g_v != value) { + _imu_cfg.field_v.hpm_g_v = value; + } + + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hpm_g_f.reg_addr, + (uint8_t)value, + _imu_fields.hpm_g_f.field_start, + _imu_fields.hpm_g_f.field_len); + return err; +} + +ERRNO_e IMU_get_hpm_g(LSM6DS3TR_C_HPM_G_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hpm_g_f.reg_addr, + &raw, + _imu_fields.hpm_g_f.field_start, + _imu_fields.hpm_g_f.field_len); + *value = (LSM6DS3TR_C_HPM_G_e)raw; + + if (_imu_cfg.field_v.hpm_g_v != *value) { + _imu_cfg.field_v.hpm_g_v = *value; + } + + return err; +} + +ERRNO_e IMU_set_lpf1_sel_g(LSM6DS3TR_C_LPF1_SEL_G_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_LPF1_SEL_G_DISABLE: break; + case LSM6DS3TR_C_LPF1_SEL_G_ENABLE: break; + default: return ERRNO_INVALID_PARAM; + } + + if (_imu_cfg.field_v.lpf1_sel_g_v != value) { + _imu_cfg.field_v.lpf1_sel_g_v = value; + } + + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lpf1_sel_g_f.reg_addr, + (uint8_t)value, + _imu_fields.lpf1_sel_g_f.field_start, + _imu_fields.lpf1_sel_g_f.field_len); + return err; +} + +ERRNO_e IMU_get_lpf1_sel_g(LSM6DS3TR_C_LPF1_SEL_G_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lpf1_sel_g_f.reg_addr, + &raw, + _imu_fields.lpf1_sel_g_f.field_start, + _imu_fields.lpf1_sel_g_f.field_len); + *value = (LSM6DS3TR_C_LPF1_SEL_G_e)raw; + + if (_imu_cfg.field_v.lpf1_sel_g_v != *value) { + _imu_cfg.field_v.lpf1_sel_g_v = *value; + } + + return err; +} + +ERRNO_e IMU_set_ftype(LSM6DS3TR_C_FTYPE_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_FTYPE_0: break; + case LSM6DS3TR_C_FTYPE_1: break; + case LSM6DS3TR_C_FTYPE_2: break; + case LSM6DS3TR_C_FTYPE_3: break; + default: return ERRNO_INVALID_PARAM; + } + + if (_imu_cfg.field_v.ftype_v != value) { + _imu_cfg.field_v.ftype_v = value; + } + + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.ftype_f.reg_addr, + (uint8_t)value, + _imu_fields.ftype_f.field_start, + _imu_fields.ftype_f.field_len); + return err; +} + +ERRNO_e IMU_get_ftype(LSM6DS3TR_C_FTYPE_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.ftype_f.reg_addr, + &raw, + _imu_fields.ftype_f.field_start, + _imu_fields.ftype_f.field_len); + *value = (LSM6DS3TR_C_FTYPE_e)raw; + + if (_imu_cfg.field_v.ftype_v != *value) { + _imu_cfg.field_v.ftype_v = *value; + } + + return err; +} + +ERRNO_e IMU_xl_enable(bool en) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + if (en) { + if (_imu_cfg.field_v.odr_xl_v == LSM6DS3TR_C_ODR_XL_POWER_DOWN) { + _imu_cfg.field_v.odr_xl_v = LSM6DS3TR_C_ODR_XL_416Hz; + } + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_xl_f.reg_addr, + (uint8_t)_imu_cfg.field_v.odr_xl_v, + _imu_fields.odr_xl_f.field_start, + _imu_fields.odr_xl_f.field_len); + } else { + _imu_cfg.field_v.odr_xl_v = LSM6DS3TR_C_ODR_XL_POWER_DOWN; + err = I2C_field_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_xl_f.reg_addr, + (uint8_t)_imu_cfg.field_v.odr_xl_v, + _imu_fields.odr_xl_f.field_start, + _imu_fields.odr_xl_f.field_len); + } + + return err; +} + +ERRNO_e IMU_is_xl_enabled(bool *en) +{ + uint8_t xl_enabled; + ERRNO_e err; + + err = I2C_field_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_xl_f.reg_addr, + &xl_enabled, + _imu_fields.odr_xl_f.field_start, + _imu_fields.odr_xl_f.field_len); + + return (bool) xl_enabled; +} + +ERRNO_e IMU_set_fs_xl(LSM6DS3TR_C_FS_XL_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_FS_XL_2g: break; + case LSM6DS3TR_C_FS_XL_16g: break; + case LSM6DS3TR_C_FS_XL_4g: break; + case LSM6DS3TR_C_FS_XL_8g: break; + default: return ERRNO_INVALID_PARAM; + } + + /* update runtime value */ + _imu_cfg.field_v.fs_xl_v = value; + + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fs_xl_f.reg_addr, + (uint8_t)value, + _imu_fields.fs_xl_f.field_start, + _imu_fields.fs_xl_f.field_len); + + return err; +} + +ERRNO_e IMU_get_fs_xl(LSM6DS3TR_C_FS_XL_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fs_xl_f.reg_addr, + &raw, + _imu_fields.fs_xl_f.field_start, + _imu_fields.fs_xl_f.field_len); + *value = (LSM6DS3TR_C_FS_XL_e)raw; + _imu_cfg.field_v.fs_xl_v = *value; + + return err; +} + +ERRNO_e IMU_set_odr_xl(LSM6DS3TR_C_ODR_XL_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_ODR_XL_POWER_DOWN: break; + case LSM6DS3TR_C_ODR_XL_12_5Hz: break; + case LSM6DS3TR_C_ODR_XL_26Hz: break; + case LSM6DS3TR_C_ODR_XL_52Hz: break; + case LSM6DS3TR_C_ODR_XL_104Hz: break; + case LSM6DS3TR_C_ODR_XL_208Hz: break; + case LSM6DS3TR_C_ODR_XL_416Hz: break; + case LSM6DS3TR_C_ODR_XL_833Hz: break; + case LSM6DS3TR_C_ODR_XL_1666Hz: break; + case LSM6DS3TR_C_ODR_XL_3332Hz: break; + case LSM6DS3TR_C_ODR_XL_6664Hz: break; + case LSM6DS3TR_C_ODR_XL_1_6Hz: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.odr_xl_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_xl_f.reg_addr, + (uint8_t)value, + _imu_fields.odr_xl_f.field_start, + _imu_fields.odr_xl_f.field_len); + return err; +} + +ERRNO_e IMU_get_odr_xl(LSM6DS3TR_C_ODR_XL_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_xl_f.reg_addr, + &raw, + _imu_fields.odr_xl_f.field_start, + _imu_fields.odr_xl_f.field_len); + + *value = (LSM6DS3TR_C_ODR_XL_e)raw; + _imu_cfg.field_v.odr_xl_v = *value; + return err; +} + +ERRNO_e IMU_set_lpf1_bw_sel(LSM6DS3TR_C_LPF1_BW_SEL_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_LPF1_BW_SEL_ODR_DIV_2: break; + case LSM6DS3TR_C_LPF1_BW_SEL_ODR_DIV_4: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.lpf1_bw_sel_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lpf1_bw_sel_f.reg_addr, + (uint8_t)value, + _imu_fields.lpf1_bw_sel_f.field_start, + _imu_fields.lpf1_bw_sel_f.field_len); + return err; +} + +ERRNO_e IMU_get_lpf1_bw_sel(LSM6DS3TR_C_LPF1_BW_SEL_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lpf1_bw_sel_f.reg_addr, + &raw, + _imu_fields.lpf1_bw_sel_f.field_start, + _imu_fields.lpf1_bw_sel_f.field_len); + + *value = (LSM6DS3TR_C_LPF1_BW_SEL_e)raw; + _imu_cfg.field_v.lpf1_bw_sel_v = *value; + return err; +} + +ERRNO_e IMU_set_lpf2_xl_en(LSM6DS3TR_C_LPF2_XL_EN_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_LPF2_XL_EN_DISABLED: break; + case LSM6DS3TR_C_LPF2_XL_EN_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.lpf2_xl_en_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lpf2_xl_en_f.reg_addr, + (uint8_t)value, + _imu_fields.lpf2_xl_en_f.field_start, + _imu_fields.lpf2_xl_en_f.field_len); + return err; +} + +ERRNO_e IMU_get_lpf2_xl_en(LSM6DS3TR_C_LPF2_XL_EN_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lpf2_xl_en_f.reg_addr, + &raw, + _imu_fields.lpf2_xl_en_f.field_start, + _imu_fields.lpf2_xl_en_f.field_len); + + *value = (LSM6DS3TR_C_LPF2_XL_EN_e)raw; + _imu_cfg.field_v.lpf2_xl_en_v = *value; + return err; +} + +ERRNO_e IMU_set_hp_slope_xl_en(LSM6DS3TR_C_HP_SLOPE_XL_EN_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_HP_SLOPE_XL_EN_DISABLED: break; + case LSM6DS3TR_C_HP_SLOPE_XL_EN_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.hp_slope_xl_en_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hp_slope_xl_en_f.reg_addr, + (uint8_t)value, + _imu_fields.hp_slope_xl_en_f.field_start, + _imu_fields.hp_slope_xl_en_f.field_len); + return err; +} + +ERRNO_e IMU_get_hp_slope_xl_en(LSM6DS3TR_C_HP_SLOPE_XL_EN_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hp_slope_xl_en_f.reg_addr, + &raw, + _imu_fields.hp_slope_xl_en_f.field_start, + _imu_fields.hp_slope_xl_en_f.field_len); + + *value = (LSM6DS3TR_C_HP_SLOPE_XL_EN_e)raw; + _imu_cfg.field_v.hp_slope_xl_en_v = *value; + return err; +} + +ERRNO_e IMU_set_hpcf_xl(LSM6DS3TR_C_HPCF_XL_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_HPCF_XL_00: break; + case LSM6DS3TR_C_HPCF_XL_01: break; + case LSM6DS3TR_C_HPCF_XL_02: break; + case LSM6DS3TR_C_HPCF_XL_03: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.hpcf_xl_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hpcf_xl_f.reg_addr, + (uint8_t)value, + _imu_fields.hpcf_xl_f.field_start, + _imu_fields.hpcf_xl_f.field_len); + return err; +} + +ERRNO_e IMU_get_hpcf_xl(LSM6DS3TR_C_HPCF_XL_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.hpcf_xl_f.reg_addr, + &raw, + _imu_fields.hpcf_xl_f.field_start, + _imu_fields.hpcf_xl_f.field_len); + + *value = (LSM6DS3TR_C_HPCF_XL_e)raw; + _imu_cfg.field_v.hpcf_xl_v = *value; + return err; +} + +ERRNO_e IMU_set_input_composite(LSM6DS3TR_C_INPUT_COMPOSITE_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_INPUT_COMPOSITE_LOW_LATENCY: break; + case LSM6DS3TR_C_INPUT_COMPOSITE_LOW_NOISE: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.input_composite_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.input_composite_f.reg_addr, + (uint8_t)value, + _imu_fields.input_composite_f.field_start, + _imu_fields.input_composite_f.field_len); + return err; +} + +ERRNO_e IMU_get_input_composite(LSM6DS3TR_C_INPUT_COMPOSITE_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.input_composite_f.reg_addr, + &raw, + _imu_fields.input_composite_f.field_start, + _imu_fields.input_composite_f.field_len); + + *value = (LSM6DS3TR_C_INPUT_COMPOSITE_e)raw; + _imu_cfg.field_v.input_composite_v = *value; + return err; +} + +ERRNO_e IMU_set_FTH(uint16_t value) +{ + ERRNO_e err; + if (value > 2047) { + return ERRNO_INVALID_PARAM; + } + _imu_cfg.field_v.fifo_threshold_v = value; + uint16_t safe_val = value & 0x07FF; + uint8_t low = safe_val & 0xFF; + uint8_t high = safe_val >> 8; + + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fifo_thresh_low_f.reg_addr, + low, + _imu_fields.fifo_thresh_low_f.field_start, + _imu_fields.fifo_thresh_low_f.field_len); + if (err == ERRNO_SUCCESS) { + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fifo_thresh_high_f.reg_addr, + high, + _imu_fields.fifo_thresh_high_f.field_start, + _imu_fields.fifo_thresh_high_f.field_len); + } + + return err; +} + +ERRNO_e IMU_get_FTH(uint16_t *value) +{ + ERRNO_e err; + uint8_t low, high; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fifo_thresh_low_f.reg_addr, + &low, + _imu_fields.fifo_thresh_low_f.field_start, + _imu_fields.fifo_thresh_low_f.field_len); + if (err == ERRNO_SUCCESS) { + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fifo_thresh_high_f.reg_addr, + &high, + _imu_fields.fifo_thresh_high_f.field_start, + _imu_fields.fifo_thresh_high_f.field_len); + } + *value = 0x07FF & ((uint16_t)low | ((uint16_t)high << 8)); + _imu_cfg.field_v.fifo_threshold_v = *value; + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_set_dec_fifo_gyro(LSM6DS3TR_C_DEC_FIFO_GYRO_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_DEC_FIFO_GYRO_DATA_NOT_IN_FIFO: break; + case LSM6DS3TR_C_DEC_FIFO_GYRO_NO_DECIMATION: break; + case LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_2: break; + case LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_3: break; + case LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_4: break; + case LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_8: break; + case LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_16: break; + case LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_32: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.dec_fifo_gyro_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.dec_fifo_gyro_f.reg_addr, + (uint8_t)value, + _imu_fields.dec_fifo_gyro_f.field_start, + _imu_fields.dec_fifo_gyro_f.field_len); + return err; +} + +ERRNO_e IMU_get_dec_fifo_gyro(LSM6DS3TR_C_DEC_FIFO_GYRO_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.dec_fifo_gyro_f.reg_addr, + &raw, + _imu_fields.dec_fifo_gyro_f.field_start, + _imu_fields.dec_fifo_gyro_f.field_len); + + *value = (LSM6DS3TR_C_DEC_FIFO_GYRO_e)raw; + _imu_cfg.field_v.dec_fifo_gyro_v = *value; + return err; +} + +ERRNO_e IMU_set_dec_fifo_xl(LSM6DS3TR_C_DEC_FIFO_XL_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_DEC_FIFO_XL_DATA_NOT_IN_FIFO: break; + case LSM6DS3TR_C_DEC_FIFO_XL_NO_DECIMATION: break; + case LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_2: break; + case LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_3: break; + case LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_4: break; + case LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_8: break; + case LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_16: break; + case LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_32: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.dec_fifo_xl_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.dec_fifo_xl_f.reg_addr, + (uint8_t)value, + _imu_fields.dec_fifo_xl_f.field_start, + _imu_fields.dec_fifo_xl_f.field_len); + return err; +} + +ERRNO_e IMU_get_dec_fifo_xl(LSM6DS3TR_C_DEC_FIFO_XL_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.dec_fifo_xl_f.reg_addr, + &raw, + _imu_fields.dec_fifo_xl_f.field_start, + _imu_fields.dec_fifo_xl_f.field_len); + + *value = (LSM6DS3TR_C_DEC_FIFO_XL_e)raw; + _imu_cfg.field_v.dec_fifo_xl_v = *value; + return err; +} + +ERRNO_e IMU_set_odr_fifo(LSM6DS3TR_C_ODR_FIFO_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_ODR_FIFO_DISABLE: break; + case LSM6DS3TR_C_ODR_FIFO_12_5Hz: break; + case LSM6DS3TR_C_ODR_FIFO_26Hz: break; + case LSM6DS3TR_C_ODR_FIFO_52Hz: break; + case LSM6DS3TR_C_ODR_FIFO_104Hz: break; + case LSM6DS3TR_C_ODR_FIFO_208Hz: break; + case LSM6DS3TR_C_ODR_FIFO_416Hz: break; + case LSM6DS3TR_C_ODR_FIFO_833Hz: break; + case LSM6DS3TR_C_ODR_FIFO_1666Hz: break; + case LSM6DS3TR_C_ODR_FIFO_3332Hz: break; + case LSM6DS3TR_C_ODR_FIFO_6664Hz: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.odr_fifo_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_fifo_f.reg_addr, + (uint8_t)value, + _imu_fields.odr_fifo_f.field_start, + _imu_fields.odr_fifo_f.field_len); + return err; +} + +ERRNO_e IMU_get_odr_fifo(LSM6DS3TR_C_ODR_FIFO_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.odr_fifo_f.reg_addr, + &raw, + _imu_fields.odr_fifo_f.field_start, + _imu_fields.odr_fifo_f.field_len); + + *value = (LSM6DS3TR_C_ODR_FIFO_e)raw; + _imu_cfg.field_v.odr_fifo_v = *value; + return err; +} + +ERRNO_e IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_FIFO_MODE_BYPASS: break; + case LSM6DS3TR_C_FIFO_MODE_FIFO_STOP_ON_FULL: break; + case LSM6DS3TR_C_FIFO_MODE_CON_THEN_FIFO: break; + case LSM6DS3TR_C_FIFO_MODE_BYPASS_THEN_CON: break; + case LSM6DS3TR_C_FIFO_MODE_CONTINUOUS: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.fifo_mode_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fifo_mode_f.reg_addr, + (uint8_t)value, + _imu_fields.fifo_mode_f.field_start, + _imu_fields.fifo_mode_f.field_len); + return err; +} + +ERRNO_e IMU_get_fifo_mode(LSM6DS3TR_C_FIFO_MODE_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.fifo_mode_f.reg_addr, + &raw, + _imu_fields.fifo_mode_f.field_start, + _imu_fields.fifo_mode_f.field_len); + + *value = (LSM6DS3TR_C_FIFO_MODE_e)raw; + _imu_cfg.field_v.fifo_mode_v = *value; + return err; +} + +ERRNO_e IMU_set_int1_fth(LSM6DS3TR_C_INT1_FTH_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_INT1_FTH_DISABLED: break; + case LSM6DS3TR_C_INT1_FTH_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.int1_fth_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_fth_f.reg_addr, + (uint8_t)value, + _imu_fields.int1_fth_f.field_start, + _imu_fields.int1_fth_f.field_len); + return err; +} + +ERRNO_e IMU_get_int1_fth(LSM6DS3TR_C_INT1_FTH_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_fth_f.reg_addr, + &raw, + _imu_fields.int1_fth_f.field_start, + _imu_fields.int1_fth_f.field_len); + + *value = (LSM6DS3TR_C_INT1_FTH_e)raw; + _imu_cfg.field_v.int1_fth_v = *value; + return err; +} + +ERRNO_e IMU_set_sw_reset(LSM6DS3TR_C_SW_RESET_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_SW_RESET_NORMAL_MODE: break; + case LSM6DS3TR_C_SW_RESET_RESET_DEVICE: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.sw_reset_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.sw_reset_f.reg_addr, + (uint8_t)value, + _imu_fields.sw_reset_f.field_start, + _imu_fields.sw_reset_f.field_len); + return err; +} + +ERRNO_e IMU_get_sw_reset(LSM6DS3TR_C_SW_RESET_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.sw_reset_f.reg_addr, + &raw, + _imu_fields.sw_reset_f.field_start, + _imu_fields.sw_reset_f.field_len); + + *value = (LSM6DS3TR_C_SW_RESET_e)raw; + _imu_cfg.field_v.sw_reset_v = *value; + return err; +} + +ERRNO_e IMU_set_bdu(LSM6DS3TR_C_BDU_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_BDU_CONTINUOUS: break; + case LSM6DS3TR_C_BDU_BLOCK_UPDATE: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.bdu_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.bdu_f.reg_addr, + (uint8_t)value, + _imu_fields.bdu_f.field_start, + _imu_fields.bdu_f.field_len); + return err; +} + +ERRNO_e IMU_get_bdu(LSM6DS3TR_C_BDU_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.bdu_f.reg_addr, + &raw, + _imu_fields.bdu_f.field_start, + _imu_fields.bdu_f.field_len); + + *value = (LSM6DS3TR_C_BDU_e)raw; + _imu_cfg.field_v.bdu_v = *value; + return err; +} + +ERRNO_e IMU_set_interrupts_enable(LSM6DS3TR_C_INTERRUPTS_ENABLE_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_INTERRUPTS_DISABLE: break; + case LSM6DS3TR_C_INTERRUPTS_ENABLE: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.interrupts_enable_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.interrupts_enable_f.reg_addr, + (uint8_t)value, + _imu_fields.interrupts_enable_f.field_start, + _imu_fields.interrupts_enable_f.field_len); + return err; +} + +ERRNO_e IMU_get_interrupts_enable(LSM6DS3TR_C_INTERRUPTS_ENABLE_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.interrupts_enable_f.reg_addr, + &raw, + _imu_fields.interrupts_enable_f.field_start, + _imu_fields.interrupts_enable_f.field_len); + + *value = (LSM6DS3TR_C_INTERRUPTS_ENABLE_e)raw; + _imu_cfg.field_v.interrupts_enable_v = *value; + return err; +} + +ERRNO_e IMU_set_slope_fds(LSM6DS3TR_C_SLOPE_FDS_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_SLOPE_FDS_SLOPE: break; + case LSM6DS3TR_C_SLOPE_FDS_HPF: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.slope_fds_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.slope_fds_f.reg_addr, + (uint8_t)value, + _imu_fields.slope_fds_f.field_start, + _imu_fields.slope_fds_f.field_len); + return err; +} + +ERRNO_e IMU_get_slope_fds(LSM6DS3TR_C_SLOPE_FDS_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.slope_fds_f.reg_addr, + &raw, + _imu_fields.slope_fds_f.field_start, + _imu_fields.slope_fds_f.field_len); + + *value = (LSM6DS3TR_C_SLOPE_FDS_e)raw; + _imu_cfg.field_v.slope_fds_v = *value; + return err; +} + +ERRNO_e IMU_set_tap_x_en(LSM6DS3TR_C_TAP_X_EN_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_TAP_X_EN_DISABLED: break; + case LSM6DS3TR_C_TAP_X_EN_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.tap_x_en_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_x_en_f.reg_addr, + (uint8_t)value, + _imu_fields.tap_x_en_f.field_start, + _imu_fields.tap_x_en_f.field_len); + return err; +} + +ERRNO_e IMU_get_tap_x_en(LSM6DS3TR_C_TAP_X_EN_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_x_en_f.reg_addr, + &raw, + _imu_fields.tap_x_en_f.field_start, + _imu_fields.tap_x_en_f.field_len); + + *value = (LSM6DS3TR_C_TAP_X_EN_e)raw; + _imu_cfg.field_v.tap_x_en_v = *value; + return err; +} + +ERRNO_e IMU_set_tap_y_en(LSM6DS3TR_C_TAP_Y_EN_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_TAP_Y_EN_DISABLED: break; + case LSM6DS3TR_C_TAP_Y_EN_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.tap_y_en_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_y_en_f.reg_addr, + (uint8_t)value, + _imu_fields.tap_y_en_f.field_start, + _imu_fields.tap_y_en_f.field_len); + return err; +} + +ERRNO_e IMU_get_tap_y_en(LSM6DS3TR_C_TAP_Y_EN_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_y_en_f.reg_addr, + &raw, + _imu_fields.tap_y_en_f.field_start, + _imu_fields.tap_y_en_f.field_len); + + *value = (LSM6DS3TR_C_TAP_Y_EN_e)raw; + _imu_cfg.field_v.tap_y_en_v = *value; + return err; +} + +ERRNO_e IMU_set_tap_z_en(LSM6DS3TR_C_TAP_Z_EN_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_TAP_Z_EN_DISABLED: break; + case LSM6DS3TR_C_TAP_Z_EN_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.tap_z_en_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_z_en_f.reg_addr, + (uint8_t)value, + _imu_fields.tap_z_en_f.field_start, + _imu_fields.tap_z_en_f.field_len); + return err; +} + +ERRNO_e IMU_get_tap_z_en(LSM6DS3TR_C_TAP_Z_EN_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_z_en_f.reg_addr, + &raw, + _imu_fields.tap_z_en_f.field_start, + _imu_fields.tap_z_en_f.field_len); + + *value = (LSM6DS3TR_C_TAP_Z_EN_e)raw; + _imu_cfg.field_v.tap_z_en_v = *value; + return err; +} + +ERRNO_e IMU_set_lir(LSM6DS3TR_C_LIR_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_LIR_DISABLED: break; + case LSM6DS3TR_C_LIR_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.lir_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lir_f.reg_addr, + (uint8_t)value, + _imu_fields.lir_f.field_start, + _imu_fields.lir_f.field_len); + return err; +} + +ERRNO_e IMU_get_lir(LSM6DS3TR_C_LIR_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.lir_f.reg_addr, + &raw, + _imu_fields.lir_f.field_start, + _imu_fields.lir_f.field_len); + + *value = (LSM6DS3TR_C_LIR_e)raw; + _imu_cfg.field_v.lir_v = *value; + return err; +} + +ERRNO_e IMU_set_tap_ths(uint8_t value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + if (value >= 32) { + return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.tap_ths_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_ths_f.reg_addr, + (uint8_t)value, + _imu_fields.tap_ths_f.field_start, + _imu_fields.tap_ths_f.field_len); + return err; +} + +ERRNO_e IMU_get_tap_ths(uint8_t *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.tap_ths_f.reg_addr, + &raw, + _imu_fields.tap_ths_f.field_start, + _imu_fields.tap_ths_f.field_len); + + *value = raw; + _imu_cfg.field_v.tap_ths_v = *value; + return err; +} + +ERRNO_e IMU_set_quiet(LSM6DS3TR_C_QUIET_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_QUIET_2_OVER_ODR_XL: break; + case LSM6DS3TR_C_QUIET_4_OVER_ODR_XL: break; + case LSM6DS3TR_C_QUIET_8_OVER_ODR_XL: break; + case LSM6DS3TR_C_QUIET_12_OVER_ODR_XL: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.quiet_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.quiet_f.reg_addr, + (uint8_t)value, + _imu_fields.quiet_f.field_start, + _imu_fields.quiet_f.field_len); + return err; +} + +ERRNO_e IMU_get_quiet(LSM6DS3TR_C_QUIET_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.quiet_f.reg_addr, + &raw, + _imu_fields.quiet_f.field_start, + _imu_fields.quiet_f.field_len); + + *value = (LSM6DS3TR_C_QUIET_e)raw; + _imu_cfg.field_v.quiet_v = *value; + return err; +} + +ERRNO_e IMU_set_shock(LSM6DS3TR_C_SHOCK_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_SHOCK_4_OVER_ODR_XL: break; + case LSM6DS3TR_C_SHOCK_8_OVER_ODR_XL: break; + case LSM6DS3TR_C_SHOCK_16_OVER_ODR_XL: break; + case LSM6DS3TR_C_SHOCK_24_OVER_ODR_XL: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.shock_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.shock_f.reg_addr, + (uint8_t)value, + _imu_fields.shock_f.field_start, + _imu_fields.shock_f.field_len); + return err; +} + +ERRNO_e IMU_get_shock(LSM6DS3TR_C_SHOCK_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.shock_f.reg_addr, + &raw, + _imu_fields.shock_f.field_start, + _imu_fields.shock_f.field_len); + + *value = (LSM6DS3TR_C_SHOCK_e)raw; + _imu_cfg.field_v.shock_v = *value; + return err; +} + +ERRNO_e IMU_set_single_double_tap(LSM6DS3TR_C_SINGLE_DOUBLE_TAP_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_SINGLE_DOUBLE_TAP_SINGLE_ONLY: break; + case LSM6DS3TR_C_SINGLE_DOUBLE_TAP_SINGLE_AND_DOUBLE: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.single_double_tap_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.single_double_tap_f.reg_addr, + (uint8_t)value, + _imu_fields.single_double_tap_f.field_start, + _imu_fields.single_double_tap_f.field_len); + return err; +} + +ERRNO_e IMU_get_single_double_tap(LSM6DS3TR_C_SINGLE_DOUBLE_TAP_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.single_double_tap_f.reg_addr, + &raw, + _imu_fields.single_double_tap_f.field_start, + _imu_fields.single_double_tap_f.field_len); + + *value = (LSM6DS3TR_C_SINGLE_DOUBLE_TAP_e)raw; + _imu_cfg.field_v.single_double_tap_v = *value; + return err; +} + +ERRNO_e IMU_set_wk_ths(uint8_t value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + if (value > 63) { + return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.wk_ths_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.wk_ths_f.reg_addr, + (uint8_t)value, + _imu_fields.wk_ths_f.field_start, + _imu_fields.wk_ths_f.field_len); + return err; +} + +ERRNO_e IMU_get_wk_ths(uint8_t *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.wk_ths_f.reg_addr, + &raw, + _imu_fields.wk_ths_f.field_start, + _imu_fields.wk_ths_f.field_len); + + *value = raw; + _imu_cfg.field_v.wk_ths_v = *value; /* is this needed? */ + return err; +} + +ERRNO_e IMU_set_wake_dur(LSM6DS3TR_C_WAKE_DUR_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_WAKE_DUR_0_ODR_XL: break; + case LSM6DS3TR_C_WAKE_DUR_1_ODR_XL: break; + case LSM6DS3TR_C_WAKE_DUR_2_ODR_XL: break; + case LSM6DS3TR_C_WAKE_DUR_3_ODR_XL: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.wake_dur_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.wake_dur_f.reg_addr, + (uint8_t)value, + _imu_fields.wake_dur_f.field_start, + _imu_fields.wake_dur_f.field_len); + return err; +} + +ERRNO_e IMU_get_wake_dur(LSM6DS3TR_C_WAKE_DUR_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.wake_dur_f.reg_addr, + &raw, + _imu_fields.wake_dur_f.field_start, + _imu_fields.wake_dur_f.field_len); + + *value = (LSM6DS3TR_C_WAKE_DUR_e)raw; + _imu_cfg.field_v.wake_dur_v = *value; + return err; +} + +ERRNO_e IMU_set_int1_single_tap(LSM6DS3TR_C_INT1_SINGLE_TAP_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_INT1_SINGLE_TAP_DISABLED: break; + case LSM6DS3TR_C_INT1_SINGLE_TAP_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.int1_single_tap_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_single_tap_f.reg_addr, + (uint8_t)value, + _imu_fields.int1_single_tap_f.field_start, + _imu_fields.int1_single_tap_f.field_len); + return err; +} + +ERRNO_e IMU_get_int1_single_tap(LSM6DS3TR_C_INT1_SINGLE_TAP_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_single_tap_f.reg_addr, + &raw, + _imu_fields.int1_single_tap_f.field_start, + _imu_fields.int1_single_tap_f.field_len); + + *value = (LSM6DS3TR_C_INT1_SINGLE_TAP_e)raw; + _imu_cfg.field_v.int1_single_tap_v = *value; + return err; +} + +ERRNO_e IMU_set_int1_wu(LSM6DS3TR_C_INT1_WU_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_INT1_WU_DISABLED: break; + case LSM6DS3TR_C_INT1_WU_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.int1_wu_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_wu_f.reg_addr, + (uint8_t)value, + _imu_fields.int1_wu_f.field_start, + _imu_fields.int1_wu_f.field_len); + return err; +} + +ERRNO_e IMU_get_int1_wu(LSM6DS3TR_C_INT1_WU_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_wu_f.reg_addr, + &raw, + _imu_fields.int1_wu_f.field_start, + _imu_fields.int1_wu_f.field_len); + + *value = (LSM6DS3TR_C_INT1_WU_e)raw; + _imu_cfg.field_v.int1_wu_v = *value; + return err; +} + +ERRNO_e IMU_set_int1_double_tap(LSM6DS3TR_C_INT1_DOUBLE_TAP_e value) +{ + ERRNO_e err; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + switch (value) { + case LSM6DS3TR_C_INT1_DOUBLE_TAP_DISABLED: break; + case LSM6DS3TR_C_INT1_DOUBLE_TAP_ENABLED: break; + default: return ERRNO_INVALID_PARAM; + } + + _imu_cfg.field_v.int1_double_tap_v = value; + err = I2C_field_write(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_double_tap_f.reg_addr, + (uint8_t)value, + _imu_fields.int1_double_tap_f.field_start, + _imu_fields.int1_double_tap_f.field_len); + return err; +} + +ERRNO_e IMU_get_int1_double_tap(LSM6DS3TR_C_INT1_DOUBLE_TAP_e *value) +{ + ERRNO_e err; + uint8_t raw; + + err = I2C_field_read(I2C_CH_1, + LSM6DS3_I2C_DEV_ADDR, + _imu_fields.int1_double_tap_f.reg_addr, + &raw, + _imu_fields.int1_double_tap_f.field_start, + _imu_fields.int1_double_tap_f.field_len); + + *value = (LSM6DS3TR_C_INT1_DOUBLE_TAP_e)raw; + _imu_cfg.field_v.int1_double_tap_v = *value; + return err; +} + +ERRNO_e IMU_temp_raw_read(int16_t *p_dest) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + else if (p_dest == NULL) { + return ERRNO_BAD_POINTER; + } + + if (I2C_words_read (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_OUT_TEMP_L, (uint16_t *) p_dest, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_temp_c_read(float *p_dest) +{ + ERRNO_e err; + int16_t reading; + + err = IMU_temp_raw_read (&reading); + if (err != ERRNO_SUCCESS) { + return err; + } + + /* Need to divide by temp sensitivity to scale. */ + *p_dest = (float) reading / (float) _imu_cfg.temp_sensitivity; + + /* Add 25 degrees to remove offset. */ + // *p_dest += 25; + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_temp_f_read(float *p_dest) +{ + ERRNO_e err; + float reading; + + err = IMU_temp_c_read(&reading); + if (err != ERRNO_SUCCESS) { + return err; + } + + /* Convert to F. */ + *p_dest = (reading * (float)9.0) / (float)5.0 + (float)32.0; + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_reset(void) +{ + uint8_t byte = 0x01; + + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + if (I2C_bytes_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_CTRL3_C, &byte, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + /* Wait for 10 ms for the reset to complete. */ + k_busy_wait(10000); + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_xl_raw_x_read(int16_t *p_dest) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + else if (p_dest == NULL) { + return ERRNO_BAD_POINTER; + } + + if (I2C_words_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_OUTX_L_XL, (uint16_t *) p_dest, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_xl_raw_y_read(int16_t *p_dest) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + else if (p_dest == NULL) { + return ERRNO_BAD_POINTER; + } + + if (I2C_words_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_OUTY_L_XL, (uint16_t *) p_dest, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_xl_raw_z_read(int16_t *p_dest) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + else if (p_dest == NULL) { + return ERRNO_BAD_POINTER; + } + + if (I2C_words_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_OUTZ_L_XL, (uint16_t *) p_dest, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_xl_x_read(float *p_dest) +{ + int16_t x; + ERRNO_e err = IMU_xl_raw_x_read(&x); + + *p_dest = _imu_xl_raw_to_float(x); + + return err; +} + +ERRNO_e IMU_xl_y_read(float *p_dest) +{ + int16_t y; + ERRNO_e err = IMU_xl_raw_y_read(&y); + + *p_dest = _imu_xl_raw_to_float(y); + + return err; +} + +ERRNO_e IMU_xl_z_read(float *p_dest) +{ + int16_t z; + ERRNO_e err = IMU_xl_raw_z_read(&z); + + *p_dest = _imu_xl_raw_to_float(z); + + return err; +} + +ERRNO_e IMU_xl_read(IMU_data_s *p_dest) +{ + ERRNO_e err; + float x; + float y; + float z; + + err = IMU_xl_x_read(&x); + err |= IMU_xl_y_read(&y); + err |= IMU_xl_z_read(&z); + + p_dest->x = x; + p_dest->y = y; + p_dest->z = z; + + return err; +} + +ERRNO_e IMU_gyro_raw_x_read(int16_t *p_dest) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + else if (p_dest == NULL) { + return ERRNO_BAD_POINTER; + } + + if (I2C_words_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_OUTX_L_G, (uint16_t *) p_dest, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_gyro_raw_y_read(int16_t *p_dest) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + else if (p_dest == NULL) { + return ERRNO_BAD_POINTER; + } + + if (I2C_words_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_OUTY_L_G, (uint16_t *) p_dest, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_gyro_raw_z_read(int16_t *p_dest) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + else if (p_dest == NULL) { + return ERRNO_BAD_POINTER; + } + + if (I2C_words_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_OUTZ_L_G, (uint16_t *) p_dest, 1) != ERRNO_SUCCESS) { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_gyro_x_read(float *p_dest) +{ + int16_t x; + ERRNO_e err = IMU_gyro_raw_x_read(&x); + + *p_dest = _imu_gyro_raw_to_float(x); + + return err; +} + +ERRNO_e IMU_gyro_y_read(float *p_dest) +{ + int16_t y; + ERRNO_e err = IMU_gyro_raw_y_read (&y); + + *p_dest = _imu_gyro_raw_to_float (y); + + return err; +} + +ERRNO_e IMU_gyro_z_read(float *p_dest) +{ + int16_t z; + ERRNO_e err = IMU_gyro_raw_z_read (&z); + + *p_dest = _imu_gyro_raw_to_float (z); + + return err; +} + +ERRNO_e IMU_gyro_read(IMU_data_s *p_dest) +{ + ERRNO_e err; + float x; + float y; + float z; + + err = IMU_gyro_x_read (&x); + err |= IMU_gyro_y_read (&y); + err |= IMU_gyro_z_read (&z); + + p_dest->x = x; + p_dest->y = y; + p_dest->z = z; + + return err; +} + +void IMU_set_shot_capture_mode(bool enable) +{ + if (enable){ + atomic_set_bit(&_imu_cfg.interrupt_mode_flags, IMU_int_shot_capture_mode); + } else { + atomic_clear_bit(&_imu_cfg.interrupt_mode_flags, IMU_int_shot_capture_mode); + } + +} + +ERRNO_e IMU_fifo_status_1to2_read (uint16_t *unread_words, uint8_t *empty, uint8_t *smart_full, uint8_t *over_run, uint8_t *waterM) +{ + ERRNO_e err; + uint16_t regs_val; + //err = I2C_words_read (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_STATUS1, ®s_val, 1); + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_STATUS1, (uint8_t *) ®s_val, 2); + if (ERRNO_SUCCESS != err) { + PRINT (ERROR, "i2c read fifo status regs failed in isr!\r\n"); + } + + if (ERRNO_SUCCESS == err) { + *unread_words = regs_val & 0x07FF; + *empty = (uint8_t)(regs_val >> 8) & LSM6DS3TR_C_FIFO_EMPTY_FIFO_EMPTY; + *smart_full = (uint8_t)(regs_val >> 8) & LSM6DS3TR_C_FIFO_FULL_FIFO_FULL; + *over_run = (uint8_t)(regs_val >> 8) & LSM6DS3TR_C_OVERRUN_OVERRUN; + *waterM = (uint8_t)(regs_val >> 8) & LSM6DS3TR_C_WTM_ABOVE_OR_EQUAL_WTM; + } + + return err; +} + +ERRNO_e IMU_func_src1_read (uint8_t *sign_motion) +{ + ERRNO_e err; + uint8_t reg_val; + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_STATUS1, ®_val, 1); + if (err != ERRNO_SUCCESS) + { + PRINT (ERROR, "i2c read func src1 reg failed in isr!\r\n"); + return err; + } + *sign_motion = reg_val & LSM6DS3TR_C_SIGN_MOT_EV_STATUS_DETECTED; + + return err; +} + +ERRNO_e IMU_fifo_th_set(uint16_t fifo_threshold) +{ + ERRNO_e err = ERRNO_SUCCESS; + uint8_t reset = 0; + LSM6DS3TR_C_ODR_FIFO_e max_odr; + + if (fifo_threshold == 0 && atomic_get(&_imu_fifo_is_set)) { + + /* unroute FIFO threshold interrupt to IMU INT1 pad */ + err = IMU_set_int1_fth(LSM6DS3TR_C_INT1_FTH_DISABLED); + + /* disable fifo by setting oodr_fifo and fifo_mode to 0 */ + if (err == ERRNO_SUCCESS) { + err = I2C_bytes_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_CTRL5, &reset, 1); + } + + /* diable accelerometer and gyro data in FIFO */ + if (err == ERRNO_SUCCESS) { + err = I2C_bytes_write(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_FIFO_CTRL3, &reset, 1); + } + + /* set bdu bit to continuous update */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_bdu(LSM6DS3TR_C_BDU_CONTINUOUS); + } + + if (err == ERRNO_SUCCESS) { + err = IMU_set_FTH(0); + } + + atomic_set(&_imu_fifo_is_set, 0); + } else if (fifo_threshold && !atomic_get(&_imu_fifo_is_set)) { + err = IMU_set_FTH(fifo_threshold); + + /* set BDU bit to enable proper reading of fifo output registers*/ + if (err == ERRNO_SUCCESS) { + err = IMU_set_bdu(LSM6DS3TR_C_BDU_BLOCK_UPDATE); + } + + /* enable gyroscope without decimation into fifo*/ + if (err == ERRNO_SUCCESS) { + err = IMU_set_dec_fifo_gyro(LSM6DS3TR_C_DEC_FIFO_GYRO_NO_DECIMATION); + } + + /* enable accelerometer data without decimation into fifo*/ + if (err == ERRNO_SUCCESS) { + err = IMU_set_dec_fifo_xl(LSM6DS3TR_C_DEC_FIFO_XL_NO_DECIMATION); + } + + /* set fifo odr rate equal to maximum of accelerometer and gyroscope odr */ + max_odr = (LSM6DS3TR_C_ODR_FIFO_e) ((uint8_t) _imu_cfg.field_v.odr_fifo_v > (uint8_t) _imu_cfg.field_v.odr_xl_v ? _imu_cfg.field_v.odr_fifo_v : _imu_cfg.field_v.odr_xl_v); + if (err == ERRNO_SUCCESS) { + err = IMU_set_odr_fifo(max_odr); + } + + /* set fifo to continuous mode */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_CONTINUOUS); + } + + /* route FIFO threshold interrupt to IMU INT1 pad*/ + if (err == ERRNO_SUCCESS) { + err = IMU_set_int1_fth(LSM6DS3TR_C_INT1_FTH_ENABLED); + } + + atomic_set(&_imu_fifo_is_set, 1); + } + + return err; +} + +bool IMU_is_fifo_set(void) +{ + return _imu_fifo_is_set; +} + +ERRNO_e IMU_motion_int_enable (bool en, uint8_t threshold, uint8_t duration) +{ + if (!_imu_init) { + return ERRNO_NOT_INITIALIZED; + } + + if (en && !atomic_get(&_imu_motion_int_enable)) { + if (IMU_set_interrupts_enable(LSM6DS3TR_C_INTERRUPTS_ENABLE)) { + return ERRNO_I2C_FAILURE; + } + + if (IMU_set_lir(LSM6DS3TR_C_LIR_DISABLED)) { + return ERRNO_I2C_FAILURE; + } + + if (IMU_set_slope_fds(LSM6DS3TR_C_SLOPE_FDS_SLOPE)) { + return ERRNO_I2C_FAILURE; + } + + if (IMU_set_wake_dur((LSM6DS3TR_C_WAKE_DUR_e)duration)) { + return ERRNO_I2C_FAILURE; + } + + if (IMU_set_wk_ths(threshold)) { + return ERRNO_I2C_FAILURE; + } + + if (IMU_set_int1_wu(LSM6DS3TR_C_INT1_WU_ENABLED)) { + return ERRNO_I2C_FAILURE; + } + + atomic_set_bit(&_imu_cfg.interrupt_mode_flags, IMU_int_wake_up_mode); + + /* Set enable flag. This is used so if the IMU is put to sleep, we keep accelerometer running in low + power mode to detect motion and interrupt. */ + atomic_set(&_imu_motion_int_enable, 1); + } + else if (!en && atomic_get(&_imu_motion_int_enable)) { + if (IMU_set_int1_wu(LSM6DS3TR_C_INT1_WU_DISABLED)) + { + return ERRNO_I2C_FAILURE; + } + + atomic_clear_bit(&_imu_cfg.interrupt_mode_flags, IMU_int_wake_up_mode); + atomic_set(&_imu_motion_int_enable, 0); + } else { + goto out; + } + +out: + return ERRNO_SUCCESS; +} + +ERRNO_e IMU_sleep (void) +{ + uint8_t byte = 0x00; + + if (!_imu_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* README: Not used in initial release. */ + /* Disable shot capture, if enabled. */ + // SHOT_CAPTURE_enable (false); + + if (atomic_get(&_imu_motion_int_enable)) + { + /* Set to accelerometer low performance mode. */ + /* This lowers the output data rate of the accelerometer to 26 Hz. */ + byte = 0x10; + if (I2C_bytes_write (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_CTRL6_C, &byte, 1) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + byte = 0x20; + if (I2C_bytes_write (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_CTRL1_XL, &byte, 1) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + /* AN5130 page 38, option b. */ + k_busy_wait (100000); + + /* Route the wake-up interrupt signal to INT1 pin. */ + // byte = 0x20; + // if (I2C_bytes_write (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_MD1_CFG, &byte, 1) != ERRNO_SUCCESS) + // { + // return ERRNO_I2C_FAILURE; + // } + + /* Configure GPIO for interrupts and register callback. */ + if (GPIO_cfg (GPIO_PIN_IMU_INT, GPIO_DIRECTION_IN, GPIO_PULLDOWN, GPIO_INIT_LOW, GPIO_INT_LL_HIGH, NULL) != ERRNO_SUCCESS) + { + return ERRNO_GPIO_FAILURE; + } + } + else + { + /* Disable accelerometer. */ + byte = 0x00; + if (I2C_bytes_write (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_CTRL1_XL, &byte, 1) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + } + + /* Disable gyro. */ + byte = 0x00; + if (I2C_bytes_write (I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_CTRL2_G, &byte, 1) != ERRNO_SUCCESS) + { + return ERRNO_I2C_FAILURE; + } + + return ERRNO_SUCCESS; +} + +void IMU_task_install(void) +{ + static k_tid_t id; + static struct k_thread data; + + id = k_thread_create(&data, + _imu_task_stack, + IMU_TASK_STACK_SIZE, + _imu_task, + NULL, + NULL, + NULL, + IMU_TASK_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&data, "IMU_task"); +} + +void IMU_interrupt_handler_task_install(void) +{ + static k_tid_t id; + static struct k_thread data; + + id = k_thread_create (&data, + _imu_interrrupt_handler_task_stack, + IMU_INTERRUPT_HANDLER_TASK_STACK_SIZE, + _imu_interrupt_handler_task, + NULL, + NULL, + NULL, + IMU_INTERRUPT_HANDLER_THREAD_PRIORITY, + 0, + K_NO_WAIT); + k_thread_name_set(&data, "IMU_interrupt_handler_task"); +} + +void IMU_cli_cmd (int32_t argc, char **argv) +{ + ERRNO_e err; + + if (argc == 1) { + if (!strcmp (argv[0], "--reset")) { + PRINT (INFO, "Ret code : %d\r\n", IMU_reset ()); + } + else if (!strcmp (argv[0], "-i") || !strcmp (argv[0], "--init")) { + PRINT (INFO, "Ret code : %d\r\n", IMU_init ()); + } + else if (!strcmp (argv[0], "-t") || !strcmp (argv[0], "--temp")) { + float celsius; + + err = IMU_temp_c_read (&celsius); + PRINT (INFO, "Ret code : %d\r\n", err); + + if (err) + { + return; + } + + PRINT (INFO, "Celsius : %.3f\r\n", (double)celsius); + PRINT (INFO, "Fahrenheit : %.3f\r\n", (((double)celsius * 9.0) / 5.0 + 32.0)); + } else if (!strcmp (argv[0], "-ds") || !strcmp (argv[0], "--dumpstatistic")) { + + _imu_statistic_dump = true; + + } else if (!strcmp (argv[0], "-r") || !strcmp (argv[0], "--read")) { + IMU_data_s xl; + IMU_data_s gyro; + + err = (IMU_xl_read (&xl) | IMU_gyro_read (&gyro)); + PRINT (INFO, "Ret code : %d\r\n", err); + + if (err) { + return; + } + else { + PRINT (INFO, "Accel X : %f\r\n", (double) xl.x); + PRINT (INFO, " Y : %f\r\n", (double) xl.y); + PRINT (INFO, " Z : %f\r\n", (double) xl.z); + PRINT (INFO, "Gyro X : %f\r\n", (double) gyro.x); + PRINT (INFO, " Y : %f\r\n", (double) gyro.y); + PRINT (INFO, " Z : %f\r\n", (double) gyro.z); + } + } else if (!strcmp (argv[0], "-rr") || !strcmp (argv[0], "--readraw")) { + int16_t xl_x, xl_y, xl_z; + int16_t gyro_x, gyro_y, gyro_z; + IMU_data_s xl; + IMU_data_s gyro; + err = (IMU_xl_raw_x_read (&xl_x) | IMU_xl_raw_y_read (&xl_y) | IMU_xl_raw_z_read (&xl_z) | IMU_gyro_raw_x_read (&gyro_x) | IMU_gyro_raw_y_read (&gyro_y) | IMU_gyro_raw_z_read (&gyro_z)); + xl.x = _imu_xl_raw_to_float(xl_x); + xl.y = _imu_xl_raw_to_float(xl_y); + xl.z = _imu_xl_raw_to_float(xl_z); + gyro.x = _imu_gyro_raw_to_float(gyro_x); + gyro.y = _imu_gyro_raw_to_float(gyro_y); + gyro.z = _imu_gyro_raw_to_float(gyro_z); + PRINT (INFO, "Ret code : %d\r\n", err); + + if (err) { + return; + } + else { + PRINT(INFO, "Accel X : %d\r\n", (int) xl_x); + PRINT(INFO, " Y : %d\r\n", (int) xl_y); + PRINT(INFO, " Z : %d\r\n", (int) xl_z); + //PRINT(INFO, "Gyro X : %d\r\n", (int) gyro_x); + //PRINT(INFO, " Y : %d\r\n", (int) gyro_y); + //PRINT(INFO, " Z : %d\r\n", (int) gyro_z); + //PRINT(INFO, "Accel X : %f\r\n", (double) xl.x); + //PRINT(INFO, " Y : %f\r\n", (double) xl.y); + //PRINT(INFO, " Z : %f\r\n", (double) xl.z); + //PRINT(INFO, "Gyro X : %f\r\n", (double) gyro.x); + //PRINT(INFO, " Y : %f\r\n", (double) gyro.y); + //PRINT(INFO, " Z : %f\r\n", (double) gyro.z); + } + } else if (!strcmp (argv[0], "-rxlsr") || !strcmp (argv[0], "--readxlsamplerate")) { + LSM6DS3TR_C_ODR_XL_e value; + float sample_rate = 0; + IMU_get_odr_xl(&value); + switch (value) { + case LSM6DS3TR_C_ODR_XL_POWER_DOWN: sample_rate = 0; break; + case LSM6DS3TR_C_ODR_XL_12_5Hz: sample_rate = 12.5; break; + case LSM6DS3TR_C_ODR_XL_26Hz: sample_rate = 26; break; + case LSM6DS3TR_C_ODR_XL_52Hz: sample_rate = 52; break; + case LSM6DS3TR_C_ODR_XL_104Hz: sample_rate = 104; break; + case LSM6DS3TR_C_ODR_XL_208Hz: sample_rate = 208; break; + case LSM6DS3TR_C_ODR_XL_416Hz: sample_rate = 416; break; + case LSM6DS3TR_C_ODR_XL_833Hz: sample_rate = 833; break; + case LSM6DS3TR_C_ODR_XL_1666Hz: sample_rate = 1666; break; + case LSM6DS3TR_C_ODR_XL_3332Hz: sample_rate = 3332; break; + case LSM6DS3TR_C_ODR_XL_6664Hz: sample_rate = 6664; break; + case LSM6DS3TR_C_ODR_XL_1_6Hz: sample_rate = 1.6; break; + } + PRINT(INFO, "Accelerometer Sample rate : %.1fHz\r\n", (double)sample_rate); + } else if (!strcmp (argv[0], "-rxlr") || !strcmp (argv[0], "--readxlrange")) { + LSM6DS3TR_C_FS_XL_e value; + uint8_t range = 0; + IMU_get_fs_xl(&value); + switch (value) { + case LSM6DS3TR_C_FS_XL_2g: range = 2; break; + case LSM6DS3TR_C_FS_XL_4g: range = 4; break; + case LSM6DS3TR_C_FS_XL_8g: range = 8; break; + case LSM6DS3TR_C_FS_XL_16g: range = 16; break; + } + PRINT(INFO, "Accelerometer range : %d g\r\n", range); + } else if (!strcmp (argv[0], "-rgsr") || !strcmp (argv[0], "--readgyrosamplerate")) { + LSM6DS3TR_C_ODR_G_e value; + float sample_rate = 0; + IMU_get_odr_g(&value); + switch (value) { + case LSM6DS3TR_C_ODR_G_POWER_DOWN: sample_rate = 0; break; + case LSM6DS3TR_C_ODR_G_12_5Hz: sample_rate = 12.5; break; + case LSM6DS3TR_C_ODR_G_26Hz: sample_rate = 26; break; + case LSM6DS3TR_C_ODR_G_52Hz: sample_rate = 52; break; + case LSM6DS3TR_C_ODR_G_104Hz: sample_rate = 104; break; + case LSM6DS3TR_C_ODR_G_208Hz: sample_rate = 208; break; + case LSM6DS3TR_C_ODR_G_416Hz: sample_rate = 416; break; + case LSM6DS3TR_C_ODR_G_833Hz: sample_rate = 833; break; + case LSM6DS3TR_C_ODR_G_1666Hz: sample_rate = 1666; break; + case LSM6DS3TR_C_ODR_G_3332Hz: sample_rate = 3332; break; + case LSM6DS3TR_C_ODR_G_6664Hz: sample_rate = 6664; break; + } + PRINT(INFO, "Gyro Sample rate : %.1fHz\r\n", (double)sample_rate); + } else if (!strcmp (argv[0], "-rgr") || !strcmp (argv[0], "--readgyrorange")) { + LSM6DS3TR_C_FS_G_ABSTR_e value; + uint16_t range = 0; + IMU_get_fs_g(&value); + switch (value) { + case LSM6DS3TR_C_FS_G_ABSTR_125dps: range = 125; break; + case LSM6DS3TR_C_FS_G_ABSTR_250dps: range = 250; break; + case LSM6DS3TR_C_FS_G_ABSTR_500dps: range = 500; break; + case LSM6DS3TR_C_FS_G_ABSTR_1000dps: range = 1000; break; + case LSM6DS3TR_C_FS_G_ABSTR_2000dps: range = 2000; break; + } + PRINT(INFO, "Gyro range : %d dps\r\n", range); + } else if (!strcmp (argv[0], "-rs") || !strcmp (argv[0], "--readshock")) { + LSM6DS3TR_C_SHOCK_e value; + const char *shock = "UNKNOWN"; + ERRNO_e err = IMU_get_shock(&value); + + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to read shock setting: %d\r\n", err); + return; + } + + switch (value) { + case LSM6DS3TR_C_SHOCK_4_OVER_ODR_XL: shock = "4/ODR_XL"; break; + case LSM6DS3TR_C_SHOCK_8_OVER_ODR_XL: shock = "8/ODR_XL"; break; + case LSM6DS3TR_C_SHOCK_16_OVER_ODR_XL: shock = "16/ODR_XL"; break; + case LSM6DS3TR_C_SHOCK_24_OVER_ODR_XL: shock = "24/ODR_XL"; break; + } + PRINT(INFO, "Tap shock duration: %s\r\n", shock); + } else if (!strcmp (argv[0], "-rq") || !strcmp (argv[0], "--readquiet")) { + LSM6DS3TR_C_QUIET_e value; + const char *quiet = "UNKNOWN"; + ERRNO_e err = IMU_get_quiet(&value); + + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to read quiet setting: %d\r\n", err); + return; + } + + switch (value) { + case LSM6DS3TR_C_QUIET_2_OVER_ODR_XL: quiet = "2/ODR_XL"; break; + case LSM6DS3TR_C_QUIET_4_OVER_ODR_XL: quiet = "4/ODR_XL"; break; + case LSM6DS3TR_C_QUIET_8_OVER_ODR_XL: quiet = "8/ODR_XL"; break; + case LSM6DS3TR_C_QUIET_12_OVER_ODR_XL: quiet = "16/ODR_XL"; break; + } + PRINT(INFO, "Tap quiet duration: %s\r\n", quiet); + } else if (!strcmp (argv[0], "-rths") || !strcmp (argv[0], "--readtapthreshold")) { + uint8_t value; + LSM6DS3TR_C_FS_XL_e range; + uint8_t fs_xl_g; + ERRNO_e err = IMU_get_tap_ths(&value); + + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to read tap threshold setting: %d\r\n", err); + return; + } + + err = IMU_get_fs_xl(&range); + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to read accelerometer range setting: %d\r\n", err); + return; + } + + switch (range) { + case LSM6DS3TR_C_FS_XL_2g: fs_xl_g = 2; break; + case LSM6DS3TR_C_FS_XL_4g: fs_xl_g = 4; break; + case LSM6DS3TR_C_FS_XL_8g: fs_xl_g = 8; break; + case LSM6DS3TR_C_FS_XL_16g: fs_xl_g = 16; break; + } + PRINT(INFO, "Tap threshold: %d, %f g\r\n", value, (float) (value * fs_xl_g/32.0f)); + } else if (!strcmp (argv[0], "-rws") || !strcmp (argv[0], "--readwakeupsrc")) { + uint8_t value; + + ERRNO_e err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_WAKE_UP_SRC, &value, 1); + + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to read wakeup src reg: %d\r\n", err); + return; + } + + PRINT(INFO, "Wakeup src reg value: %d\r\n", value); + + } else if (!strcmp (argv[0], "-ri") || !strcmp (argv[0], "--readint1cfg")) { + uint8_t value; + + ERRNO_e err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_INT1_CTRL, &value, 1); + + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to read int1 ctrl reg: %d\r\n", err); + return; + } + + PRINT(INFO, "Int1 ctrl reg value: 0x%02X\r\n", value); + + } else if (!strcmp (argv[0], "-rmd") || !strcmp (argv[0], "--readmd1cfg")) { + uint8_t value; + + ERRNO_e err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_MD1_CFG, &value, 1); + + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to read md1 cfg reg: %d\r\n", err); + return; + } + + PRINT(INFO, "MD1 cfg reg value: 0x%02X\r\n", value); + + } else if (!strcmp (argv[0], "-stc") || !strcmp (argv[0], "--settapconfig")) { + ERRNO_e err = IMU_set_interrupts_enable(LSM6DS3TR_C_INTERRUPTS_ENABLE); + + if (err != ERRNO_SUCCESS) { + PRINT(ERROR, "Failed to set tap config reg: %d\r\n", err); + return; + } + } else { + goto USAGE; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-ftr") || !strcmp (argv[0], "--fifothresholdread")) + { + + _imu_cfg.field_v.fifo_threshold_v = (uint16_t)strtoul (argv[1], NULL, 0); + if (_imu_cfg.field_v.fifo_threshold_v <= 2047 && _imu_cfg.field_v.fifo_threshold_v >= 0) { + PRINT (INFO, "Ret code : %d\r\n", IMU_fifo_th_set(_imu_cfg.field_v.fifo_threshold_v)); + } + else { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-d") || !strcmp (argv[0], "--dump")) + { + _imu_data_dump_time_ms = strtoul (argv[1], NULL, 0); + + _imu_data_dump = true; + + PRINT (INFO, "//Dumping IMU data for %d milliseconds...\r\n//ACCEL.x,ACCEL.y,ACCEL.z,GYRO.x,GYRO.y,GYRO.z\r\n", _imu_data_dump_time_ms); + } + else if (!strcmp (argv[0], "-ge") || !strcmp (argv[0], "--gyroenable")) + { + if (!strcmp (argv[1], "true")) + { + PRINT (INFO, "Ret code : %d\r\n", IMU_gyro_enable (true)); + } + else if (!strcmp (argv[1], "false")) + { + PRINT (INFO, "Ret code : %d\r\n", IMU_gyro_enable (false)); + } + else + { + goto USAGE; + } + } + else if (!strcmp(argv[0], "-gr") || !strcmp(argv[0], "--gyrorange")) + { + LSM6DS3TR_C_FS_G_ABSTR_e value; + uint16_t range = (uint16_t) strtoul(argv[1], NULL, 0); + switch (range) { + case 125: value = LSM6DS3TR_C_FS_G_ABSTR_125dps; break; + case 250: value = LSM6DS3TR_C_FS_G_ABSTR_250dps; break; + case 500: value = LSM6DS3TR_C_FS_G_ABSTR_500dps; break; + case 1000: value = LSM6DS3TR_C_FS_G_ABSTR_1000dps; break; + case 2000: value = LSM6DS3TR_C_FS_G_ABSTR_2000dps; break; + default: goto USAGE; + } + + PRINT(INFO, "Ret code : %d\r\n", IMU_set_fs_g(value)); + } + else if (!strcmp(argv[0], "-gsr") || !strcmp(argv[0], "--gyrosamplerate")) + { + LSM6DS3TR_C_ODR_G_e value; + uint16_t sample_rate = (uint16_t) strtoul(argv[1], NULL, 0); + switch (sample_rate) { + case 13: value = LSM6DS3TR_C_ODR_G_12_5Hz; break; + case 26: value = LSM6DS3TR_C_ODR_G_26Hz; break; + case 52: value = LSM6DS3TR_C_ODR_G_52Hz; break; + case 104: value = LSM6DS3TR_C_ODR_G_104Hz; break; + case 208: value = LSM6DS3TR_C_ODR_G_208Hz; break; + case 416: value = LSM6DS3TR_C_ODR_G_416Hz; break; + case 833: value = LSM6DS3TR_C_ODR_G_833Hz; break; + case 1666: value = LSM6DS3TR_C_ODR_G_1666Hz; break; + case 3332: value = LSM6DS3TR_C_ODR_G_3332Hz; break; + case 6664: value = LSM6DS3TR_C_ODR_G_6664Hz; break; + default: goto USAGE; + } + PRINT (INFO, "Ret code : %d\r\n", IMU_set_odr_g(value)); + } + else if (!strcmp (argv[0], "-gle") || !strcmp (argv[0], "--gyrolowpassen")) + { + if (!strcmp (argv[1], "true")) + { + PRINT (INFO, "Ret code : %d\r\n", IMU_set_lpf1_sel_g(LSM6DS3TR_C_LPF1_SEL_G_ENABLE)); + } + else if (!strcmp (argv[1], "false")) + { + PRINT (INFO, "Ret code : %d\r\n", IMU_set_lpf1_sel_g(LSM6DS3TR_C_LPF1_SEL_G_DISABLE)); + } + else + { + goto USAGE; + } + } + else if (!strcmp (argv[0], "-glb") || !strcmp (argv[0], "--gyrolowband")) + { + LSM6DS3TR_C_FTYPE_e bandwidth = (LSM6DS3TR_C_FTYPE_e) strtoul (argv[1], NULL, 0); + switch (bandwidth) { + case LSM6DS3TR_C_FTYPE_0: break; + case LSM6DS3TR_C_FTYPE_1: break; + case LSM6DS3TR_C_FTYPE_2: break; + case LSM6DS3TR_C_FTYPE_3: break; + default: goto USAGE; + } + PRINT (INFO, "Ret code : %d\r\n", IMU_set_ftype(bandwidth)); + } + else if (!strcmp (argv[0], "-xe") || !strcmp (argv[0], "--xlenable")) + { + if (!strcmp (argv[1], "true")) + { + PRINT (INFO, "Ret code : %d\r\n", IMU_xl_enable(true)); + } + else if (!strcmp (argv[1], "false")) + { + PRINT (INFO, "Ret code : %d\r\n", IMU_xl_enable(false)); + } + else + { + goto USAGE; + } + } + else if (!strcmp(argv[0], "-xr") || !strcmp(argv[0], "--xlrange")) + { + LSM6DS3TR_C_FS_G_e value; + uint8_t range = (uint8_t) strtoul(argv[1], NULL, 0); + switch (range) { + case 2: value = LSM6DS3TR_C_FS_XL_2g; break; + case 4: value = LSM6DS3TR_C_FS_XL_4g; break; + case 8: value = LSM6DS3TR_C_FS_XL_8g; break; + case 16: value = LSM6DS3TR_C_FS_XL_16g; break; + default: goto USAGE; + } + + PRINT(INFO, "Ret code : %d\r\n", IMU_set_fs_xl(value)); + } + else if (!strcmp(argv[0], "-xsr") || !strcmp(argv[0], "--xlsamplerate")) + { + LSM6DS3TR_C_ODR_XL_e value; + uint16_t sample_rate = (uint16_t) strtoul(argv[1], NULL, 0); + switch (sample_rate) { + case 13: value = LSM6DS3TR_C_ODR_G_12_5Hz; break; + case 26: value = LSM6DS3TR_C_ODR_G_26Hz; break; + case 52: value = LSM6DS3TR_C_ODR_G_52Hz; break; + case 104: value = LSM6DS3TR_C_ODR_G_104Hz; break; + case 208: value = LSM6DS3TR_C_ODR_G_208Hz; break; + case 416: value = LSM6DS3TR_C_ODR_G_416Hz; break; + case 833: value = LSM6DS3TR_C_ODR_G_833Hz; break; + case 1666: value = LSM6DS3TR_C_ODR_G_1666Hz; break; + case 3332: value = LSM6DS3TR_C_ODR_G_3332Hz; break; + case 6664: value = LSM6DS3TR_C_ODR_G_6664Hz; break; + case 2: value = LSM6DS3TR_C_ODR_XL_1_6Hz; break; + default: goto USAGE; + } + PRINT(INFO, "Ret code : %d\r\n", IMU_set_odr_xl(value)); + } + else if (!strcmp(argv[0], "-xlpf1b") || !strcmp(argv[0], "--xlow1band")) + { + LSM6DS3TR_C_LPF1_BW_SEL_e bandwidth = (LSM6DS3TR_C_FTYPE_e) strtoul(argv[1], NULL, 0); + switch (bandwidth) { + case LSM6DS3TR_C_LPF1_BW_SEL_ODR_DIV_2: break; + case LSM6DS3TR_C_LPF1_BW_SEL_ODR_DIV_4: break; + default: goto USAGE; + } + PRINT(INFO, "Ret code : %d\r\n", IMU_set_lpf1_bw_sel(bandwidth)); + } + else if (!strcmp (argv[0], "-gfd") || !strcmp (argv[0], "--gyrofifodecimate")) + { + LSM6DS3TR_C_DEC_FIFO_GYRO_e value; + uint8_t factor = (uint8_t) strtoul(argv[1], NULL, 0); + switch (factor) { + case 0: value = LSM6DS3TR_C_DEC_FIFO_GYRO_DATA_NOT_IN_FIFO; break; + case 1: value = LSM6DS3TR_C_DEC_FIFO_GYRO_NO_DECIMATION; break; + case 2: value = LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_2; break; + case 3: value = LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_3; break; + case 4: value = LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_4; break; + case 5: value = LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_8; break; + case 6: value = LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_16; break; + case 7: value = LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_32; break; + default: goto USAGE; + } + PRINT (INFO, "Ret code : %d\r\n", IMU_set_dec_fifo_gyro(value)); + } + else if (!strcmp (argv[0], "-xfd") || !strcmp (argv[0], "--xlfifodecimate")) + { + LSM6DS3TR_C_DEC_FIFO_XL_e value; + uint8_t factor = (uint8_t) strtoul(argv[1], NULL, 0); + switch (factor) { + case 0: value = LSM6DS3TR_C_DEC_FIFO_XL_DATA_NOT_IN_FIFO; break; + case 1: value = LSM6DS3TR_C_DEC_FIFO_XL_NO_DECIMATION; break; + case 2: value = LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_2; break; + case 3: value = LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_3; break; + case 4: value = LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_4; break; + case 5: value = LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_8; break; + case 6: value = LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_16; break; + case 7: value = LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_32; break; + default: goto USAGE; + } + PRINT (INFO, "Ret code : %d\r\n", IMU_set_dec_fifo_xl(value)); + } + else + { + goto USAGE; + } + } + else if (argc == 3) + { + if (!strcmp (argv[0], "-d") || !strcmp (argv[0], "--dump")) + { + _imu_data_dump_time_ms = strtoul (argv[1], NULL, 0); + + if (!strcmp (argv[2], "hex")) + { + _imu_data_dump_hex = true; + } + _imu_data_dump = true; + + PRINT (INFO, "//Dumping IMU data for %d milliseconds...\r\n//ACCEL.x,ACCEL.y,ACCEL.z,GYRO.x,GYRO.y,GYRO.z\r\n", _imu_data_dump_time_ms); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: imu [OPTION...]\r\n"); + PRINT (INFO, " --reset Resets LSM6DS3 IMU\r\n"); + PRINT (INFO, " -i | --init Initializes LSM6DS3 IMU\r\n"); + PRINT (INFO, " -t | --temp Gets the IMU temperature\r\n"); + PRINT (INFO, " -r | --read Reads IMU measurement\r\n"); + PRINT (INFO, " -lp | --sleep Puts the IMU to sleep\r\n"); + PRINT (INFO, " -ftr | ----fifothresholdread Read fifo when threshold reached \r\n"); + PRINT (INFO, " <%%d> threshold level 1 - 2023, 0 to stop fifo\r\n"); + PRINT (INFO, " -d | --dump Dumps IMU raw data\r\n"); + PRINT (INFO, " <%%d> number of milliseconds to dump for\r\n"); + PRINT (INFO, " hex [optional] dumps data in hexadecimal format\r\n"); + PRINT (INFO, " -ge | --gyroenable Enables / Disables gyroscope\r\n"); + PRINT (INFO, " <%%s> true - enable , false - disable\r\n"); + PRINT (INFO, " -gr | --gyrorange Max deg/s\r\n"); + PRINT (INFO, " <%%d> [125, 245, 500, 1000, 2000]\r\n"); + PRINT (INFO, " -gsr | --gyrosamplerate Gyroscope sample rate (Hz)\r\n"); + PRINT (INFO, " <%%d> [13, 26, 52, 104, 208, 416, 833, 1666, 3332, 6664]\r\n"); + PRINT (INFO, " -gle | --gyrolowpassen Enables / Disables gyroscope lowpass filter\r\n"); + PRINT (INFO, " <%%s> true - enable , false - disable\r\n"); + PRINT (INFO, " -glb | --gyrolowband Gyroscope lowpass bandwidth select\r\n"); + PRINT (INFO, " <%%d> [0, 1, 2, 3] (see table 68 for detailed bandwidth selecton)\r\n"); + PRINT (INFO, " -xe | --xlenable Enables / Disables accelerometer\r\n"); + PRINT (INFO, " <%%s> true - enable , false - disable\r\n"); + PRINT (INFO, " -xr | --xlrange Max G force\r\n"); + PRINT (INFO, " <%%d> [2, 4, 8, 16]\r\n"); + PRINT (INFO, " -xsr | --xlsamplerate Accelerometer sample rate (Hz)\r\n"); + PRINT (INFO, " <%%d> [13, 26, 52, 104, 208, 416, 833, 1666, 3332, 6664, 2]\r\n"); + PRINT (INFO, " -xlpf1b | --xlow1band Accelerometer lpf1 bandwidth\r\n"); + PRINT (INFO, " <%%d> [0 - odr/2, 1 - odr/4]\r\n"); + PRINT (INFO, " -gfd | --gyrofifodecimate Fifo decimation\r\n"); + PRINT (INFO, " <%%d> [0 - disable, 1, 2, 3, 4, 5 - 8, 6 - 16, 7 - 32]\r\n"); + PRINT (INFO, " -xfd | --xlfifodecimate Fifo decimation\r\n"); + PRINT (INFO, " <%%d> [0 - disable, 1, 2, 3, 4, 5 - 8, 6 - 16, 7 - 32]\r\n"); + } +} + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ +static void _imu_task(void *p1, void *p2, void *p3) +{ + while (1) { + DBG_STAT_INC(imu_task_counter); + + DBG_STAT_INC_IF(i2c_transfer_active,imu_task_during_i2c_counter); + + if (_imu_statistic_dump) + { + PRINT (INFO, "//Dumping statistic data... \r\n"); + _imu_statistic_dump = false; + + /* get a copy of statistic data */ + #ifdef CONFIG_DBG_STATS + atomic_val_t interrupt_ctr = atomic_get(&_interrupt_count); + k_mutex_lock(&stat_mutex, K_FOREVER); + uint32_t _fifo_read_task_counter = fifo_read_task_counter; + uint32_t _read_shot_data_counter = read_shot_data_counter; + uint32_t _read_into_dummy_counter = read_into_dummy_counter; + uint32_t _fifo_read_FTH_counter = fifo_read_FTH_counter; + uint32_t _read_shot_data_after_fifo_read_counter = read_shot_data_after_fifo_read_counter; + uint32_t _fifo_waterM_total_hit_counter = fifo_waterM_total_hit_counter; + uint32_t _fifo_waterM_hit_counter = fifo_waterM_hit_counter; + uint32_t _fifo_overrun_counter = fifo_overrun_counter; + uint32_t _unread_words_before_read = unread_words_before_read; + uint32_t _unread_words_after_read = unread_words_after_read; + uint32_t _max_unread_words_before_read = max_unread_words_before_read; + uint32_t _max_unread_words_after_read = max_unread_words_after_read; + k_mutex_unlock(&stat_mutex); + #endif + + k_mutex_lock(&shot_data_mutex, K_FOREVER); + bool _marked = shot_data.marked; + bool _ready = shot_data.ready; + bool _saved = shot_data.saved; + k_mutex_unlock(&shot_data_mutex); + + #ifdef CONFIG_DBG_STATS + PRINT(INFO, "fth interrupt is hit %u times !\r\n", interrupt_ctr); + PRINT(INFO, "shot capture task has run %u times !\r\n", _fifo_read_task_counter); + PRINT(INFO, "shot data is read into ring buffer %u times !\r\n", _read_shot_data_counter); + PRINT(INFO, "fifo watermark full is totally hit %u times !\r\n", _fifo_waterM_total_hit_counter); + PRINT(INFO, "fifo watermark full is truely hit %u times !\r\n", _fifo_waterM_hit_counter); + PRINT(INFO, "fifo overrun is hit %u times !\r\n", _fifo_overrun_counter); + PRINT(INFO, "fifo data is read into dummy buffer for %u times !\r\n", _read_into_dummy_counter); + PRINT(INFO, "fifo FTH data is read into ring buffer %u times!\r\n", _fifo_read_FTH_counter); + PRINT(INFO, "shot data is read after fifo FTH read %u times !\r\n", _read_shot_data_after_fifo_read_counter); + PRINT(INFO, "last unread words before i2c read is %u !\r\n", _unread_words_before_read); + PRINT(INFO, "last unread words after i2c read is %u !\r\n", _unread_words_after_read); + PRINT(INFO, "maximum unread words before i2c read %u times !\r\n", _max_unread_words_before_read); + PRINT(INFO, "maximum unread words after i2c read %u times !\r\n\r\n", _max_unread_words_after_read); + PRINT(INFO, "_imu_task running during I2C transfer: %u times!\r\n", imu_task_during_i2c_counter); + PRINT(INFO, "_imu_task running: %u times!\r\n\r\n", imu_task_counter); + PRINT(INFO, "_fsm_task running during I2C transfer: %u times!\r\n", fsm_task_during_i2c_counter); + PRINT(INFO, "_fsm_task running: %u times!\r\n\r\n", fsm_task_counter); + PRINT(INFO, "_event_task running during I2C transfer: %u times!\r\n", event_task_during_i2c_counter); + PRINT(INFO, "_event_task running: %u times!\r\n\r\n", event_task_counter); + #endif + + if (_marked){ + PRINT(INFO, "shot data is marked!\r\n"); + } else { + PRINT(INFO, "shot data is not marked!\r\n"); + } + if (_ready) { + PRINT(INFO, "shot data is ready!\r\n"); + } else { + PRINT(INFO, "shot data is not ready!\r\n"); + } + if (_saved) { + PRINT(INFO, "shot data is saved!\r\n"); + } else { + PRINT(INFO, "shot data is not saved!\r\n"); + } + + // PRINT (INFO, "_imu_task running: %u times !\r\n", imu_task_counter); + // PRINT (INFO, "_imu_task running during busy wait: %u times!\r\n\r\n", imu_task_during_busy_wait_counter); + // PRINT (INFO, "_interface_task running: %u times!\r\n", _interface_task_counter); + // PRINT (INFO, "_interface_task running during I2C transfer: %u times!\r\n\r\n", _interface_task_during_i2c_counter); + } + + switch (_imu_int_source) + { + case IMU_INT_SOURCE_WAKEUP: + if (_imu_wakeup_cb != NULL) + { + _imu_wakeup_cb (); + } + break; + + default: + } + + /* Clear interrupt source. */ + _imu_int_source = IMU_INT_SOURCE_NONE; + + if (_imu_data_dump) + { + static uint32_t timer_cnt = IMU_TASK_PERIOD_MS; + int16_t xlx, xly, xlz; + int16_t gyx, gyy, gyz; + + (void) IMU_xl_raw_x_read (&xlx); + (void) IMU_xl_raw_y_read (&xly); + (void) IMU_xl_raw_z_read (&xlz); + (void) IMU_gyro_raw_x_read (&gyx); + (void) IMU_gyro_raw_y_read (&gyy); + (void) IMU_gyro_raw_z_read (&gyz); + + if (_imu_data_dump_hex) + { + PRINT(INFO, "%X,%X,%X,%X,%X,%X\r\n", xlx, xly, xlz, gyx, gyy, gyz); + } + else + { + PRINT(INFO, "%d,%d,%d,%d,%d,%d\r\n", xlx, xly, xlz, gyx, gyy, gyz); + } + + if (timer_cnt >= _imu_data_dump_time_ms) + { + /* Reset timer counter. */ + timer_cnt = 0; + + /* Clear dump flag. */ + _imu_data_dump = false; + + /* Clear hex flag. */ + _imu_data_dump_hex = false; + } + else + { + timer_cnt += IMU_TASK_PERIOD_MS; + } + } + + k_msleep(IMU_TASK_PERIOD_MS); + } +} + +static void _imu_interrupt_handler_task(void *p1, void *p2, void *p3) +{ + uint8_t reg; + + while (1) { + k_sem_take(&_imu_int_sem, K_FOREVER); + + if (atomic_test_bit(&_imu_cfg.interrupt_mode_flags, IMU_int_wake_up_mode)) { + if (I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_WAKE_UP_SRC, ®, 1)) { + PRINT (ERROR, "i2c read fifo status regs failed in isr!\r\n"); + } + if (reg & 0x08) { + /* FSM wakes up from idle disable IMU motion wake up function until fsm goes to idle again */ + MOTION_detected_flag_set(); + IMU_motion_int_enable(false, 0x2, 0x1); + } + } + + if (atomic_test_bit(&_imu_cfg.interrupt_mode_flags, IMU_int_shot_capture_mode)) { + k_sem_give(&shot_capture_sem); + } + } +} + +static ERRNO_e _imu_who_am_i(void) +{ + ERRNO_e err; + uint8_t result; + + err = I2C_bytes_read(I2C_CH_1, LSM6DS3_I2C_DEV_ADDR, LSM6DS3TR_C_WHO_AM_I_REG, &result, 1); + if (err != ERRNO_SUCCESS) { + return ERRNO_ID_FAILURE; + } + + /* Setup the internal temperature sensor. */ + if (_imu_cfg.temp_enabled == 1) { + /* 0x69 = LSM6DS3TR */ + if (result == LSM6DS3TR_WHO_AM_I) { + /* Sensitivity to scale 16 */ + _imu_cfg.temp_sensitivity = 16; + } + /* 0x6A = LSM6DS3TR-C */ + else if (result == LSM6DS3TR_C_WHO_AM_I) { + /* Sensitivity to scale 256 */ + _imu_cfg.temp_sensitivity = 256; + } + } + + return err; +} + +static ERRNO_e _imu_cfg_init(void) +{ + ERRNO_e err; + /* Gyroscope */ + err = IMU_set_fs_g(_imu_field_v_default.fs_g_v); + if (err == ERRNO_SUCCESS) { + err = IMU_set_odr_g(_imu_field_v_default.odr_g_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_hp_en_g(_imu_field_v_default.hp_en_g_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_hpm_g(_imu_field_v_default.hpm_g_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_lpf1_sel_g(_imu_field_v_default.lpf1_sel_g_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_ftype(_imu_field_v_default.ftype_v); + } + + /* Accelerometer */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_fs_xl(_imu_field_v_default.fs_xl_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_odr_xl(_imu_field_v_default.odr_xl_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_lpf1_bw_sel(_imu_field_v_default.lpf1_bw_sel_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_lpf2_xl_en(_imu_field_v_default.lpf2_xl_en_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_hp_slope_xl_en(_imu_field_v_default.hp_slope_xl_en_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_hpcf_xl(_imu_field_v_default.hpcf_xl_v); + } + if (err == ERRNO_SUCCESS) { + err = IMU_set_input_composite(_imu_field_v_default.input_composite_v); + } + + /* wakeup function */ + /* set the wake up threshold to be 2 so noise does not + * trigger the interrupt + */ + if (err == ERRNO_SUCCESS) { + err = IMU_set_wk_ths(_imu_field_v_default.wk_ths_v); + } + + + return err; +} + +static float _imu_xl_raw_to_float(int16_t raw) +{ + uint8_t factor; + switch (_imu_cfg.field_v.fs_xl_v) { + case LSM6DS3TR_C_FS_XL_2g: factor = 1; break; + case LSM6DS3TR_C_FS_XL_4g: factor = 2; break; + case LSM6DS3TR_C_FS_XL_8g: factor = 4; break; + case LSM6DS3TR_C_FS_XL_16g: factor = 8; break; + default: factor = 1; + } + + return (float) raw * (float)0.061 * factor / 1000; +} + +static float _imu_gyro_raw_to_float (int16_t raw) +{ + uint8_t factor; + switch (_imu_cfg.field_v.fs_g_v) { + case LSM6DS3TR_C_FS_G_ABSTR_125dps: factor = 1; break; + case LSM6DS3TR_C_FS_G_ABSTR_250dps: factor = 2; break; + case LSM6DS3TR_C_FS_G_ABSTR_500dps: factor = 4; break; + case LSM6DS3TR_C_FS_G_ABSTR_1000dps: factor = 8; break; + case LSM6DS3TR_C_FS_G_ABSTR_2000dps: factor = 16; break; + default: factor = 1; + } + + return (float) raw * (float)4.375 * (factor) / 1000; +} + +static void _imu_isr(void) +{ + atomic_inc(&_interrupt_count); + + /* Signal IMU I2C fifo read thread to read IMU data via DMA */ + k_sem_give(&_imu_int_sem); +} diff --git a/src/bsp/imu.h b/src/bsp/imu.h new file mode 100644 index 0000000..34dec90 --- /dev/null +++ b/src/bsp/imu.h @@ -0,0 +1,202 @@ +/** + * @file imu.h + * @brief LSM6DS3TR-C IMU module + * @author Kenny Tran + * @date Feb 28 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef IMU_H_ +#define IMU_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include + +#include "../bsp/imu_reg.h" + +#include "../lib/errno.h" + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define IMU_TASK_PERIOD_MS (100) +#define IMU_TASK_STACK_SIZE (1024) +#define IMU_INTERRUPT_HANDLER_TASK_STACK_SIZE (1024) +#define IMU_TASK_THREAD_PRIORITY (9) +#define IMU_INTERRUPT_HANDLER_THREAD_PRIORITY (3) + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum IMU_int_source +{ + IMU_INT_SOURCE_NONE = 0, + IMU_INT_SOURCE_WAKEUP, + + IMU_INT_SOURCE_COUNT +} IMU_int_source_e; + +typedef enum IMU_sensor +{ + IMU_SENSOR_ACCEL = 0, + IMU_SENSOR_GYRO, + IMU_SENSOR_COUNT +} IMU_sensor_e; + +typedef struct IMU_data +{ + float x; + float y; + float z; +} IMU_data_s; + +typedef void (*IMU_cb_t) (void); + +typedef enum IMU_int_mode_flags +{ + IMU_int_shot_capture_mode = 0, // bit 0, set when fifo threshold and single tap detection is rounted to INT1 pin + IMU_int_wake_up_mode, // bit 1, set when wake up event is rounted to INT1 pin +} IMU_int_mode_flags_e; + + +/*-------------------------------------------------------------------------------------------------- + * GLOBAL VARIABLE + *------------------------------------------------------------------------------------------------*/ +extern struct k_sem shot_capture_sem; +extern volatile bool busy_wait_active; +extern volatile uint32_t imu_task_counter; +extern volatile uint32_t _interface_task_counter; +extern volatile uint32_t _interface_task_during_i2c_counter; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e IMU_init (void); +ERRNO_e IMU_int_cb_register (IMU_int_source_e src, IMU_cb_t cb); + +/* Gyro Fields Accessors*/ +ERRNO_e IMU_gyro_enable(bool en); +ERRNO_e IMU_is_gyro_enabled(bool *en); +ERRNO_e IMU_set_fs_g(LSM6DS3TR_C_FS_G_ABSTR_e value); +ERRNO_e IMU_get_fs_g(LSM6DS3TR_C_FS_G_ABSTR_e* value); +ERRNO_e IMU_set_odr_g(LSM6DS3TR_C_ODR_G_e value); +ERRNO_e IMU_get_odr_g(LSM6DS3TR_C_ODR_G_e *value); +ERRNO_e IMU_set_hp_en_g(LSM6DS3TR_C_HP_EN_G_e value); +ERRNO_e IMU_get_hp_en_g(LSM6DS3TR_C_HP_EN_G_e *value); +ERRNO_e IMU_set_hpm_g(LSM6DS3TR_C_HPM_G_e value); +ERRNO_e IMU_get_hpm_g(LSM6DS3TR_C_HPM_G_e *value); +ERRNO_e IMU_set_lpf1_sel_g(LSM6DS3TR_C_LPF1_SEL_G_e value); +ERRNO_e IMU_get_lpf1_sel_g(LSM6DS3TR_C_LPF1_SEL_G_e* value); +ERRNO_e IMU_set_ftype(LSM6DS3TR_C_FTYPE_e value); +ERRNO_e IMU_get_ftype(LSM6DS3TR_C_FTYPE_e *value); + +/* Accelerometer Fields Accessors */ +ERRNO_e IMU_xl_enable (bool en); +ERRNO_e IMU_is_xl_enabled (bool *en); +ERRNO_e IMU_set_fs_xl(LSM6DS3TR_C_FS_XL_e value); +ERRNO_e IMU_get_fs_xl(LSM6DS3TR_C_FS_XL_e *value); +ERRNO_e IMU_set_odr_xl(LSM6DS3TR_C_ODR_XL_e value); +ERRNO_e IMU_get_odr_xl(LSM6DS3TR_C_ODR_XL_e *value); +ERRNO_e IMU_set_lpf1_bw_sel(LSM6DS3TR_C_LPF1_BW_SEL_e value); +ERRNO_e IMU_get_lpf1_bw_sel(LSM6DS3TR_C_LPF1_BW_SEL_e *value); +ERRNO_e IMU_set_lpf2_xl_en(LSM6DS3TR_C_LPF2_XL_EN_e value); +ERRNO_e IMU_get_lpf2_xl_en(LSM6DS3TR_C_LPF2_XL_EN_e *value); +ERRNO_e IMU_set_hp_slope_xl_en(LSM6DS3TR_C_HP_SLOPE_XL_EN_e value); +ERRNO_e IMU_get_hp_slope_xl_en(LSM6DS3TR_C_HP_SLOPE_XL_EN_e *value); +ERRNO_e IMU_set_hpcf_xl(LSM6DS3TR_C_HPCF_XL_e value); +ERRNO_e IMU_get_hpcf_xl(LSM6DS3TR_C_HPCF_XL_e *value); +ERRNO_e IMU_set_input_composite(LSM6DS3TR_C_INPUT_COMPOSITE_e value); +ERRNO_e IMU_get_input_composite(LSM6DS3TR_C_INPUT_COMPOSITE_e *value); + +/* FIFO Fields Accessors*/ +ERRNO_e IMU_set_FTH(uint16_t value); +ERRNO_e IMU_get_FTH(uint16_t *value); +ERRNO_e IMU_set_dec_fifo_gyro(LSM6DS3TR_C_DEC_FIFO_GYRO_e value); +ERRNO_e IMU_get_dec_fifo_gyro(LSM6DS3TR_C_DEC_FIFO_GYRO_e *value); +ERRNO_e IMU_set_dec_fifo_xl(LSM6DS3TR_C_DEC_FIFO_XL_e value); +ERRNO_e IMU_get_dec_fifo_xl(LSM6DS3TR_C_DEC_FIFO_XL_e *value); +ERRNO_e IMU_set_odr_fifo(LSM6DS3TR_C_ODR_FIFO_e value); +ERRNO_e IMU_get_odr_fifo(LSM6DS3TR_C_ODR_FIFO_e *value); +ERRNO_e IMU_set_fifo_mode(LSM6DS3TR_C_FIFO_MODE_e value); +ERRNO_e IMU_get_fifo_mode(LSM6DS3TR_C_FIFO_MODE_e *value); + +/* Other Fields Accessors */ +ERRNO_e IMU_set_int1_fth(LSM6DS3TR_C_INT1_FTH_e value); +ERRNO_e IMU_get_int1_fth(LSM6DS3TR_C_INT1_FTH_e *value); +ERRNO_e IMU_set_bdu(LSM6DS3TR_C_BDU_e value); +ERRNO_e IMU_get_bdu(LSM6DS3TR_C_BDU_e *value); +ERRNO_e IMU_set_interrupts_enable(LSM6DS3TR_C_INTERRUPTS_ENABLE_e value); +ERRNO_e IMU_get_interrupts_enable(LSM6DS3TR_C_INTERRUPTS_ENABLE_e *value); +ERRNO_e IMU_set_tap_x_en(LSM6DS3TR_C_TAP_X_EN_e value); +ERRNO_e IMU_get_tap_x_en(LSM6DS3TR_C_TAP_X_EN_e *value); +ERRNO_e IMU_set_tap_y_en(LSM6DS3TR_C_TAP_Y_EN_e value); +ERRNO_e IMU_get_tap_y_en(LSM6DS3TR_C_TAP_Y_EN_e *value); +ERRNO_e IMU_set_tap_z_en(LSM6DS3TR_C_TAP_Z_EN_e value); +ERRNO_e IMU_get_tap_z_en(LSM6DS3TR_C_TAP_Z_EN_e *value); +ERRNO_e IMU_set_lir(LSM6DS3TR_C_LIR_e value); +ERRNO_e IMU_get_lir(LSM6DS3TR_C_LIR_e *value); +ERRNO_e IMU_set_tap_ths(uint8_t value); +ERRNO_e IMU_get_tap_ths(uint8_t *value); +ERRNO_e IMU_set_quiet(LSM6DS3TR_C_QUIET_e value); +ERRNO_e IMU_get_quiet(LSM6DS3TR_C_QUIET_e *value); +ERRNO_e IMU_set_shock(LSM6DS3TR_C_SHOCK_e value); +ERRNO_e IMU_get_shock(LSM6DS3TR_C_SHOCK_e *value); +ERRNO_e IMU_set_single_double_tap(LSM6DS3TR_C_SINGLE_DOUBLE_TAP_e value); +ERRNO_e IMU_get_single_double_tap(LSM6DS3TR_C_SINGLE_DOUBLE_TAP_e *value); +ERRNO_e IMU_set_wk_ths(uint8_t value); +ERRNO_e IMU_get_wk_ths(uint8_t *value); +ERRNO_e IMU_set_wake_dur(LSM6DS3TR_C_WAKE_DUR_e value); +ERRNO_e IMU_get_wake_dur(LSM6DS3TR_C_WAKE_DUR_e *value); +ERRNO_e IMU_set_int1_single_tap(LSM6DS3TR_C_INT1_SINGLE_TAP_e value); +ERRNO_e IMU_get_int1_single_tap(LSM6DS3TR_C_INT1_SINGLE_TAP_e *value); +ERRNO_e IMU_set_int1_double_tap(LSM6DS3TR_C_INT1_DOUBLE_TAP_e value); +ERRNO_e IMU_get_int1_double_tap(LSM6DS3TR_C_INT1_DOUBLE_TAP_e *value); + +ERRNO_e IMU_temp_raw_read (int16_t *p_dest); +ERRNO_e IMU_temp_c_read (float *p_dest); +ERRNO_e IMU_temp_f_read (float *p_dest); +ERRNO_e IMU_reset (void); +ERRNO_e IMU_xl_raw_x_read (int16_t *p_dest); +ERRNO_e IMU_xl_raw_y_read (int16_t *p_dest); +ERRNO_e IMU_xl_raw_z_read (int16_t *p_dest); +ERRNO_e IMU_xl_x_read (float *p_dest); +ERRNO_e IMU_xl_y_read (float *p_dest); +ERRNO_e IMU_xl_z_read (float *p_dest); +ERRNO_e IMU_xl_read (IMU_data_s *p_dest); +ERRNO_e IMU_gyro_raw_x_read (int16_t *p_dest); +ERRNO_e IMU_gyro_raw_y_read (int16_t *p_dest); +ERRNO_e IMU_gyro_raw_z_read (int16_t *p_dest); +ERRNO_e IMU_gyro_x_read (float *p_dest); +ERRNO_e IMU_gyro_y_read (float *p_dest); +ERRNO_e IMU_gyro_z_read (float *p_dest); +ERRNO_e IMU_gyro_read (IMU_data_s *p_dest); + + +void IMU_set_shot_capture_mode(bool enable); +ERRNO_e IMU_fifo_status_1to2_read (uint16_t *unread_words, uint8_t *empty, uint8_t *full_smart, uint8_t *over_run, uint8_t *waterM); +ERRNO_e IMU_func_src1_read (uint8_t *sign_motion); +ERRNO_e IMU_fifo_th_set(uint16_t fifo_threshold); +bool IMU_is_fifo_set(void); + +ERRNO_e IMU_motion_int_enable (bool en, uint8_t threshold, uint8_t duration); +ERRNO_e IMU_sleep (void); + +void IMU_task_install(void); +void IMU_interrupt_handler_task_install(void); +void Dummy_task_install (void); + +void IMU_cli_cmd (int32_t argc, char **argv); +ERRNO_e IMU_fifo_th_set(uint16_t fifo_threshold); + +#endif /* IMU_H_ */ \ No newline at end of file diff --git a/src/bsp/imu_reg.h b/src/bsp/imu_reg.h new file mode 100644 index 0000000..cd09c7f --- /dev/null +++ b/src/bsp/imu_reg.h @@ -0,0 +1,2211 @@ +/** + * @file imu_reg.h + * @brief LSM6DS3 IMU register addresses and definitions (lsm6ds3tr-c) + * @author Kenny Tran + * @date Feb 28 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef IMU_REG_H_ +#define IMU_REG_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define LSM6DS3_I2C_DEV_ADDR (0X6A) /* AD0 / SA0 determines LSB (grounded): + * if grounded, use 0x6A, if high, use 0x6B */ + +#define LSM6DS3TR_WHO_AM_I 0X69 +#define LSM6DS3TR_C_WHO_AM_I 0x6A + +/************** Device Register *******************/ +#define LSM6DS3TR_C_CFG_ACCESS 0X01 +#define LSM6DS3TR_C_SENSOR_SYNC_TIME 0X04 +#define LSM6DS3TR_C_SENSOR_SYNC_EN 0X05 +#define LSM6DS3TR_C_FIFO_CTRL1 0X06 +#define LSM6DS3TR_C_FIFO_CTRL2 0X07 +#define LSM6DS3TR_C_FIFO_CTRL3 0X08 +#define LSM6DS3TR_C_FIFO_CTRL4 0X09 +#define LSM6DS3TR_C_FIFO_CTRL5 0X0A +#define LSM6DS3TR_C_DRDY_PULSE_CFG_G 0X0B +#define LSM6DS3TR_C_INT1_CTRL 0X0D +#define LSM6DS3TR_C_INT2_CTRL 0X0E +#define LSM6DS3TR_C_WHO_AM_I_REG 0X0F +#define LSM6DS3TR_C_CTRL1_XL 0X10 +#define LSM6DS3TR_C_CTRL2_G 0X11 +#define LSM6DS3TR_C_CTRL3_C 0X12 +#define LSM6DS3TR_C_CTRL4_C 0X13 +#define LSM6DS3TR_C_CTRL5_C 0X14 +#define LSM6DS3TR_C_CTRL6_C 0X15 +#define LSM6DS3TR_C_CTRL7_G 0X16 +#define LSM6DS3TR_C_CTRL8_XL 0X17 +#define LSM6DS3TR_C_CTRL9_XL 0X18 +#define LSM6DS3TR_C_CTRL10_C 0X19 +#define LSM6DS3TR_C_MASTER_CONFIG 0X1A +#define LSM6DS3TR_C_WAKE_UP_SRC 0X1B +#define LSM6DS3TR_C_TAP_SRC 0X1C +#define LSM6DS3TR_C_D6D_SRC 0X1D +#define LSM6DS3TR_C_STATUS_REG 0X1E +#define LSM6DS3TR_C_OUT_TEMP_L 0X20 +#define LSM6DS3TR_C_OUT_TEMP_H 0X21 +#define LSM6DS3TR_C_OUTX_L_G 0X22 +#define LSM6DS3TR_C_OUTX_H_G 0X23 +#define LSM6DS3TR_C_OUTY_L_G 0X24 +#define LSM6DS3TR_C_OUTY_H_G 0X25 +#define LSM6DS3TR_C_OUTZ_L_G 0X26 +#define LSM6DS3TR_C_OUTZ_H_G 0X27 +#define LSM6DS3TR_C_OUTX_L_XL 0X28 +#define LSM6DS3TR_C_OUTX_H_XL 0X29 +#define LSM6DS3TR_C_OUTY_L_XL 0X2A +#define LSM6DS3TR_C_OUTY_H_XL 0X2B +#define LSM6DS3TR_C_OUTZ_L_XL 0X2C +#define LSM6DS3TR_C_OUTZ_H_XL 0X2D +#define LSM6DS3TR_C_SENSORHUB1_REG 0X2E +#define LSM6DS3TR_C_SENSORHUB2_REG 0X2F +#define LSM6DS3TR_C_SENSORHUB3_REG 0X30 +#define LSM6DS3TR_C_SENSORHUB4_REG 0X31 +#define LSM6DS3TR_C_SENSORHUB5_REG 0X32 +#define LSM6DS3TR_C_SENSORHUB6_REG 0X33 +#define LSM6DS3TR_C_SENSORHUB7_REG 0X34 +#define LSM6DS3TR_C_SENSORHUB8_REG 0X35 +#define LSM6DS3TR_C_SENSORHUB9_REG 0X36 +#define LSM6DS3TR_C_SENSORHUB10_REG 0X37 +#define LSM6DS3TR_C_SENSORHUB11_REG 0X38 +#define LSM6DS3TR_C_SENSORHUB12_REG 0X39 +#define LSM6DS3TR_C_FIFO_STATUS1 0X3A +#define LSM6DS3TR_C_FIFO_STATUS2 0X3B +#define LSM6DS3TR_C_FIFO_STATUS3 0X3C +#define LSM6DS3TR_C_FIFO_STATUS4 0X3D +#define LSM6DS3TR_C_FIFO_DATA_OUT_L 0X3E +#define LSM6DS3TR_C_FIFO_DATA_OUT_H 0X3F +#define LSM6DS3TR_C_TIMESTAMP0_REG 0X40 +#define LSM6DS3TR_C_TIMESTAMP1_REG 0X41 +#define LSM6DS3TR_C_TIMESTAMP2_REG 0X42 +#define LSM6DS3TR_C_STEP_TIMESTAMP_L 0X49 +#define LSM6DS3TR_C_STEP_TIMESTAMP_H 0X4A +#define LSM6DS3TR_C_STEP_COUNTER_L 0X4B +#define LSM6DS3TR_C_STEP_COUNTER_H 0X4C +#define LSM6DS3TR_C_SENSORHUB13_REG 0X4D +#define LSM6DS3TR_C_SENSORHUB14_REG 0X4E +#define LSM6DS3TR_C_SENSORHUB15_REG 0X4F +#define LSM6DS3TR_C_SENSORHUB16_REG 0X50 +#define LSM6DS3TR_C_SENSORHUB17_REG 0X51 +#define LSM6DS3TR_C_SENSORHUB18_REG 0X52 +#define LSM6DS3TR_C_FUNC_SRC1 0X53 +#define LSM6DS3TR_C_FUNC_SRC2 0X54 +#define LSM6DS3TR_C_WRIST_TILT_IA 0X55 +#define LSM6DS3TR_C_TAP_CFG 0X58 +#define LSM6DS3TR_C_TAP_THS_6D 0X59 +#define LSM6DS3TR_C_INT_DUR2 0X5A +#define LSM6DS3TR_C_WAKE_UP_THS 0X5B +#define LSM6DS3TR_C_WAKE_UP_DUR 0X5C +#define LSM6DS3TR_C_FREE_FALL 0X5D +#define LSM6DS3TR_C_MD1_CFG 0X5E +#define LSM6DS3TR_C_MD2_CFG 0X5F +#define LSM6DS3TR_C_MASTER_CMD_CODE 0X60 +#define LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE 0X61 +#define LSM6DS3TR_C_OUT_MAG_RAW_X_L 0X66 +#define LSM6DS3TR_C_OUT_MAG_RAW_X_H 0X67 +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_L 0X68 +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_H 0X69 +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_L 0X6A +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_H 0X6B +#define LSM6DS3TR_C_X_OFS_USR 0X73 +#define LSM6DS3TR_C_Y_OFS_USR 0X74 +#define LSM6DS3TR_C_Z_OFS_USR 0X75 + +/************** Access Device RAM *******************/ +#define LSM6DS3TR_C_ADDR0_TO_RW_RAM 0x62 +#define LSM6DS3TR_C_ADDR1_TO_RW_RAM 0x63 +#define LSM6DS3TR_C_DATA_TO_WR_RAM 0x64 +#define LSM6DS3TR_C_DATA_RD_FROM_RAM 0x65 + +#define LSM6DS3TR_C_RAM_SIZE 4096 + +/************** Embedded functions register mapping *******************/ +#define LSM6DS3TR_C_SLV0_ADD 0x02 +#define LSM6DS3TR_C_SLV0_SUBADD 0x03 +#define LSM6DS3TR_C_SLAVE0_CONFIG 0x04 +#define LSM6DS3TR_C_SLV1_ADD 0x05 +#define LSM6DS3TR_C_SLV1_SUBADD 0x06 +#define LSM6DS3TR_C_SLAVE1_CONFIG 0x07 +#define LSM6DS3TR_C_SLV2_ADD 0x08 +#define LSM6DS3TR_C_SLV2_SUBADD 0x09 +#define LSM6DS3TR_C_SLAVE2_CONFIG 0x0A +#define LSM6DS3TR_C_SLV3_ADD 0x0B +#define LSM6DS3TR_C_SLV3_SUBADD 0x0C +#define LSM6DS3TR_C_SLAVE3_CONFIG 0x0D +#define LSM6DS3TR_C_DATAWRITE_SRC_MODE_SUB_SLV0 0x0E +#define LSM6DS3TR_C_CONFIG_PEDO_THS_MIN 0x0F +#define LSM6DS3TR_C_CONFIG_TILT_IIR 0x10 +#define LSM6DS3TR_C_CONFIG_TILT_ACOS 0x11 +#define LSM6DS3TR_C_CONFIG_TILT_WTIME 0x12 +#define LSM6DS3TR_C_SM_STEP_THS 0x13 +#define LSM6DS3TR_C_MAG_SI_XX 0x24 +#define LSM6DS3TR_C_MAG_SI_XY 0x25 +#define LSM6DS3TR_C_MAG_SI_XZ 0x26 +#define LSM6DS3TR_C_MAG_SI_YX 0x27 +#define LSM6DS3TR_C_MAG_SI_YY 0x28 +#define LSM6DS3TR_C_MAG_SI_YZ 0x29 +#define LSM6DS3TR_C_MAG_SI_ZX 0x2A +#define LSM6DS3TR_C_MAG_SI_ZY 0x2B +#define LSM6DS3TR_C_MAG_SI_ZZ 0x2C +#define LSM6DS3TR_C_MAG_OFFX_L 0x2D +#define LSM6DS3TR_C_MAG_OFFX_H 0x2E +#define LSM6DS3TR_C_MAG_OFFY_L 0x2F +#define LSM6DS3TR_C_MAG_OFFY_H 0x30 +#define LSM6DS3TR_C_MAG_OFFZ_L 0x31 +#define LSM6DS3TR_C_MAG_OFFZ_H 0x32 + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +/******************************************************************************* + Register : RAM_ACCESS + Address : 0X01 + Bit Group Name: PROG_RAM1 + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_PROG_RAM1_DISABLED = 0x00, + LSM6DS3TR_C_PROG_RAM1_ENABLED = 0x01, +} LSM6DS3TR_C_PROG_RAM1_e; + +/******************************************************************************* + Register : RAM_ACCESS + Address : 0X01 + Bit Group Name: CUSTOMROM1 + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_CUSTOMROM1_DISABLED = 0x00, + LSM6DS3TR_C_CUSTOMROM1_ENABLED = 0x04, +} LSM6DS3TR_C_CUSTOMROM1_e; + +/******************************************************************************* + Register : RAM_ACCESS + Address : 0X01 + Bit Group Name: RAM_PAGE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_RAM_PAGE_DISABLED = 0x00, + LSM6DS3TR_C_RAM_PAGE_ENABLED = 0x80, +} LSM6DS3TR_C_RAM_PAGE_e; + +/******************************************************************************* + Register : SENSOR_SYNC_TIME + Address : 0X04 + Bit Group Name: TPH + Permission : RW +*******************************************************************************/ +#define LSM6DS3TR_C_TPH_MASK 0xFF +#define LSM6DS3TR_C_TPH_POSITION 0 + +/******************************************************************************* + Register : SENSOR_SYNC_EN + Address : 0X05 + Bit Group Name: SYNC_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SYNC_EN_DISABLED = 0x00, + LSM6DS3TR_C_SYNC_EN_ENABLED = 0x01, +} LSM6DS3TR_C_SYNC_EN_e; + +/******************************************************************************* + Register : SENSOR_SYNC_EN + Address : 0X05 + Bit Group Name: HP_RST + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_HP_RST_RST_OFF = 0x00, + LSM6DS3TR_C_HP_RST_RST_ON = 0x02, +} LSM6DS3TR_C_HP_RST_e; + +/******************************************************************************* + Register : FIFO_CTRL2 + Address : 0X07 + Bit Group Name: TIM_PEDO_FIFO_DRDY + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TIM_PEDO_FIFO_DRDY_DISABLED = 0x00, + LSM6DS3TR_C_TIM_PEDO_FIFO_DRDY_ENABLED = 0x40, +} LSM6DS3TR_C_TIM_PEDO_FIFO_DRDY_e; + +/******************************************************************************* + Register : FIFO_CTRL2 + Address : 0X07 + Bit Group Name: TIM_PEDO_FIFO_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TIM_PEDO_FIFO_EN_DISABLED = 0x00, + LSM6DS3TR_C_TIM_PEDO_FIFO_EN_ENABLED = 0x80, +} LSM6DS3TR_C_TIM_PEDO_FIFO_EN_e; + +/******************************************************************************* + Register : FIFO_CTRL3 [2:0] + Address : 0X08 + Bit Group Name: DEC_FIFO_XL[2:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DEC_FIFO_XL_DATA_NOT_IN_FIFO = 0x00, + LSM6DS3TR_C_DEC_FIFO_XL_NO_DECIMATION = 0x01, + LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_2 = 0x02, + LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_3 = 0x03, + LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_4 = 0x04, + LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_8 = 0x05, + LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_16 = 0x06, + LSM6DS3TR_C_DEC_FIFO_XL_DECIMATION_BY_32 = 0x07, +} LSM6DS3TR_C_DEC_FIFO_XL_e; + +/******************************************************************************* + Register : FIFO_CTRL3 [5:3] + Address : 0X08 + Bit Group Name: DEC_FIFO_GYRO[2:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DEC_FIFO_GYRO_DATA_NOT_IN_FIFO = 0x00, + LSM6DS3TR_C_DEC_FIFO_GYRO_NO_DECIMATION = 0x01, + LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_2 = 0x02, + LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_3 = 0x03, + LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_4 = 0x04, + LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_8 = 0x05, + LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_16 = 0x06, + LSM6DS3TR_C_DEC_FIFO_GYRO_DECIMATION_BY_32 = 0x07, +} LSM6DS3TR_C_DEC_FIFO_GYRO_e; + +/******************************************************************************* + Register : FIFO_CTRL4 [2:0] + Address : 0X09 + Bit Group Name: DEC_DS3_FIFO[2:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DEC_DS3_FIFO_DATA_NOT_IN_FIFO = 0x00, + LSM6DS3TR_C_DEC_DS3_FIFO_NO_DECIMATION = 0x01, + LSM6DS3TR_C_DEC_DS3_FIFO_DECIMATION_BY_2 = 0x02, + LSM6DS3TR_C_DEC_DS3_FIFO_DECIMATION_BY_3 = 0x03, + LSM6DS3TR_C_DEC_DS3_FIFO_DECIMATION_BY_4 = 0x04, + LSM6DS3TR_C_DEC_DS3_FIFO_DECIMATION_BY_8 = 0x05, + LSM6DS3TR_C_DEC_DS3_FIFO_DECIMATION_BY_16 = 0x06, + LSM6DS3TR_C_DEC_DS3_FIFO_DECIMATION_BY_32 = 0x07, +} LSM6DS3TR_C_DEC_DS3_FIFO_e; + +/******************************************************************************* + Register : FIFO_CTRL4 [5:3] + Address : 0X09 + Bit Group Name: DEC_DS4_FIFO[2:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DEC_DS4_FIFO_DATA_NOT_IN_FIFO = 0x00, + LSM6DS3TR_C_DEC_DS4_FIFO_NO_DECIMATION = 0x08, + LSM6DS3TR_C_DEC_DS4_FIFO_DECIMATION_BY_2 = 0x10, + LSM6DS3TR_C_DEC_DS4_FIFO_DECIMATION_BY_3 = 0x18, + LSM6DS3TR_C_DEC_DS4_FIFO_DECIMATION_BY_4 = 0x20, + LSM6DS3TR_C_DEC_DS4_FIFO_DECIMATION_BY_8 = 0x28, + LSM6DS3TR_C_DEC_DS4_FIFO_DECIMATION_BY_16 = 0x30, + LSM6DS3TR_C_DEC_DS4_FIFO_DECIMATION_BY_32 = 0x38, +} LSM6DS3TR_C_DEC_DS4_FIFO_e; + +/******************************************************************************* + Register : FIFO_CTRL4 [6] + Address : 0X09 + Bit Group Name: ONLY_HIGH_DATA + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ONLY_HIGH_DATA_DISABLED = 0x00, + LSM6DS3TR_C_ONLY_HIGH_DATA_ENABLED = 0x40, +} LSM6DS3TR_C_ONLY_HIGH_DATA_e; + +/******************************************************************************* + Register : FIFO_CTRL4 [7] + Address : 0X09 + Bit Group Name: STOP_ON_FTH + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_STOP_ON_FTH_DISABLED = 0x00, + LSM6DS3TR_C_STOP_ON_FTH_ENABLED = 0x80, +} LSM6DS3TR_C_STOP_ON_FTH_DATA_e; + +/******************************************************************************* + Register : FIFO_CTRL5 [2:0] + Address : 0X0A + Bit Group Name: FIFO_MODE[2:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FIFO_MODE_BYPASS = 0x00, + LSM6DS3TR_C_FIFO_MODE_FIFO_STOP_ON_FULL = 0x01, + LSM6DS3TR_C_FIFO_MODE_CON_THEN_FIFO = 0x03, + LSM6DS3TR_C_FIFO_MODE_BYPASS_THEN_CON = 0x04, + LSM6DS3TR_C_FIFO_MODE_CONTINUOUS = 0x06, +} LSM6DS3TR_C_FIFO_MODE_e; + +/******************************************************************************* + Register : FIFO_CTRL5 [6:3] + Address : 0X0A + Bit Group Name: ODR_FIFO[3:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ODR_FIFO_DISABLE = 0x00, + LSM6DS3TR_C_ODR_FIFO_12_5Hz = 0x01, + LSM6DS3TR_C_ODR_FIFO_26Hz = 0x02, + LSM6DS3TR_C_ODR_FIFO_52Hz = 0x03, + LSM6DS3TR_C_ODR_FIFO_104Hz = 0x04, + LSM6DS3TR_C_ODR_FIFO_208Hz = 0x05, + LSM6DS3TR_C_ODR_FIFO_416Hz = 0x06, + LSM6DS3TR_C_ODR_FIFO_833Hz = 0x07, + LSM6DS3TR_C_ODR_FIFO_1666Hz = 0x08, + LSM6DS3TR_C_ODR_FIFO_3332Hz = 0x09, + LSM6DS3TR_C_ODR_FIFO_6664Hz = 0x0A, +} LSM6DS3TR_C_ODR_FIFO_e; + +/******************************************************************************* + Register : ORIENT_CFG_G + Address : 0X0B + Bit Group Name: ORIENT + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ORIENT_XYZ = 0x00, + LSM6DS3TR_C_ORIENT_XZY = 0x01, + LSM6DS3TR_C_ORIENT_YXZ = 0x02, + LSM6DS3TR_C_ORIENT_YZX = 0x03, + LSM6DS3TR_C_ORIENT_ZXY = 0x04, + LSM6DS3TR_C_ORIENT_ZYX = 0x05, +} LSM6DS3TR_C_ORIENT_e; + +/******************************************************************************* + Register : ORIENT_CFG_G + Address : 0X0B + Bit Group Name: SIGN_Z_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SIGN_Z_G_POSITIVE = 0x00, + LSM6DS3TR_C_SIGN_Z_G_NEGATIVE = 0x08, +} LSM6DS3TR_C_SIGN_Z_G_e; + +/******************************************************************************* + Register : ORIENT_CFG_G + Address : 0X0B + Bit Group Name: SIGN_Y_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SIGN_Y_G_POSITIVE = 0x00, + LSM6DS3TR_C_SIGN_Y_G_NEGATIVE = 0x10, +} LSM6DS3TR_C_SIGN_Y_G_e; + +/******************************************************************************* + Register : ORIENT_CFG_G + Address : 0X0B + Bit Group Name: SIGN_X_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SIGN_X_G_POSITIVE = 0x00, + LSM6DS3TR_C_SIGN_X_G_NEGATIVE = 0x20, +} LSM6DS3TR_C_SIGN_X_G_e; + +/******************************************************************************* + Register : REFERENCE_G + Address : 0X0C + Bit Group Name: REF_G + Permission : RW +*******************************************************************************/ +#define LSM6DS3TR_C_REF_G_MASK 0xFF +#define LSM6DS3TR_C_REF_G_POSITION 0 + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_DRDY_XL + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_DRDY_XL_DISABLED = 0x00, + LSM6DS3TR_C_INT1_DRDY_XL_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_DRDY_XL_e; + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_DRDY_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_DRDY_G_DISABLED = 0x00, + LSM6DS3TR_C_INT1_DRDY_G_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_DRDY_G_e; + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_BOOT + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_BOOT_DISABLED = 0x00, + LSM6DS3TR_C_INT1_BOOT_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_BOOT_e; + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_FTH + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_FTH_DISABLED = 0x00, + LSM6DS3TR_C_INT1_FTH_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_FTH_e; + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_FIFO_OVR + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_FIFO_OVR_DISABLED = 0x00, + LSM6DS3TR_C_INT1_FIFO_OVR_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_FIFO_OVR_e; + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_FULL_FLAG + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_FULL_FLAG_DISABLED = 0x00, + LSM6DS3TR_C_INT1_FULL_FLAG_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_FULL_FLAG_e; + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_SIGN_MOT + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_SIGN_MOT_DISABLED = 0x00, + LSM6DS3TR_C_INT1_SIGN_MOT_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_SIGN_MOT_e; + +/******************************************************************************* + Register : INT1_CTRL + Address : 0X0D + Bit Group Name: INT1_PEDO + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_PEDO_DISABLED = 0x00, + LSM6DS3TR_C_INT1_PEDO_ENABLED = 0x80, +} LSM6DS3TR_C_INT1_PEDO_e; + +/******************************************************************************* + Register : INT2_CTRL + Address : 0X0E + Bit Group Name: INT2_DRDY_XL + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_DRDY_XL_DISABLED = 0x00, + LSM6DS3TR_C_INT2_DRDY_XL_ENABLED = 0x01, +} LSM6DS3TR_C_INT2_DRDY_XL_e; + +/******************************************************************************* + Register : INT2_CTRL + Address : 0X0E + Bit Group Name: INT2_DRDY_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_DRDY_G_DISABLED = 0x00, + LSM6DS3TR_C_INT2_DRDY_G_ENABLED = 0x02, +} LSM6DS3TR_C_INT2_DRDY_G_e; + +/******************************************************************************* + Register : INT2_CTRL + Address : 0X0E + Bit Group Name: INT2_FTH + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_FTH_DISABLED = 0x00, + LSM6DS3TR_C_INT2_FTH_ENABLED = 0x08, +} LSM6DS3TR_C_INT2_FTH_e; + +/******************************************************************************* + Register : INT2_CTRL + Address : 0X0E + Bit Group Name: INT2_OVR + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_OVR_DISABLED = 0x00, + LSM6DS3TR_C_INT2_OVR_ENABLED = 0x10, +} LSM6DS3TR_C_INT2_OVR_e; + +/******************************************************************************* + Register : INT2_CTRL + Address : 0X0E + Bit Group Name: INT2_FSS5 + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_FSS5_DISABLED = 0x00, + LSM6DS3TR_C_INT2_FSS5_ENABLED = 0x20, +} LSM6DS3TR_C_INT2_FSS5_e; + +/******************************************************************************* + Register : INT2_CTRL + Address : 0X0E + Bit Group Name: INT2_SIGN_MOT + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_SIGN_MOT_DISABLED = 0x00, + LSM6DS3TR_C_INT2_SIGN_MOT_ENABLED = 0x40, +} LSM6DS3TR_C_INT2_SIGN_MOT_e; + +/******************************************************************************* + Register : INT2_CTRL + Address : 0X0E + Bit Group Name: INT2_PEDO + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_PEDO_DISABLED = 0x00, + LSM6DS3TR_C_INT2_PEDO_ENABLED = 0x80, +} LSM6DS3TR_C_INT2_PEDO_e; + +/******************************************************************************* + Register : WHO_AM_I + Address : 0X0F + Bit Group Name: WHO_AM_I_BIT + Permission : RO +*******************************************************************************/ +#define LSM6DS3TR_C_WHO_AM_I_BIT_MASK 0xFF +#define LSM6DS3TR_C_WHO_AM_I_BIT_POSITION 0 + +/******************************************************************************* + Register : CTRL1_XL [1] + Address : 0X10 + Bit Group Name: LPF1_BW_SEL + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_LPF1_BW_SEL_ODR_DIV_2 = 0x00, + LSM6DS3TR_C_LPF1_BW_SEL_ODR_DIV_4 = 0x01, +} LSM6DS3TR_C_LPF1_BW_SEL_e; + +/******************************************************************************* + Register : CTRL1_XL [3:2] + Address : 0X10 + Bit Group Name: FS_XL[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FS_XL_2g = 0x00, + LSM6DS3TR_C_FS_XL_16g = 0x01, + LSM6DS3TR_C_FS_XL_4g = 0x02, + LSM6DS3TR_C_FS_XL_8g = 0x03, +} LSM6DS3TR_C_FS_XL_e; + +/******************************************************************************* + Register : CTRL1_XL [7:4] + Address : 0X10 + Bit Group Name: ODR_XL[3:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ODR_XL_POWER_DOWN = 0x00, + LSM6DS3TR_C_ODR_XL_12_5Hz = 0x01, + LSM6DS3TR_C_ODR_XL_26Hz = 0x02, + LSM6DS3TR_C_ODR_XL_52Hz = 0x03, + LSM6DS3TR_C_ODR_XL_104Hz = 0x04, + LSM6DS3TR_C_ODR_XL_208Hz = 0x05, + LSM6DS3TR_C_ODR_XL_416Hz = 0x06, + LSM6DS3TR_C_ODR_XL_833Hz = 0x07, + LSM6DS3TR_C_ODR_XL_1666Hz = 0x08, + LSM6DS3TR_C_ODR_XL_3332Hz = 0x09, + LSM6DS3TR_C_ODR_XL_6664Hz = 0x0A, + LSM6DS3TR_C_ODR_XL_1_6Hz = 0x0B, +} LSM6DS3TR_C_ODR_XL_e; + +/******************************************************************************* + Register : CTRL2_G [1] + Address : 0X11 + Bit Group Name: FS_125 + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FS_125_DISABLED = 0x00, + LSM6DS3TR_C_FS_125_ENABLED = 0x01, +} LSM6DS3TR_C_FS_125_e; + +/******************************************************************************* + Register : CTRL2_G [3:2] + Address : 0X11 + Bit Group Name: FS_G[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FS_G_250dps = 0x00, + LSM6DS3TR_C_FS_G_500dps = 0x01, + LSM6DS3TR_C_FS_G_1000dps = 0x02, + LSM6DS3TR_C_FS_G_2000dps = 0x03, +} LSM6DS3TR_C_FS_G_e; + +/******************************************************************************* + Register : CTRL2_G + Address : 0X11 + Bit Group Name: FS_G_ABSTR + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FS_G_ABSTR_125dps, + LSM6DS3TR_C_FS_G_ABSTR_250dps, + LSM6DS3TR_C_FS_G_ABSTR_500dps, + LSM6DS3TR_C_FS_G_ABSTR_1000dps, + LSM6DS3TR_C_FS_G_ABSTR_2000dps, +} LSM6DS3TR_C_FS_G_ABSTR_e; + +/******************************************************************************* + Register : CTRL2_G [7:4] + Address : 0X11 + Bit Group Name: ODR_G[3:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ODR_G_POWER_DOWN = 0x00, + LSM6DS3TR_C_ODR_G_12_5Hz = 0x01, + LSM6DS3TR_C_ODR_G_26Hz = 0x02, + LSM6DS3TR_C_ODR_G_52Hz = 0x03, + LSM6DS3TR_C_ODR_G_104Hz = 0x04, + LSM6DS3TR_C_ODR_G_208Hz = 0x05, + LSM6DS3TR_C_ODR_G_416Hz = 0x06, + LSM6DS3TR_C_ODR_G_833Hz = 0x07, + LSM6DS3TR_C_ODR_G_1666Hz = 0x08, + LSM6DS3TR_C_ODR_G_3332Hz = 0x09, + LSM6DS3TR_C_ODR_G_6664Hz = 0x0A, +} LSM6DS3TR_C_ODR_G_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: SW_RESET + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SW_RESET_NORMAL_MODE = 0x00, + LSM6DS3TR_C_SW_RESET_RESET_DEVICE = 0x01, +} LSM6DS3TR_C_SW_RESET_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: BLE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_BLE_LSB = 0x00, + LSM6DS3TR_C_BLE_MSB = 0x01, +} LSM6DS3TR_C_BLE_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: IF_INC + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_IF_INC_DISABLED = 0x00, + LSM6DS3TR_C_IF_INC_ENABLED = 0x01, +} LSM6DS3TR_C_IF_INC_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: SIM + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SIM_4_WIRE = 0x00, + LSM6DS3TR_C_SIM_3_WIRE = 0x01, +} LSM6DS3TR_C_SIM_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: PP_OD + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_PP_OD_PUSH_PULL = 0x00, + LSM6DS3TR_C_PP_OD_OPEN_DRAIN = 0x01, +} LSM6DS3TR_C_PP_OD_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: H_LACTIVE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_H_LACTIVE_ACTIVE_HI = 0x00, + LSM6DS3TR_C_H_LACTIVE_ACTIVE_LO = 0x01, +} LSM6DS3TR_C_H_LACTIVE_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: BDU + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_BDU_CONTINUOUS = 0x00, + LSM6DS3TR_C_BDU_BLOCK_UPDATE = 0x01, +} LSM6DS3TR_C_BDU_e; + +/******************************************************************************* + Register : CTRL3_C + Address : 0X12 + Bit Group Name: BOOT + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_BOOT_NORMAL_MODE = 0x00, + LSM6DS3TR_C_BOOT_REBOOT_MODE = 0x01, +} LSM6DS3TR_C_BOOT_e; + +/******************************************************************************* + Register : CTRL4_C [1] + Address : 0X13 + Bit Group Name: LPF1_SEL_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_LPF1_SEL_G_DISABLE = 0x00, + LSM6DS3TR_C_LPF1_SEL_G_ENABLE = 0x01, +} LSM6DS3TR_C_LPF1_SEL_G_e; + +/******************************************************************************* + Register : CTRL4_C [2] + Address : 0X13 + Bit Group Name: I2C_DISABLE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_I2C_DISABLE_I2C_AND_SPI = 0x00, + LSM6DS3TR_C_I2C_DISABLE_SPI_ONLY = 0x04, +} LSM6DS3TR_C_I2C_DISABLE_e; + +/******************************************************************************* + Register : CTRL4_C [3] + Address : 0X13 + Bit Group Name: DRDY_MSK + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DRDY_MSK_DISABLED = 0x00, + LSM6DS3TR_C_DRDY_MSK_ENABLED = 0x08, +} LSM6DS3TR_C_DRDY_MSK_e; + +/******************************************************************************* + Register : CTRL4_C [5] + Address : 0X13 + Bit Group Name: INT2_ON_INT1 + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_ON_INT1_DISABLED = 0x00, + LSM6DS3TR_C_INT2_ON_INT1_ENABLED = 0x20, +} LSM6DS3TR_C_INT2_ON_INT1_e; + +/******************************************************************************* + Register : CTRL4_C [6] + Address : 0X13 + Bit Group Name: SLEEP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SLEEP_DISABLED = 0x00, + LSM6DS3TR_C_SLEEP_ENABLED = 0x40, +} LSM6DS3TR_C_SLEEP_G_e; + +/******************************************************************************* + Register : CTRL5_C + Address : 0X14 + Bit Group Name: ST_XL + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ST_XL_NORMAL_MODE = 0x00, + LSM6DS3TR_C_ST_XL_POS_SIGN_TEST = 0x01, + LSM6DS3TR_C_ST_XL_NEG_SIGN_TEST = 0x02, + LSM6DS3TR_C_ST_XL_NA = 0x03, +} LSM6DS3TR_C_ST_XL_e; + +/******************************************************************************* + Register : CTRL5_C + Address : 0X14 + Bit Group Name: ST_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ST_G_NORMAL_MODE = 0x00, + LSM6DS3TR_C_ST_G_POS_SIGN_TEST = 0x04, + LSM6DS3TR_C_ST_G_NA = 0x08, + LSM6DS3TR_C_ST_G_NEG_SIGN_TEST = 0x0C, +} LSM6DS3TR_C_ST_G_e; + +/******************************************************************************* + Register : CTRL6_C [1:0] + Address : 0X15 + Bit Group Name: FTYPE[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FTYPE_0 = 0x00, + LSM6DS3TR_C_FTYPE_1 = 0x01, + LSM6DS3TR_C_FTYPE_2 = 0x02, + LSM6DS3TR_C_FTYPE_3 = 0x03, +} LSM6DS3TR_C_FTYPE_e; + +/******************************************************************************* + Register : CTRL6_C [4] + Address : 0X15 + Bit Group Name: XL_HM_MODE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_XL_HM_MODE_ENABLED = 0x00, + LSM6DS3TR_C_XL_HM_MODE_DISABLED = 0x10, +} LSM6DS3TR_C_XL_HM_MODE_e; + +/******************************************************************************* + Register : CTRL6_C [5] + Address : 0X15 + Bit Group Name: LVL2_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_LVL2_EN_DISABLED = 0x00, + LSM6DS3TR_C_LVL2_EN_ENABLED = 0x20, +} LSM6DS3TR_C_DEN_LVL2_EN_e; + +/******************************************************************************* + Register : CTRL6_C [6] + Address : 0X15 + Bit Group Name: LVL_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_LVL_EN_DISABLED = 0x00, + LSM6DS3TR_C_LVL_EN_ENABLED = 0x40, +} LSM6DS3TR_C_DEN_LVL_EN_e; + +/******************************************************************************* + Register : CTRL6_C [7] + Address : 0X15 + Bit Group Name: TRIG_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TRIG_EN_DISABLED = 0x00, + LSM6DS3TR_C_TRIG_EN_ENABLED = 0x80, +} LSM6DS3TR_C_TRIG_EN_e; + +/******************************************************************************* + Register : CTRL7_G [5:4] + Address : 0X16 + Bit Group Name: HPM_G[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_HPM_G_16mHz = 0x00, + LSM6DS3TR_C_HPM_G_65mHz = 0x01, + LSM6DS3TR_C_HPM_G_260mHz = 0x02, + LSM6DS3TR_C_HPM_G_1_04Hz = 0x03, +} LSM6DS3TR_C_HPM_G_e; + +/******************************************************************************* + Register : CTRL7_G [6] + Address : 0X16 + Bit Group Name: HP_EN_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_HP_EN_G_DISABLE = 0x00, + LSM6DS3TR_C_HP_EN_G_ENABLE = 0x01, +} LSM6DS3TR_C_HP_EN_G_e; + +/******************************************************************************* + Register : CTRL7_G [7] + Address : 0X16 + Bit Group Name: G_HM_MODE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_G_HM_MODE_ENABLED = 0x80, + LSM6DS3TR_C_G_HM_MODE_DISABLED = 0x00, +} LSM6DS3TR_C_G_HM_MODE_e; + +/******************************************************************************* + Register : CTRL8_XL [2] + Address : 0X17 + Bit Group Name: HP_SLOPE_XL_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_HP_SLOPE_XL_EN_DISABLED = 0x00, + LSM6DS3TR_C_HP_SLOPE_XL_EN_ENABLED = 0x01, +} LSM6DS3TR_C_HP_SLOPE_XL_EN_e; + +/******************************************************************************* + Register : CTRL8_XL [3] + Address : 0X17 + Bit Group Name: INPUT_COMPOSITE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INPUT_COMPOSITE_LOW_LATENCY = 0x00, + LSM6DS3TR_C_INPUT_COMPOSITE_LOW_NOISE = 0x01, +} LSM6DS3TR_C_INPUT_COMPOSITE_e; + +/******************************************************************************* + Register : CTRL8_XL [6:5] + Address : 0X17 + Bit Group Name: HPCF_XL[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_HPCF_XL_00 = 0x00, + LSM6DS3TR_C_HPCF_XL_01 = 0x01, + LSM6DS3TR_C_HPCF_XL_02 = 0x02, + LSM6DS3TR_C_HPCF_XL_03 = 0x03, +} LSM6DS3TR_C_HPCF_XL_e; + +/******************************************************************************* + Register : CTRL8_XL [7] + Address : 0X17 + Bit Group Name: LPF2_XL_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_LPF2_XL_EN_DISABLED = 0x00, + LSM6DS3TR_C_LPF2_XL_EN_ENABLED = 0x01, +} LSM6DS3TR_C_LPF2_XL_EN_e; + +/******************************************************************************* + Register : CTRL9_XL + Address : 0X18 + Bit Group Name: XEN_XL + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_XEN_XL_DISABLED = 0x00, + LSM6DS3TR_C_XEN_XL_ENABLED = 0x08, +} LSM6DS3TR_C_XEN_XL_e; + +/******************************************************************************* + Register : CTRL9_XL + Address : 0X18 + Bit Group Name: YEN_XL + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_YEN_XL_DISABLED = 0x00, + LSM6DS3TR_C_YEN_XL_ENABLED = 0x10, +} LSM6DS3TR_C_YEN_XL_e; + +/******************************************************************************* + Register : CTRL9_XL + Address : 0X18 + Bit Group Name: ZEN_XL + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ZEN_XL_DISABLED = 0x00, + LSM6DS3TR_C_ZEN_XL_ENABLED = 0x20, +} LSM6DS3TR_C_ZEN_XL_e; + +/******************************************************************************* + Register : CTRL10_C + Address : 0X19 + Bit Group Name: SIGN_MOTION_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SIGN_MOTION_EN_DISABLED = 0x00, + LSM6DS3TR_C_SIGN_MOTION_EN_ENABLED = 0x01, +} LSM6DS3TR_C_SIGN_MOTION_EN_e; + +/******************************************************************************* + Register : CTRL10_C + Address : 0X19 + Bit Group Name: PEDO_RST_STEP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_PEDO_RST_STEP_DISABLED = 0x00, + LSM6DS3TR_C_PEDO_RST_STEP_ENABLED = 0x02, +} LSM6DS3TR_C_PEDO_RST_STEP_e; + +/******************************************************************************* + Register : CTRL10_C + Address : 0X19 + Bit Group Name: XEN_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_XEN_G_DISABLED = 0x00, + LSM6DS3TR_C_XEN_G_ENABLED = 0x08, +} LSM6DS3TR_C_XEN_G_e; + +/******************************************************************************* + Register : CTRL10_C + Address : 0X19 + Bit Group Name: YEN_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_YEN_G_DISABLED = 0x00, + LSM6DS3TR_C_YEN_G_ENABLED = 0x10, +} LSM6DS3TR_C_YEN_G_e; + +/******************************************************************************* + Register : CTRL10_C + Address : 0X19 + Bit Group Name: ZEN_G + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_ZEN_G_DISABLED = 0x00, + LSM6DS3TR_C_ZEN_G_ENABLED = 0x20, +} LSM6DS3TR_C_ZEN_G_e; + +/******************************************************************************* + Register : CTRL10_C + Address : 0X19 + Bit Group Name: FUNC_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FUNC_EN_DISABLED = 0x00, + LSM6DS3TR_C_FUNC_EN_ENABLED = 0x04, +} LSM6DS3TR_C_FUNC_EN_e; + +/******************************************************************************* + Register : MASTER_CONFIG + Address : 0X1A + Bit Group Name: MASTER_ON + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_MASTER_ON_DISABLED = 0x00, + LSM6DS3TR_C_MASTER_ON_ENABLED = 0x01, +} LSM6DS3TR_C_MASTER_ON_e; + +/******************************************************************************* + Register : MASTER_CONFIG + Address : 0X1A + Bit Group Name: IRON_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_IRON_EN_DISABLED = 0x00, + LSM6DS3TR_C_IRON_EN_ENABLED = 0x02, +} LSM6DS3TR_C_IRON_EN_e; + +/******************************************************************************* + Register : MASTER_CONFIG + Address : 0X1A + Bit Group Name: PASS_THRU_MODE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_PASS_THRU_MODE_DISABLED = 0x00, + LSM6DS3TR_C_PASS_THRU_MODE_ENABLED = 0x04, +} LSM6DS3TR_C_PASS_THRU_MODE_e; + +/******************************************************************************* + Register : MASTER_CONFIG + Address : 0X1A + Bit Group Name: PULL_UP_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_PULL_UP_EN_DISABLED = 0x00, + LSM6DS3TR_C_PULL_UP_EN_ENABLED = 0x08, +} LSM6DS3TR_C_PULL_UP_EN_e; + +/******************************************************************************* + Register : MASTER_CONFIG + Address : 0X1A + Bit Group Name: START_CONFIG + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_START_CONFIG_XL_G_DRDY = 0x00, + LSM6DS3TR_C_START_CONFIG_EXT_INT2 = 0x10, +} LSM6DS3TR_C_START_CONFIG_e; + +/******************************************************************************* + Register : MASTER_CONFIG + Address : 0X1A + Bit Group Name: DATA_VAL_SEL_FIFO + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DATA_VAL_SEL_FIFO_XL_G_DRDY = 0x00, + LSM6DS3TR_C_DATA_VAL_SEL_FIFO_SHUB_DRDY = 0x40, +} LSM6DS3TR_C_DATA_VAL_SEL_FIFO_e; + +/******************************************************************************* + Register : MASTER_CONFIG + Address : 0X1A + Bit Group Name: DRDY_ON_INT1 + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DRDY_ON_INT1_DISABLED = 0x00, + LSM6DS3TR_C_DRDY_ON_INT1_ENABLED = 0x80, +} LSM6DS3TR_C_DRDY_ON_INT1_e; + +/******************************************************************************* + Register : WAKE_UP_SRC + Address : 0X1B + Bit Group Name: Z_WU + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_Z_WU_NOT_DETECTED = 0x00, + LSM6DS3TR_C_Z_WU_DETECTED = 0x01, +} LSM6DS3TR_C_Z_WU_e; + +/******************************************************************************* + Register : WAKE_UP_SRC + Address : 0X1B + Bit Group Name: Y_WU + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_Y_WU_NOT_DETECTED = 0x00, + LSM6DS3TR_C_Y_WU_DETECTED = 0x02, +} LSM6DS3TR_C_Y_WU_e; + +/******************************************************************************* + Register : WAKE_UP_SRC + Address : 0X1B + Bit Group Name: X_WU + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_X_WU_NOT_DETECTED = 0x00, + LSM6DS3TR_C_X_WU_DETECTED = 0x04, +} LSM6DS3TR_C_X_WU_e; + +/******************************************************************************* + Register : WAKE_UP_SRC + Address : 0X1B + Bit Group Name: WU_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_WU_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_WU_EV_STATUS_DETECTED = 0x08, +} LSM6DS3TR_C_WU_EV_STATUS_e; + +/******************************************************************************* + Register : WAKE_UP_SRC + Address : 0X1B + Bit Group Name: SLEEP_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SLEEP_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_SLEEP_EV_STATUS_DETECTED = 0x10, +} LSM6DS3TR_C_SLEEP_EV_STATUS_e; + +/******************************************************************************* + Register : WAKE_UP_SRC + Address : 0X1B + Bit Group Name: FF_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FF_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_FF_EV_STATUS_DETECTED = 0x20, +} LSM6DS3TR_C_FF_EV_STATUS_e; + +/******************************************************************************* + Register : TAP_SRC + Address : 0X1C + Bit Group Name: Z_TAP + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_Z_TAP_NOT_DETECTED = 0x00, + LSM6DS3TR_C_Z_TAP_DETECTED = 0x01, +} LSM6DS3TR_C_Z_TAP_e; + +/******************************************************************************* + Register : TAP_SRC + Address : 0X1C + Bit Group Name: Y_TAP + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_Y_TAP_NOT_DETECTED = 0x00, + LSM6DS3TR_C_Y_TAP_DETECTED = 0x02, +} LSM6DS3TR_C_Y_TAP_e; + +/******************************************************************************* + Register : TAP_SRC + Address : 0X1C + Bit Group Name: X_TAP + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_X_TAP_NOT_DETECTED = 0x00, + LSM6DS3TR_C_X_TAP_DETECTED = 0x04, +} LSM6DS3TR_C_X_TAP_e; + +/******************************************************************************* + Register : TAP_SRC + Address : 0X1C + Bit Group Name: TAP_SIGN + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TAP_SIGN_POS_SIGN = 0x00, + LSM6DS3TR_C_TAP_SIGN_NEG_SIGN = 0x08, +} LSM6DS3TR_C_TAP_SIGN_e; + +/******************************************************************************* + Register : TAP_SRC + Address : 0X1C + Bit Group Name: DOUBLE_TAP_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DOUBLE_TAP_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_DOUBLE_TAP_EV_STATUS_DETECTED = 0x10, +} LSM6DS3TR_C_DOUBLE_TAP_EV_STATUS_e; + +/******************************************************************************* + Register : TAP_SRC + Address : 0X1C + Bit Group Name: SINGLE_TAP_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SINGLE_TAP_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_SINGLE_TAP_EV_STATUS_DETECTED = 0x20, +} LSM6DS3TR_C_SINGLE_TAP_EV_STATUS_e; + +/******************************************************************************* + Register : TAP_SRC + Address : 0X1C + Bit Group Name: TAP_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TAP_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_TAP_EV_STATUS_DETECTED = 0x40, +} LSM6DS3TR_C_TAP_EV_STATUS_e; + +/******************************************************************************* + Register : D6D_SRC + Address : 0X1D + Bit Group Name: DSD_XL + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DSD_XL_NOT_DETECTED = 0x00, + LSM6DS3TR_C_DSD_XL_DETECTED = 0x01, +} LSM6DS3TR_C_DSD_XL_e; + +/******************************************************************************* + Register : D6D_SRC + Address : 0X1D + Bit Group Name: DSD_XH + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DSD_XH_NOT_DETECTED = 0x00, + LSM6DS3TR_C_DSD_XH_DETECTED = 0x02, +} LSM6DS3TR_C_DSD_XH_e; + +/******************************************************************************* + Register : D6D_SRC + Address : 0X1D + Bit Group Name: DSD_YL + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DSD_YL_NOT_DETECTED = 0x00, + LSM6DS3TR_C_DSD_YL_DETECTED = 0x04, +} LSM6DS3TR_C_DSD_YL_e; + +/******************************************************************************* + Register : D6D_SRC + Address : 0X1D + Bit Group Name: DSD_YH + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DSD_YH_NOT_DETECTED = 0x00, + LSM6DS3TR_C_DSD_YH_DETECTED = 0x08, +} LSM6DS3TR_C_DSD_YH_e; + +/******************************************************************************* + Register : D6D_SRC + Address : 0X1D + Bit Group Name: DSD_ZL + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DSD_ZL_NOT_DETECTED = 0x00, + LSM6DS3TR_C_DSD_ZL_DETECTED = 0x10, +} LSM6DS3TR_C_DSD_ZL_e; + +/******************************************************************************* + Register : D6D_SRC + Address : 0X1D + Bit Group Name: DSD_ZH + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DSD_ZH_NOT_DETECTED = 0x00, + LSM6DS3TR_C_DSD_ZH_DETECTED = 0x20, +} LSM6DS3TR_C_DSD_ZH_e; + +/******************************************************************************* + Register : D6D_SRC + Address : 0X1D + Bit Group Name: D6D_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_D6D_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_D6D_EV_STATUS_DETECTED = 0x40, +} LSM6DS3TR_C_D6D_EV_STATUS_e; + +/******************************************************************************* + Register : STATUS_REG + Address : 0X1E + Bit Group Name: XLDA + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_XLDA_NO_DATA_AVAIL = 0x00, + LSM6DS3TR_C_XLDA_DATA_AVAIL = 0x01, +} LSM6DS3TR_C_XLDA_e; + +/******************************************************************************* + Register : STATUS_REG + Address : 0X1E + Bit Group Name: GDA + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_GDA_NO_DATA_AVAIL = 0x00, + LSM6DS3TR_C_GDA_DATA_AVAIL = 0x02, +} LSM6DS3TR_C_GDA_e; + +/******************************************************************************* + Register : STATUS_REG + Address : 0X1E + Bit Group Name: EV_BOOT + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_EV_BOOT_NO_BOOT_RUNNING = 0x00, + LSM6DS3TR_C_EV_BOOT_BOOT_IS_RUNNING = 0x08, +} LSM6DS3TR_C_EV_BOOT_e; + +/******************************************************************************* + Register : FIFO_STATUS1 + Address : 0X3A + Bit Group Name: DIFF_FIFO + Permission : RO +*******************************************************************************/ +#define LSM6DS3TR_C_DIFF_FIFO_STATUS1_MASK 0xFF +#define LSM6DS3TR_C_DIFF_FIFO_STATUS1_POSITION 0 +#define LSM6DS3TR_C_DIFF_FIFO_STATUS2_MASK 0xF +#define LSM6DS3TR_C_DIFF_FIFO_STATUS2_POSITION 0 + +/******************************************************************************* + Register : FIFO_STATUS2 + Address : 0X3B + Bit Group Name: FIFO_EMPTY + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FIFO_EMPTY_FIFO_NOT_EMPTY = 0x00, + LSM6DS3TR_C_FIFO_EMPTY_FIFO_EMPTY = 0x10, +} LSM6DS3TR_C_FIFO_EMPTY_e; + +/******************************************************************************* + Register : FIFO_STATUS2 + Address : 0X3B + Bit Group Name: FIFO_FULL + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FIFO_FULL_FIFO_NOT_FULL = 0x00, + LSM6DS3TR_C_FIFO_FULL_FIFO_FULL = 0x20, +} LSM6DS3TR_C_FIFO_FULL_e; + +/******************************************************************************* + Register : FIFO_STATUS2 + Address : 0X3B + Bit Group Name: OVERRUN + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_OVERRUN_NO_OVERRUN = 0x00, + LSM6DS3TR_C_OVERRUN_OVERRUN = 0x40, +} LSM6DS3TR_C_OVERRUN_e; + +/******************************************************************************* + Register : FIFO_STATUS2 + Address : 0X3B + Bit Group Name: WTM + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_WTM_BELOW_WTM = 0x00, + LSM6DS3TR_C_WTM_ABOVE_OR_EQUAL_WTM = 0x80, +} LSM6DS3TR_C_WTM_e; + +/******************************************************************************* + Register : FIFO_STATUS3 + Address : 0X3C + Bit Group Name: FIFO_PATTERN + Permission : RO +*******************************************************************************/ +#define LSM6DS3TR_C_FIFO_STATUS3_PATTERN_MASK 0xFF +#define LSM6DS3TR_C_FIFO_STATUS3_PATTERN_POSITION 0 +#define LSM6DS3TR_C_FIFO_STATUS4_PATTERN_MASK 0x03 +#define LSM6DS3TR_C_FIFO_STATUS4_PATTERN_POSITION 0 + +/******************************************************************************* + Register : FUNC_SRC + Address : 0X53 + Bit Group Name: SENS_HUB_END + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SENS_HUB_END_STILL_ONGOING = 0x00, + LSM6DS3TR_C_SENS_HUB_END_OP_COMPLETED = 0x01, +} LSM6DS3TR_C_SENS_HUB_END_e; + +/******************************************************************************* + Register : FUNC_SRC + Address : 0X53 + Bit Group Name: SOFT_IRON_END + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SOFT_IRON_END_NOT_COMPLETED = 0x00, + LSM6DS3TR_C_SOFT_IRON_END_COMPLETED = 0x02, +} LSM6DS3TR_C_SOFT_IRON_END_e; + +/******************************************************************************* + Register : FUNC_SRC + Address : 0X53 + Bit Group Name: PEDO_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_PEDO_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_PEDO_EV_STATUS_DETECTED = 0x10, +} LSM6DS3TR_C_PEDO_EV_STATUS_e; + +/******************************************************************************* + Register : FUNC_SRC + Address : 0X53 + Bit Group Name: TILT_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TILT_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_TILT_EV_STATUS_DETECTED = 0x20, +} LSM6DS3TR_C_TILT_EV_STATUS_e; + +/******************************************************************************* + Register : FUNC_SRC + Address : 0X53 + Bit Group Name: SIGN_MOT_EV_STATUS + Permission : RO +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SIGN_MOT_EV_STATUS_NOT_DETECTED = 0x00, + LSM6DS3TR_C_SIGN_MOT_EV_STATUS_DETECTED = 0x40, +} LSM6DS3TR_C_SIGN_MOT_EV_STATUS_e; + +/******************************************************************************* + Register : TAP_CFG1 + Address : 0X58 + Bit Group Name: LIR + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_LIR_DISABLED = 0x00, + LSM6DS3TR_C_LIR_ENABLED = 0x01, +} LSM6DS3TR_C_LIR_e; + +/******************************************************************************* + Register : TAP_CFG1 + Address : 0X58 + Bit Group Name: TAP_Z_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TAP_Z_EN_DISABLED = 0x00, + LSM6DS3TR_C_TAP_Z_EN_ENABLED = 0x01, +} LSM6DS3TR_C_TAP_Z_EN_e; + +/******************************************************************************* + Register : TAP_CFG1 + Address : 0X58 + Bit Group Name: TAP_Y_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TAP_Y_EN_DISABLED = 0x00, + LSM6DS3TR_C_TAP_Y_EN_ENABLED = 0x01, +} LSM6DS3TR_C_TAP_Y_EN_e; + +/******************************************************************************* + Register : TAP_CFG1 + Address : 0X58 + Bit Group Name: TAP_X_EN + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TAP_X_EN_DISABLED = 0x00, + LSM6DS3TR_C_TAP_X_EN_ENABLED = 0x01, +} LSM6DS3TR_C_TAP_X_EN_e; + +/******************************************************************************* + Register : TAP_CFG1 + Address : 0X58 + Bit Group Name: SLOPE_FDS + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SLOPE_FDS_SLOPE = 0x00, + LSM6DS3TR_C_SLOPE_FDS_HPF = 0x01, +} LSM6DS3TR_C_SLOPE_FDS_e; + +/******************************************************************************* + Register : TAP_CFG1 [6:5] + Address : 0X58 + Bit Group Name: INACT_EN[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INACT_EN_DISABLED = 0x00, + LSM6DS3TR_C_INACT_EN_GYRO_NOT_CHANGE = 0x01, + LSM6DS3TR_C_INACT_EN_GYRO_SLEEP = 0x02, + LSM6DS3TR_C_INACT_EN_GYRO_POWER_DOWN = 0x03, +} LSM6DS3TR_C_TILT_EN_e; + +/******************************************************************************* + Register : TAP_CFG1 + Address : 0X58 + Bit Group Name: INTERRUPTS_ENABLE + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INTERRUPTS_DISABLE = 0x00, + LSM6DS3TR_C_INTERRUPTS_ENABLE = 0x01, +} LSM6DS3TR_C_INTERRUPTS_ENABLE_e; + +/******************************************************************************* + Register : TAP_THS_6D [6:5] + Address : 0X59 + Bit Group Name: SIXD_THS [1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SIXD_THS_80_degree = 0x00, + LSM6DS3TR_C_SIXD_THS_70_degree = 0x01, + LSM6DS3TR_C_SIXD_THS_60_degree = 0x02, + LSM6DS3TR_C_SIXD_THS_50_degree = 0x03, +} LSM6DS3TR_C_SIXD_THS_e; + +/******************************************************************************* + Register : INT_DUR2 [1:0] + Address : 0X5A + Bit Group Name: SHOCK[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SHOCK_4_OVER_ODR_XL = 0x00, + LSM6DS3TR_C_SHOCK_8_OVER_ODR_XL = 0x01, + LSM6DS3TR_C_SHOCK_16_OVER_ODR_XL = 0x02, + LSM6DS3TR_C_SHOCK_24_OVER_ODR_XL = 0x03, +} LSM6DS3TR_C_SHOCK_e; + +/******************************************************************************* + Register : INT_DUR2 [3:2] + Address : 0X5A + Bit Group Name: QUIET[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_QUIET_2_OVER_ODR_XL = 0x00, + LSM6DS3TR_C_QUIET_4_OVER_ODR_XL = 0x01, + LSM6DS3TR_C_QUIET_8_OVER_ODR_XL = 0x02, + LSM6DS3TR_C_QUIET_12_OVER_ODR_XL = 0x03, +} LSM6DS3TR_C_QUIET_e; + +/******************************************************************************* + Register : INT_DUR2 [7:4] + Address : 0X5A + Bit Group Name: DUR[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_DUR_16_OVER_ODR_XL = 0x00, + LSM6DS3TR_C_DUR_32_OVER_ODR_XL = 0x01, + LSM6DS3TR_C_DUR_64_OVER_ODR_XL = 0x02, + LSM6DS3TR_C_DUR_96_OVER_ODR_XL = 0x03, + LSM6DS3TR_C_DUR_128_OVER_ODR_XL = 0x04, + LSM6DS3TR_C_DUR_160_OVER_ODR_XL = 0x05, + LSM6DS3TR_C_DUR_192_OVER_ODR_XL = 0x06, + LSM6DS3TR_C_DUR_224_OVER_ODR_XL = 0x07, + LSM6DS3TR_C_DUR_256_OVER_ODR_XL = 0x08, + LSM6DS3TR_C_DUR_288_OVER_ODR_XL = 0x09, + LSM6DS3TR_C_DUR_320_OVER_ODR_XL = 0x0A, + LSM6DS3TR_C_DUR_352_OVER_ODR_XL = 0x0B, + LSM6DS3TR_C_DUR_384_OVER_ODR_XL = 0x0C, + LSM6DS3TR_C_DUR_416_OVER_ODR_XL = 0x0D, + LSM6DS3TR_C_DUR_448_OVER_ODR_XL = 0x0E, + LSM6DS3TR_C_DUR_480_OVER_ODR_XL = 0x0F, +} LSM6DS3TR_C_DUR_e; + +/******************************************************************************* + Register : WAKE_UP_THS + Address : 0X5B + Bit Group Name: SINGLE_DOUBLE_TAP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_SINGLE_DOUBLE_TAP_SINGLE_ONLY = 0x00, + LSM6DS3TR_C_SINGLE_DOUBLE_TAP_SINGLE_AND_DOUBLE = 0x01, +} LSM6DS3TR_C_SINGLE_DOUBLE_TAP_e; + +/******************************************************************************* + Register : WAKE_UP_DUR + Address : 0X5C + Bit Group Name: SLEEP_DUR + Permission : RW +*******************************************************************************/ +#define LSM6DS3TR_C_SLEEP_DUR_MASK 0x0F +#define LSM6DS3TR_C_SLEEP_DUR_POSITION 0 + +/******************************************************************************* + Register : WAKE_UP_DUR + Address : 0X5C + Bit Group Name: TIMER_HR + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_TIMER_HR_6_4ms = 0x00, + LSM6DS3TR_C_TIMER_HR_25us = 0x10, +} LSM6DS3TR_C_TIMER_HR_e; + +/******************************************************************************* + Register : WAKE_UP_DUR [6:5] + Address : 0X5C + Bit Group Name: WAKE_DUR[1:0] + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_WAKE_DUR_0_ODR_XL = 0x00, + LSM6DS3TR_C_WAKE_DUR_1_ODR_XL = 0x01, + LSM6DS3TR_C_WAKE_DUR_2_ODR_XL = 0x02, + LSM6DS3TR_C_WAKE_DUR_3_ODR_XL = 0x03, +} LSM6DS3TR_C_WAKE_DUR_e; + +/******************************************************************************* + Register : FREE_FALL + Address : 0X5D + Bit Group Name: FF_DUR + Permission : RW +*******************************************************************************/ +#define LSM6DS3TR_C_FF_FREE_FALL_DUR_MASK 0xF8 +#define LSM6DS3TR_C_FF_FREE_FALL_DUR_POSITION 3 +#define LSM6DS3TR_C_FF_WAKE_UP_DUR_MASK 0x80 +#define LSM6DS3TR_C_FF_WAKE_UP_DUR_POSITION 7 + + +/******************************************************************************* + Register : FREE_FALL + Address : 0X5D + Bit Group Name: FF_THS + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_FF_THS_5 = 0x00, + LSM6DS3TR_C_FF_THS_7 = 0x01, + LSM6DS3TR_C_FF_THS_8 = 0x02, + LSM6DS3TR_C_FF_THS_10 = 0x03, + LSM6DS3TR_C_FF_THS_11 = 0x04, + LSM6DS3TR_C_FF_THS_13 = 0x05, + LSM6DS3TR_C_FF_THS_15 = 0x06, + LSM6DS3TR_C_FF_THS_16 = 0x07, +} LSM6DS3TR_C_FF_THS_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_TIMER + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_TIMER_DISABLED = 0x00, + LSM6DS3TR_C_INT1_TIMER_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_TIMER_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_TILT + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_TILT_DISABLED = 0x00, + LSM6DS3TR_C_INT1_TILT_ENABLED = 0x02, +} LSM6DS3TR_C_INT1_TILT_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_6D + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_6D_DISABLED = 0x00, + LSM6DS3TR_C_INT1_6D_ENABLED = 0x04, +} LSM6DS3TR_C_INT1_6D_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_TAP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_DOUBLE_TAP_DISABLED = 0x00, + LSM6DS3TR_C_INT1_DOUBLE_TAP_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_DOUBLE_TAP_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_FF + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_FF_DISABLED = 0x00, + LSM6DS3TR_C_INT1_FF_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_FF_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_WU + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_WU_DISABLED = 0x00, + LSM6DS3TR_C_INT1_WU_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_WU_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_SINGLE_TAP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_SINGLE_TAP_DISABLED = 0x00, + LSM6DS3TR_C_INT1_SINGLE_TAP_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_SINGLE_TAP_e; + +/******************************************************************************* + Register : MD1_CFG + Address : 0X5E + Bit Group Name: INT1_SLEEP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT1_SLEEP_DISABLED = 0x00, + LSM6DS3TR_C_INT1_SLEEP_ENABLED = 0x01, +} LSM6DS3TR_C_INT1_SLEEP_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_TIMER + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_TIMER_DISABLED = 0x00, + LSM6DS3TR_C_INT2_TIMER_ENABLED = 0x01, +} LSM6DS3TR_C_INT2_TIMER_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_TILT + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_TILT_DISABLED = 0x00, + LSM6DS3TR_C_INT2_TILT_ENABLED = 0x02, +} LSM6DS3TR_C_INT2_TILT_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_6D + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_6D_DISABLED = 0x00, + LSM6DS3TR_C_INT2_6D_ENABLED = 0x04, +} LSM6DS3TR_C_INT2_6D_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_TAP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_TAP_DISABLED = 0x00, + LSM6DS3TR_C_INT2_TAP_ENABLED = 0x08, +} LSM6DS3TR_C_INT2_TAP_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_FF + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_FF_DISABLED = 0x00, + LSM6DS3TR_C_INT2_FF_ENABLED = 0x10, +} LSM6DS3TR_C_INT2_FF_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_WU + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_WU_DISABLED = 0x00, + LSM6DS3TR_C_INT2_WU_ENABLED = 0x20, +} LSM6DS3TR_C_INT2_WU_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_SINGLE_TAP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_SINGLE_TAP_DISABLED = 0x00, + LSM6DS3TR_C_INT2_SINGLE_TAP_ENABLED = 0x40, +} LSM6DS3TR_C_INT2_SINGLE_TAP_e; + +/******************************************************************************* + Register : MD2_CFG + Address : 0X5F + Bit Group Name: INT2_SLEEP + Permission : RW +*******************************************************************************/ +typedef enum +{ + LSM6DS3TR_C_INT2_SLEEP_DISABLED = 0x00, + LSM6DS3TR_C_INT2_SLEEP_ENABLED = 0x80, +} LSM6DS3TR_C_INT2_SLEEP_e; + + +/*-------------------------------------------------------------------------------------------------- + * CONVERSION MACROS + *------------------------------------------------------------------------------------------------*/ + +/* Convert Hz value to accelerometer ODR enum */ +#define LSM6DS3TR_C_HZ_TO_ODR_XL(hz) ( \ + (hz) == 0 ? LSM6DS3TR_C_ODR_XL_POWER_DOWN : \ + (hz) == 1 ? LSM6DS3TR_C_ODR_XL_1_6Hz : \ + (hz) == 12 ? LSM6DS3TR_C_ODR_XL_12_5Hz : \ + (hz) == 26 ? LSM6DS3TR_C_ODR_XL_26Hz : \ + (hz) == 52 ? LSM6DS3TR_C_ODR_XL_52Hz : \ + (hz) == 104 ? LSM6DS3TR_C_ODR_XL_104Hz : \ + (hz) == 208 ? LSM6DS3TR_C_ODR_XL_208Hz : \ + (hz) == 416 ? LSM6DS3TR_C_ODR_XL_416Hz : \ + (hz) == 833 ? LSM6DS3TR_C_ODR_XL_833Hz : \ + (hz) == 1666 ? LSM6DS3TR_C_ODR_XL_1666Hz : \ + (hz) == 3332 ? LSM6DS3TR_C_ODR_XL_3332Hz : \ + (hz) == 6664 ? LSM6DS3TR_C_ODR_XL_6664Hz : \ + LSM6DS3TR_C_ODR_XL_POWER_DOWN) + +/* Convert accelerometer ODR enum to Hz value */ +#define LSM6DS3TR_C_ODR_XL_TO_HZ(odr) ( \ + (odr) == LSM6DS3TR_C_ODR_XL_POWER_DOWN ? 0 : \ + (odr) == LSM6DS3TR_C_ODR_XL_1_6Hz ? 1 : \ + (odr) == LSM6DS3TR_C_ODR_XL_12_5Hz ? 12 : \ + (odr) == LSM6DS3TR_C_ODR_XL_26Hz ? 26 : \ + (odr) == LSM6DS3TR_C_ODR_XL_52Hz ? 52 : \ + (odr) == LSM6DS3TR_C_ODR_XL_104Hz ? 104 : \ + (odr) == LSM6DS3TR_C_ODR_XL_208Hz ? 208 : \ + (odr) == LSM6DS3TR_C_ODR_XL_416Hz ? 416 : \ + (odr) == LSM6DS3TR_C_ODR_XL_833Hz ? 833 : \ + (odr) == LSM6DS3TR_C_ODR_XL_1666Hz ? 1666 : \ + (odr) == LSM6DS3TR_C_ODR_XL_3332Hz ? 3332 : \ + (odr) == LSM6DS3TR_C_ODR_XL_6664Hz ? 6664 : 0) + +/* Convert Hz value to gyroscope ODR enum */ +#define LSM6DS3TR_C_HZ_TO_ODR_G(hz) ( \ + (hz) == 0 ? LSM6DS3TR_C_ODR_G_POWER_DOWN : \ + (hz) == 12 ? LSM6DS3TR_C_ODR_G_12_5Hz : \ + (hz) == 26 ? LSM6DS3TR_C_ODR_G_26Hz : \ + (hz) == 52 ? LSM6DS3TR_C_ODR_G_52Hz : \ + (hz) == 104 ? LSM6DS3TR_C_ODR_G_104Hz : \ + (hz) == 208 ? LSM6DS3TR_C_ODR_G_208Hz : \ + (hz) == 416 ? LSM6DS3TR_C_ODR_G_416Hz : \ + (hz) == 833 ? LSM6DS3TR_C_ODR_G_833Hz : \ + (hz) == 1666 ? LSM6DS3TR_C_ODR_G_1666Hz : \ + (hz) == 3332 ? LSM6DS3TR_C_ODR_G_3332Hz : \ + (hz) == 6664 ? LSM6DS3TR_C_ODR_G_6664Hz : \ + LSM6DS3TR_C_ODR_G_POWER_DOWN) + +/* Convert gyroscope ODR enum to Hz value */ +#define LSM6DS3TR_C_ODR_G_TO_HZ(odr) ( \ + (odr) == LSM6DS3TR_C_ODR_G_POWER_DOWN ? 0 : \ + (odr) == LSM6DS3TR_C_ODR_G_12_5Hz ? 12 : \ + (odr) == LSM6DS3TR_C_ODR_G_26Hz ? 26 : \ + (odr) == LSM6DS3TR_C_ODR_G_52Hz ? 52 : \ + (odr) == LSM6DS3TR_C_ODR_G_104Hz ? 104 : \ + (odr) == LSM6DS3TR_C_ODR_G_208Hz ? 208 : \ + (odr) == LSM6DS3TR_C_ODR_G_416Hz ? 416 : \ + (odr) == LSM6DS3TR_C_ODR_G_833Hz ? 833 : \ + (odr) == LSM6DS3TR_C_ODR_G_1666Hz ? 1666 : \ + (odr) == LSM6DS3TR_C_ODR_G_3332Hz ? 3332 : \ + (odr) == LSM6DS3TR_C_ODR_G_6664Hz ? 6664 : 0) + +/* Convert g value to accelerometer full scale enum */ +#define LSM6DS3TR_C_G_TO_FS_XL(g) ( \ + (g) == 2 ? LSM6DS3TR_C_FS_XL_2g : \ + (g) == 4 ? LSM6DS3TR_C_FS_XL_4g : \ + (g) == 8 ? LSM6DS3TR_C_FS_XL_8g : \ + (g) == 16 ? LSM6DS3TR_C_FS_XL_16g : \ + LSM6DS3TR_C_FS_XL_2g) + +/* Convert accelerometer full scale enum to g value */ +#define LSM6DS3TR_C_FS_XL_TO_G(fs) ( \ + (fs) == LSM6DS3TR_C_FS_XL_2g ? 2 : \ + (fs) == LSM6DS3TR_C_FS_XL_4g ? 4 : \ + (fs) == LSM6DS3TR_C_FS_XL_8g ? 8 : \ + (fs) == LSM6DS3TR_C_FS_XL_16g ? 16 : 2) + +/* Convert dps value to gyroscope full scale enum */ +#define LSM6DS3TR_C_DPS_TO_FS_G(dps) ( \ + (dps) == 125 ? LSM6DS3TR_C_FS_G_ABSTR_125dps : \ + (dps) == 250 ? LSM6DS3TR_C_FS_G_ABSTR_250dps : \ + (dps) == 500 ? LSM6DS3TR_C_FS_G_ABSTR_500dps : \ + (dps) == 1000 ? LSM6DS3TR_C_FS_G_ABSTR_1000dps : \ + (dps) == 2000 ? LSM6DS3TR_C_FS_G_ABSTR_2000dps : \ + LSM6DS3TR_C_FS_G_ABSTR_250dps) + +/* Convert gyroscope full scale enum to dps value */ +#define LSM6DS3TR_C_FS_G_TO_DPS(fs) ( \ + (fs) == LSM6DS3TR_C_FS_G_ABSTR_125dps ? 125 : \ + (fs) == LSM6DS3TR_C_FS_G_ABSTR_250dps ? 250 : \ + (fs) == LSM6DS3TR_C_FS_G_ABSTR_500dps ? 500 : \ + (fs) == LSM6DS3TR_C_FS_G_ABSTR_1000dps ? 1000 : \ + (fs) == LSM6DS3TR_C_FS_G_ABSTR_2000dps ? 2000 : 250) + +#endif /* IMU_REG_H_ */ diff --git a/src/bsp/led.c b/src/bsp/led.c new file mode 100644 index 0000000..f2cd413 --- /dev/null +++ b/src/bsp/led.c @@ -0,0 +1,159 @@ +/** + * @file led.c + * @brief LED module for bubble and pin + * @author Kenny Tran + * @date Feb 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../wrappers/pwm.h" + +#include "led.h" + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static uint8_t _led_pwm_inst_id; +static bool _led_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e LED_init (void) +{ + ERRNO_e err; + + if (_led_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (PWM_init (&_led_pwm_inst_id, LED_PWM_PERIOD_NS) != ERRNO_SUCCESS) + { + return ERRNO_PWM_FAILURE; + } + + /* Set initialized flag. */ + _led_init = true; + + /* Initialize all LEDs to off. */ + err = LED_pwm_set (LED_BUBBLE, 0.0); + err |= LED_pwm_set (LED_PIN, 0.0); + err |= LED_pwm_set (LED_FIBER, 0.0); + + return err; +} + +ERRNO_e LED_pwm_set (LED_e led, float duty_cycle) +{ + /* Each PWM instance supports 4 PWM channels. Configure pins on different channels. + * Pin = channel 0 + * Bubble = channel 1 + * Fiber = channel 2 + */ + + if (!_led_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (duty_cycle > (float) 100.0 || duty_cycle < (float) 0.0) + { + return ERRNO_INVALID_PARAM; + } + + if (PWM_duty_cycle_set (_led_pwm_inst_id, (PWM_ch_e) led, duty_cycle)) + { + return ERRNO_PWM_FAILURE; + } + + return ERRNO_SUCCESS; +} + +void LED_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 1) + { + if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_init ()); + } + else + { + goto USAGE; + } + } + else if (argc == 3) + { + if (!strcmp (argv[0], "--set") || !strcmp (argv[0], "-s")) + { + float dc = strtof (argv[2], NULL); + + if (!strcmp (argv[1], "pin")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_pwm_set (LED_PIN, dc)); + } + else if (!strcmp (argv[1], "bubble")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_pwm_set (LED_BUBBLE, dc)); + } + else if (!strcmp (argv[1], "fiber")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_pwm_set (LED_FIBER, dc)); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: led [OPTION...]\r\n"); + PRINT (INFO, " -i | --init Initializes LED module\r\n"); + PRINT (INFO, " -s | --set Sets LED to specified PWM duty cycle\r\n"); + PRINT (INFO, " <%%s> [bubble | pin | fiber]\r\n"); + PRINT (INFO, " <%%f> Duty Cycle\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/bsp/led.h b/src/bsp/led.h new file mode 100644 index 0000000..c886281 --- /dev/null +++ b/src/bsp/led.h @@ -0,0 +1,59 @@ +/** + * @file led.h + * @brief LED module for bubble and pin + * @author Kenny Tran + * @date Feb 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef LED_H_ +#define LED_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + +#include "../wrappers/pwm.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define LED_PWM_PERIOD_US (4000) /* 250 Hz */ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +/** Each PWM instance supports 4 PWM channels. Configure pins on different channels. + * Bubble = channel 0 + * Pin = channel 1 + * Fiber = channel 2 + */ +typedef enum LED +{ + LED_BUBBLE = PWM_CH_0, + LED_PIN = PWM_CH_1, + LED_FIBER = PWM_CH_2 +} LED_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e LED_init (void); + +ERRNO_e LED_pwm_set (LED_e led, float duty_cycle); + +void LED_cli_cmd (int32_t argc, char **argv); + + +#endif /* LED_H_ */ diff --git a/src/bsp/led_ring.c b/src/bsp/led_ring.c new file mode 100644 index 0000000..879ebb8 --- /dev/null +++ b/src/bsp/led_ring.c @@ -0,0 +1,501 @@ +/** + * @file led_ring.c + * @brief LED ring module + * @author Kenny Tran + * @date Feb 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" +#include "../lib/rgb.h" + +#include "../wrappers/gpio.h" + +#include "led_ring.h" + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct led_rgb _led_ring_rgb_s; + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static const struct device *const _led_ring = DEVICE_DT_GET(DT_ALIAS(led_ring)); +static _led_ring_rgb_s _led_ring_rgb_arr[LED_RING_PIXELS]; +static _led_ring_rgb_s _led_ring_rgb_arr_prev[LED_RING_PIXELS]; +static bool _led_ring_init = false; +static bool _led_ring_din = false; +static uint16_t _led_ring_rainbow_bounds = LED_RING_PIXELS - 1; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static RGB_s _led_ring_hsv (uint16_t hue, uint8_t sat, uint8_t val); +static void _led_ring_din_isr (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e LED_RING_init (void) +{ + if (_led_ring_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (!device_is_ready (_led_ring)) + { + return ERRNO_NOT_READY; + } + + /* Initialize GPIO used to control FET (power to LED ring). */ + if (GPIO_init () < ERRNO_SUCCESS) + { + return ERRNO_GPIO_FAILURE; + } + + /* Configure GPIO as output and initialize to low. */ + if (GPIO_cfg (GPIO_PIN_LED_RING_PWR, GPIO_DIRECTION_OUT, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + + /* Configure GPIO as output to enable / disable level shifter. */ + if (GPIO_cfg (GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE, GPIO_DIRECTION_OUT, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + + /* Configure GPIO for data in from LED ring. */ + if (GPIO_cfg (GPIO_PIN_LED_RING_DIN, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, _led_ring_din_isr)) + { + return ERRNO_GPIO_FAILURE; + } + + /* Set initialized flag. */ + _led_ring_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e LED_RING_clear (void) +{ + if (!_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Zero out RGB array. */ + memset (_led_ring_rgb_arr, 0, sizeof (_led_ring_rgb_arr)); + + return ERRNO_SUCCESS; +} + +ERRNO_e LED_RING_set (uint16_t idx, uint8_t r, uint8_t g, uint8_t b) +{ + if (!_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (idx >= LED_RING_PIXELS) + { + return ERRNO_INVALID_PARAM; + } + + /* Update RGB array. */ + _led_ring_rgb_arr[idx].r = r; + _led_ring_rgb_arr[idx].g = g; + _led_ring_rgb_arr[idx].b = b; + + return ERRNO_SUCCESS; +} + +ERRNO_e LED_RING_bounds_set (uint16_t idx) +{ + if (!_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + _led_ring_rainbow_bounds = idx; + + return ERRNO_SUCCESS; +} + +/* + @param first_hue Hue of first pixel, 0-65535, representing one full + cycle of the color wheel. Each subsequent pixel will + be offset to complete one or more cycles over the + length of the strip. + @param reps Number of cycles of the color wheel over the length + of the strip. Default is 1. Negative values can be + used to reverse the hue order. + @param saturation Saturation (optional), 0-255 = gray to pure hue, + default = 255. + @param brightness Brightness/value (optional), 0-255 = off to max, + default = 255. This is distinct and in combination + with any configured global strip brightness. + @param gammify If true (default), apply gamma correction to colors + for better appearance. +*/ +ERRNO_e LED_RING_rainbow (uint16_t first_hue, int8_t reps, uint8_t sat, uint8_t brightness, bool gammify) +{ + ERRNO_e err = ERRNO_SUCCESS; + uint8_t i; + static const uint8_t rgb_gamma_table[] = + { + 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, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, + 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, + 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, + 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114, + 115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142, + 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175, + 177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, + 215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255 + }; + + if (!_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + for (i = 0; i <= _led_ring_rainbow_bounds; i++) + { + uint16_t hue = first_hue + (i * reps * 65536) / LED_RING_PIXELS; + RGB_s color = _led_ring_hsv (hue, sat, brightness); + + if (gammify) + { + color.r = rgb_gamma_table[color.r]; + color.g = rgb_gamma_table[color.g]; + color.b = rgb_gamma_table[color.b]; + } + + err |= LED_RING_set (i, color.r, color.g, color.b); + } + + return err; +} + +ERRNO_e LED_RING_update (void) +{ + if (!_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (!memcmp (_led_ring_rgb_arr, _led_ring_rgb_arr_prev, sizeof (_led_ring_rgb_arr))) + { + return ERRNO_SUCCESS; + } + + memcpy (_led_ring_rgb_arr_prev, _led_ring_rgb_arr, sizeof (_led_ring_rgb_arr)); + + if (led_strip_update_rgb (_led_ring, _led_ring_rgb_arr, LED_RING_PIXELS)) + { + return ERRNO_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e LED_RING_enable (bool en) +{ + if (!_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Enable / disable level shifter for LED ring. Active-high. */ + if (GPIO_level_set (GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE, en)) + { + return ERRNO_GPIO_FAILURE; + } + + /* Enable / disable power to LED ring. Active-high. */ + if (GPIO_level_set (GPIO_PIN_LED_RING_PWR, en)) + { + return ERRNO_GPIO_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e LED_RING_din_received (bool *p_rx) +{ + if (!_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + *p_rx = _led_ring_din; + + _led_ring_din = false; + + return ERRNO_SUCCESS; +} + +void LED_RING_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 1) + { + if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_RING_init ()); + } + else if (!strcmp (argv[0], "--update") || !strcmp (argv[0], "-u")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_RING_update ()); + } + else if (!strcmp (argv[0], "--din")) + { + bool din; + + PRINT (INFO, "Ret code : %d\r\n", LED_RING_din_received (&din)); + PRINT (INFO, "DIN : %s\r\n", (din ? "true" : "false")); + } + else + { + goto USAGE; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "--enable") || !strcmp (argv[0], "-e")) + { + if (!strcmp (argv[1], "true")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_RING_enable (true)); + } + else if (!strcmp (argv[1], "false")) + { + PRINT (INFO, "Ret code : %d\r\n", LED_RING_enable (false)); + } + else + { + goto USAGE; + } + + } + else + { + goto USAGE; + } + } + else if (argc == 5) + { + if (!strcmp (argv[0], "--set") || !strcmp (argv[0], "-s")) + { + uint16_t idx = (uint16_t) strtoul (argv[1], NULL, 0); + uint8_t r = (uint8_t) strtoul (argv[2], NULL, 0); + uint8_t g = (uint8_t) strtoul (argv[3], NULL, 0); + uint8_t b = (uint8_t) strtoul (argv[4], NULL, 0); + + PRINT (INFO, "Ret code : %d\r\n", LED_RING_set (idx, r, g, b)); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: led_ring [OPTION...]\r\n"); + PRINT (INFO, " -i | --init Initializes LED ring module\r\n"); + PRINT (INFO, " -e | --enable Enables power to the LED ring\r\n"); + PRINT (INFO, " <%%b> true = enable\r\n"); + PRINT (INFO, " false = disable\r\n"); + PRINT (INFO, " -s | --set Sets addressed pixel to specified RGB value\r\n"); + PRINT (INFO, " <%%d> LED index\r\n"); + PRINT (INFO, " <%%d> Red [0-255]\r\n"); + PRINT (INFO, " <%%d> Green [0-255]\r\n"); + PRINT (INFO, " <%%d> Blue [0-255]\r\n"); + PRINT (INFO, " -u | --update Updates / refreshes LED ring to newest setting\r\n"); + PRINT (INFO, " --din Checks if data out from LED ring is going back to MCU\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +/*! + @brief Convert hue, saturation and value into a packed 32-bit RGB color + that can be passed to setPixelColor() or other RGB-compatible + functions. + @param hue An unsigned 16-bit value, 0 to 65535, representing one full + loop of the color wheel, which allows 16-bit hues to "roll + over" while still doing the expected thing (and allowing + more precision than the wheel() function that was common to + prior NeoPixel examples). + @param sat Saturation, 8-bit value, 0 (min or pure grayscale) to 255 + (max or pure hue). Default of 255 if unspecified. + @param val Value (brightness), 8-bit value, 0 (min / black / off) to + 255 (max or full brightness). Default of 255 if unspecified. + @return Packed 32-bit RGB with the most significant byte set to 0 -- the + white element of WRGB pixels is NOT utilized. Result is linearly + but not perceptually correct, so you may want to pass the result + through the gamma32() function (or your own gamma-correction + operation) else colors may appear washed out. This is not done + automatically by this function because coders may desire a more + refined gamma-correction function than the simplified + one-size-fits-all operation of gamma32(). Diffusing the LEDs also + really seems to help when using low-saturation colors. +*/ +static RGB_s _led_ring_hsv (uint16_t hue, uint8_t sat, uint8_t val) +{ + RGB_s color; + uint32_t packed; + /* Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover; + * 0 is not the start of pure red, but the midpoint...a few values above + * zero and a few below 65536 all yield pure red (similarly, 32768 is the + * midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values + * each for red, green, blue) really only allows for 1530 distinct hues + * (not 1536, more on that below), but the full unsigned 16-bit type was + * chosen for hue so that one's code can easily handle a contiguous color + * wheel by allowing hue to roll over in either direction. + */ + hue = (hue * 1530L + 32768) / 65536; + /* Because red is centered on the rollover point (the +32768 above, + * essentially a fixed-point + 0.5), the above actually yields 0 to 1530, + * where 0 and 1530 would yield the same thing. Rather than apply a + * costly modulo operator, 1530 is handled as a special case below. + */ + + /* So you'd think that the color "hexcone" (the thing that ramps from + * pure red, to pure yellow, to pure green and so forth back to red, + * yielding six slices), and with each color component having 256 + * possible values (0-255), might have 1536 possible items (6*256), + * but in reality there's 1530. This is because the last element in + * each 256-element slice is equal to the first element of the next + * slice, and keeping those in there this would create small + * discontinuities in the color wheel. So the last element of each + * slice is dropped...we regard only elements 0-254, with item 255 + * being picked up as element 0 of the next slice. Like this: + * Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0 + * Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0 + * Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254 + * and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why + * the constants below are not the multiples of 256 you might expect. + */ + + /* Convert hue to R,G,B (nested ifs faster than divide+mod+switch) */ + if (hue < 510) + { + /* Red to Green-1 */ + color.b = 0; + if (hue < 255) + { + /* Red to Yellow-1 */ + color.r = 255; + /* color.g = 0 to 254 */ + color.g = hue; + } + else + { + /* Yellow to Green-1 */ + /* color.r = 255 to 1 */ + color.r = 510 - hue; + color.g = 255; + } + } + else if (hue < 1020) + { + /* Green to Blue-1 */ + color.r = 0; + if (hue < 765) + { + /* Green to Cyan-1 */ + color.g = 255; + /* color.b = 0 to 254 */ + color.b = hue - 510; + } + else + { + /* Cyan to Blue-1 */ + /* color.g = 255 to 1 */ + color.g = 1020 - hue; + color.b = 255; + } + } + else if (hue < 1530) + { + /* Blue to Red-1 */ + color.g = 0; + if (hue < 1275) + { + /* Blue to Magenta-1 */ + /* color.r = 0 to 254 */ + color.r = hue - 1020; + color.b = 255; + } + else + { + /* Magenta to Red-1 */ + color.r = 255; + /* color.b = 255 to 1 */ + color.b = 1530 - hue; + } + } + else + { + /* Last 0.5 Red (quicker than % operator) */ + color.r = 255; + color.g = color.b = 0; + } + + /* Apply saturation and value to R,G,B, pack into 32-bit result. */ + uint32_t v1 = 1 + val; /* 1 to 256; allows >> 8 instead of / 255. */ + uint16_t s1 = 1 + sat; /* 1 to 256; allows >> 8 instead of / 255. */ + uint8_t s2 = 255 - sat; /* 255 to 0 */ + + packed = ((((((color.r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | + (((((color.g * s1) >> 8) + s2) * v1) & 0xff00) | + (((((color.b * s1) >> 8) + s2) * v1) >> 8); + + return (RGB_s) { .r = ((packed >> 16) & 0xFF), + .g = ((packed >> 8) & 0xFF), + .b = ((packed >> 0) & 0xFF) }; +} + +static void _led_ring_din_isr (void) +{ + _led_ring_din = true; +} + + diff --git a/src/bsp/led_ring.h b/src/bsp/led_ring.h new file mode 100644 index 0000000..67bb933 --- /dev/null +++ b/src/bsp/led_ring.h @@ -0,0 +1,60 @@ +/** + * @file led_ring.h + * @brief LED ring module + * @author Kenny Tran + * @date Feb 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef LED_RING_H_ +#define LED_RING_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +// #define LED_RING_PIXELS DT_PROP(DT_ALIAS(led_ring), chain_length) +#define LED_RING_PIXELS (24) +#define LED_RING_MAX_BRIGHTNESS (255) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e LED_RING_init (void); + +ERRNO_e LED_RING_clear (void); + +ERRNO_e LED_RING_set (uint16_t idx, uint8_t r, uint8_t g, uint8_t b); + +ERRNO_e LED_RING_bounds_set (uint16_t idx); + +ERRNO_e LED_RING_rainbow (uint16_t first_hue, int8_t reps, uint8_t sat, uint8_t brightness, bool gammify); + +ERRNO_e LED_RING_update (void); + +ERRNO_e LED_RING_enable (bool en); + +ERRNO_e LED_RING_din_received (bool *p_rx); + +void LED_RING_cli_cmd (int32_t argc, char **argv); + + +#endif /* LED_RING_H_ */ diff --git a/src/bsp/mcu.c b/src/bsp/mcu.c new file mode 100644 index 0000000..473f473 --- /dev/null +++ b/src/bsp/mcu.c @@ -0,0 +1,365 @@ +/** + * @file mcu.c + * @brief MCU module + * @author Kenny Tran + * @date Aug 16 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../lib/errno.h" +#include "../lib/crc.h" +#include "../lib/print.h" + +#include "mcu.h" + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _MCU_RAM_RETENTION_CRC_LEN (4) + +/* nRF52 RAM (really, RAM AHB slaves) are partitioned as: + * * Up to 8 blocks of two 4 KiBy byte "small" sections + * * A 9th block of with 32 KiBy "large" sections + * + * At time of writing the maximum number of large sections is 6, all + * within the first large block. Theoretically there could be more + * sections in the 9th block, and possibly more blocks. + */ +/* Inclusive address of RAM start */ +#define _MCU_SRAM_BEGIN (uintptr_t)DT_REG_ADDR(DT_NODELABEL(sram0)) + +/* Exclusive address of RAM end */ +#define _MCU_SRAM_END (_MCU_SRAM_BEGIN + (uintptr_t)DT_REG_SIZE(DT_NODELABEL(sram0))) + +/* Size of a controllable RAM section in the small blocks */ +#define _MCU_SMALL_SECTION_SIZE (4096) + +/* Number of controllable RAM sections in each of the lower blocks */ +#define _MCU_SMALL_SECTIONS_PER_BLOCK (2) + +/* Span of a small block */ +#define _MCU_SMALL_BLOCK_SIZE (_MCU_SMALL_SECTIONS_PER_BLOCK * _MCU_SMALL_SECTION_SIZE) + +/* Number of small blocks */ +#define _MCU_SMALL_BLOCK_COUNT (8) + +/* Span of the SRAM area covered by small sections */ +#define _MCU_SMALL_SECTION_SPAN (_MCU_SMALL_BLOCK_COUNT * _MCU_SMALL_BLOCK_SIZE) + +/* Inclusive address of the RAM range covered by large sections */ +#define _MCU_LARGE_SECTION_BEGIN (_MCU_SRAM_BEGIN + _MCU_SMALL_SECTION_SPAN) + +/* Size of a controllable RAM section in large blocks */ +#define _MCU_LARGE_SECTION_SIZE (32768) + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +__noinit static uint8_t _mcu_ram_data[MCU_RAM_RETENTION_LEN + _MCU_RAM_RETENTION_CRC_LEN]; +// another option: static uint8_t _mcu_ram_data[MCU_RAM_RETENTION_LEN + _MCU_RAM_RETENTION_CRC_LEN] __attribute__((section(".noinit"))); + static bool _mcu_init = false; + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +/* Set or clear RAM retention in SYSTEM_OFF for the provided object. + * + * @note This only works for nRF52 with the POWER module. The other + * Nordic chips use a different low-level API, which is not currently + * used by this function. + * + * @param ptr pointer to the start of the retainable object + * + * @param len length of the retainable object + * + * @param enable true to enable retention, false to clear retention + */ +static void _mcu_ram_retain (const void *ptr, size_t len, bool enable); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e MCU_init (void) +{ + if (_mcu_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + else if (CRC_init () < ERRNO_SUCCESS) + { + return ERRNO_CRC_FAILURE; + } + + _mcu_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e MCU_reset (bool cold) +{ + if (!_mcu_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (cold) + { + sys_reboot (SYS_REBOOT_COLD); + } + else + { + sys_reboot (SYS_REBOOT_WARM); + } + + return ERRNO_SUCCESS; +} + +ERRNO_e MCU_sleep (void) +{ + if (!_mcu_init) + { + return ERRNO_NOT_INITIALIZED; + } + + sys_poweroff (); + + return ERRNO_SUCCESS; +} + +ERRNO_e MCU_ram_retain (uint8_t *p_data, uint16_t len, bool enable) +{ + uint32_t checksum; + + if (!_mcu_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (len > MCU_RAM_RETENTION_LEN) + { + return ERRNO_LIMIT_REACHED; + } + + if (enable) + { + /* Calculate CRC checksum of data. */ + if (CRC_32_calculate (p_data, len, CRC_32_CHECKSUM_INIT, &checksum)) + { + return ERRNO_CRC_FAILURE; + } + + /* Copy data to retention buffer. */ + memcpy (_mcu_ram_data, p_data, len); + /* Append CRC checksum. */ + memcpy (&_mcu_ram_data[len], &checksum, _MCU_RAM_RETENTION_CRC_LEN); + } + + _mcu_ram_retain (_mcu_ram_data, sizeof (_mcu_ram_data), enable); + + return ERRNO_SUCCESS; +} + +ERRNO_e MCU_ram_fetch (uint8_t *p_dest, uint16_t len) +{ + uint32_t checksum; + + if (!_mcu_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (len > MCU_RAM_RETENTION_LEN) + { + return ERRNO_LIMIT_REACHED; + } + + /* Calculate CRC checksum of data. */ + if (CRC_32_calculate (_mcu_ram_data, len, CRC_32_CHECKSUM_INIT, &checksum)) + { + return ERRNO_CRC_FAILURE; + } + + if (checksum != *(uint32_t *) &_mcu_ram_data[len]) + { + memset (p_dest, 0, len); + return ERRNO_CRC_FAILURE; + } + + /* Copy data to retention buffer. */ + memcpy (p_dest, _mcu_ram_data, len); + + return ERRNO_SUCCESS; +} + +void MCU_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 1) + { + if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + PRINT (INFO, "Ret code : %d\r\n", MCU_init ()); + } + else if (!strcmp (argv[0], "--sleep") || !strcmp (argv[0], "-s")) + { + PRINT (INFO, "Ret code : %d\r\n", MCU_sleep ()); + } + else + { + goto USAGE; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "--reset") || !strcmp (argv[0], "-r")) + { + if (!strcmp (argv[1], "cold")) + { + PRINT (INFO, "Ret code : %d\r\n", MCU_reset (true)); + } + else if (!strcmp (argv[1], "warm")) + { + PRINT (INFO, "Ret code : %d\r\n", MCU_reset (false)); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else if (argc == 3) + { + + if (!strcmp (argv[0], "--ram")) + { + if (!strcmp (argv[1], "fetch")) + { + uint8_t len = (uint8_t) strtoul (argv[2], NULL, 0); + uint8_t buf[len + 1]; + + buf[len] = 0; // NULL termination + + PRINT (INFO, "Ret code : %d\r\n", MCU_ram_fetch (buf, len)); + + PRINT (INFO, "Ram data : %s\r\n", buf); + } + else if (!strcmp (argv[1], "write")) + { + PRINT (INFO, "Ret code : %d\r\n", MCU_ram_retain (argv[2], strlen (argv[2]), true)); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: mcu [OPTION...]\r\n"); + PRINT (INFO, " -i | --init Initializes MCU\r\n"); + PRINT (INFO, " -r | --reset Resets MCU\r\n"); + PRINT (INFO, " <%%s> [cold, warm]\r\n"); + PRINT (INFO, " -s | --sleep Puts MCU to sleep\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _mcu_ram_retain (const void *ptr, size_t len, bool enable) +{ + uintptr_t addr = (uintptr_t)ptr; + uintptr_t addr_end = addr + len; + + /* Error if the provided range is empty or doesn't lie + * entirely within the SRAM address space. + */ + if ((len == 0U) || (addr < _MCU_SRAM_BEGIN) || (addr > (_MCU_SRAM_END - len))) + { + PRINT (ERROR, "Range is empty or doesn't lie entirely within SRAM space\r\n"); + return; + } + + /* Iterate over each section covered by the range, setting the + * corresponding RAM OFF retention bit in the parent block. + */ + do { + uintptr_t block_base = _MCU_SRAM_BEGIN; + uint32_t section_size = _MCU_SMALL_SECTION_SIZE; + uint32_t sections_per_block = _MCU_SMALL_SECTIONS_PER_BLOCK; + bool is_large = (addr >= _MCU_LARGE_SECTION_BEGIN); + uint8_t block = 0; + uint32_t section; + uint32_t section_mask; + + if (is_large) + { + block = 8; + block_base = _MCU_LARGE_SECTION_BEGIN; + section_size = _MCU_LARGE_SECTION_SIZE; + + /* RAM[x] supports only 16 sections, each its own bit + * for POWER (0..15) and RETENTION (16..31). We don't + * know directly how many sections are present, so + * assume they all are; the true limit will be + * determined by the SRAM size. + */ + sections_per_block = 16; + } + + section = (addr - block_base) / section_size; + + if (section >= sections_per_block) + { + block += section / sections_per_block; + section %= sections_per_block; + } + + section_mask = (POWER_RAM_POWERSET_S0RETENTION_On << (section + POWER_RAM_POWERSET_S0RETENTION_Pos)); + + if (enable) + { + nrf_power_rampower_mask_on (NRF_POWER, block, section_mask); + } + else + { + nrf_power_rampower_mask_off (NRF_POWER, block, section_mask); + } + + /* Move to the first address in the next section. */ + addr += section_size - (addr % section_size); + } while (addr < addr_end); +} diff --git a/src/bsp/mcu.h b/src/bsp/mcu.h new file mode 100644 index 0000000..029a9d0 --- /dev/null +++ b/src/bsp/mcu.h @@ -0,0 +1,53 @@ +/** + * @file mcu.h + * @brief MCU module + * @author Kenny Tran + * @date Aug 16 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef MCU_H_ +#define MCU_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define MCU_RAM_RETENTION_LEN (512) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e MCU_init (void); + +ERRNO_e MCU_reset (bool cold); + +ERRNO_e MCU_sleep (void); + +ERRNO_e MCU_ram_retain (uint8_t *p_data, uint16_t len, bool enable); + +ERRNO_e MCU_ram_fetch (uint8_t *p_dest, uint16_t len); + +void MCU_cli_cmd (int32_t argc, char **argv); + + +#endif /* MCU_H_ */ diff --git a/src/bsp/nvm_ext.c b/src/bsp/nvm_ext.c new file mode 100644 index 0000000..c4acfe1 --- /dev/null +++ b/src/bsp/nvm_ext.c @@ -0,0 +1,736 @@ +/** + * @file nvm_ext.c + * @brief Non-volatile memory external module : W25Q16JV + * @author Kenny Tran + * @date Feb 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../wrappers/spi.h" + +#include "nvm_ext.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _NVM_EXT_MFG_ID (0xC8) /* C8 for GD25Q1C */ +#define _NVM_EXT_DEV_ID (0x4015) +// #define _NVM_EXT_DEV_ID (0x4015) /* 0x4015 for W25Q16JV-IQ/JQ +// 0x7015 for W25Q16JV-IM/JM */ +#define _NVM_EXT_JEDEC_ID ((_NVM_EXT_MFG_ID << 16) | _NVM_EXT_DEV_ID) + +/** + * @brief For use with reading NVM external status bit. + * */ +#define _NVM_EXT_STATUS_BUSY_MASK (0x01) +#define _NVM_EXT_STATUS_BUSY (0x01) + +/** + * @brief Value after byte is erased. Nature of a NOR NVM external chip. + */ +#define _NVM_EXT_ERASED_BYTE_VAL (0xFF) + +/** + * @brief Macro used for splitting larger values in to byte-size pieces. + */ +#define _NVM_EXT_SPLIT_BYTE(x, offset) (0xFF & (x >> (8 * offset))) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct _nvm_ext_reg +{ + uint32_t opcode; + uint8_t tx_len; /* Note: Includes dummy bytes */ + uint8_t rx_len; +} _nvm_ext_reg_s; + +typedef struct _nvm_ext_regs +{ + _nvm_ext_reg_s wr_enable; + _nvm_ext_reg_s sr_wr_enable; + _nvm_ext_reg_s wr_disable; + _nvm_ext_reg_s pwr_down_release; + _nvm_ext_reg_s mfg_id; + _nvm_ext_reg_s jedec_id; + _nvm_ext_reg_s unique_id; + _nvm_ext_reg_s rd_data; + _nvm_ext_reg_s fast_rd_data; + _nvm_ext_reg_s page_program; + _nvm_ext_reg_s sector_erase_4k; + _nvm_ext_reg_s block_erase_32k; + _nvm_ext_reg_s block_erase_64k; + _nvm_ext_reg_s chip_erase; + _nvm_ext_reg_s rd_status_1; + _nvm_ext_reg_s wr_status_1; + _nvm_ext_reg_s rd_status_2; + _nvm_ext_reg_s wr_status_2; + _nvm_ext_reg_s rd_status_3; + _nvm_ext_reg_s wr_status_3; + _nvm_ext_reg_s sfdp; + _nvm_ext_reg_s erase_security; + _nvm_ext_reg_s program_security; + _nvm_ext_reg_s rd_security; + _nvm_ext_reg_s gbl_block_lock; + _nvm_ext_reg_s gbl_block_unlock; + _nvm_ext_reg_s rd_block_lock; + _nvm_ext_reg_s individual_block_lock; + _nvm_ext_reg_s individual_block_unlock; + _nvm_ext_reg_s erase_suspend; + _nvm_ext_reg_s erase_resume; + _nvm_ext_reg_s pwr_down; + _nvm_ext_reg_s rst_enable; + _nvm_ext_reg_s rst_device; +} _nvm_ext_regs_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _nvm_ext_init = false; +static const _nvm_ext_regs_s _nvm_ext_reg_table = { + .wr_enable = (_nvm_ext_reg_s) { .opcode = 0x06, .tx_len = 1, .rx_len = 0 }, + .sr_wr_enable = (_nvm_ext_reg_s) { .opcode = 0x50, .tx_len = 1, .rx_len = 0 }, + .wr_disable = (_nvm_ext_reg_s) { .opcode = 0x04, .tx_len = 1, .rx_len = 0 }, + .pwr_down_release = (_nvm_ext_reg_s) { .opcode = 0xAB, .tx_len = 4, .rx_len = 1 }, + .mfg_id = (_nvm_ext_reg_s) { .opcode = 0x90, .tx_len = 4, .rx_len = 2 }, + .jedec_id = (_nvm_ext_reg_s) { .opcode = 0x9F, .tx_len = 1, .rx_len = 3 }, + .unique_id = (_nvm_ext_reg_s) { .opcode = 0x4B, .tx_len = 5, .rx_len = 1 }, + .rd_data = (_nvm_ext_reg_s) { .opcode = 0x03, .tx_len = 4, .rx_len = 1 }, + .fast_rd_data = (_nvm_ext_reg_s) { .opcode = 0x0B, .tx_len = 5, .rx_len = 1 }, + .page_program = (_nvm_ext_reg_s) { .opcode = 0x02, .tx_len = 4, .rx_len = 0 }, + .sector_erase_4k = (_nvm_ext_reg_s) { .opcode = 0x20, .tx_len = 4, .rx_len = 0 }, + .block_erase_32k = (_nvm_ext_reg_s) { .opcode = 0x52, .tx_len = 4, .rx_len = 0 }, + .block_erase_64k = (_nvm_ext_reg_s) { .opcode = 0xD8, .tx_len = 4, .rx_len = 0 }, + .chip_erase = (_nvm_ext_reg_s) { .opcode = 0x60, .tx_len = 1, .rx_len = 0 }, + .rd_status_1 = (_nvm_ext_reg_s) { .opcode = 0x05, .tx_len = 1, .rx_len = 1 }, + .wr_status_1 = (_nvm_ext_reg_s) { .opcode = 0x01, .tx_len = 2, .rx_len = 0 }, + .rd_status_2 = (_nvm_ext_reg_s) { .opcode = 0x35, .tx_len = 1, .rx_len = 1 }, + .wr_status_2 = (_nvm_ext_reg_s) { .opcode = 0x31, .tx_len = 2, .rx_len = 0 }, + .rd_status_3 = (_nvm_ext_reg_s) { .opcode = 0x15, .tx_len = 1, .rx_len = 1 }, + .wr_status_3 = (_nvm_ext_reg_s) { .opcode = 0x11, .tx_len = 2, .rx_len = 0 }, + .sfdp = (_nvm_ext_reg_s) { .opcode = 0x5A, .tx_len = 5, .rx_len = 1 }, + .erase_security = (_nvm_ext_reg_s) { .opcode = 0x44, .tx_len = 4, .rx_len = 0 }, + .program_security = (_nvm_ext_reg_s) { .opcode = 0x42, .tx_len = 6, .rx_len = 0 }, + .rd_security = (_nvm_ext_reg_s) { .opcode = 0x48, .tx_len = 5, .rx_len = 1 }, + .gbl_block_lock = (_nvm_ext_reg_s) { .opcode = 0x7E, .tx_len = 1, .rx_len = 0 }, + .gbl_block_unlock = (_nvm_ext_reg_s) { .opcode = 0x98, .tx_len = 1, .rx_len = 0 }, + .rd_block_lock = (_nvm_ext_reg_s) { .opcode = 0x3D, .tx_len = 4, .rx_len = 1 }, + .individual_block_lock = (_nvm_ext_reg_s) { .opcode = 0x36, .tx_len = 4, .rx_len = 0 }, + .individual_block_unlock = (_nvm_ext_reg_s) { .opcode = 0x39, .tx_len = 4, .rx_len = 0 }, + .erase_suspend = (_nvm_ext_reg_s) { .opcode = 0x75, .tx_len = 1, .rx_len = 0 }, + .erase_resume = (_nvm_ext_reg_s) { .opcode = 0x7A, .tx_len = 1, .rx_len = 0 }, + .pwr_down = (_nvm_ext_reg_s) { .opcode = 0xB9, .tx_len = 1, .rx_len = 0 }, + .rst_enable = (_nvm_ext_reg_s) { .opcode = 0x66, .tx_len = 1, .rx_len = 0 }, + .rst_device = (_nvm_ext_reg_s) { .opcode = 0x99, .tx_len = 1, .rx_len = 0 } +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static uint32_t _nvm_ext_jedec_id_get (void); +static void _nvm_ext_write_enable (void); +static void _nvm_ext_write_disable (void); +static void _nvm_wait_til_ready (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e NVM_EXT_init (void) +{ + ERRNO_e err = ERRNO_FAILURE; + + if (_nvm_ext_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (SPI_init () != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + /* SPI can sometimes read back all 0xFFs if transferring right after init. + Delay to work around. */ + k_busy_wait(10000); + + /* Set init flag so we only execute once. */ + /* NOTE : should be set before calling any NVM_EXT public functions since they will check for this flag. */ + _nvm_ext_init = true; + + /* Release power down. */ + err = NVM_EXT_release_power_down (); + if (err != ERRNO_SUCCESS) + { + return err; + } + + /* Check JEDEC ID to verify flash chip. */ + if (_nvm_ext_jedec_id_get () != (uint32_t) _NVM_EXT_JEDEC_ID) + { + return ERRNO_ID_FAILURE; + } + + /* Disable write until we are ready to use. */ + _nvm_ext_write_disable (); + + return ERRNO_SUCCESS; +} + +ERRNO_e NVM_EXT_power_down (void) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.pwr_down; + uint8_t tx_buf[reg.tx_len]; + + if (!_nvm_ext_init) + { + return ERRNO_NOT_INITIALIZED; + } + + tx_buf[0] = reg.opcode; + + /* Write power down register. */ + if (SPI_xfer (tx_buf, sizeof (tx_buf), NULL, 0) != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e NVM_EXT_release_power_down (void) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.pwr_down_release; + uint8_t tx_buf[reg.tx_len]; + uint8_t rx_buf[reg.rx_len]; + + if (!_nvm_ext_init) + { + return ERRNO_NOT_INITIALIZED; + } + + tx_buf[0] = reg.opcode; + + /* Write release power down register. ID returned, check optional. */ + if (SPI_xfer (tx_buf, sizeof (tx_buf), rx_buf, sizeof (rx_buf)) != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e NVM_EXT_erase_all (void) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.chip_erase; + uint8_t tx_buf[reg.tx_len]; + + if (!_nvm_ext_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Write enable command must set before performing an erase. */ + _nvm_ext_write_enable (); + + tx_buf[0] = reg.opcode; + + /* Write chip erase register. */ + if (SPI_xfer (tx_buf, sizeof (tx_buf), NULL, 0) != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + /* Wait for erase to finish. */ + _nvm_wait_til_ready (); + + return ERRNO_SUCCESS; +} + +ERRNO_e NVM_EXT_erase_block (uint32_t addr, NVM_EXT_block_size_e block_size) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.sector_erase_4k; + uint8_t tx_buf[reg.tx_len]; + + if (!_nvm_ext_init) + { + return ERRNO_NOT_INITIALIZED; + } + /* Perform checks on input parameters. */ + else if (addr >= NVM_EXT_SIZE) + { + return ERRNO_INVALID_PARAM; + } + + /* Write enable command must set before performing an erase. */ + _nvm_ext_write_enable (); + + /* Split up address into bytes. */ + tx_buf[1] = _NVM_EXT_SPLIT_BYTE (addr, 2); /* A23 - A16 */ + tx_buf[2] = _NVM_EXT_SPLIT_BYTE (addr, 1); /* A15 - A8 */ + tx_buf[3] = _NVM_EXT_SPLIT_BYTE (addr, 0); /* A7 - A0 */ + + /* Perform a parameter check and set correct opcode. */ + switch (block_size) + { + case NVM_EXT_BLOCK_SIZE_4KB: + tx_buf[0] = _nvm_ext_reg_table.sector_erase_4k.opcode; + break; + + case NVM_EXT_BLOCK_SIZE_32KB: + tx_buf[0] = _nvm_ext_reg_table.block_erase_32k.opcode; + break; + + case NVM_EXT_BLOCK_SIZE_64KB: + tx_buf[0] = _nvm_ext_reg_table.block_erase_64k.opcode; + break; + + default: + return ERRNO_INVALID_PARAM; + } + + /* Write block / sector erase register. */ + if (SPI_xfer (tx_buf, sizeof (tx_buf), NULL, 0) != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + /* Wait for erase to finish and measure elapsed time. */ + #ifdef CONFIG_DBG_STATS + uint32_t t_start = k_uptime_get_32(); + #endif + _nvm_wait_til_ready (); + #ifdef CONFIG_DBG_STATS + uint32_t elapsed = k_uptime_get_32() - t_start; + PRINT(INFO, "NVM_EXT_erase_block: addr=0x%06X size=%uKB took %u ms\r\n", + addr, (block_size == NVM_EXT_BLOCK_SIZE_4KB) ? 4 : (block_size == NVM_EXT_BLOCK_SIZE_32KB) ? 32 : 64, elapsed); + #endif + + return ERRNO_SUCCESS; +} + +ERRNO_e NVM_EXT_erase_range (uint32_t start_addr, uint32_t end_addr) +{ + /* To keep things simple, range erasing will use always use a block size of 4 Kb. */ + ERRNO_e err; + uint32_t i; + uint8_t data_start_buf[NVM_EXT_BLOCK_SIZE_4KB - 1]; + uint8_t data_end_buf [NVM_EXT_BLOCK_SIZE_4KB - 1]; + uint16_t block_start = start_addr / NVM_EXT_BLOCK_SIZE_4KB; + uint16_t block_end = end_addr / NVM_EXT_BLOCK_SIZE_4KB; + uint16_t data_start_addr = block_start * NVM_EXT_BLOCK_SIZE_4KB; + uint16_t data_end_addr = end_addr + 1; + uint16_t data_start_len = start_addr & (NVM_EXT_BLOCK_SIZE_4KB - 1); + uint16_t data_end_len = (~(end_addr % NVM_EXT_BLOCK_SIZE_4KB)) & (NVM_EXT_BLOCK_SIZE_4KB - 1); + + /* Perform checks on input parameters. */ + if ((start_addr >= NVM_EXT_SIZE) || (end_addr >= NVM_EXT_SIZE) || (start_addr > end_addr)) + { + return ERRNO_INVALID_PARAM; + } + + if (data_start_len != 0) + { + /* Read and save starting data in block section that is out of erase range. */ + err = NVM_EXT_read (data_start_addr, data_start_buf, data_start_len); + if (err != ERRNO_SUCCESS) + { + return err; + } + } + + if (data_end_len != 0) + { + /* Read and save ending data in block section that is out of erase range. */ + err = NVM_EXT_read (data_end_addr, data_end_buf, data_end_len); + if (err != ERRNO_SUCCESS) + { + return err; + } + } + + /* Loop through blocks and perform erases. */ + for (i = block_start; i <= block_end; i++) + { + err = NVM_EXT_erase_block (i * NVM_EXT_BLOCK_SIZE_4KB, NVM_EXT_BLOCK_SIZE_4KB); + if (err != ERRNO_SUCCESS) + { + return err; + } + } + + /* Rewrite back starting data our of the desired erase range that got erased. */ + if (data_start_len != 0) + { + err = NVM_EXT_write (data_start_addr, data_start_buf, data_start_len); + if (err != ERRNO_SUCCESS) + { + return err; + } + } + + /* Rewrite back ending data our of the desired erase range that got erased. */ + if (data_end_len != 0) + { + err = NVM_EXT_write (data_end_addr, data_end_buf, data_end_len); + if (err != ERRNO_SUCCESS) + { + return err; + } + } + + return ERRNO_SUCCESS; +} + +ERRNO_e NVM_EXT_read (uint32_t addr, uint8_t *p_dest, uint32_t len) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.rd_data; + uint8_t tx_buf[reg.tx_len]; + + /* Perform checks on input parameter. */ + if ((addr >= NVM_EXT_SIZE) || (len == 0) || (addr + len) > NVM_EXT_SIZE) + { + return ERRNO_INVALID_PARAM; + } + else if (p_dest == NULL) + { + return ERRNO_BAD_POINTER; + } + + /* Split up address into len. */ + tx_buf[0] = reg.opcode; + tx_buf[1] = _NVM_EXT_SPLIT_BYTE (addr, 2); /* A23 - A16 */ + tx_buf[2] = _NVM_EXT_SPLIT_BYTE (addr, 1); /* A15 - A8 */ + tx_buf[3] = _NVM_EXT_SPLIT_BYTE (addr, 0); /* A7 - A0 */ + + /* Perform read. */ + if (SPI_xfer (tx_buf, sizeof (tx_buf), p_dest, len) != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e NVM_EXT_write (uint32_t addr, uint8_t *p_src, uint32_t len) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.page_program; + uint8_t tx_buf[reg.tx_len + len]; + + /* The calculated number of len to write */ + uint32_t cnt; + + /* Perform checks on input parameter. */ + if (addr >= NVM_EXT_SIZE || len == 0 || (addr + len) > NVM_EXT_SIZE) + { + return ERRNO_INVALID_PARAM; + } + + if (p_src == NULL) + { + return ERRNO_BAD_POINTER; + } + + while (len > 0) + { + /* Calculate the number of bytes to write (bytes until end of page) */ + cnt = NVM_EXT_PAGE_SIZE - (addr % NVM_EXT_PAGE_SIZE); + + /* Do not span pages if the data will fit in the remaining page space. */ + if (cnt >= len) + { + cnt = len; + } + + /* Write enable command must set before performing a write. */ + _nvm_ext_write_enable (); + + tx_buf[0] = reg.opcode; + tx_buf[1] = _NVM_EXT_SPLIT_BYTE (addr, 2); /* A23 - A16 */ + tx_buf[2] = _NVM_EXT_SPLIT_BYTE (addr, 1); /* A15 - A8 */ + tx_buf[3] = _NVM_EXT_SPLIT_BYTE (addr, 0); /* A7 - A0 */ + + /* Copy data buffer into tx_buffer. */ + memcpy (&tx_buf[reg.tx_len], p_src, cnt); + + /* Perform write. */ + if (SPI_xfer (tx_buf, (reg.tx_len + cnt), NULL, 0) != ERRNO_SUCCESS) + { + return ERRNO_SPI_FAILURE; + } + + /* Decrement byte count, increment address, and increment source pointer. */ + len -= cnt; + addr += cnt; + p_src += cnt; + + /* Wait for write to complete. */ + _nvm_wait_til_ready (); + } + + return ERRNO_SUCCESS; +} + +void NVM_EXT_cli_cmd (int32_t argc, char **argv) +{ + uint16_t i; + uint8_t buf[4096]; + uint32_t start_addr; + uint8_t data; + uint16_t n_bytes; + ERRNO_e err; + + if (argc == 0) + { + PRINT (INFO, "NVM EXTERNAL\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "JEDEC ID : 0x%06X\r\n", _nvm_ext_jedec_id_get ()); + PRINT (INFO, "Size (bytes) : %d\r\n", NVM_EXT_SIZE); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--help") || !strcmp (argv[0], "?")) + { + goto USAGE; + } + else + { + start_addr = (uint32_t) strtoul (argv[0], NULL, 0); + + err = NVM_EXT_read (start_addr, buf, 1); + PRINT (INFO, "Ret code : %d\r\n", err); + + if (err == ERRNO_SUCCESS) + { + PRINT (INFO, "Addr: 0x%06X = %d\r\n", start_addr, buf[0]); + } + } + } + else if (argc == 2) + { + if (!strcmp (argv[1], "--erase") || !strcmp (argv[1], "-e")) + { + if (!strcmp (argv[0], "all")) + { + err = NVM_EXT_erase_all (); + PRINT (INFO, "Ret code : %d\r\n", err); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else if (argc == 3) + { + if (!strcmp (argv[1], "--read") || !strcmp (argv[1], "-r")) + { + start_addr = (uint32_t) strtoul (argv[0], NULL, 0); + n_bytes = (uint16_t) strtoul (argv[2], NULL, 0); + err = NVM_EXT_read (start_addr, buf, n_bytes); + + PRINT (INFO, "Ret code : %d\r\n", err); + + if (err == ERRNO_SUCCESS) + { + for (i = 0; i < n_bytes; i++) + { + PRINT (INFO, "Addr: 0x%06X = %d\r\n", (start_addr + i), buf[i]); + } + } + } + else if (!strcmp (argv[1], "--set") || !strcmp (argv[1], "-s")) + { + start_addr = (uint32_t) strtoul (argv[0], NULL, 0); + data = (uint8_t) strtoul (argv[2], NULL, 0); + err = NVM_EXT_write (start_addr, &data, 1); + + PRINT (INFO, "Ret code : %d\r\n", err); + } + else if (!strcmp (argv[1], "--erase") || !strcmp (argv[1], "-e")) + { + uint32_t end_addr; + + start_addr = (uint32_t) strtoul (argv[0], NULL, 0); + end_addr = (uint32_t) strtoul (argv[2], NULL, 0) + start_addr; + err = NVM_EXT_erase_range (start_addr, end_addr); + + PRINT (INFO, "Ret code : %d\r\n", err); + + if (err == ERRNO_SUCCESS) + { + PRINT (INFO, "Addr: 0x%06X - 0x%06X erased\r\n", start_addr, end_addr); + } + } + else if (!strcmp (argv[1], "-type") || !strcmp (argv[1], "-t")) + { + start_addr = (uint32_t) strtoul (argv[0], NULL, 0); + err = NVM_EXT_read (start_addr, buf, 4); + + PRINT (INFO, "Ret code : %d\r\n", err); + + if (err == ERRNO_SUCCESS) + { + if (!strcmp (argv[2], "u8")) + { + PRINT (INFO, "%u\r\n", *(uint8_t *) buf); + } + else if (!strcmp (argv[2], "u16")) + { + PRINT (INFO, "%u\r\n", *(uint16_t *) buf); + } + else if (!strcmp (argv[2], "u32")) + { + PRINT (INFO, "%u\r\n", *(uint32_t *) buf); + } + else if (!strcmp (argv[2], "s8")) + { + PRINT (INFO, "%d\r\n", *(int8_t *) buf); + } + else if (!strcmp (argv[2], "s16")) + { + PRINT (INFO, "%d\r\n", *(int16_t *) buf); + } + else if (!strcmp (argv[2], "s32")) + { + PRINT (INFO, "%d\r\n", *(int32_t *) buf); + } + else if (!strcmp (argv[2], "float")) + { + PRINT (INFO, "%f\r\n", *(float *) buf); + } + else + { + PRINT (INFO, "Invalid argument\r\n"); + } + } + } + else + { + goto USAGE; + } + } + else if (argc == 4) + { + if (!strcmp (argv[1], "--set") || !strcmp (argv[1], "-s")) + { + start_addr = (uint32_t) strtoul (argv[0], NULL, 0); + data = (uint8_t) strtoul (argv[2], NULL, 0); + n_bytes = (uint32_t) strtoul (argv[3], NULL, 0); + + memset (buf, data, n_bytes); + + err = NVM_EXT_write (start_addr, buf, n_bytes); + PRINT (INFO, "Ret code : %d\r\n", err); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: nvm [OPTION...]\r\n"); + PRINT (INFO, " -r | --read reads from flash\r\n"); + PRINT (INFO, " <%%d> number of bytes [1-255]\r\n"); + PRINT (INFO, " -s |--set write value to flash\r\n"); + PRINT (INFO, " <%%d> value to set\r\n"); + PRINT (INFO, " -e | --erase erase flash contents\r\n"); + PRINT (INFO, " <%%d> bytes to erase\r\n"); + PRINT (INFO, " -t | --type typecast flash content\r\n"); + PRINT (INFO, " <%%s> [u8, u16, u32, s8, s16, s32, float]\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static uint32_t _nvm_ext_jedec_id_get (void) +{ + uint32_t jedec_id = 0; + _nvm_ext_reg_s reg = _nvm_ext_reg_table.jedec_id; + uint8_t tx_buf[reg.tx_len]; + uint8_t rx_buf[reg.rx_len]; + + tx_buf[0] = reg.opcode; + + /* Read JEDEC ID register. */ + if (SPI_xfer (tx_buf, sizeof (tx_buf), rx_buf, sizeof (rx_buf)) != ERRNO_SUCCESS) + { + PRINT (ERROR, "SPI failure reading JEDEC ID.\r\n"); + } + + jedec_id = (((uint32_t) rx_buf[0] << 16) | ((uint32_t) rx_buf[1] << 8) | (uint32_t) rx_buf[2]); + + return jedec_id; +} + +static void _nvm_ext_write_enable (void) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.wr_enable; + uint8_t tx_buf[reg.tx_len]; + + tx_buf[0] = reg.opcode; + + if (SPI_xfer (tx_buf, sizeof (tx_buf), NULL, 0) != ERRNO_SUCCESS) + { + PRINT (ERROR, "SPI failure transferring write enable.\r\n"); + } +} + +static void _nvm_ext_write_disable (void) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.wr_disable; + uint8_t tx_buf[reg.tx_len]; + + tx_buf[0] = reg.opcode; + + if (SPI_xfer (tx_buf, sizeof (tx_buf), NULL, 0) != ERRNO_SUCCESS) + { + PRINT (ERROR, "SPI failure transferring write enable.\r\n"); + } +} + +static void _nvm_wait_til_ready (void) +{ + _nvm_ext_reg_s reg = _nvm_ext_reg_table.rd_status_1; + uint8_t tx_buf[reg.tx_len]; + uint8_t rx_buf[reg.rx_len]; + + tx_buf[0] = reg.opcode; + + /* Wait for erase to complete by polling READYn/BUSY bit. */ + do + { + if (SPI_xfer (tx_buf, sizeof (tx_buf), rx_buf, sizeof (rx_buf)) != ERRNO_SUCCESS) + { + PRINT (ERROR, "SPI failure polling ready bit.\r\n"); + } + } while ((rx_buf[0] & _NVM_EXT_STATUS_BUSY_MASK) == _NVM_EXT_STATUS_BUSY); +} diff --git a/src/bsp/nvm_ext.h b/src/bsp/nvm_ext.h new file mode 100644 index 0000000..23ace35 --- /dev/null +++ b/src/bsp/nvm_ext.h @@ -0,0 +1,73 @@ +/** + * @file nvm_ext.h + * @brief Non-volatile memory external module : W25Q16JV + * @author Kenny Tran + * @date Feb 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef NVM_EXT_H_ +#define NVM_EXT_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define NVM_EXT_SCK_PIN BSP_NVM_EXT_SCK_PIN +#define NVM_EXT_MOSI_PIN BSP_NVM_EXT_MOSI_PIN +#define NVM_EXT_MISO_PIN BSP_NVM_EXT_MISO_PIN +#define NVM_EXT_CS_PIN BSP_NVM_EXT_CS_PIN /* Active-low */ + +#define NVM_EXT_START_ADDR (0x000000) /* Flash start address */ +#define NVM_EXT_PAGE_SIZE (256) /* Flash page size */ + +#define NVM_EXT_SIZE (0x200000) /* Flash size in bytes (2MB) */ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum NVM_EXT_block_size +{ + NVM_EXT_BLOCK_SIZE_4KB = 4096, + NVM_EXT_BLOCK_SIZE_32KB = 32768, + NVM_EXT_BLOCK_SIZE_64KB = 65536 +} NVM_EXT_block_size_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e NVM_EXT_init (void); + +ERRNO_e NVM_EXT_power_down (void); + +ERRNO_e NVM_EXT_release_power_down (void); + +ERRNO_e NVM_EXT_erase_all (void); + +ERRNO_e NVM_EXT_erase_block (uint32_t addr, NVM_EXT_block_size_e block_size); + +ERRNO_e NVM_EXT_erase_range (uint32_t start_addr, uint32_t end_addr); + +ERRNO_e NVM_EXT_read (uint32_t addr, uint8_t *p_dest, uint32_t len); + +ERRNO_e NVM_EXT_write (uint32_t addr, uint8_t *p_src, uint32_t len); + +void NVM_EXT_cli_cmd (int32_t argc, char **argv); + + +#endif /* NVM_EXT_H_ */ diff --git a/src/bsp/power.c b/src/bsp/power.c new file mode 100644 index 0000000..bbf2d7d --- /dev/null +++ b/src/bsp/power.c @@ -0,0 +1,161 @@ +/** + * @file battery.h + * @brief Battery module + * @author Kenny Tran + * @date Dec 02 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../wrappers/gpio.h" + +#include "power.h" + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _power_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e POWER_init (void) +{ + if (_power_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (GPIO_init () < ERRNO_SUCCESS) + { + return ERRNO_GPIO_FAILURE; + } + + if (GPIO_cfg (GPIO_PIN_5V_ENABLE, GPIO_DIRECTION_OUT, GPIO_PULLDOWN, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, NULL)) + { + return ERRNO_GPIO_FAILURE; + } + + _power_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e POWER_enable (bool en) +{ + if (!_power_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (GPIO_level_set (GPIO_PIN_5V_ENABLE, en) != ERRNO_SUCCESS) + { + return ERRNO_GPIO_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e POWER_is_enabled (bool *p_enabled) +{ + if (!_power_init) + { + return ERRNO_NOT_INITIALIZED; + } + + *p_enabled = GPIO_level_get (GPIO_PIN_5V_ENABLE); + + return ERRNO_SUCCESS; +} + +void POWER_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + bool enabled; + + PRINT (INFO, "POWER\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + (void) POWER_is_enabled (&enabled); + PRINT (INFO, "5V power is %s\r\n", (enabled ? "enabled" : "disabled")); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "-i") || !strcmp (argv[0], "--init")) + { + PRINT (INFO, "Ret code : %d\r\n", POWER_init ()); + } + else + { + goto USAGE; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-e") || !strcmp (argv[0], "--enable")) + { + if (!strcmp (argv[1], "true")) + { + PRINT (INFO, "Ret code : %d\r\n", POWER_enable (true)); + } + else if (!strcmp (argv[1], "false")) + { + PRINT (INFO, "Ret code : %d\r\n", POWER_enable (false)); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: power [OPTION...]\r\n"); + PRINT (INFO, " -i | --init Initializes TPS61230A 5V power module\r\n"); + PRINT (INFO, " -e | --enable Enables TPS61230A 5V power module\r\n"); + PRINT (INFO, " <%%b> true = enable\r\n"); + PRINT (INFO, " false = disable\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/bsp/power.h b/src/bsp/power.h new file mode 100644 index 0000000..2432b38 --- /dev/null +++ b/src/bsp/power.h @@ -0,0 +1,48 @@ +/** + * @file power.h + * @brief TPS61230A power module + * @author Kenny Tran + * @date Mar 07 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef POWER_H_ +#define POWER_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e POWER_init (void); + +ERRNO_e POWER_enable (bool en); + +ERRNO_e POWER_is_enabled (bool *p_enabled); + +void POWER_cli_cmd (int32_t argc, char **argv); + + +#endif /* POWER_H_ */ diff --git a/src/bsp/rtc.c b/src/bsp/rtc.c new file mode 100644 index 0000000..a334024 --- /dev/null +++ b/src/bsp/rtc.c @@ -0,0 +1,150 @@ +/** + * @file rtc.c + * @brief Real Time Counter Modules + * @author Kenny Tran + * @date Nov 04 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "rtc.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +/* Ticks per sec = clock frequency / (prescaler + 1) */ +#define _RTC_TICKS_TO_SECONDS(ticks) (ticks / (32768 / 4096)) + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static const struct device *_rtc_dev = DEVICE_DT_GET(DT_NODELABEL(rtc2)); +static bool _rtc_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e RTC_init (void) +{ + if (_rtc_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (!device_is_ready (_rtc_dev)) + { + return ERRNO_NOT_READY; + } + + _rtc_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e RTC_start (void) +{ + /* Start the counter (starts LFCLK if not running). */ + if (counter_start (_rtc_dev)) + { + return ERRNO_RTC_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e RTC_stop (void) +{ + /* Stop the counter. */ + if (counter_stop (_rtc_dev)) + { + return ERRNO_RTC_FAILURE; + } + + return ERRNO_SUCCESS; +} + +uint32_t RTC_seconds_elasped (void) +{ + uint32_t ticks; + + counter_get_value(_rtc_dev, &ticks); + + return _RTC_TICKS_TO_SECONDS(ticks); +} + +void RTC_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + uint32_t seconds = RTC_seconds_elasped (); + + PRINT (INFO, "RTC\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "%d seconds have elapsed since start.\r\n", seconds); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--init")) + { + PRINT (INFO, "Ret code : %d\r\n", RTC_init ()); + } + else if (!strcmp (argv[0], "--start")) + { + PRINT (INFO, "Ret code : %d\r\n", RTC_start ()); + } + else if (!strcmp (argv[0], "--stop")) + { + PRINT (INFO, "Ret code : %d\r\n", RTC_stop ()); + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: rtc [OPTION...]\r\n"); + PRINT (INFO, " --init Initializes rtc counter\r\n"); + PRINT (INFO, " --start Starts rtc counter\r\n"); + PRINT (INFO, " --stop Stops rtc counter\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/bsp/rtc.h b/src/bsp/rtc.h new file mode 100644 index 0000000..69ce05e --- /dev/null +++ b/src/bsp/rtc.h @@ -0,0 +1,50 @@ +/** + * @file rtc.h + * @brief Real Time Counter Modules + * @author Kenny Tran + * @date Nov 04 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef RTC_H_ +#define RTC_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e RTC_init (void); + +ERRNO_e RTC_start (void); + +ERRNO_e RTC_stop (void); + +uint32_t RTC_seconds_elasped (void); + +void RTC_cli_cmd (int32_t argc, char **argv); + + +#endif /* RTC_H_ */ diff --git a/src/bsp/wdt.c b/src/bsp/wdt.c new file mode 100644 index 0000000..51ceee3 --- /dev/null +++ b/src/bsp/wdt.c @@ -0,0 +1,175 @@ +/** + * @file wdt.c + * @brief Watchdog Timer Library + * @author Kenny Tran + * @date Jan 28 2026 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "wdt.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static const struct device *_wdt_dev; +static int _wdt_channel_id = -1; +static bool _wdt_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIOPNS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e WDT_init (void) +{ + int err; + struct wdt_timeout_cfg wdt_cfg; + + if (_wdt_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + /* Get watchdog device. */ + _wdt_dev = DEVICE_DT_GET(DT_ALIAS(watchdog0)); + if (!device_is_ready (_wdt_dev)) + { + return ERRNO_WDT_FAILURE; + } + + /* Configure timeout. */ + wdt_cfg.flags = WDT_FLAG_RESET_SOC; + wdt_cfg.window.min = 0; + wdt_cfg.window.max = WDT_TIMEOUT_MS; + wdt_cfg.callback = NULL; + + /* Install timeout. */ + _wdt_channel_id = wdt_install_timeout (_wdt_dev, &wdt_cfg); + if (_wdt_channel_id < 0) + { + return ERRNO_WDT_FAILURE; + } + + /* Start watchdog - CANNOT BE STOPPED. */ + err = wdt_setup (_wdt_dev, WDT_OPT_PAUSE_HALTED_BY_DBG); + if (err < 0) + { + return ERRNO_WDT_FAILURE; + } + + _wdt_init = true; + + return ERRNO_SUCCESS; +} + +void WDT_feed (void) +{ + if (_wdt_init) + { + wdt_feed (_wdt_dev, _wdt_channel_id); + } +} + +ERRNO_e WDT_was_reset (bool *p_was_reset) +{ + if (p_was_reset == NULL) + { + return ERRNO_BAD_POINTER; + } + + *p_was_reset = false; + +#if defined(NRF_POWER) + if (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk) + { + *p_was_reset = true; + NRF_POWER->RESETREAS = POWER_RESETREAS_DOG_Msk; /* Clear flag */ + } +#endif + + return ERRNO_SUCCESS; +} + +void WDT_cli_cmd (int32_t argc, char **argv) +{ + bool was_reset; + + if (argc == 0) + { + WDT_was_reset(&was_reset); + + PRINT (INFO, "WATCHDOG TIMER\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Initialized : %s\r\n", _wdt_init ? "true" : "false"); + PRINT (INFO, "Timeout : %d ms\r\n", WDT_TIMEOUT_MS); + PRINT (INFO, "Last reset : %s\r\n", was_reset ? "WATCHDOG" : "normal"); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i")) + { + PRINT (INFO, "Ret code: %d\r\n", WDT_init ()); + } + else if (!strcmp (argv[0], "--feed") || !strcmp (argv[0], "-f")) + { + WDT_feed (); + PRINT (INFO, "Fed watchdog\r\n"); + } + else if (!strcmp (argv[0], "--test") || !strcmp (argv[0], "-t")) + { + PRINT (INFO, "Stopping feeds - reset in %d ms...\r\n", WDT_TIMEOUT_MS); + + while (1) + { + /* Hang to trigger watchdog. */ + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: watchdog [OPTION]\r\n"); + PRINT (INFO, " -i | --init Initialize watchdog\r\n"); + PRINT (INFO, " -f | --feed Feed watchdog\r\n"); + PRINT (INFO, " -t | --test Test watchdog (triggers reset!)\r\n"); + } +} diff --git a/src/bsp/wdt.h b/src/bsp/wdt.h new file mode 100644 index 0000000..e23ee1c --- /dev/null +++ b/src/bsp/wdt.h @@ -0,0 +1,49 @@ +/** + * @file wdt.h + * @brief Watchdog Timer Library + * @author Kenny Tran + * @date Jan 28 2026 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef WDT_H_ +#define WDT_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define WDT_TIMEOUT_MS (1500) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e WDT_init (void); + +void WDT_feed (void); + +ERRNO_e WDT_was_reset (bool *p_was_reset); + +void WDT_cli_cmd (int32_t argc, char **argv); + + +#endif /* WDT_H_ */ diff --git a/src/lib/cli.c b/src/lib/cli.c new file mode 100644 index 0000000..a254606 --- /dev/null +++ b/src/lib/cli.c @@ -0,0 +1,333 @@ +/** + * @file cli.c + * @brief Command line interface + * @author Kenny Tran + * @date Apr 25 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include + +#include "../wrappers/uart.h" + +#include "cli.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _CLI_CR ('\r') +#define _CLI_LF ('\n') +#define _CLI_BS ('\b') +#define _CLI_NULL ('\0') +#define _CLI_SPACE (' ') + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct _cli +{ + char *cmd_str; + void (*cmd) (int32_t argc, char **argv); + bool debug; +} _cli_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _cli_init = false; +static _cli_s _cli_list[CLI_MAX_COMMANDS+3]; /* +3 for "help", "?", and "debug" */ +static uint8_t _cli_list_len = 0; +static char *_cli_tokens[CLI_MAX_ARGS]; +static char _cli_buf[CLI_MAX_BUF_LEN]; +static CLI_cmd_e _cli_cmd_mode = CLI_CMD_APP; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _cli_line_clear (uint8_t n_chars); +static uint8_t _cli_tokens_parse (char *p_buf); +static bool _cli_chars_parse (void); +static void _cli_cmd_execute (void); +static void _cli_cmd_help (int32_t argc, char **argv); +static void _cli_cmd_debug (int32_t argc, char **argv); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e CLI_init (void) +{ + if (_cli_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + /* Initialize UART for use with CLI. */ + if (UART_init () < ERRNO_SUCCESS) + { + return ERRNO_UART_FAILURE; + } + + /* Set init flag so we this doesn't get called more than once. */ + _cli_init = true; + + /* Register CLI help commands + debug. */ + CLI_cmd_register (CLI_CMD_APP, (char *) "help", _cli_cmd_help); + CLI_cmd_register (CLI_CMD_APP, (char *) "?", _cli_cmd_help); + CLI_cmd_register (CLI_CMD_APP, (char *) "debug", _cli_cmd_debug); + + return ERRNO_SUCCESS; +} + +void CLI_task (void) +{ + /* Check for command. */ + if (_cli_chars_parse ()) + { + /* Execute command. */ + _cli_cmd_execute (); + + printk (">>"); + } +} + +ERRNO_e CLI_cmd_register (CLI_cmd_e type, char *cmd_str, void (*cmd) (int32_t argc, char **argv)) +{ + if (!_cli_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (cmd_str == NULL) + { + return ERRNO_BAD_POINTER; + } + else if (type > CLI_CMD_DEBUG) + { + return ERRNO_INVALID_PARAM; + } + /* +3 for "help", "?", and "debug" */ + else if (_cli_list_len >= CLI_MAX_COMMANDS+3) + { + return ERRNO_LIMIT_REACHED; + } + + /* Append new command to the list. */ + _cli_list[_cli_list_len++] = (_cli_s) { .cmd_str = cmd_str, + .cmd = cmd, + .debug = (bool) type }; + + return ERRNO_SUCCESS; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _cli_line_clear (uint8_t n_chars) +{ + uint8_t i; + + /* Return cursor to beginning of line. */ + printk ("\r"); + + for (i = 0; i < n_chars; i++) + { + /* Print spaces to clear. */ + printk (" "); + } + + /* Return cursor to beginning of line. */ + printk ("\r>>"); +} + +static uint8_t _cli_tokens_parse (char *p_buf) +{ + uint8_t argc = 0; + + char *p_token = strtok (_cli_buf, " "); + + while (p_token != NULL) + { + if (argc >= CLI_MAX_ARGS) + { + printk ("Error: Exceeded max arguments."); + return 0xFF; + } + + _cli_tokens[argc++] = p_token; + + p_token = strtok (NULL, " "); + } + + return argc; +} + +static bool _cli_chars_parse (void) +{ + static uint8_t idx = 0; + + /* Read in character. */ + char ch; + + if (UART_getc (&ch)) + { + switch (ch) + { + /* Line feed or carriage return found. */ + case _CLI_CR: + case _CLI_LF: + /* Echo back character. */ + printk ("%c", ch); + _cli_buf[idx] = _CLI_NULL; + if (idx > 0) + { + /* Reset index. */ + idx = 0; + printk ("\r\n"); + return true; + } + else + { + printk ("\r\n>>"); + } + break; + + case _CLI_BS: + if (idx > 0) + { + /* Clear line. */ + _cli_line_clear (idx + 2); + + _cli_buf[--idx] = _CLI_NULL; + + /* Reprint */ + printk ("%s", _cli_buf); + } + break; + + default: + /* Echo back character. */ + printk ("%c", ch); + /* Check if number of characters read have exceeded max length. */ + if (idx < CLI_MAX_BUF_LEN) + { + /* Append character to buffer and update index. */ + _cli_buf[idx++] = ch; + } + break; + } + } + + return false; +} + +static void _cli_cmd_execute (void) +{ + uint8_t i; + uint8_t argc; + + /* Parse CLI tokens and get argument count. */ + argc = _cli_tokens_parse (_cli_buf); + + for (i = 0; i < _cli_list_len; i++) + { + /* If debug mode is enabled. */ + if (_cli_cmd_mode == CLI_CMD_DEBUG) + { + /* Loop through command list and compare commands. */ + if (!strcmp (_cli_tokens[0], _cli_list[i].cmd_str)) + { + /* Execute command. */ + _cli_list[i].cmd ((int32_t) (argc - 1), (char **) &_cli_tokens[1]); + + goto DONE; + } + } + /* If not, only execute app commands. */ + else + { + /* Loop through command list and compare commands. */ + if (!_cli_list[i].debug && !strcmp (_cli_tokens[0], _cli_list[i].cmd_str)) + { + /* Execute command. */ + _cli_list[i].cmd ((int32_t) (argc - 1), (char **) &_cli_tokens[1]); + + goto DONE; + } + } + } + + printk ("Invalid command\r\n"); + +DONE: + + /* Clear CLI buffer for next command. */ + memset (_cli_buf, 0, sizeof (_cli_buf)); + +} + +static void _cli_cmd_help (int32_t argc, char **argv) +{ + uint8_t i; + + printk ("COMMANDS\r\n"); + printk ("-------------------------------------------\r\n"); + + /* Start at index of 2 to skip "help", "?", and "debug" command. */ + for (i = 3; i < _cli_list_len; i++) + { + /* If debug mode is enabled. */ + if (_cli_cmd_mode == CLI_CMD_DEBUG) + { + printk ("%s", _cli_list[i].cmd_str); + printk ("\r\n"); + } + else + { + if (!_cli_list[i].debug) + { + printk ("%s", _cli_list[i].cmd_str); + printk ("\r\n"); + } + } + } +} + +static void _cli_cmd_debug (int32_t argc, char **argv) +{ + if (argc == 0) + { + if (_cli_cmd_mode == CLI_CMD_DEBUG) + { + _cli_cmd_mode = CLI_CMD_APP; + printk ("CLI mode : app\r\n"); + + } + else + { + _cli_cmd_mode = CLI_CMD_DEBUG; + printk ("CLI mode : debug\r\n"); + } + } +} diff --git a/src/lib/cli.h b/src/lib/cli.h new file mode 100644 index 0000000..f88f2c1 --- /dev/null +++ b/src/lib/cli.h @@ -0,0 +1,57 @@ +/** + * @file cli.h + * @brief Command line interface + * @author Kenny Tran + * @date Apr 25 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef CLI_H_ +#define CLI_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define CLI_TASK_PERIOD_MS (1) /* NOTE: Needs to be 1 ms to process data correctly. */ +#define CLI_STACK_SIZE (1024) +#define CLI_THREAD_PRIORITY (7) + +#define CLI_MAX_COMMANDS (32) +#define CLI_MAX_BUF_LEN (100) +#define CLI_MAX_ARGS (10) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum CLI_cmd +{ + CLI_CMD_APP = 0, + CLI_CMD_DEBUG +} CLI_cmd_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e CLI_init (void); + +void CLI_task (void); + +ERRNO_e CLI_cmd_register (CLI_cmd_e type, char *cmd_str, void (*cmd) (int32_t argc, char **argv)); + + +#endif /* CLI_H_ */ diff --git a/src/lib/crc.c b/src/lib/crc.c new file mode 100644 index 0000000..3a66e3a --- /dev/null +++ b/src/lib/crc.c @@ -0,0 +1,127 @@ +/** + * @file crc.c + * @brief Software cyclical redundancy check (CRC). + * @author Kenny Tran + * @date May 20 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include "../lib/errno.h" + +#include "crc.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _CRC_TABLE_SIZE (256) + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static uint32_t _crc_32_table[_CRC_TABLE_SIZE]; +static bool _crc_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +/** + * @fn _crc_32_table_init (void) + * @brief Initializes the _crc_32_table buffer for calculating CRC 32 checksums. + */ +static void _crc_32_table_init (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e CRC_init (void) +{ + if (_crc_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + /* Initialize CRC 32 table. */ + _crc_32_table_init (); + + /* Set init flag so we don't need to keep initializing table. */ + _crc_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e CRC_32_calculate (uint8_t *p_data, uint16_t len, uint32_t checksum_init, uint32_t *p_checksum) +{ + uint8_t *p; + uint8_t *q; + uint8_t octet; + uint32_t crc = checksum_init; + + if (!_crc_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (p_data == NULL || p_checksum == NULL) + { + return ERRNO_BAD_POINTER; + } + + q = p_data + len; + + for (p = p_data; p < q; p++) + { + /* Cast to unsigned octet */ + octet = *p; + crc = (crc >> 8) ^ _crc_32_table[(crc & 0xFF) ^ octet]; + } + + *p_checksum = ~crc; + + return ERRNO_SUCCESS; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void _crc_32_table_init (void) +{ + uint16_t i; + uint16_t j; + uint32_t rem; + + /* Calculate and fill CRC table. */ + for (i = 0; i < _CRC_TABLE_SIZE; i++) + { + /* Remainder from polynomial division. */ + rem = i; + + for (j = 0; j < 8; j++) + { + if (rem & 1) + { + rem >>= 1; + rem ^= CRC_32_POLYNOMIAL; + } + else + { + rem >>= 1; + } + } + _crc_32_table[i] = rem; + } +} diff --git a/src/lib/crc.h b/src/lib/crc.h new file mode 100644 index 0000000..a824b18 --- /dev/null +++ b/src/lib/crc.h @@ -0,0 +1,57 @@ +/** + * @file crc.h + * @brief Software cyclical redundancy check (CRC). + * @author Kenny Tran + * @date May 20 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef CRC_H_ +#define CRC_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define CRC_32_CHECKSUM_INIT (0xFFFFFFFF) +#define CRC_32_POLYNOMIAL (0xEDB88320) + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +/** + * @fn CRC_init (void) + * @brief Initializes table for CRC calculations. + * + * @return ERRNO_SUCCESS if successful, else ERRNO_e. + */ +ERRNO_e CRC_init (void); + +/** + * @fn CRC_32_calculate (uint8_t *const p_data, uint16_t len, uint32_t crc_init) + * @brief Calculates the CRC 32 checksum of the specified buffer and length. + * + * @param p_data - Pointer to data to CRC. + * @param len - Length of the data buffer. + * @param crc_init - The initial CRC value. Default is 0xFFFFFFFF. + * @param p_checksum - Pointer to register to store resulting CRC checksum. + * + * @return ERRNO_SUCCESS if successful, else ERRNO_e. + */ +ERRNO_e CRC_32_calculate (uint8_t *p_data, uint16_t len, uint32_t checksum_init, uint32_t *p_checksum); + + +#endif /* CRC_H_ */ diff --git a/src/lib/debug.c b/src/lib/debug.c new file mode 100644 index 0000000..3ebce38 --- /dev/null +++ b/src/lib/debug.c @@ -0,0 +1,37 @@ +/** + * @file debug.c + * @brief DEBUG module - variable definitions + * @author Eric Xu + * @date Oct 15 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + +#include + +#include "debug.h" + +#ifdef CONFIG_DBG_STATS +atomic_t i2c_transfer_active = ATOMIC_INIT(0); + +volatile uint32_t fifo_read_task_counter = 0; +volatile uint32_t read_shot_data_counter = 0; +volatile uint32_t read_into_dummy_counter = 0; +volatile uint32_t fifo_read_FTH_counter = 0; +volatile uint32_t read_shot_data_after_fifo_read_counter = 0; +volatile uint32_t fifo_waterM_total_hit_counter = 0; +volatile uint32_t fifo_waterM_hit_counter = 0; +volatile uint32_t fifo_overrun_counter = 0; +volatile uint32_t unread_words_before_read = 0; +volatile uint32_t unread_words_after_read = 0; +volatile uint32_t max_unread_words_before_read = 0; +volatile uint32_t max_unread_words_after_read = 0; + +volatile uint32_t imu_task_during_i2c_counter = 0; +volatile uint32_t fsm_task_counter = 0; +volatile uint32_t fsm_task_during_i2c_counter = 0; +volatile uint32_t event_task_counter = 0; +volatile uint32_t event_task_during_i2c_counter = 0; +#endif diff --git a/src/lib/debug.h b/src/lib/debug.h new file mode 100644 index 0000000..1916880 --- /dev/null +++ b/src/lib/debug.h @@ -0,0 +1,102 @@ +/** + * @file debug.h + * @brief DEBUG module + * @author Eric Xu + * @date Oct 15 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +#include + +#ifdef CONFIG_DBG_STATS +/*-------------------------------------------------------------------------------------------------- + * DEBUG VARIABLE + *------------------------------------------------------------------------------------------------*/ +extern atomic_t i2c_transfer_active; + +extern volatile uint32_t fifo_read_task_counter; +extern volatile uint32_t read_shot_data_counter; +extern volatile uint32_t read_into_dummy_counter; +extern volatile uint32_t fifo_read_FTH_counter; +extern volatile uint32_t read_shot_data_after_fifo_read_counter; +extern volatile uint32_t fifo_waterM_total_hit_counter; +extern volatile uint32_t fifo_waterM_hit_counter; +extern volatile uint32_t fifo_overrun_counter; +extern volatile uint32_t unread_words_before_read; +extern volatile uint32_t unread_words_after_read; +extern volatile uint32_t max_unread_words_before_read; +extern volatile uint32_t max_unread_words_after_read; + +extern volatile uint32_t imu_task_during_i2c_counter; +extern volatile uint32_t fsm_task_counter; +extern volatile uint32_t fsm_task_during_i2c_counter; +extern volatile uint32_t event_task_counter; +extern volatile uint32_t event_task_during_i2c_counter; + + +/*-------------------------------------------------------------------------------------------------- + * DEBUG MACRO + *------------------------------------------------------------------------------------------------*/ +#define DBG_STAT_INC(counter) counter++ +#define DBG_STAT_INC_IF(condition, counter) do { if (condition) counter++; } while(0) +#define DBG_STAT_TRACK_MAX(current_value, max_value) \ + do { \ + __typeof__(current_value) _temp_val = (current_value); \ + if (_temp_val > (max_value)) { \ + (max_value) = _temp_val; \ + } \ + } while(0) + +/* Thread-safe versions using stat_mutex */ +#define DBG_STAT_INC_SAFE(counter, mutex) \ + do { \ + k_mutex_lock(&(mutex), K_FOREVER); \ + counter++; \ + k_mutex_unlock(&(mutex)); \ + } while(0) + +#define DBG_STAT_INC_IF_SAFE(condition, counter, mutex) \ + do { \ + if (condition) { \ + k_mutex_lock(&(mutex), K_FOREVER); \ + counter++; \ + k_mutex_unlock(&(mutex)); \ + } \ + } while(0) + +#define DBG_STAT_TRACK_VALUE_SAFE(current_value, latest_value, mutex) \ + do { \ + __typeof__(current_value) _temp_val = (current_value); \ + k_mutex_lock(&(mutex), K_FOREVER); \ + (latest_value) = _temp_val; \ + k_mutex_unlock(&(mutex)); \ + } while(0) + +#define DBG_STAT_TRACK_MAX_SAFE(current_value, max_value, mutex) \ + do { \ + __typeof__(current_value) _temp_val = (current_value); \ + k_mutex_lock(&(mutex), K_FOREVER); \ + if (_temp_val > (max_value)) { \ + (max_value) = _temp_val; \ + } \ + k_mutex_unlock(&(mutex)); \ + } while(0) + +#else +#define DBG_STAT_INC(counter) do {} while(0) +#define DBG_STAT_INC_IF(condition, counter) do {} while(0) +#define DBG_STAT_TRACK_MAX(current_value, max_value) do {} while(0) +#define DBG_STAT_INC_SAFE(counter, mutex) do {} while(0) +#define DBG_STAT_INC_IF_SAFE(condition, counter, mutex) do {} while(0) +#define DBG_STAT_TRACK_VALUE_SAFE(current_value, latest_value, mutex) do {} while(0) +#define DBG_STAT_TRACK_MAX_SAFE(current_value, max_value, mutex) do {} while(0) +#endif /* CONFIG_DBG_STATS */ + + +#endif \ No newline at end of file diff --git a/src/lib/errno.c b/src/lib/errno.c new file mode 100644 index 0000000..23f62a2 --- /dev/null +++ b/src/lib/errno.c @@ -0,0 +1,83 @@ +/** + * @file errno.c + * @brief Error codes + * @author Kenny Tran + * @date Aug 28 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "print.h" +#include "errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void ERRNO_cli_cmd (int32_t argc, char **argv) +{ + PRINT (INFO, "ERROR CODES\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "ERRNO_BUSY = %d\r\n", ERRNO_BUSY); + PRINT (INFO, "ERRNO_ALREADY_INITIALIZED = %d\r\n", ERRNO_ALREADY_INITIALIZED); + PRINT (INFO, "ERRNO_SUCCESS = %d\r\n", ERRNO_SUCCESS); + PRINT (INFO, "ERRNO_FAILURE = %d\r\n", ERRNO_FAILURE); + PRINT (INFO, "ERRNO_NOT_INITIALIZED = %d\r\n", ERRNO_NOT_INITIALIZED); + PRINT (INFO, "ERRNO_INVALID_PARAM = %d\r\n", ERRNO_INVALID_PARAM); + PRINT (INFO, "ERRNO_BAD_POINTER = %d\r\n", ERRNO_BAD_POINTER); + PRINT (INFO, "ERRNO_ID_FAILURE = %d\r\n", ERRNO_ID_FAILURE); + PRINT (INFO, "ERRNO_LIMIT_REACHED = %d\r\n", ERRNO_LIMIT_REACHED); + PRINT (INFO, "ERRNO_CRC_FAILURE = %d\r\n", ERRNO_CRC_FAILURE); + PRINT (INFO, "ERRNO_OUT_OF_RANGE = %d\r\n", ERRNO_OUT_OF_RANGE); + PRINT (INFO, "ERRNO_NOT_READY = %d\r\n", ERRNO_NOT_READY); + PRINT (INFO, "ERRNO_GPIO_FAILURE = %d\r\n", ERRNO_GPIO_FAILURE); + PRINT (INFO, "ERRNO_I2C_FAILURE = %d\r\n", ERRNO_I2C_FAILURE); + PRINT (INFO, "ERRNO_PWM_FAILURE = %d\r\n", ERRNO_PWM_FAILURE); + PRINT (INFO, "ERRNO_SPI_FAILURE = %d\r\n", ERRNO_SPI_FAILURE); + PRINT (INFO, "ERRNO_UART_FAILURE = %d\r\n", ERRNO_UART_FAILURE); + PRINT (INFO, "ERRNO_ADC_FAILURE = %d\r\n", ERRNO_ADC_FAILURE); + PRINT (INFO, "ERRNO_RTC_FAILURE = %d\r\n", ERRNO_RTC_FAILURE); + PRINT (INFO, "ERRNO_PWR_MGMT_FAILURE = %d\r\n", ERRNO_PWR_MGMT_FAILURE); + PRINT (INFO, "ERRNO_NVM_FAILURE = %d\r\n", ERRNO_NVM_FAILURE); + PRINT (INFO, "ERRNO_NOT_ENOUGH_MEMORY = %d\r\n", ERRNO_NOT_ENOUGH_MEMORY); + PRINT (INFO, "ERRNO_FILE_NOT_FOUND = %d\r\n", ERRNO_FILE_NOT_FOUND); + PRINT (INFO, "ERRNO_INVALID_DEPTH = %d\r\n", ERRNO_INVALID_DEPTH); + PRINT (INFO, "ERRNO_SIZE_MISMATCH = %d\r\n", ERRNO_SIZE_MISMATCH); + PRINT (INFO, "ERRNO_NO_VALID_ENTRIES = %d\r\n", ERRNO_NO_VALID_ENTRIES); + PRINT (INFO, "ERRNO_MEMORY_NOT_AVAILABLE = %d\r\n", ERRNO_MEMORY_NOT_AVAILABLE); + PRINT (INFO, "ERRNO_EXCEED_FILE_NUM = %d\r\n", ERRNO_EXCEED_FILE_NUM); + PRINT (INFO, "ERRNO_EXCEED_FILE_SIZE = %d\r\n", ERRNO_EXCEED_FILE_SIZE); + PRINT (INFO, "ERRNO_EXCEED_NAME_SIZE = %d\r\n", ERRNO_EXCEED_NAME_SIZE); +} diff --git a/src/lib/errno.h b/src/lib/errno.h new file mode 100644 index 0000000..68ab8e4 --- /dev/null +++ b/src/lib/errno.h @@ -0,0 +1,83 @@ +/** + * @file errno.h + * @brief Error codes + * @author Kenny Tran + * @date Aug 28 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef ERRNO_H_ +#define ERRNO_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum ERRNO +{ + ERRNO_BUSY = 2, + ERRNO_ALREADY_INITIALIZED = 1, + + ERRNO_SUCCESS = 0, + + ERRNO_FAILURE = -1, + ERRNO_NOT_INITIALIZED = -2, + ERRNO_INVALID_PARAM = -3, + ERRNO_BAD_POINTER = -4, + ERRNO_ID_FAILURE = -5, + ERRNO_LIMIT_REACHED = -6, + ERRNO_CRC_FAILURE = -7, + ERRNO_OUT_OF_RANGE = -8, + // ERRNO_NOT_FOUND = -9, + + /* Peripherals */ + ERRNO_NOT_READY = -10, + ERRNO_GPIO_FAILURE = -11, + ERRNO_I2C_FAILURE = -12, + ERRNO_PWM_FAILURE = -13, + ERRNO_SPI_FAILURE = -14, + ERRNO_UART_FAILURE = -15, + ERRNO_ADC_FAILURE = -16, + ERRNO_RTC_FAILURE = -17, /* Real-Time Counter */ + + ERRNO_PWR_MGMT_FAILURE = -18, + ERRNO_NVM_FAILURE = -19, + ERRNO_WDT_FAILURE = -20, + + /* File IO */ + ERRNO_NOT_ENOUGH_MEMORY = -30, + ERRNO_FILE_NOT_FOUND = -31, + ERRNO_INVALID_DEPTH = -32, + ERRNO_SIZE_MISMATCH = -33, + ERRNO_NO_VALID_ENTRIES = -34, + ERRNO_MEMORY_NOT_AVAILABLE = -35, + + /* Shot Storage */ + ERRNO_EXCEED_FILE_NUM = -40, + ERRNO_EXCEED_FILE_SIZE = -41, + ERRNO_EXCEED_NAME_SIZE = -42 +} ERRNO_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void ERRNO_cli_cmd (int32_t argc, char **argv); + + +#endif /* ERRNO_H_ */ diff --git a/src/lib/fs.c b/src/lib/fs.c new file mode 100644 index 0000000..c090cc2 --- /dev/null +++ b/src/lib/fs.c @@ -0,0 +1,1196 @@ +/** + * @file fs.c + * @brief File system using non-volatile external memory. + * @author Kenny Tran + * @date Mar 04 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include + +#include + +#include "../lib/crc.h" +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "../bsp/nvm_ext.h" + +#include "fs.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +/** + * @brief Size of flash space allocated to store metadata. + */ +#define _FS_META_SIZE (0x1000) +/** + * @brief Starting address of metadata. + */ +#define _FS_META_START_ADDR (NVM_EXT_START_ADDR) +/** + * @brief Ending address of metadata. + */ +#define _FS_META_END_ADDR (_FS_META_START_ADDR + _FS_META_SIZE - 1) +/** + * @brief Starting address for file storage. + */ +#define _FS_START_ADDR (_FS_META_END_ADDR + 1) +/** + * @brief Ending address for file storage. + */ +#define _FS_END_ADDR (NVM_EXT_START_ADDR + NVM_EXT_SIZE - 1) +/** + * @brief Size of flash space allocated to store files. + */ +#define _FS_SIZE (_FS_END_ADDR - _FS_START_ADDR + 1) +/** + * @brief Minimum sectors allocated for a file for wear-leveling. + */ +#define _FS_SECTOR_ALLOCATION_MIN (1) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +/** + * @brief Struct containing necessary metadata for each file. + * + * @note Packed and align to nearest 1 byte to save padding/alignment space. No need to align by + * word size for performance as this is read only once at startup. + */ +typedef struct +{ + char name[(FS_FILENAME_LEN + 1)]; + uint16_t size; + uint32_t start_addr; + uint32_t end_addr; + uint32_t checksum; +} __attribute__((packed, aligned(1))) _fs_meta_s; + +/** + * @brief File structure used to keep records and wear leveling. + */ +typedef struct +{ + _fs_meta_s meta; + uint32_t newest_idx_addr; + uint32_t empty_idx_addr; + uint32_t newest_idx; + uint16_t entries_per_sector; + uint16_t sectors; +} _fs_file_ctx_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static K_MUTEX_DEFINE (_fs_mutex); +static uint32_t _fs_meta_empty_addr = _FS_META_START_ADDR; +static uint8_t _fs_file_cnt = 0; +static _fs_file_ctx_s _fs_files[FS_MAX_FILES]; +static bool _fs_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +/** + * @fn ERRNO_e _fs_meta_init (void) + * @brief Scans the dedicated metadata section in flash for file table. + * Then scans each file to update the file context structure. + * + * @note Flash reserved space for metadata is assumed to be sector aligned. + * + * @return ERRNO_SUCCESS if successful, error code if not. + */ +static ERRNO_e _fs_meta_init (void); + +/** + * @fn ERRNO_e _fs_file_meta_add (_fs_meta_s file_meta) + * @brief Adds a new file entry to the metadata space. + * + * @param file_meta - The file's metadata information. + * + * @return ERRNO_SUCCESS if successful, error code if not. + */ +static ERRNO_e _fs_file_meta_add (_fs_meta_s file_meta); + +/** + * @fn ERRNO_e _fs_is_empty (uint32_t start_addr, uint16_t len) + * @brief Checks for empty space in the provided flash segment. + * + * @param start_addr - The starting address of the flash segment. + * @param len - The number of bytes to check. + * + * @return ERRNO_SUCCESS if empty, ERRNO_MEMORY_NOT_AVAILABLE if not, and error code if + * failure. + */ +static ERRNO_e _fs_is_empty (uint32_t start_addr, uint16_t len); + +/** + * @fn static uint32_t _fs_prev_entry_addr_get (uint32_t start_addr, _fs_file_ctx_s* file_ctx) + * @brief Returns the previous entry's starting address. + * + * @param start_addr - The current starting address. + * @param file - pointer to the file context. + * + * @return The starting address of the previous entry. + */ +static uint32_t _fs_prev_entry_addr_get (uint32_t start_addr, _fs_file_ctx_s* file_ctx); + +/** + * @fn uint32_t _fs_sector_boundary_addr_get (uint32_t addr) + * @brief Gets the starting address of the next sector starting at the provided address. + * + * @param addr - The address to start at. + * + * @return The next sector boundary address, starting at the provided address. + */ +static uint32_t _fs_sector_boundary_addr_get (uint32_t addr); + +/** + * @fn bool _fs_is_entry_valid (uint32_t start_addr, uint16_t len) + * @brief Checks if the entry is valid. + * + * @note Need to include index and checksum sizes into the length. + * + * @param start_addr - The starting address of the data to verify. + * @param len - The length of the data to verify, including index and checksum. + * + * @return True if entry is valid, false if not. + */ +static bool _fs_is_entry_valid (uint32_t start_addr, uint16_t len); + +/** + * @fn _fs_file_ctx_s* _fs_file_get (char* filename) + * @brief Gets file context for specified file. + * + * @param filename - The file name. + * + * @return The file context pointer, NULL if failure. + */ +static _fs_file_ctx_s *_fs_file_get (char *filename); + +/** + * @fn ERRNO_e _fs_meta_rewrite (void) + * @brief Erases the metadata sector and rewrites all current file metadata. + * + * @return ERRNO_SUCCESS if successful, error code if not. + */ +static ERRNO_e _fs_meta_rewrite (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e FS_init (void) +{ + ERRNO_e err = ERRNO_FAILURE; + + if (_fs_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + /* Initialize CRC.*/ + CRC_init (); + + if (NVM_EXT_init () < ERRNO_SUCCESS) + { + return ERRNO_NVM_FAILURE; + } + + /* Initialized metadata space. */ + err = _fs_meta_init (); + if (err != ERRNO_SUCCESS) + { + return err; + } + + /* Set init flag in case this gets called more than once. */ + _fs_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e FS_open_or_create (char *p_filename, FS_file_t *p_file, uint16_t size, uint16_t depth) +{ + ERRNO_e err; + + /* Check filesystem has been initialized. */ + if (!_fs_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Check if we've exceeded file limit. */ + if (_fs_file_cnt >= FS_MAX_FILES) + { + *p_file = NULL; + return ERRNO_LIMIT_REACHED; + } + + /* Check if we've exceeded size limit, including checksum. */ + if (size > FS_MAX_FILE_ENTRY_SIZE) + { + *p_file = NULL; + return ERRNO_NOT_ENOUGH_MEMORY; + } + + /* Depth minimum is 1. */ + if (depth < 1) + { + *p_file = NULL; + return ERRNO_INVALID_DEPTH; + } + + /* Fetch file. */ + *p_file = _fs_file_get (p_filename); + + /* Create file if it does not exist. */ + if (*p_file == NULL) + { + /* Calculate number of entries per sector. */ + _fs_files[_fs_file_cnt].entries_per_sector = (uint16_t) (FS_SECTOR_SIZE / + (size + FS_FILE_INDEX_SIZE + FS_FILE_CHECKSUM_SIZE)); + + /* Calculate number of sectors needed. */ + if (((depth / _fs_files[_fs_file_cnt].entries_per_sector) + 1) >= + _FS_SECTOR_ALLOCATION_MIN) + { + _fs_files[_fs_file_cnt].sectors = + ((depth / _fs_files[_fs_file_cnt].entries_per_sector) + 1); + } + else + { + _fs_files[_fs_file_cnt].sectors = _FS_SECTOR_ALLOCATION_MIN; + } + + /* Assign file name. */ + strcpy (_fs_files[_fs_file_cnt].meta.name, p_filename); + + /* Calculate and set starting address. */ + _fs_files[_fs_file_cnt].meta.start_addr = + (_fs_file_cnt) ? (_fs_files[(_fs_file_cnt - 1)].meta.end_addr + 1) : _FS_START_ADDR; + + /* Calculate size with depth (aligned by sector) and determine ending address. */ + _fs_files[_fs_file_cnt].meta.end_addr = _fs_files[_fs_file_cnt].meta.start_addr + + (_fs_files[_fs_file_cnt].sectors * FS_SECTOR_SIZE) - 1; + + /* Set size. */ + _fs_files[_fs_file_cnt].meta.size = size; + + /* Calculate new checksum for file metadata. */ + if (CRC_32_calculate ((uint8_t *) &_fs_files[_fs_file_cnt], + (sizeof (_fs_meta_s) - FS_FILE_CHECKSUM_SIZE), + CRC_32_CHECKSUM_INIT, + &_fs_files[_fs_file_cnt].meta.checksum)) + { + return ERRNO_CRC_FAILURE; + } + + /* Set newest index address to starting address since this is a new file. */ + _fs_files[_fs_file_cnt].newest_idx_addr = _fs_files[_fs_file_cnt].meta.start_addr; + + /* Set empty index address to starting address since this is a new file. */ + _fs_files[_fs_file_cnt].empty_idx_addr = _fs_files[_fs_file_cnt].meta.start_addr; + + /* Initialize entry index to -1 since we roll over to 0 for first write. */ + _fs_files[_fs_file_cnt].newest_idx = (uint32_t) -1; + + /* Add file information to flash metadata. */ + err = _fs_file_meta_add (_fs_files[_fs_file_cnt].meta); + if (err) + { + *p_file = NULL; + return err; + } + + /* Set file address and increment file count. */ + *p_file = (FS_file_t)&_fs_files[_fs_file_cnt++]; + } + else + { + /* Verify existing file size matches. */ + if (((_fs_file_ctx_s*)*p_file)->meta.size != size) + { + *p_file = NULL; + return ERRNO_SIZE_MISMATCH; + } + } + + return ERRNO_SUCCESS; +} + +ERRNO_e FS_write (FS_file_t file, uint8_t *p_src) +{ + ERRNO_e err; + _fs_file_ctx_s *file_ctx = (_fs_file_ctx_s *) file; + uint16_t entry_len = file_ctx->meta.size + FS_FILE_INDEX_SIZE + FS_FILE_CHECKSUM_SIZE; + uint32_t checksum; + uint32_t sector_boundary_addr = _fs_sector_boundary_addr_get (file_ctx->empty_idx_addr); + uint8_t buf[entry_len]; + + /* Check filesystem has been initialized. */ + if (!_fs_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Check if source pointer is valid. */ + if (p_src == NULL) + { + return ERRNO_BAD_POINTER; + } + + k_mutex_lock (&_fs_mutex, K_FOREVER); + + /* Verify space is empty prior to writing. */ + err = _fs_is_empty (file_ctx->empty_idx_addr, entry_len); + + if (err) + { + k_mutex_unlock (&_fs_mutex); + return err; + } + + /* Increment and check if we've exceeded number of entries. */ + if (++file_ctx->newest_idx >= FS_MAX_FILE_ENTRIES) + { + k_mutex_unlock (&_fs_mutex); + return ERRNO_LIMIT_REACHED; + } + + /* Copy data to buffer and append index. */ + memcpy (buf, p_src, file_ctx->meta.size); + memcpy (&buf[file_ctx->meta.size ], (uint8_t *) &file_ctx->newest_idx, FS_FILE_INDEX_SIZE); + + /* Calculate checksum of source data and newest index. */ + if (CRC_32_calculate (buf, (file_ctx->meta.size + FS_FILE_INDEX_SIZE), CRC_32_CHECKSUM_INIT, &checksum)) + { + k_mutex_unlock (&_fs_mutex); + return ERRNO_CRC_FAILURE; + } + + /* Copy calculated CRC to buffer. */ + memcpy (&buf[(file_ctx->meta.size + FS_FILE_INDEX_SIZE)], (uint8_t *) &checksum, FS_FILE_CHECKSUM_SIZE); + + /* Write the entire entry. */ + if (NVM_EXT_write (file_ctx->empty_idx_addr, buf, entry_len)) + { + k_mutex_unlock (&_fs_mutex); + return ERRNO_NVM_FAILURE; + } + + /* Update newest index address. */ + file_ctx->newest_idx_addr = file_ctx->empty_idx_addr; + + /* Check if next write will span sectors. */ + if ((file_ctx->empty_idx_addr + (2 * entry_len)) > sector_boundary_addr) + { + /* Check if we have reached the end of the file. */ + if (sector_boundary_addr > file_ctx->meta.end_addr) + { + /* Wrap to beginning of file. */ + sector_boundary_addr = file_ctx->meta.start_addr; + + /* Update empty index address to start address for the next write. */ + file_ctx->empty_idx_addr = file_ctx->meta.start_addr; + + /* If sector allocation is only 1, we will erase our own data that was just written above. + and rewrite to starting address. */ + if (file_ctx->sectors == 1) + { + /* Erase the sector. */ + if (NVM_EXT_erase_block (sector_boundary_addr, NVM_EXT_BLOCK_SIZE_4KB)) + { + k_mutex_unlock (&_fs_mutex); + return ERRNO_NVM_FAILURE; + } + + /* Write the entire entry. */ + if (NVM_EXT_write (file_ctx->empty_idx_addr, buf, entry_len)) + { + k_mutex_unlock (&_fs_mutex); + return ERRNO_NVM_FAILURE; + } + + /* Update newest and empty index address. + * The empty_idx_addr must be updated to prepare for the next write */ + file_ctx->newest_idx_addr = file_ctx->empty_idx_addr; + file_ctx->empty_idx_addr += entry_len; + + k_mutex_unlock (&_fs_mutex); + return ERRNO_SUCCESS; + } + } + else + { + /* Update empty index address to start at the next sector. */ + file_ctx->empty_idx_addr = sector_boundary_addr; + } + + /* Erase the sector. */ + if (NVM_EXT_erase_block (sector_boundary_addr, NVM_EXT_BLOCK_SIZE_4KB)) + { + k_mutex_unlock (&_fs_mutex); + return ERRNO_NVM_FAILURE; + } + } + else + { + /* Update empty index address for the next write. */ + file_ctx->empty_idx_addr += entry_len; + } + + k_mutex_unlock (&_fs_mutex); + + return ERRNO_SUCCESS; +} + +ERRNO_e FS_write_at_offset (FS_file_t file, uint8_t *p_src, uint16_t offset, uint16_t len) +{ + _fs_file_ctx_s *file_ctx = (_fs_file_ctx_s *) file; + uint8_t buf[file_ctx->meta.size]; + ERRNO_e err; + + /* Check filesystem has been initialized. */ + if (!_fs_init) + { + return ERRNO_NOT_INITIALIZED; + } + + err = FS_read (file, buf, 0, file_ctx->meta.size); + + if (err) + { + return err; + } + + /* Update contents. */ + memcpy (&buf[offset], p_src, len); + + err = FS_write (file, buf); + + return err; +} + +ERRNO_e FS_read (FS_file_t file, uint8_t *p_dest, uint16_t offset, uint16_t len) +{ + _fs_file_ctx_s *file_ctx = (_fs_file_ctx_s *) file; + uint32_t entry_start_addr = file_ctx->newest_idx_addr; + uint32_t entry_len = file_ctx->meta.size + FS_FILE_INDEX_SIZE + FS_FILE_CHECKSUM_SIZE; + uint16_t entries = (file_ctx->sectors * file_ctx->entries_per_sector); + uint16_t i; + + /* Check filesystem has been initialized. */ + if (!_fs_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Check if p_destination pointer is valid. */ + if (p_dest == NULL) + { + return ERRNO_BAD_POINTER; + } + + /* Check if we are over file size limit. */ + if (len > file_ctx->meta.size) + { + return ERRNO_NOT_ENOUGH_MEMORY; + } + + if (offset + len > file_ctx->meta.size) + { + return ERRNO_LIMIT_REACHED; + } + + k_mutex_lock (&_fs_mutex, K_FOREVER); + + /* Loop through entries until we find a valid entry. */ + for (i = 0; i < entries; i++) { + /* Check if entry is valid. */ + if (_fs_is_entry_valid (entry_start_addr, entry_len)) { + break; + } else { + if (i == (entries - 1)) { + return ERRNO_NO_VALID_ENTRIES; + } + } + + /* Get starting address for the previous entry. */ + entry_start_addr = _fs_prev_entry_addr_get (entry_start_addr, file_ctx); + } + + /* Read the entry. */ + if (NVM_EXT_read (entry_start_addr + offset, p_dest, len)) + { + return ERRNO_NVM_FAILURE; + } + + k_mutex_unlock (&_fs_mutex); + + return ERRNO_SUCCESS; +} + + +ERRNO_e FS_clear (FS_file_t file) +{ + _fs_file_ctx_s *file_ctx = (_fs_file_ctx_s *) file; + // uint32_t sector_len = (file_ctx->meta.end_addr - file_ctx->meta.start_addr + 1) % + // FS_SECTOR_SIZE + 1; + + /* Check filesystem has been initialized. */ + if (!_fs_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (NVM_EXT_erase_range (file_ctx->meta.start_addr, file_ctx->meta.end_addr)) + { + return ERRNO_NVM_FAILURE; + } + + /* Reset index addresses and entries. */ + file_ctx->newest_idx_addr = file_ctx->meta.start_addr; + file_ctx->empty_idx_addr = file_ctx->meta.start_addr; + file_ctx->newest_idx = (uint32_t) -1; + + return ERRNO_SUCCESS; +} + +ERRNO_e FS_delete (char *p_filename) +{ + ERRNO_e err; + int32_t del_idx = -1; + uint8_t buf[FS_SECTOR_SIZE]; + + /* Check filesystem has been initialized. */ + if (!_fs_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Find the file index. */ + for (uint8_t i = 0; i < _fs_file_cnt; i++) + { + if (!strcmp (p_filename, _fs_files[i].meta.name)) + { + del_idx = i; + break; + } + } + + if (del_idx == -1) + { + return ERRNO_FILE_NOT_FOUND; + } + + /* Erase the deleted file's data range. */ + err = NVM_EXT_erase_range (_fs_files[del_idx].meta.start_addr, _fs_files[del_idx].meta.end_addr); + if (err) + { + return err; + } + + /* Shift the in-memory array to remove the deleted file. */ + for (uint8_t i = del_idx + 1; i < _fs_file_cnt; i++) + { + _fs_files[i - 1] = _fs_files[i]; + } + _fs_file_cnt--; + + /* Reclaim space by shifting subsequent files' addresses and copying their data. */ + uint32_t next_start = (del_idx == 0) ? _FS_START_ADDR : (_fs_files[del_idx - 1].meta.end_addr + 1); + + for (uint8_t i = del_idx; i < _fs_file_cnt; i++) + { + uint32_t old_start = _fs_files[i].meta.start_addr; + uint32_t old_end = _fs_files[i].meta.end_addr; + uint32_t file_size = old_end - old_start + 1; + uint32_t new_start = next_start; + uint32_t new_end = new_start + file_size - 1; + + /* Erase the new range for the file. */ + err = NVM_EXT_erase_range (new_start, new_end); + if (err) + { + return err; + } + + /* Copy data from old address to new address in chunks. */ + for (uint32_t offset = 0; offset < file_size; offset += FS_SECTOR_SIZE) + { + uint32_t chunk_size = (file_size - offset < FS_SECTOR_SIZE) ? (file_size - offset) : FS_SECTOR_SIZE; + + err = NVM_EXT_read (old_start + offset, buf, chunk_size); + if (err) + { + return err; + } + + err = NVM_EXT_write (new_start + offset, buf, chunk_size); + if (err) + { + return err; + } + } + + /* Update the file's metadata with new addresses. */ + _fs_files[i].meta.start_addr = new_start; + _fs_files[i].meta.end_addr = new_end; + + /* Adjust internal addresses by the offset difference. */ + int32_t addr_offset = new_start - old_start; + _fs_files[i].newest_idx_addr += addr_offset; + _fs_files[i].empty_idx_addr += addr_offset; + + /* Recalculate metadata checksum. */ + if (CRC_32_calculate ((uint8_t *) &_fs_files[i].meta, + (sizeof (_fs_meta_s) - FS_FILE_CHECKSUM_SIZE), + CRC_32_CHECKSUM_INIT, + &_fs_files[i].meta.checksum)) + { + return ERRNO_CRC_FAILURE; + } + + /* Update next start for the following file. */ + next_start = new_end + 1; + } + + /* Rewrite the metadata sector without the deleted file. */ + err = _fs_meta_rewrite (); + if (err) + { + return err; + } + + return ERRNO_SUCCESS; +} + +uint32_t FS_memory_used (void) +{ + return (_fs_file_cnt ? + (_fs_files[(_fs_file_cnt - 1)].meta.end_addr - _FS_START_ADDR + 1) : 0); +} + +uint32_t FS_memory_available (void) +{ + return _FS_SIZE - FS_memory_used (); +} + +void FS_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + PRINT (INFO, "FS\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Memory used (bytes) : %d\r\n", FS_memory_used ()); + PRINT (INFO, "Memory available (bytes) : %d\r\n", FS_memory_available ()); + PRINT (INFO, "Total memory (bytes) : %d\r\n", _FS_SIZE); + } + else if (argc == 1) + { + if (!strcmp (argv[0], "--meta")) + { + PRINT (INFO, "FS Metadata Space\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + PRINT (INFO, "Start address : %d\r\n", _FS_META_START_ADDR); + PRINT (INFO, "End address : %d\r\n", _FS_META_END_ADDR); + PRINT (INFO, "Total memory (bytes) : %d\r\n", _FS_META_SIZE); + PRINT (INFO, "Next entry address : %d\r\n", _fs_meta_empty_addr); + } + else if (!strcmp (argv[0], "--list")) + { + uint8_t i; + + PRINT (INFO, "FILES\r\n"); + PRINT (INFO, "-------------------------------------------\r\n"); + for (i = 0; i < _fs_file_cnt; i++) + { + PRINT (INFO, "%s\r\n", _fs_files[i].meta.name); + } + } + else + { + goto USAGE; + } + } + else if (argc == 2) + { + _fs_file_ctx_s *p_file; + + if (!strcmp (argv[0], "--details")) + { + p_file = _fs_file_get (argv[1]); + + if (p_file != NULL) + { + PRINT (INFO, "%s : \r\n", p_file->meta.name); + PRINT (INFO, "file start address : %d\r\n", p_file->meta.start_addr); + PRINT (INFO, "file end address : %d\r\n", p_file->meta.end_addr); + PRINT (INFO, "file size (bytes) : %d\r\n", p_file->meta.size); + PRINT (INFO, "checksum : 0x%08X\r\n", p_file->meta.checksum); + PRINT (INFO, "newest index address : %d\r\n", p_file->newest_idx_addr); + PRINT (INFO, "empty index address : %d\r\n", p_file->empty_idx_addr); + PRINT (INFO, "newest index : %d\r\n", p_file->newest_idx); + PRINT (INFO, "entries per sector : %d\r\n", p_file->entries_per_sector); + PRINT (INFO, "sectors : %d\r\n", p_file->sectors); + } + else + { + PRINT (INFO, "ERROR : %s does not exist.\r\n", argv[1]); + } + } + else if (!strcmp (argv[0], "--delete")) + { + ERRNO_e err = FS_delete (argv[1]); + if (err == ERRNO_SUCCESS) + { + PRINT (INFO, "%s deleted successfully.\r\n", argv[1]); + } + else + { + PRINT (INFO, "ERROR : Failed to delete %s (error: %d).\r\n", argv[1], err); + } + } + else + { + goto USAGE; + } + } + else if (argc == 4) + { + if (!strcmp (argv[0], "--create")) + { + ERRNO_e err; + FS_file_t file; + char *p_filename = argv[1]; + uint16_t size = (uint16_t) strtoul(argv[2], NULL, 0); + uint16_t depth = (uint16_t) strtoul(argv[3], NULL, 0); + + err = FS_open_or_create (p_filename, &file, size, depth); + if (err == ERRNO_SUCCESS) + { + PRINT (INFO, "%s created successfully.\r\n", p_filename); + } + else + { + PRINT (INFO, "ERROR : Failed to create %s (error: %d).\r\n", p_filename, err); + } + } + else if (!strcmp (argv[0], "--read")) + { + ERRNO_e err; + FS_file_t file; + char *p_filename = argv[1]; + uint16_t size = (uint16_t) strtoul(argv[2], NULL, 0); + uint16_t depth = (uint16_t) strtoul(argv[3], NULL, 0); + uint8_t buf[size]; + uint16_t i; + + err = FS_open_or_create (p_filename, &file, size, depth); + if (err != ERRNO_SUCCESS) + { + PRINT (INFO, "ERROR : Failed to open %s (error: %d).\r\n", p_filename, err); + return; + } + + err = FS_read (file, buf, 0, size); + if (err != ERRNO_SUCCESS) + { + PRINT (INFO, "ERROR : Failed to read %s (error: %d).\r\n", p_filename, err); + return; + } + + for (i = 0; i < size; i++) + { + PRINT (INFO, "%X", buf[i]); + } + PRINT (INFO, "\r\n"); + } + else + { + goto USAGE; + } + } + else if (argc == 5) + { + if (!strcmp (argv[0], "--write")) + { + ERRNO_e err; + FS_file_t file; + char *p_filename = argv[1]; + uint16_t size = (uint16_t) strtoul(argv[2], NULL, 0); + uint16_t depth = (uint16_t) strtoul(argv[3], NULL, 0); + + err = FS_open_or_create (p_filename, &file, size, depth); + if (err != ERRNO_SUCCESS) + { + PRINT (INFO, "ERROR : Failed to open or create %s (error: %d).\r\n", p_filename, err); + return; + } + + err = FS_write (file, argv[4]); + if (err == ERRNO_SUCCESS) + { + PRINT (INFO, "Data written to %s successfully.\r\n", p_filename); + } + else + { + PRINT (INFO, "ERROR : Failed to write to %s (error: %d).\r\n", p_filename, err); + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + PRINT (INFO, "Usage: fs [OPTION...]\r\n"); + PRINT (INFO, " --meta gets metadata boundary addresses and indexes\r\n"); + PRINT (INFO, " --list lists all files\r\n"); + PRINT (INFO, " --details gets file details\r\n"); + PRINT (INFO, " <%%s> filename\r\n"); + PRINT (INFO, " --delete deletes a file\r\n"); + PRINT (INFO, " <%%s> filename\r\n"); + PRINT (INFO, " --create creates a file\r\n"); + PRINT (INFO, " <%%s> filename\r\n"); + PRINT (INFO, " <%%u> size\r\n"); + PRINT (INFO, " <%%u> depth\r\n"); + PRINT (INFO, " --write writes hex data to a file\r\n"); + PRINT (INFO, " <%%s> filename\r\n"); + PRINT (INFO, " <%%d> size\r\n"); + PRINT (INFO, " <%%d> depth\r\n"); + PRINT (INFO, " <%%s> string\r\n"); + PRINT (INFO, " --read prints the contents of a file in hex\r\n"); + PRINT (INFO, " <%%s> filename\r\n"); + PRINT (INFO, " <%%d> size\r\n"); + PRINT (INFO, " <%%d> depth\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static ERRNO_e _fs_meta_init (void) +{ + ERRNO_e err; + uint32_t addr; + uint8_t file_idx; + uint32_t calc_checksum; + + /* Scan and update empty metadata address for next write. */ + for (_fs_meta_empty_addr = _FS_META_START_ADDR; + _fs_meta_empty_addr < _FS_META_END_ADDR; + _fs_meta_empty_addr += sizeof(_fs_meta_s)) + { + /* Check if metadata space is empty. */ + err = _fs_is_empty (_fs_meta_empty_addr, sizeof (_fs_meta_s)); + + if (!err) + { + /* Break if we've reached an empty slot. */ + break; + } + else if (err != ERRNO_MEMORY_NOT_AVAILABLE) + { + return err; + } + } + + /* Scan the entire metadata space. */ + /* TODO : Further analysis needed: + Previously scanned entire space, now only scanning til next empty space. This might be sufficient. + for (addr = _FS_META_START_ADDR; addr < _FS_META_END_ADDR; addr += sizeof(_fs_meta_s)) */ + for (addr = _FS_META_START_ADDR; addr < _fs_meta_empty_addr; addr += sizeof(_fs_meta_s)) + { + uint8_t buf[sizeof(_fs_meta_s)]; + uint16_t checksum_idx = sizeof(_fs_meta_s) - FS_FILE_CHECKSUM_SIZE; + + /* Read entry length. */ + if (NVM_EXT_read (addr, buf, sizeof(_fs_meta_s))) + { + return ERRNO_NVM_FAILURE; + } + + /* Verify if entry is valid by checking CRC checksum. */ + if (CRC_32_calculate (buf, checksum_idx, CRC_32_CHECKSUM_INIT, &calc_checksum)) + { + return ERRNO_CRC_FAILURE; + } + if (calc_checksum == *(uint32_t*)&buf[checksum_idx]) + { + /* Check if we've reached max files. */ + if (_fs_file_cnt == FS_MAX_FILES) + { + return ERRNO_LIMIT_REACHED; + } + else + { + /* Copy to local file table and increment file count. */ + _fs_files[_fs_file_cnt++].meta = *(_fs_meta_s*)buf; + } + } + } + + /* Loop through each file's metadata and scan for information. */ + for (file_idx = 0; file_idx < _fs_file_cnt; file_idx++) + { + uint16_t entry_len = _fs_files[file_idx].meta.size + FS_FILE_INDEX_SIZE + FS_FILE_CHECKSUM_SIZE; + uint16_t checksum_idx = _fs_files[file_idx].meta.size + FS_FILE_INDEX_SIZE; + uint8_t buf[entry_len]; + + /* Calculate number of entries per sector. */ + _fs_files[file_idx].entries_per_sector = (uint16_t) (FS_SECTOR_SIZE / entry_len); + + /* Calculate number of sectors needed. */ + _fs_files[file_idx].sectors = ((_fs_files[file_idx].meta.end_addr - _fs_files[file_idx].meta.start_addr) / + FS_SECTOR_SIZE) + 1; + + /* Set newest index address. */ + _fs_files[file_idx].newest_idx_addr = _fs_files[file_idx].meta.start_addr; + + /* Initialize entry index to -1 since we roll over to 0 for first write. */ + _fs_files[file_idx].newest_idx = (uint16_t) -1; + + /* Loop to find newest index and newest index address. */ + for (addr = _fs_files[file_idx].meta.start_addr; + addr < _fs_files[file_idx].meta.end_addr; ) + { + /* Read entry length. */ + if (NVM_EXT_read (addr, buf, entry_len)) + { + return ERRNO_NVM_FAILURE; + } + + if (CRC_32_calculate (buf, checksum_idx, CRC_32_CHECKSUM_INIT, &calc_checksum)) + { + return ERRNO_CRC_FAILURE; + } + /* Verify if entry is valid by checking CRC checksum. */ + if (calc_checksum == *(uint32_t *) &buf[checksum_idx]) + { + /* Check if index differences are greater than 0 to determine if index is newer. */ + if ((*(int16_t *) &buf[_fs_files[file_idx].meta.size] - + (int16_t) _fs_files[file_idx].newest_idx) > 0) + { + /* Update newest index address. */ + _fs_files[file_idx].newest_idx_addr = addr; + + /* Update the newest index. */ + _fs_files[file_idx].newest_idx = *(uint16_t *) &buf[_fs_files[file_idx].meta.size]; + } + } + + /* Check if we will span sectors. */ + if ((addr + entry_len) > _fs_sector_boundary_addr_get (addr)) + { + /* Update address to next sector. */ + addr = _fs_sector_boundary_addr_get (addr); + } + else + { + addr += entry_len; + } + } + + /* Loop to find empty index address. */ + for (addr = _fs_files[file_idx].meta.start_addr; + addr < _fs_files[file_idx].meta.end_addr; ) + { + /* Check if entry space is empty. */ + err = _fs_is_empty (addr, entry_len); + + if (!err) + { + /* Set empty index address if we've reached an empty slot. */ + _fs_files[file_idx].empty_idx_addr = addr; + break; + } + else if (err != ERRNO_MEMORY_NOT_AVAILABLE) + { + return err; + } + + /* Check if we will span sectors. */ + if ((addr + entry_len) > _fs_sector_boundary_addr_get (addr)) + { + /* Update address to next sector. */ + addr = _fs_sector_boundary_addr_get (addr); + } + else + { + addr += entry_len; + } + } + } + + return ERRNO_SUCCESS; +} + +static ERRNO_e _fs_file_meta_add (_fs_meta_s file_meta) +{ + /* Verify space is empty prior to writing. */ + ERRNO_e err = _fs_is_empty (_fs_meta_empty_addr, sizeof(_fs_meta_s)); + + if (err) + { + return err; + } + + /* Verify we have enough space in flash. */ + if ((_fs_meta_empty_addr + sizeof(_fs_meta_s)) > (_FS_META_END_ADDR + 1)) + { + return ERRNO_NOT_ENOUGH_MEMORY; + } + + /* Write new file to flash file table. */ + if (NVM_EXT_write (_fs_meta_empty_addr, (uint8_t *) &file_meta, sizeof(_fs_meta_s))) + { + return ERRNO_NVM_FAILURE; + } + + /* Update metadata address for next write. */ + _fs_meta_empty_addr += sizeof(_fs_meta_s); + + return ERRNO_SUCCESS; +} + +static ERRNO_e _fs_is_empty (uint32_t start_addr, uint16_t len) +{ + uint16_t i; + uint8_t buf[len]; + + /* Read entry length. */ + if (NVM_EXT_read (start_addr, buf, len)) + { + return ERRNO_NVM_FAILURE; + } + + /* Loop through the read data. */ + for (i = 0; i < len; i++) + { + /* Scan for empty space. */ + if (buf[i] != 0xFF) + { + return ERRNO_MEMORY_NOT_AVAILABLE; + } + } + + return ERRNO_SUCCESS; +} + +static uint32_t _fs_prev_entry_addr_get (uint32_t start_addr, _fs_file_ctx_s* file_ctx) +{ + uint16_t entry_len = file_ctx->meta.size + FS_FILE_INDEX_SIZE + FS_FILE_CHECKSUM_SIZE; + + /* Check for wraparound. */ + if ((int32_t) (start_addr - entry_len) < (int32_t) file_ctx->meta.start_addr) + { + /* Update address to entry before wraparound. */ + start_addr = file_ctx->meta.end_addr - entry_len - (FS_SECTOR_SIZE % entry_len) + 1; + } + else if (_fs_sector_boundary_addr_get (start_addr - entry_len) < + _fs_sector_boundary_addr_get (start_addr)) + { + /* Update address if we span sectors. */ + start_addr = _fs_sector_boundary_addr_get (start_addr - entry_len) - + entry_len - (FS_SECTOR_SIZE % entry_len); + } + else + { + /* Update next entry start address. */ + start_addr -= entry_len; + } + + return start_addr; +} + +static bool _fs_is_entry_valid (uint32_t start_addr, uint16_t len) +{ + uint8_t buf[len]; + uint16_t checksum_idx = len - FS_FILE_CHECKSUM_SIZE; + uint32_t calc_checksum; + + /* Read entry length. */ + if (NVM_EXT_read (start_addr, buf, len)) + { + return false; + } + + /* Verify if entry is valid by checking CRC checksum. */ + if (CRC_32_calculate (buf, checksum_idx, CRC_32_CHECKSUM_INIT, &calc_checksum)) + { + return false; + } + return (calc_checksum == *(uint32_t *) &buf[checksum_idx]); +} + +static _fs_file_ctx_s *_fs_file_get (char *filename) +{ + uint8_t i; + + /* Loop through existing files. */ + for (i = 0; i < _fs_file_cnt; i++) + { + /* Compare file names. */ + if (!strcmp (filename, _fs_files[i].meta.name)) + { + return &_fs_files[i]; + } + } + + /* Return NULL if file does not exist. */ + return NULL; +} + +static uint32_t _fs_sector_boundary_addr_get (uint32_t addr) +{ + uint16_t sectors = (uint16_t) (addr / FS_SECTOR_SIZE) + 1; + + return (sectors * FS_SECTOR_SIZE); +} + +static ERRNO_e _fs_meta_rewrite (void) +{ + ERRNO_e err; + + /* Erase the metadata sector. */ + err = NVM_EXT_erase_block (_FS_META_START_ADDR, NVM_EXT_BLOCK_SIZE_4KB); + if (err) + { + return err; + } + + /* Reset the empty metadata address. */ + _fs_meta_empty_addr = _FS_META_START_ADDR; + + /* Rewrite metadata for all remaining files. */ + for (uint8_t i = 0; i < _fs_file_cnt; i++) + { + err = _fs_file_meta_add (_fs_files[i].meta); + if (err) + { + return err; + } + } + + return ERRNO_SUCCESS; +} diff --git a/src/lib/fs.h b/src/lib/fs.h new file mode 100644 index 0000000..5e45a76 --- /dev/null +++ b/src/lib/fs.h @@ -0,0 +1,94 @@ +/** + * @file fs.h + * @brief File system using non-volatile external memory. + * @author Kenny Tran + * @date Mar 04 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef FS_H_ +#define FS_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + +#include "../bsp/nvm_ext.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define FS_SECTOR_SIZE (NVM_EXT_BLOCK_SIZE_4KB) +/** + * @brief Size of the index written after each file entry. + */ +#define FS_FILE_INDEX_SIZE (sizeof (uint32_t)) +/** + * @brief Size of checksum written after each file entry and index. + */ +#define FS_FILE_CHECKSUM_SIZE (sizeof (uint32_t)) +/** + * @brief Max amount of characters in the filename. + */ +#define FS_FILENAME_LEN (16) +/** + * @brief Max file entry size, accounting for the appended index and checksum. + */ +#define FS_MAX_FILE_ENTRY_SIZE (FS_SECTOR_SIZE - FS_FILE_INDEX_SIZE - FS_FILE_CHECKSUM_SIZE) +/** + * @brief Max amount of files to be created / stored. + */ +#define FS_MAX_FILES (16) +/** + * @brief Max amount of entries per file. + */ +#define FS_MAX_FILE_ENTRIES (0xFFFFFFFF) +/** + * @brief Default file depth + */ +#define FS_FILE_DEPTH_DEFAULT (16) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +/** + * @brief Used as the file handle. + */ +typedef void* FS_file_t; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e FS_init (void); + +ERRNO_e FS_open_or_create (char *p_filename, FS_file_t *p_file, uint16_t size, uint16_t depth); + +ERRNO_e FS_write (FS_file_t file, uint8_t *p_src); + +ERRNO_e FS_write_at_offset (FS_file_t file, uint8_t *p_src, uint16_t offset, uint16_t len); + +ERRNO_e FS_read (FS_file_t file, uint8_t *p_dest, uint16_t offset, uint16_t len); + +ERRNO_e FS_clear (FS_file_t file); + +ERRNO_e FS_delete (char *p_filename); + +uint32_t FS_memory_used (void); + +uint32_t FS_memory_available (void); + +void FS_cli_cmd (int32_t argc, char **argv); + + +#endif /* FS_H_ */ diff --git a/src/lib/gamma.c b/src/lib/gamma.c new file mode 100644 index 0000000..d32c152 --- /dev/null +++ b/src/lib/gamma.c @@ -0,0 +1,64 @@ +/** + * @file gamma.h + * @brief Gamma correction + * @author Kenny Tran + * @date Sep 19 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "gamma.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +uint32_t GAMMA_corrected_get (uint32_t step, uint32_t n_steps, uint32_t max_brightness, double cf) +{ + double normalized; + double corrected; + + if (step >= n_steps) + { + /* 0 based. Clamp to max step. */ + step = (n_steps - 1); + } + + normalized = (double) step / (n_steps - 1); + corrected = (double) pow (normalized, (double) cf); + + return (uint32_t) round (corrected * (double) max_brightness); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/lib/gamma.h b/src/lib/gamma.h new file mode 100644 index 0000000..195642f --- /dev/null +++ b/src/lib/gamma.h @@ -0,0 +1,39 @@ +/** + * @file gamma.h + * @brief Gamma correction + * @author Kenny Tran + * @date Sep 19 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef GAMMA_H_ +#define GAMMA_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +uint32_t GAMMA_corrected_get (uint32_t step, uint32_t n_steps, uint32_t max_brightness, double cf); + + +#endif /* GAMMA_H_ */ diff --git a/src/lib/print.c b/src/lib/print.c new file mode 100644 index 0000000..bacd7c1 --- /dev/null +++ b/src/lib/print.c @@ -0,0 +1,179 @@ +/** + * @file print.c + * @brief Printing / logging tool + * @author Kenny Tran + * @date Aug 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "print.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * THREAD DEFINITIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static PRINT_level_e _print_level = DEBUG; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +void PRINT_level_set (PRINT_level_e level) +{ + _print_level = level; +} + +int32_t PRINT (PRINT_level_e level, const char* format, ...) +{ + va_list vl; + int32_t ret = 0; + + if (_print_level == DISABLED) + { + return 0; + } + + va_start (vl, format); + + /* If debugging, print everything. */ + if (_print_level == DEBUG) + { + ret = vprintf (format, vl); + } + else if (_print_level == level) + { + ret = vprintf (format, vl); + } + + /* CRITICAL, system needs to be halted. */ + if (level == CRITICAL) + { + // __ASSERT (false, "Critial error\r\n"); + } + + va_end (vl); + + return ret; +} + +void PRINT_cli_cmd (int32_t argc, char **argv) +{ + if (argc == 0) + { + printk ("PRINT\r\n"); + printk ("-------------------------------------------\r\n"); + printk ("Severity Level : "); + switch (_print_level) + { + case DISABLED: + printk ("disabled\r\n"); + break; + case DEBUG: + printk ("debug\r\n"); + break; + case INFO: + printk ("info\r\n"); + break; + case WARNING: + printk ("warning\r\n"); + break; + case ERROR: + printk ("error\r\n"); + break; + case CRITICAL: + printk ("critical\r\n"); + break; + } + } + else if (argc == 2) + { + if (!strcmp (argv[0], "-l") || !strcmp (argv[0], "--level")) + { + if (!strcmp (argv[1], "disabled")) + { + PRINT_level_set (DISABLED); + } + else if (!strcmp (argv[1], "debug")) + { + PRINT_level_set (DEBUG); + } + else if (!strcmp (argv[1], "info")) + { + PRINT_level_set (INFO); + } + else if (!strcmp (argv[1], "warning")) + { + PRINT_level_set (WARNING); + } + else if (!strcmp (argv[1], "error")) + { + PRINT_level_set (ERROR); + } + else if (!strcmp (argv[1], "critical")) + { + PRINT_level_set (CRITICAL); + } + else + { + goto USAGE; + } + } + else + { + goto USAGE; + } + } + else + { +USAGE: + printk ("Usage: print [OPTION...]\r\n"); + printk (" -l | --level Sets the severity level to print\r\n"); + printk (" <%%s> debug\r\n"); + printk (" info\r\n"); + printk (" warning\r\n"); + printk (" error\r\n"); + printk (" critical\r\n"); + } +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/lib/print.h b/src/lib/print.h new file mode 100644 index 0000000..3503075 --- /dev/null +++ b/src/lib/print.h @@ -0,0 +1,53 @@ +/** + * @file print.h + * @brief Printing / logging tool + * @author Kenny Tran + * @date Aug 29 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef PRINT_H_ +#define PRINT_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum PRINT_level_e +{ + DISABLED = 0, + DEBUG, + INFO, + WARNING, + ERROR, + CRITICAL +} PRINT_level_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +void PRINT_level_set (PRINT_level_e level); + +int32_t PRINT (PRINT_level_e level, const char* format, ...); + +void PRINT_cli_cmd (int32_t argc, char **argv); + + +#endif /* PRINT_H_ */ diff --git a/src/lib/rgb.c b/src/lib/rgb.c new file mode 100644 index 0000000..7bcad7d --- /dev/null +++ b/src/lib/rgb.c @@ -0,0 +1,93 @@ +/** + * @file rgb.c + * @brief RGB color definitions + * @author Kenny Tran + * @date Mar 01 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "gamma.h" + +#include "rgb.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static const RGB_s _rgb_color_arr[RGB_COLOR_COUNT] = +{ + (RGB_s) { 255, 0, 0 }, /* RGB_COLOR_RED */ + (RGB_s) { 0, 255, 0 }, /* RGB_COLOR_GREEN */ + (RGB_s) { 0, 0, 255 }, /* RGB_COLOR_BLUE */ + (RGB_s) { 255, 127, 0 }, /* RGB_COLOR_ORANGE */ + (RGB_s) { 255, 255, 0 }, /* RGB_COLOR_YELLOW */ + (RGB_s) { 128, 0, 128 }, /* RGB_COLOR_PURPLE */ + (RGB_s) { 254, 1, 154 }, /* RGB_COLOR_PINK */ + (RGB_s) { 255, 255, 255 } /* RGB_COLOR_WHITE */ +}; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +RGB_s RGB_color_get (RGB_color_e color) +{ + if (color >= RGB_COLOR_COUNT) + { + return (RGB_s) { 0, 0, 0 }; + } + + return _rgb_color_arr[(uint32_t) color]; +} + +RGB_s RGB_gamma_corrected (RGB_color_e color, uint8_t step, uint8_t n_steps, uint8_t max_brightness, double cf) +{ + RGB_s ret; + double scaled; + + if (color >= RGB_COLOR_COUNT) + { + return (RGB_s) { 0, 0, 0 }; + } + + /* Calculate scaled max brightness. */ + scaled = ((double) _rgb_color_arr[(uint32_t) color].r / (double) RGB_BRIGHTNESS_MAX) * (double) max_brightness; + ret.r = (uint8_t) GAMMA_corrected_get ((uint32_t) step, (uint32_t) n_steps, (uint32_t) scaled, cf); + + scaled = ((double) _rgb_color_arr[(uint32_t) color].g / (double) RGB_BRIGHTNESS_MAX) * (double) max_brightness; + ret.g = (uint8_t) GAMMA_corrected_get ((uint32_t) step, (uint32_t) n_steps, (uint32_t) scaled, cf); + + scaled = ((double) _rgb_color_arr[(uint32_t) color].b / (double) RGB_BRIGHTNESS_MAX) * (double) max_brightness; + ret.b = (uint8_t) GAMMA_corrected_get ((uint32_t) step, (uint32_t) n_steps, (uint32_t) scaled, cf); + + return ret; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/lib/rgb.h b/src/lib/rgb.h new file mode 100644 index 0000000..c555d25 --- /dev/null +++ b/src/lib/rgb.h @@ -0,0 +1,63 @@ +/** + * @file rgb.h + * @brief RGB color definitions + * @author Kenny Tran + * @date Mar 01 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef RGB_H_ +#define RGB_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define RGB_BRIGHTNESS_MAX ((uint8_t) 255) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct RGB +{ + uint8_t r; + uint8_t g; + uint8_t b; +} RGB_s; + +typedef enum RGB_color +{ + RGB_COLOR_RED = 0, + RGB_COLOR_GREEN, + RGB_COLOR_BLUE, + RGB_COLOR_ORANGE, + RGB_COLOR_YELLOW, + RGB_COLOR_PURPLE, + RGB_COLOR_PINK, + RGB_COLOR_WHITE, + + RGB_COLOR_COUNT, + RGB_COLOR_INVALID = RGB_COLOR_COUNT, +} RGB_color_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +RGB_s RGB_color_get (RGB_color_e color); + +RGB_s RGB_gamma_corrected (RGB_color_e color, uint8_t step, uint8_t n_steps, uint8_t max_brightness, double cf); + + +#endif /* RGB_H_ */ diff --git a/src/lib/virtual_led_ring.c b/src/lib/virtual_led_ring.c new file mode 100644 index 0000000..3d7f048 --- /dev/null +++ b/src/lib/virtual_led_ring.c @@ -0,0 +1,284 @@ +/** + * @file virtual_led_ring.c + * @brief Virtual LED ring module to simulate visual effect of having more LEDs. + * @author Kenny Tran + * @date Nov 13 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include "../lib/errno.h" + +#include "../bsp/led_ring.h" + +#include "virtual_led_ring.h" + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE TYPE DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct _virtual_led +{ + uint16_t r; + uint16_t g; + uint16_t b; +} _virtual_led_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE VARIABLES + *------------------------------------------------------------------------------------------------*/ +static bool _virtual_led_ring_init = false; +static _virtual_led_s _virtual_led_ring_buf[LED_RING_PIXELS]; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void _virtural_led_ring_acc_clear (void); +static void _virtual_led_ring_acc_add (uint16_t idx, uint16_t r, uint16_t g, uint16_t b); +static ERRNO_e _virtual_led_ring_acc_commit (void); +static float _virtual_led_ring_distance_calc(float pos1, float pos2); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e VIRTUAL_LED_RING_init (void) +{ + ERRNO_e err; + + if (_virtual_led_ring_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + _virtural_led_ring_acc_clear (); + + err = LED_RING_init (); + if (err != ERRNO_SUCCESS) + { + return err; + } + + _virtual_led_ring_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e VIRTUAL_LED_RING_set (uint16_t idx, uint8_t r, uint8_t g, uint8_t b, VIRTUAL_LED_RING_mode_e mode) +{ + float position; + + if (!_virtual_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (idx >= VIRTUAL_LED_RING_PIXELS) + { + return ERRNO_OUT_OF_RANGE; + } + + position = (float) idx / (float) VIRTUAL_LED_RING_MULTIPLIER; + + return VIRTUAL_LED_RING_set_position (position, r, g, b, mode); +} + +ERRNO_e VIRTUAL_LED_RING_set_position (float position, uint8_t r, uint8_t g, uint8_t b, VIRTUAL_LED_RING_mode_e mode) +{ + if (!_virtual_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (position < 0.0f || position >= (float) LED_RING_PIXELS) + { + return ERRNO_OUT_OF_RANGE; + } + + // _virtural_led_ring_acc_clear (); + + switch (mode) + { + case VIRTUAL_LED_RING_MODE_LINEAR: + { + /* Linear interpolation between two adjacent LEDs. */ + uint16_t led_low = (uint16_t) position; + uint16_t led_high = (led_low + 1) % LED_RING_PIXELS; + float fraction = position - (float) led_low; + + uint16_t brightness_low_r = (uint16_t) (r * (1.0f - fraction)); + uint16_t brightness_low_g = (uint16_t) (g * (1.0f - fraction)); + uint16_t brightness_low_b = (uint16_t) (b * (1.0f - fraction)); + + uint16_t brightness_high_r = (uint16_t) (r * fraction); + uint16_t brightness_high_g = (uint16_t) (g * fraction); + uint16_t brightness_high_b = (uint16_t) (b * fraction); + + _virtual_led_ring_acc_add (led_low, brightness_low_r, brightness_low_g, brightness_low_b); + _virtual_led_ring_acc_add (led_high, brightness_high_r, brightness_high_g, brightness_high_b); + break; + } + + case VIRTUAL_LED_RING_MODE_GAUSSIAN: + { + uint16_t i; + + /* Gaussian blur across multiple LEDs. */ + for (i = 0; i < LED_RING_PIXELS; i++) + { + float distance = _virtual_led_ring_distance_calc (position, (float) i); + + /* Gaussian falloff: exp(-(distance^2) / (2 * width^2)) */ + float intensity = expf (-(distance * distance) / (2.0f * VIRTUAL_LED_RING_GAUSSIAN_WIDTH * VIRTUAL_LED_RING_GAUSSIAN_WIDTH)); + + /* Only add if intensity is significant (optimization). */ + if (intensity > 0.01f) + { + _virtual_led_ring_acc_add (i, (uint16_t) (r * intensity), + (uint16_t) (g * intensity), + (uint16_t) (b * intensity)); + } + } + break; + } + + case VIRTUAL_LED_RING_MODE_COMET: + { + /* Trailing comet effect. */ + uint16_t i; + uint16_t head = (uint16_t) position; + float decay = 1.0f; + + for (i = 0; i < VIRTUAL_LED_RING_COMET_TAIL_LENGTH; i++) + { + uint16_t led_idx = (head - i + LED_RING_PIXELS) % LED_RING_PIXELS; + + _virtual_led_ring_acc_add (led_idx, (uint16_t) (r * decay), + (uint16_t) (g * decay), + (uint16_t) (b * decay)); + + decay *= VIRTUAL_LED_RING_COMET_DECAY; + } + break; + } + + default: + return ERRNO_INVALID_PARAM; + } + + return _virtual_led_ring_acc_commit (); +} + +ERRNO_e VIRTUAL_LED_RING_clear (void) +{ + if (!_virtual_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + _virtural_led_ring_acc_clear (); + + return LED_RING_clear (); +} + +ERRNO_e VIRTUAL_LED_RING_update (void) +{ + if (!_virtual_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + return LED_RING_update (); +} + +ERRNO_e VIRTUAL_LED_RING_enable (bool en) +{ + if (!_virtual_led_ring_init) + { + return ERRNO_NOT_INITIALIZED; + } + + return LED_RING_enable (en); +} + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTIONS + *------------------------------------------------------------------------------------------------*/ +static void _virtural_led_ring_acc_clear (void) +{ + memset (_virtual_led_ring_buf, 0, sizeof (_virtual_led_ring_buf)); +} + +static void _virtual_led_ring_acc_add (uint16_t idx, uint16_t r, uint16_t g, uint16_t b) +{ + if (idx >= LED_RING_PIXELS) + { + return; + } + + _virtual_led_ring_buf[idx].r += r; + _virtual_led_ring_buf[idx].g += g; + _virtual_led_ring_buf[idx].b += b; + + /* Clamp to max brightness. */ + if (_virtual_led_ring_buf[idx].r > LED_RING_MAX_BRIGHTNESS) + { + _virtual_led_ring_buf[idx].r = LED_RING_MAX_BRIGHTNESS; + } + if (_virtual_led_ring_buf[idx].g > LED_RING_MAX_BRIGHTNESS) + { + _virtual_led_ring_buf[idx].g = LED_RING_MAX_BRIGHTNESS; + } + if (_virtual_led_ring_buf[idx].b > LED_RING_MAX_BRIGHTNESS) + { + _virtual_led_ring_buf[idx].b = LED_RING_MAX_BRIGHTNESS; + } +} + +static ERRNO_e _virtual_led_ring_acc_commit (void) +{ + ERRNO_e err; + uint16_t i; + + for (i = 0; i < LED_RING_PIXELS; i++) + { + err = LED_RING_set (i, (uint8_t) _virtual_led_ring_buf[i].r, + (uint8_t) _virtual_led_ring_buf[i].g, + (uint8_t) _virtual_led_ring_buf[i].b); + if (err != ERRNO_SUCCESS) + { + return err; + } + } + + return ERRNO_SUCCESS; +} + +static float _virtual_led_ring_distance_calc(float pos1, float pos2) +{ + float dist = fabsf(pos1 - pos2); + + /* Account for wraparound. */ + if (dist > (LED_RING_PIXELS / 2.0f)) + { + dist = LED_RING_PIXELS - dist; + } + + return dist; +} diff --git a/src/lib/virtual_led_ring.h b/src/lib/virtual_led_ring.h new file mode 100644 index 0000000..777e6ae --- /dev/null +++ b/src/lib/virtual_led_ring.h @@ -0,0 +1,97 @@ +/** + * @file virtual_led_ring.h + * @brief Virtual LED ring module to simulate visual effect of having more LEDs. + * @author Kenny Tran + * @date Nov 13 2025 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef VIRTUAL_LED_RING_H_ +#define VIRTUAL_LED_RING_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + +#include "../bsp/led_ring.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +/* Virtual resolution multiplier (e.g., 4x means 96 virtual positions for 24 physical LEDs). */ +#define VIRTUAL_LED_RING_MULTIPLIER (4) +#define VIRTUAL_LED_RING_PIXELS (LED_RING_PIXELS * VIRTUAL_LED_RING_MULTIPLIER) + +#define VIRTUAL_LED_RING_GAUSSIAN_WIDTH (1.2f) /* Adjust for wider/narrower blur. */ +#define VIRTUAL_LED_RING_COMET_TAIL_LENGTH (4) /* Number of trailing LEDs. */ +#define VIRTUAL_LED_RING_COMET_DECAY (0.5f) /* Brightness decay per LED. */ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum +{ + VIRTUAL_LED_RING_MODE_LINEAR, /* Linear interpolation (2 LEDs) */ + VIRTUAL_LED_RING_MODE_GAUSSIAN, /* Gaussian blur (3-5 LEDs) */ + VIRTUAL_LED_RING_MODE_COMET /* Trailing comet effect */ +} VIRTUAL_LED_RING_mode_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + +/** + * @brief Initialize the smooth LED ring (calls LED_RING_init internally) + * @return ERRNO_e status code + */ +ERRNO_e VIRTUAL_LED_RING_init (void); + +/** + * @brief Set a virtual LED position with smooth interpolation + * @param idx Virtual index (0 to VIRTUAL_LED_RING_PIXELS - 1) + * @param r Red brightness (0-255) + * @param g Green brightness (0-255) + * @param b Blue brightness (0-255) + * @param mode Smoothing mode to use + * @return ERRNO_e status code + */ +ERRNO_e VIRTUAL_LED_RING_set (uint16_t idx, uint8_t r, uint8_t g, uint8_t b, VIRTUAL_LED_RING_mode_e mode); + +/** + * @brief Set a floating point position (more direct control) + * @param position Floating point position (0.0 to LED_RING_PIXELS - 0.001) + * @param r Red brightness (0-255) + * @param g Green brightness (0-255) + * @param b Blue brightness (0-255) + * @param mode Smoothing mode to use + * @return ERRNO_e status code + */ +ERRNO_e VIRTUAL_LED_RING_set_position (float position, uint8_t r, uint8_t g, uint8_t b, VIRTUAL_LED_RING_mode_e mode); + +/** + * @brief Clear all LEDs (wrapper for LED_RING_clear) + * @return ERRNO_e status code + */ +ERRNO_e VIRTUAL_LED_RING_clear (void); + +/** + * @brief Update the LED ring display (wrapper for LED_RING_update) + * @return ERRNO_e status code + */ +ERRNO_e VIRTUAL_LED_RING_update (void); + +ERRNO_e VIRTUAL_LED_RING_enable (bool en); + + +#endif /* VIRTUAL_LED_RING_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..c41f247 --- /dev/null +++ b/src/main.c @@ -0,0 +1,29 @@ +/** + * @file main.c + * @brief Main module + * @author Kenny Tran + * @date Jul 05 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include "app/app.h" + + +/*-------------------------------------------------------------------------------------------------- + * MAIN FUNCTION + *------------------------------------------------------------------------------------------------*/ +int main (void) +{ + APP_init (); + + APP_start (); + + return 0; +} diff --git a/src/wrappers/adc.c b/src/wrappers/adc.c new file mode 100644 index 0000000..12d2f58 --- /dev/null +++ b/src/wrappers/adc.c @@ -0,0 +1,185 @@ +/** + * @file adc.h + * @brief ADC module + * @author Kenny Tran + * @date Dec 3 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include +#include +#include + +#include "../lib/errno.h" + +#include "adc.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct adc_dt_spec _adc_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static _adc_s _adc_ch_arr[] = +{ + { + .dev = DEVICE_DT_GET(DT_NODELABEL(adc)), + .channel_id = (uint8_t) 0, + .channel_cfg_dt_node_exists = (bool) true, + .channel_cfg = (struct adc_channel_cfg) { + .gain = (enum adc_gain) ADC_GAIN_DIV3, + .reference = (enum adc_reference) ADC_REF_INT, + .acquisition_time = (uint16_t) 0, + .channel_id = (uint8_t) ADC_CH_0, + .input_positive = (uint8_t) 1, + .input_negative = (uint8_t) 0 + }, + .vref_mv = (uint16_t) 0, + .resolution = (uint8_t) ADC_RESOLUTION_12_BITS, + .oversampling = (uint8_t) 0 + }, +}; + +// #define DT_SPEC_AND_COMMA(node_id, prop, idx) \ +// ADC_DT_SPEC_GET_BY_IDX(node_id, idx), + +// /* Data of ADC io-channels specified in devicetree. */ +// static const struct adc_dt_spec _adc_ch_arr[] = { +// DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, +// DT_SPEC_AND_COMMA) +// }; + +static bool _adc_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e ADC_init (void) +{ + ADC_ch_e ch; + + if (_adc_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + /* Only using channel 0. */ + for (ch = 0; ch < ADC_CH_1; ch++) + { + if (!adc_is_ready_dt (&_adc_ch_arr[ch])) + { + return ERRNO_NOT_READY; + } + } + + _adc_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e ADC_cfg (ADC_ch_e ch, ADC_gain_e gain, ADC_ref_e ref, ADC_resolution_e res) +{ + if (!_adc_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if ((ch >= ADC_CH_COUNT) || (gain > ADC_GAIN_X128) || (ref > ADC_REF_EXT_1) || + ((res != ADC_RESOLUTION_12_BITS) && (res != ADC_RESOLUTION_14_BITS))) + { + return ERRNO_INVALID_PARAM; + } + + _adc_ch_arr[ch].channel_cfg.gain = gain; + _adc_ch_arr[ch].channel_cfg.reference = ref; + + if (res == ADC_RESOLUTION_12_BITS) + { + _adc_ch_arr[ch].resolution = ADC_RESOLUTION_12_BITS; + _adc_ch_arr[ch].oversampling = 0; + } + else + { + _adc_ch_arr[ch].resolution = ADC_RESOLUTION_14_BITS; + _adc_ch_arr[ch].oversampling = 8; + } + + if (adc_channel_setup_dt (&_adc_ch_arr[ch]) < 0) + { + return ERRNO_ADC_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e ADC_read (ADC_ch_e ch, int32_t *p_dest) +{ + uint16_t buf; + struct adc_sequence sequence = + { + .buffer = &buf, + /* Buffer size in bytes, not number of samples. */ + .buffer_size = sizeof(buf), + }; + + if (!_adc_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (ch >= ADC_CH_COUNT) + { + return ERRNO_INVALID_PARAM; + } + + (void) adc_sequence_init_dt (&_adc_ch_arr[ch], &sequence); + + if (adc_read_dt (&_adc_ch_arr[ch], &sequence) < 0) + { + *p_dest = 0; + + return ERRNO_ADC_FAILURE; + } + + /* If using differential mode, the 16 bit value in the ADC sample buffer should be a signed 2's complement value. */ + if (_adc_ch_arr[ch].channel_cfg.differential) + { + *p_dest = (int32_t) ((int16_t) buf); + } + else + { + *p_dest = (int32_t) buf; + } + + return ERRNO_SUCCESS; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/wrappers/adc.h b/src/wrappers/adc.h new file mode 100644 index 0000000..e68c7cc --- /dev/null +++ b/src/wrappers/adc.h @@ -0,0 +1,104 @@ +/** + * @file adc.h + * @brief ADC module + * @author Kenny Tran + * @date Dec 3 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef ADC_H_ +#define ADC_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum +{ + ADC_CH_0 = 0, /* P0.02 */ + ADC_CH_1, /* P0.03 */ + ADC_CH_2, /* P0.04 */ + ADC_CH_3, /* P0.05 */ + ADC_CH_4, /* P0.28 */ + ADC_CH_5, /* P0.29 */ + ADC_CH_6, /* P0.30 */ + ADC_CH_7, /* P0.31 */ + + ADC_CH_COUNT +} ADC_ch_e; + +typedef enum +{ + ADC_GAIN_DIV6 = ADC_GAIN_1_6, + ADC_GAIN_DIV5 = ADC_GAIN_1_5, + ADC_GAIN_DIV4 = ADC_GAIN_1_4, + ADC_GAIN_DIV3 = ADC_GAIN_1_3, + ADC_GAIN_X2_DIV5 = ADC_GAIN_2_5, + ADC_GAIN_DIV2 = ADC_GAIN_1_2, + ADC_GAIN_X2_DIV3 = ADC_GAIN_2_3, + ADC_GAIN_X4_DIV5 = ADC_GAIN_4_5, + ADC_GAIN_X1 = ADC_GAIN_1, + ADC_GAIN_X2 = ADC_GAIN_2, + ADC_GAIN_X3 = ADC_GAIN_3, + ADC_GAIN_X4 = ADC_GAIN_4, + ADC_GAIN_X6 = ADC_GAIN_6, + ADC_GAIN_X8 = ADC_GAIN_8, + ADC_GAIN_X12 = ADC_GAIN_12, + ADC_GAIN_X16 = ADC_GAIN_16, + ADC_GAIN_X24 = ADC_GAIN_24, + ADC_GAIN_X32 = ADC_GAIN_32, + ADC_GAIN_X64 = ADC_GAIN_64, + ADC_GAIN_X128 = ADC_GAIN_128 +} ADC_gain_e; + +typedef enum +{ + ADC_REF_VDD = ADC_REF_VDD_1, + ADC_REF_VDD_DIV2 = ADC_REF_VDD_1_2, + ADC_REF_VDD_DIV3 = ADC_REF_VDD_1_3, + ADC_REF_VDD_DIV4 = ADC_REF_VDD_1_4, + ADC_REF_INT = ADC_REF_INTERNAL, + ADC_REF_EXT_0 = ADC_REF_EXTERNAL0, + ADC_REF_EXT_1 = ADC_REF_EXTERNAL1 +} ADC_ref_e; + +typedef enum +{ + ADC_RESOLUTION_12_BITS = 12, + ADC_RESOLUTION_14_BITS = 14 +} ADC_resolution_e; + + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e ADC_init (void); + +ERRNO_e ADC_cfg (ADC_ch_e ch, ADC_gain_e gain, ADC_ref_e ref, ADC_resolution_e res); + +ERRNO_e ADC_read (ADC_ch_e ch, int32_t *p_dest); + + +#endif /* ADC_H_ */ + diff --git a/src/wrappers/gpio - Copy.c b/src/wrappers/gpio - Copy.c new file mode 100644 index 0000000..cb8136a --- /dev/null +++ b/src/wrappers/gpio - Copy.c @@ -0,0 +1,437 @@ +/** + * @file gpio.c + * @brief GPIO module + * @author Kenny Tran + * @date Mar 01 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include +#include + +#include "../lib/errno.h" + +#include "gpio.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct _gpio +{ + struct gpio_dt_spec spec; + struct gpio_callback cb; + bool cb_registered; + bool cfg_output; + bool high; /* NOTE: gpio_pin_get_dt() can't read status if configured + for output. Temporarily using flag to store state. */ +} _gpio_s; + + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void left_button_isr (void); +static void center_button_isr (void); +static void right_button_isr (void); +static void pwr_5v_isr (void); +static void led_ring_pwr_isr (void); +static void led_ring_level_shift_enable_isr (void); +static void led_ring_din_isr (void); +static void battery_check_enable_isr (void); +static void imu_int_isr (void); +static void charger_status_isr (void); + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static _gpio_s _gpio_arr[GPIO_PIN_COUNT] = { + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(left_button), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) left_button_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(center_button), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) center_button_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(right_button), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) right_button_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(pwr_5v_enable), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) pwr_5v_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(led_ring_pwr), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) led_ring_pwr_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(led_ring_level_shift_enable), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) led_ring_level_shift_enable_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(led_ring_din), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) led_ring_din_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(battery_check_enable), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) battery_check_enable_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(imu_int), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) imu_int_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(charger_status), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) charger_status_isr, + .cb_registered = false, + .cfg_output = false, + .high = false + }, +}; +static bool _gpio_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e GPIO_init (void) +{ + uint32_t i; + + if (_gpio_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + for (i = 0; i < GPIO_PIN_COUNT; i++) + { + if (!gpio_is_ready_dt (&_gpio_arr[i].spec)) + { + return ERRNO_NOT_READY; + } + } + + _gpio_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e GPIO_cfg (GPIO_pin_e pin, GPIO_direction_e dir, GPIO_pull_e pull, GPIO_init_e init, GPIO_int_edge_e edge, GPIO_cb_t cb) +{ + uint32_t flags = 0; + + if (!_gpio_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (pin >= GPIO_PIN_COUNT) + { + return ERRNO_INVALID_PARAM; + } + + if (pull == GPIO_PULL_DOWN) + { + flags = GPIO_PULL_DOWN; + } + else if (pull == GPIO_PULL_UP) + { + flags = GPIO_PULL_UP; + } + + /* Disable interrupts. */ + gpio_pin_interrupt_configure_dt (&_gpio_arr[pin].spec, GPIO_INT_DISABLE); + + /* Clear any pending interrupts. */ + NVIC_ClearPendingIRQ (GPIOTE_IRQn); + + switch (dir) + { + case GPIO_DIRECTION_IN: + flags |= GPIO_INPUT; + + if (gpio_pin_configure_dt (&_gpio_arr[pin].spec, flags)) + { + return ERRNO_GPIO_FAILURE; + } + + switch (edge) + { + case GPIO_INT_RISING_EDGE: + /* GPIO interrupt to be triggered on pin rising edge. */ + flags = GPIO_INT_EDGE_RISING; + break; + case GPIO_INT_FALLING_EDGE: + /* GPIO interrupt to be triggered on pin falling edge. */ + flags = GPIO_INT_EDGE_FALLING; + break; + case GPIO_INT_BOTH_EDGES: + /* GPIO interrupt to be triggered on pin rising or falling edge. */ + flags = GPIO_INT_EDGE_BOTH; + break; + case GPIO_INT_PL_LOW: + /* GPIO interrupt to be triggered on pin physical level low. */ + flags = GPIO_INT_LEVEL_LOW; + break; + case GPIO_INT_PL_HIGH: + /* GPIO interrupt to be triggered on pin physical level high. */ + flags = GPIO_INT_LEVEL_HIGH; + break; + case GPIO_INT_STATE_CHANGE_LL_LOW: + /* GPIO interrupt to be triggered on pin state change to logical level 0. */ + flags = GPIO_INT_EDGE_TO_INACTIVE; + break; + case GPIO_INT_STATE_CHANGE_LL_HIGH: + /* GPIO interrupt to be triggered on pin state change to logical level 1. */ + flags = GPIO_INT_EDGE_TO_ACTIVE; + break; + case GPIO_INT_LL_LOW: + /* GPIO interrupt to be triggered on pin logical level 0. */ + flags = GPIO_INT_LEVEL_INACTIVE; + break; + case GPIO_INT_LL_HIGH: + /* GPIO interrupt to be triggered on pin logical level 1. */ + flags = GPIO_INT_LEVEL_ACTIVE; + break; + } + + if (gpio_pin_interrupt_configure_dt (&_gpio_arr[pin].spec, flags)) + { + return ERRNO_GPIO_FAILURE; + } + + if (_gpio_arr[pin].cb_registered) + { + /* Before adding the new callback, remove any existing. */ + gpio_remove_callback (_gpio_arr[pin].spec.port, &_gpio_arr[pin].cb); + } + + if (cb == NULL) + { + gpio_init_callback (&_gpio_arr[pin].cb, (gpio_callback_handler_t) _gpio_arr[pin].cb.handler, BIT(_gpio_arr[pin].spec.pin)); + } + else + { + gpio_init_callback (&_gpio_arr[pin].cb, (gpio_callback_handler_t) cb, BIT(_gpio_arr[pin].spec.pin)); + } + + if (gpio_add_callback (_gpio_arr[pin].spec.port, &_gpio_arr[pin].cb)) + { + return ERRNO_GPIO_FAILURE; + } + + _gpio_arr[pin].cb_registered = true; + _gpio_arr[pin].cfg_output = false; + + break; + + case GPIO_DIRECTION_OUT: + if (init == GPIO_INIT_LOW) + { + flags |= GPIO_OUTPUT_INACTIVE; + _gpio_arr[pin].high = false; + } + else + { + flags |= GPIO_OUTPUT_ACTIVE; + _gpio_arr[pin].high = true; + } + + if (gpio_pin_configure_dt (&_gpio_arr[pin].spec, flags)) + { + return ERRNO_GPIO_FAILURE; + } + + _gpio_arr[pin].cfg_output = true; + + break; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e GPIO_level_set (GPIO_pin_e pin, bool high) +{ + if (_gpio_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (pin >= GPIO_PIN_COUNT) + { + return ERRNO_INVALID_PARAM; + } + + /* Check that pin is not configured for input. */ + if (!_gpio_arr[pin].cfg_output) + { + return ERRNO_GPIO_FAILURE; + } + + if (gpio_pin_set_dt (&_gpio_arr[pin].spec, high)) + { + return ERRNO_GPIO_FAILURE; + } + + _gpio_arr[pin].high = high; + + return ERRNO_SUCCESS; +} + +bool GPIO_level_get (GPIO_pin_e pin) +{ + if (pin >= GPIO_PIN_COUNT) + { + /* TODO: Add asserts. */ + while (1) + ; + } + + /* FIXME: When configured as output, can't read back GPIO state. Using flag to store and readback. */ + if (_gpio_arr[pin].cfg_output) + { + return _gpio_arr[pin].high; + } + + return (bool) gpio_pin_get_dt (&_gpio_arr[pin].spec); +} + +ERRNO_e GPIO_interrupt_disable (GPIO_pin_e pin) +{ + if (_gpio_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (pin >= GPIO_PIN_COUNT) + { + return ERRNO_INVALID_PARAM; + } + + /* Disable interrupts. */ + if (gpio_pin_interrupt_configure_dt (&_gpio_arr[pin].spec, GPIO_INT_DISABLE)) + { + return ERRNO_GPIO_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e GPIO_pending_interrupts_clear (void) +{ + if (_gpio_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + /* Clear any pending interrupts. */ + NVIC_ClearPendingIRQ (GPIOTE_IRQn); + + return ERRNO_SUCCESS; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void left_button_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ + +} + +static void center_button_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ + +} + +static void right_button_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ + +} + +static void pwr_5v_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ + +} + +static void led_ring_pwr_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ + +} + +static void led_ring_level_shift_enable_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ + +} + +static void led_ring_din_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ +} + +static void battery_check_enable_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ +} + +static void imu_int_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ +} + +static void charger_status_isr (void) +{ + /* Cannot have a NULL callback. Used for empty callback. */ +} diff --git a/src/wrappers/gpio - Copy.h b/src/wrappers/gpio - Copy.h new file mode 100644 index 0000000..4318fa1 --- /dev/null +++ b/src/wrappers/gpio - Copy.h @@ -0,0 +1,100 @@ +/** + * @file gpio.h + * @brief GPIO module + * @author Kenny Tran + * @date Mar 01 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef GPIO_H_ +#define GPIO_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum GPIO_direction +{ + GPIO_DIRECTION_IN = 0, + GPIO_DIRECTION_OUT +} GPIO_direction_e; + +typedef enum GPIO_pull +{ + GPIO_PULLNONE = 0, + GPIO_PULLDOWN, + GPIO_PULLUP +} GPIO_pull_e; + +typedef enum GPIO_init +{ + GPIO_INIT_LOW = 0, + GPIO_INIT_HIGH, +} GPIO_init_e; + +typedef enum GPIO_int_edge +{ + GPIO_INT_RISING_EDGE = 0, /* GPIO interrupt to be triggered on pin rising edge. */ + GPIO_INT_FALLING_EDGE, /* GPIO interrupt to be triggered on pin falling edge. */ + GPIO_INT_BOTH_EDGES, /* GPIO interrupt to be triggered on pin rising or falling edge. */ + GPIO_INT_PL_LOW, /* GPIO interrupt to be triggered on pin physical level low. */ + GPIO_INT_PL_HIGH, /* GPIO interrupt to be triggered on pin physical level high. */ + GPIO_INT_STATE_CHANGE_LL_LOW, /* GPIO interrupt to be triggered on pin state change to logical level 0. */ + GPIO_INT_STATE_CHANGE_LL_HIGH, /* GPIO interrupt to be triggered on pin state change to logical level 1. */ + GPIO_INT_LL_LOW, /* GPIO interrupt to be triggered on pin logical level 0. */ + GPIO_INT_LL_HIGH /* GPIO interrupt to be triggered on pin logical level 1. */ +} GPIO_int_edge_e; + +typedef enum GPIO_pin +{ + GPIO_PIN_BUTTON_LEFT = 0, + GPIO_PIN_BUTTON_CENTER, + GPIO_PIN_BUTTON_RIGHT, + GPIO_PIN_5V_ENABLE, + GPIO_PIN_LED_RING_PWR, + GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE, + GPIO_PIN_LED_RING_DIN, + GPIO_PIN_BATTERY_CHECK_ENABLE, + GPIO_PIN_IMU_INT, + GPIO_PIN_CHARGER_STATUS, + + GPIO_PIN_COUNT +} GPIO_pin_e; + +typedef void (*GPIO_cb_t) (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e GPIO_init (void); + +ERRNO_e GPIO_cfg (GPIO_pin_e pin, GPIO_direction_e dir, GPIO_pull_e pull, GPIO_init_e init, GPIO_int_edge_e edge, GPIO_cb_t cb); + +ERRNO_e GPIO_level_set (GPIO_pin_e pin, bool high); + +bool GPIO_level_get (GPIO_pin_e pin); + +ERRNO_e GPIO_interrupt_disable (GPIO_pin_e pin); + +ERRNO_e GPIO_pending_interrupts_clear (void); + +#endif /* GPIO_H_ */ diff --git a/src/wrappers/gpio.c b/src/wrappers/gpio.c new file mode 100644 index 0000000..e19ea4d --- /dev/null +++ b/src/wrappers/gpio.c @@ -0,0 +1,523 @@ +/** + * @file gpio.c + * @brief GPIO module + * @author Kenny Tran + * @date Mar 01 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include + +#include "../lib/errno.h" + +#include "gpio.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct _gpio +{ + struct gpio_dt_spec spec; + struct gpio_callback cb; + bool cfg_output; + bool high; /* NOTE: gpio_pin_get_dt() can't read status if configured + for output. Temporarily using flag to store state. */ + GPIO_cb_t user_cb; + uint32_t int_flags; +} _gpio_s; + + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +static void left_button_isr (void); +static void center_button_isr (void); +static void right_button_isr (void); +static void pwr_5v_isr (void); +static void led_ring_pwr_isr (void); +static void led_ring_level_shift_enable_isr (void); +static void led_ring_din_isr (void); +static void battery_check_enable_isr (void); +static void imu_int_isr (void); +static void charger_status_isr (void); + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static _gpio_s _gpio_arr[GPIO_PIN_COUNT] = { + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(left_button), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) left_button_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(center_button), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) center_button_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(right_button), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) right_button_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(pwr_5v_enable), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) pwr_5v_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(led_ring_pwr), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) led_ring_pwr_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(led_ring_level_shift_enable), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) led_ring_level_shift_enable_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(led_ring_din), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) led_ring_din_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(battery_check_enable), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) battery_check_enable_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(imu_int), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) imu_int_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, + (_gpio_s) { + .spec = GPIO_DT_SPEC_GET_OR(DT_NODELABEL(charger_status), gpios, {0}), + .cb.handler = (gpio_callback_handler_t) charger_status_isr, + .cfg_output = false, + .high = false, + .user_cb = NULL, + .int_flags = 0 + }, +}; +static bool _gpio_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e GPIO_init (void) +{ + uint32_t i; + + if (_gpio_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + for (i = 0; i < GPIO_PIN_COUNT; i++) + { + if (!gpio_is_ready_dt (&_gpio_arr[i].spec)) + { + return ERRNO_NOT_READY; + } + } + + _gpio_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e GPIO_cfg (GPIO_pin_e pin, GPIO_direction_e dir, GPIO_pull_e pull, GPIO_init_e init, GPIO_int_edge_e edge, GPIO_cb_t cb) +{ + uint32_t flags = 0; + + if (!_gpio_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (pin >= GPIO_PIN_COUNT) + { + return ERRNO_INVALID_PARAM; + } + + if (pull == GPIO_PULL_DOWN) + { + flags = GPIO_PULL_DOWN; + } + else if (pull == GPIO_PULL_UP) + { + flags = GPIO_PULL_UP; + } + + if (gpio_pin_interrupt_configure_dt (&_gpio_arr[pin].spec, GPIO_INT_DISABLE)) + { + return ERRNO_GPIO_FAILURE; + } + + switch (dir) + { + case GPIO_DIRECTION_IN: + flags |= GPIO_INPUT; + + if (gpio_pin_configure_dt (&_gpio_arr[pin].spec, flags)) + { + return ERRNO_GPIO_FAILURE; + } + + switch (edge) + { + case GPIO_INT_RISING_EDGE: + /* GPIO interrupt to be triggered on pin rising edge. */ + flags = GPIO_INT_EDGE_RISING; + break; + case GPIO_INT_FALLING_EDGE: + /* GPIO interrupt to be triggered on pin falling edge. */ + flags = GPIO_INT_EDGE_FALLING; + break; + case GPIO_INT_BOTH_EDGES: + /* GPIO interrupt to be triggered on pin rising or falling edge. */ + flags = GPIO_INT_EDGE_BOTH; + break; + case GPIO_INT_PL_LOW: + /* GPIO interrupt to be triggered on pin physical level low. */ + flags = GPIO_INT_LEVEL_LOW; + break; + case GPIO_INT_PL_HIGH: + /* GPIO interrupt to be triggered on pin physical level high. */ + flags = GPIO_INT_LEVEL_HIGH; + break; + case GPIO_INT_STATE_CHANGE_LL_LOW: + /* GPIO interrupt to be triggered on pin state change to logical level 0. */ + flags = GPIO_INT_EDGE_TO_INACTIVE; + break; + case GPIO_INT_STATE_CHANGE_LL_HIGH: + /* GPIO interrupt to be triggered on pin state change to logical level 1. */ + flags = GPIO_INT_EDGE_TO_ACTIVE; + break; + case GPIO_INT_LL_LOW: + /* GPIO interrupt to be triggered on pin logical level 0. */ + flags = GPIO_INT_LEVEL_INACTIVE; + break; + case GPIO_INT_LL_HIGH: + /* GPIO interrupt to be triggered on pin logical level 1. */ + flags = GPIO_INT_LEVEL_ACTIVE; + break; + } + + /* Preserve interrupt flag settings. */ + _gpio_arr[pin].int_flags = flags; + + if (gpio_pin_interrupt_configure_dt (&_gpio_arr[pin].spec, flags)) + { + return ERRNO_GPIO_FAILURE; + } + + /* Set the user callback. */ + _gpio_arr[pin].user_cb = cb; + + gpio_init_callback (&_gpio_arr[pin].cb, (gpio_callback_handler_t) _gpio_arr[pin].cb.handler, BIT(_gpio_arr[pin].spec.pin)); + + if (gpio_add_callback (_gpio_arr[pin].spec.port, &_gpio_arr[pin].cb)) + { + return ERRNO_GPIO_FAILURE; + } + + _gpio_arr[pin].cfg_output = false; + + break; + + case GPIO_DIRECTION_OUT: + if (init == GPIO_INIT_LOW) + { + flags |= GPIO_OUTPUT_INACTIVE; + _gpio_arr[pin].high = false; + } + else + { + flags |= GPIO_OUTPUT_ACTIVE; + _gpio_arr[pin].high = true; + } + + if (gpio_pin_configure_dt (&_gpio_arr[pin].spec, flags)) + { + return ERRNO_GPIO_FAILURE; + } + + _gpio_arr[pin].cfg_output = true; + + break; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e GPIO_level_set (GPIO_pin_e pin, bool high) +{ + if (pin >= GPIO_PIN_COUNT) + { + return ERRNO_INVALID_PARAM; + } + + /* Check that pin is not configured for input. */ + if (!_gpio_arr[pin].cfg_output) + { + return ERRNO_GPIO_FAILURE; + } + + if (gpio_pin_set_dt (&_gpio_arr[pin].spec, high)) + { + return ERRNO_GPIO_FAILURE; + } + + _gpio_arr[pin].high = high; + + return ERRNO_SUCCESS; +} + +bool GPIO_level_get (GPIO_pin_e pin) +{ + if (pin >= GPIO_PIN_COUNT) + { + /* TODO: Add asserts. */ + while (1) + ; + } + + /* FIXME: When configured as output, can't read back GPIO state. Using flag to store and readback. */ + if (_gpio_arr[pin].cfg_output) + { + return _gpio_arr[pin].high; + } + + return (bool) gpio_pin_get_dt (&_gpio_arr[pin].spec); +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +static void left_button_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BUTTON_LEFT].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_BUTTON_LEFT].user_cb) + { + _gpio_arr[GPIO_PIN_BUTTON_LEFT].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_BUTTON_LEFT].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BUTTON_LEFT].spec, _gpio_arr[GPIO_PIN_BUTTON_LEFT].int_flags); + } +} + +static void center_button_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BUTTON_CENTER].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_BUTTON_CENTER].user_cb) + { + _gpio_arr[GPIO_PIN_BUTTON_CENTER].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_BUTTON_CENTER].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BUTTON_CENTER].spec, _gpio_arr[GPIO_PIN_BUTTON_CENTER].int_flags); + } +} + +static void right_button_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BUTTON_RIGHT].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_BUTTON_RIGHT].user_cb) + { + _gpio_arr[GPIO_PIN_BUTTON_RIGHT].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_BUTTON_RIGHT].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BUTTON_RIGHT].spec, _gpio_arr[GPIO_PIN_BUTTON_RIGHT].int_flags); + } +} + +static void pwr_5v_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_5V_ENABLE].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_5V_ENABLE].user_cb) + { + _gpio_arr[GPIO_PIN_5V_ENABLE].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_5V_ENABLE].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_5V_ENABLE].spec, _gpio_arr[GPIO_PIN_5V_ENABLE].int_flags); + } +} + +static void led_ring_pwr_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_LED_RING_PWR].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_LED_RING_PWR].user_cb) + { + _gpio_arr[GPIO_PIN_LED_RING_PWR].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_LED_RING_PWR].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_LED_RING_PWR].spec, _gpio_arr[GPIO_PIN_LED_RING_PWR].int_flags); + } +} + +static void led_ring_level_shift_enable_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE].user_cb) + { + _gpio_arr[GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE].spec, _gpio_arr[GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE].int_flags); + } +} + +static void led_ring_din_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_LED_RING_DIN].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_LED_RING_DIN].user_cb) + { + _gpio_arr[GPIO_PIN_LED_RING_DIN].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_LED_RING_DIN].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_LED_RING_DIN].spec, _gpio_arr[GPIO_PIN_LED_RING_DIN].int_flags); + } +} + +static void battery_check_enable_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BATTERY_CHECK_ENABLE].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_BATTERY_CHECK_ENABLE].user_cb) + { + _gpio_arr[GPIO_PIN_BATTERY_CHECK_ENABLE].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_BATTERY_CHECK_ENABLE].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_BATTERY_CHECK_ENABLE].spec, _gpio_arr[GPIO_PIN_BATTERY_CHECK_ENABLE].int_flags); + } +} + +static void imu_int_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_IMU_INT].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_IMU_INT].user_cb) + { + _gpio_arr[GPIO_PIN_IMU_INT].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_IMU_INT].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_IMU_INT].spec, _gpio_arr[GPIO_PIN_IMU_INT].int_flags); + } +} + +static void charger_status_isr (void) +{ + /* Disable interrupts to clear. */ + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_CHARGER_STATUS].spec, GPIO_INT_DISABLE); + + /* Invoke user callback if not null. */ + if (_gpio_arr[GPIO_PIN_CHARGER_STATUS].user_cb) + { + _gpio_arr[GPIO_PIN_CHARGER_STATUS].user_cb (); + } + + /* Reconfigure preserved interrupt settings.*/ + if (_gpio_arr[GPIO_PIN_CHARGER_STATUS].int_flags) + { + (void) gpio_pin_interrupt_configure_dt (&_gpio_arr[GPIO_PIN_CHARGER_STATUS].spec, _gpio_arr[GPIO_PIN_CHARGER_STATUS].int_flags); + } +} diff --git a/src/wrappers/gpio.h b/src/wrappers/gpio.h new file mode 100644 index 0000000..f673d62 --- /dev/null +++ b/src/wrappers/gpio.h @@ -0,0 +1,98 @@ +/** + * @file gpio.h + * @brief GPIO module + * @author Kenny Tran + * @date Mar 01 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef GPIO_H_ +#define GPIO_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum GPIO_direction +{ + GPIO_DIRECTION_IN = 0, + GPIO_DIRECTION_OUT +} GPIO_direction_e; + +typedef enum GPIO_pull +{ + GPIO_PULLNONE = 0, + GPIO_PULLDOWN, + GPIO_PULLUP +} GPIO_pull_e; + +typedef enum GPIO_init +{ + GPIO_INIT_LOW = 0, + GPIO_INIT_HIGH, +} GPIO_init_e; + +typedef enum GPIO_int_edge +{ + GPIO_INT_RISING_EDGE = 0, /* GPIO interrupt to be triggered on pin rising edge. */ + GPIO_INT_FALLING_EDGE, /* GPIO interrupt to be triggered on pin falling edge. */ + GPIO_INT_BOTH_EDGES, /* GPIO interrupt to be triggered on pin rising or falling edge. */ + GPIO_INT_PL_LOW, /* GPIO interrupt to be triggered on pin physical level low. */ + GPIO_INT_PL_HIGH, /* GPIO interrupt to be triggered on pin physical level high. */ + GPIO_INT_STATE_CHANGE_LL_LOW, /* GPIO interrupt to be triggered on pin state change to logical level 0. */ + GPIO_INT_STATE_CHANGE_LL_HIGH, /* GPIO interrupt to be triggered on pin state change to logical level 1. */ + GPIO_INT_LL_LOW, /* GPIO interrupt to be triggered on pin logical level 0. */ + GPIO_INT_LL_HIGH /* GPIO interrupt to be triggered on pin logical level 1. */ +} GPIO_int_edge_e; + +typedef enum GPIO_pin +{ + GPIO_PIN_BUTTON_LEFT = 0, + GPIO_PIN_BUTTON_CENTER, + GPIO_PIN_BUTTON_RIGHT, + GPIO_PIN_5V_ENABLE, + GPIO_PIN_LED_RING_PWR, + GPIO_PIN_LED_RING_LEVEL_SHIFT_ENABLE, + GPIO_PIN_LED_RING_DIN, + GPIO_PIN_BATTERY_CHECK_ENABLE, + GPIO_PIN_IMU_INT, + GPIO_PIN_CHARGER_STATUS, + + GPIO_PIN_COUNT +} GPIO_pin_e; + +typedef void (*GPIO_cb_t) (void); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e GPIO_init (void); + +ERRNO_e GPIO_cfg (GPIO_pin_e pin, GPIO_direction_e dir, GPIO_pull_e pull, GPIO_init_e init, GPIO_int_edge_e edge, GPIO_cb_t cb); + +ERRNO_e GPIO_level_set (GPIO_pin_e pin, bool high); + +bool GPIO_level_get (GPIO_pin_e pin); + + +#endif /* GPIO_H_ */ + diff --git a/src/wrappers/i2c.c b/src/wrappers/i2c.c new file mode 100644 index 0000000..b3962fb --- /dev/null +++ b/src/wrappers/i2c.c @@ -0,0 +1,468 @@ +/** + * @file i2c.c + * @brief I2C Library + * @author Kenny Tran + * @date Jul 05 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include +#include +#include + +#include "../lib/errno.h" +#include "../lib/debug.h" +#include "../bsp/imu_reg.h" + +#include "i2c.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +/* Ideally should be part of ctx structure but using Zephyr defines is easier. */ +K_MUTEX_DEFINE(_i2c0_mutex); +K_MUTEX_DEFINE(_i2c1_mutex); + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct _i2c_ctx +{ + const struct device *p_dev; + bool init; + struct k_mutex *p_mutex; +} _i2c_ctx_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +/* NOTE: SPI instance 0 and 1 share the same base address as TWI instance 0 and 1. + Reserve 0 and 1 for TWI and use 2 and 3 for SPI. */ +static _i2c_ctx_s _i2c_ctx_arr[I2C_CH_COUNT] = +{ + { + .p_dev = DEVICE_DT_GET(DT_NODELABEL(i2c0)), + .init = false, + .p_mutex = &_i2c0_mutex + }, + { + .p_dev = DEVICE_DT_GET(DT_NODELABEL(i2c1)), + .init = false, + .p_mutex = &_i2c1_mutex + }, +}; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e I2C_init (I2C_ch_e ch) +{ + if (ch >= I2C_CH_COUNT) + { + return ERRNO_LIMIT_REACHED; + } + else if (_i2c_ctx_arr[ch].init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (!device_is_ready (_i2c_ctx_arr[ch].p_dev)) + { + return ERRNO_NOT_READY; + } + + /* Set init flag upon successful initialization. */ + _i2c_ctx_arr[ch].init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e I2C_suspend (void) +{ + uint8_t i; + + for (i = 0; i < I2C_CH_COUNT; i++) + { + if (!_i2c_ctx_arr[i].init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Lock mutex */ + k_mutex_lock(_i2c_ctx_arr[i].p_mutex, K_FOREVER); + + if (pm_device_action_run (_i2c_ctx_arr[i].p_dev, PM_DEVICE_ACTION_SUSPEND) < 0) + { + return ERRNO_PWR_MGMT_FAILURE; + } + } + + return ERRNO_SUCCESS; +} + +ERRNO_e I2C_resume (void) +{ + uint8_t i; + + for (i = 0; i < I2C_CH_COUNT; i++) + { + if (!_i2c_ctx_arr[i].init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (pm_device_action_run (_i2c_ctx_arr[i].p_dev, PM_DEVICE_ACTION_RESUME) < 0) + { + return ERRNO_PWR_MGMT_FAILURE; + } + + /* Unlock mutex */ + k_mutex_unlock(_i2c_ctx_arr[i].p_mutex); + } + + return ERRNO_SUCCESS; +} + +ERRNO_e I2C_words_read (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint16_t *p_dest, uint16_t len) +{ + uint16_t i; + + if (ch >= I2C_CH_COUNT) + { + return ERRNO_INVALID_PARAM; + } + else if (p_dest == NULL) + { + return ERRNO_BAD_POINTER; + } + else if (!_i2c_ctx_arr[ch].init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Lock mutex */ + k_mutex_lock(_i2c_ctx_arr[ch].p_mutex, K_FOREVER); + + /* Write the reg_addr. */ + if (i2c_write (_i2c_ctx_arr[ch].p_dev, ®_addr, 1, dev_addr)) + { + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + return ERRNO_I2C_FAILURE; + } + + /* Loop through to read words. */ + for (i = 0; i < len; i++) + { + /* Transfer data */ + if (i2c_read (_i2c_ctx_arr[ch].p_dev, (uint8_t *) &p_dest[i], 2, dev_addr)) + { + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + return ERRNO_I2C_FAILURE; + } + } + + /* Unlock mutex */ + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + + return ERRNO_SUCCESS; +} + +ERRNO_e I2C_words_write (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint16_t *p_src, uint8_t len) +{ + uint8_t i; + const uint8_t tx_len = len * sizeof (uint16_t); + uint8_t tx_buf[tx_len]; + + if (ch >= I2C_CH_COUNT) + { + return ERRNO_INVALID_PARAM; + } + else if (p_src == NULL) + { + return ERRNO_BAD_POINTER; + } + else if (!_i2c_ctx_arr[ch].init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Lock mutex */ + k_mutex_lock(_i2c_ctx_arr[ch].p_mutex, K_FOREVER); + + /* Write the reg_addr. */ + if (i2c_write (_i2c_ctx_arr[ch].p_dev, ®_addr, 1, dev_addr)) + { + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + return ERRNO_I2C_FAILURE; + } + + /* Loop through mask and write words. */ + for (i = 0; i < len; i++) + { + tx_buf[(i * sizeof (uint16_t))] = p_src[i] >> 8; + tx_buf[(i * sizeof (uint16_t)) + 1] = p_src[i] & 0xFF; + } + + if (i2c_write (_i2c_ctx_arr[ch].p_dev, tx_buf, tx_len, dev_addr)) + { + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + return ERRNO_I2C_FAILURE; + } + + /* Unlock */ + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + + return ERRNO_SUCCESS; +} + +ERRNO_e I2C_bytes_read (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *p_dest, uint32_t len) +{ + if (ch >= I2C_CH_COUNT) + { + return ERRNO_INVALID_PARAM; + } + else if (p_dest == NULL) + { + return ERRNO_BAD_POINTER; + } + else if (!_i2c_ctx_arr[ch].init) + { + return ERRNO_NOT_INITIALIZED; + } + + #ifdef CONFIG_DBG_STATS + if (reg_addr == LSM6DS3TR_C_FIFO_DATA_OUT_L) { + atomic_set(&i2c_transfer_active, 1); + } + #endif + + /* Lock mutex */ + k_mutex_lock(_i2c_ctx_arr[ch].p_mutex, K_FOREVER); + + if (i2c_write_read (_i2c_ctx_arr[ch].p_dev, dev_addr, ®_addr, 1, p_dest, len)) + { + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + return ERRNO_I2C_FAILURE; + } + + /* Unlock */ + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + + #ifdef CONFIG_DBG_STATS + atomic_set(&i2c_transfer_active, 0); + #endif + + return ERRNO_SUCCESS; +} + +ERRNO_e I2C_bytes_write (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *p_src, uint8_t len) +{ + const uint8_t tx_len = len + 1; + uint8_t tx_buf[tx_len]; + + if (ch >= I2C_CH_COUNT) + { + return ERRNO_INVALID_PARAM; + } + else if (p_src == NULL) + { + return ERRNO_BAD_POINTER; + } + else if (!_i2c_ctx_arr[ch].init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Write the register address and data into transmit buffer. */ + tx_buf[0] = reg_addr; + memcpy (&tx_buf[1], p_src, len); + + /* Lock */ + k_mutex_lock(_i2c_ctx_arr[ch].p_mutex, K_FOREVER); + + if (i2c_write (_i2c_ctx_arr[ch].p_dev, tx_buf, tx_len, dev_addr)) + { + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + return ERRNO_I2C_FAILURE; + } + + /* Unlock */ + k_mutex_unlock(_i2c_ctx_arr[ch].p_mutex); + + return ERRNO_SUCCESS; +} + +ERRNO_e I2C_bits_read (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *p_dest, uint8_t bit_start, uint8_t len) +{ + // 01101001 read byte + // 76543210 bit numbers + // xxx args: bitStart=4, length=3 + // 010 masked + // -> 010 shifted + ERRNO_e err = ERRNO_I2C_FAILURE; + uint8_t byte; + + if (p_dest == NULL) + { + return ERRNO_BAD_POINTER; + } + + err = I2C_bytes_read (ch, dev_addr, reg_addr, &byte, 1); + if (err == ERRNO_SUCCESS) + { + uint8_t mask = ((1 << len) - 1) << (bit_start - len + 1); + byte &= mask; + byte >>= (bit_start - len + 1); + *p_dest = byte; + } + + return err; +} + +ERRNO_e I2C_bits_write (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t data, uint8_t bit_start, uint8_t len) +{ + // 010 value to write + // 76543210 bit numbers + // xxx args: bitStart=4, length=3 + // 00011100 mask byte + // 10101111 original value (sample) + // 10100011 original & ~mask + // 10101011 masked | value + ERRNO_e err = ERRNO_I2C_FAILURE; + uint8_t byte; + uint8_t mask; + + err = I2C_bytes_read (ch, dev_addr, reg_addr, &byte, 1); + + if (err != ERRNO_SUCCESS) + { + return err; + } + + /* Determine correct mask. */ + mask = ((1 << len) - 1) << (bit_start - len + 1); + + /* Shift data into correct position */ + data <<= (bit_start - len + 1); + + /* Zero non-important bits in the data. */ + data &= mask; + + /* Zero all important bits in existing byte. */ + byte &= ~(mask); + + /* Combine new data with existing byte. */ + byte |= data; + + err = I2C_bytes_write (ch, dev_addr, reg_addr, &byte, 1); + + return err; +} + +ERRNO_e I2C_field_write(I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t field_data, uint8_t field_start, uint8_t len) +{ + // 010 data to write + // 76543210 bit index + // xxx args: field_start=2, length=3 + // 00011100 mask + // 00001000 data shifted + // 00001000 data & mask + // 10101111 original value (sample) + // 10100011 original & ~mask + // 10101011 (original value & ~mask) | (data & mask) + ERRNO_e err = ERRNO_I2C_FAILURE; + uint8_t byte = 0; + uint8_t mask = 0; + + err = I2C_bytes_read (ch, dev_addr, reg_addr, &byte, 1); + if (err != ERRNO_SUCCESS) + { + return err; + } + + /* Determine correct mask. */ + mask = ((1U << len) - 1) << field_start; + + /* Shift data into correct position */ + uint8_t data_shifted = field_data << field_start; + + /* Zero non-important bits in the data. */ + data_shifted &= mask; + + // Isolate the current bits in the register and compare to the new data + if ((byte & mask) == data_shifted) { + // The register already holds the correct value. + // Return SUCCESS immediately and skip the write. + return ERRNO_SUCCESS; + } + + /* Zero all important bits in existing byte. */ + byte &= ~(mask); + + /* Combine new data with existing byte. */ + byte |= data_shifted; + + err = I2C_bytes_write (ch, dev_addr, reg_addr, &byte, 1); + + return err; +} + +ERRNO_e I2C_field_read(I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *field_data, uint8_t field_start, uint8_t len) +{ + ERRNO_e err = ERRNO_I2C_FAILURE; + uint8_t byte = 0; + uint8_t mask = 0; + + // 1. Safety Check + // Since we are returning data via a pointer, we must ensure it exists. + if (field_data == NULL) { + return ERRNO_BAD_POINTER; + } + + // 2. READ the full register value + err = I2C_bytes_read(ch, dev_addr, reg_addr, &byte, 1); + if (err != ERRNO_SUCCESS) { + return err; + } + + // 3. CALCULATE Mask + // Same logic as the write function: Create a window of 1s at the field location + mask = ((1U << len) - 1) << field_start; + + // 4. ISOLATE the field bits + // Clear everything outside the mask (zero out bits we don't care about) + byte &= mask; + + // 5. NORMALIZE the value (Right Shift) + // Move the bits down to position 0 so the value makes sense to the user. + // Example: If reading bits [3:2] and the value is binary '10', + // the register holds 0x08. We shift right by 2 to get 0x02. + byte >>= field_start; + + // 6. RETURN the value + *field_data = byte; + + return ERRNO_SUCCESS; +} + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/wrappers/i2c.h b/src/wrappers/i2c.h new file mode 100644 index 0000000..07dbcef --- /dev/null +++ b/src/wrappers/i2c.h @@ -0,0 +1,67 @@ +/** + * @file i2c.h + * @brief I2C Library + * @author Kenny Tran + * @date Jul 05 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef I2C_H_ +#define I2C_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum I2C_ch +{ + I2C_CH_0 = 0, + I2C_CH_1, + + I2C_CH_COUNT, + I2C_CH_INVALID = I2C_CH_COUNT +} I2C_ch_e; + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e I2C_init (I2C_ch_e ch); + +ERRNO_e I2C_suspend (void); + +ERRNO_e I2C_resume (void); + +ERRNO_e I2C_words_read (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint16_t *p_dest, uint16_t len); + +ERRNO_e I2C_words_write (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint16_t *p_src, uint8_t len); + +ERRNO_e I2C_bytes_read (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *p_dest, uint32_t len); + +ERRNO_e I2C_bytes_write (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *p_src, uint8_t len); + +ERRNO_e I2C_bits_read (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *p_dest, uint8_t bit_start, uint8_t len); + +ERRNO_e I2C_bits_write (I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t data, uint8_t bit_start, uint8_t len); + +ERRNO_e I2C_field_write(I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t field_data, uint8_t field_start, uint8_t len); +ERRNO_e I2C_field_read(I2C_ch_e ch, uint8_t dev_addr, uint8_t reg_addr, uint8_t *field_data, uint8_t field_start, uint8_t len); + +#endif /* I2C_H_ */ diff --git a/src/wrappers/pwm.c b/src/wrappers/pwm.c new file mode 100644 index 0000000..576369d --- /dev/null +++ b/src/wrappers/pwm.c @@ -0,0 +1,173 @@ +/** + * @file pwm.c + * @brief PWM module + * @author Kenny Tran + * @date May 11 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include +#include + +#include "../lib/errno.h" + +#include "pwm.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define _PWM_MAX_INSTANCES (1) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef struct _pwm +{ + struct pwm_dt_spec spec[PWM_CH_COUNT]; + uint32_t period_ns; + bool init; +} _pwm_s; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static _pwm_s _pwm_arr[_PWM_MAX_INSTANCES] = { + (_pwm_s) { + .spec = + { + PWM_DT_SPEC_GET(DT_ALIAS(bubble_led)), + PWM_DT_SPEC_GET(DT_ALIAS(pin_led)), + PWM_DT_SPEC_GET(DT_ALIAS(fiber_led)), + }, + .period_ns = 0, + .init = false, + }, +}; +static uint8_t _pwm_idx = 0; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e PWM_init (uint8_t *p_inst_id, uint32_t period_ns) +{ + uint8_t i; + + if (_pwm_idx >= _PWM_MAX_INSTANCES) + { + return ERRNO_LIMIT_REACHED; + } + else if (p_inst_id == NULL) + { + return ERRNO_BAD_POINTER; + } + + for (i = 0; i < PWM_CH_COUNT; i++) + { + if (!pwm_is_ready_dt (&_pwm_arr[_pwm_idx].spec[i])) + { + return ERRNO_NOT_READY; + } + } + + _pwm_arr[_pwm_idx].period_ns = period_ns; + _pwm_arr[_pwm_idx].init = true; + + /* Set instance index and increment for next instance. */ + *p_inst_id = _pwm_idx++; + + return ERRNO_SUCCESS; +} + +ERRNO_e PWM_suspend (void) +{ + uint8_t i; + + for (i = 0; i < _PWM_MAX_INSTANCES; i++) + { + if (!_pwm_arr[i].init) + { + return ERRNO_NOT_INITIALIZED; + } + if (pm_device_action_run (_pwm_arr[i].spec[PWM_CH_0].dev, PM_DEVICE_ACTION_SUSPEND) < 0) + { + return ERRNO_PWR_MGMT_FAILURE; + } + } + + return ERRNO_SUCCESS; +} + +ERRNO_e PWM_resume (void) +{ + uint8_t i; + + for (i = 0; i < _PWM_MAX_INSTANCES; i++) + { + if (!_pwm_arr[i].init) + { + return ERRNO_NOT_INITIALIZED; + } + if (pm_device_action_run (_pwm_arr[i].spec[PWM_CH_0].dev, PM_DEVICE_ACTION_RESUME) < 0) + { + return ERRNO_PWR_MGMT_FAILURE; + } + } + + return ERRNO_SUCCESS; +} + +ERRNO_e PWM_duty_cycle_set (uint8_t inst_id, PWM_ch_e ch, float duty_cycle) +{ + uint32_t pulse; + + if (inst_id >= _pwm_idx) + { + return ERRNO_INVALID_PARAM; + } + else if (!_pwm_arr[inst_id].init) + { + return ERRNO_NOT_INITIALIZED; + } + else if (ch >= PWM_CH_COUNT) + { + return ERRNO_INVALID_PARAM; + } + + pulse = (duty_cycle / 100.0f) * _pwm_arr[inst_id].period_ns; + + if (pulse > _pwm_arr[inst_id].period_ns) + { + pulse = _pwm_arr[inst_id].period_ns; + } + + if (pwm_set_dt (&_pwm_arr[inst_id].spec[ch], _pwm_arr[inst_id].period_ns, pulse)) + { + return ERRNO_PWM_FAILURE; + } + + return ERRNO_SUCCESS; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/wrappers/pwm.h b/src/wrappers/pwm.h new file mode 100644 index 0000000..8ab7a9b --- /dev/null +++ b/src/wrappers/pwm.h @@ -0,0 +1,65 @@ +/** + * @file pwm.h + * @brief PWM module + * @author Kenny Tran + * @date May 11 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef PWM_H_ +#define PWM_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define LED_PWM_PERIOD_NS (4000) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ +typedef enum PWM_ch +{ + PWM_CH_0 = 0, /* Bubble LED */ + PWM_CH_1, /* Pin LED */ + PWM_CH_2, /* Fiber LED */ + // PWM_CH_3, /* Unused */ + + PWM_CH_COUNT +} PWM_ch_e; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e PWM_init (uint8_t *p_inst_id, uint32_t period_ns); + +ERRNO_e PWM_suspend (void); + +ERRNO_e PWM_duty_cycle_set (uint8_t inst_id, PWM_ch_e ch, float duty_cycle); + + +#endif /* PWM_H_ */ diff --git a/src/wrappers/spi.c b/src/wrappers/spi.c new file mode 100644 index 0000000..ca06975 --- /dev/null +++ b/src/wrappers/spi.c @@ -0,0 +1,179 @@ +/** + * @file spi.c + * @brief SPI Library + * @author Kenny Tran + * @date Jul 05 2022 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include +#include +#include +#include + +#include "../lib/errno.h" + +#include "spi.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +K_MUTEX_DEFINE(_spi_mutex); + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static const struct device *_spi_dev = DEVICE_DT_GET(DT_NODELABEL(spi2)); +static struct spi_config _spi_cfg = { 0 }; +static struct spi_cs_control _spi_cs = { 0 }; +static bool _spi_init = false; + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +// (struct spi_cs_control){(void).gpio = struct gpio_dt_spec{(void).port = (const struct device *)&__device_dts_ord_11, (void).pin = (gpio_pin_t)18U, (void).dt_flags = (gpio_dt_flags_t)1U}, (void).delay = (uint32_t)0U} + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e SPI_init (void) +{ + if (_spi_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + /* Configure chip select. */ + _spi_cs = (struct spi_cs_control) + { + .gpio = GPIO_DT_SPEC_GET(DT_NODELABEL(spi2), cs_gpios), + .delay = 0 + }; + + /* Configure SPI to run at 4 MHz. */ + _spi_cfg.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB; + _spi_cfg.frequency = 4000000U; + _spi_cfg.cs = _spi_cs; + + if (!device_is_ready (_spi_dev)) + { + return ERRNO_NOT_READY; + } + + /* Set init flag upon successful initialization. */ + _spi_init = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e SPI_suspend (void) +{ + if (!_spi_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Lock mutex */ + k_mutex_lock(&_spi_mutex, K_FOREVER); + + if (pm_device_action_run (_spi_dev, PM_DEVICE_ACTION_SUSPEND) < 0) + { + return ERRNO_PWR_MGMT_FAILURE; + } + + return ERRNO_SUCCESS; +} + +ERRNO_e SPI_resume (void) +{ + if (!_spi_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (pm_device_action_run (_spi_dev, PM_DEVICE_ACTION_RESUME) < 0) + { + return ERRNO_PWR_MGMT_FAILURE; + } + + /* Unlock mutex */ + k_mutex_unlock(&_spi_mutex); + + return ERRNO_SUCCESS; +} + +ERRNO_e SPI_xfer (uint8_t *p_src, uint32_t src_len, uint8_t *p_dest, uint32_t dest_len) +{ + struct spi_buf tx_buf = { + .buf = p_src, + .len = src_len + }; + + /* Two buffers because we aren't reading during the send transaction. */ + struct spi_buf rx_buf[] = + { + { + .buf = NULL, + .len = src_len + }, + { + .buf = p_dest, + .len = dest_len + } + }; + + struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + struct spi_buf_set rx = { + .buffers = rx_buf, + .count = 2 + }; + + /* Note: Only check p_src. Not all SPI transfers perform a read. */ + if (p_src == NULL) + { + return ERRNO_BAD_POINTER; + } + else if (!_spi_init) + { + return ERRNO_NOT_INITIALIZED; + } + + /* Lock mutex */ + k_mutex_lock(&_spi_mutex, K_FOREVER); + + if (spi_transceive (_spi_dev, &_spi_cfg, &tx, &rx)) + { + k_mutex_unlock(&_spi_mutex); + return ERRNO_SPI_FAILURE; + } + + /* Unlock mutex */ + k_mutex_unlock(&_spi_mutex); + + return ERRNO_SUCCESS; +} + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ diff --git a/src/wrappers/spi.h b/src/wrappers/spi.h new file mode 100644 index 0000000..19b43e4 --- /dev/null +++ b/src/wrappers/spi.h @@ -0,0 +1,47 @@ +/** + * @file spi.h + * @brief SPI Library + * @author Kenny Tran + * @date Feb 28 2024 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef SPI_H_ +#define SPI_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e SPI_init (void); + +ERRNO_e SPI_suspend (void); + +ERRNO_e SPI_resume (void); + +ERRNO_e SPI_xfer (uint8_t *p_src, uint32_t src_len, uint8_t *p_dest, uint32_t dest_len); + + +#endif /* SPI_H_ */ diff --git a/src/wrappers/uart.c b/src/wrappers/uart.c new file mode 100644 index 0000000..7af1c15 --- /dev/null +++ b/src/wrappers/uart.c @@ -0,0 +1,184 @@ +/** + * @file uart.c + * @brief UART module + * @author Kenny Tran + * @date Jul 05 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include +#include + +#include "../lib/errno.h" +#include "../lib/print.h" + +#include "uart.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS, STRUCTURES & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ +static const struct device *_uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart0)); +static uint8_t _uart_rx_buf[UART_BUF_SIZE] = { 0 }; +static uint8_t _uart_rx_ch; +static bool _uart_rx_rdy = false; +static bool _uart_init = false; +static bool _uart_suspend = false; + + +/*------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *-----------------------------------------------------------------------------------------------*/ +static void _uart_cb (const struct device *dev, struct uart_event *evt, void *user_data); + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION DEFINITIONS + *------------------------------------------------------------------------------------------------*/ +ERRNO_e UART_init (void) +{ + if (_uart_init) + { + return ERRNO_ALREADY_INITIALIZED; + } + + if (!device_is_ready (_uart_dev)) + { + return ERRNO_NOT_READY; + } + + if (uart_callback_set (_uart_dev, _uart_cb, NULL) != 0) + { + return ERRNO_UART_FAILURE; + } + + if (uart_rx_enable(_uart_dev, _uart_rx_buf, UART_BUF_SIZE, UART_TIMEOUT_MS) != 0) + { + return ERRNO_UART_FAILURE; + } + + k_busy_wait(100000); + + _uart_init = true; + + return ERRNO_SUCCESS; +} + +bool UART_getc (uint8_t *ch) +{ + if (_uart_rx_rdy) + { + *ch = _uart_rx_ch; + + _uart_rx_rdy = false; + + return true; + } + + return false; +} + +ERRNO_e UART_suspend (void) +{ + if (!_uart_init) + { + return ERRNO_NOT_INITIALIZED; + } + + _uart_suspend = true; + + return ERRNO_SUCCESS; +} + +ERRNO_e UART_resume (void) +{ + if (!_uart_init) + { + return ERRNO_NOT_INITIALIZED; + } + + if (pm_device_action_run (_uart_dev, PM_DEVICE_ACTION_RESUME) < 0) + { + PRINT (ERROR, "Failure to suspend UART.\r\n"); + } + + _uart_suspend = false; + + return ERRNO_SUCCESS; +} + +/*------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION DEFINITIONS + *-----------------------------------------------------------------------------------------------*/ +static void _uart_cb (const struct device *dev, struct uart_event *evt, void *user_data) +{ + switch (evt->type) + { + // case UART_TX_DONE: + // break; + + // case UART_TX_ABORTED: + // break; + + case UART_RX_RDY: + _uart_rx_ch = evt->data.rx.buf[evt->data.rx.offset]; + _uart_rx_rdy = true; + break; + + // case UART_RX_BUF_REQUEST: + // break; + + // case UART_RX_BUF_RELEASED: + // break; + + /* Continuous reception is not enabled by default, which means once the receive buffer is full, + you must manually enable reception. Inside the UART_RX_DISABLED case of the UART callback, + you must re-enable UART to have continuous reception */ + case UART_RX_DISABLED: + if (_uart_suspend) + { + /* Disable RX if it's enabled (important to avoid issues during suspend). */ + if (uart_rx_disable(_uart_dev) != 0) + { + PRINT (ERROR, "Failure to disable UART RX.\r\n"); + } + /* UART has finished and is disables, suspend. */ + if (pm_device_action_run (_uart_dev, PM_DEVICE_ACTION_SUSPEND) < 0) + { + + PRINT (ERROR, "Failure to suspend UART.\r\n"); + } + } + else + { + uart_rx_enable (dev, _uart_rx_buf, UART_BUF_SIZE, UART_TIMEOUT_MS); + } + break; + + // case UART_RX_STOPPED: + // break; + + default: + break; + } +} diff --git a/src/wrappers/uart.h b/src/wrappers/uart.h new file mode 100644 index 0000000..f44cc44 --- /dev/null +++ b/src/wrappers/uart.h @@ -0,0 +1,60 @@ +/** + * @file uart.c + * @brief UART module + * @author Kenny Tran + * @date Jul 05 2023 + * @version 0.0.1 + * + * @section LICENSES + * Copyright (C) Morgan Advanced Programmable Systems, Inc. + */ + + +#ifndef UART_H_ +#define UART_H_ + + +/*-------------------------------------------------------------------------------------------------- + * INCLUDE FILES + *------------------------------------------------------------------------------------------------*/ +#include +#include + +#include "../lib/errno.h" + + +/*-------------------------------------------------------------------------------------------------- + * MACROS & CONSTANTS + *------------------------------------------------------------------------------------------------*/ +#define UART_BUF_SIZE (16) +#define UART_TIMEOUT_MS (100) + + +/*-------------------------------------------------------------------------------------------------- + * TYPE DEFINITIONS & ENUMERATIONS + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE CONSTANTS & VARIABLES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PRIVATE FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------------------------------- + * PUBLIC FUNCTION PROTOTYPES + *------------------------------------------------------------------------------------------------*/ +ERRNO_e UART_init (void); + +bool UART_getc (uint8_t *ch); + +ERRNO_e UART_suspend (void); + +ERRNO_e UART_resume (void); + + +#endif /* UART_H_ */ diff --git a/sysbuild.conf b/sysbuild.conf new file mode 100644 index 0000000..c47b805 --- /dev/null +++ b/sysbuild.conf @@ -0,0 +1,7 @@ +SB_CONFIG_PARTITION_MANAGER=y + +# # Enable MCU bootloader +SB_CONFIG_BOOTLOADER_MCUBOOT=y + +# Private key for signing image +SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="C:/ncs/apps/trunk/keys/cyber_pk.pem" diff --git a/sysbuild/mcuboot.conf b/sysbuild/mcuboot.conf new file mode 100644 index 0000000..62caeb8 --- /dev/null +++ b/sysbuild/mcuboot.conf @@ -0,0 +1,12 @@ +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n +CONFIG_LOG=n + +# Using the external crystal for more precise timing (+/-10 PPM) +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=n +CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_20PPM=y + +# Using the internal oscillator (500 PPM) +# CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y +# CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=n \ No newline at end of file

    V(UMvOIoep+-5zp|gs;AkZ{JQ9RaBtbRuUNQj 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 z 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

  • ipOWeN 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